// 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 .
use std::time::Duration;
use std::io::{Read, Write, stderr};
use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use std::collections::BTreeMap;
use std::cmp::max;
use cli::{Args, ArgsError};
use util::{Hashable, H256, U256, Bytes, version_data, Address};
use util::journaldb::Algorithm;
use util::Colour;
use ethsync::{NetworkConfiguration, is_valid_node_url, AllowIP};
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::{SpecType, ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras, Pruning, Switch};
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;
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};
#[derive(Debug, PartialEq)]
pub enum Cmd {
Run(RunCmd),
Version,
Account(AccountCmd),
ImportPresaleWallet(ImportWallet),
Blockchain(BlockchainCmd),
SignerToken(WsConfiguration, UiConfiguration),
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),
}
pub struct Execute {
pub logger: LogConfig,
pub cmd: Cmd,
}
#[derive(Debug, PartialEq)]
pub struct Configuration {
pub args: Args,
pub spec_name_override: Option,
}
impl Configuration {
pub fn parse>(command: &[S], spec_name_override: Option) -> Result {
let args = Args::parse(command)?;
let config = Configuration {
args: args,
spec_name_override: spec_name_override,
};
Ok(config)
}
pub fn into_command(self) -> Result {
let dirs = self.directories();
let pruning = self.args.flag_pruning.parse()?;
let pruning_history = self.args.flag_pruning_history;
let vm_type = self.vm_type()?;
let spec = self.chain().parse()?;
let mode = match self.args.flag_mode.as_ref() {
"last" => None,
mode => Some(to_mode(&mode, self.args.flag_mode_timeout, self.args.flag_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.flag_tracing.parse()?;
let fat_db = self.args.flag_fat_db.parse()?;
let compaction = self.args.flag_db_compaction.parse()?;
let wal = !self.args.flag_fast_and_loose;
match self.args.flag_warp {
// Logging is not initialized yet, so we print directly to stderr
Some(true) if fat_db == Switch::On => writeln!(&mut stderr(), "Warning: Warp Sync is disabled because Fat DB is turned on").expect("Error writing to stderr"),
Some(true) if tracing == Switch::On => writeln!(&mut stderr(), "Warning: Warp Sync is disabled because tracing is turned on").expect("Error writing to stderr"),
Some(true) if pruning == Pruning::Specific(Algorithm::Archive) => writeln!(&mut stderr(), "Warning: Warp Sync is disabled because pruning mode is set to archive").expect("Error writing to stderr"),
_ => {},
};
let public_node = self.args.flag_public_node;
let warp_sync = !self.args.flag_no_warp && fat_db != Switch::On && tracing != Switch::On && pruning != Pruning::Specific(Algorithm::Archive);
let geth_compatibility = self.args.flag_geth;
let mut dapps_conf = self.dapps_config();
let ipfs_conf = self.ipfs_config();
let secretstore_conf = self.secretstore_config()?;
let format = self.format()?;
if self.args.flag_jsonrpc_server_threads.is_some() && dapps_conf.enabled {
dapps_conf.enabled = false;
writeln!(&mut stderr(), "Warning: Disabling Dapps server because fast RPC server was enabled.").expect("Error writing to stderr.")
}
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_new_token {
Cmd::SignerToken(ws_conf, ui_conf)
} else if self.args.cmd_sign {
let pwfile = self.args.flag_password.get(0).map(|pwfile| {
PathBuf::from(pwfile)
});
Cmd::SignerSign {
id: self.args.arg_id,
pwfile: pwfile,
port: ws_conf.port,
authfile: authfile,
}
} else if self.args.cmd_reject {
Cmd::SignerReject {
id: self.args.arg_id,
port: ws_conf.port,
authfile: authfile,
}
} else if self.args.cmd_list {
Cmd::SignerList {
port: ws_conf.port,
authfile: authfile,
}
} else {
unreachable!();
}
} else if self.args.cmd_tools && self.args.cmd_hash {
Cmd::Hash(self.args.arg_file)
} else if self.args.cmd_db && self.args.cmd_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_new {
let new_acc = NewAccount {
iterations: self.args.flag_keys_iterations,
path: dirs.keys,
spec: spec,
password_file: self.args.flag_password.first().cloned(),
};
AccountCmd::New(new_acc)
} else if self.args.cmd_list {
let list_acc = ListAccounts {
path: dirs.keys,
spec: spec,
};
AccountCmd::List(list_acc)
} else if self.args.cmd_import {
let import_acc = ImportAccounts {
from: self.args.arg_path.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.flag_keys_iterations,
path: dirs.keys,
spec: spec,
wallet_path: self.args.arg_path.first().unwrap().clone(),
password_file: self.args.flag_password.first().cloned(),
};
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_file.clone(),
format: format,
pruning: pruning,
pruning_history: pruning_history,
pruning_memory: self.args.flag_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_blocks {
let export_cmd = ExportBlockchain {
spec: spec,
cache_config: cache_config,
dirs: dirs,
file_path: self.args.arg_file.clone(),
format: format,
pruning: pruning,
pruning_history: pruning_history,
pruning_memory: self.args.flag_pruning_memory,
compaction: compaction,
wal: wal,
tracing: tracing,
fat_db: fat_db,
from_block: to_block_id(&self.args.flag_from)?,
to_block: to_block_id(&self.args.flag_to)?,
check_seal: !self.args.flag_no_seal_check,
};
Cmd::Blockchain(BlockchainCmd::Export(export_cmd))
} else if self.args.cmd_state {
let export_cmd = ExportState {
spec: spec,
cache_config: cache_config,
dirs: dirs,
file_path: self.args.arg_file.clone(),
format: format,
pruning: pruning,
pruning_history: pruning_history,
pruning_memory: self.args.flag_pruning_memory,
compaction: compaction,
wal: wal,
tracing: tracing,
fat_db: fat_db,
at: to_block_id(&self.args.flag_at)?,
storage: !self.args.flag_no_storage,
code: !self.args.flag_no_code,
min_balance: self.args.flag_min_balance.and_then(|s| to_u256(&s).ok()),
max_balance: self.args.flag_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.flag_pruning_memory,
tracing: tracing,
fat_db: fat_db,
compaction: compaction,
file_path: self.args.arg_file.clone(),
wal: wal,
kind: snapshot::Kind::Take,
block_at: to_block_id(&self.args.flag_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.flag_pruning_memory,
tracing: tracing,
fat_db: fat_db,
compaction: compaction,
file_path: self.args.arg_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_pid_file.clone())
} else {
None
};
let verifier_settings = self.verifier_settings();
// Special presets are present for the dev chain.
let (gas_pricer, miner_options) = match spec {
SpecType::Dev => (GasPricerConfig::Fixed(0.into()), self.miner_options(0)?),
_ => (self.gas_pricer_config()?, self.miner_options(self.args.flag_reseal_min_period)?),
};
let run_cmd = RunCmd {
cache_config: cache_config,
dirs: dirs,
spec: spec,
pruning: pruning,
pruning_history: pruning_history,
pruning_memory: self.args.flag_pruning_memory,
daemon: daemon,
logger_config: logger_config.clone(),
miner_options: miner_options,
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: gas_pricer,
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.flag_identity,
custom_bootnodes: self.args.flag_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,
};
Cmd::Run(run_cmd)
};
Ok(Execute {
logger: logger_config,
cmd: cmd,
})
}
fn vm_type(&self) -> Result {
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 {
let extras = MinerExtras {
author: self.author()?,
extra_data: self.extra_data()?,
gas_floor_target: to_u256(&self.args.flag_gas_floor_target)?,
gas_ceil_target: to_u256(&self.args.flag_gas_cap)?,
engine_signer: self.engine_signer()?,
};
Ok(extras)
}
fn author(&self) -> Result {
to_address(self.args.flag_etherbase.clone().or(self.args.flag_author.clone()))
}
fn engine_signer(&self) -> Result {
to_address(self.args.flag_engine_signer.clone())
}
fn format(&self) -> Result