a257827f27
* Merge pull request #7368 from paritytech/td-future-blocks Wait for future blocks in AuRa * Fix tracing failed calls. * Problem: sending any Whisper message fails The error is "PoW too low to compete with other messages" This has been previously reported in #7144 Solution: prevent the move semantics The source of the error is in PoolHandle.relay implementation for NetPoolHandle. Because of the move semantics, `res` variable is in fact copied (as it implements Copy) into the closure and for that reason, the returned result is always `false. * Merge pull request #7433 from paritytech/td-strict-config Strict config parsing * Problem: AuRa's unsafeties around step duration (#7282) Firstly, `Step.duration_remaining` casts it to u32, unnecesarily limiting it to 2^32. While theoretically this is "good enough" (at 3 seconds steps it provides room for a little over 400 years), it is still a lossy way to calculate the remaining time until the next step. Secondly, step duration might be zero, triggering division by zero in `Step.calibrate` Solution: rework the code around the fact that duration is typically in single digits and never grows, hence, it can be represented by a much narrower range (u16) and this highlights the fact that multiplying u64 by u16 will only result in an overflow in even further future, at which point we should panic informatively (if anybody's still around) Similarly, panic when it is detected that incrementing the step counter wrapped around on the overflow of usize. As for the division by zero, prevent it by making zero an invalid value for step duration. This will make AuRa log the constraint mismatch and panic (after all, what purpose would zero step duration serve? it makes no sense within the definition of the protocol, as finality can only be achieved as per the specification if messages are received within the step duration, which would violate the speed of light and other physical laws in this case). * Merge pull request #7437 from paritytech/a5-chains-expanse Remove expanse chain * Expanse Byzantium update w/ correct metropolis difficulty increment divisor (#7463) * Byzantium Update for Expanse Here the changes go. Hope I didnt miss anything. * expip2 changes - update duration limit * Fix missing EXPIP-2 fields * Format numbers as hex * Fix compilation errors * Group expanse chain spec fields together * Set metropolisDifficultyIncrementDivisor for Expanse * Revert #7437 * Add Expanse block 900_000 hash checkpoint * Advance AuRa step as far as we can and prevent invalid blocks. (#7451) * Advance AuRa step as far as we can. * Wait for future blocks. * fixed panic when io is not available for export block, closes #7486 (#7495) * Update Parity Mainnet Bootnodes (#7476) * Update Parity Mainnet Bootnodes * Replace the Azure HDD bootnodes with the new ones :) * Use https connection (#7503) Use https when connecting to etherscan.io API for price-info * Expose default gas price percentile configuration in CLI (#7497) * Expose gas price percentile. * Fix light eth_call. * fix gas_price in light client
1846 lines
59 KiB
Rust
1846 lines
59 KiB
Rust
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
|
// This file is part of Parity.
|
|
|
|
// Parity is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// Parity is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
use std::time::Duration;
|
|
use std::io::Read;
|
|
use std::net::SocketAddr;
|
|
use std::path::{Path, PathBuf};
|
|
use std::collections::BTreeMap;
|
|
use std::cmp::max;
|
|
use std::str::FromStr;
|
|
use cli::{Args, ArgsError};
|
|
use hash::keccak;
|
|
use bigint::prelude::U256;
|
|
use bigint::hash::H256;
|
|
use util::{version_data, Address};
|
|
use bytes::Bytes;
|
|
use ansi_term::Colour;
|
|
use ethsync::{NetworkConfiguration, is_valid_node_url};
|
|
use ethcore::ethstore::ethkey::{Secret, Public};
|
|
use ethcore::client::{VMType};
|
|
use ethcore::miner::{MinerOptions, Banning, StratumOptions};
|
|
use ethcore::verification::queue::VerifierSettings;
|
|
|
|
use rpc::{IpcConfiguration, HttpConfiguration, WsConfiguration, UiConfiguration};
|
|
use rpc_apis::ApiSet;
|
|
use parity_rpc::NetworkSettings;
|
|
use cache::CacheConfig;
|
|
use helpers::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_price, replace_home, replace_home_and_local,
|
|
geth_ipc_path, parity_ipc_path, to_bootnodes, to_addresses, to_address, to_gas_limit, to_queue_strategy};
|
|
use params::{ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras, SpecType};
|
|
use ethcore_logger::Config as LogConfig;
|
|
use dir::{self, Directories, default_hypervisor_path, default_local_path, default_data_path};
|
|
use dapps::Configuration as DappsConfiguration;
|
|
use ipfs::Configuration as IpfsConfiguration;
|
|
use secretstore::{Configuration as SecretStoreConfiguration, NodeSecretKey};
|
|
use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack};
|
|
use run::RunCmd;
|
|
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, KillBlockchain, ExportState, DataFormat};
|
|
use presale::ImportWallet;
|
|
use account::{AccountCmd, NewAccount, ListAccounts, ImportAccounts, ImportFromGethAccounts};
|
|
use snapshot::{self, SnapshotCommand};
|
|
use network::{IpFilter};
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
pub enum Cmd {
|
|
Run(RunCmd),
|
|
Version,
|
|
Account(AccountCmd),
|
|
ImportPresaleWallet(ImportWallet),
|
|
Blockchain(BlockchainCmd),
|
|
SignerToken(WsConfiguration, UiConfiguration, LogConfig),
|
|
SignerSign {
|
|
id: Option<usize>,
|
|
pwfile: Option<PathBuf>,
|
|
port: u16,
|
|
authfile: PathBuf,
|
|
},
|
|
SignerList {
|
|
port: u16,
|
|
authfile: PathBuf
|
|
},
|
|
SignerReject {
|
|
id: Option<usize>,
|
|
port: u16,
|
|
authfile: PathBuf
|
|
},
|
|
Snapshot(SnapshotCommand),
|
|
Hash(Option<String>),
|
|
}
|
|
|
|
pub struct Execute {
|
|
pub logger: LogConfig,
|
|
pub cmd: Cmd,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
pub struct Configuration {
|
|
pub args: Args,
|
|
pub spec_name_override: Option<String>,
|
|
}
|
|
|
|
impl Configuration {
|
|
pub fn parse<S: AsRef<str>>(command: &[S], spec_name_override: Option<String>) -> Result<Self, ArgsError> {
|
|
let args = Args::parse(command)?;
|
|
|
|
let config = Configuration {
|
|
args: args,
|
|
spec_name_override: spec_name_override,
|
|
};
|
|
|
|
Ok(config)
|
|
}
|
|
|
|
pub fn into_command(self) -> Result<Execute, String> {
|
|
let dirs = self.directories();
|
|
let pruning = self.args.arg_pruning.parse()?;
|
|
let pruning_history = self.args.arg_pruning_history;
|
|
let vm_type = self.vm_type()?;
|
|
let spec = self.chain()?;
|
|
let mode = match self.args.arg_mode.as_ref() {
|
|
"last" => None,
|
|
mode => Some(to_mode(&mode, self.args.arg_mode_timeout, self.args.arg_mode_alarm)?),
|
|
};
|
|
let update_policy = self.update_policy()?;
|
|
let logger_config = self.logger_config();
|
|
let ws_conf = self.ws_config()?;
|
|
let http_conf = self.http_config()?;
|
|
let ipc_conf = self.ipc_config()?;
|
|
let net_conf = self.net_config()?;
|
|
let ui_conf = self.ui_config();
|
|
let network_id = self.network_id();
|
|
let cache_config = self.cache_config();
|
|
let tracing = self.args.arg_tracing.parse()?;
|
|
let fat_db = self.args.arg_fat_db.parse()?;
|
|
let compaction = self.args.arg_db_compaction.parse()?;
|
|
let wal = !self.args.flag_fast_and_loose;
|
|
let public_node = self.args.flag_public_node;
|
|
let warp_sync = !self.args.flag_no_warp;
|
|
let geth_compatibility = self.args.flag_geth;
|
|
let dapps_conf = self.dapps_config();
|
|
let ipfs_conf = self.ipfs_config();
|
|
let secretstore_conf = self.secretstore_config()?;
|
|
let format = self.format()?;
|
|
|
|
let cmd = if self.args.flag_version {
|
|
Cmd::Version
|
|
} else if self.args.cmd_signer {
|
|
let authfile = ::signer::codes_path(&ws_conf.signer_path);
|
|
|
|
if self.args.cmd_signer_new_token {
|
|
Cmd::SignerToken(ws_conf, ui_conf, logger_config.clone())
|
|
} else if self.args.cmd_signer_sign {
|
|
let pwfile = self.args.arg_password.first().map(|pwfile| {
|
|
PathBuf::from(pwfile)
|
|
});
|
|
Cmd::SignerSign {
|
|
id: self.args.arg_signer_sign_id,
|
|
pwfile: pwfile,
|
|
port: ws_conf.port,
|
|
authfile: authfile,
|
|
}
|
|
} else if self.args.cmd_signer_reject {
|
|
Cmd::SignerReject {
|
|
id: self.args.arg_signer_reject_id,
|
|
port: ws_conf.port,
|
|
authfile: authfile,
|
|
}
|
|
} else if self.args.cmd_signer_list {
|
|
Cmd::SignerList {
|
|
port: ws_conf.port,
|
|
authfile: authfile,
|
|
}
|
|
} else {
|
|
unreachable!();
|
|
}
|
|
} else if self.args.cmd_tools && self.args.cmd_tools_hash {
|
|
Cmd::Hash(self.args.arg_tools_hash_file)
|
|
} else if self.args.cmd_db && self.args.cmd_db_kill {
|
|
Cmd::Blockchain(BlockchainCmd::Kill(KillBlockchain {
|
|
spec: spec,
|
|
dirs: dirs,
|
|
pruning: pruning,
|
|
}))
|
|
} else if self.args.cmd_account {
|
|
let account_cmd = if self.args.cmd_account_new {
|
|
let new_acc = NewAccount {
|
|
iterations: self.args.arg_keys_iterations,
|
|
path: dirs.keys,
|
|
spec: spec,
|
|
password_file: self.args.arg_password.first().map(|x| x.to_owned()),
|
|
};
|
|
AccountCmd::New(new_acc)
|
|
} else if self.args.cmd_account_list {
|
|
let list_acc = ListAccounts {
|
|
path: dirs.keys,
|
|
spec: spec,
|
|
};
|
|
AccountCmd::List(list_acc)
|
|
} else if self.args.cmd_account_import {
|
|
let import_acc = ImportAccounts {
|
|
from: self.args.arg_account_import_path.expect("CLI argument is required; qed").clone(),
|
|
to: dirs.keys,
|
|
spec: spec,
|
|
};
|
|
AccountCmd::Import(import_acc)
|
|
} else {
|
|
unreachable!();
|
|
};
|
|
Cmd::Account(account_cmd)
|
|
} else if self.args.flag_import_geth_keys {
|
|
let account_cmd = AccountCmd::ImportFromGeth(
|
|
ImportFromGethAccounts {
|
|
spec: spec,
|
|
to: dirs.keys,
|
|
testnet: self.args.flag_testnet
|
|
}
|
|
);
|
|
Cmd::Account(account_cmd)
|
|
} else if self.args.cmd_wallet {
|
|
let presale_cmd = ImportWallet {
|
|
iterations: self.args.arg_keys_iterations,
|
|
path: dirs.keys,
|
|
spec: spec,
|
|
wallet_path: self.args.arg_wallet_import_path.unwrap().clone(),
|
|
password_file: self.args.arg_password.first().map(|x| x.to_owned()),
|
|
};
|
|
Cmd::ImportPresaleWallet(presale_cmd)
|
|
} else if self.args.cmd_import {
|
|
let import_cmd = ImportBlockchain {
|
|
spec: spec,
|
|
cache_config: cache_config,
|
|
dirs: dirs,
|
|
file_path: self.args.arg_import_file.clone(),
|
|
format: format,
|
|
pruning: pruning,
|
|
pruning_history: pruning_history,
|
|
pruning_memory: self.args.arg_pruning_memory,
|
|
compaction: compaction,
|
|
wal: wal,
|
|
tracing: tracing,
|
|
fat_db: fat_db,
|
|
vm_type: vm_type,
|
|
check_seal: !self.args.flag_no_seal_check,
|
|
with_color: logger_config.color,
|
|
verifier_settings: self.verifier_settings(),
|
|
light: self.args.flag_light,
|
|
};
|
|
Cmd::Blockchain(BlockchainCmd::Import(import_cmd))
|
|
} else if self.args.cmd_export {
|
|
if self.args.cmd_export_blocks {
|
|
let export_cmd = ExportBlockchain {
|
|
spec: spec,
|
|
cache_config: cache_config,
|
|
dirs: dirs,
|
|
file_path: self.args.arg_export_blocks_file.clone(),
|
|
format: format,
|
|
pruning: pruning,
|
|
pruning_history: pruning_history,
|
|
pruning_memory: self.args.arg_pruning_memory,
|
|
compaction: compaction,
|
|
wal: wal,
|
|
tracing: tracing,
|
|
fat_db: fat_db,
|
|
from_block: to_block_id(&self.args.arg_export_blocks_from)?,
|
|
to_block: to_block_id(&self.args.arg_export_blocks_to)?,
|
|
check_seal: !self.args.flag_no_seal_check,
|
|
};
|
|
Cmd::Blockchain(BlockchainCmd::Export(export_cmd))
|
|
} else if self.args.cmd_export_state {
|
|
let export_cmd = ExportState {
|
|
spec: spec,
|
|
cache_config: cache_config,
|
|
dirs: dirs,
|
|
file_path: self.args.arg_export_state_file.clone(),
|
|
format: format,
|
|
pruning: pruning,
|
|
pruning_history: pruning_history,
|
|
pruning_memory: self.args.arg_pruning_memory,
|
|
compaction: compaction,
|
|
wal: wal,
|
|
tracing: tracing,
|
|
fat_db: fat_db,
|
|
at: to_block_id(&self.args.arg_export_state_at)?,
|
|
storage: !self.args.flag_export_state_no_storage,
|
|
code: !self.args.flag_export_state_no_code,
|
|
min_balance: self.args.arg_export_state_min_balance.and_then(|s| to_u256(&s).ok()),
|
|
max_balance: self.args.arg_export_state_max_balance.and_then(|s| to_u256(&s).ok()),
|
|
};
|
|
Cmd::Blockchain(BlockchainCmd::ExportState(export_cmd))
|
|
} else {
|
|
unreachable!();
|
|
}
|
|
} else if self.args.cmd_snapshot {
|
|
let snapshot_cmd = SnapshotCommand {
|
|
cache_config: cache_config,
|
|
dirs: dirs,
|
|
spec: spec,
|
|
pruning: pruning,
|
|
pruning_history: pruning_history,
|
|
pruning_memory: self.args.arg_pruning_memory,
|
|
tracing: tracing,
|
|
fat_db: fat_db,
|
|
compaction: compaction,
|
|
file_path: self.args.arg_snapshot_file.clone(),
|
|
wal: wal,
|
|
kind: snapshot::Kind::Take,
|
|
block_at: to_block_id(&self.args.arg_snapshot_at)?,
|
|
};
|
|
Cmd::Snapshot(snapshot_cmd)
|
|
} else if self.args.cmd_restore {
|
|
let restore_cmd = SnapshotCommand {
|
|
cache_config: cache_config,
|
|
dirs: dirs,
|
|
spec: spec,
|
|
pruning: pruning,
|
|
pruning_history: pruning_history,
|
|
pruning_memory: self.args.arg_pruning_memory,
|
|
tracing: tracing,
|
|
fat_db: fat_db,
|
|
compaction: compaction,
|
|
file_path: self.args.arg_restore_file.clone(),
|
|
wal: wal,
|
|
kind: snapshot::Kind::Restore,
|
|
block_at: to_block_id("latest")?, // unimportant.
|
|
};
|
|
Cmd::Snapshot(restore_cmd)
|
|
} else {
|
|
let daemon = if self.args.cmd_daemon {
|
|
Some(self.args.arg_daemon_pid_file.clone().expect("CLI argument is required; qed"))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let verifier_settings = self.verifier_settings();
|
|
let whisper_config = self.whisper_config();
|
|
|
|
let run_cmd = RunCmd {
|
|
cache_config: cache_config,
|
|
dirs: dirs,
|
|
spec: spec,
|
|
pruning: pruning,
|
|
pruning_history: pruning_history,
|
|
pruning_memory: self.args.arg_pruning_memory,
|
|
daemon: daemon,
|
|
logger_config: logger_config.clone(),
|
|
miner_options: self.miner_options()?,
|
|
gas_price_percentile: self.args.arg_gas_price_percentile,
|
|
ntp_servers: self.ntp_servers(),
|
|
ws_conf: ws_conf,
|
|
http_conf: http_conf,
|
|
ipc_conf: ipc_conf,
|
|
net_conf: net_conf,
|
|
network_id: network_id,
|
|
acc_conf: self.accounts_config()?,
|
|
gas_pricer_conf: self.gas_pricer_config()?,
|
|
miner_extras: self.miner_extras()?,
|
|
stratum: self.stratum_options()?,
|
|
update_policy: update_policy,
|
|
mode: mode,
|
|
tracing: tracing,
|
|
fat_db: fat_db,
|
|
compaction: compaction,
|
|
wal: wal,
|
|
vm_type: vm_type,
|
|
warp_sync: warp_sync,
|
|
public_node: public_node,
|
|
geth_compatibility: geth_compatibility,
|
|
net_settings: self.network_settings()?,
|
|
dapps_conf: dapps_conf,
|
|
ipfs_conf: ipfs_conf,
|
|
ui_conf: ui_conf,
|
|
secretstore_conf: secretstore_conf,
|
|
dapp: self.dapp_to_open()?,
|
|
ui: self.args.cmd_ui,
|
|
name: self.args.arg_identity,
|
|
custom_bootnodes: self.args.arg_bootnodes.is_some(),
|
|
no_periodic_snapshot: self.args.flag_no_periodic_snapshot,
|
|
check_seal: !self.args.flag_no_seal_check,
|
|
download_old_blocks: !self.args.flag_no_ancient_blocks,
|
|
verifier_settings: verifier_settings,
|
|
serve_light: !self.args.flag_no_serve_light,
|
|
light: self.args.flag_light,
|
|
no_persistent_txqueue: self.args.flag_no_persistent_txqueue,
|
|
whisper: whisper_config,
|
|
};
|
|
Cmd::Run(run_cmd)
|
|
};
|
|
|
|
Ok(Execute {
|
|
logger: logger_config,
|
|
cmd: cmd,
|
|
})
|
|
}
|
|
|
|
fn vm_type(&self) -> Result<VMType, String> {
|
|
if self.args.flag_jitvm {
|
|
VMType::jit().ok_or("Parity is built without the JIT EVM.".into())
|
|
} else {
|
|
Ok(VMType::Interpreter)
|
|
}
|
|
}
|
|
|
|
fn miner_extras(&self) -> Result<MinerExtras, String> {
|
|
let extras = MinerExtras {
|
|
author: self.author()?,
|
|
extra_data: self.extra_data()?,
|
|
gas_floor_target: to_u256(&self.args.arg_gas_floor_target)?,
|
|
gas_ceil_target: to_u256(&self.args.arg_gas_cap)?,
|
|
engine_signer: self.engine_signer()?,
|
|
};
|
|
|
|
Ok(extras)
|
|
}
|
|
|
|
fn author(&self) -> Result<Address, String> {
|
|
to_address(self.args.arg_etherbase.clone().or(self.args.arg_author.clone()))
|
|
}
|
|
|
|
fn engine_signer(&self) -> Result<Address, String> {
|
|
to_address(self.args.arg_engine_signer.clone())
|
|
}
|
|
|
|
fn format(&self) -> Result<Option<DataFormat>, String> {
|
|
match self.args.arg_import_format.clone()
|
|
.or(self.args.arg_export_blocks_format.clone())
|
|
.or(self.args.arg_export_state_format.clone()) {
|
|
Some(ref f) => Ok(Some(f.parse()?)),
|
|
None => Ok(None),
|
|
}
|
|
}
|
|
|
|
fn cache_config(&self) -> CacheConfig {
|
|
match self.args.arg_cache_size.or(self.args.arg_cache) {
|
|
Some(size) => CacheConfig::new_with_total_cache_size(size),
|
|
None => CacheConfig::new(
|
|
self.args.arg_cache_size_db,
|
|
self.args.arg_cache_size_blocks,
|
|
self.args.arg_cache_size_queue,
|
|
self.args.arg_cache_size_state,
|
|
),
|
|
}
|
|
}
|
|
|
|
fn logger_config(&self) -> LogConfig {
|
|
LogConfig {
|
|
mode: self.args.arg_logging.clone(),
|
|
color: !self.args.flag_no_color && !cfg!(windows),
|
|
file: self.args.arg_log_file.clone(),
|
|
}
|
|
}
|
|
|
|
fn chain(&self) -> Result<SpecType, String> {
|
|
let name = if let Some(ref s) = self.spec_name_override {
|
|
s.clone()
|
|
} else if self.args.flag_testnet {
|
|
"testnet".to_owned()
|
|
} else {
|
|
self.args.arg_chain.clone()
|
|
};
|
|
|
|
Ok(name.parse()?)
|
|
}
|
|
|
|
fn max_peers(&self) -> u32 {
|
|
let peers = self.args.arg_max_peers as u32;
|
|
max(self.min_peers(), peers)
|
|
}
|
|
|
|
fn ip_filter(&self) -> Result<IpFilter, String> {
|
|
match IpFilter::parse(self.args.arg_allow_ips.as_str()) {
|
|
Ok(allow_ip) => Ok(allow_ip),
|
|
Err(_) => Err("Invalid IP filter value".to_owned()),
|
|
}
|
|
}
|
|
|
|
fn min_peers(&self) -> u32 {
|
|
self.args.arg_peers.unwrap_or(self.args.arg_min_peers) as u32
|
|
}
|
|
|
|
fn max_pending_peers(&self) -> u32 {
|
|
self.args.arg_max_pending_peers as u32
|
|
}
|
|
|
|
fn snapshot_peers(&self) -> u32 {
|
|
self.args.arg_snapshot_peers as u32
|
|
}
|
|
|
|
fn work_notify(&self) -> Vec<String> {
|
|
self.args.arg_notify_work.as_ref().map_or_else(Vec::new, |s| s.split(',').map(|s| s.to_owned()).collect())
|
|
}
|
|
|
|
fn accounts_config(&self) -> Result<AccountsConfig, String> {
|
|
let cfg = AccountsConfig {
|
|
iterations: self.args.arg_keys_iterations,
|
|
testnet: self.args.flag_testnet,
|
|
password_files: self.args.arg_password.clone(),
|
|
unlocked_accounts: to_addresses(&self.args.arg_unlock)?,
|
|
enable_hardware_wallets: !self.args.flag_no_hardware_wallets,
|
|
enable_fast_unlock: self.args.flag_fast_unlock,
|
|
};
|
|
|
|
Ok(cfg)
|
|
}
|
|
|
|
fn stratum_options(&self) -> Result<Option<StratumOptions>, String> {
|
|
if self.args.flag_stratum {
|
|
Ok(Some(StratumOptions {
|
|
io_path: self.directories().db,
|
|
listen_addr: self.stratum_interface(),
|
|
port: self.args.arg_ports_shift + self.args.arg_stratum_port,
|
|
secret: self.args.arg_stratum_secret.as_ref().map(|s| s.parse::<H256>().unwrap_or_else(|_| keccak(s))),
|
|
}))
|
|
} else { Ok(None) }
|
|
}
|
|
|
|
fn miner_options(&self) -> Result<MinerOptions, String> {
|
|
let is_dev_chain = self.chain()? == SpecType::Dev;
|
|
if is_dev_chain && self.args.flag_force_sealing && self.args.arg_reseal_min_period == 0 {
|
|
return Err("Force sealing can't be used with reseal_min_period = 0".into());
|
|
}
|
|
|
|
let reseal = self.args.arg_reseal_on_txs.parse::<ResealPolicy>()?;
|
|
|
|
let options = MinerOptions {
|
|
new_work_notify: self.work_notify(),
|
|
force_sealing: self.args.flag_force_sealing,
|
|
reseal_on_external_tx: reseal.external,
|
|
reseal_on_own_tx: reseal.own,
|
|
reseal_on_uncle: self.args.flag_reseal_on_uncle,
|
|
tx_gas_limit: match self.args.arg_tx_gas_limit {
|
|
Some(ref d) => to_u256(d)?,
|
|
None => U256::max_value(),
|
|
},
|
|
tx_queue_size: self.args.arg_tx_queue_size,
|
|
tx_queue_memory_limit: if self.args.arg_tx_queue_mem_limit > 0 {
|
|
Some(self.args.arg_tx_queue_mem_limit as usize * 1024 * 1024)
|
|
} else { None },
|
|
tx_queue_gas_limit: to_gas_limit(&self.args.arg_tx_queue_gas)?,
|
|
tx_queue_strategy: to_queue_strategy(&self.args.arg_tx_queue_strategy)?,
|
|
pending_set: to_pending_set(&self.args.arg_relay_set)?,
|
|
reseal_min_period: Duration::from_millis(self.args.arg_reseal_min_period),
|
|
reseal_max_period: Duration::from_millis(self.args.arg_reseal_max_period),
|
|
work_queue_size: self.args.arg_work_queue_size,
|
|
enable_resubmission: !self.args.flag_remove_solved,
|
|
tx_queue_banning: match self.args.arg_tx_time_limit {
|
|
Some(limit) => Banning::Enabled {
|
|
min_offends: self.args.arg_tx_queue_ban_count,
|
|
offend_threshold: Duration::from_millis(limit),
|
|
ban_duration: Duration::from_secs(self.args.arg_tx_queue_ban_time as u64),
|
|
},
|
|
None => Banning::Disabled,
|
|
},
|
|
refuse_service_transactions: self.args.flag_refuse_service_transactions,
|
|
};
|
|
|
|
Ok(options)
|
|
}
|
|
|
|
fn ui_port(&self) -> u16 {
|
|
self.args.arg_ports_shift + self.args.arg_ui_port
|
|
}
|
|
|
|
fn ntp_servers(&self) -> Vec<String> {
|
|
self.args.arg_ntp_servers.split(",").map(str::to_owned).collect()
|
|
}
|
|
|
|
fn ui_config(&self) -> UiConfiguration {
|
|
UiConfiguration {
|
|
enabled: self.ui_enabled(),
|
|
interface: self.ui_interface(),
|
|
port: self.ui_port(),
|
|
hosts: self.ui_hosts(),
|
|
}
|
|
}
|
|
|
|
fn dapps_config(&self) -> DappsConfiguration {
|
|
let dev_ui = if self.args.flag_ui_no_validation { vec![("localhost".to_owned(), 3000)] } else { vec![] };
|
|
let ui_port = self.ui_port();
|
|
|
|
DappsConfiguration {
|
|
enabled: self.dapps_enabled(),
|
|
dapps_path: PathBuf::from(self.directories().dapps),
|
|
extra_dapps: if self.args.cmd_dapp {
|
|
self.args.arg_dapp_path.iter().map(|path| PathBuf::from(path)).collect()
|
|
} else {
|
|
vec![]
|
|
},
|
|
extra_embed_on: {
|
|
let mut extra_embed = dev_ui.clone();
|
|
match self.ui_hosts() {
|
|
// In case host validation is disabled allow all frame ancestors
|
|
None => extra_embed.push(("*".to_owned(), ui_port)),
|
|
Some(hosts) => extra_embed.extend(hosts.into_iter().filter_map(|host| {
|
|
let mut it = host.split(":");
|
|
let host = it.next();
|
|
let port = it.next().and_then(|v| u16::from_str(v).ok());
|
|
|
|
match (host, port) {
|
|
(Some(host), Some(port)) => Some((host.into(), port)),
|
|
(Some(host), None) => Some((host.into(), ui_port)),
|
|
_ => None,
|
|
}
|
|
})),
|
|
}
|
|
extra_embed
|
|
},
|
|
extra_script_src: dev_ui,
|
|
}
|
|
}
|
|
|
|
fn secretstore_config(&self) -> Result<SecretStoreConfiguration, String> {
|
|
Ok(SecretStoreConfiguration {
|
|
enabled: self.secretstore_enabled(),
|
|
http_enabled: self.secretstore_http_enabled(),
|
|
acl_check_enabled: self.secretstore_acl_check_enabled(),
|
|
self_secret: self.secretstore_self_secret()?,
|
|
nodes: self.secretstore_nodes()?,
|
|
interface: self.secretstore_interface(),
|
|
port: self.args.arg_ports_shift + self.args.arg_secretstore_port,
|
|
http_interface: self.secretstore_http_interface(),
|
|
http_port: self.args.arg_ports_shift + self.args.arg_secretstore_http_port,
|
|
data_path: self.directories().secretstore,
|
|
admin_public: self.secretstore_admin_public()?,
|
|
})
|
|
}
|
|
|
|
fn ipfs_config(&self) -> IpfsConfiguration {
|
|
IpfsConfiguration {
|
|
enabled: self.args.flag_ipfs_api,
|
|
port: self.args.arg_ports_shift + self.args.arg_ipfs_api_port,
|
|
interface: self.ipfs_interface(),
|
|
cors: self.ipfs_cors(),
|
|
hosts: self.ipfs_hosts(),
|
|
}
|
|
}
|
|
|
|
fn dapp_to_open(&self) -> Result<Option<String>, String> {
|
|
if !self.args.cmd_dapp {
|
|
return Ok(None);
|
|
}
|
|
let path = self.args.arg_dapp_path.as_ref().map(String::as_str).unwrap_or(".");
|
|
let path = Path::new(path).canonicalize()
|
|
.map_err(|e| format!("Invalid path: {}. Error: {:?}", path, e))?;
|
|
let name = path.file_name()
|
|
.and_then(|name| name.to_str())
|
|
.ok_or_else(|| "Root path is not supported.".to_owned())?;
|
|
Ok(Some(name.into()))
|
|
}
|
|
|
|
fn gas_pricer_config(&self) -> Result<GasPricerConfig, String> {
|
|
fn wei_per_gas(usd_per_tx: f32, usd_per_eth: f32) -> U256 {
|
|
let wei_per_usd: f32 = 1.0e18 / usd_per_eth;
|
|
let gas_per_tx: f32 = 21000.0;
|
|
let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx;
|
|
U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap()
|
|
}
|
|
|
|
if let Some(dec) = self.args.arg_gasprice.as_ref() {
|
|
return Ok(GasPricerConfig::Fixed(to_u256(dec)?));
|
|
} else if let Some(dec) = self.args.arg_min_gas_price {
|
|
return Ok(GasPricerConfig::Fixed(U256::from(dec)));
|
|
}
|
|
|
|
let usd_per_tx = to_price(&self.args.arg_usd_per_tx)?;
|
|
if "auto" == self.args.arg_usd_per_eth.as_str() {
|
|
// Just a very rough estimate to avoid accepting
|
|
// ZGP transactions before the price is fetched
|
|
// if user does not want it.
|
|
let last_known_usd_per_eth = 10.0;
|
|
return Ok(GasPricerConfig::Calibrated {
|
|
initial_minimum: wei_per_gas(usd_per_tx, last_known_usd_per_eth),
|
|
usd_per_tx: usd_per_tx,
|
|
recalibration_period: to_duration(self.args.arg_price_update_period.as_str())?,
|
|
});
|
|
}
|
|
|
|
let usd_per_eth = to_price(&self.args.arg_usd_per_eth)?;
|
|
let wei_per_gas = wei_per_gas(usd_per_tx, usd_per_eth);
|
|
|
|
info!(
|
|
"Using a fixed conversion rate of Ξ1 = {} ({} wei/gas)",
|
|
Colour::White.bold().paint(format!("US${:.2}", usd_per_eth)),
|
|
Colour::Yellow.bold().paint(format!("{}", wei_per_gas))
|
|
);
|
|
|
|
Ok(GasPricerConfig::Fixed(wei_per_gas))
|
|
}
|
|
|
|
fn extra_data(&self) -> Result<Bytes, String> {
|
|
match self.args.arg_extradata.as_ref().or(self.args.arg_extra_data.as_ref()) {
|
|
Some(x) if x.len() <= 32 => Ok(x.as_bytes().to_owned()),
|
|
None => Ok(version_data()),
|
|
Some(_) => Err("Extra data must be at most 32 characters".into()),
|
|
}
|
|
}
|
|
|
|
fn init_reserved_nodes(&self) -> Result<Vec<String>, String> {
|
|
use std::fs::File;
|
|
|
|
match self.args.arg_reserved_peers {
|
|
Some(ref path) => {
|
|
let mut buffer = String::new();
|
|
let mut node_file = File::open(path).map_err(|e| format!("Error opening reserved nodes file: {}", e))?;
|
|
node_file.read_to_string(&mut buffer).map_err(|_| "Error reading reserved node file")?;
|
|
let lines = buffer.lines().map(|s| s.trim().to_owned()).filter(|s| !s.is_empty() && !s.starts_with("#")).collect::<Vec<_>>();
|
|
if let Some(invalid) = lines.iter().find(|s| !is_valid_node_url(s)) {
|
|
return Err(format!("Invalid node address format given for a boot node: {}", invalid));
|
|
}
|
|
Ok(lines)
|
|
},
|
|
None => Ok(Vec::new())
|
|
}
|
|
}
|
|
|
|
fn net_addresses(&self) -> Result<(SocketAddr, Option<SocketAddr>), String> {
|
|
let port = self.args.arg_ports_shift + self.args.arg_port;
|
|
let listen_address = SocketAddr::new("0.0.0.0".parse().unwrap(), port);
|
|
let public_address = if self.args.arg_nat.starts_with("extip:") {
|
|
let host = &self.args.arg_nat[6..];
|
|
let host = host.parse().map_err(|_| format!("Invalid host given with `--nat extip:{}`", host))?;
|
|
Some(SocketAddr::new(host, port))
|
|
} else {
|
|
None
|
|
};
|
|
Ok((listen_address, public_address))
|
|
}
|
|
|
|
fn net_config(&self) -> Result<NetworkConfiguration, String> {
|
|
let mut ret = NetworkConfiguration::new();
|
|
ret.nat_enabled = self.args.arg_nat == "any" || self.args.arg_nat == "upnp";
|
|
ret.boot_nodes = to_bootnodes(&self.args.arg_bootnodes)?;
|
|
let (listen, public) = self.net_addresses()?;
|
|
ret.listen_address = Some(format!("{}", listen));
|
|
ret.public_address = public.map(|p| format!("{}", p));
|
|
ret.use_secret = match self.args.arg_node_key.as_ref()
|
|
.map(|s| s.parse::<Secret>().or_else(|_| Secret::from_unsafe_slice(&keccak(s))).map_err(|e| format!("Invalid key: {:?}", e))
|
|
) {
|
|
None => None,
|
|
Some(Ok(key)) => Some(key),
|
|
Some(Err(err)) => return Err(err),
|
|
};
|
|
ret.discovery_enabled = !self.args.flag_no_discovery && !self.args.flag_nodiscover;
|
|
ret.max_peers = self.max_peers();
|
|
ret.min_peers = self.min_peers();
|
|
ret.snapshot_peers = self.snapshot_peers();
|
|
ret.ip_filter = self.ip_filter()?;
|
|
ret.max_pending_peers = self.max_pending_peers();
|
|
let mut net_path = PathBuf::from(self.directories().base);
|
|
net_path.push("network");
|
|
ret.config_path = Some(net_path.to_str().unwrap().to_owned());
|
|
ret.reserved_nodes = self.init_reserved_nodes()?;
|
|
ret.allow_non_reserved = !self.args.flag_reserved_only;
|
|
Ok(ret)
|
|
}
|
|
|
|
fn network_id(&self) -> Option<u64> {
|
|
self.args.arg_network_id.or(self.args.arg_networkid)
|
|
}
|
|
|
|
fn rpc_apis(&self) -> String {
|
|
let mut apis: Vec<&str> = self.args.arg_rpcapi
|
|
.as_ref()
|
|
.unwrap_or(&self.args.arg_jsonrpc_apis)
|
|
.split(",")
|
|
.collect();
|
|
|
|
if self.args.flag_geth {
|
|
apis.insert(0, "personal");
|
|
}
|
|
|
|
apis.join(",")
|
|
}
|
|
|
|
fn cors(cors: Option<&String>) -> Option<Vec<String>> {
|
|
// Never return `None` here (enables CORS for all hosts).
|
|
Some(cors.map(|ref c| c.split(',').map(Into::into).collect()).unwrap_or_default())
|
|
}
|
|
|
|
fn rpc_cors(&self) -> Option<Vec<String>> {
|
|
let cors = self.args.arg_jsonrpc_cors.as_ref().or(self.args.arg_rpccorsdomain.as_ref());
|
|
Self::cors(cors)
|
|
}
|
|
|
|
fn ipfs_cors(&self) -> Option<Vec<String>> {
|
|
Self::cors(self.args.arg_ipfs_api_cors.as_ref())
|
|
}
|
|
|
|
fn hosts(&self, hosts: &str, interface: &str) -> Option<Vec<String>> {
|
|
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<Vec<String>> {
|
|
match hosts {
|
|
"none" => return Some(Vec::new()),
|
|
"*" | "all" | "any" => return None,
|
|
_ => {}
|
|
}
|
|
let hosts = hosts.split(',').map(Into::into).collect();
|
|
Some(hosts)
|
|
}
|
|
|
|
fn ui_hosts(&self) -> Option<Vec<String>> {
|
|
self.hosts(&self.args.arg_ui_hosts, &self.ui_interface())
|
|
}
|
|
|
|
fn rpc_hosts(&self) -> Option<Vec<String>> {
|
|
self.hosts(&self.args.arg_jsonrpc_hosts, &self.rpc_interface())
|
|
}
|
|
|
|
fn ws_hosts(&self) -> Option<Vec<String>> {
|
|
self.hosts(&self.args.arg_ws_hosts, &self.ws_interface())
|
|
}
|
|
|
|
fn ws_origins(&self) -> Option<Vec<String>> {
|
|
if self.args.flag_unsafe_expose || self.args.flag_ui_no_validation {
|
|
return None;
|
|
}
|
|
|
|
Self::parse_hosts(&self.args.arg_ws_origins)
|
|
}
|
|
|
|
fn ipfs_hosts(&self) -> Option<Vec<String>> {
|
|
self.hosts(&self.args.arg_ipfs_api_hosts, &self.ipfs_interface())
|
|
}
|
|
|
|
fn ipc_config(&self) -> Result<IpcConfiguration, String> {
|
|
let conf = IpcConfiguration {
|
|
enabled: !(self.args.flag_ipcdisable || self.args.flag_ipc_off || self.args.flag_no_ipc),
|
|
socket_addr: self.ipc_path(),
|
|
apis: {
|
|
let mut apis = self.args.arg_ipcapi.clone().unwrap_or(self.args.arg_ipc_apis.clone());
|
|
if self.args.flag_geth {
|
|
if !apis.is_empty() {
|
|
apis.push_str(",");
|
|
}
|
|
apis.push_str("personal");
|
|
}
|
|
apis.parse()?
|
|
},
|
|
};
|
|
|
|
Ok(conf)
|
|
}
|
|
|
|
fn http_config(&self) -> Result<HttpConfiguration, String> {
|
|
let conf = HttpConfiguration {
|
|
enabled: self.rpc_enabled(),
|
|
interface: self.rpc_interface(),
|
|
port: self.args.arg_ports_shift + self.args.arg_rpcport.unwrap_or(self.args.arg_jsonrpc_port),
|
|
apis: match self.args.flag_public_node {
|
|
false => self.rpc_apis().parse()?,
|
|
true => self.rpc_apis().parse::<ApiSet>()?.retain(ApiSet::PublicContext),
|
|
},
|
|
hosts: self.rpc_hosts(),
|
|
cors: self.rpc_cors(),
|
|
server_threads: match self.args.arg_jsonrpc_server_threads {
|
|
Some(threads) if threads > 0 => threads,
|
|
_ => 1,
|
|
},
|
|
processing_threads: self.args.arg_jsonrpc_threads,
|
|
};
|
|
|
|
Ok(conf)
|
|
}
|
|
|
|
fn ws_config(&self) -> Result<WsConfiguration, String> {
|
|
let ui = self.ui_config();
|
|
|
|
let conf = WsConfiguration {
|
|
enabled: self.ws_enabled(),
|
|
interface: self.ws_interface(),
|
|
port: self.args.arg_ports_shift + self.args.arg_ws_port,
|
|
apis: self.args.arg_ws_apis.parse()?,
|
|
hosts: self.ws_hosts(),
|
|
origins: self.ws_origins(),
|
|
signer_path: self.directories().signer.into(),
|
|
support_token_api: !self.args.flag_public_node,
|
|
ui_address: ui.address(),
|
|
};
|
|
|
|
Ok(conf)
|
|
}
|
|
|
|
fn network_settings(&self) -> Result<NetworkSettings, String> {
|
|
let http_conf = self.http_config()?;
|
|
let net_addresses = self.net_addresses()?;
|
|
Ok(NetworkSettings {
|
|
name: self.args.arg_identity.clone(),
|
|
chain: format!("{}", self.chain()?),
|
|
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<UpdatePolicy, String> {
|
|
Ok(UpdatePolicy {
|
|
enable_downloading: !self.args.flag_no_download,
|
|
require_consensus: !self.args.flag_no_consensus,
|
|
filter: match self.args.arg_auto_update.as_ref() {
|
|
"none" => UpdateFilter::None,
|
|
"critical" => UpdateFilter::Critical,
|
|
"all" => UpdateFilter::All,
|
|
_ => return Err("Invalid value for `--auto-update`. See `--help` for more information.".into()),
|
|
},
|
|
track: match self.args.arg_release_track.as_ref() {
|
|
"stable" => ReleaseTrack::Stable,
|
|
"beta" => ReleaseTrack::Beta,
|
|
"nightly" => ReleaseTrack::Nightly,
|
|
"testing" => ReleaseTrack::Testing,
|
|
"current" => ReleaseTrack::Unknown,
|
|
_ => return Err("Invalid value for `--releases-track`. See `--help` for more information.".into()),
|
|
},
|
|
path: default_hypervisor_path(),
|
|
})
|
|
}
|
|
|
|
fn directories(&self) -> Directories {
|
|
let local_path = default_local_path();
|
|
let base_path = self.args.arg_base_path.as_ref().or_else(|| self.args.arg_datadir.as_ref()).map_or_else(|| default_data_path(), |s| s.clone());
|
|
let data_path = replace_home("", &base_path);
|
|
let is_using_base_path = self.args.arg_base_path.is_some();
|
|
// If base_path is set and db_path is not we default to base path subdir instead of LOCAL.
|
|
let base_db_path = if is_using_base_path && self.args.arg_db_path.is_none() {
|
|
"$BASE/chains"
|
|
} else {
|
|
self.args.arg_db_path.as_ref().map_or(dir::CHAINS_PATH, |s| &s)
|
|
};
|
|
let cache_path = if is_using_base_path { "$BASE/cache" } else { dir::CACHE_PATH };
|
|
|
|
let db_path = replace_home_and_local(&data_path, &local_path, &base_db_path);
|
|
let cache_path = replace_home_and_local(&data_path, &local_path, cache_path);
|
|
let keys_path = replace_home(&data_path, &self.args.arg_keys_path);
|
|
let dapps_path = replace_home(&data_path, &self.args.arg_dapps_path);
|
|
let secretstore_path = replace_home(&data_path, &self.args.arg_secretstore_path);
|
|
let ui_path = replace_home(&data_path, &self.args.arg_ui_path);
|
|
|
|
Directories {
|
|
keys: keys_path,
|
|
base: data_path,
|
|
cache: cache_path,
|
|
db: db_path,
|
|
dapps: dapps_path,
|
|
signer: ui_path,
|
|
secretstore: secretstore_path,
|
|
}
|
|
}
|
|
|
|
fn ipc_path(&self) -> String {
|
|
if self.args.flag_geth {
|
|
geth_ipc_path(self.args.flag_testnet)
|
|
} else {
|
|
parity_ipc_path(
|
|
&self.directories().base,
|
|
&self.args.arg_ipcpath.clone().unwrap_or(self.args.arg_ipc_path.clone()),
|
|
self.args.arg_ports_shift,
|
|
)
|
|
}
|
|
}
|
|
|
|
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",
|
|
x => x,
|
|
}.into()
|
|
}
|
|
|
|
fn ui_interface(&self) -> String {
|
|
self.interface(&self.args.arg_ui_interface)
|
|
}
|
|
|
|
fn rpc_interface(&self) -> String {
|
|
let rpc_interface = self.args.arg_rpcaddr.clone().unwrap_or(self.args.arg_jsonrpc_interface.clone());
|
|
self.interface(&rpc_interface)
|
|
}
|
|
|
|
fn ws_interface(&self) -> String {
|
|
self.interface(&self.args.arg_ws_interface)
|
|
}
|
|
|
|
fn ipfs_interface(&self) -> String {
|
|
self.interface(&self.args.arg_ipfs_api_interface)
|
|
}
|
|
|
|
fn secretstore_interface(&self) -> String {
|
|
self.interface(&self.args.arg_secretstore_interface)
|
|
}
|
|
|
|
fn secretstore_http_interface(&self) -> String {
|
|
self.interface(&self.args.arg_secretstore_http_interface)
|
|
}
|
|
|
|
fn secretstore_self_secret(&self) -> Result<Option<NodeSecretKey>, String> {
|
|
match self.args.arg_secretstore_secret {
|
|
Some(ref s) if s.len() == 64 => Ok(Some(NodeSecretKey::Plain(s.parse()
|
|
.map_err(|e| format!("Invalid secret store secret: {}. Error: {:?}", s, e))?))),
|
|
Some(ref s) if s.len() == 40 => Ok(Some(NodeSecretKey::KeyStore(s.parse()
|
|
.map_err(|e| format!("Invalid secret store secret address: {}. Error: {:?}", s, e))?))),
|
|
Some(_) => Err(format!("Invalid secret store secret. Must be either existing account address, or hex-encoded private key")),
|
|
None => Ok(None),
|
|
}
|
|
}
|
|
|
|
fn secretstore_admin_public(&self) -> Result<Option<Public>, String> {
|
|
match self.args.arg_secretstore_admin_public.as_ref() {
|
|
Some(admin_public) => Ok(Some(admin_public.parse().map_err(|e| format!("Invalid secret store admin public: {}", e))?)),
|
|
None => Ok(None),
|
|
}
|
|
}
|
|
|
|
fn secretstore_nodes(&self) -> Result<BTreeMap<Public, (String, u16)>, String> {
|
|
let mut nodes = BTreeMap::new();
|
|
for node in self.args.arg_secretstore_nodes.split(',').filter(|n| n != &"") {
|
|
let public_and_addr: Vec<_> = node.split('@').collect();
|
|
if public_and_addr.len() != 2 {
|
|
return Err(format!("Invalid secret store node: {}", node));
|
|
}
|
|
|
|
let ip_and_port: Vec<_> = public_and_addr[1].split(':').collect();
|
|
if ip_and_port.len() != 2 {
|
|
return Err(format!("Invalid secret store node: {}", node));
|
|
}
|
|
|
|
let public = public_and_addr[0].parse()
|
|
.map_err(|e| format!("Invalid public key in secret store node: {}. Error: {:?}", public_and_addr[0], e))?;
|
|
let port = ip_and_port[1].parse()
|
|
.map_err(|e| format!("Invalid port in secret store node: {}. Error: {:?}", ip_and_port[1], e))?;
|
|
|
|
nodes.insert(public, (ip_and_port[0].into(), port));
|
|
}
|
|
|
|
Ok(nodes)
|
|
}
|
|
|
|
fn stratum_interface(&self) -> String {
|
|
self.interface(&self.args.arg_stratum_interface)
|
|
}
|
|
|
|
fn rpc_enabled(&self) -> bool {
|
|
!self.args.flag_jsonrpc_off && !self.args.flag_no_jsonrpc
|
|
}
|
|
|
|
fn ws_enabled(&self) -> bool {
|
|
!self.args.flag_no_ws
|
|
}
|
|
|
|
fn dapps_enabled(&self) -> bool {
|
|
!self.args.flag_dapps_off && !self.args.flag_no_dapps && self.rpc_enabled() && cfg!(feature = "dapps")
|
|
}
|
|
|
|
fn secretstore_enabled(&self) -> bool {
|
|
!self.args.flag_no_secretstore && cfg!(feature = "secretstore")
|
|
}
|
|
|
|
fn secretstore_http_enabled(&self) -> bool {
|
|
!self.args.flag_no_secretstore_http && cfg!(feature = "secretstore")
|
|
}
|
|
|
|
fn secretstore_acl_check_enabled(&self) -> bool {
|
|
!self.args.flag_no_secretstore_acl_check
|
|
}
|
|
|
|
fn ui_enabled(&self) -> bool {
|
|
if self.args.flag_force_ui {
|
|
return true;
|
|
}
|
|
|
|
let ui_disabled = self.args.arg_unlock.is_some() ||
|
|
self.args.flag_geth ||
|
|
self.args.flag_no_ui;
|
|
|
|
!ui_disabled && cfg!(feature = "ui-enabled")
|
|
}
|
|
|
|
fn verifier_settings(&self) -> VerifierSettings {
|
|
let mut settings = VerifierSettings::default();
|
|
settings.scale_verifiers = self.args.flag_scale_verifiers;
|
|
if let Some(num_verifiers) = self.args.arg_num_verifiers {
|
|
settings.num_verifiers = num_verifiers;
|
|
}
|
|
|
|
settings
|
|
}
|
|
|
|
fn whisper_config(&self) -> ::whisper::Config {
|
|
::whisper::Config {
|
|
enabled: self.args.flag_whisper,
|
|
target_message_pool_size: self.args.arg_whisper_pool_size * 1024 * 1024,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::io::Write;
|
|
use std::fs::{File, create_dir};
|
|
use std::str::FromStr;
|
|
|
|
use devtools::{RandomTempPath};
|
|
use ethcore::client::{VMType, BlockId};
|
|
use ethcore::miner::{MinerOptions, PrioritizationStrategy};
|
|
use parity_rpc::NetworkSettings;
|
|
use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack};
|
|
|
|
use account::{AccountCmd, NewAccount, ImportAccounts, ListAccounts};
|
|
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, DataFormat, ExportState};
|
|
use cli::Args;
|
|
use dir::{Directories, default_hypervisor_path};
|
|
use helpers::{default_network_config};
|
|
use params::SpecType;
|
|
use presale::ImportWallet;
|
|
use rpc::{WsConfiguration, UiConfiguration};
|
|
use run::RunCmd;
|
|
|
|
use network::{AllowIP, IpFilter};
|
|
|
|
extern crate ipnetwork;
|
|
use self::ipnetwork::IpNetwork;
|
|
|
|
use super::*;
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
struct TestPasswordReader(&'static str);
|
|
|
|
fn parse(args: &[&str]) -> Configuration {
|
|
Configuration {
|
|
args: Args::parse_without_config(args).unwrap(),
|
|
spec_name_override: None,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_command_version() {
|
|
let args = vec!["parity", "--version"];
|
|
let conf = parse(&args);
|
|
assert_eq!(conf.into_command().unwrap().cmd, Cmd::Version);
|
|
}
|
|
|
|
#[test]
|
|
fn test_command_account_new() {
|
|
let args = vec!["parity", "account", "new"];
|
|
let conf = parse(&args);
|
|
assert_eq!(conf.into_command().unwrap().cmd, Cmd::Account(AccountCmd::New(NewAccount {
|
|
iterations: 10240,
|
|
path: Directories::default().keys,
|
|
password_file: None,
|
|
spec: SpecType::default(),
|
|
})));
|
|
}
|
|
|
|
#[test]
|
|
fn test_command_account_list() {
|
|
let args = vec!["parity", "account", "list"];
|
|
let conf = parse(&args);
|
|
assert_eq!(conf.into_command().unwrap().cmd, Cmd::Account(
|
|
AccountCmd::List(ListAccounts {
|
|
path: Directories::default().keys,
|
|
spec: SpecType::default(),
|
|
})
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn test_command_account_import() {
|
|
let args = vec!["parity", "account", "import", "my_dir", "another_dir"];
|
|
let conf = parse(&args);
|
|
assert_eq!(conf.into_command().unwrap().cmd, Cmd::Account(AccountCmd::Import(ImportAccounts {
|
|
from: vec!["my_dir".into(), "another_dir".into()],
|
|
to: Directories::default().keys,
|
|
spec: SpecType::default(),
|
|
})));
|
|
}
|
|
|
|
#[test]
|
|
fn test_command_wallet_import() {
|
|
let args = vec!["parity", "wallet", "import", "my_wallet.json", "--password", "pwd"];
|
|
let conf = parse(&args);
|
|
assert_eq!(conf.into_command().unwrap().cmd, Cmd::ImportPresaleWallet(ImportWallet {
|
|
iterations: 10240,
|
|
path: Directories::default().keys,
|
|
wallet_path: "my_wallet.json".into(),
|
|
password_file: Some("pwd".into()),
|
|
spec: SpecType::default(),
|
|
}));
|
|
}
|
|
|
|
#[test]
|
|
fn test_command_blockchain_import() {
|
|
let args = vec!["parity", "import", "blockchain.json"];
|
|
let conf = parse(&args);
|
|
assert_eq!(conf.into_command().unwrap().cmd, Cmd::Blockchain(BlockchainCmd::Import(ImportBlockchain {
|
|
spec: Default::default(),
|
|
cache_config: Default::default(),
|
|
dirs: Default::default(),
|
|
file_path: Some("blockchain.json".into()),
|
|
format: Default::default(),
|
|
pruning: Default::default(),
|
|
pruning_history: 64,
|
|
pruning_memory: 32,
|
|
compaction: Default::default(),
|
|
wal: true,
|
|
tracing: Default::default(),
|
|
fat_db: Default::default(),
|
|
vm_type: VMType::Interpreter,
|
|
check_seal: true,
|
|
with_color: !cfg!(windows),
|
|
verifier_settings: Default::default(),
|
|
light: false,
|
|
})));
|
|
}
|
|
|
|
#[test]
|
|
fn test_command_blockchain_export() {
|
|
let args = vec!["parity", "export", "blocks", "blockchain.json"];
|
|
let conf = parse(&args);
|
|
assert_eq!(conf.into_command().unwrap().cmd, Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain {
|
|
spec: Default::default(),
|
|
cache_config: Default::default(),
|
|
dirs: Default::default(),
|
|
file_path: Some("blockchain.json".into()),
|
|
pruning: Default::default(),
|
|
pruning_history: 64,
|
|
pruning_memory: 32,
|
|
format: Default::default(),
|
|
compaction: Default::default(),
|
|
wal: true,
|
|
tracing: Default::default(),
|
|
fat_db: Default::default(),
|
|
from_block: BlockId::Number(1),
|
|
to_block: BlockId::Latest,
|
|
check_seal: true,
|
|
})));
|
|
}
|
|
|
|
#[test]
|
|
fn test_command_state_export() {
|
|
let args = vec!["parity", "export", "state", "state.json"];
|
|
let conf = parse(&args);
|
|
assert_eq!(conf.into_command().unwrap().cmd, Cmd::Blockchain(BlockchainCmd::ExportState(ExportState {
|
|
spec: Default::default(),
|
|
cache_config: Default::default(),
|
|
dirs: Default::default(),
|
|
file_path: Some("state.json".into()),
|
|
pruning: Default::default(),
|
|
pruning_history: 64,
|
|
pruning_memory: 32,
|
|
format: Default::default(),
|
|
compaction: Default::default(),
|
|
wal: true,
|
|
tracing: Default::default(),
|
|
fat_db: Default::default(),
|
|
at: BlockId::Latest,
|
|
storage: true,
|
|
code: true,
|
|
min_balance: None,
|
|
max_balance: None,
|
|
})));
|
|
}
|
|
|
|
#[test]
|
|
fn test_command_blockchain_export_with_custom_format() {
|
|
let args = vec!["parity", "export", "blocks", "--format", "hex", "blockchain.json"];
|
|
let conf = parse(&args);
|
|
assert_eq!(conf.into_command().unwrap().cmd, Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain {
|
|
spec: Default::default(),
|
|
cache_config: Default::default(),
|
|
dirs: Default::default(),
|
|
file_path: Some("blockchain.json".into()),
|
|
pruning: Default::default(),
|
|
pruning_history: 64,
|
|
pruning_memory: 32,
|
|
format: Some(DataFormat::Hex),
|
|
compaction: Default::default(),
|
|
wal: true,
|
|
tracing: Default::default(),
|
|
fat_db: Default::default(),
|
|
from_block: BlockId::Number(1),
|
|
to_block: BlockId::Latest,
|
|
check_seal: true,
|
|
})));
|
|
}
|
|
|
|
#[test]
|
|
fn test_command_signer_new_token() {
|
|
let args = vec!["parity", "signer", "new-token"];
|
|
let conf = parse(&args);
|
|
let expected = Directories::default().signer;
|
|
assert_eq!(conf.into_command().unwrap().cmd, Cmd::SignerToken(WsConfiguration {
|
|
enabled: true,
|
|
interface: "127.0.0.1".into(),
|
|
port: 8546,
|
|
apis: ApiSet::UnsafeContext,
|
|
origins: Some(vec!["chrome-extension://*".into(), "moz-extension://*".into()]),
|
|
hosts: Some(vec![]),
|
|
signer_path: expected.into(),
|
|
ui_address: Some("127.0.0.1:8180".into()),
|
|
support_token_api: true
|
|
}, UiConfiguration {
|
|
enabled: true,
|
|
interface: "127.0.0.1".into(),
|
|
port: 8180,
|
|
hosts: Some(vec![]),
|
|
}, LogConfig {
|
|
color: true,
|
|
mode: None,
|
|
file: None,
|
|
} ));
|
|
}
|
|
|
|
#[test]
|
|
fn test_run_cmd() {
|
|
let args = vec!["parity"];
|
|
let conf = parse(&args);
|
|
let mut expected = RunCmd {
|
|
cache_config: Default::default(),
|
|
dirs: Default::default(),
|
|
spec: Default::default(),
|
|
pruning: Default::default(),
|
|
pruning_history: 64,
|
|
pruning_memory: 32,
|
|
daemon: None,
|
|
logger_config: Default::default(),
|
|
miner_options: Default::default(),
|
|
gas_price_percentile: 50,
|
|
ntp_servers: vec![
|
|
"0.parity.pool.ntp.org:123".into(),
|
|
"1.parity.pool.ntp.org:123".into(),
|
|
"2.parity.pool.ntp.org:123".into(),
|
|
"3.parity.pool.ntp.org:123".into(),
|
|
],
|
|
ws_conf: Default::default(),
|
|
http_conf: Default::default(),
|
|
ipc_conf: Default::default(),
|
|
net_conf: default_network_config(),
|
|
network_id: None,
|
|
public_node: false,
|
|
warp_sync: true,
|
|
acc_conf: Default::default(),
|
|
gas_pricer_conf: Default::default(),
|
|
miner_extras: Default::default(),
|
|
update_policy: UpdatePolicy { enable_downloading: true, require_consensus: true, filter: UpdateFilter::Critical, track: ReleaseTrack::Unknown, path: default_hypervisor_path() },
|
|
mode: Default::default(),
|
|
tracing: Default::default(),
|
|
compaction: Default::default(),
|
|
wal: true,
|
|
vm_type: Default::default(),
|
|
geth_compatibility: false,
|
|
net_settings: Default::default(),
|
|
dapps_conf: Default::default(),
|
|
ipfs_conf: Default::default(),
|
|
ui_conf: Default::default(),
|
|
secretstore_conf: Default::default(),
|
|
ui: false,
|
|
dapp: None,
|
|
name: "".into(),
|
|
custom_bootnodes: false,
|
|
fat_db: Default::default(),
|
|
no_periodic_snapshot: false,
|
|
stratum: None,
|
|
check_seal: true,
|
|
download_old_blocks: true,
|
|
verifier_settings: Default::default(),
|
|
serve_light: true,
|
|
light: false,
|
|
no_persistent_txqueue: false,
|
|
whisper: Default::default(),
|
|
};
|
|
expected.secretstore_conf.enabled = cfg!(feature = "secretstore");
|
|
expected.secretstore_conf.http_enabled = cfg!(feature = "secretstore");
|
|
assert_eq!(conf.into_command().unwrap().cmd, Cmd::Run(expected));
|
|
}
|
|
|
|
#[test]
|
|
fn should_parse_mining_options() {
|
|
// given
|
|
let mut mining_options = MinerOptions::default();
|
|
|
|
// when
|
|
let conf0 = parse(&["parity"]);
|
|
let conf1 = parse(&["parity", "--tx-queue-strategy", "gas_factor"]);
|
|
let conf2 = parse(&["parity", "--tx-queue-strategy", "gas_price"]);
|
|
let conf3 = parse(&["parity", "--tx-queue-strategy", "gas"]);
|
|
|
|
// then
|
|
assert_eq!(conf0.miner_options().unwrap(), mining_options);
|
|
mining_options.tx_queue_strategy = PrioritizationStrategy::GasFactorAndGasPrice;
|
|
assert_eq!(conf1.miner_options().unwrap(), mining_options);
|
|
mining_options.tx_queue_strategy = PrioritizationStrategy::GasPriceOnly;
|
|
assert_eq!(conf2.miner_options().unwrap(), mining_options);
|
|
mining_options.tx_queue_strategy = PrioritizationStrategy::GasAndGasPrice;
|
|
assert_eq!(conf3.miner_options().unwrap(), mining_options);
|
|
}
|
|
|
|
#[test]
|
|
fn should_fail_on_force_reseal_and_reseal_min_period() {
|
|
let conf = parse(&["parity", "--chain", "dev", "--force-sealing", "--reseal-min-period", "0"]);
|
|
|
|
assert!(conf.miner_options().is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn should_parse_updater_options() {
|
|
// when
|
|
let conf0 = parse(&["parity", "--release-track=testing"]);
|
|
let conf1 = parse(&["parity", "--auto-update", "all", "--no-consensus"]);
|
|
let conf2 = parse(&["parity", "--no-download", "--auto-update=all", "--release-track=beta"]);
|
|
let conf3 = parse(&["parity", "--auto-update=xxx"]);
|
|
|
|
// then
|
|
assert_eq!(conf0.update_policy().unwrap(), UpdatePolicy{enable_downloading: true, require_consensus: true, filter: UpdateFilter::Critical, track: ReleaseTrack::Testing, path: default_hypervisor_path()});
|
|
assert_eq!(conf1.update_policy().unwrap(), UpdatePolicy{enable_downloading: true, require_consensus: false, filter: UpdateFilter::All, track: ReleaseTrack::Unknown, path: default_hypervisor_path()});
|
|
assert_eq!(conf2.update_policy().unwrap(), UpdatePolicy{enable_downloading: false, require_consensus: true, filter: UpdateFilter::All, track: ReleaseTrack::Beta, path: default_hypervisor_path()});
|
|
assert!(conf3.update_policy().is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn should_parse_network_settings() {
|
|
// given
|
|
|
|
// when
|
|
let conf = parse(&["parity", "--testnet", "--identity", "testname"]);
|
|
|
|
// then
|
|
assert_eq!(conf.network_settings(), Ok(NetworkSettings {
|
|
name: "testname".to_owned(),
|
|
chain: "kovan".to_owned(),
|
|
network_port: 30303,
|
|
rpc_enabled: true,
|
|
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().unwrap();
|
|
assert_eq!(net.rpc_enabled, true);
|
|
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());
|
|
}
|
|
|
|
// when
|
|
let conf1 = parse(&["parity", "-j",
|
|
"--jsonrpc-port", "8000",
|
|
"--jsonrpc-interface", "all",
|
|
"--jsonrpc-cors", "*",
|
|
"--jsonrpc-apis", "web3,eth"
|
|
]);
|
|
let conf2 = parse(&["parity", "--rpc",
|
|
"--rpcport", "8000",
|
|
"--rpcaddr", "all",
|
|
"--rpccorsdomain", "*",
|
|
"--rpcapi", "web3,eth"
|
|
]);
|
|
|
|
// then
|
|
assert(conf1);
|
|
assert(conf2);
|
|
}
|
|
|
|
#[test]
|
|
fn should_parse_rpc_hosts() {
|
|
// given
|
|
|
|
// when
|
|
let conf0 = parse(&["parity"]);
|
|
let conf1 = parse(&["parity", "--jsonrpc-hosts", "none"]);
|
|
let conf2 = parse(&["parity", "--jsonrpc-hosts", "all"]);
|
|
let conf3 = parse(&["parity", "--jsonrpc-hosts", "parity.io,something.io"]);
|
|
|
|
// then
|
|
assert_eq!(conf0.rpc_hosts(), Some(Vec::new()));
|
|
assert_eq!(conf1.rpc_hosts(), Some(Vec::new()));
|
|
assert_eq!(conf2.rpc_hosts(), None);
|
|
assert_eq!(conf3.rpc_hosts(), Some(vec!["parity.io".into(), "something.io".into()]));
|
|
}
|
|
|
|
#[test]
|
|
fn should_parse_ipfs_hosts() {
|
|
// given
|
|
|
|
// when
|
|
let conf0 = parse(&["parity"]);
|
|
let conf1 = parse(&["parity", "--ipfs-api-hosts", "none"]);
|
|
let conf2 = parse(&["parity", "--ipfs-api-hosts", "all"]);
|
|
let conf3 = parse(&["parity", "--ipfs-api-hosts", "parity.io,something.io"]);
|
|
|
|
// then
|
|
assert_eq!(conf0.ipfs_hosts(), Some(Vec::new()));
|
|
assert_eq!(conf1.ipfs_hosts(), Some(Vec::new()));
|
|
assert_eq!(conf2.ipfs_hosts(), None);
|
|
assert_eq!(conf3.ipfs_hosts(), Some(vec!["parity.io".into(), "something.io".into()]));
|
|
}
|
|
|
|
#[test]
|
|
fn should_parse_ipfs_cors() {
|
|
// given
|
|
|
|
// when
|
|
let conf0 = parse(&["parity"]);
|
|
let conf1 = parse(&["parity", "--ipfs-api-cors", "*"]);
|
|
let conf2 = parse(&["parity", "--ipfs-api-cors", "http://parity.io,http://something.io"]);
|
|
|
|
// then
|
|
assert_eq!(conf0.ipfs_cors(), Some(vec![]));
|
|
assert_eq!(conf1.ipfs_cors(), Some(vec!["*".into()]));
|
|
assert_eq!(conf2.ipfs_cors(), Some(vec!["http://parity.io".into(),"http://something.io".into()]));
|
|
}
|
|
|
|
#[test]
|
|
fn should_disable_signer_in_geth_compat() {
|
|
// given
|
|
|
|
// when
|
|
let conf0 = parse(&["parity", "--geth"]);
|
|
let conf1 = parse(&["parity", "--geth", "--force-ui"]);
|
|
|
|
// then
|
|
assert_eq!(conf0.ui_enabled(), false);
|
|
assert_eq!(conf1.ui_enabled(), true);
|
|
}
|
|
|
|
#[test]
|
|
fn should_disable_signer_when_account_is_unlocked() {
|
|
// given
|
|
|
|
// when
|
|
let conf0 = parse(&["parity", "--unlock", "0x0"]);
|
|
|
|
// then
|
|
assert_eq!(conf0.ui_enabled(), false);
|
|
}
|
|
|
|
#[test]
|
|
fn should_parse_ui_configuration() {
|
|
// given
|
|
|
|
// when
|
|
let conf0 = parse(&["parity", "--ui-path=signer"]);
|
|
let conf1 = parse(&["parity", "--ui-path=signer", "--ui-no-validation"]);
|
|
let conf2 = parse(&["parity", "--ui-path=signer", "--ui-port", "3123"]);
|
|
let conf3 = parse(&["parity", "--ui-path=signer", "--ui-interface", "test"]);
|
|
|
|
// then
|
|
assert_eq!(conf0.directories().signer, "signer".to_owned());
|
|
assert_eq!(conf0.ui_config(), UiConfiguration {
|
|
enabled: true,
|
|
interface: "127.0.0.1".into(),
|
|
port: 8180,
|
|
hosts: Some(vec![]),
|
|
});
|
|
assert!(conf0.ws_config().unwrap().hosts.is_some());
|
|
assert_eq!(conf1.directories().signer, "signer".to_owned());
|
|
assert_eq!(conf1.ui_config(), UiConfiguration {
|
|
enabled: true,
|
|
interface: "127.0.0.1".into(),
|
|
port: 8180,
|
|
hosts: Some(vec![]),
|
|
});
|
|
assert_eq!(conf1.dapps_config().extra_embed_on, vec![("localhost".to_owned(), 3000)]);
|
|
assert_eq!(conf1.ws_config().unwrap().origins, None);
|
|
assert_eq!(conf2.directories().signer, "signer".to_owned());
|
|
assert_eq!(conf2.ui_config(), UiConfiguration {
|
|
enabled: true,
|
|
interface: "127.0.0.1".into(),
|
|
port: 3123,
|
|
hosts: Some(vec![]),
|
|
});
|
|
assert!(conf2.ws_config().unwrap().hosts.is_some());
|
|
assert_eq!(conf3.directories().signer, "signer".to_owned());
|
|
assert_eq!(conf3.ui_config(), UiConfiguration {
|
|
enabled: true,
|
|
interface: "test".into(),
|
|
port: 8180,
|
|
hosts: Some(vec![]),
|
|
});
|
|
assert!(conf3.ws_config().unwrap().hosts.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn should_parse_dapp_opening() {
|
|
// given
|
|
let temp = RandomTempPath::new();
|
|
let name = temp.file_name().unwrap().to_str().unwrap();
|
|
create_dir(temp.as_str().to_owned()).unwrap();
|
|
|
|
// when
|
|
let conf0 = parse(&["parity", "dapp", temp.to_str().unwrap()]);
|
|
|
|
// then
|
|
assert_eq!(conf0.dapp_to_open(), Ok(Some(name.into())));
|
|
let extra_dapps = conf0.dapps_config().extra_dapps;
|
|
assert_eq!(extra_dapps, vec![temp.to_owned()]);
|
|
}
|
|
|
|
#[test]
|
|
fn should_not_bail_on_empty_line_in_reserved_peers() {
|
|
let temp = RandomTempPath::new();
|
|
create_dir(temp.as_str().to_owned()).unwrap();
|
|
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, None).unwrap();
|
|
assert!(conf.init_reserved_nodes().is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn should_ignore_comments_in_reserved_peers() {
|
|
let temp = RandomTempPath::new();
|
|
create_dir(temp.as_str().to_owned()).unwrap();
|
|
let filename = temp.as_str().to_owned() + "/peers_comments";
|
|
File::create(filename.clone()).unwrap().write_all(b"# Sample comment\nenode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@172.0.0.1:30303\n").unwrap();
|
|
let args = vec!["parity", "--reserved-peers", &filename];
|
|
let conf = Configuration::parse(&args, None).unwrap();
|
|
let reserved_nodes = conf.init_reserved_nodes();
|
|
assert!(reserved_nodes.is_ok());
|
|
assert_eq!(reserved_nodes.unwrap().len(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_dev_preset() {
|
|
let args = vec!["parity", "--config", "dev"];
|
|
let conf = Configuration::parse(&args, None).unwrap();
|
|
match conf.into_command().unwrap().cmd {
|
|
Cmd::Run(c) => {
|
|
assert_eq!(c.net_settings.chain, "dev");
|
|
assert_eq!(c.gas_pricer_conf, GasPricerConfig::Fixed(0.into()));
|
|
assert_eq!(c.miner_options.reseal_min_period, Duration::from_millis(0));
|
|
},
|
|
_ => panic!("Should be Cmd::Run"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_mining_preset() {
|
|
let args = vec!["parity", "--config", "mining"];
|
|
let conf = Configuration::parse(&args, None).unwrap();
|
|
match conf.into_command().unwrap().cmd {
|
|
Cmd::Run(c) => {
|
|
assert_eq!(c.net_conf.min_peers, 50);
|
|
assert_eq!(c.net_conf.max_peers, 100);
|
|
assert_eq!(c.ipc_conf.enabled, false);
|
|
assert_eq!(c.dapps_conf.enabled, false);
|
|
assert_eq!(c.miner_options.force_sealing, true);
|
|
assert_eq!(c.miner_options.reseal_on_external_tx, true);
|
|
assert_eq!(c.miner_options.reseal_on_own_tx, true);
|
|
assert_eq!(c.miner_options.reseal_min_period, Duration::from_millis(4000));
|
|
assert_eq!(c.miner_options.tx_queue_size, 2048);
|
|
assert_eq!(c.cache_config, CacheConfig::new_with_total_cache_size(256));
|
|
assert_eq!(c.logger_config.mode.unwrap(), "miner=trace,own_tx=trace");
|
|
},
|
|
_ => panic!("Should be Cmd::Run"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_non_standard_ports_preset() {
|
|
let args = vec!["parity", "--config", "non-standard-ports"];
|
|
let conf = Configuration::parse(&args, None).unwrap();
|
|
match conf.into_command().unwrap().cmd {
|
|
Cmd::Run(c) => {
|
|
assert_eq!(c.net_settings.network_port, 30305);
|
|
assert_eq!(c.net_settings.rpc_port, 8645);
|
|
},
|
|
_ => panic!("Should be Cmd::Run"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_insecure_preset() {
|
|
let args = vec!["parity", "--config", "insecure"];
|
|
let conf = Configuration::parse(&args, None).unwrap();
|
|
match conf.into_command().unwrap().cmd {
|
|
Cmd::Run(c) => {
|
|
assert_eq!(c.update_policy.require_consensus, false);
|
|
assert_eq!(c.net_settings.rpc_interface, "0.0.0.0");
|
|
match c.http_conf.apis {
|
|
ApiSet::List(set) => assert_eq!(set, ApiSet::All.list_apis()),
|
|
_ => panic!("Incorrect rpc apis"),
|
|
}
|
|
// "web3,eth,net,personal,parity,parity_set,traces,rpc,parity_accounts");
|
|
assert_eq!(c.http_conf.hosts, None);
|
|
assert_eq!(c.ipfs_conf.hosts, None);
|
|
},
|
|
_ => panic!("Should be Cmd::Run"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_dev_insecure_preset() {
|
|
let args = vec!["parity", "--config", "dev-insecure"];
|
|
let conf = Configuration::parse(&args, None).unwrap();
|
|
match conf.into_command().unwrap().cmd {
|
|
Cmd::Run(c) => {
|
|
assert_eq!(c.net_settings.chain, "dev");
|
|
assert_eq!(c.gas_pricer_conf, GasPricerConfig::Fixed(0.into()));
|
|
assert_eq!(c.miner_options.reseal_min_period, Duration::from_millis(0));
|
|
assert_eq!(c.update_policy.require_consensus, false);
|
|
assert_eq!(c.net_settings.rpc_interface, "0.0.0.0");
|
|
match c.http_conf.apis {
|
|
ApiSet::List(set) => assert_eq!(set, ApiSet::All.list_apis()),
|
|
_ => panic!("Incorrect rpc apis"),
|
|
}
|
|
// "web3,eth,net,personal,parity,parity_set,traces,rpc,parity_accounts");
|
|
assert_eq!(c.http_conf.hosts, None);
|
|
assert_eq!(c.ipfs_conf.hosts, None);
|
|
},
|
|
_ => panic!("Should be Cmd::Run"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_override_preset() {
|
|
let args = vec!["parity", "--config", "mining", "--min-peers=99"];
|
|
let conf = Configuration::parse(&args, None).unwrap();
|
|
match conf.into_command().unwrap().cmd {
|
|
Cmd::Run(c) => {
|
|
assert_eq!(c.net_conf.min_peers, 99);
|
|
},
|
|
_ => 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.ui_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.ui_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.ws_config().unwrap().origins, None);
|
|
assert_eq!(&conf0.ui_config().interface, "0.0.0.0");
|
|
assert_eq!(conf0.ui_config().hosts, None);
|
|
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);
|
|
}
|
|
|
|
#[test]
|
|
fn allow_ips() {
|
|
let all = parse(&["parity", "--allow-ips", "all"]);
|
|
let private = parse(&["parity", "--allow-ips", "private"]);
|
|
let block_custom = parse(&["parity", "--allow-ips", "-10.0.0.0/8"]);
|
|
let combo = parse(&["parity", "--allow-ips", "public 10.0.0.0/8 -1.0.0.0/8"]);
|
|
let ipv6_custom_public = parse(&["parity", "--allow-ips", "public fc00::/7"]);
|
|
let ipv6_custom_private = parse(&["parity", "--allow-ips", "private -fc00::/7"]);
|
|
|
|
assert_eq!(all.ip_filter().unwrap(), IpFilter {
|
|
predefined: AllowIP::All,
|
|
custom_allow: vec![],
|
|
custom_block: vec![],
|
|
});
|
|
|
|
assert_eq!(private.ip_filter().unwrap(), IpFilter {
|
|
predefined: AllowIP::Private,
|
|
custom_allow: vec![],
|
|
custom_block: vec![],
|
|
});
|
|
|
|
assert_eq!(block_custom.ip_filter().unwrap(), IpFilter {
|
|
predefined: AllowIP::All,
|
|
custom_allow: vec![],
|
|
custom_block: vec![IpNetwork::from_str("10.0.0.0/8").unwrap()],
|
|
});
|
|
|
|
assert_eq!(combo.ip_filter().unwrap(), IpFilter {
|
|
predefined: AllowIP::Public,
|
|
custom_allow: vec![IpNetwork::from_str("10.0.0.0/8").unwrap()],
|
|
custom_block: vec![IpNetwork::from_str("1.0.0.0/8").unwrap()],
|
|
});
|
|
|
|
assert_eq!(ipv6_custom_public.ip_filter().unwrap(), IpFilter {
|
|
predefined: AllowIP::Public,
|
|
custom_allow: vec![IpNetwork::from_str("fc00::/7").unwrap()],
|
|
custom_block: vec![],
|
|
});
|
|
|
|
assert_eq!(ipv6_custom_private.ip_filter().unwrap(), IpFilter {
|
|
predefined: AllowIP::Private,
|
|
custom_allow: vec![],
|
|
custom_block: vec![IpNetwork::from_str("fc00::/7").unwrap()],
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn should_use_correct_cache_path_if_base_is_set() {
|
|
let std = parse(&["parity"]);
|
|
let base = parse(&["parity", "--base-path", "/test"]);
|
|
|
|
let base_path = ::dir::default_data_path();
|
|
let local_path = ::dir::default_local_path();
|
|
assert_eq!(std.directories().cache, ::helpers::replace_home_and_local(&base_path, &local_path, ::dir::CACHE_PATH));
|
|
assert_eq!(base.directories().cache, "/test/cache");
|
|
}
|
|
}
|