// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see .
use ansi_term::Colour;
use bytes::Bytes;
use cli::{Args, ArgsError};
use ethcore::{
client::VMType,
miner::{stratum, MinerOptions},
snapshot::SnapshotConfiguration,
verification::queue::VerifierSettings,
};
use ethereum_types::{Address, H256, U256};
use ethkey::{Public, Secret};
use hash::keccak;
use miner::pool;
use num_cpus;
use parity_version::{version, version_data};
use std::{
cmp,
collections::{BTreeMap, HashSet},
io::Read,
iter::FromIterator,
net::{SocketAddr, ToSocketAddrs},
num::NonZeroU32,
path::PathBuf,
time::Duration,
};
use sync::{self, validate_node_url, NetworkConfiguration};
use account::{AccountCmd, ImportAccounts, ListAccounts, NewAccount};
use blockchain::{
BlockchainCmd, ExportBlockchain, ExportState, ImportBlockchain, KillBlockchain, ResetBlockchain,
};
use cache::CacheConfig;
use dir::{
self, default_data_path, default_hypervisor_path, default_local_path,
helpers::{replace_home, replace_home_and_local},
Directories,
};
use ethcore_logger::Config as LogConfig;
use ethcore_private_tx::{EncryptorConfig, ProviderConfig};
use export_hardcoded_sync::ExportHsyncCmd;
use helpers::{
parity_ipc_path, to_address, to_addresses, to_block_id, to_bootnodes, to_duration, to_mode,
to_pending_set, to_price, to_queue_penalization, to_queue_strategy, to_u256,
};
use ipfs::Configuration as IpfsConfiguration;
use network::IpFilter;
use params::{AccountsConfig, GasPricerConfig, MinerExtras, ResealPolicy, SpecType};
use parity_rpc::NetworkSettings;
use presale::ImportWallet;
use rpc::{HttpConfiguration, IpcConfiguration, WsConfiguration};
use run::RunCmd;
use secretstore::{
Configuration as SecretStoreConfiguration, ContractAddress as SecretStoreContractAddress,
NodeSecretKey,
};
use snapshot::{self, SnapshotCommand};
use types::data_format::DataFormat;
use updater::{ReleaseTrack, UpdateFilter, UpdatePolicy};
const DEFAULT_MAX_PEERS: u16 = 50;
const DEFAULT_MIN_PEERS: u16 = 25;
pub const ETHERSCAN_ETH_PRICE_ENDPOINT: &str =
"https://api.etherscan.io/api?module=stats&action=ethprice";
#[derive(Debug, PartialEq)]
pub enum Cmd {
Run(RunCmd),
Version,
Account(AccountCmd),
ImportPresaleWallet(ImportWallet),
Blockchain(BlockchainCmd),
SignerToken(WsConfiguration, LogConfig),
SignerSign {
id: Option,
pwfile: Option,
port: u16,
authfile: PathBuf,
},
SignerList {
port: u16,
authfile: PathBuf,
},
SignerReject {
id: Option,
port: u16,
authfile: PathBuf,
},
Snapshot(SnapshotCommand),
Hash(Option),
ExportHardcodedSync(ExportHsyncCmd),
}
pub struct Execute {
pub logger: LogConfig,
pub cmd: Cmd,
}
/// Configuration for the Parity client.
#[derive(Debug, PartialEq)]
pub struct Configuration {
/// Arguments to be interpreted.
pub args: Args,
}
impl Configuration {
/// Parses a configuration from a list of command line arguments.
///
/// # Example
///
/// ```
/// let _cfg = parity_ethereum::Configuration::parse_cli(&["--light", "--chain", "kovan"]).unwrap();
/// ```
pub fn parse_cli>(command: &[S]) -> Result {
let config = Configuration {
args: Args::parse(command)?,
};
Ok(config)
}
pub(crate) fn into_command(self) -> Result {
let dirs = self.directories();
let pruning = self.args.arg_pruning.parse()?;
let pruning_history = self.args.arg_pruning_history;
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 snapshot_conf = self.snapshot_config()?;
let http_conf = self.http_config()?;
let ipc_conf = self.ipc_config()?;
let net_conf = self.net_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 warp_sync = !self.args.flag_no_warp;
let experimental_rpcs = self.args.flag_jsonrpc_experimental;
let ipfs_conf = self.ipfs_config();
let secretstore_conf = self.secretstore_config()?;
let format = self.format()?;
let keys_iterations = NonZeroU32::new(self.args.arg_keys_iterations)
.ok_or_else(|| "--keys-iterations must be non-zero")?;
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, logger_config.clone())
} else if self.args.cmd_signer_sign {
let pwfile = self
.accounts_config()?
.password_files
.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_reset {
Cmd::Blockchain(BlockchainCmd::Reset(ResetBlockchain {
dirs,
spec,
pruning,
pruning_history,
pruning_memory: self.args.arg_pruning_memory,
tracing,
fat_db,
compaction,
cache_config,
num: self.args.arg_db_reset_num,
}))
} 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: keys_iterations,
path: dirs.keys,
spec: spec,
password_file: self
.accounts_config()?
.password_files
.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.cmd_wallet {
let presale_cmd = ImportWallet {
iterations: keys_iterations,
path: dirs.keys,
spec: spec,
wallet_path: self.args.arg_wallet_import_path.clone().unwrap(),
password_file: self
.accounts_config()?
.password_files
.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,
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,
max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import,
};
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,
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,
max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import,
};
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,
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()),
max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import,
};
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(),
kind: snapshot::Kind::Take,
block_at: to_block_id(&self.args.arg_snapshot_at)?,
max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import,
snapshot_conf: snapshot_conf,
};
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(),
kind: snapshot::Kind::Restore,
block_at: to_block_id("latest")?, // unimportant.
max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import,
snapshot_conf: snapshot_conf,
};
Cmd::Snapshot(restore_cmd)
} else if self.args.cmd_export_hardcoded_sync {
let export_hs_cmd = ExportHsyncCmd {
cache_config: cache_config,
dirs: dirs,
spec: spec,
pruning: pruning,
compaction: compaction,
};
Cmd::ExportHardcodedSync(export_hs_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 (private_provider_conf, private_enc_conf, private_tx_enabled) =
self.private_provider_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,
poll_lifetime: self.args.arg_poll_lifetime,
ws_conf: ws_conf,
snapshot_conf: snapshot_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,
allow_missing_blocks: self.args.flag_jsonrpc_allow_missing_blocks,
mode: mode,
tracing: tracing,
fat_db: fat_db,
compaction: compaction,
vm_type: vm_type,
warp_sync: warp_sync,
warp_barrier: self.args.arg_warp_barrier,
experimental_rpcs,
net_settings: self.network_settings()?,
ipfs_conf: ipfs_conf,
secretstore_conf: secretstore_conf,
private_provider_conf: private_provider_conf,
private_encryptor_conf: private_enc_conf,
private_tx_enabled,
name: self.args.arg_identity,
custom_bootnodes: self.args.arg_bootnodes.is_some(),
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,
no_hardcoded_sync: self.args.flag_no_hardcoded_sync,
max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import,
on_demand_response_time_window: self.args.arg_on_demand_response_time_window,
on_demand_request_backoff_start: self.args.arg_on_demand_request_backoff_start,
on_demand_request_backoff_max: self.args.arg_on_demand_request_backoff_max,
on_demand_request_backoff_rounds_max: self
.args
.arg_on_demand_request_backoff_rounds_max,
on_demand_request_consecutive_failures: self
.args
.arg_on_demand_request_consecutive_failures,
};
Cmd::Run(run_cmd)
};
Ok(Execute {
logger: logger_config,
cmd: cmd,
})
}
fn vm_type(&self) -> Result {
Ok(VMType::Interpreter)
}
fn miner_extras(&self) -> Result {
let floor = to_u256(&self.args.arg_gas_floor_target)?;
let ceil = to_u256(&self.args.arg_gas_cap)?;
let extras = MinerExtras {
author: self.author()?,
extra_data: self.extra_data()?,
gas_range_target: (floor, ceil),
engine_signer: self.engine_signer()?,
work_notify: self.work_notify(),
local_accounts: HashSet::from_iter(
to_addresses(&self.args.arg_tx_queue_locals)?.into_iter(),
),
};
Ok(extras)
}
fn author(&self) -> Result {
to_address(self.args.arg_author.clone())
}
fn engine_signer(&self) -> Result {
to_address(self.args.arg_engine_signer.clone())
}
fn format(&self) -> Result