Reformat the source code
This commit is contained in:
@@ -14,143 +14,159 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::num::NonZeroU32;
|
||||
use params::SpecType;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AccountCmd {
|
||||
New(NewAccount),
|
||||
List(ListAccounts),
|
||||
Import(ImportAccounts),
|
||||
ImportFromGeth(ImportFromGethAccounts)
|
||||
New(NewAccount),
|
||||
List(ListAccounts),
|
||||
Import(ImportAccounts),
|
||||
ImportFromGeth(ImportFromGethAccounts),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ListAccounts {
|
||||
pub path: String,
|
||||
pub spec: SpecType,
|
||||
pub path: String,
|
||||
pub spec: SpecType,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct NewAccount {
|
||||
pub iterations: NonZeroU32,
|
||||
pub path: String,
|
||||
pub spec: SpecType,
|
||||
pub password_file: Option<String>,
|
||||
pub iterations: NonZeroU32,
|
||||
pub path: String,
|
||||
pub spec: SpecType,
|
||||
pub password_file: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ImportAccounts {
|
||||
pub from: Vec<String>,
|
||||
pub to: String,
|
||||
pub spec: SpecType,
|
||||
pub from: Vec<String>,
|
||||
pub to: String,
|
||||
pub spec: SpecType,
|
||||
}
|
||||
|
||||
/// Parameters for geth accounts' import
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ImportFromGethAccounts {
|
||||
/// import mainnet (false) or testnet (true) accounts
|
||||
pub testnet: bool,
|
||||
/// directory to import accounts to
|
||||
pub to: String,
|
||||
pub spec: SpecType,
|
||||
/// import mainnet (false) or testnet (true) accounts
|
||||
pub testnet: bool,
|
||||
/// directory to import accounts to
|
||||
pub to: String,
|
||||
pub spec: SpecType,
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "accounts"))]
|
||||
pub fn execute(_cmd: AccountCmd) -> Result<String, String> {
|
||||
Err("Account management is deprecated. Please see #9997 for alternatives:\nhttps://github.com/paritytech/parity-ethereum/issues/9997".into())
|
||||
Err("Account management is deprecated. Please see #9997 for alternatives:\nhttps://github.com/paritytech/parity-ethereum/issues/9997".into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "accounts")]
|
||||
mod command {
|
||||
use super::*;
|
||||
use std::path::PathBuf;
|
||||
use accounts::{AccountProvider, AccountProviderSettings};
|
||||
use ethstore::{EthStore, SecretStore, SecretVaultRef, import_account, import_accounts, read_geth_accounts};
|
||||
use ethstore::accounts_dir::RootDiskDirectory;
|
||||
use helpers::{password_prompt, password_from_file};
|
||||
use super::*;
|
||||
use accounts::{AccountProvider, AccountProviderSettings};
|
||||
use ethstore::{
|
||||
accounts_dir::RootDiskDirectory, import_account, import_accounts, read_geth_accounts,
|
||||
EthStore, SecretStore, SecretVaultRef,
|
||||
};
|
||||
use helpers::{password_from_file, password_prompt};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn execute(cmd: AccountCmd) -> Result<String, String> {
|
||||
match cmd {
|
||||
AccountCmd::New(new_cmd) => new(new_cmd),
|
||||
AccountCmd::List(list_cmd) => list(list_cmd),
|
||||
AccountCmd::Import(import_cmd) => import(import_cmd),
|
||||
AccountCmd::ImportFromGeth(import_geth_cmd) => import_geth(import_geth_cmd)
|
||||
}
|
||||
}
|
||||
pub fn execute(cmd: AccountCmd) -> Result<String, String> {
|
||||
match cmd {
|
||||
AccountCmd::New(new_cmd) => new(new_cmd),
|
||||
AccountCmd::List(list_cmd) => list(list_cmd),
|
||||
AccountCmd::Import(import_cmd) => import(import_cmd),
|
||||
AccountCmd::ImportFromGeth(import_geth_cmd) => import_geth(import_geth_cmd),
|
||||
}
|
||||
}
|
||||
|
||||
fn keys_dir(path: String, spec: SpecType) -> Result<RootDiskDirectory, String> {
|
||||
let spec = spec.spec(&::std::env::temp_dir())?;
|
||||
let mut path = PathBuf::from(&path);
|
||||
path.push(spec.data_dir);
|
||||
RootDiskDirectory::create(path).map_err(|e| format!("Could not open keys directory: {}", e))
|
||||
}
|
||||
fn keys_dir(path: String, spec: SpecType) -> Result<RootDiskDirectory, String> {
|
||||
let spec = spec.spec(&::std::env::temp_dir())?;
|
||||
let mut path = PathBuf::from(&path);
|
||||
path.push(spec.data_dir);
|
||||
RootDiskDirectory::create(path).map_err(|e| format!("Could not open keys directory: {}", e))
|
||||
}
|
||||
|
||||
fn secret_store(dir: Box<RootDiskDirectory>, iterations: Option<NonZeroU32>) -> Result<EthStore, String> {
|
||||
match iterations {
|
||||
Some(i) => EthStore::open_with_iterations(dir, i),
|
||||
_ => EthStore::open(dir)
|
||||
}.map_err(|e| format!("Could not open keys store: {}", e))
|
||||
}
|
||||
fn secret_store(
|
||||
dir: Box<RootDiskDirectory>,
|
||||
iterations: Option<NonZeroU32>,
|
||||
) -> Result<EthStore, String> {
|
||||
match iterations {
|
||||
Some(i) => EthStore::open_with_iterations(dir, i),
|
||||
_ => EthStore::open(dir),
|
||||
}
|
||||
.map_err(|e| format!("Could not open keys store: {}", e))
|
||||
}
|
||||
|
||||
fn new(n: NewAccount) -> Result<String, String> {
|
||||
let password = match n.password_file {
|
||||
Some(file) => password_from_file(file)?,
|
||||
None => password_prompt()?,
|
||||
};
|
||||
fn new(n: NewAccount) -> Result<String, String> {
|
||||
let password = match n.password_file {
|
||||
Some(file) => password_from_file(file)?,
|
||||
None => password_prompt()?,
|
||||
};
|
||||
|
||||
let dir = Box::new(keys_dir(n.path, n.spec)?);
|
||||
let secret_store = Box::new(secret_store(dir, Some(n.iterations))?);
|
||||
let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default());
|
||||
let new_account = acc_provider.new_account(&password).map_err(|e| format!("Could not create new account: {}", e))?;
|
||||
Ok(format!("0x{:x}", new_account))
|
||||
}
|
||||
let dir = Box::new(keys_dir(n.path, n.spec)?);
|
||||
let secret_store = Box::new(secret_store(dir, Some(n.iterations))?);
|
||||
let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default());
|
||||
let new_account = acc_provider
|
||||
.new_account(&password)
|
||||
.map_err(|e| format!("Could not create new account: {}", e))?;
|
||||
Ok(format!("0x{:x}", new_account))
|
||||
}
|
||||
|
||||
fn list(list_cmd: ListAccounts) -> Result<String, String> {
|
||||
let dir = Box::new(keys_dir(list_cmd.path, list_cmd.spec)?);
|
||||
let secret_store = Box::new(secret_store(dir, None)?);
|
||||
let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default());
|
||||
let accounts = acc_provider.accounts().map_err(|e| format!("{}", e))?;
|
||||
let result = accounts.into_iter()
|
||||
.map(|a| format!("0x{:x}", a))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
fn list(list_cmd: ListAccounts) -> Result<String, String> {
|
||||
let dir = Box::new(keys_dir(list_cmd.path, list_cmd.spec)?);
|
||||
let secret_store = Box::new(secret_store(dir, None)?);
|
||||
let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default());
|
||||
let accounts = acc_provider.accounts().map_err(|e| format!("{}", e))?;
|
||||
let result = accounts
|
||||
.into_iter()
|
||||
.map(|a| format!("0x{:x}", a))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn import(i: ImportAccounts) -> Result<String, String> {
|
||||
let to = keys_dir(i.to, i.spec)?;
|
||||
let mut imported = 0;
|
||||
fn import(i: ImportAccounts) -> Result<String, String> {
|
||||
let to = keys_dir(i.to, i.spec)?;
|
||||
let mut imported = 0;
|
||||
|
||||
for path in &i.from {
|
||||
let path = PathBuf::from(path);
|
||||
if path.is_dir() {
|
||||
let from = RootDiskDirectory::at(&path);
|
||||
imported += import_accounts(&from, &to).map_err(|e| format!("Importing accounts from {:?} failed: {}", path, e))?.len();
|
||||
} else if path.is_file() {
|
||||
import_account(&path, &to).map_err(|e| format!("Importing account from {:?} failed: {}", path, e))?;
|
||||
imported += 1;
|
||||
}
|
||||
}
|
||||
for path in &i.from {
|
||||
let path = PathBuf::from(path);
|
||||
if path.is_dir() {
|
||||
let from = RootDiskDirectory::at(&path);
|
||||
imported += import_accounts(&from, &to)
|
||||
.map_err(|e| format!("Importing accounts from {:?} failed: {}", path, e))?
|
||||
.len();
|
||||
} else if path.is_file() {
|
||||
import_account(&path, &to)
|
||||
.map_err(|e| format!("Importing account from {:?} failed: {}", path, e))?;
|
||||
imported += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(format!("{} account(s) imported", imported))
|
||||
}
|
||||
Ok(format!("{} account(s) imported", imported))
|
||||
}
|
||||
|
||||
fn import_geth(i: ImportFromGethAccounts) -> Result<String, String> {
|
||||
use std::io::ErrorKind;
|
||||
use ethstore::Error;
|
||||
fn import_geth(i: ImportFromGethAccounts) -> Result<String, String> {
|
||||
use ethstore::Error;
|
||||
use std::io::ErrorKind;
|
||||
|
||||
let dir = Box::new(keys_dir(i.to, i.spec)?);
|
||||
let secret_store = Box::new(secret_store(dir, None)?);
|
||||
let geth_accounts = read_geth_accounts(i.testnet);
|
||||
match secret_store.import_geth_accounts(SecretVaultRef::Root, geth_accounts, i.testnet) {
|
||||
Ok(v) => Ok(format!("Successfully imported {} account(s) from geth.", v.len())),
|
||||
Err(Error::Io(ref io_err)) if io_err.kind() == ErrorKind::NotFound => Err("Failed to find geth keys folder.".into()),
|
||||
Err(err) => Err(format!("Import geth accounts failed. {}", err))
|
||||
}
|
||||
}
|
||||
let dir = Box::new(keys_dir(i.to, i.spec)?);
|
||||
let secret_store = Box::new(secret_store(dir, None)?);
|
||||
let geth_accounts = read_geth_accounts(i.testnet);
|
||||
match secret_store.import_geth_accounts(SecretVaultRef::Root, geth_accounts, i.testnet) {
|
||||
Ok(v) => Ok(format!(
|
||||
"Successfully imported {} account(s) from geth.",
|
||||
v.len()
|
||||
)),
|
||||
Err(Error::Io(ref io_err)) if io_err.kind() == ErrorKind::NotFound => {
|
||||
Err("Failed to find geth keys folder.".into())
|
||||
}
|
||||
Err(err) => Err(format!("Import geth accounts failed. {}", err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "accounts")]
|
||||
|
||||
@@ -20,225 +20,306 @@ use dir::Directories;
|
||||
use ethereum_types::Address;
|
||||
use ethkey::Password;
|
||||
|
||||
use params::{SpecType, AccountsConfig};
|
||||
use params::{AccountsConfig, SpecType};
|
||||
|
||||
#[cfg(not(feature = "accounts"))]
|
||||
mod accounts {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
/// Dummy AccountProvider
|
||||
pub struct AccountProvider;
|
||||
/// Dummy AccountProvider
|
||||
pub struct AccountProvider;
|
||||
|
||||
impl ::ethcore::miner::LocalAccounts for AccountProvider {
|
||||
fn is_local(&self, _address: &Address) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
impl ::ethcore::miner::LocalAccounts for AccountProvider {
|
||||
fn is_local(&self, _address: &Address) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare_account_provider(_spec: &SpecType, _dirs: &Directories, _data_dir: &str, _cfg: AccountsConfig, _passwords: &[Password]) -> Result<AccountProvider, String> {
|
||||
warn!("Note: Your instance of Parity Ethereum is running without account support. Some CLI options are ignored.");
|
||||
Ok(AccountProvider)
|
||||
}
|
||||
pub fn prepare_account_provider(
|
||||
_spec: &SpecType,
|
||||
_dirs: &Directories,
|
||||
_data_dir: &str,
|
||||
_cfg: AccountsConfig,
|
||||
_passwords: &[Password],
|
||||
) -> Result<AccountProvider, String> {
|
||||
warn!("Note: Your instance of Parity Ethereum is running without account support. Some CLI options are ignored.");
|
||||
Ok(AccountProvider)
|
||||
}
|
||||
|
||||
pub fn miner_local_accounts(_: Arc<AccountProvider>) -> AccountProvider {
|
||||
AccountProvider
|
||||
}
|
||||
pub fn miner_local_accounts(_: Arc<AccountProvider>) -> AccountProvider {
|
||||
AccountProvider
|
||||
}
|
||||
|
||||
pub fn miner_author(_spec: &SpecType, _dirs: &Directories, _account_provider: &Arc<AccountProvider>, _engine_signer: Address, _passwords: &[Password]) -> Result<Option<::ethcore::miner::Author>, String> {
|
||||
Ok(None)
|
||||
}
|
||||
pub fn miner_author(
|
||||
_spec: &SpecType,
|
||||
_dirs: &Directories,
|
||||
_account_provider: &Arc<AccountProvider>,
|
||||
_engine_signer: Address,
|
||||
_passwords: &[Password],
|
||||
) -> Result<Option<::ethcore::miner::Author>, String> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn private_tx_signer(_account_provider: Arc<AccountProvider>, _passwords: &[Password]) -> Result<Arc<::ethcore_private_tx::Signer>, String> {
|
||||
Ok(Arc::new(::ethcore_private_tx::DummySigner))
|
||||
}
|
||||
pub fn private_tx_signer(
|
||||
_account_provider: Arc<AccountProvider>,
|
||||
_passwords: &[Password],
|
||||
) -> Result<Arc<::ethcore_private_tx::Signer>, String> {
|
||||
Ok(Arc::new(::ethcore_private_tx::DummySigner))
|
||||
}
|
||||
|
||||
pub fn accounts_list(_account_provider: Arc<AccountProvider>) -> Arc<Fn() -> Vec<Address> + Send + Sync> {
|
||||
Arc::new(|| vec![])
|
||||
}
|
||||
pub fn accounts_list(
|
||||
_account_provider: Arc<AccountProvider>,
|
||||
) -> Arc<Fn() -> Vec<Address> + Send + Sync> {
|
||||
Arc::new(|| vec![])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "accounts")]
|
||||
mod accounts {
|
||||
use super::*;
|
||||
use upgrade::upgrade_key_location;
|
||||
use super::*;
|
||||
use upgrade::upgrade_key_location;
|
||||
|
||||
pub use accounts::AccountProvider;
|
||||
pub use accounts::AccountProvider;
|
||||
|
||||
/// Pops along with error messages when a password is missing or invalid.
|
||||
const VERIFY_PASSWORD_HINT: &str = "Make sure valid password is present in files passed using `--password` or in the configuration file.";
|
||||
/// Pops along with error messages when a password is missing or invalid.
|
||||
const VERIFY_PASSWORD_HINT: &str = "Make sure valid password is present in files passed using `--password` or in the configuration file.";
|
||||
|
||||
/// Initialize account provider
|
||||
pub fn prepare_account_provider(spec: &SpecType, dirs: &Directories, data_dir: &str, cfg: AccountsConfig, passwords: &[Password]) -> Result<AccountProvider, String> {
|
||||
use ethstore::EthStore;
|
||||
use ethstore::accounts_dir::RootDiskDirectory;
|
||||
use accounts::AccountProviderSettings;
|
||||
/// Initialize account provider
|
||||
pub fn prepare_account_provider(
|
||||
spec: &SpecType,
|
||||
dirs: &Directories,
|
||||
data_dir: &str,
|
||||
cfg: AccountsConfig,
|
||||
passwords: &[Password],
|
||||
) -> Result<AccountProvider, String> {
|
||||
use accounts::AccountProviderSettings;
|
||||
use ethstore::{accounts_dir::RootDiskDirectory, EthStore};
|
||||
|
||||
let path = dirs.keys_path(data_dir);
|
||||
upgrade_key_location(&dirs.legacy_keys_path(cfg.testnet), &path);
|
||||
let dir = Box::new(RootDiskDirectory::create(&path).map_err(|e| format!("Could not open keys directory: {}", e))?);
|
||||
let account_settings = AccountProviderSettings {
|
||||
enable_hardware_wallets: cfg.enable_hardware_wallets,
|
||||
hardware_wallet_classic_key: spec == &SpecType::Classic,
|
||||
unlock_keep_secret: cfg.enable_fast_unlock,
|
||||
blacklisted_accounts: match *spec {
|
||||
SpecType::Morden | SpecType::Mordor | SpecType::Ropsten | SpecType::Kovan | SpecType::Goerli | SpecType::Kotti | SpecType::Sokol | SpecType::Dev => vec![],
|
||||
_ => vec![
|
||||
"00a329c0648769a73afac7f9381e08fb43dbea72".into()
|
||||
],
|
||||
},
|
||||
};
|
||||
let path = dirs.keys_path(data_dir);
|
||||
upgrade_key_location(&dirs.legacy_keys_path(cfg.testnet), &path);
|
||||
let dir = Box::new(
|
||||
RootDiskDirectory::create(&path)
|
||||
.map_err(|e| format!("Could not open keys directory: {}", e))?,
|
||||
);
|
||||
let account_settings = AccountProviderSettings {
|
||||
enable_hardware_wallets: cfg.enable_hardware_wallets,
|
||||
hardware_wallet_classic_key: spec == &SpecType::Classic,
|
||||
unlock_keep_secret: cfg.enable_fast_unlock,
|
||||
blacklisted_accounts: match *spec {
|
||||
SpecType::Morden
|
||||
| SpecType::Mordor
|
||||
| SpecType::Ropsten
|
||||
| SpecType::Kovan
|
||||
| SpecType::Goerli
|
||||
| SpecType::Kotti
|
||||
| SpecType::Sokol
|
||||
| SpecType::Dev => vec![],
|
||||
_ => vec!["00a329c0648769a73afac7f9381e08fb43dbea72".into()],
|
||||
},
|
||||
};
|
||||
|
||||
let ethstore = EthStore::open_with_iterations(dir, cfg.iterations).map_err(|e| format!("Could not open keys directory: {}", e))?;
|
||||
if cfg.refresh_time > 0 {
|
||||
ethstore.set_refresh_time(::std::time::Duration::from_secs(cfg.refresh_time));
|
||||
}
|
||||
let account_provider = AccountProvider::new(
|
||||
Box::new(ethstore),
|
||||
account_settings,
|
||||
);
|
||||
let ethstore = EthStore::open_with_iterations(dir, cfg.iterations)
|
||||
.map_err(|e| format!("Could not open keys directory: {}", e))?;
|
||||
if cfg.refresh_time > 0 {
|
||||
ethstore.set_refresh_time(::std::time::Duration::from_secs(cfg.refresh_time));
|
||||
}
|
||||
let account_provider = AccountProvider::new(Box::new(ethstore), account_settings);
|
||||
|
||||
// Add development account if running dev chain:
|
||||
if let SpecType::Dev = *spec {
|
||||
insert_dev_account(&account_provider);
|
||||
}
|
||||
// Add development account if running dev chain:
|
||||
if let SpecType::Dev = *spec {
|
||||
insert_dev_account(&account_provider);
|
||||
}
|
||||
|
||||
for a in cfg.unlocked_accounts {
|
||||
// Check if the account exists
|
||||
if !account_provider.has_account(a) {
|
||||
return Err(format!("Account {} not found for the current chain. {}", a, build_create_account_hint(spec, &dirs.keys)));
|
||||
}
|
||||
for a in cfg.unlocked_accounts {
|
||||
// Check if the account exists
|
||||
if !account_provider.has_account(a) {
|
||||
return Err(format!(
|
||||
"Account {} not found for the current chain. {}",
|
||||
a,
|
||||
build_create_account_hint(spec, &dirs.keys)
|
||||
));
|
||||
}
|
||||
|
||||
// Check if any passwords have been read from the password file(s)
|
||||
if passwords.is_empty() {
|
||||
return Err(format!("No password found to unlock account {}. {}", a, VERIFY_PASSWORD_HINT));
|
||||
}
|
||||
// Check if any passwords have been read from the password file(s)
|
||||
if passwords.is_empty() {
|
||||
return Err(format!(
|
||||
"No password found to unlock account {}. {}",
|
||||
a, VERIFY_PASSWORD_HINT
|
||||
));
|
||||
}
|
||||
|
||||
if !passwords.iter().any(|p| account_provider.unlock_account_permanently(a, (*p).clone()).is_ok()) {
|
||||
return Err(format!("No valid password to unlock account {}. {}", a, VERIFY_PASSWORD_HINT));
|
||||
}
|
||||
}
|
||||
if !passwords.iter().any(|p| {
|
||||
account_provider
|
||||
.unlock_account_permanently(a, (*p).clone())
|
||||
.is_ok()
|
||||
}) {
|
||||
return Err(format!(
|
||||
"No valid password to unlock account {}. {}",
|
||||
a, VERIFY_PASSWORD_HINT
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(account_provider)
|
||||
}
|
||||
Ok(account_provider)
|
||||
}
|
||||
|
||||
pub struct LocalAccounts(Arc<AccountProvider>);
|
||||
impl ::ethcore::miner::LocalAccounts for LocalAccounts {
|
||||
fn is_local(&self, address: &Address) -> bool {
|
||||
self.0.has_account(*address)
|
||||
}
|
||||
}
|
||||
pub struct LocalAccounts(Arc<AccountProvider>);
|
||||
impl ::ethcore::miner::LocalAccounts for LocalAccounts {
|
||||
fn is_local(&self, address: &Address) -> bool {
|
||||
self.0.has_account(*address)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn miner_local_accounts(account_provider: Arc<AccountProvider>) -> LocalAccounts {
|
||||
LocalAccounts(account_provider)
|
||||
}
|
||||
pub fn miner_local_accounts(account_provider: Arc<AccountProvider>) -> LocalAccounts {
|
||||
LocalAccounts(account_provider)
|
||||
}
|
||||
|
||||
pub fn miner_author(spec: &SpecType, dirs: &Directories, account_provider: &Arc<AccountProvider>, engine_signer: Address, passwords: &[Password]) -> Result<Option<::ethcore::miner::Author>, String> {
|
||||
use ethcore::engines::EngineSigner;
|
||||
pub fn miner_author(
|
||||
spec: &SpecType,
|
||||
dirs: &Directories,
|
||||
account_provider: &Arc<AccountProvider>,
|
||||
engine_signer: Address,
|
||||
passwords: &[Password],
|
||||
) -> Result<Option<::ethcore::miner::Author>, String> {
|
||||
use ethcore::engines::EngineSigner;
|
||||
|
||||
// Check if engine signer exists
|
||||
if !account_provider.has_account(engine_signer) {
|
||||
return Err(format!("Consensus signer account not found for the current chain. {}", build_create_account_hint(spec, &dirs.keys)));
|
||||
}
|
||||
// Check if engine signer exists
|
||||
if !account_provider.has_account(engine_signer) {
|
||||
return Err(format!(
|
||||
"Consensus signer account not found for the current chain. {}",
|
||||
build_create_account_hint(spec, &dirs.keys)
|
||||
));
|
||||
}
|
||||
|
||||
// Check if any passwords have been read from the password file(s)
|
||||
if passwords.is_empty() {
|
||||
return Err(format!("No password found for the consensus signer {}. {}", engine_signer, VERIFY_PASSWORD_HINT));
|
||||
}
|
||||
// Check if any passwords have been read from the password file(s)
|
||||
if passwords.is_empty() {
|
||||
return Err(format!(
|
||||
"No password found for the consensus signer {}. {}",
|
||||
engine_signer, VERIFY_PASSWORD_HINT
|
||||
));
|
||||
}
|
||||
|
||||
let mut author = None;
|
||||
for password in passwords {
|
||||
let signer = parity_rpc::signer::EngineSigner::new(
|
||||
account_provider.clone(),
|
||||
engine_signer,
|
||||
password.clone(),
|
||||
);
|
||||
if signer.sign(Default::default()).is_ok() {
|
||||
author = Some(::ethcore::miner::Author::Sealer(Box::new(signer)));
|
||||
}
|
||||
}
|
||||
if author.is_none() {
|
||||
return Err(format!("No valid password for the consensus signer {}. {}", engine_signer, VERIFY_PASSWORD_HINT));
|
||||
}
|
||||
let mut author = None;
|
||||
for password in passwords {
|
||||
let signer = parity_rpc::signer::EngineSigner::new(
|
||||
account_provider.clone(),
|
||||
engine_signer,
|
||||
password.clone(),
|
||||
);
|
||||
if signer.sign(Default::default()).is_ok() {
|
||||
author = Some(::ethcore::miner::Author::Sealer(Box::new(signer)));
|
||||
}
|
||||
}
|
||||
if author.is_none() {
|
||||
return Err(format!(
|
||||
"No valid password for the consensus signer {}. {}",
|
||||
engine_signer, VERIFY_PASSWORD_HINT
|
||||
));
|
||||
}
|
||||
|
||||
Ok(author)
|
||||
}
|
||||
Ok(author)
|
||||
}
|
||||
|
||||
mod private_tx {
|
||||
use super::*;
|
||||
use ethcore_private_tx::Error;
|
||||
use ethkey::{Message, Signature};
|
||||
|
||||
mod private_tx {
|
||||
use super::*;
|
||||
use ethkey::{Signature, Message};
|
||||
use ethcore_private_tx::{Error};
|
||||
pub struct AccountSigner {
|
||||
pub accounts: Arc<AccountProvider>,
|
||||
pub passwords: Vec<Password>,
|
||||
}
|
||||
|
||||
pub struct AccountSigner {
|
||||
pub accounts: Arc<AccountProvider>,
|
||||
pub passwords: Vec<Password>,
|
||||
}
|
||||
impl ::ethcore_private_tx::Signer for AccountSigner {
|
||||
fn decrypt(
|
||||
&self,
|
||||
account: Address,
|
||||
shared_mac: &[u8],
|
||||
payload: &[u8],
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
let password = self.find_account_password(&account);
|
||||
Ok(self
|
||||
.accounts
|
||||
.decrypt(account, password, shared_mac, payload)
|
||||
.map_err(|e| e.to_string())?)
|
||||
}
|
||||
|
||||
impl ::ethcore_private_tx::Signer for AccountSigner {
|
||||
fn decrypt(&self, account: Address, shared_mac: &[u8], payload: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let password = self.find_account_password(&account);
|
||||
Ok(self.accounts.decrypt(account, password, shared_mac, payload).map_err(|e| e.to_string())?)
|
||||
}
|
||||
fn sign(&self, account: Address, hash: Message) -> Result<Signature, Error> {
|
||||
let password = self.find_account_password(&account);
|
||||
Ok(self
|
||||
.accounts
|
||||
.sign(account, password, hash)
|
||||
.map_err(|e| e.to_string())?)
|
||||
}
|
||||
}
|
||||
|
||||
fn sign(&self, account: Address, hash: Message) -> Result<Signature, Error> {
|
||||
let password = self.find_account_password(&account);
|
||||
Ok(self.accounts.sign(account, password, hash).map_err(|e| e.to_string())?)
|
||||
}
|
||||
}
|
||||
impl AccountSigner {
|
||||
/// Try to unlock account using stored password, return found password if any
|
||||
fn find_account_password(&self, account: &Address) -> Option<Password> {
|
||||
for password in &self.passwords {
|
||||
if let Ok(true) = self.accounts.test_password(account, password) {
|
||||
return Some(password.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AccountSigner {
|
||||
/// Try to unlock account using stored password, return found password if any
|
||||
fn find_account_password(&self, account: &Address) -> Option<Password> {
|
||||
for password in &self.passwords {
|
||||
if let Ok(true) = self.accounts.test_password(account, password) {
|
||||
return Some(password.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn private_tx_signer(
|
||||
accounts: Arc<AccountProvider>,
|
||||
passwords: &[Password],
|
||||
) -> Result<Arc<::ethcore_private_tx::Signer>, String> {
|
||||
Ok(Arc::new(self::private_tx::AccountSigner {
|
||||
accounts,
|
||||
passwords: passwords.to_vec(),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn private_tx_signer(accounts: Arc<AccountProvider>, passwords: &[Password]) -> Result<Arc<::ethcore_private_tx::Signer>, String> {
|
||||
Ok(Arc::new(self::private_tx::AccountSigner {
|
||||
accounts,
|
||||
passwords: passwords.to_vec(),
|
||||
}))
|
||||
}
|
||||
pub fn accounts_list(
|
||||
account_provider: Arc<AccountProvider>,
|
||||
) -> Arc<Fn() -> Vec<Address> + Send + Sync> {
|
||||
Arc::new(move || account_provider.accounts().unwrap_or_default())
|
||||
}
|
||||
|
||||
pub fn accounts_list(account_provider: Arc<AccountProvider>) -> Arc<Fn() -> Vec<Address> + Send + Sync> {
|
||||
Arc::new(move || account_provider.accounts().unwrap_or_default())
|
||||
}
|
||||
fn insert_dev_account(account_provider: &AccountProvider) {
|
||||
let secret: ethkey::Secret =
|
||||
"4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7".into();
|
||||
let dev_account = ethkey::KeyPair::from_secret(secret.clone())
|
||||
.expect("Valid secret produces valid key;qed");
|
||||
if !account_provider.has_account(dev_account.address()) {
|
||||
match account_provider.insert_account(secret, &Password::from(String::new())) {
|
||||
Err(e) => warn!("Unable to add development account: {}", e),
|
||||
Ok(address) => {
|
||||
let _ = account_provider
|
||||
.set_account_name(address.clone(), "Development Account".into());
|
||||
let _ = account_provider.set_account_meta(
|
||||
address,
|
||||
::serde_json::to_string(
|
||||
&(vec![
|
||||
(
|
||||
"description",
|
||||
"Never use this account outside of development chain!",
|
||||
),
|
||||
("passwordHint", "Password is empty string"),
|
||||
]
|
||||
.into_iter()
|
||||
.collect::<::std::collections::HashMap<_, _>>()),
|
||||
)
|
||||
.expect("Serialization of hashmap does not fail."),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_dev_account(account_provider: &AccountProvider) {
|
||||
let secret: ethkey::Secret = "4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7".into();
|
||||
let dev_account = ethkey::KeyPair::from_secret(secret.clone()).expect("Valid secret produces valid key;qed");
|
||||
if !account_provider.has_account(dev_account.address()) {
|
||||
match account_provider.insert_account(secret, &Password::from(String::new())) {
|
||||
Err(e) => warn!("Unable to add development account: {}", e),
|
||||
Ok(address) => {
|
||||
let _ = account_provider.set_account_name(address.clone(), "Development Account".into());
|
||||
let _ = account_provider.set_account_meta(address, ::serde_json::to_string(&(vec![
|
||||
("description", "Never use this account outside of development chain!"),
|
||||
("passwordHint","Password is empty string"),
|
||||
].into_iter().collect::<::std::collections::HashMap<_,_>>())).expect("Serialization of hashmap does not fail."));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct an error `String` with an adaptive hint on how to create an account.
|
||||
fn build_create_account_hint(spec: &SpecType, keys: &str) -> String {
|
||||
format!("You can create an account via RPC, UI or `parity account new --chain {} --keys-path {}`.", spec, keys)
|
||||
}
|
||||
// Construct an error `String` with an adaptive hint on how to create an account.
|
||||
fn build_create_account_hint(spec: &SpecType, keys: &str) -> String {
|
||||
format!("You can create an account via RPC, UI or `parity account new --chain {} --keys-path {}`.", spec, keys)
|
||||
}
|
||||
}
|
||||
|
||||
pub use self::accounts::{
|
||||
AccountProvider,
|
||||
prepare_account_provider,
|
||||
miner_local_accounts,
|
||||
miner_author,
|
||||
private_tx_signer,
|
||||
accounts_list,
|
||||
accounts_list, miner_author, miner_local_accounts, prepare_account_provider, private_tx_signer,
|
||||
AccountProvider,
|
||||
};
|
||||
|
||||
|
||||
1118
parity/blockchain.rs
1118
parity/blockchain.rs
File diff suppressed because it is too large
Load Diff
176
parity/cache.rs
176
parity/cache.rs
@@ -29,110 +29,114 @@ const DEFAULT_STATE_CACHE_SIZE: u32 = 25;
|
||||
/// All values are represented in MB.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct CacheConfig {
|
||||
/// Size of rocksDB cache. Almost all goes to the state column.
|
||||
db: u32,
|
||||
/// Size of blockchain cache.
|
||||
blockchain: u32,
|
||||
/// Size of transaction queue cache.
|
||||
queue: u32,
|
||||
/// Size of traces cache.
|
||||
traces: u32,
|
||||
/// Size of the state cache.
|
||||
state: u32,
|
||||
/// Size of rocksDB cache. Almost all goes to the state column.
|
||||
db: u32,
|
||||
/// Size of blockchain cache.
|
||||
blockchain: u32,
|
||||
/// Size of transaction queue cache.
|
||||
queue: u32,
|
||||
/// Size of traces cache.
|
||||
traces: u32,
|
||||
/// Size of the state cache.
|
||||
state: u32,
|
||||
}
|
||||
|
||||
impl Default for CacheConfig {
|
||||
fn default() -> Self {
|
||||
CacheConfig::new(
|
||||
DEFAULT_DB_CACHE_SIZE,
|
||||
DEFAULT_BC_CACHE_SIZE,
|
||||
DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB,
|
||||
DEFAULT_STATE_CACHE_SIZE)
|
||||
}
|
||||
fn default() -> Self {
|
||||
CacheConfig::new(
|
||||
DEFAULT_DB_CACHE_SIZE,
|
||||
DEFAULT_BC_CACHE_SIZE,
|
||||
DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB,
|
||||
DEFAULT_STATE_CACHE_SIZE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl CacheConfig {
|
||||
/// Creates new cache config with cumulative size equal `total`.
|
||||
pub fn new_with_total_cache_size(total: u32) -> Self {
|
||||
CacheConfig {
|
||||
db: total * 7 / 10,
|
||||
blockchain: total / 10,
|
||||
queue: DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB,
|
||||
traces: DEFAULT_TRACE_CACHE_SIZE,
|
||||
state: total * 2 / 10,
|
||||
}
|
||||
}
|
||||
/// Creates new cache config with cumulative size equal `total`.
|
||||
pub fn new_with_total_cache_size(total: u32) -> Self {
|
||||
CacheConfig {
|
||||
db: total * 7 / 10,
|
||||
blockchain: total / 10,
|
||||
queue: DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB,
|
||||
traces: DEFAULT_TRACE_CACHE_SIZE,
|
||||
state: total * 2 / 10,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new cache config with gitven details.
|
||||
pub fn new(db: u32, blockchain: u32, queue: u32, state: u32) -> Self {
|
||||
CacheConfig {
|
||||
db: db,
|
||||
blockchain: blockchain,
|
||||
queue: queue,
|
||||
traces: DEFAULT_TRACE_CACHE_SIZE,
|
||||
state: state,
|
||||
}
|
||||
}
|
||||
/// Creates new cache config with gitven details.
|
||||
pub fn new(db: u32, blockchain: u32, queue: u32, state: u32) -> Self {
|
||||
CacheConfig {
|
||||
db: db,
|
||||
blockchain: blockchain,
|
||||
queue: queue,
|
||||
traces: DEFAULT_TRACE_CACHE_SIZE,
|
||||
state: state,
|
||||
}
|
||||
}
|
||||
|
||||
/// Size of db cache.
|
||||
pub fn db_cache_size(&self) -> u32 {
|
||||
max(MIN_DB_CACHE_MB, self.db)
|
||||
}
|
||||
/// Size of db cache.
|
||||
pub fn db_cache_size(&self) -> u32 {
|
||||
max(MIN_DB_CACHE_MB, self.db)
|
||||
}
|
||||
|
||||
/// Size of block queue size limit
|
||||
pub fn queue(&self) -> u32 {
|
||||
max(self.queue, MIN_BLOCK_QUEUE_SIZE_LIMIT_MB)
|
||||
}
|
||||
/// Size of block queue size limit
|
||||
pub fn queue(&self) -> u32 {
|
||||
max(self.queue, MIN_BLOCK_QUEUE_SIZE_LIMIT_MB)
|
||||
}
|
||||
|
||||
/// Size of the blockchain cache.
|
||||
pub fn blockchain(&self) -> u32 {
|
||||
max(self.blockchain, MIN_BC_CACHE_MB)
|
||||
}
|
||||
/// Size of the blockchain cache.
|
||||
pub fn blockchain(&self) -> u32 {
|
||||
max(self.blockchain, MIN_BC_CACHE_MB)
|
||||
}
|
||||
|
||||
/// Size of the traces cache.
|
||||
pub fn traces(&self) -> u32 {
|
||||
self.traces
|
||||
}
|
||||
/// Size of the traces cache.
|
||||
pub fn traces(&self) -> u32 {
|
||||
self.traces
|
||||
}
|
||||
|
||||
/// Size of the state cache.
|
||||
pub fn state(&self) -> u32 {
|
||||
self.state * 3 / 4
|
||||
}
|
||||
/// Size of the state cache.
|
||||
pub fn state(&self) -> u32 {
|
||||
self.state * 3 / 4
|
||||
}
|
||||
|
||||
/// Size of the jump-tables cache.
|
||||
pub fn jump_tables(&self) -> u32 {
|
||||
self.state / 4
|
||||
}
|
||||
/// Size of the jump-tables cache.
|
||||
pub fn jump_tables(&self) -> u32 {
|
||||
self.state / 4
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::CacheConfig;
|
||||
use super::CacheConfig;
|
||||
|
||||
#[test]
|
||||
fn test_cache_config_constructor() {
|
||||
let config = CacheConfig::new_with_total_cache_size(200);
|
||||
assert_eq!(config.db, 140);
|
||||
assert_eq!(config.blockchain(), 20);
|
||||
assert_eq!(config.queue(), 40);
|
||||
assert_eq!(config.state(), 30);
|
||||
assert_eq!(config.jump_tables(), 10);
|
||||
}
|
||||
#[test]
|
||||
fn test_cache_config_constructor() {
|
||||
let config = CacheConfig::new_with_total_cache_size(200);
|
||||
assert_eq!(config.db, 140);
|
||||
assert_eq!(config.blockchain(), 20);
|
||||
assert_eq!(config.queue(), 40);
|
||||
assert_eq!(config.state(), 30);
|
||||
assert_eq!(config.jump_tables(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cache_config_db_cache_sizes() {
|
||||
let config = CacheConfig::new_with_total_cache_size(400);
|
||||
assert_eq!(config.db, 280);
|
||||
assert_eq!(config.db_cache_size(), 280);
|
||||
}
|
||||
#[test]
|
||||
fn test_cache_config_db_cache_sizes() {
|
||||
let config = CacheConfig::new_with_total_cache_size(400);
|
||||
assert_eq!(config.db, 280);
|
||||
assert_eq!(config.db_cache_size(), 280);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cache_config_default() {
|
||||
assert_eq!(CacheConfig::default(),
|
||||
CacheConfig::new(
|
||||
super::DEFAULT_DB_CACHE_SIZE,
|
||||
super::DEFAULT_BC_CACHE_SIZE,
|
||||
super::DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB,
|
||||
super::DEFAULT_STATE_CACHE_SIZE));
|
||||
}
|
||||
#[test]
|
||||
fn test_cache_config_default() {
|
||||
assert_eq!(
|
||||
CacheConfig::default(),
|
||||
CacheConfig::new(
|
||||
super::DEFAULT_DB_CACHE_SIZE,
|
||||
super::DEFAULT_BC_CACHE_SIZE,
|
||||
super::DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB,
|
||||
super::DEFAULT_STATE_CACHE_SIZE
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
3938
parity/cli/mod.rs
3938
parity/cli/mod.rs
File diff suppressed because it is too large
Load Diff
@@ -15,17 +15,24 @@
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
macro_rules! return_if_parse_error {
|
||||
($e:expr) => (
|
||||
match $e {
|
||||
Err(clap_error @ ClapError { kind: ClapErrorKind::ValueValidation, .. }) => {
|
||||
return Err(clap_error);
|
||||
},
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Err(
|
||||
clap_error
|
||||
@
|
||||
ClapError {
|
||||
kind: ClapErrorKind::ValueValidation,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
return Err(clap_error);
|
||||
}
|
||||
|
||||
// Otherwise, if $e is ClapErrorKind::ArgumentNotFound or Ok(),
|
||||
// then convert to Option
|
||||
_ => $e.ok()
|
||||
}
|
||||
)
|
||||
// Otherwise, if $e is ClapErrorKind::ArgumentNotFound or Ok(),
|
||||
// then convert to Option
|
||||
_ => $e.ok(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! if_option {
|
||||
@@ -47,46 +54,46 @@ macro_rules! if_vec {
|
||||
}
|
||||
|
||||
macro_rules! if_option_vec {
|
||||
(Option<Vec<String>>, THEN {$then:expr} ELSE {$otherwise:expr}) => (
|
||||
$then
|
||||
);
|
||||
(Option<$type:ty>, THEN {$then:expr} ELSE {$otherwise:expr}) => (
|
||||
$otherwise
|
||||
);
|
||||
(Option<Vec<String>>, THEN {$then:expr} ELSE {$otherwise:expr}) => {
|
||||
$then
|
||||
};
|
||||
(Option<$type:ty>, THEN {$then:expr} ELSE {$otherwise:expr}) => {
|
||||
$otherwise
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! inner_option_type {
|
||||
(Option<$type:ty>) => (
|
||||
$type
|
||||
)
|
||||
(Option<$type:ty>) => {
|
||||
$type
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! inner_vec_type {
|
||||
(Vec<$type:ty>) => (
|
||||
$type
|
||||
)
|
||||
(Vec<$type:ty>) => {
|
||||
$type
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! inner_option_vec_type {
|
||||
(Option<Vec<String>>) => (
|
||||
String
|
||||
)
|
||||
(Option<Vec<String>>) => {
|
||||
String
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! usage_with_ident {
|
||||
($name:expr, $usage:expr, $help:expr) => (
|
||||
if $usage.contains("<") {
|
||||
format!("<{}> {} '{}'",$name, $usage, $help)
|
||||
} else {
|
||||
format!("[{}] {} '{}'",$name, $usage, $help)
|
||||
}
|
||||
);
|
||||
($name:expr, $usage:expr, $help:expr) => {
|
||||
if $usage.contains("<") {
|
||||
format!("<{}> {} '{}'", $name, $usage, $help)
|
||||
} else {
|
||||
format!("[{}] {} '{}'", $name, $usage, $help)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! underscore_to_hyphen {
|
||||
($e:expr) => (
|
||||
str::replace($e, "_", "-")
|
||||
)
|
||||
($e:expr) => {
|
||||
str::replace($e, "_", "-")
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! usage {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,10 +16,10 @@
|
||||
|
||||
//! Database-related operations.
|
||||
|
||||
#[path="rocksdb/mod.rs"]
|
||||
#[path = "rocksdb/mod.rs"]
|
||||
mod impls;
|
||||
|
||||
pub use self::impls::{open_db, restoration_db_handler, migrate};
|
||||
pub use self::impls::{migrate, open_db, restoration_db_handler};
|
||||
|
||||
#[cfg(feature = "secretstore")]
|
||||
pub use self::impls::open_secretstore_db;
|
||||
|
||||
@@ -16,73 +16,68 @@
|
||||
|
||||
//! Blooms migration from rocksdb to blooms-db
|
||||
|
||||
use std::path::Path;
|
||||
use ethereum_types::Bloom;
|
||||
use super::{kvdb_rocksdb::DatabaseConfig, open_database};
|
||||
use ethcore::error::Error;
|
||||
use ethereum_types::Bloom;
|
||||
use rlp;
|
||||
use super::kvdb_rocksdb::DatabaseConfig;
|
||||
use super::open_database;
|
||||
use std::path::Path;
|
||||
|
||||
const LOG_BLOOMS_ELEMENTS_PER_INDEX: u64 = 16;
|
||||
|
||||
pub fn migrate_blooms<P: AsRef<Path>>(path: P, config: &DatabaseConfig) -> Result<(), Error> {
|
||||
// init
|
||||
let db = open_database(&path.as_ref().to_string_lossy(), config)?;
|
||||
// init
|
||||
let db = open_database(&path.as_ref().to_string_lossy(), config)?;
|
||||
|
||||
// possible optimization:
|
||||
// pre-allocate space on disk for faster migration
|
||||
// possible optimization:
|
||||
// pre-allocate space on disk for faster migration
|
||||
|
||||
// iterate over header blooms and insert them in blooms-db
|
||||
// Some(3) -> COL_EXTRA
|
||||
// 3u8 -> ExtrasIndex::BlocksBlooms
|
||||
// 0u8 -> level 0
|
||||
let blooms_iterator = db.key_value()
|
||||
.iter_from_prefix(Some(3), &[3u8, 0u8])
|
||||
.filter(|(key, _)| key.len() == 6)
|
||||
.take_while(|(key, _)| {
|
||||
key[0] == 3u8 && key[1] == 0u8
|
||||
})
|
||||
.map(|(key, group)| {
|
||||
let index =
|
||||
(key[2] as u64) << 24 |
|
||||
(key[3] as u64) << 16 |
|
||||
(key[4] as u64) << 8 |
|
||||
(key[5] as u64);
|
||||
let number = index * LOG_BLOOMS_ELEMENTS_PER_INDEX;
|
||||
// iterate over header blooms and insert them in blooms-db
|
||||
// Some(3) -> COL_EXTRA
|
||||
// 3u8 -> ExtrasIndex::BlocksBlooms
|
||||
// 0u8 -> level 0
|
||||
let blooms_iterator = db
|
||||
.key_value()
|
||||
.iter_from_prefix(Some(3), &[3u8, 0u8])
|
||||
.filter(|(key, _)| key.len() == 6)
|
||||
.take_while(|(key, _)| key[0] == 3u8 && key[1] == 0u8)
|
||||
.map(|(key, group)| {
|
||||
let index = (key[2] as u64) << 24
|
||||
| (key[3] as u64) << 16
|
||||
| (key[4] as u64) << 8
|
||||
| (key[5] as u64);
|
||||
let number = index * LOG_BLOOMS_ELEMENTS_PER_INDEX;
|
||||
|
||||
let blooms = rlp::decode_list::<Bloom>(&group);
|
||||
(number, blooms)
|
||||
});
|
||||
let blooms = rlp::decode_list::<Bloom>(&group);
|
||||
(number, blooms)
|
||||
});
|
||||
|
||||
for (number, blooms) in blooms_iterator {
|
||||
db.blooms().insert_blooms(number, blooms.iter())?;
|
||||
}
|
||||
for (number, blooms) in blooms_iterator {
|
||||
db.blooms().insert_blooms(number, blooms.iter())?;
|
||||
}
|
||||
|
||||
// iterate over trace blooms and insert them in blooms-db
|
||||
// Some(4) -> COL_TRACE
|
||||
// 1u8 -> TraceDBIndex::BloomGroups
|
||||
// 0u8 -> level 0
|
||||
let trace_blooms_iterator = db.key_value()
|
||||
.iter_from_prefix(Some(4), &[1u8, 0u8])
|
||||
.filter(|(key, _)| key.len() == 6)
|
||||
.take_while(|(key, _)| {
|
||||
key[0] == 1u8 && key[1] == 0u8
|
||||
})
|
||||
.map(|(key, group)| {
|
||||
let index =
|
||||
(key[2] as u64) |
|
||||
(key[3] as u64) << 8 |
|
||||
(key[4] as u64) << 16 |
|
||||
(key[5] as u64) << 24;
|
||||
let number = index * LOG_BLOOMS_ELEMENTS_PER_INDEX;
|
||||
// iterate over trace blooms and insert them in blooms-db
|
||||
// Some(4) -> COL_TRACE
|
||||
// 1u8 -> TraceDBIndex::BloomGroups
|
||||
// 0u8 -> level 0
|
||||
let trace_blooms_iterator = db
|
||||
.key_value()
|
||||
.iter_from_prefix(Some(4), &[1u8, 0u8])
|
||||
.filter(|(key, _)| key.len() == 6)
|
||||
.take_while(|(key, _)| key[0] == 1u8 && key[1] == 0u8)
|
||||
.map(|(key, group)| {
|
||||
let index = (key[2] as u64)
|
||||
| (key[3] as u64) << 8
|
||||
| (key[4] as u64) << 16
|
||||
| (key[5] as u64) << 24;
|
||||
let number = index * LOG_BLOOMS_ELEMENTS_PER_INDEX;
|
||||
|
||||
let blooms = rlp::decode_list::<Bloom>(&group);
|
||||
(number, blooms)
|
||||
});
|
||||
let blooms = rlp::decode_list::<Bloom>(&group);
|
||||
(number, blooms)
|
||||
});
|
||||
|
||||
for (number, blooms) in trace_blooms_iterator {
|
||||
db.trace_blooms().insert_blooms(number, blooms.iter())?;
|
||||
}
|
||||
for (number, blooms) in trace_blooms_iterator {
|
||||
db.trace_blooms().insert_blooms(number, blooms.iter())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -14,24 +14,27 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::path::Path;
|
||||
use ethcore_db::NUM_COLUMNS;
|
||||
use ethcore::client::{ClientConfig, DatabaseCompactionProfile};
|
||||
use super::kvdb_rocksdb::{CompactionProfile, DatabaseConfig};
|
||||
use ethcore::client::{ClientConfig, DatabaseCompactionProfile};
|
||||
use ethcore_db::NUM_COLUMNS;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn compaction_profile(profile: &DatabaseCompactionProfile, db_path: &Path) -> CompactionProfile {
|
||||
match profile {
|
||||
&DatabaseCompactionProfile::Auto => CompactionProfile::auto(db_path),
|
||||
&DatabaseCompactionProfile::SSD => CompactionProfile::ssd(),
|
||||
&DatabaseCompactionProfile::HDD => CompactionProfile::hdd(),
|
||||
}
|
||||
pub fn compaction_profile(
|
||||
profile: &DatabaseCompactionProfile,
|
||||
db_path: &Path,
|
||||
) -> CompactionProfile {
|
||||
match profile {
|
||||
&DatabaseCompactionProfile::Auto => CompactionProfile::auto(db_path),
|
||||
&DatabaseCompactionProfile::SSD => CompactionProfile::ssd(),
|
||||
&DatabaseCompactionProfile::HDD => CompactionProfile::hdd(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client_db_config(client_path: &Path, client_config: &ClientConfig) -> DatabaseConfig {
|
||||
let mut client_db_config = DatabaseConfig::with_columns(NUM_COLUMNS);
|
||||
let mut client_db_config = DatabaseConfig::with_columns(NUM_COLUMNS);
|
||||
|
||||
client_db_config.memory_budget = client_config.db_cache_size;
|
||||
client_db_config.compaction = compaction_profile(&client_config.db_compaction, &client_path);
|
||||
client_db_config.memory_budget = client_config.db_cache_size;
|
||||
client_db_config.compaction = compaction_profile(&client_config.db_compaction, &client_path);
|
||||
|
||||
client_db_config
|
||||
client_db_config
|
||||
}
|
||||
|
||||
@@ -14,32 +14,34 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fs;
|
||||
use std::io::{Read, Write, Error as IoError, ErrorKind};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fmt::{Display, Formatter, Error as FmtError};
|
||||
use super::migration_rocksdb::{Manager as MigrationManager, Config as MigrationConfig, ChangeColumns};
|
||||
use super::kvdb_rocksdb::{CompactionProfile, DatabaseConfig};
|
||||
use ethcore::client::DatabaseCompactionProfile;
|
||||
use ethcore;
|
||||
use super::{
|
||||
kvdb_rocksdb::{CompactionProfile, DatabaseConfig},
|
||||
migration_rocksdb::{ChangeColumns, Config as MigrationConfig, Manager as MigrationManager},
|
||||
};
|
||||
use ethcore::{self, client::DatabaseCompactionProfile};
|
||||
use std::{
|
||||
fmt::{Display, Error as FmtError, Formatter},
|
||||
fs,
|
||||
io::{Error as IoError, ErrorKind, Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use super::helpers;
|
||||
use super::blooms::migrate_blooms;
|
||||
use super::{blooms::migrate_blooms, helpers};
|
||||
|
||||
/// The migration from v10 to v11.
|
||||
/// Adds a column for node info.
|
||||
pub const TO_V11: ChangeColumns = ChangeColumns {
|
||||
pre_columns: Some(6),
|
||||
post_columns: Some(7),
|
||||
version: 11,
|
||||
pre_columns: Some(6),
|
||||
post_columns: Some(7),
|
||||
version: 11,
|
||||
};
|
||||
|
||||
/// The migration from v11 to v12.
|
||||
/// Adds a column for light chain storage.
|
||||
pub const TO_V12: ChangeColumns = ChangeColumns {
|
||||
pre_columns: Some(7),
|
||||
post_columns: Some(8),
|
||||
version: 12,
|
||||
pre_columns: Some(7),
|
||||
post_columns: Some(8),
|
||||
version: 12,
|
||||
};
|
||||
|
||||
/// Database is assumed to be at default version, when no version file is found.
|
||||
@@ -56,22 +58,22 @@ const VERSION_FILE_NAME: &'static str = "db_version";
|
||||
/// Migration related erorrs.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Returned when current version cannot be read or guessed.
|
||||
UnknownDatabaseVersion,
|
||||
/// Existing DB is newer than the known one.
|
||||
FutureDBVersion,
|
||||
/// Migration is not possible.
|
||||
MigrationImpossible,
|
||||
/// Blooms-db migration error.
|
||||
BloomsDB(ethcore::error::Error),
|
||||
/// Migration was completed succesfully,
|
||||
/// but there was a problem with io.
|
||||
Io(IoError),
|
||||
/// Returned when current version cannot be read or guessed.
|
||||
UnknownDatabaseVersion,
|
||||
/// Existing DB is newer than the known one.
|
||||
FutureDBVersion,
|
||||
/// Migration is not possible.
|
||||
MigrationImpossible,
|
||||
/// Blooms-db migration error.
|
||||
BloomsDB(ethcore::error::Error),
|
||||
/// Migration was completed succesfully,
|
||||
/// but there was a problem with io.
|
||||
Io(IoError),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
||||
let out = match *self {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
||||
let out = match *self {
|
||||
Error::UnknownDatabaseVersion => "Current database version cannot be read".into(),
|
||||
Error::FutureDBVersion => "Database was created with newer client version. Upgrade your client or delete DB and resync.".into(),
|
||||
Error::MigrationImpossible => format!("Database migration to version {} is not possible.", CURRENT_VERSION),
|
||||
@@ -79,153 +81,173 @@ impl Display for Error {
|
||||
Error::Io(ref err) => format!("Unexpected io error on DB migration: {}.", err),
|
||||
};
|
||||
|
||||
write!(f, "{}", out)
|
||||
}
|
||||
write!(f, "{}", out)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IoError> for Error {
|
||||
fn from(err: IoError) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
fn from(err: IoError) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the version file path.
|
||||
fn version_file_path(path: &Path) -> PathBuf {
|
||||
let mut file_path = path.to_owned();
|
||||
file_path.push(VERSION_FILE_NAME);
|
||||
file_path
|
||||
let mut file_path = path.to_owned();
|
||||
file_path.push(VERSION_FILE_NAME);
|
||||
file_path
|
||||
}
|
||||
|
||||
/// Reads current database version from the file at given path.
|
||||
/// If the file does not exist returns `DEFAULT_VERSION`.
|
||||
fn current_version(path: &Path) -> Result<u32, Error> {
|
||||
match fs::File::open(version_file_path(path)) {
|
||||
Err(ref err) if err.kind() == ErrorKind::NotFound => Ok(DEFAULT_VERSION),
|
||||
Err(_) => Err(Error::UnknownDatabaseVersion),
|
||||
Ok(mut file) => {
|
||||
let mut s = String::new();
|
||||
file.read_to_string(&mut s).map_err(|_| Error::UnknownDatabaseVersion)?;
|
||||
u32::from_str_radix(&s, 10).map_err(|_| Error::UnknownDatabaseVersion)
|
||||
},
|
||||
}
|
||||
match fs::File::open(version_file_path(path)) {
|
||||
Err(ref err) if err.kind() == ErrorKind::NotFound => Ok(DEFAULT_VERSION),
|
||||
Err(_) => Err(Error::UnknownDatabaseVersion),
|
||||
Ok(mut file) => {
|
||||
let mut s = String::new();
|
||||
file.read_to_string(&mut s)
|
||||
.map_err(|_| Error::UnknownDatabaseVersion)?;
|
||||
u32::from_str_radix(&s, 10).map_err(|_| Error::UnknownDatabaseVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes current database version to the file.
|
||||
/// Creates a new file if the version file does not exist yet.
|
||||
fn update_version(path: &Path) -> Result<(), Error> {
|
||||
fs::create_dir_all(path)?;
|
||||
let mut file = fs::File::create(version_file_path(path))?;
|
||||
file.write_all(format!("{}", CURRENT_VERSION).as_bytes())?;
|
||||
Ok(())
|
||||
fs::create_dir_all(path)?;
|
||||
let mut file = fs::File::create(version_file_path(path))?;
|
||||
file.write_all(format!("{}", CURRENT_VERSION).as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Consolidated database path
|
||||
fn consolidated_database_path(path: &Path) -> PathBuf {
|
||||
let mut state_path = path.to_owned();
|
||||
state_path.push("db");
|
||||
state_path
|
||||
let mut state_path = path.to_owned();
|
||||
state_path.push("db");
|
||||
state_path
|
||||
}
|
||||
|
||||
/// Database backup
|
||||
fn backup_database_path(path: &Path) -> PathBuf {
|
||||
let mut backup_path = path.to_owned();
|
||||
backup_path.pop();
|
||||
backup_path.push("temp_backup");
|
||||
backup_path
|
||||
let mut backup_path = path.to_owned();
|
||||
backup_path.pop();
|
||||
backup_path.push("temp_backup");
|
||||
backup_path
|
||||
}
|
||||
|
||||
/// Default migration settings.
|
||||
pub fn default_migration_settings(compaction_profile: &CompactionProfile) -> MigrationConfig {
|
||||
MigrationConfig {
|
||||
batch_size: BATCH_SIZE,
|
||||
compaction_profile: *compaction_profile,
|
||||
}
|
||||
MigrationConfig {
|
||||
batch_size: BATCH_SIZE,
|
||||
compaction_profile: *compaction_profile,
|
||||
}
|
||||
}
|
||||
|
||||
/// Migrations on the consolidated database.
|
||||
fn consolidated_database_migrations(compaction_profile: &CompactionProfile) -> Result<MigrationManager, Error> {
|
||||
let mut manager = MigrationManager::new(default_migration_settings(compaction_profile));
|
||||
manager.add_migration(TO_V11).map_err(|_| Error::MigrationImpossible)?;
|
||||
manager.add_migration(TO_V12).map_err(|_| Error::MigrationImpossible)?;
|
||||
Ok(manager)
|
||||
fn consolidated_database_migrations(
|
||||
compaction_profile: &CompactionProfile,
|
||||
) -> Result<MigrationManager, Error> {
|
||||
let mut manager = MigrationManager::new(default_migration_settings(compaction_profile));
|
||||
manager
|
||||
.add_migration(TO_V11)
|
||||
.map_err(|_| Error::MigrationImpossible)?;
|
||||
manager
|
||||
.add_migration(TO_V12)
|
||||
.map_err(|_| Error::MigrationImpossible)?;
|
||||
Ok(manager)
|
||||
}
|
||||
|
||||
/// Migrates database at given position with given migration rules.
|
||||
fn migrate_database(version: u32, db_path: &Path, mut migrations: MigrationManager) -> Result<(), Error> {
|
||||
// check if migration is needed
|
||||
if !migrations.is_needed(version) {
|
||||
return Ok(())
|
||||
}
|
||||
fn migrate_database(
|
||||
version: u32,
|
||||
db_path: &Path,
|
||||
mut migrations: MigrationManager,
|
||||
) -> Result<(), Error> {
|
||||
// check if migration is needed
|
||||
if !migrations.is_needed(version) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let backup_path = backup_database_path(&db_path);
|
||||
// remove the backup dir if it exists
|
||||
let _ = fs::remove_dir_all(&backup_path);
|
||||
let backup_path = backup_database_path(&db_path);
|
||||
// remove the backup dir if it exists
|
||||
let _ = fs::remove_dir_all(&backup_path);
|
||||
|
||||
// migrate old database to the new one
|
||||
let temp_path = migrations.execute(&db_path, version)?;
|
||||
// migrate old database to the new one
|
||||
let temp_path = migrations.execute(&db_path, version)?;
|
||||
|
||||
// completely in-place migration leads to the paths being equal.
|
||||
// in that case, no need to shuffle directories.
|
||||
if temp_path == db_path { return Ok(()) }
|
||||
// completely in-place migration leads to the paths being equal.
|
||||
// in that case, no need to shuffle directories.
|
||||
if temp_path == db_path {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// create backup
|
||||
fs::rename(&db_path, &backup_path)?;
|
||||
// create backup
|
||||
fs::rename(&db_path, &backup_path)?;
|
||||
|
||||
// replace the old database with the new one
|
||||
if let Err(err) = fs::rename(&temp_path, &db_path) {
|
||||
// if something went wrong, bring back backup
|
||||
fs::rename(&backup_path, &db_path)?;
|
||||
return Err(err.into());
|
||||
}
|
||||
// replace the old database with the new one
|
||||
if let Err(err) = fs::rename(&temp_path, &db_path) {
|
||||
// if something went wrong, bring back backup
|
||||
fs::rename(&backup_path, &db_path)?;
|
||||
return Err(err.into());
|
||||
}
|
||||
|
||||
// remove backup
|
||||
fs::remove_dir_all(&backup_path).map_err(Into::into)
|
||||
// remove backup
|
||||
fs::remove_dir_all(&backup_path).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn exists(path: &Path) -> bool {
|
||||
fs::metadata(path).is_ok()
|
||||
fs::metadata(path).is_ok()
|
||||
}
|
||||
|
||||
/// Migrates the database.
|
||||
pub fn migrate(path: &Path, compaction_profile: &DatabaseCompactionProfile) -> Result<(), Error> {
|
||||
let compaction_profile = helpers::compaction_profile(&compaction_profile, path);
|
||||
let compaction_profile = helpers::compaction_profile(&compaction_profile, path);
|
||||
|
||||
// read version file.
|
||||
let version = current_version(path)?;
|
||||
// read version file.
|
||||
let version = current_version(path)?;
|
||||
|
||||
// migrate the databases.
|
||||
// main db directory may already exists, so let's check if we have blocks dir
|
||||
if version > CURRENT_VERSION {
|
||||
return Err(Error::FutureDBVersion);
|
||||
}
|
||||
// migrate the databases.
|
||||
// main db directory may already exists, so let's check if we have blocks dir
|
||||
if version > CURRENT_VERSION {
|
||||
return Err(Error::FutureDBVersion);
|
||||
}
|
||||
|
||||
// We are in the latest version, yay!
|
||||
if version == CURRENT_VERSION {
|
||||
return Ok(())
|
||||
}
|
||||
// We are in the latest version, yay!
|
||||
if version == CURRENT_VERSION {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let db_path = consolidated_database_path(path);
|
||||
let db_path = consolidated_database_path(path);
|
||||
|
||||
// Further migrations
|
||||
if version < CURRENT_VERSION && exists(&db_path) {
|
||||
println!("Migrating database from version {} to {}", version, CURRENT_VERSION);
|
||||
migrate_database(version, &db_path, consolidated_database_migrations(&compaction_profile)?)?;
|
||||
// Further migrations
|
||||
if version < CURRENT_VERSION && exists(&db_path) {
|
||||
println!(
|
||||
"Migrating database from version {} to {}",
|
||||
version, CURRENT_VERSION
|
||||
);
|
||||
migrate_database(
|
||||
version,
|
||||
&db_path,
|
||||
consolidated_database_migrations(&compaction_profile)?,
|
||||
)?;
|
||||
|
||||
if version < BLOOMS_DB_VERSION {
|
||||
println!("Migrating blooms to blooms-db...");
|
||||
let db_config = DatabaseConfig {
|
||||
max_open_files: 64,
|
||||
memory_budget: None,
|
||||
compaction: compaction_profile,
|
||||
columns: ethcore_db::NUM_COLUMNS,
|
||||
};
|
||||
if version < BLOOMS_DB_VERSION {
|
||||
println!("Migrating blooms to blooms-db...");
|
||||
let db_config = DatabaseConfig {
|
||||
max_open_files: 64,
|
||||
memory_budget: None,
|
||||
compaction: compaction_profile,
|
||||
columns: ethcore_db::NUM_COLUMNS,
|
||||
};
|
||||
|
||||
migrate_blooms(&db_path, &db_config).map_err(Error::BloomsDB)?;
|
||||
}
|
||||
migrate_blooms(&db_path, &db_config).map_err(Error::BloomsDB)?;
|
||||
}
|
||||
|
||||
println!("Migration finished");
|
||||
}
|
||||
println!("Migration finished");
|
||||
}
|
||||
|
||||
// update version file.
|
||||
update_version(path)
|
||||
// update version file.
|
||||
update_version(path)
|
||||
}
|
||||
|
||||
@@ -14,104 +14,115 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate ethcore_blockchain;
|
||||
extern crate kvdb_rocksdb;
|
||||
extern crate migration_rocksdb;
|
||||
extern crate ethcore_blockchain;
|
||||
|
||||
use std::{io, fs};
|
||||
use std::sync::Arc;
|
||||
use std::path::Path;
|
||||
use self::{
|
||||
ethcore_blockchain::{BlockChainDB, BlockChainDBHandler},
|
||||
kvdb_rocksdb::{Database, DatabaseConfig},
|
||||
};
|
||||
use blooms_db;
|
||||
use ethcore_db::NUM_COLUMNS;
|
||||
use ethcore::client::{ClientConfig, DatabaseCompactionProfile};
|
||||
use ethcore_db::NUM_COLUMNS;
|
||||
use kvdb::KeyValueDB;
|
||||
use self::ethcore_blockchain::{BlockChainDBHandler, BlockChainDB};
|
||||
use self::kvdb_rocksdb::{Database, DatabaseConfig};
|
||||
use std::{fs, io, path::Path, sync::Arc};
|
||||
|
||||
use cache::CacheConfig;
|
||||
|
||||
mod blooms;
|
||||
mod migration;
|
||||
mod helpers;
|
||||
mod migration;
|
||||
|
||||
pub use self::migration::migrate;
|
||||
|
||||
struct AppDB {
|
||||
key_value: Arc<KeyValueDB>,
|
||||
blooms: blooms_db::Database,
|
||||
trace_blooms: blooms_db::Database,
|
||||
key_value: Arc<KeyValueDB>,
|
||||
blooms: blooms_db::Database,
|
||||
trace_blooms: blooms_db::Database,
|
||||
}
|
||||
|
||||
impl BlockChainDB for AppDB {
|
||||
fn key_value(&self) -> &Arc<KeyValueDB> {
|
||||
&self.key_value
|
||||
}
|
||||
fn key_value(&self) -> &Arc<KeyValueDB> {
|
||||
&self.key_value
|
||||
}
|
||||
|
||||
fn blooms(&self) -> &blooms_db::Database {
|
||||
&self.blooms
|
||||
}
|
||||
fn blooms(&self) -> &blooms_db::Database {
|
||||
&self.blooms
|
||||
}
|
||||
|
||||
fn trace_blooms(&self) -> &blooms_db::Database {
|
||||
&self.trace_blooms
|
||||
}
|
||||
fn trace_blooms(&self) -> &blooms_db::Database {
|
||||
&self.trace_blooms
|
||||
}
|
||||
}
|
||||
|
||||
/// Open a secret store DB using the given secret store data path. The DB path is one level beneath the data path.
|
||||
#[cfg(feature = "secretstore")]
|
||||
pub fn open_secretstore_db(data_path: &str) -> Result<Arc<KeyValueDB>, String> {
|
||||
use std::path::PathBuf;
|
||||
use std::path::PathBuf;
|
||||
|
||||
let mut db_path = PathBuf::from(data_path);
|
||||
db_path.push("db");
|
||||
let db_path = db_path.to_str().ok_or_else(|| "Invalid secretstore path".to_string())?;
|
||||
Ok(Arc::new(Database::open_default(&db_path).map_err(|e| format!("Error opening database: {:?}", e))?))
|
||||
let mut db_path = PathBuf::from(data_path);
|
||||
db_path.push("db");
|
||||
let db_path = db_path
|
||||
.to_str()
|
||||
.ok_or_else(|| "Invalid secretstore path".to_string())?;
|
||||
Ok(Arc::new(
|
||||
Database::open_default(&db_path).map_err(|e| format!("Error opening database: {:?}", e))?,
|
||||
))
|
||||
}
|
||||
|
||||
/// Create a restoration db handler using the config generated by `client_path` and `client_config`.
|
||||
pub fn restoration_db_handler(client_path: &Path, client_config: &ClientConfig) -> Box<BlockChainDBHandler> {
|
||||
let client_db_config = helpers::client_db_config(client_path, client_config);
|
||||
pub fn restoration_db_handler(
|
||||
client_path: &Path,
|
||||
client_config: &ClientConfig,
|
||||
) -> Box<BlockChainDBHandler> {
|
||||
let client_db_config = helpers::client_db_config(client_path, client_config);
|
||||
|
||||
struct RestorationDBHandler {
|
||||
config: DatabaseConfig,
|
||||
}
|
||||
struct RestorationDBHandler {
|
||||
config: DatabaseConfig,
|
||||
}
|
||||
|
||||
impl BlockChainDBHandler for RestorationDBHandler {
|
||||
fn open(&self, db_path: &Path) -> io::Result<Arc<BlockChainDB>> {
|
||||
open_database(&db_path.to_string_lossy(), &self.config)
|
||||
}
|
||||
}
|
||||
impl BlockChainDBHandler for RestorationDBHandler {
|
||||
fn open(&self, db_path: &Path) -> io::Result<Arc<BlockChainDB>> {
|
||||
open_database(&db_path.to_string_lossy(), &self.config)
|
||||
}
|
||||
}
|
||||
|
||||
Box::new(RestorationDBHandler {
|
||||
config: client_db_config,
|
||||
})
|
||||
Box::new(RestorationDBHandler {
|
||||
config: client_db_config,
|
||||
})
|
||||
}
|
||||
|
||||
/// Open a new main DB.
|
||||
pub fn open_db(client_path: &str, cache_config: &CacheConfig, compaction: &DatabaseCompactionProfile) -> io::Result<Arc<BlockChainDB>> {
|
||||
let path = Path::new(client_path);
|
||||
pub fn open_db(
|
||||
client_path: &str,
|
||||
cache_config: &CacheConfig,
|
||||
compaction: &DatabaseCompactionProfile,
|
||||
) -> io::Result<Arc<BlockChainDB>> {
|
||||
let path = Path::new(client_path);
|
||||
|
||||
let db_config = DatabaseConfig {
|
||||
memory_budget: Some(cache_config.blockchain() as usize * 1024 * 1024),
|
||||
compaction: helpers::compaction_profile(&compaction, path),
|
||||
.. DatabaseConfig::with_columns(NUM_COLUMNS)
|
||||
};
|
||||
let db_config = DatabaseConfig {
|
||||
memory_budget: Some(cache_config.blockchain() as usize * 1024 * 1024),
|
||||
compaction: helpers::compaction_profile(&compaction, path),
|
||||
..DatabaseConfig::with_columns(NUM_COLUMNS)
|
||||
};
|
||||
|
||||
open_database(client_path, &db_config)
|
||||
open_database(client_path, &db_config)
|
||||
}
|
||||
|
||||
pub fn open_database(client_path: &str, config: &DatabaseConfig) -> io::Result<Arc<BlockChainDB>> {
|
||||
let path = Path::new(client_path);
|
||||
let path = Path::new(client_path);
|
||||
|
||||
let blooms_path = path.join("blooms");
|
||||
let trace_blooms_path = path.join("trace_blooms");
|
||||
fs::create_dir_all(&blooms_path)?;
|
||||
fs::create_dir_all(&trace_blooms_path)?;
|
||||
let blooms_path = path.join("blooms");
|
||||
let trace_blooms_path = path.join("trace_blooms");
|
||||
fs::create_dir_all(&blooms_path)?;
|
||||
fs::create_dir_all(&trace_blooms_path)?;
|
||||
|
||||
let db = AppDB {
|
||||
key_value: Arc::new(Database::open(&config, client_path)?),
|
||||
blooms: blooms_db::Database::open(blooms_path)?,
|
||||
trace_blooms: blooms_db::Database::open(trace_blooms_path)?,
|
||||
};
|
||||
let db = AppDB {
|
||||
key_value: Arc::new(Database::open(&config, client_path)?),
|
||||
blooms: blooms_db::Database::open(blooms_path)?,
|
||||
trace_blooms: blooms_db::Database::open(trace_blooms_path)?,
|
||||
};
|
||||
|
||||
Ok(Arc::new(db))
|
||||
Ok(Arc::new(db))
|
||||
}
|
||||
|
||||
@@ -14,274 +14,287 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
use cli::Args;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Deprecated {
|
||||
DoesNothing(&'static str),
|
||||
Replaced(&'static str, &'static str),
|
||||
Removed(&'static str),
|
||||
DoesNothing(&'static str),
|
||||
Replaced(&'static str, &'static str),
|
||||
Removed(&'static str),
|
||||
}
|
||||
|
||||
impl fmt::Display for Deprecated {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Deprecated::DoesNothing(s) => write!(f, "Option '{}' does nothing. It's on by default.", s),
|
||||
Deprecated::Replaced(old, new) => write!(f, "Option '{}' is deprecated. Please use '{}' instead.", old, new),
|
||||
Deprecated::Removed(s) => write!(f, "Option '{}' has been removed and is no longer supported.", s)
|
||||
}
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Deprecated::DoesNothing(s) => {
|
||||
write!(f, "Option '{}' does nothing. It's on by default.", s)
|
||||
}
|
||||
Deprecated::Replaced(old, new) => write!(
|
||||
f,
|
||||
"Option '{}' is deprecated. Please use '{}' instead.",
|
||||
old, new
|
||||
),
|
||||
Deprecated::Removed(s) => write!(
|
||||
f,
|
||||
"Option '{}' has been removed and is no longer supported.",
|
||||
s
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_deprecated(args: &Args) -> Vec<Deprecated> {
|
||||
let mut result = vec![];
|
||||
let mut result = vec![];
|
||||
|
||||
// Removed in 1.6 or before.
|
||||
// Removed in 1.6 or before.
|
||||
|
||||
if args.flag_warp {
|
||||
result.push(Deprecated::DoesNothing("--warp"));
|
||||
}
|
||||
if args.flag_warp {
|
||||
result.push(Deprecated::DoesNothing("--warp"));
|
||||
}
|
||||
|
||||
if args.flag_jsonrpc {
|
||||
result.push(Deprecated::DoesNothing("--jsonrpc"));
|
||||
}
|
||||
if args.flag_jsonrpc {
|
||||
result.push(Deprecated::DoesNothing("--jsonrpc"));
|
||||
}
|
||||
|
||||
if args.flag_rpc {
|
||||
result.push(Deprecated::DoesNothing("--rpc"));
|
||||
}
|
||||
if args.flag_rpc {
|
||||
result.push(Deprecated::DoesNothing("--rpc"));
|
||||
}
|
||||
|
||||
if args.flag_jsonrpc_off {
|
||||
result.push(Deprecated::Replaced("--jsonrpc-off", "--no-jsonrpc"));
|
||||
}
|
||||
if args.flag_jsonrpc_off {
|
||||
result.push(Deprecated::Replaced("--jsonrpc-off", "--no-jsonrpc"));
|
||||
}
|
||||
|
||||
if args.flag_webapp {
|
||||
result.push(Deprecated::DoesNothing("--webapp"));
|
||||
}
|
||||
if args.flag_webapp {
|
||||
result.push(Deprecated::DoesNothing("--webapp"));
|
||||
}
|
||||
|
||||
if args.flag_dapps_off {
|
||||
result.push(Deprecated::Replaced("--dapps-off", "--no-dapps"));
|
||||
}
|
||||
if args.flag_dapps_off {
|
||||
result.push(Deprecated::Replaced("--dapps-off", "--no-dapps"));
|
||||
}
|
||||
|
||||
if args.flag_ipcdisable {
|
||||
result.push(Deprecated::Replaced("--ipcdisable", "--no-ipc"));
|
||||
}
|
||||
if args.flag_ipcdisable {
|
||||
result.push(Deprecated::Replaced("--ipcdisable", "--no-ipc"));
|
||||
}
|
||||
|
||||
if args.flag_ipc_off {
|
||||
result.push(Deprecated::Replaced("--ipc-off", "--no-ipc"));
|
||||
}
|
||||
if args.flag_ipc_off {
|
||||
result.push(Deprecated::Replaced("--ipc-off", "--no-ipc"));
|
||||
}
|
||||
|
||||
if args.arg_etherbase.is_some() {
|
||||
result.push(Deprecated::Replaced("--etherbase", "--author"));
|
||||
}
|
||||
if args.arg_etherbase.is_some() {
|
||||
result.push(Deprecated::Replaced("--etherbase", "--author"));
|
||||
}
|
||||
|
||||
if args.arg_extradata.is_some() {
|
||||
result.push(Deprecated::Replaced("--extradata", "--extra-data"));
|
||||
}
|
||||
if args.arg_extradata.is_some() {
|
||||
result.push(Deprecated::Replaced("--extradata", "--extra-data"));
|
||||
}
|
||||
|
||||
if args.flag_testnet {
|
||||
result.push(Deprecated::Replaced("--testnet", "--chain testnet"));
|
||||
}
|
||||
if args.flag_testnet {
|
||||
result.push(Deprecated::Replaced("--testnet", "--chain testnet"));
|
||||
}
|
||||
|
||||
if args.flag_nodiscover {
|
||||
result.push(Deprecated::Replaced("--nodiscover", "--no-discovery"));
|
||||
}
|
||||
if args.flag_nodiscover {
|
||||
result.push(Deprecated::Replaced("--nodiscover", "--no-discovery"));
|
||||
}
|
||||
|
||||
if args.arg_datadir.is_some() {
|
||||
result.push(Deprecated::Replaced("--datadir", "--base-path"));
|
||||
}
|
||||
if args.arg_datadir.is_some() {
|
||||
result.push(Deprecated::Replaced("--datadir", "--base-path"));
|
||||
}
|
||||
|
||||
if args.arg_networkid.is_some() {
|
||||
result.push(Deprecated::Replaced("--networkid", "--network-id"));
|
||||
}
|
||||
if args.arg_networkid.is_some() {
|
||||
result.push(Deprecated::Replaced("--networkid", "--network-id"));
|
||||
}
|
||||
|
||||
if args.arg_peers.is_some() {
|
||||
result.push(Deprecated::Replaced("--peers", "--min-peers"));
|
||||
}
|
||||
if args.arg_peers.is_some() {
|
||||
result.push(Deprecated::Replaced("--peers", "--min-peers"));
|
||||
}
|
||||
|
||||
if args.arg_nodekey.is_some() {
|
||||
result.push(Deprecated::Replaced("--nodekey", "--node-key"));
|
||||
}
|
||||
if args.arg_nodekey.is_some() {
|
||||
result.push(Deprecated::Replaced("--nodekey", "--node-key"));
|
||||
}
|
||||
|
||||
if args.arg_rpcaddr.is_some() {
|
||||
result.push(Deprecated::Replaced("--rpcaddr", "--jsonrpc-interface"));
|
||||
}
|
||||
if args.arg_rpcaddr.is_some() {
|
||||
result.push(Deprecated::Replaced("--rpcaddr", "--jsonrpc-interface"));
|
||||
}
|
||||
|
||||
if args.arg_rpcport.is_some() {
|
||||
result.push(Deprecated::Replaced("--rpcport", "--jsonrpc-port"));
|
||||
}
|
||||
if args.arg_rpcport.is_some() {
|
||||
result.push(Deprecated::Replaced("--rpcport", "--jsonrpc-port"));
|
||||
}
|
||||
|
||||
if args.arg_rpcapi.is_some() {
|
||||
result.push(Deprecated::Replaced("--rpcapi", "--jsonrpc-api"));
|
||||
}
|
||||
if args.arg_rpcapi.is_some() {
|
||||
result.push(Deprecated::Replaced("--rpcapi", "--jsonrpc-api"));
|
||||
}
|
||||
|
||||
if args.arg_rpccorsdomain.is_some() {
|
||||
result.push(Deprecated::Replaced("--rpccorsdomain", "--jsonrpc-cors"));
|
||||
}
|
||||
if args.arg_rpccorsdomain.is_some() {
|
||||
result.push(Deprecated::Replaced("--rpccorsdomain", "--jsonrpc-cors"));
|
||||
}
|
||||
|
||||
if args.arg_ipcapi.is_some() {
|
||||
result.push(Deprecated::Replaced("--ipcapi", "--ipc-apis"));
|
||||
}
|
||||
if args.arg_ipcapi.is_some() {
|
||||
result.push(Deprecated::Replaced("--ipcapi", "--ipc-apis"));
|
||||
}
|
||||
|
||||
if args.arg_ipcpath.is_some() {
|
||||
result.push(Deprecated::Replaced("--ipcpath", "--ipc-path"));
|
||||
}
|
||||
if args.arg_ipcpath.is_some() {
|
||||
result.push(Deprecated::Replaced("--ipcpath", "--ipc-path"));
|
||||
}
|
||||
|
||||
if args.arg_gasprice.is_some() {
|
||||
result.push(Deprecated::Replaced("--gasprice", "--min-gas-price"));
|
||||
}
|
||||
if args.arg_gasprice.is_some() {
|
||||
result.push(Deprecated::Replaced("--gasprice", "--min-gas-price"));
|
||||
}
|
||||
|
||||
if args.arg_cache.is_some() {
|
||||
result.push(Deprecated::Replaced("--cache", "--cache-size"));
|
||||
}
|
||||
if args.arg_cache.is_some() {
|
||||
result.push(Deprecated::Replaced("--cache", "--cache-size"));
|
||||
}
|
||||
|
||||
// Removed in 1.7.
|
||||
// Removed in 1.7.
|
||||
|
||||
if args.arg_dapps_port.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-port"));
|
||||
}
|
||||
if args.arg_dapps_port.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-port"));
|
||||
}
|
||||
|
||||
if args.arg_dapps_interface.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-interface"));
|
||||
}
|
||||
if args.arg_dapps_interface.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-interface"));
|
||||
}
|
||||
|
||||
if args.arg_dapps_hosts.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-hosts"));
|
||||
}
|
||||
if args.arg_dapps_hosts.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-hosts"));
|
||||
}
|
||||
|
||||
if args.arg_dapps_cors.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-cors"));
|
||||
}
|
||||
if args.arg_dapps_cors.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-cors"));
|
||||
}
|
||||
|
||||
if args.arg_dapps_user.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-user"));
|
||||
}
|
||||
if args.arg_dapps_user.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-user"));
|
||||
}
|
||||
|
||||
if args.arg_dapps_pass.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-pass"));
|
||||
}
|
||||
if args.arg_dapps_pass.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-pass"));
|
||||
}
|
||||
|
||||
if args.flag_dapps_apis_all {
|
||||
result.push(Deprecated::Replaced("--dapps-apis-all", "--jsonrpc-apis"));
|
||||
}
|
||||
if args.flag_dapps_apis_all {
|
||||
result.push(Deprecated::Replaced("--dapps-apis-all", "--jsonrpc-apis"));
|
||||
}
|
||||
|
||||
// Removed in 1.11.
|
||||
// Removed in 1.11.
|
||||
|
||||
if args.flag_public_node {
|
||||
result.push(Deprecated::Removed("--public-node"));
|
||||
}
|
||||
if args.flag_public_node {
|
||||
result.push(Deprecated::Removed("--public-node"));
|
||||
}
|
||||
|
||||
if args.flag_force_ui {
|
||||
result.push(Deprecated::Removed("--force-ui"));
|
||||
}
|
||||
if args.flag_force_ui {
|
||||
result.push(Deprecated::Removed("--force-ui"));
|
||||
}
|
||||
|
||||
if args.flag_no_ui {
|
||||
result.push(Deprecated::Removed("--no-ui"));
|
||||
}
|
||||
if args.flag_no_ui {
|
||||
result.push(Deprecated::Removed("--no-ui"));
|
||||
}
|
||||
|
||||
if args.flag_ui_no_validation {
|
||||
result.push(Deprecated::Removed("--ui-no-validation"));
|
||||
}
|
||||
if args.flag_ui_no_validation {
|
||||
result.push(Deprecated::Removed("--ui-no-validation"));
|
||||
}
|
||||
|
||||
if args.arg_ui_interface.is_some() {
|
||||
result.push(Deprecated::Removed("--ui-interface"));
|
||||
}
|
||||
if args.arg_ui_interface.is_some() {
|
||||
result.push(Deprecated::Removed("--ui-interface"));
|
||||
}
|
||||
|
||||
if args.arg_ui_hosts.is_some() {
|
||||
result.push(Deprecated::Removed("--ui-hosts"));
|
||||
}
|
||||
if args.arg_ui_hosts.is_some() {
|
||||
result.push(Deprecated::Removed("--ui-hosts"));
|
||||
}
|
||||
|
||||
if args.arg_ui_port.is_some() {
|
||||
result.push(Deprecated::Removed("--ui-port"));
|
||||
}
|
||||
if args.arg_ui_port.is_some() {
|
||||
result.push(Deprecated::Removed("--ui-port"));
|
||||
}
|
||||
|
||||
if args.arg_tx_queue_ban_count.is_some() {
|
||||
result.push(Deprecated::Removed("--tx-queue-ban-count"));
|
||||
}
|
||||
if args.arg_tx_queue_ban_count.is_some() {
|
||||
result.push(Deprecated::Removed("--tx-queue-ban-count"));
|
||||
}
|
||||
|
||||
if args.arg_tx_queue_ban_time.is_some() {
|
||||
result.push(Deprecated::Removed("--tx-queue-ban-time"));
|
||||
}
|
||||
if args.arg_tx_queue_ban_time.is_some() {
|
||||
result.push(Deprecated::Removed("--tx-queue-ban-time"));
|
||||
}
|
||||
|
||||
// Removed in 2.0.
|
||||
// Removed in 2.0.
|
||||
|
||||
if args.flag_fast_and_loose {
|
||||
result.push(Deprecated::Removed("--fast-and-loose"));
|
||||
}
|
||||
if args.flag_fast_and_loose {
|
||||
result.push(Deprecated::Removed("--fast-and-loose"));
|
||||
}
|
||||
|
||||
if args.cmd_dapp {
|
||||
result.push(Deprecated::Removed("parity dapp"));
|
||||
}
|
||||
if args.cmd_dapp {
|
||||
result.push(Deprecated::Removed("parity dapp"));
|
||||
}
|
||||
|
||||
if args.arg_dapp_path.is_some() {
|
||||
result.push(Deprecated::Removed("--dapp-path"));
|
||||
}
|
||||
if args.arg_dapp_path.is_some() {
|
||||
result.push(Deprecated::Removed("--dapp-path"));
|
||||
}
|
||||
|
||||
if args.flag_no_dapps {
|
||||
result.push(Deprecated::Removed("--no-dapps"));
|
||||
}
|
||||
if args.flag_no_dapps {
|
||||
result.push(Deprecated::Removed("--no-dapps"));
|
||||
}
|
||||
|
||||
if args.arg_dapps_path.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-path"));
|
||||
}
|
||||
if args.arg_dapps_path.is_some() {
|
||||
result.push(Deprecated::Removed("--dapps-path"));
|
||||
}
|
||||
|
||||
if args.arg_ntp_servers.is_some() {
|
||||
result.push(Deprecated::Removed("--ntp-servers"));
|
||||
}
|
||||
if args.arg_ntp_servers.is_some() {
|
||||
result.push(Deprecated::Removed("--ntp-servers"));
|
||||
}
|
||||
|
||||
result
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use cli::Args;
|
||||
use super::{Deprecated, find_deprecated};
|
||||
use super::{find_deprecated, Deprecated};
|
||||
use cli::Args;
|
||||
|
||||
#[test]
|
||||
fn test_find_deprecated() {
|
||||
assert_eq!(find_deprecated(&Args::default()), vec![]);
|
||||
assert_eq!(find_deprecated(&{
|
||||
let mut args = Args::default();
|
||||
args.flag_warp = true;
|
||||
args.flag_jsonrpc = true;
|
||||
args.flag_rpc = true;
|
||||
args.flag_jsonrpc_off = true;
|
||||
args.flag_webapp = true;
|
||||
args.flag_dapps_off = true;
|
||||
args.flag_ipcdisable = true;
|
||||
args.flag_ipc_off = true;
|
||||
args.arg_etherbase = Some(Default::default());
|
||||
args.arg_extradata = Some(Default::default());
|
||||
args.arg_dapps_port = Some(Default::default());
|
||||
args.arg_dapps_interface = Some(Default::default());
|
||||
args.arg_dapps_hosts = Some(Default::default());
|
||||
args.arg_dapps_cors = Some(Default::default());
|
||||
args.arg_dapps_user = Some(Default::default());
|
||||
args.arg_dapps_pass = Some(Default::default());
|
||||
args.flag_dapps_apis_all = true;
|
||||
args.flag_fast_and_loose = true;
|
||||
args.arg_ntp_servers = Some(Default::default());
|
||||
args
|
||||
}), vec![
|
||||
Deprecated::DoesNothing("--warp"),
|
||||
Deprecated::DoesNothing("--jsonrpc"),
|
||||
Deprecated::DoesNothing("--rpc"),
|
||||
Deprecated::Replaced("--jsonrpc-off", "--no-jsonrpc"),
|
||||
Deprecated::DoesNothing("--webapp"),
|
||||
Deprecated::Replaced("--dapps-off", "--no-dapps"),
|
||||
Deprecated::Replaced("--ipcdisable", "--no-ipc"),
|
||||
Deprecated::Replaced("--ipc-off", "--no-ipc"),
|
||||
Deprecated::Replaced("--etherbase", "--author"),
|
||||
Deprecated::Replaced("--extradata", "--extra-data"),
|
||||
Deprecated::Removed("--dapps-port"),
|
||||
Deprecated::Removed("--dapps-interface"),
|
||||
Deprecated::Removed("--dapps-hosts"),
|
||||
Deprecated::Removed("--dapps-cors"),
|
||||
Deprecated::Removed("--dapps-user"),
|
||||
Deprecated::Removed("--dapps-pass"),
|
||||
Deprecated::Replaced("--dapps-apis-all", "--jsonrpc-apis"),
|
||||
Deprecated::Removed("--fast-and-loose"),
|
||||
Deprecated::Removed("--ntp-servers"),
|
||||
]);
|
||||
}
|
||||
#[test]
|
||||
fn test_find_deprecated() {
|
||||
assert_eq!(find_deprecated(&Args::default()), vec![]);
|
||||
assert_eq!(
|
||||
find_deprecated(&{
|
||||
let mut args = Args::default();
|
||||
args.flag_warp = true;
|
||||
args.flag_jsonrpc = true;
|
||||
args.flag_rpc = true;
|
||||
args.flag_jsonrpc_off = true;
|
||||
args.flag_webapp = true;
|
||||
args.flag_dapps_off = true;
|
||||
args.flag_ipcdisable = true;
|
||||
args.flag_ipc_off = true;
|
||||
args.arg_etherbase = Some(Default::default());
|
||||
args.arg_extradata = Some(Default::default());
|
||||
args.arg_dapps_port = Some(Default::default());
|
||||
args.arg_dapps_interface = Some(Default::default());
|
||||
args.arg_dapps_hosts = Some(Default::default());
|
||||
args.arg_dapps_cors = Some(Default::default());
|
||||
args.arg_dapps_user = Some(Default::default());
|
||||
args.arg_dapps_pass = Some(Default::default());
|
||||
args.flag_dapps_apis_all = true;
|
||||
args.flag_fast_and_loose = true;
|
||||
args.arg_ntp_servers = Some(Default::default());
|
||||
args
|
||||
}),
|
||||
vec![
|
||||
Deprecated::DoesNothing("--warp"),
|
||||
Deprecated::DoesNothing("--jsonrpc"),
|
||||
Deprecated::DoesNothing("--rpc"),
|
||||
Deprecated::Replaced("--jsonrpc-off", "--no-jsonrpc"),
|
||||
Deprecated::DoesNothing("--webapp"),
|
||||
Deprecated::Replaced("--dapps-off", "--no-dapps"),
|
||||
Deprecated::Replaced("--ipcdisable", "--no-ipc"),
|
||||
Deprecated::Replaced("--ipc-off", "--no-ipc"),
|
||||
Deprecated::Replaced("--etherbase", "--author"),
|
||||
Deprecated::Replaced("--extradata", "--extra-data"),
|
||||
Deprecated::Removed("--dapps-port"),
|
||||
Deprecated::Removed("--dapps-interface"),
|
||||
Deprecated::Removed("--dapps-hosts"),
|
||||
Deprecated::Removed("--dapps-cors"),
|
||||
Deprecated::Removed("--dapps-user"),
|
||||
Deprecated::Removed("--dapps-pass"),
|
||||
Deprecated::Replaced("--dapps-apis-all", "--jsonrpc-apis"),
|
||||
Deprecated::Removed("--fast-and-loose"),
|
||||
Deprecated::Removed("--ntp-servers"),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,20 +14,20 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use ethcore::client::DatabaseCompactionProfile;
|
||||
use ethcore::spec::{SpecParams, OptimizeFor};
|
||||
use light::client::fetch::Unavailable as UnavailableDataFetcher;
|
||||
use light::Cache as LightDataCache;
|
||||
use ethcore::{
|
||||
client::DatabaseCompactionProfile,
|
||||
spec::{OptimizeFor, SpecParams},
|
||||
};
|
||||
use light::{client::fetch::Unavailable as UnavailableDataFetcher, Cache as LightDataCache};
|
||||
|
||||
use params::{SpecType, Pruning};
|
||||
use helpers::execute_upgrades;
|
||||
use dir::Directories;
|
||||
use cache::CacheConfig;
|
||||
use user_defaults::UserDefaults;
|
||||
use db;
|
||||
use dir::Directories;
|
||||
use helpers::execute_upgrades;
|
||||
use params::{Pruning, SpecType};
|
||||
use user_defaults::UserDefaults;
|
||||
|
||||
// Number of minutes before a given gas price corpus should expire.
|
||||
// Light client only.
|
||||
@@ -35,69 +35,87 @@ const GAS_CORPUS_EXPIRATION_MINUTES: u64 = 60 * 6;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ExportHsyncCmd {
|
||||
pub cache_config: CacheConfig,
|
||||
pub dirs: Directories,
|
||||
pub spec: SpecType,
|
||||
pub pruning: Pruning,
|
||||
pub compaction: DatabaseCompactionProfile,
|
||||
pub cache_config: CacheConfig,
|
||||
pub dirs: Directories,
|
||||
pub spec: SpecType,
|
||||
pub pruning: Pruning,
|
||||
pub compaction: DatabaseCompactionProfile,
|
||||
}
|
||||
|
||||
pub fn execute(cmd: ExportHsyncCmd) -> Result<String, String> {
|
||||
use light::client as light_client;
|
||||
use parking_lot::Mutex;
|
||||
use light::client as light_client;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
// load spec
|
||||
let spec = cmd.spec.spec(SpecParams::new(cmd.dirs.cache.as_ref(), OptimizeFor::Memory))?;
|
||||
// load spec
|
||||
let spec = cmd.spec.spec(SpecParams::new(
|
||||
cmd.dirs.cache.as_ref(),
|
||||
OptimizeFor::Memory,
|
||||
))?;
|
||||
|
||||
// load genesis hash
|
||||
let genesis_hash = spec.genesis_header().hash();
|
||||
// load genesis hash
|
||||
let genesis_hash = spec.genesis_header().hash();
|
||||
|
||||
// database paths
|
||||
let db_dirs = cmd.dirs.database(genesis_hash, cmd.spec.legacy_fork_name(), spec.data_dir.clone());
|
||||
// database paths
|
||||
let db_dirs = cmd.dirs.database(
|
||||
genesis_hash,
|
||||
cmd.spec.legacy_fork_name(),
|
||||
spec.data_dir.clone(),
|
||||
);
|
||||
|
||||
// user defaults path
|
||||
let user_defaults_path = db_dirs.user_defaults_path();
|
||||
// user defaults path
|
||||
let user_defaults_path = db_dirs.user_defaults_path();
|
||||
|
||||
// load user defaults
|
||||
let user_defaults = UserDefaults::load(&user_defaults_path)?;
|
||||
// load user defaults
|
||||
let user_defaults = UserDefaults::load(&user_defaults_path)?;
|
||||
|
||||
// select pruning algorithm
|
||||
let algorithm = cmd.pruning.to_algorithm(&user_defaults);
|
||||
// select pruning algorithm
|
||||
let algorithm = cmd.pruning.to_algorithm(&user_defaults);
|
||||
|
||||
// execute upgrades
|
||||
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?;
|
||||
// execute upgrades
|
||||
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?;
|
||||
|
||||
// create dirs used by parity
|
||||
cmd.dirs.create_dirs(false, false)?;
|
||||
// create dirs used by parity
|
||||
cmd.dirs.create_dirs(false, false)?;
|
||||
|
||||
// TODO: configurable cache size.
|
||||
let cache = LightDataCache::new(Default::default(), Duration::from_secs(60 * GAS_CORPUS_EXPIRATION_MINUTES));
|
||||
let cache = Arc::new(Mutex::new(cache));
|
||||
// TODO: configurable cache size.
|
||||
let cache = LightDataCache::new(
|
||||
Default::default(),
|
||||
Duration::from_secs(60 * GAS_CORPUS_EXPIRATION_MINUTES),
|
||||
);
|
||||
let cache = Arc::new(Mutex::new(cache));
|
||||
|
||||
// start client and create transaction queue.
|
||||
let mut config = light_client::Config {
|
||||
queue: Default::default(),
|
||||
chain_column: ::ethcore_db::COL_LIGHT_CHAIN,
|
||||
verify_full: true,
|
||||
check_seal: true,
|
||||
no_hardcoded_sync: true,
|
||||
};
|
||||
// start client and create transaction queue.
|
||||
let mut config = light_client::Config {
|
||||
queue: Default::default(),
|
||||
chain_column: ::ethcore_db::COL_LIGHT_CHAIN,
|
||||
verify_full: true,
|
||||
check_seal: true,
|
||||
no_hardcoded_sync: true,
|
||||
};
|
||||
|
||||
config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024;
|
||||
config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024;
|
||||
|
||||
// initialize database.
|
||||
let db = db::open_db(&db_dirs.client_path(algorithm).to_str().expect("DB path could not be converted to string."),
|
||||
&cmd.cache_config,
|
||||
&cmd.compaction).map_err(|e| format!("Failed to open database {:?}", e))?;
|
||||
// initialize database.
|
||||
let db = db::open_db(
|
||||
&db_dirs
|
||||
.client_path(algorithm)
|
||||
.to_str()
|
||||
.expect("DB path could not be converted to string."),
|
||||
&cmd.cache_config,
|
||||
&cmd.compaction,
|
||||
)
|
||||
.map_err(|e| format!("Failed to open database {:?}", e))?;
|
||||
|
||||
let service = light_client::Service::start(config, &spec, UnavailableDataFetcher, db, cache)
|
||||
.map_err(|e| format!("Error starting light client: {}", e))?;
|
||||
let service = light_client::Service::start(config, &spec, UnavailableDataFetcher, db, cache)
|
||||
.map_err(|e| format!("Error starting light client: {}", e))?;
|
||||
|
||||
let hs = service.client().read_hardcoded_sync()
|
||||
.map_err(|e| format!("Error reading hardcoded sync: {}", e))?;
|
||||
if let Some(hs) = hs {
|
||||
Ok(::serde_json::to_string_pretty(&hs.to_json()).expect("generated JSON is always valid"))
|
||||
} else {
|
||||
Err("Error: cannot generate hardcoded sync because the database is empty.".into())
|
||||
}
|
||||
let hs = service
|
||||
.client()
|
||||
.read_hardcoded_sync()
|
||||
.map_err(|e| format!("Error reading hardcoded sync: {}", e))?;
|
||||
if let Some(hs) = hs {
|
||||
Ok(::serde_json::to_string_pretty(&hs.to_json()).expect("generated JSON is always valid"))
|
||||
} else {
|
||||
Err("Error: cannot generate hardcoded sync because the database is empty.".into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,311 +14,369 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::io;
|
||||
use std::io::{Write, BufReader, BufRead};
|
||||
use std::time::Duration;
|
||||
use std::fs::File;
|
||||
use std::collections::HashSet;
|
||||
use ethereum_types::{U256, clean_0x, Address};
|
||||
use journaldb::Algorithm;
|
||||
use ethcore::client::{Mode, BlockId, VMType, DatabaseCompactionProfile, ClientConfig, VerifierType};
|
||||
use ethcore::miner::{PendingSet, Penalization};
|
||||
use miner::pool::PrioritizationStrategy;
|
||||
use cache::CacheConfig;
|
||||
use dir::DatabaseDirectories;
|
||||
use dir::helpers::replace_home;
|
||||
use upgrade::{upgrade, upgrade_data_paths};
|
||||
use sync::{validate_node_url, self};
|
||||
use db::migrate;
|
||||
use path;
|
||||
use dir::{helpers::replace_home, DatabaseDirectories};
|
||||
use ethcore::{
|
||||
client::{BlockId, ClientConfig, DatabaseCompactionProfile, Mode, VMType, VerifierType},
|
||||
miner::{Penalization, PendingSet},
|
||||
};
|
||||
use ethereum_types::{clean_0x, Address, U256};
|
||||
use ethkey::Password;
|
||||
use journaldb::Algorithm;
|
||||
use miner::pool::PrioritizationStrategy;
|
||||
use path;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fs::File,
|
||||
io,
|
||||
io::{BufRead, BufReader, Write},
|
||||
time::Duration,
|
||||
};
|
||||
use sync::{self, validate_node_url};
|
||||
use upgrade::{upgrade, upgrade_data_paths};
|
||||
|
||||
pub fn to_duration(s: &str) -> Result<Duration, String> {
|
||||
to_seconds(s).map(Duration::from_secs)
|
||||
to_seconds(s).map(Duration::from_secs)
|
||||
}
|
||||
|
||||
fn to_seconds(s: &str) -> Result<u64, String> {
|
||||
let bad = |_| {
|
||||
format!("{}: Invalid duration given. See parity --help for more information.", s)
|
||||
};
|
||||
let bad = |_| {
|
||||
format!(
|
||||
"{}: Invalid duration given. See parity --help for more information.",
|
||||
s
|
||||
)
|
||||
};
|
||||
|
||||
match s {
|
||||
"twice-daily" => Ok(12 * 60 * 60),
|
||||
"half-hourly" => Ok(30 * 60),
|
||||
"1second" | "1 second" | "second" => Ok(1),
|
||||
"1minute" | "1 minute" | "minute" => Ok(60),
|
||||
"hourly" | "1hour" | "1 hour" | "hour" => Ok(60 * 60),
|
||||
"daily" | "1day" | "1 day" | "day" => Ok(24 * 60 * 60),
|
||||
x if x.ends_with("seconds") => x[0..x.len() - 7].trim().parse().map_err(bad),
|
||||
x if x.ends_with("minutes") => x[0..x.len() - 7].trim().parse::<u64>().map_err(bad).map(|x| x * 60),
|
||||
x if x.ends_with("hours") => x[0..x.len() - 5].trim().parse::<u64>().map_err(bad).map(|x| x * 60 * 60),
|
||||
x if x.ends_with("days") => x[0..x.len() - 4].trim().parse::<u64>().map_err(bad).map(|x| x * 24 * 60 * 60),
|
||||
x => x.trim().parse().map_err(bad),
|
||||
}
|
||||
match s {
|
||||
"twice-daily" => Ok(12 * 60 * 60),
|
||||
"half-hourly" => Ok(30 * 60),
|
||||
"1second" | "1 second" | "second" => Ok(1),
|
||||
"1minute" | "1 minute" | "minute" => Ok(60),
|
||||
"hourly" | "1hour" | "1 hour" | "hour" => Ok(60 * 60),
|
||||
"daily" | "1day" | "1 day" | "day" => Ok(24 * 60 * 60),
|
||||
x if x.ends_with("seconds") => x[0..x.len() - 7].trim().parse().map_err(bad),
|
||||
x if x.ends_with("minutes") => x[0..x.len() - 7]
|
||||
.trim()
|
||||
.parse::<u64>()
|
||||
.map_err(bad)
|
||||
.map(|x| x * 60),
|
||||
x if x.ends_with("hours") => x[0..x.len() - 5]
|
||||
.trim()
|
||||
.parse::<u64>()
|
||||
.map_err(bad)
|
||||
.map(|x| x * 60 * 60),
|
||||
x if x.ends_with("days") => x[0..x.len() - 4]
|
||||
.trim()
|
||||
.parse::<u64>()
|
||||
.map_err(bad)
|
||||
.map(|x| x * 24 * 60 * 60),
|
||||
x => x.trim().parse().map_err(bad),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_mode(s: &str, timeout: u64, alarm: u64) -> Result<Mode, String> {
|
||||
match s {
|
||||
"active" => Ok(Mode::Active),
|
||||
"passive" => Ok(Mode::Passive(Duration::from_secs(timeout), Duration::from_secs(alarm))),
|
||||
"dark" => Ok(Mode::Dark(Duration::from_secs(timeout))),
|
||||
"offline" => Ok(Mode::Off),
|
||||
_ => Err(format!("{}: Invalid value for --mode. Must be one of active, passive, dark or offline.", s)),
|
||||
}
|
||||
match s {
|
||||
"active" => Ok(Mode::Active),
|
||||
"passive" => Ok(Mode::Passive(
|
||||
Duration::from_secs(timeout),
|
||||
Duration::from_secs(alarm),
|
||||
)),
|
||||
"dark" => Ok(Mode::Dark(Duration::from_secs(timeout))),
|
||||
"offline" => Ok(Mode::Off),
|
||||
_ => Err(format!(
|
||||
"{}: Invalid value for --mode. Must be one of active, passive, dark or offline.",
|
||||
s
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_block_id(s: &str) -> Result<BlockId, String> {
|
||||
if s == "latest" {
|
||||
Ok(BlockId::Latest)
|
||||
} else if let Ok(num) = s.parse() {
|
||||
Ok(BlockId::Number(num))
|
||||
} else if let Ok(hash) = s.parse() {
|
||||
Ok(BlockId::Hash(hash))
|
||||
} else {
|
||||
Err("Invalid block.".into())
|
||||
}
|
||||
if s == "latest" {
|
||||
Ok(BlockId::Latest)
|
||||
} else if let Ok(num) = s.parse() {
|
||||
Ok(BlockId::Number(num))
|
||||
} else if let Ok(hash) = s.parse() {
|
||||
Ok(BlockId::Hash(hash))
|
||||
} else {
|
||||
Err("Invalid block.".into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_u256(s: &str) -> Result<U256, String> {
|
||||
if let Ok(decimal) = U256::from_dec_str(s) {
|
||||
Ok(decimal)
|
||||
} else if let Ok(hex) = clean_0x(s).parse() {
|
||||
Ok(hex)
|
||||
} else {
|
||||
Err(format!("Invalid numeric value: {}", s))
|
||||
}
|
||||
if let Ok(decimal) = U256::from_dec_str(s) {
|
||||
Ok(decimal)
|
||||
} else if let Ok(hex) = clean_0x(s).parse() {
|
||||
Ok(hex)
|
||||
} else {
|
||||
Err(format!("Invalid numeric value: {}", s))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_pending_set(s: &str) -> Result<PendingSet, String> {
|
||||
match s {
|
||||
"cheap" => Ok(PendingSet::AlwaysQueue),
|
||||
"strict" => Ok(PendingSet::AlwaysSealing),
|
||||
"lenient" => Ok(PendingSet::SealingOrElseQueue),
|
||||
other => Err(format!("Invalid pending set value: {:?}", other)),
|
||||
}
|
||||
match s {
|
||||
"cheap" => Ok(PendingSet::AlwaysQueue),
|
||||
"strict" => Ok(PendingSet::AlwaysSealing),
|
||||
"lenient" => Ok(PendingSet::SealingOrElseQueue),
|
||||
other => Err(format!("Invalid pending set value: {:?}", other)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_queue_strategy(s: &str) -> Result<PrioritizationStrategy, String> {
|
||||
match s {
|
||||
"gas_price" => Ok(PrioritizationStrategy::GasPriceOnly),
|
||||
other => Err(format!("Invalid queue strategy: {}", other)),
|
||||
}
|
||||
match s {
|
||||
"gas_price" => Ok(PrioritizationStrategy::GasPriceOnly),
|
||||
other => Err(format!("Invalid queue strategy: {}", other)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_queue_penalization(time: Option<u64>) -> Result<Penalization, String> {
|
||||
Ok(match time {
|
||||
Some(threshold_ms) => Penalization::Enabled {
|
||||
offend_threshold: Duration::from_millis(threshold_ms),
|
||||
},
|
||||
None => Penalization::Disabled,
|
||||
})
|
||||
Ok(match time {
|
||||
Some(threshold_ms) => Penalization::Enabled {
|
||||
offend_threshold: Duration::from_millis(threshold_ms),
|
||||
},
|
||||
None => Penalization::Disabled,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_address(s: Option<String>) -> Result<Address, String> {
|
||||
match s {
|
||||
Some(ref a) => clean_0x(a).parse().map_err(|_| format!("Invalid address: {:?}", a)),
|
||||
None => Ok(Address::default())
|
||||
}
|
||||
match s {
|
||||
Some(ref a) => clean_0x(a)
|
||||
.parse()
|
||||
.map_err(|_| format!("Invalid address: {:?}", a)),
|
||||
None => Ok(Address::default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_addresses(s: &Option<String>) -> Result<Vec<Address>, String> {
|
||||
match *s {
|
||||
Some(ref adds) if !adds.is_empty() => adds.split(',')
|
||||
.map(|a| clean_0x(a).parse().map_err(|_| format!("Invalid address: {:?}", a)))
|
||||
.collect(),
|
||||
_ => Ok(Vec::new()),
|
||||
}
|
||||
match *s {
|
||||
Some(ref adds) if !adds.is_empty() => adds
|
||||
.split(',')
|
||||
.map(|a| {
|
||||
clean_0x(a)
|
||||
.parse()
|
||||
.map_err(|_| format!("Invalid address: {:?}", a))
|
||||
})
|
||||
.collect(),
|
||||
_ => Ok(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to parse string as a price.
|
||||
pub fn to_price(s: &str) -> Result<f32, String> {
|
||||
s.parse::<f32>().map_err(|_| format!("Invalid transaction price {:?} given. Must be a decimal number.", s))
|
||||
s.parse::<f32>().map_err(|_| {
|
||||
format!(
|
||||
"Invalid transaction price {:?} given. Must be a decimal number.",
|
||||
s
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn join_set(set: Option<&HashSet<String>>) -> Option<String> {
|
||||
match set {
|
||||
Some(s) => Some(s.iter().map(|s| s.as_str()).collect::<Vec<&str>>().join(",")),
|
||||
None => None
|
||||
}
|
||||
match set {
|
||||
Some(s) => Some(
|
||||
s.iter()
|
||||
.map(|s| s.as_str())
|
||||
.collect::<Vec<&str>>()
|
||||
.join(","),
|
||||
),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Flush output buffer.
|
||||
pub fn flush_stdout() {
|
||||
io::stdout().flush().expect("stdout is flushable; qed");
|
||||
io::stdout().flush().expect("stdout is flushable; qed");
|
||||
}
|
||||
|
||||
/// Returns default geth ipc path.
|
||||
pub fn geth_ipc_path(testnet: bool) -> String {
|
||||
// Windows path should not be hardcoded here.
|
||||
// Instead it should be a part of path::ethereum
|
||||
if cfg!(windows) {
|
||||
return r"\\.\pipe\geth.ipc".to_owned();
|
||||
}
|
||||
// Windows path should not be hardcoded here.
|
||||
// Instead it should be a part of path::ethereum
|
||||
if cfg!(windows) {
|
||||
return r"\\.\pipe\geth.ipc".to_owned();
|
||||
}
|
||||
|
||||
if testnet {
|
||||
path::ethereum::with_testnet("geth.ipc").to_str().unwrap().to_owned()
|
||||
} else {
|
||||
path::ethereum::with_default("geth.ipc").to_str().unwrap().to_owned()
|
||||
}
|
||||
if testnet {
|
||||
path::ethereum::with_testnet("geth.ipc")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned()
|
||||
} else {
|
||||
path::ethereum::with_default("geth.ipc")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats and returns parity ipc path.
|
||||
pub fn parity_ipc_path(base: &str, path: &str, shift: u16) -> String {
|
||||
let mut path = path.to_owned();
|
||||
if shift != 0 {
|
||||
path = path.replace("jsonrpc.ipc", &format!("jsonrpc-{}.ipc", shift));
|
||||
}
|
||||
replace_home(base, &path)
|
||||
let mut path = path.to_owned();
|
||||
if shift != 0 {
|
||||
path = path.replace("jsonrpc.ipc", &format!("jsonrpc-{}.ipc", shift));
|
||||
}
|
||||
replace_home(base, &path)
|
||||
}
|
||||
|
||||
/// Validates and formats bootnodes option.
|
||||
pub fn to_bootnodes(bootnodes: &Option<String>) -> Result<Vec<String>, String> {
|
||||
match *bootnodes {
|
||||
Some(ref x) if !x.is_empty() => x.split(',').map(|s| {
|
||||
match validate_node_url(s).map(Into::into) {
|
||||
None => Ok(s.to_owned()),
|
||||
Some(sync::ErrorKind::AddressResolve(_)) => Err(format!("Failed to resolve hostname of a boot node: {}", s)),
|
||||
Some(_) => Err(format!("Invalid node address format given for a boot node: {}", s)),
|
||||
}
|
||||
}).collect(),
|
||||
Some(_) => Ok(vec![]),
|
||||
None => Ok(vec![])
|
||||
}
|
||||
match *bootnodes {
|
||||
Some(ref x) if !x.is_empty() => x
|
||||
.split(',')
|
||||
.map(|s| match validate_node_url(s).map(Into::into) {
|
||||
None => Ok(s.to_owned()),
|
||||
Some(sync::ErrorKind::AddressResolve(_)) => {
|
||||
Err(format!("Failed to resolve hostname of a boot node: {}", s))
|
||||
}
|
||||
Some(_) => Err(format!(
|
||||
"Invalid node address format given for a boot node: {}",
|
||||
s
|
||||
)),
|
||||
})
|
||||
.collect(),
|
||||
Some(_) => Ok(vec![]),
|
||||
None => Ok(vec![]),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn default_network_config() -> ::sync::NetworkConfiguration {
|
||||
use sync::{NetworkConfiguration};
|
||||
use super::network::IpFilter;
|
||||
NetworkConfiguration {
|
||||
config_path: Some(replace_home(&::dir::default_data_path(), "$BASE/network")),
|
||||
net_config_path: None,
|
||||
listen_address: Some("0.0.0.0:30303".into()),
|
||||
public_address: None,
|
||||
udp_port: None,
|
||||
nat_enabled: true,
|
||||
discovery_enabled: true,
|
||||
boot_nodes: Vec::new(),
|
||||
use_secret: None,
|
||||
max_peers: 50,
|
||||
min_peers: 25,
|
||||
snapshot_peers: 0,
|
||||
max_pending_peers: 64,
|
||||
ip_filter: IpFilter::default(),
|
||||
reserved_nodes: Vec::new(),
|
||||
allow_non_reserved: true,
|
||||
client_version: ::parity_version::version(),
|
||||
}
|
||||
use super::network::IpFilter;
|
||||
use sync::NetworkConfiguration;
|
||||
NetworkConfiguration {
|
||||
config_path: Some(replace_home(&::dir::default_data_path(), "$BASE/network")),
|
||||
net_config_path: None,
|
||||
listen_address: Some("0.0.0.0:30303".into()),
|
||||
public_address: None,
|
||||
udp_port: None,
|
||||
nat_enabled: true,
|
||||
discovery_enabled: true,
|
||||
boot_nodes: Vec::new(),
|
||||
use_secret: None,
|
||||
max_peers: 50,
|
||||
min_peers: 25,
|
||||
snapshot_peers: 0,
|
||||
max_pending_peers: 64,
|
||||
ip_filter: IpFilter::default(),
|
||||
reserved_nodes: Vec::new(),
|
||||
allow_non_reserved: true,
|
||||
client_version: ::parity_version::version(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_client_config(
|
||||
cache_config: &CacheConfig,
|
||||
spec_name: String,
|
||||
mode: Mode,
|
||||
tracing: bool,
|
||||
fat_db: bool,
|
||||
compaction: DatabaseCompactionProfile,
|
||||
vm_type: VMType,
|
||||
name: String,
|
||||
pruning: Algorithm,
|
||||
pruning_history: u64,
|
||||
pruning_memory: usize,
|
||||
check_seal: bool,
|
||||
max_round_blocks_to_import: usize,
|
||||
cache_config: &CacheConfig,
|
||||
spec_name: String,
|
||||
mode: Mode,
|
||||
tracing: bool,
|
||||
fat_db: bool,
|
||||
compaction: DatabaseCompactionProfile,
|
||||
vm_type: VMType,
|
||||
name: String,
|
||||
pruning: Algorithm,
|
||||
pruning_history: u64,
|
||||
pruning_memory: usize,
|
||||
check_seal: bool,
|
||||
max_round_blocks_to_import: usize,
|
||||
) -> ClientConfig {
|
||||
let mut client_config = ClientConfig::default();
|
||||
let mut client_config = ClientConfig::default();
|
||||
|
||||
let mb = 1024 * 1024;
|
||||
// in bytes
|
||||
client_config.blockchain.max_cache_size = cache_config.blockchain() as usize * mb;
|
||||
// in bytes
|
||||
client_config.blockchain.pref_cache_size = cache_config.blockchain() as usize * 3 / 4 * mb;
|
||||
// db cache size, in megabytes
|
||||
client_config.db_cache_size = Some(cache_config.db_cache_size() as usize);
|
||||
// db queue cache size, in bytes
|
||||
client_config.queue.max_mem_use = cache_config.queue() as usize * mb;
|
||||
// in bytes
|
||||
client_config.tracing.max_cache_size = cache_config.traces() as usize * mb;
|
||||
// in bytes
|
||||
client_config.tracing.pref_cache_size = cache_config.traces() as usize * 3 / 4 * mb;
|
||||
// in bytes
|
||||
client_config.state_cache_size = cache_config.state() as usize * mb;
|
||||
// in bytes
|
||||
client_config.jump_table_size = cache_config.jump_tables() as usize * mb;
|
||||
// in bytes
|
||||
client_config.history_mem = pruning_memory * mb;
|
||||
let mb = 1024 * 1024;
|
||||
// in bytes
|
||||
client_config.blockchain.max_cache_size = cache_config.blockchain() as usize * mb;
|
||||
// in bytes
|
||||
client_config.blockchain.pref_cache_size = cache_config.blockchain() as usize * 3 / 4 * mb;
|
||||
// db cache size, in megabytes
|
||||
client_config.db_cache_size = Some(cache_config.db_cache_size() as usize);
|
||||
// db queue cache size, in bytes
|
||||
client_config.queue.max_mem_use = cache_config.queue() as usize * mb;
|
||||
// in bytes
|
||||
client_config.tracing.max_cache_size = cache_config.traces() as usize * mb;
|
||||
// in bytes
|
||||
client_config.tracing.pref_cache_size = cache_config.traces() as usize * 3 / 4 * mb;
|
||||
// in bytes
|
||||
client_config.state_cache_size = cache_config.state() as usize * mb;
|
||||
// in bytes
|
||||
client_config.jump_table_size = cache_config.jump_tables() as usize * mb;
|
||||
// in bytes
|
||||
client_config.history_mem = pruning_memory * mb;
|
||||
|
||||
client_config.mode = mode;
|
||||
client_config.tracing.enabled = tracing;
|
||||
client_config.fat_db = fat_db;
|
||||
client_config.pruning = pruning;
|
||||
client_config.history = pruning_history;
|
||||
client_config.db_compaction = compaction;
|
||||
client_config.vm_type = vm_type;
|
||||
client_config.name = name;
|
||||
client_config.verifier_type = if check_seal { VerifierType::Canon } else { VerifierType::CanonNoSeal };
|
||||
client_config.spec_name = spec_name;
|
||||
client_config.max_round_blocks_to_import = max_round_blocks_to_import;
|
||||
client_config
|
||||
client_config.mode = mode;
|
||||
client_config.tracing.enabled = tracing;
|
||||
client_config.fat_db = fat_db;
|
||||
client_config.pruning = pruning;
|
||||
client_config.history = pruning_history;
|
||||
client_config.db_compaction = compaction;
|
||||
client_config.vm_type = vm_type;
|
||||
client_config.name = name;
|
||||
client_config.verifier_type = if check_seal {
|
||||
VerifierType::Canon
|
||||
} else {
|
||||
VerifierType::CanonNoSeal
|
||||
};
|
||||
client_config.spec_name = spec_name;
|
||||
client_config.max_round_blocks_to_import = max_round_blocks_to_import;
|
||||
client_config
|
||||
}
|
||||
|
||||
pub fn execute_upgrades(
|
||||
base_path: &str,
|
||||
dirs: &DatabaseDirectories,
|
||||
pruning: Algorithm,
|
||||
compaction_profile: &DatabaseCompactionProfile
|
||||
base_path: &str,
|
||||
dirs: &DatabaseDirectories,
|
||||
pruning: Algorithm,
|
||||
compaction_profile: &DatabaseCompactionProfile,
|
||||
) -> Result<(), String> {
|
||||
upgrade_data_paths(base_path, dirs, pruning);
|
||||
|
||||
upgrade_data_paths(base_path, dirs, pruning);
|
||||
match upgrade(&dirs.path) {
|
||||
Ok(upgrades_applied) if upgrades_applied > 0 => {
|
||||
debug!("Executed {} upgrade scripts - ok", upgrades_applied);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("Error upgrading parity data: {:?}", e));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match upgrade(&dirs.path) {
|
||||
Ok(upgrades_applied) if upgrades_applied > 0 => {
|
||||
debug!("Executed {} upgrade scripts - ok", upgrades_applied);
|
||||
},
|
||||
Err(e) => {
|
||||
return Err(format!("Error upgrading parity data: {:?}", e));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
let client_path = dirs.db_path(pruning);
|
||||
migrate(&client_path, compaction_profile).map_err(|e| format!("{}", e))
|
||||
let client_path = dirs.db_path(pruning);
|
||||
migrate(&client_path, compaction_profile).map_err(|e| format!("{}", e))
|
||||
}
|
||||
|
||||
/// Prompts user asking for password.
|
||||
pub fn password_prompt() -> Result<Password, String> {
|
||||
use rpassword::read_password;
|
||||
const STDIN_ERROR: &'static str = "Unable to ask for password on non-interactive terminal.";
|
||||
use rpassword::read_password;
|
||||
const STDIN_ERROR: &'static str = "Unable to ask for password on non-interactive terminal.";
|
||||
|
||||
println!("Please note that password is NOT RECOVERABLE.");
|
||||
print!("Type password: ");
|
||||
flush_stdout();
|
||||
println!("Please note that password is NOT RECOVERABLE.");
|
||||
print!("Type password: ");
|
||||
flush_stdout();
|
||||
|
||||
let password = read_password().map_err(|_| STDIN_ERROR.to_owned())?.into();
|
||||
let password = read_password().map_err(|_| STDIN_ERROR.to_owned())?.into();
|
||||
|
||||
print!("Repeat password: ");
|
||||
flush_stdout();
|
||||
print!("Repeat password: ");
|
||||
flush_stdout();
|
||||
|
||||
let password_repeat = read_password().map_err(|_| STDIN_ERROR.to_owned())?.into();
|
||||
let password_repeat = read_password().map_err(|_| STDIN_ERROR.to_owned())?.into();
|
||||
|
||||
if password != password_repeat {
|
||||
return Err("Passwords do not match!".into());
|
||||
}
|
||||
if password != password_repeat {
|
||||
return Err("Passwords do not match!".into());
|
||||
}
|
||||
|
||||
Ok(password)
|
||||
Ok(password)
|
||||
}
|
||||
|
||||
/// Read a password from password file.
|
||||
pub fn password_from_file(path: String) -> Result<Password, String> {
|
||||
let passwords = passwords_from_files(&[path])?;
|
||||
// use only first password from the file
|
||||
passwords.get(0).map(Password::clone)
|
||||
.ok_or_else(|| "Password file seems to be empty.".to_owned())
|
||||
let passwords = passwords_from_files(&[path])?;
|
||||
// use only first password from the file
|
||||
passwords
|
||||
.get(0)
|
||||
.map(Password::clone)
|
||||
.ok_or_else(|| "Password file seems to be empty.".to_owned())
|
||||
}
|
||||
|
||||
/// Reads passwords from files. Treats each line as a separate password.
|
||||
pub fn passwords_from_files(files: &[String]) -> Result<Vec<Password>, String> {
|
||||
let passwords = files.iter().map(|filename| {
|
||||
let passwords = files.iter().map(|filename| {
|
||||
let file = File::open(filename).map_err(|_| format!("{} Unable to read password file. Ensure it exists and permissions are correct.", filename))?;
|
||||
let reader = BufReader::new(&file);
|
||||
let lines = reader.lines()
|
||||
@@ -327,174 +385,258 @@ pub fn passwords_from_files(files: &[String]) -> Result<Vec<Password>, String> {
|
||||
.collect::<Vec<Password>>();
|
||||
Ok(lines)
|
||||
}).collect::<Result<Vec<Vec<Password>>, String>>();
|
||||
Ok(passwords?.into_iter().flat_map(|x| x).collect())
|
||||
Ok(passwords?.into_iter().flat_map(|x| x).collect())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::collections::HashSet;
|
||||
use tempdir::TempDir;
|
||||
use ethereum_types::U256;
|
||||
use ethcore::client::{Mode, BlockId};
|
||||
use ethcore::miner::PendingSet;
|
||||
use ethkey::Password;
|
||||
use super::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_address, to_addresses, to_price, geth_ipc_path, to_bootnodes, join_set, password_from_file};
|
||||
use super::{
|
||||
geth_ipc_path, join_set, password_from_file, to_address, to_addresses, to_block_id,
|
||||
to_bootnodes, to_duration, to_mode, to_pending_set, to_price, to_u256,
|
||||
};
|
||||
use ethcore::{
|
||||
client::{BlockId, Mode},
|
||||
miner::PendingSet,
|
||||
};
|
||||
use ethereum_types::U256;
|
||||
use ethkey::Password;
|
||||
use std::{collections::HashSet, fs::File, io::Write, time::Duration};
|
||||
use tempdir::TempDir;
|
||||
|
||||
#[test]
|
||||
fn test_to_duration() {
|
||||
assert_eq!(to_duration("twice-daily").unwrap(), Duration::from_secs(12 * 60 * 60));
|
||||
assert_eq!(to_duration("half-hourly").unwrap(), Duration::from_secs(30 * 60));
|
||||
assert_eq!(to_duration("1second").unwrap(), Duration::from_secs(1));
|
||||
assert_eq!(to_duration("2seconds").unwrap(), Duration::from_secs(2));
|
||||
assert_eq!(to_duration("15seconds").unwrap(), Duration::from_secs(15));
|
||||
assert_eq!(to_duration("1minute").unwrap(), Duration::from_secs(1 * 60));
|
||||
assert_eq!(to_duration("2minutes").unwrap(), Duration::from_secs(2 * 60));
|
||||
assert_eq!(to_duration("15minutes").unwrap(), Duration::from_secs(15 * 60));
|
||||
assert_eq!(to_duration("hourly").unwrap(), Duration::from_secs(60 * 60));
|
||||
assert_eq!(to_duration("daily").unwrap(), Duration::from_secs(24 * 60 * 60));
|
||||
assert_eq!(to_duration("1hour").unwrap(), Duration::from_secs(1 * 60 * 60));
|
||||
assert_eq!(to_duration("2hours").unwrap(), Duration::from_secs(2 * 60 * 60));
|
||||
assert_eq!(to_duration("15hours").unwrap(), Duration::from_secs(15 * 60 * 60));
|
||||
assert_eq!(to_duration("1day").unwrap(), Duration::from_secs(1 * 24 * 60 * 60));
|
||||
assert_eq!(to_duration("2days").unwrap(), Duration::from_secs(2 * 24 *60 * 60));
|
||||
assert_eq!(to_duration("15days").unwrap(), Duration::from_secs(15 * 24 * 60 * 60));
|
||||
assert_eq!(to_duration("15 days").unwrap(), Duration::from_secs(15 * 24 * 60 * 60));
|
||||
assert_eq!(to_duration("2 seconds").unwrap(), Duration::from_secs(2));
|
||||
}
|
||||
#[test]
|
||||
fn test_to_duration() {
|
||||
assert_eq!(
|
||||
to_duration("twice-daily").unwrap(),
|
||||
Duration::from_secs(12 * 60 * 60)
|
||||
);
|
||||
assert_eq!(
|
||||
to_duration("half-hourly").unwrap(),
|
||||
Duration::from_secs(30 * 60)
|
||||
);
|
||||
assert_eq!(to_duration("1second").unwrap(), Duration::from_secs(1));
|
||||
assert_eq!(to_duration("2seconds").unwrap(), Duration::from_secs(2));
|
||||
assert_eq!(to_duration("15seconds").unwrap(), Duration::from_secs(15));
|
||||
assert_eq!(to_duration("1minute").unwrap(), Duration::from_secs(1 * 60));
|
||||
assert_eq!(
|
||||
to_duration("2minutes").unwrap(),
|
||||
Duration::from_secs(2 * 60)
|
||||
);
|
||||
assert_eq!(
|
||||
to_duration("15minutes").unwrap(),
|
||||
Duration::from_secs(15 * 60)
|
||||
);
|
||||
assert_eq!(to_duration("hourly").unwrap(), Duration::from_secs(60 * 60));
|
||||
assert_eq!(
|
||||
to_duration("daily").unwrap(),
|
||||
Duration::from_secs(24 * 60 * 60)
|
||||
);
|
||||
assert_eq!(
|
||||
to_duration("1hour").unwrap(),
|
||||
Duration::from_secs(1 * 60 * 60)
|
||||
);
|
||||
assert_eq!(
|
||||
to_duration("2hours").unwrap(),
|
||||
Duration::from_secs(2 * 60 * 60)
|
||||
);
|
||||
assert_eq!(
|
||||
to_duration("15hours").unwrap(),
|
||||
Duration::from_secs(15 * 60 * 60)
|
||||
);
|
||||
assert_eq!(
|
||||
to_duration("1day").unwrap(),
|
||||
Duration::from_secs(1 * 24 * 60 * 60)
|
||||
);
|
||||
assert_eq!(
|
||||
to_duration("2days").unwrap(),
|
||||
Duration::from_secs(2 * 24 * 60 * 60)
|
||||
);
|
||||
assert_eq!(
|
||||
to_duration("15days").unwrap(),
|
||||
Duration::from_secs(15 * 24 * 60 * 60)
|
||||
);
|
||||
assert_eq!(
|
||||
to_duration("15 days").unwrap(),
|
||||
Duration::from_secs(15 * 24 * 60 * 60)
|
||||
);
|
||||
assert_eq!(to_duration("2 seconds").unwrap(), Duration::from_secs(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_mode() {
|
||||
assert_eq!(to_mode("active", 0, 0).unwrap(), Mode::Active);
|
||||
assert_eq!(to_mode("passive", 10, 20).unwrap(), Mode::Passive(Duration::from_secs(10), Duration::from_secs(20)));
|
||||
assert_eq!(to_mode("dark", 20, 30).unwrap(), Mode::Dark(Duration::from_secs(20)));
|
||||
assert!(to_mode("other", 20, 30).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn test_to_mode() {
|
||||
assert_eq!(to_mode("active", 0, 0).unwrap(), Mode::Active);
|
||||
assert_eq!(
|
||||
to_mode("passive", 10, 20).unwrap(),
|
||||
Mode::Passive(Duration::from_secs(10), Duration::from_secs(20))
|
||||
);
|
||||
assert_eq!(
|
||||
to_mode("dark", 20, 30).unwrap(),
|
||||
Mode::Dark(Duration::from_secs(20))
|
||||
);
|
||||
assert!(to_mode("other", 20, 30).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_block_id() {
|
||||
assert_eq!(to_block_id("latest").unwrap(), BlockId::Latest);
|
||||
assert_eq!(to_block_id("0").unwrap(), BlockId::Number(0));
|
||||
assert_eq!(to_block_id("2").unwrap(), BlockId::Number(2));
|
||||
assert_eq!(to_block_id("15").unwrap(), BlockId::Number(15));
|
||||
assert_eq!(
|
||||
to_block_id("9fc84d84f6a785dc1bd5abacfcf9cbdd3b6afb80c0f799bfb2fd42c44a0c224e").unwrap(),
|
||||
BlockId::Hash("9fc84d84f6a785dc1bd5abacfcf9cbdd3b6afb80c0f799bfb2fd42c44a0c224e".parse().unwrap())
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_to_block_id() {
|
||||
assert_eq!(to_block_id("latest").unwrap(), BlockId::Latest);
|
||||
assert_eq!(to_block_id("0").unwrap(), BlockId::Number(0));
|
||||
assert_eq!(to_block_id("2").unwrap(), BlockId::Number(2));
|
||||
assert_eq!(to_block_id("15").unwrap(), BlockId::Number(15));
|
||||
assert_eq!(
|
||||
to_block_id("9fc84d84f6a785dc1bd5abacfcf9cbdd3b6afb80c0f799bfb2fd42c44a0c224e")
|
||||
.unwrap(),
|
||||
BlockId::Hash(
|
||||
"9fc84d84f6a785dc1bd5abacfcf9cbdd3b6afb80c0f799bfb2fd42c44a0c224e"
|
||||
.parse()
|
||||
.unwrap()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_u256() {
|
||||
assert_eq!(to_u256("0").unwrap(), U256::from(0));
|
||||
assert_eq!(to_u256("11").unwrap(), U256::from(11));
|
||||
assert_eq!(to_u256("0x11").unwrap(), U256::from(17));
|
||||
assert!(to_u256("u").is_err())
|
||||
}
|
||||
#[test]
|
||||
fn test_to_u256() {
|
||||
assert_eq!(to_u256("0").unwrap(), U256::from(0));
|
||||
assert_eq!(to_u256("11").unwrap(), U256::from(11));
|
||||
assert_eq!(to_u256("0x11").unwrap(), U256::from(17));
|
||||
assert!(to_u256("u").is_err())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pending_set() {
|
||||
assert_eq!(to_pending_set("cheap").unwrap(), PendingSet::AlwaysQueue);
|
||||
assert_eq!(to_pending_set("strict").unwrap(), PendingSet::AlwaysSealing);
|
||||
assert_eq!(to_pending_set("lenient").unwrap(), PendingSet::SealingOrElseQueue);
|
||||
assert!(to_pending_set("othe").is_err());
|
||||
}
|
||||
#[test]
|
||||
fn test_pending_set() {
|
||||
assert_eq!(to_pending_set("cheap").unwrap(), PendingSet::AlwaysQueue);
|
||||
assert_eq!(to_pending_set("strict").unwrap(), PendingSet::AlwaysSealing);
|
||||
assert_eq!(
|
||||
to_pending_set("lenient").unwrap(),
|
||||
PendingSet::SealingOrElseQueue
|
||||
);
|
||||
assert!(to_pending_set("othe").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_address() {
|
||||
assert_eq!(
|
||||
to_address(Some("0xD9A111feda3f362f55Ef1744347CDC8Dd9964a41".into())).unwrap(),
|
||||
"D9A111feda3f362f55Ef1744347CDC8Dd9964a41".parse().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
to_address(Some("D9A111feda3f362f55Ef1744347CDC8Dd9964a41".into())).unwrap(),
|
||||
"D9A111feda3f362f55Ef1744347CDC8Dd9964a41".parse().unwrap()
|
||||
);
|
||||
assert_eq!(to_address(None).unwrap(), Default::default());
|
||||
}
|
||||
#[test]
|
||||
fn test_to_address() {
|
||||
assert_eq!(
|
||||
to_address(Some("0xD9A111feda3f362f55Ef1744347CDC8Dd9964a41".into())).unwrap(),
|
||||
"D9A111feda3f362f55Ef1744347CDC8Dd9964a41".parse().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
to_address(Some("D9A111feda3f362f55Ef1744347CDC8Dd9964a41".into())).unwrap(),
|
||||
"D9A111feda3f362f55Ef1744347CDC8Dd9964a41".parse().unwrap()
|
||||
);
|
||||
assert_eq!(to_address(None).unwrap(), Default::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_addresses() {
|
||||
let addresses = to_addresses(&Some("0xD9A111feda3f362f55Ef1744347CDC8Dd9964a41,D9A111feda3f362f55Ef1744347CDC8Dd9964a42".into())).unwrap();
|
||||
assert_eq!(
|
||||
addresses,
|
||||
vec![
|
||||
"D9A111feda3f362f55Ef1744347CDC8Dd9964a41".parse().unwrap(),
|
||||
"D9A111feda3f362f55Ef1744347CDC8Dd9964a42".parse().unwrap(),
|
||||
]
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_to_addresses() {
|
||||
let addresses = to_addresses(&Some(
|
||||
"0xD9A111feda3f362f55Ef1744347CDC8Dd9964a41,D9A111feda3f362f55Ef1744347CDC8Dd9964a42"
|
||||
.into(),
|
||||
))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
addresses,
|
||||
vec![
|
||||
"D9A111feda3f362f55Ef1744347CDC8Dd9964a41".parse().unwrap(),
|
||||
"D9A111feda3f362f55Ef1744347CDC8Dd9964a42".parse().unwrap(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_password() {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let path = tempdir.path().join("file");
|
||||
let mut file = File::create(&path).unwrap();
|
||||
file.write_all(b"a bc ").unwrap();
|
||||
assert_eq!(password_from_file(path.to_str().unwrap().into()).unwrap().as_bytes(), b"a bc");
|
||||
}
|
||||
#[test]
|
||||
fn test_password() {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let path = tempdir.path().join("file");
|
||||
let mut file = File::create(&path).unwrap();
|
||||
file.write_all(b"a bc ").unwrap();
|
||||
assert_eq!(
|
||||
password_from_file(path.to_str().unwrap().into())
|
||||
.unwrap()
|
||||
.as_bytes(),
|
||||
b"a bc"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_password_multiline() {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let path = tempdir.path().join("file");
|
||||
let mut file = File::create(path.as_path()).unwrap();
|
||||
file.write_all(br#" password with trailing whitespace
|
||||
#[test]
|
||||
fn test_password_multiline() {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let path = tempdir.path().join("file");
|
||||
let mut file = File::create(path.as_path()).unwrap();
|
||||
file.write_all(
|
||||
br#" password with trailing whitespace
|
||||
those passwords should be
|
||||
ignored
|
||||
but the first password is trimmed
|
||||
|
||||
"#).unwrap();
|
||||
assert_eq!(password_from_file(path.to_str().unwrap().into()).unwrap(), Password::from("password with trailing whitespace"));
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
password_from_file(path.to_str().unwrap().into()).unwrap(),
|
||||
Password::from("password with trailing whitespace")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_price() {
|
||||
assert_eq!(to_price("1").unwrap(), 1.0);
|
||||
assert_eq!(to_price("2.3").unwrap(), 2.3);
|
||||
assert_eq!(to_price("2.33").unwrap(), 2.33);
|
||||
}
|
||||
#[test]
|
||||
fn test_to_price() {
|
||||
assert_eq!(to_price("1").unwrap(), 1.0);
|
||||
assert_eq!(to_price("2.3").unwrap(), 2.3);
|
||||
assert_eq!(to_price("2.33").unwrap(), 2.33);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn test_geth_ipc_path() {
|
||||
assert_eq!(geth_ipc_path(true), r"\\.\pipe\geth.ipc".to_owned());
|
||||
assert_eq!(geth_ipc_path(false), r"\\.\pipe\geth.ipc".to_owned());
|
||||
}
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn test_geth_ipc_path() {
|
||||
assert_eq!(geth_ipc_path(true), r"\\.\pipe\geth.ipc".to_owned());
|
||||
assert_eq!(geth_ipc_path(false), r"\\.\pipe\geth.ipc".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_geth_ipc_path() {
|
||||
use path;
|
||||
assert_eq!(geth_ipc_path(true), path::ethereum::with_testnet("geth.ipc").to_str().unwrap().to_owned());
|
||||
assert_eq!(geth_ipc_path(false), path::ethereum::with_default("geth.ipc").to_str().unwrap().to_owned());
|
||||
}
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_geth_ipc_path() {
|
||||
use path;
|
||||
assert_eq!(
|
||||
geth_ipc_path(true),
|
||||
path::ethereum::with_testnet("geth.ipc")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned()
|
||||
);
|
||||
assert_eq!(
|
||||
geth_ipc_path(false),
|
||||
path::ethereum::with_default("geth.ipc")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_bootnodes() {
|
||||
let one_bootnode = "enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303";
|
||||
let two_bootnodes = "enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303,enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303";
|
||||
#[test]
|
||||
fn test_to_bootnodes() {
|
||||
let one_bootnode = "enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303";
|
||||
let two_bootnodes = "enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303,enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303";
|
||||
|
||||
assert_eq!(to_bootnodes(&Some("".into())), Ok(vec![]));
|
||||
assert_eq!(to_bootnodes(&None), Ok(vec![]));
|
||||
assert_eq!(to_bootnodes(&Some(one_bootnode.into())), Ok(vec![one_bootnode.into()]));
|
||||
assert_eq!(to_bootnodes(&Some(two_bootnodes.into())), Ok(vec![one_bootnode.into(), one_bootnode.into()]));
|
||||
}
|
||||
assert_eq!(to_bootnodes(&Some("".into())), Ok(vec![]));
|
||||
assert_eq!(to_bootnodes(&None), Ok(vec![]));
|
||||
assert_eq!(
|
||||
to_bootnodes(&Some(one_bootnode.into())),
|
||||
Ok(vec![one_bootnode.into()])
|
||||
);
|
||||
assert_eq!(
|
||||
to_bootnodes(&Some(two_bootnodes.into())),
|
||||
Ok(vec![one_bootnode.into(), one_bootnode.into()])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_join_set() {
|
||||
let mut test_set = HashSet::new();
|
||||
test_set.insert("0x1111111111111111111111111111111111111111".to_string());
|
||||
test_set.insert("0x0000000000000000000000000000000000000000".to_string());
|
||||
#[test]
|
||||
fn test_join_set() {
|
||||
let mut test_set = HashSet::new();
|
||||
test_set.insert("0x1111111111111111111111111111111111111111".to_string());
|
||||
test_set.insert("0x0000000000000000000000000000000000000000".to_string());
|
||||
|
||||
let res = join_set(Some(&test_set)).unwrap();
|
||||
|
||||
let res = join_set(Some(&test_set)).unwrap();
|
||||
|
||||
assert!(
|
||||
assert!(
|
||||
res == "0x1111111111111111111111111111111111111111,0x0000000000000000000000000000000000000000"
|
||||
||
|
||||
res == "0x0000000000000000000000000000000000000000,0x1111111111111111111111111111111111111111"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,437 +15,481 @@
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate ansi_term;
|
||||
use self::ansi_term::Colour::{White, Yellow, Green, Cyan, Blue};
|
||||
use self::ansi_term::{Colour, Style};
|
||||
use self::ansi_term::{
|
||||
Colour,
|
||||
Colour::{Blue, Cyan, Green, White, Yellow},
|
||||
Style,
|
||||
};
|
||||
|
||||
use std::sync::{Arc};
|
||||
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::time::{Instant, Duration};
|
||||
use std::{
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering},
|
||||
Arc,
|
||||
},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use atty;
|
||||
use ethcore::client::{
|
||||
BlockId, BlockChainClient, ChainInfo, BlockInfo, BlockChainInfo,
|
||||
BlockQueueInfo, ChainNotify, NewBlocks, ClientReport, Client, ClientIoMessage
|
||||
use ethcore::{
|
||||
client::{
|
||||
BlockChainClient, BlockChainInfo, BlockId, BlockInfo, BlockQueueInfo, ChainInfo,
|
||||
ChainNotify, Client, ClientIoMessage, ClientReport, NewBlocks,
|
||||
},
|
||||
snapshot::{service::Service as SnapshotService, RestorationStatus, SnapshotService as SS},
|
||||
};
|
||||
use types::BlockNumber;
|
||||
use ethcore::snapshot::{RestorationStatus, SnapshotService as SS};
|
||||
use ethcore::snapshot::service::Service as SnapshotService;
|
||||
use sync::{LightSyncProvider, LightSync, SyncProvider, ManageNetwork};
|
||||
use io::{TimerToken, IoContext, IoHandler};
|
||||
use light::Cache as LightDataCache;
|
||||
use light::client::{LightChainClient, LightChainNotify};
|
||||
use number_prefix::{binary_prefix, Standalone, Prefixed};
|
||||
use parity_rpc::is_major_importing_or_waiting;
|
||||
use parity_rpc::informant::RpcStats;
|
||||
use ethereum_types::H256;
|
||||
use parking_lot::{RwLock, Mutex};
|
||||
use io::{IoContext, IoHandler, TimerToken};
|
||||
use light::{
|
||||
client::{LightChainClient, LightChainNotify},
|
||||
Cache as LightDataCache,
|
||||
};
|
||||
use number_prefix::{binary_prefix, Prefixed, Standalone};
|
||||
use parity_rpc::{informant::RpcStats, is_major_importing_or_waiting};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use sync::{LightSync, LightSyncProvider, ManageNetwork, SyncProvider};
|
||||
use types::BlockNumber;
|
||||
|
||||
/// Format byte counts to standard denominations.
|
||||
pub fn format_bytes(b: usize) -> String {
|
||||
match binary_prefix(b as f64) {
|
||||
Standalone(bytes) => format!("{} bytes", bytes),
|
||||
Prefixed(prefix, n) => format!("{:.0} {}B", n, prefix),
|
||||
}
|
||||
match binary_prefix(b as f64) {
|
||||
Standalone(bytes) => format!("{} bytes", bytes),
|
||||
Prefixed(prefix, n) => format!("{:.0} {}B", n, prefix),
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that can be converted to milliseconds.
|
||||
pub trait MillisecondDuration {
|
||||
/// Get the value in milliseconds.
|
||||
fn as_milliseconds(&self) -> u64;
|
||||
/// Get the value in milliseconds.
|
||||
fn as_milliseconds(&self) -> u64;
|
||||
}
|
||||
|
||||
impl MillisecondDuration for Duration {
|
||||
fn as_milliseconds(&self) -> u64 {
|
||||
self.as_secs() * 1000 + self.subsec_nanos() as u64 / 1_000_000
|
||||
}
|
||||
fn as_milliseconds(&self) -> u64 {
|
||||
self.as_secs() * 1000 + self.subsec_nanos() as u64 / 1_000_000
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CacheSizes {
|
||||
sizes: ::std::collections::BTreeMap<&'static str, usize>,
|
||||
sizes: ::std::collections::BTreeMap<&'static str, usize>,
|
||||
}
|
||||
|
||||
impl CacheSizes {
|
||||
fn insert(&mut self, key: &'static str, bytes: usize) {
|
||||
self.sizes.insert(key, bytes);
|
||||
}
|
||||
fn insert(&mut self, key: &'static str, bytes: usize) {
|
||||
self.sizes.insert(key, bytes);
|
||||
}
|
||||
|
||||
fn display<F>(&self, style: Style, paint: F) -> String
|
||||
where F: Fn(Style, String) -> String
|
||||
{
|
||||
use std::fmt::Write;
|
||||
fn display<F>(&self, style: Style, paint: F) -> String
|
||||
where
|
||||
F: Fn(Style, String) -> String,
|
||||
{
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut buf = String::new();
|
||||
for (name, &size) in &self.sizes {
|
||||
let mut buf = String::new();
|
||||
for (name, &size) in &self.sizes {
|
||||
write!(buf, " {:>8} {}", paint(style, format_bytes(size)), name)
|
||||
.expect("writing to string won't fail unless OOM; qed")
|
||||
}
|
||||
|
||||
write!(buf, " {:>8} {}", paint(style, format_bytes(size)), name)
|
||||
.expect("writing to string won't fail unless OOM; qed")
|
||||
}
|
||||
|
||||
buf
|
||||
}
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SyncInfo {
|
||||
last_imported_block_number: BlockNumber,
|
||||
last_imported_old_block_number: Option<BlockNumber>,
|
||||
num_peers: usize,
|
||||
max_peers: u32,
|
||||
snapshot_sync: bool,
|
||||
last_imported_block_number: BlockNumber,
|
||||
last_imported_old_block_number: Option<BlockNumber>,
|
||||
num_peers: usize,
|
||||
max_peers: u32,
|
||||
snapshot_sync: bool,
|
||||
}
|
||||
|
||||
pub struct Report {
|
||||
importing: bool,
|
||||
chain_info: BlockChainInfo,
|
||||
client_report: ClientReport,
|
||||
queue_info: BlockQueueInfo,
|
||||
cache_sizes: CacheSizes,
|
||||
sync_info: Option<SyncInfo>,
|
||||
importing: bool,
|
||||
chain_info: BlockChainInfo,
|
||||
client_report: ClientReport,
|
||||
queue_info: BlockQueueInfo,
|
||||
cache_sizes: CacheSizes,
|
||||
sync_info: Option<SyncInfo>,
|
||||
}
|
||||
|
||||
/// Something which can provide data to the informant.
|
||||
pub trait InformantData: Send + Sync {
|
||||
/// Whether it executes transactions
|
||||
fn executes_transactions(&self) -> bool;
|
||||
/// Whether it executes transactions
|
||||
fn executes_transactions(&self) -> bool;
|
||||
|
||||
/// Whether it is currently importing (also included in `Report`)
|
||||
fn is_major_importing(&self) -> bool;
|
||||
/// Whether it is currently importing (also included in `Report`)
|
||||
fn is_major_importing(&self) -> bool;
|
||||
|
||||
/// Generate a report of blockchain status, memory usage, and sync info.
|
||||
fn report(&self) -> Report;
|
||||
/// Generate a report of blockchain status, memory usage, and sync info.
|
||||
fn report(&self) -> Report;
|
||||
}
|
||||
|
||||
/// Informant data for a full node.
|
||||
pub struct FullNodeInformantData {
|
||||
pub client: Arc<Client>,
|
||||
pub sync: Option<Arc<SyncProvider>>,
|
||||
pub net: Option<Arc<ManageNetwork>>,
|
||||
pub client: Arc<Client>,
|
||||
pub sync: Option<Arc<SyncProvider>>,
|
||||
pub net: Option<Arc<ManageNetwork>>,
|
||||
}
|
||||
|
||||
impl InformantData for FullNodeInformantData {
|
||||
fn executes_transactions(&self) -> bool { true }
|
||||
fn executes_transactions(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn is_major_importing(&self) -> bool {
|
||||
let state = self.sync.as_ref().map(|sync| sync.status().state);
|
||||
is_major_importing_or_waiting(state, self.client.queue_info(), false)
|
||||
}
|
||||
fn is_major_importing(&self) -> bool {
|
||||
let state = self.sync.as_ref().map(|sync| sync.status().state);
|
||||
is_major_importing_or_waiting(state, self.client.queue_info(), false)
|
||||
}
|
||||
|
||||
fn report(&self) -> Report {
|
||||
let (client_report, queue_info, blockchain_cache_info) =
|
||||
(self.client.report(), self.client.queue_info(), self.client.blockchain_cache_info());
|
||||
fn report(&self) -> Report {
|
||||
let (client_report, queue_info, blockchain_cache_info) = (
|
||||
self.client.report(),
|
||||
self.client.queue_info(),
|
||||
self.client.blockchain_cache_info(),
|
||||
);
|
||||
|
||||
let chain_info = self.client.chain_info();
|
||||
let chain_info = self.client.chain_info();
|
||||
|
||||
let mut cache_sizes = CacheSizes::default();
|
||||
cache_sizes.insert("db", client_report.state_db_mem);
|
||||
cache_sizes.insert("queue", queue_info.mem_used);
|
||||
cache_sizes.insert("chain", blockchain_cache_info.total());
|
||||
let mut cache_sizes = CacheSizes::default();
|
||||
cache_sizes.insert("db", client_report.state_db_mem);
|
||||
cache_sizes.insert("queue", queue_info.mem_used);
|
||||
cache_sizes.insert("chain", blockchain_cache_info.total());
|
||||
|
||||
let importing = self.is_major_importing();
|
||||
let sync_info = match (self.sync.as_ref(), self.net.as_ref()) {
|
||||
(Some(sync), Some(net)) => {
|
||||
let status = sync.status();
|
||||
let num_peers_range = net.num_peers_range();
|
||||
debug_assert!(num_peers_range.end() >= num_peers_range.start());
|
||||
let importing = self.is_major_importing();
|
||||
let sync_info = match (self.sync.as_ref(), self.net.as_ref()) {
|
||||
(Some(sync), Some(net)) => {
|
||||
let status = sync.status();
|
||||
let num_peers_range = net.num_peers_range();
|
||||
debug_assert!(num_peers_range.end() >= num_peers_range.start());
|
||||
|
||||
cache_sizes.insert("sync", status.mem_used);
|
||||
cache_sizes.insert("sync", status.mem_used);
|
||||
|
||||
Some(SyncInfo {
|
||||
last_imported_block_number: status.last_imported_block_number.unwrap_or(chain_info.best_block_number),
|
||||
last_imported_old_block_number: status.last_imported_old_block_number,
|
||||
num_peers: status.num_peers,
|
||||
max_peers: status.current_max_peers(*num_peers_range.start(), *num_peers_range.end()),
|
||||
snapshot_sync: status.is_snapshot_syncing(),
|
||||
})
|
||||
}
|
||||
_ => None
|
||||
};
|
||||
Some(SyncInfo {
|
||||
last_imported_block_number: status
|
||||
.last_imported_block_number
|
||||
.unwrap_or(chain_info.best_block_number),
|
||||
last_imported_old_block_number: status.last_imported_old_block_number,
|
||||
num_peers: status.num_peers,
|
||||
max_peers: status
|
||||
.current_max_peers(*num_peers_range.start(), *num_peers_range.end()),
|
||||
snapshot_sync: status.is_snapshot_syncing(),
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Report {
|
||||
importing,
|
||||
chain_info,
|
||||
client_report,
|
||||
queue_info,
|
||||
cache_sizes,
|
||||
sync_info,
|
||||
}
|
||||
}
|
||||
Report {
|
||||
importing,
|
||||
chain_info,
|
||||
client_report,
|
||||
queue_info,
|
||||
cache_sizes,
|
||||
sync_info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Informant data for a light node -- note that the network is required.
|
||||
pub struct LightNodeInformantData {
|
||||
pub client: Arc<LightChainClient>,
|
||||
pub sync: Arc<LightSync>,
|
||||
pub cache: Arc<Mutex<LightDataCache>>,
|
||||
pub client: Arc<LightChainClient>,
|
||||
pub sync: Arc<LightSync>,
|
||||
pub cache: Arc<Mutex<LightDataCache>>,
|
||||
}
|
||||
|
||||
impl InformantData for LightNodeInformantData {
|
||||
fn executes_transactions(&self) -> bool { false }
|
||||
fn executes_transactions(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn is_major_importing(&self) -> bool {
|
||||
self.sync.is_major_importing()
|
||||
}
|
||||
fn is_major_importing(&self) -> bool {
|
||||
self.sync.is_major_importing()
|
||||
}
|
||||
|
||||
fn report(&self) -> Report {
|
||||
let (client_report, queue_info, chain_info) =
|
||||
(self.client.report(), self.client.queue_info(), self.client.chain_info());
|
||||
fn report(&self) -> Report {
|
||||
let (client_report, queue_info, chain_info) = (
|
||||
self.client.report(),
|
||||
self.client.queue_info(),
|
||||
self.client.chain_info(),
|
||||
);
|
||||
|
||||
let mut cache_sizes = CacheSizes::default();
|
||||
cache_sizes.insert("queue", queue_info.mem_used);
|
||||
cache_sizes.insert("cache", self.cache.lock().mem_used());
|
||||
let mut cache_sizes = CacheSizes::default();
|
||||
cache_sizes.insert("queue", queue_info.mem_used);
|
||||
cache_sizes.insert("cache", self.cache.lock().mem_used());
|
||||
|
||||
let peer_numbers = self.sync.peer_numbers();
|
||||
let sync_info = Some(SyncInfo {
|
||||
last_imported_block_number: chain_info.best_block_number,
|
||||
last_imported_old_block_number: None,
|
||||
num_peers: peer_numbers.connected,
|
||||
max_peers: peer_numbers.max as u32,
|
||||
snapshot_sync: false,
|
||||
});
|
||||
let peer_numbers = self.sync.peer_numbers();
|
||||
let sync_info = Some(SyncInfo {
|
||||
last_imported_block_number: chain_info.best_block_number,
|
||||
last_imported_old_block_number: None,
|
||||
num_peers: peer_numbers.connected,
|
||||
max_peers: peer_numbers.max as u32,
|
||||
snapshot_sync: false,
|
||||
});
|
||||
|
||||
Report {
|
||||
importing: self.sync.is_major_importing(),
|
||||
chain_info,
|
||||
client_report,
|
||||
queue_info,
|
||||
cache_sizes,
|
||||
sync_info,
|
||||
}
|
||||
}
|
||||
Report {
|
||||
importing: self.sync.is_major_importing(),
|
||||
chain_info,
|
||||
client_report,
|
||||
queue_info,
|
||||
cache_sizes,
|
||||
sync_info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Informant<T> {
|
||||
last_tick: RwLock<Instant>,
|
||||
with_color: bool,
|
||||
target: T,
|
||||
snapshot: Option<Arc<SnapshotService>>,
|
||||
rpc_stats: Option<Arc<RpcStats>>,
|
||||
last_import: Mutex<Instant>,
|
||||
skipped: AtomicUsize,
|
||||
skipped_txs: AtomicUsize,
|
||||
in_shutdown: AtomicBool,
|
||||
last_report: Mutex<ClientReport>,
|
||||
last_tick: RwLock<Instant>,
|
||||
with_color: bool,
|
||||
target: T,
|
||||
snapshot: Option<Arc<SnapshotService>>,
|
||||
rpc_stats: Option<Arc<RpcStats>>,
|
||||
last_import: Mutex<Instant>,
|
||||
skipped: AtomicUsize,
|
||||
skipped_txs: AtomicUsize,
|
||||
in_shutdown: AtomicBool,
|
||||
last_report: Mutex<ClientReport>,
|
||||
}
|
||||
|
||||
impl<T: InformantData> Informant<T> {
|
||||
/// Make a new instance potentially `with_color` output.
|
||||
pub fn new(
|
||||
target: T,
|
||||
snapshot: Option<Arc<SnapshotService>>,
|
||||
rpc_stats: Option<Arc<RpcStats>>,
|
||||
with_color: bool,
|
||||
) -> Self {
|
||||
Informant {
|
||||
last_tick: RwLock::new(Instant::now()),
|
||||
with_color: with_color,
|
||||
target: target,
|
||||
snapshot: snapshot,
|
||||
rpc_stats: rpc_stats,
|
||||
last_import: Mutex::new(Instant::now()),
|
||||
skipped: AtomicUsize::new(0),
|
||||
skipped_txs: AtomicUsize::new(0),
|
||||
in_shutdown: AtomicBool::new(false),
|
||||
last_report: Mutex::new(Default::default()),
|
||||
}
|
||||
}
|
||||
/// Make a new instance potentially `with_color` output.
|
||||
pub fn new(
|
||||
target: T,
|
||||
snapshot: Option<Arc<SnapshotService>>,
|
||||
rpc_stats: Option<Arc<RpcStats>>,
|
||||
with_color: bool,
|
||||
) -> Self {
|
||||
Informant {
|
||||
last_tick: RwLock::new(Instant::now()),
|
||||
with_color: with_color,
|
||||
target: target,
|
||||
snapshot: snapshot,
|
||||
rpc_stats: rpc_stats,
|
||||
last_import: Mutex::new(Instant::now()),
|
||||
skipped: AtomicUsize::new(0),
|
||||
skipped_txs: AtomicUsize::new(0),
|
||||
in_shutdown: AtomicBool::new(false),
|
||||
last_report: Mutex::new(Default::default()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Signal that we're shutting down; no more output necessary.
|
||||
pub fn shutdown(&self) {
|
||||
self.in_shutdown.store(true, ::std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
/// Signal that we're shutting down; no more output necessary.
|
||||
pub fn shutdown(&self) {
|
||||
self.in_shutdown
|
||||
.store(true, ::std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub fn tick(&self) {
|
||||
let now = Instant::now();
|
||||
let elapsed = now.duration_since(*self.last_tick.read());
|
||||
pub fn tick(&self) {
|
||||
let now = Instant::now();
|
||||
let elapsed = now.duration_since(*self.last_tick.read());
|
||||
|
||||
let (client_report, full_report) = {
|
||||
let mut last_report = self.last_report.lock();
|
||||
let full_report = self.target.report();
|
||||
let diffed = full_report.client_report.clone() - &*last_report;
|
||||
(diffed, full_report)
|
||||
};
|
||||
let (client_report, full_report) = {
|
||||
let mut last_report = self.last_report.lock();
|
||||
let full_report = self.target.report();
|
||||
let diffed = full_report.client_report.clone() - &*last_report;
|
||||
(diffed, full_report)
|
||||
};
|
||||
|
||||
let Report {
|
||||
importing,
|
||||
chain_info,
|
||||
queue_info,
|
||||
cache_sizes,
|
||||
sync_info,
|
||||
..
|
||||
} = full_report;
|
||||
let Report {
|
||||
importing,
|
||||
chain_info,
|
||||
queue_info,
|
||||
cache_sizes,
|
||||
sync_info,
|
||||
..
|
||||
} = full_report;
|
||||
|
||||
let rpc_stats = self.rpc_stats.as_ref();
|
||||
let snapshot_sync = sync_info.as_ref().map_or(false, |s| s.snapshot_sync) && self.snapshot.as_ref().map_or(false, |s|
|
||||
match s.status() {
|
||||
RestorationStatus::Ongoing { .. } | RestorationStatus::Initializing { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
);
|
||||
if !importing && !snapshot_sync && elapsed < Duration::from_secs(30) {
|
||||
return;
|
||||
}
|
||||
let rpc_stats = self.rpc_stats.as_ref();
|
||||
let snapshot_sync = sync_info.as_ref().map_or(false, |s| s.snapshot_sync)
|
||||
&& self.snapshot.as_ref().map_or(false, |s| match s.status() {
|
||||
RestorationStatus::Ongoing { .. } | RestorationStatus::Initializing { .. } => true,
|
||||
_ => false,
|
||||
});
|
||||
if !importing && !snapshot_sync && elapsed < Duration::from_secs(30) {
|
||||
return;
|
||||
}
|
||||
|
||||
*self.last_tick.write() = now;
|
||||
*self.last_report.lock() = full_report.client_report.clone();
|
||||
*self.last_tick.write() = now;
|
||||
*self.last_report.lock() = full_report.client_report.clone();
|
||||
|
||||
let paint = |c: Style, t: String| match self.with_color && atty::is(atty::Stream::Stdout) {
|
||||
true => format!("{}", c.paint(t)),
|
||||
false => t,
|
||||
};
|
||||
let paint = |c: Style, t: String| match self.with_color && atty::is(atty::Stream::Stdout) {
|
||||
true => format!("{}", c.paint(t)),
|
||||
false => t,
|
||||
};
|
||||
|
||||
info!(target: "import", "{} {} {} {}",
|
||||
match importing {
|
||||
true => match snapshot_sync {
|
||||
false => format!("Syncing {} {} {} {}+{} Qed",
|
||||
paint(White.bold(), format!("{:>8}", format!("#{}", chain_info.best_block_number))),
|
||||
paint(White.bold(), format!("{}", chain_info.best_block_hash)),
|
||||
if self.target.executes_transactions() {
|
||||
format!("{} blk/s {} tx/s {} Mgas/s",
|
||||
paint(Yellow.bold(), format!("{:7.2}", (client_report.blocks_imported * 1000) as f64 / elapsed.as_milliseconds() as f64)),
|
||||
paint(Yellow.bold(), format!("{:6.1}", (client_report.transactions_applied * 1000) as f64 / elapsed.as_milliseconds() as f64)),
|
||||
paint(Yellow.bold(), format!("{:6.1}", (client_report.gas_processed / 1000).low_u64() as f64 / elapsed.as_milliseconds() as f64))
|
||||
)
|
||||
} else {
|
||||
format!("{} hdr/s",
|
||||
paint(Yellow.bold(), format!("{:6.1}", (client_report.blocks_imported * 1000) as f64 / elapsed.as_milliseconds() as f64))
|
||||
)
|
||||
},
|
||||
paint(Green.bold(), format!("{:5}", queue_info.unverified_queue_size)),
|
||||
paint(Green.bold(), format!("{:5}", queue_info.verified_queue_size))
|
||||
),
|
||||
true => {
|
||||
self.snapshot.as_ref().map_or(String::new(), |s|
|
||||
match s.status() {
|
||||
RestorationStatus::Ongoing { state_chunks, block_chunks, state_chunks_done, block_chunks_done } => {
|
||||
format!("Syncing snapshot {}/{}", state_chunks_done + block_chunks_done, state_chunks + block_chunks)
|
||||
},
|
||||
RestorationStatus::Initializing { chunks_done } => {
|
||||
format!("Snapshot initializing ({} chunks restored)", chunks_done)
|
||||
},
|
||||
_ => String::new(),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
false => String::new(),
|
||||
},
|
||||
match sync_info.as_ref() {
|
||||
Some(ref sync_info) => format!("{}{}/{} peers",
|
||||
match importing {
|
||||
true => format!("{}",
|
||||
if self.target.executes_transactions() {
|
||||
paint(Green.bold(), format!("{:>8} ", format!("#{}", sync_info.last_imported_block_number)))
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
),
|
||||
false => match sync_info.last_imported_old_block_number {
|
||||
Some(number) => format!("{} ", paint(Yellow.bold(), format!("{:>8}", format!("#{}", number)))),
|
||||
None => String::new(),
|
||||
}
|
||||
},
|
||||
paint(Cyan.bold(), format!("{:2}", sync_info.num_peers)),
|
||||
paint(Cyan.bold(), format!("{:2}", sync_info.max_peers)),
|
||||
),
|
||||
_ => String::new(),
|
||||
},
|
||||
cache_sizes.display(Blue.bold(), &paint),
|
||||
match rpc_stats {
|
||||
Some(ref rpc_stats) => format!(
|
||||
"RPC: {} conn, {} req/s, {} µs",
|
||||
paint(Blue.bold(), format!("{:2}", rpc_stats.sessions())),
|
||||
paint(Blue.bold(), format!("{:4}", rpc_stats.requests_rate())),
|
||||
paint(Blue.bold(), format!("{:4}", rpc_stats.approximated_roundtrip())),
|
||||
),
|
||||
_ => String::new(),
|
||||
},
|
||||
);
|
||||
}
|
||||
info!(target: "import", "{} {} {} {}",
|
||||
match importing {
|
||||
true => match snapshot_sync {
|
||||
false => format!("Syncing {} {} {} {}+{} Qed",
|
||||
paint(White.bold(), format!("{:>8}", format!("#{}", chain_info.best_block_number))),
|
||||
paint(White.bold(), format!("{}", chain_info.best_block_hash)),
|
||||
if self.target.executes_transactions() {
|
||||
format!("{} blk/s {} tx/s {} Mgas/s",
|
||||
paint(Yellow.bold(), format!("{:7.2}", (client_report.blocks_imported * 1000) as f64 / elapsed.as_milliseconds() as f64)),
|
||||
paint(Yellow.bold(), format!("{:6.1}", (client_report.transactions_applied * 1000) as f64 / elapsed.as_milliseconds() as f64)),
|
||||
paint(Yellow.bold(), format!("{:6.1}", (client_report.gas_processed / 1000).low_u64() as f64 / elapsed.as_milliseconds() as f64))
|
||||
)
|
||||
} else {
|
||||
format!("{} hdr/s",
|
||||
paint(Yellow.bold(), format!("{:6.1}", (client_report.blocks_imported * 1000) as f64 / elapsed.as_milliseconds() as f64))
|
||||
)
|
||||
},
|
||||
paint(Green.bold(), format!("{:5}", queue_info.unverified_queue_size)),
|
||||
paint(Green.bold(), format!("{:5}", queue_info.verified_queue_size))
|
||||
),
|
||||
true => {
|
||||
self.snapshot.as_ref().map_or(String::new(), |s|
|
||||
match s.status() {
|
||||
RestorationStatus::Ongoing { state_chunks, block_chunks, state_chunks_done, block_chunks_done } => {
|
||||
format!("Syncing snapshot {}/{}", state_chunks_done + block_chunks_done, state_chunks + block_chunks)
|
||||
},
|
||||
RestorationStatus::Initializing { chunks_done } => {
|
||||
format!("Snapshot initializing ({} chunks restored)", chunks_done)
|
||||
},
|
||||
_ => String::new(),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
false => String::new(),
|
||||
},
|
||||
match sync_info.as_ref() {
|
||||
Some(ref sync_info) => format!("{}{}/{} peers",
|
||||
match importing {
|
||||
true => format!("{}",
|
||||
if self.target.executes_transactions() {
|
||||
paint(Green.bold(), format!("{:>8} ", format!("#{}", sync_info.last_imported_block_number)))
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
),
|
||||
false => match sync_info.last_imported_old_block_number {
|
||||
Some(number) => format!("{} ", paint(Yellow.bold(), format!("{:>8}", format!("#{}", number)))),
|
||||
None => String::new(),
|
||||
}
|
||||
},
|
||||
paint(Cyan.bold(), format!("{:2}", sync_info.num_peers)),
|
||||
paint(Cyan.bold(), format!("{:2}", sync_info.max_peers)),
|
||||
),
|
||||
_ => String::new(),
|
||||
},
|
||||
cache_sizes.display(Blue.bold(), &paint),
|
||||
match rpc_stats {
|
||||
Some(ref rpc_stats) => format!(
|
||||
"RPC: {} conn, {} req/s, {} µs",
|
||||
paint(Blue.bold(), format!("{:2}", rpc_stats.sessions())),
|
||||
paint(Blue.bold(), format!("{:4}", rpc_stats.requests_rate())),
|
||||
paint(Blue.bold(), format!("{:4}", rpc_stats.approximated_roundtrip())),
|
||||
),
|
||||
_ => String::new(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl ChainNotify for Informant<FullNodeInformantData> {
|
||||
fn new_blocks(&self, new_blocks: NewBlocks) {
|
||||
if new_blocks.has_more_blocks_to_import { return }
|
||||
let mut last_import = self.last_import.lock();
|
||||
let client = &self.target.client;
|
||||
fn new_blocks(&self, new_blocks: NewBlocks) {
|
||||
if new_blocks.has_more_blocks_to_import {
|
||||
return;
|
||||
}
|
||||
let mut last_import = self.last_import.lock();
|
||||
let client = &self.target.client;
|
||||
|
||||
let importing = self.target.is_major_importing();
|
||||
let ripe = Instant::now() > *last_import + Duration::from_secs(1) && !importing;
|
||||
let txs_imported = new_blocks.imported.iter()
|
||||
.take(new_blocks.imported.len().saturating_sub(if ripe { 1 } else { 0 }))
|
||||
.filter_map(|h| client.block(BlockId::Hash(*h)))
|
||||
.map(|b| b.transactions_count())
|
||||
.sum();
|
||||
let importing = self.target.is_major_importing();
|
||||
let ripe = Instant::now() > *last_import + Duration::from_secs(1) && !importing;
|
||||
let txs_imported = new_blocks
|
||||
.imported
|
||||
.iter()
|
||||
.take(
|
||||
new_blocks
|
||||
.imported
|
||||
.len()
|
||||
.saturating_sub(if ripe { 1 } else { 0 }),
|
||||
)
|
||||
.filter_map(|h| client.block(BlockId::Hash(*h)))
|
||||
.map(|b| b.transactions_count())
|
||||
.sum();
|
||||
|
||||
if ripe {
|
||||
if let Some(block) = new_blocks.imported.last().and_then(|h| client.block(BlockId::Hash(*h))) {
|
||||
let header_view = block.header_view();
|
||||
let size = block.rlp().as_raw().len();
|
||||
let (skipped, skipped_txs) = (self.skipped.load(AtomicOrdering::Relaxed) + new_blocks.imported.len() - 1, self.skipped_txs.load(AtomicOrdering::Relaxed) + txs_imported);
|
||||
info!(target: "import", "Imported {} {} ({} txs, {} Mgas, {} ms, {} KiB){}",
|
||||
Colour::White.bold().paint(format!("#{}", header_view.number())),
|
||||
Colour::White.bold().paint(format!("{}", header_view.hash())),
|
||||
Colour::Yellow.bold().paint(format!("{}", block.transactions_count())),
|
||||
Colour::Yellow.bold().paint(format!("{:.2}", header_view.gas_used().low_u64() as f32 / 1000000f32)),
|
||||
Colour::Purple.bold().paint(format!("{}", new_blocks.duration.as_milliseconds())),
|
||||
Colour::Blue.bold().paint(format!("{:.2}", size as f32 / 1024f32)),
|
||||
if skipped > 0 {
|
||||
format!(" + another {} block(s) containing {} tx(s)",
|
||||
Colour::Red.bold().paint(format!("{}", skipped)),
|
||||
Colour::Red.bold().paint(format!("{}", skipped_txs))
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
);
|
||||
self.skipped.store(0, AtomicOrdering::Relaxed);
|
||||
self.skipped_txs.store(0, AtomicOrdering::Relaxed);
|
||||
*last_import = Instant::now();
|
||||
}
|
||||
} else {
|
||||
self.skipped.fetch_add(new_blocks.imported.len(), AtomicOrdering::Relaxed);
|
||||
self.skipped_txs.fetch_add(txs_imported, AtomicOrdering::Relaxed);
|
||||
}
|
||||
}
|
||||
if ripe {
|
||||
if let Some(block) = new_blocks
|
||||
.imported
|
||||
.last()
|
||||
.and_then(|h| client.block(BlockId::Hash(*h)))
|
||||
{
|
||||
let header_view = block.header_view();
|
||||
let size = block.rlp().as_raw().len();
|
||||
let (skipped, skipped_txs) = (
|
||||
self.skipped.load(AtomicOrdering::Relaxed) + new_blocks.imported.len() - 1,
|
||||
self.skipped_txs.load(AtomicOrdering::Relaxed) + txs_imported,
|
||||
);
|
||||
info!(target: "import", "Imported {} {} ({} txs, {} Mgas, {} ms, {} KiB){}",
|
||||
Colour::White.bold().paint(format!("#{}", header_view.number())),
|
||||
Colour::White.bold().paint(format!("{}", header_view.hash())),
|
||||
Colour::Yellow.bold().paint(format!("{}", block.transactions_count())),
|
||||
Colour::Yellow.bold().paint(format!("{:.2}", header_view.gas_used().low_u64() as f32 / 1000000f32)),
|
||||
Colour::Purple.bold().paint(format!("{}", new_blocks.duration.as_milliseconds())),
|
||||
Colour::Blue.bold().paint(format!("{:.2}", size as f32 / 1024f32)),
|
||||
if skipped > 0 {
|
||||
format!(" + another {} block(s) containing {} tx(s)",
|
||||
Colour::Red.bold().paint(format!("{}", skipped)),
|
||||
Colour::Red.bold().paint(format!("{}", skipped_txs))
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
);
|
||||
self.skipped.store(0, AtomicOrdering::Relaxed);
|
||||
self.skipped_txs.store(0, AtomicOrdering::Relaxed);
|
||||
*last_import = Instant::now();
|
||||
}
|
||||
} else {
|
||||
self.skipped
|
||||
.fetch_add(new_blocks.imported.len(), AtomicOrdering::Relaxed);
|
||||
self.skipped_txs
|
||||
.fetch_add(txs_imported, AtomicOrdering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LightChainNotify for Informant<LightNodeInformantData> {
|
||||
fn new_headers(&self, good: &[H256]) {
|
||||
let mut last_import = self.last_import.lock();
|
||||
let client = &self.target.client;
|
||||
fn new_headers(&self, good: &[H256]) {
|
||||
let mut last_import = self.last_import.lock();
|
||||
let client = &self.target.client;
|
||||
|
||||
let importing = self.target.is_major_importing();
|
||||
let ripe = Instant::now() > *last_import + Duration::from_secs(1) && !importing;
|
||||
let importing = self.target.is_major_importing();
|
||||
let ripe = Instant::now() > *last_import + Duration::from_secs(1) && !importing;
|
||||
|
||||
if ripe {
|
||||
if let Some(header) = good.last().and_then(|h| client.block_header(BlockId::Hash(*h))) {
|
||||
info!(target: "import", "Imported {} {} ({} Mgas){}",
|
||||
Colour::White.bold().paint(format!("#{}", header.number())),
|
||||
Colour::White.bold().paint(format!("{}", header.hash())),
|
||||
Colour::Yellow.bold().paint(format!("{:.2}", header.gas_used().low_u64() as f32 / 1000000f32)),
|
||||
if good.len() > 1 {
|
||||
format!(" + another {} header(s)",
|
||||
Colour::Red.bold().paint(format!("{}", good.len() - 1)))
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
);
|
||||
*last_import = Instant::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
if ripe {
|
||||
if let Some(header) = good
|
||||
.last()
|
||||
.and_then(|h| client.block_header(BlockId::Hash(*h)))
|
||||
{
|
||||
info!(target: "import", "Imported {} {} ({} Mgas){}",
|
||||
Colour::White.bold().paint(format!("#{}", header.number())),
|
||||
Colour::White.bold().paint(format!("{}", header.hash())),
|
||||
Colour::Yellow.bold().paint(format!("{:.2}", header.gas_used().low_u64() as f32 / 1000000f32)),
|
||||
if good.len() > 1 {
|
||||
format!(" + another {} header(s)",
|
||||
Colour::Red.bold().paint(format!("{}", good.len() - 1)))
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
);
|
||||
*last_import = Instant::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const INFO_TIMER: TimerToken = 0;
|
||||
|
||||
impl<T: InformantData> IoHandler<ClientIoMessage> for Informant<T> {
|
||||
fn initialize(&self, io: &IoContext<ClientIoMessage>) {
|
||||
io.register_timer(INFO_TIMER, Duration::from_secs(5)).expect("Error registering timer");
|
||||
}
|
||||
fn initialize(&self, io: &IoContext<ClientIoMessage>) {
|
||||
io.register_timer(INFO_TIMER, Duration::from_secs(5))
|
||||
.expect("Error registering timer");
|
||||
}
|
||||
|
||||
fn timeout(&self, _io: &IoContext<ClientIoMessage>, timer: TimerToken) {
|
||||
if timer == INFO_TIMER && !self.in_shutdown.load(AtomicOrdering::SeqCst) {
|
||||
self.tick();
|
||||
}
|
||||
}
|
||||
fn timeout(&self, _io: &IoContext<ClientIoMessage>, timer: TimerToken) {
|
||||
if timer == INFO_TIMER && !self.in_shutdown.load(AtomicOrdering::SeqCst) {
|
||||
self.tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,45 +14,48 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use parity_ipfs_api::{self, AccessControlAllowOrigin, Host, Listening};
|
||||
use parity_ipfs_api::error::ServerError;
|
||||
use ethcore::client::BlockChainClient;
|
||||
use parity_ipfs_api::{self, error::ServerError, AccessControlAllowOrigin, Host, Listening};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Configuration {
|
||||
pub enabled: bool,
|
||||
pub port: u16,
|
||||
pub interface: String,
|
||||
pub cors: Option<Vec<String>>,
|
||||
pub hosts: Option<Vec<String>>,
|
||||
pub enabled: bool,
|
||||
pub port: u16,
|
||||
pub interface: String,
|
||||
pub cors: Option<Vec<String>>,
|
||||
pub hosts: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl Default for Configuration {
|
||||
fn default() -> Self {
|
||||
Configuration {
|
||||
enabled: false,
|
||||
port: 5001,
|
||||
interface: "127.0.0.1".into(),
|
||||
cors: Some(vec![]),
|
||||
hosts: Some(vec![]),
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
Configuration {
|
||||
enabled: false,
|
||||
port: 5001,
|
||||
interface: "127.0.0.1".into(),
|
||||
cors: Some(vec![]),
|
||||
hosts: Some(vec![]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_server(conf: Configuration, client: Arc<BlockChainClient>) -> Result<Option<Listening>, ServerError> {
|
||||
if !conf.enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
pub fn start_server(
|
||||
conf: Configuration,
|
||||
client: Arc<BlockChainClient>,
|
||||
) -> Result<Option<Listening>, ServerError> {
|
||||
if !conf.enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let cors = conf.cors.map(|cors| cors.into_iter().map(AccessControlAllowOrigin::from).collect());
|
||||
let hosts = conf.hosts.map(|hosts| hosts.into_iter().map(Host::from).collect());
|
||||
let cors = conf.cors.map(|cors| {
|
||||
cors.into_iter()
|
||||
.map(AccessControlAllowOrigin::from)
|
||||
.collect()
|
||||
});
|
||||
let hosts = conf
|
||||
.hosts
|
||||
.map(|hosts| hosts.into_iter().map(Host::from).collect());
|
||||
|
||||
parity_ipfs_api::start_server(
|
||||
conf.port,
|
||||
conf.interface,
|
||||
cors.into(),
|
||||
hosts.into(),
|
||||
client
|
||||
).map(Some)
|
||||
parity_ipfs_api::start_server(conf.port, conf.interface, cors.into(), hosts.into(), client)
|
||||
.map(Some)
|
||||
}
|
||||
|
||||
192
parity/lib.rs
192
parity/lib.rs
@@ -21,9 +21,9 @@ extern crate ansi_term;
|
||||
extern crate docopt;
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
extern crate atty;
|
||||
extern crate dir;
|
||||
extern crate futures;
|
||||
extern crate atty;
|
||||
extern crate jsonrpc_core;
|
||||
extern crate num_cpus;
|
||||
extern crate number_prefix;
|
||||
@@ -98,11 +98,12 @@ mod blockchain;
|
||||
mod cache;
|
||||
mod cli;
|
||||
mod configuration;
|
||||
mod export_hardcoded_sync;
|
||||
mod ipfs;
|
||||
mod db;
|
||||
mod deprecated;
|
||||
mod export_hardcoded_sync;
|
||||
mod helpers;
|
||||
mod informant;
|
||||
mod ipfs;
|
||||
mod light_helpers;
|
||||
mod modules;
|
||||
mod params;
|
||||
@@ -116,11 +117,8 @@ mod snapshot;
|
||||
mod upgrade;
|
||||
mod user_defaults;
|
||||
mod whisper;
|
||||
mod db;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::sync::Arc;
|
||||
use std::{fs::File, io::BufReader, sync::Arc};
|
||||
|
||||
use cli::Args;
|
||||
use configuration::{Cmd, Execute};
|
||||
@@ -130,93 +128,121 @@ use hash::keccak_buffer;
|
||||
#[cfg(feature = "memory_profiling")]
|
||||
use std::alloc::System;
|
||||
|
||||
pub use self::configuration::Configuration;
|
||||
pub use self::run::RunningClient;
|
||||
pub use self::{configuration::Configuration, run::RunningClient};
|
||||
pub use ethcore_logger::{setup_log, Config as LoggerConfig, RotatingLogger};
|
||||
pub use parity_rpc::PubSubSession;
|
||||
pub use ethcore_logger::{Config as LoggerConfig, setup_log, RotatingLogger};
|
||||
|
||||
#[cfg(feature = "memory_profiling")]
|
||||
#[global_allocator]
|
||||
static A: System = System;
|
||||
|
||||
fn print_hash_of(maybe_file: Option<String>) -> Result<String, String> {
|
||||
if let Some(file) = maybe_file {
|
||||
let mut f = BufReader::new(File::open(&file).map_err(|_| "Unable to open file".to_owned())?);
|
||||
let hash = keccak_buffer(&mut f).map_err(|_| "Unable to read from file".to_owned())?;
|
||||
Ok(format!("{:x}", hash))
|
||||
} else {
|
||||
Err("Streaming from standard input not yet supported. Specify a file.".to_owned())
|
||||
}
|
||||
if let Some(file) = maybe_file {
|
||||
let mut f =
|
||||
BufReader::new(File::open(&file).map_err(|_| "Unable to open file".to_owned())?);
|
||||
let hash = keccak_buffer(&mut f).map_err(|_| "Unable to read from file".to_owned())?;
|
||||
Ok(format!("{:x}", hash))
|
||||
} else {
|
||||
Err("Streaming from standard input not yet supported. Specify a file.".to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
fn run_deadlock_detection_thread() {
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use parking_lot::deadlock;
|
||||
use ansi_term::Style;
|
||||
use ansi_term::Style;
|
||||
use parking_lot::deadlock;
|
||||
use std::{thread, time::Duration};
|
||||
|
||||
info!("Starting deadlock detection thread.");
|
||||
// Create a background thread which checks for deadlocks every 10s
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
thread::sleep(Duration::from_secs(10));
|
||||
let deadlocks = deadlock::check_deadlock();
|
||||
if deadlocks.is_empty() {
|
||||
continue;
|
||||
}
|
||||
info!("Starting deadlock detection thread.");
|
||||
// Create a background thread which checks for deadlocks every 10s
|
||||
thread::spawn(move || loop {
|
||||
thread::sleep(Duration::from_secs(10));
|
||||
let deadlocks = deadlock::check_deadlock();
|
||||
if deadlocks.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
warn!("{} {} detected", deadlocks.len(), Style::new().bold().paint("deadlock(s)"));
|
||||
for (i, threads) in deadlocks.iter().enumerate() {
|
||||
warn!("{} #{}", Style::new().bold().paint("Deadlock"), i);
|
||||
for t in threads {
|
||||
warn!("Thread Id {:#?}", t.thread_id());
|
||||
warn!("{:#?}", t.backtrace());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
warn!(
|
||||
"{} {} detected",
|
||||
deadlocks.len(),
|
||||
Style::new().bold().paint("deadlock(s)")
|
||||
);
|
||||
for (i, threads) in deadlocks.iter().enumerate() {
|
||||
warn!("{} #{}", Style::new().bold().paint("Deadlock"), i);
|
||||
for t in threads {
|
||||
warn!("Thread Id {:#?}", t.thread_id());
|
||||
warn!("{:#?}", t.backtrace());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Action that Parity performed when running `start`.
|
||||
pub enum ExecutionAction {
|
||||
/// The execution didn't require starting a node, and thus has finished.
|
||||
/// Contains the string to print on stdout, if any.
|
||||
Instant(Option<String>),
|
||||
/// The execution didn't require starting a node, and thus has finished.
|
||||
/// Contains the string to print on stdout, if any.
|
||||
Instant(Option<String>),
|
||||
|
||||
/// The client has started running and must be shut down manually by calling `shutdown`.
|
||||
///
|
||||
/// If you don't call `shutdown()`, execution will continue in the background.
|
||||
Running(RunningClient),
|
||||
/// The client has started running and must be shut down manually by calling `shutdown`.
|
||||
///
|
||||
/// If you don't call `shutdown()`, execution will continue in the background.
|
||||
Running(RunningClient),
|
||||
}
|
||||
|
||||
fn execute<Cr, Rr>(
|
||||
command: Execute,
|
||||
logger: Arc<RotatingLogger>,
|
||||
on_client_rq: Cr, on_updater_rq: Rr) -> Result<ExecutionAction, String>
|
||||
where Cr: Fn(String) + 'static + Send,
|
||||
Rr: Fn() + 'static + Send
|
||||
command: Execute,
|
||||
logger: Arc<RotatingLogger>,
|
||||
on_client_rq: Cr,
|
||||
on_updater_rq: Rr,
|
||||
) -> Result<ExecutionAction, String>
|
||||
where
|
||||
Cr: Fn(String) + 'static + Send,
|
||||
Rr: Fn() + 'static + Send,
|
||||
{
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
run_deadlock_detection_thread();
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
run_deadlock_detection_thread();
|
||||
|
||||
match command.cmd {
|
||||
Cmd::Run(run_cmd) => {
|
||||
let outcome = run::execute(run_cmd, logger, on_client_rq, on_updater_rq)?;
|
||||
Ok(ExecutionAction::Running(outcome))
|
||||
},
|
||||
Cmd::Version => Ok(ExecutionAction::Instant(Some(Args::print_version()))),
|
||||
Cmd::Hash(maybe_file) => print_hash_of(maybe_file).map(|s| ExecutionAction::Instant(Some(s))),
|
||||
Cmd::Account(account_cmd) => account::execute(account_cmd).map(|s| ExecutionAction::Instant(Some(s))),
|
||||
Cmd::ImportPresaleWallet(presale_cmd) => presale::execute(presale_cmd).map(|s| ExecutionAction::Instant(Some(s))),
|
||||
Cmd::Blockchain(blockchain_cmd) => blockchain::execute(blockchain_cmd).map(|_| ExecutionAction::Instant(None)),
|
||||
Cmd::SignerToken(ws_conf, logger_config) => signer::execute(ws_conf, logger_config).map(|s| ExecutionAction::Instant(Some(s))),
|
||||
Cmd::SignerSign { id, pwfile, port, authfile } => cli_signer::signer_sign(id, pwfile, port, authfile).map(|s| ExecutionAction::Instant(Some(s))),
|
||||
Cmd::SignerList { port, authfile } => cli_signer::signer_list(port, authfile).map(|s| ExecutionAction::Instant(Some(s))),
|
||||
Cmd::SignerReject { id, port, authfile } => cli_signer::signer_reject(id, port, authfile).map(|s| ExecutionAction::Instant(Some(s))),
|
||||
Cmd::Snapshot(snapshot_cmd) => snapshot::execute(snapshot_cmd).map(|s| ExecutionAction::Instant(Some(s))),
|
||||
Cmd::ExportHardcodedSync(export_hs_cmd) => export_hardcoded_sync::execute(export_hs_cmd).map(|s| ExecutionAction::Instant(Some(s))),
|
||||
}
|
||||
match command.cmd {
|
||||
Cmd::Run(run_cmd) => {
|
||||
let outcome = run::execute(run_cmd, logger, on_client_rq, on_updater_rq)?;
|
||||
Ok(ExecutionAction::Running(outcome))
|
||||
}
|
||||
Cmd::Version => Ok(ExecutionAction::Instant(Some(Args::print_version()))),
|
||||
Cmd::Hash(maybe_file) => {
|
||||
print_hash_of(maybe_file).map(|s| ExecutionAction::Instant(Some(s)))
|
||||
}
|
||||
Cmd::Account(account_cmd) => {
|
||||
account::execute(account_cmd).map(|s| ExecutionAction::Instant(Some(s)))
|
||||
}
|
||||
Cmd::ImportPresaleWallet(presale_cmd) => {
|
||||
presale::execute(presale_cmd).map(|s| ExecutionAction::Instant(Some(s)))
|
||||
}
|
||||
Cmd::Blockchain(blockchain_cmd) => {
|
||||
blockchain::execute(blockchain_cmd).map(|_| ExecutionAction::Instant(None))
|
||||
}
|
||||
Cmd::SignerToken(ws_conf, logger_config) => {
|
||||
signer::execute(ws_conf, logger_config).map(|s| ExecutionAction::Instant(Some(s)))
|
||||
}
|
||||
Cmd::SignerSign {
|
||||
id,
|
||||
pwfile,
|
||||
port,
|
||||
authfile,
|
||||
} => cli_signer::signer_sign(id, pwfile, port, authfile)
|
||||
.map(|s| ExecutionAction::Instant(Some(s))),
|
||||
Cmd::SignerList { port, authfile } => {
|
||||
cli_signer::signer_list(port, authfile).map(|s| ExecutionAction::Instant(Some(s)))
|
||||
}
|
||||
Cmd::SignerReject { id, port, authfile } => {
|
||||
cli_signer::signer_reject(id, port, authfile).map(|s| ExecutionAction::Instant(Some(s)))
|
||||
}
|
||||
Cmd::Snapshot(snapshot_cmd) => {
|
||||
snapshot::execute(snapshot_cmd).map(|s| ExecutionAction::Instant(Some(s)))
|
||||
}
|
||||
Cmd::ExportHardcodedSync(export_hs_cmd) => {
|
||||
export_hardcoded_sync::execute(export_hs_cmd).map(|s| ExecutionAction::Instant(Some(s)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts the parity client.
|
||||
@@ -232,19 +258,19 @@ fn execute<Cr, Rr>(
|
||||
/// On error, returns what to print on stderr.
|
||||
// FIXME: totally independent logging capability, see https://github.com/paritytech/parity-ethereum/issues/10252
|
||||
pub fn start<Cr, Rr>(
|
||||
conf: Configuration,
|
||||
logger: Arc<RotatingLogger>,
|
||||
on_client_rq: Cr,
|
||||
on_updater_rq: Rr
|
||||
conf: Configuration,
|
||||
logger: Arc<RotatingLogger>,
|
||||
on_client_rq: Cr,
|
||||
on_updater_rq: Rr,
|
||||
) -> Result<ExecutionAction, String>
|
||||
where
|
||||
Cr: Fn(String) + 'static + Send,
|
||||
Rr: Fn() + 'static + Send
|
||||
where
|
||||
Cr: Fn(String) + 'static + Send,
|
||||
Rr: Fn() + 'static + Send,
|
||||
{
|
||||
let deprecated = find_deprecated(&conf.args);
|
||||
for d in deprecated {
|
||||
println!("{}", d);
|
||||
}
|
||||
let deprecated = find_deprecated(&conf.args);
|
||||
for d in deprecated {
|
||||
println!("{}", d);
|
||||
}
|
||||
|
||||
execute(conf.into_command()?, logger, on_client_rq, on_updater_rq)
|
||||
execute(conf.into_command()?, logger, on_client_rq, on_updater_rq)
|
||||
}
|
||||
|
||||
@@ -16,21 +16,22 @@
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use ethcore::engines::{EthEngine, StateDependentProof};
|
||||
use ethcore::machine::EthereumMachine;
|
||||
use sync::{LightSync, LightNetworkDispatcher};
|
||||
use types::encoded;
|
||||
use types::header::Header;
|
||||
use types::receipt::Receipt;
|
||||
use ethcore::{
|
||||
engines::{EthEngine, StateDependentProof},
|
||||
machine::EthereumMachine,
|
||||
};
|
||||
use sync::{LightNetworkDispatcher, LightSync};
|
||||
use types::{encoded, header::Header, receipt::Receipt};
|
||||
|
||||
use futures::{future, Future};
|
||||
use futures::future::Either;
|
||||
use futures::{future, future::Either, Future};
|
||||
|
||||
use light::client::fetch::ChainDataFetcher;
|
||||
use light::on_demand::{request, OnDemand, OnDemandRequester};
|
||||
use light::{
|
||||
client::fetch::ChainDataFetcher,
|
||||
on_demand::{request, OnDemand, OnDemandRequester},
|
||||
};
|
||||
|
||||
use parking_lot::RwLock;
|
||||
use ethereum_types::H256;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
const ALL_VALID_BACKREFS: &str = "no back-references, therefore all back-references valid; qed";
|
||||
|
||||
@@ -38,57 +39,62 @@ type BoxFuture<T, E> = Box<Future<Item = T, Error = E>>;
|
||||
|
||||
/// Allows on-demand fetch of data useful for the light client.
|
||||
pub struct EpochFetch {
|
||||
/// A handle to the sync service.
|
||||
pub sync: Arc<RwLock<Weak<LightSync>>>,
|
||||
/// The on-demand request service.
|
||||
pub on_demand: Arc<OnDemand>,
|
||||
/// A handle to the sync service.
|
||||
pub sync: Arc<RwLock<Weak<LightSync>>>,
|
||||
/// The on-demand request service.
|
||||
pub on_demand: Arc<OnDemand>,
|
||||
}
|
||||
|
||||
impl EpochFetch {
|
||||
fn request<T>(&self, req: T) -> BoxFuture<T::Out, &'static str>
|
||||
where T: Send + request::RequestAdapter + 'static, T::Out: Send + 'static
|
||||
{
|
||||
Box::new(match self.sync.read().upgrade() {
|
||||
Some(sync) => {
|
||||
let on_demand = &self.on_demand;
|
||||
let maybe_future = sync.with_context(move |ctx| {
|
||||
on_demand.request(ctx, req).expect(ALL_VALID_BACKREFS)
|
||||
});
|
||||
fn request<T>(&self, req: T) -> BoxFuture<T::Out, &'static str>
|
||||
where
|
||||
T: Send + request::RequestAdapter + 'static,
|
||||
T::Out: Send + 'static,
|
||||
{
|
||||
Box::new(match self.sync.read().upgrade() {
|
||||
Some(sync) => {
|
||||
let on_demand = &self.on_demand;
|
||||
let maybe_future = sync.with_context(move |ctx| {
|
||||
on_demand.request(ctx, req).expect(ALL_VALID_BACKREFS)
|
||||
});
|
||||
|
||||
match maybe_future {
|
||||
Some(x) => Either::A(x.map_err(|_| "Request canceled")),
|
||||
None => Either::B(future::err("Unable to access network.")),
|
||||
}
|
||||
}
|
||||
None => Either::B(future::err("Unable to access network")),
|
||||
})
|
||||
}
|
||||
match maybe_future {
|
||||
Some(x) => Either::A(x.map_err(|_| "Request canceled")),
|
||||
None => Either::B(future::err("Unable to access network.")),
|
||||
}
|
||||
}
|
||||
None => Either::B(future::err("Unable to access network")),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ChainDataFetcher for EpochFetch {
|
||||
type Error = &'static str;
|
||||
type Error = &'static str;
|
||||
|
||||
type Body = BoxFuture<encoded::Block, &'static str>;
|
||||
type Receipts = BoxFuture<Vec<Receipt>, &'static str>;
|
||||
type Transition = BoxFuture<Vec<u8>, &'static str>;
|
||||
type Body = BoxFuture<encoded::Block, &'static str>;
|
||||
type Receipts = BoxFuture<Vec<Receipt>, &'static str>;
|
||||
type Transition = BoxFuture<Vec<u8>, &'static str>;
|
||||
|
||||
fn block_body(&self, header: &Header) -> Self::Body {
|
||||
self.request(request::Body(header.encoded().into()))
|
||||
}
|
||||
fn block_body(&self, header: &Header) -> Self::Body {
|
||||
self.request(request::Body(header.encoded().into()))
|
||||
}
|
||||
|
||||
/// Fetch block receipts.
|
||||
fn block_receipts(&self, header: &Header) -> Self::Receipts {
|
||||
self.request(request::BlockReceipts(header.encoded().into()))
|
||||
}
|
||||
/// Fetch block receipts.
|
||||
fn block_receipts(&self, header: &Header) -> Self::Receipts {
|
||||
self.request(request::BlockReceipts(header.encoded().into()))
|
||||
}
|
||||
|
||||
/// Fetch epoch transition proof at given header.
|
||||
fn epoch_transition(&self, hash: H256, engine: Arc<EthEngine>, checker: Arc<StateDependentProof<EthereumMachine>>)
|
||||
-> Self::Transition
|
||||
{
|
||||
self.request(request::Signal {
|
||||
hash: hash,
|
||||
engine: engine,
|
||||
proof_check: checker,
|
||||
})
|
||||
}
|
||||
/// Fetch epoch transition proof at given header.
|
||||
fn epoch_transition(
|
||||
&self,
|
||||
hash: H256,
|
||||
engine: Arc<EthEngine>,
|
||||
checker: Arc<StateDependentProof<EthereumMachine>>,
|
||||
) -> Self::Transition {
|
||||
self.request(request::Signal {
|
||||
hash: hash,
|
||||
engine: engine,
|
||||
proof_check: checker,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,136 +30,161 @@ extern crate lazy_static;
|
||||
|
||||
mod rotating;
|
||||
|
||||
use std::{env, thread, fs};
|
||||
use std::sync::{Weak, Arc};
|
||||
use std::io::Write;
|
||||
use env_logger::{Builder as LogBuilder, Formatter};
|
||||
use regex::Regex;
|
||||
use ansi_term::Colour;
|
||||
use env_logger::{Builder as LogBuilder, Formatter};
|
||||
use parking_lot::Mutex;
|
||||
use regex::Regex;
|
||||
use std::{
|
||||
env, fs,
|
||||
io::Write,
|
||||
sync::{Arc, Weak},
|
||||
thread,
|
||||
};
|
||||
|
||||
pub use rotating::{RotatingLogger, init_log};
|
||||
pub use rotating::{init_log, RotatingLogger};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Config {
|
||||
pub mode: Option<String>,
|
||||
pub color: bool,
|
||||
pub file: Option<String>,
|
||||
pub mode: Option<String>,
|
||||
pub color: bool,
|
||||
pub file: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
mode: None,
|
||||
color: !cfg!(windows),
|
||||
file: None,
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
mode: None,
|
||||
color: !cfg!(windows),
|
||||
file: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref ROTATING_LOGGER : Mutex<Weak<RotatingLogger>> = Mutex::new(Default::default());
|
||||
static ref ROTATING_LOGGER: Mutex<Weak<RotatingLogger>> = Mutex::new(Default::default());
|
||||
}
|
||||
|
||||
/// Sets up the logger
|
||||
pub fn setup_log(config: &Config) -> Result<Arc<RotatingLogger>, String> {
|
||||
use rlog::*;
|
||||
use rlog::*;
|
||||
|
||||
let mut levels = String::new();
|
||||
let mut builder = LogBuilder::new();
|
||||
// Disable info logging by default for some modules:
|
||||
builder.filter(Some("ws"), LevelFilter::Warn);
|
||||
builder.filter(Some("hyper"), LevelFilter::Warn);
|
||||
builder.filter(Some("rustls"), LevelFilter::Error);
|
||||
// Enable info for others.
|
||||
builder.filter(None, LevelFilter::Info);
|
||||
let mut levels = String::new();
|
||||
let mut builder = LogBuilder::new();
|
||||
// Disable info logging by default for some modules:
|
||||
builder.filter(Some("ws"), LevelFilter::Warn);
|
||||
builder.filter(Some("hyper"), LevelFilter::Warn);
|
||||
builder.filter(Some("rustls"), LevelFilter::Error);
|
||||
// Enable info for others.
|
||||
builder.filter(None, LevelFilter::Info);
|
||||
|
||||
if let Ok(lvl) = env::var("RUST_LOG") {
|
||||
levels.push_str(&lvl);
|
||||
levels.push_str(",");
|
||||
builder.parse(&lvl);
|
||||
}
|
||||
if let Ok(lvl) = env::var("RUST_LOG") {
|
||||
levels.push_str(&lvl);
|
||||
levels.push_str(",");
|
||||
builder.parse(&lvl);
|
||||
}
|
||||
|
||||
if let Some(ref s) = config.mode {
|
||||
levels.push_str(s);
|
||||
builder.parse(s);
|
||||
}
|
||||
if let Some(ref s) = config.mode {
|
||||
levels.push_str(s);
|
||||
builder.parse(s);
|
||||
}
|
||||
|
||||
let isatty = atty::is(atty::Stream::Stderr);
|
||||
let enable_color = config.color && isatty;
|
||||
let logs = Arc::new(RotatingLogger::new(levels));
|
||||
let logger = logs.clone();
|
||||
let mut open_options = fs::OpenOptions::new();
|
||||
let isatty = atty::is(atty::Stream::Stderr);
|
||||
let enable_color = config.color && isatty;
|
||||
let logs = Arc::new(RotatingLogger::new(levels));
|
||||
let logger = logs.clone();
|
||||
let mut open_options = fs::OpenOptions::new();
|
||||
|
||||
let maybe_file = match config.file.as_ref() {
|
||||
Some(f) => Some(open_options
|
||||
.append(true).create(true).open(f)
|
||||
.map_err(|e| format!("Cannot write to log file given: {}, {}", f, e))?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let format = move |buf: &mut Formatter, record: &Record| {
|
||||
let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap();
|
||||
|
||||
let with_color = if max_level() <= LevelFilter::Info {
|
||||
format!("{} {}", Colour::Black.bold().paint(timestamp), record.args())
|
||||
} else {
|
||||
let name = thread::current().name().map_or_else(Default::default, |x| format!("{}", Colour::Blue.bold().paint(x)));
|
||||
format!("{} {} {} {} {}", Colour::Black.bold().paint(timestamp), name, record.level(), record.target(), record.args())
|
||||
};
|
||||
|
||||
let removed_color = kill_color(with_color.as_ref());
|
||||
|
||||
let ret = match enable_color {
|
||||
true => with_color,
|
||||
false => removed_color.clone(),
|
||||
};
|
||||
|
||||
if let Some(mut file) = maybe_file.as_ref() {
|
||||
// ignore errors - there's nothing we can do
|
||||
let _ = file.write_all(removed_color.as_bytes());
|
||||
let _ = file.write_all(b"\n");
|
||||
}
|
||||
logger.append(removed_color);
|
||||
if !isatty && record.level() <= Level::Info && atty::is(atty::Stream::Stdout) {
|
||||
// duplicate INFO/WARN output to console
|
||||
println!("{}", ret);
|
||||
}
|
||||
|
||||
writeln!(buf, "{}", ret)
|
||||
let maybe_file = match config.file.as_ref() {
|
||||
Some(f) => Some(
|
||||
open_options
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(f)
|
||||
.map_err(|e| format!("Cannot write to log file given: {}, {}", f, e))?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
|
||||
builder.format(format);
|
||||
builder.try_init()
|
||||
.and_then(|_| {
|
||||
*ROTATING_LOGGER.lock() = Arc::downgrade(&logs);
|
||||
Ok(logs)
|
||||
})
|
||||
// couldn't create new logger - try to fall back on previous logger.
|
||||
.or_else(|err| match ROTATING_LOGGER.lock().upgrade() {
|
||||
Some(l) => Ok(l),
|
||||
// no previous logger. fatal.
|
||||
None => Err(format!("{:?}", err)),
|
||||
})
|
||||
let format = move |buf: &mut Formatter, record: &Record| {
|
||||
let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap();
|
||||
|
||||
let with_color = if max_level() <= LevelFilter::Info {
|
||||
format!(
|
||||
"{} {}",
|
||||
Colour::Black.bold().paint(timestamp),
|
||||
record.args()
|
||||
)
|
||||
} else {
|
||||
let name = thread::current().name().map_or_else(Default::default, |x| {
|
||||
format!("{}", Colour::Blue.bold().paint(x))
|
||||
});
|
||||
format!(
|
||||
"{} {} {} {} {}",
|
||||
Colour::Black.bold().paint(timestamp),
|
||||
name,
|
||||
record.level(),
|
||||
record.target(),
|
||||
record.args()
|
||||
)
|
||||
};
|
||||
|
||||
let removed_color = kill_color(with_color.as_ref());
|
||||
|
||||
let ret = match enable_color {
|
||||
true => with_color,
|
||||
false => removed_color.clone(),
|
||||
};
|
||||
|
||||
if let Some(mut file) = maybe_file.as_ref() {
|
||||
// ignore errors - there's nothing we can do
|
||||
let _ = file.write_all(removed_color.as_bytes());
|
||||
let _ = file.write_all(b"\n");
|
||||
}
|
||||
logger.append(removed_color);
|
||||
if !isatty && record.level() <= Level::Info && atty::is(atty::Stream::Stdout) {
|
||||
// duplicate INFO/WARN output to console
|
||||
println!("{}", ret);
|
||||
}
|
||||
|
||||
writeln!(buf, "{}", ret)
|
||||
};
|
||||
|
||||
builder.format(format);
|
||||
builder
|
||||
.try_init()
|
||||
.and_then(|_| {
|
||||
*ROTATING_LOGGER.lock() = Arc::downgrade(&logs);
|
||||
Ok(logs)
|
||||
})
|
||||
// couldn't create new logger - try to fall back on previous logger.
|
||||
.or_else(|err| match ROTATING_LOGGER.lock().upgrade() {
|
||||
Some(l) => Ok(l),
|
||||
// no previous logger. fatal.
|
||||
None => Err(format!("{:?}", err)),
|
||||
})
|
||||
}
|
||||
|
||||
fn kill_color(s: &str) -> String {
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new("\x1b\\[[^m]+m").unwrap();
|
||||
}
|
||||
RE.replace_all(s, "").to_string()
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new("\x1b\\[[^m]+m").unwrap();
|
||||
}
|
||||
RE.replace_all(s, "").to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_colour() {
|
||||
let before = "test";
|
||||
let after = kill_color(&Colour::Red.bold().paint(before));
|
||||
assert_eq!(after, "test");
|
||||
let before = "test";
|
||||
let after = kill_color(&Colour::Red.bold().paint(before));
|
||||
assert_eq!(after, "test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_multiple_colour() {
|
||||
let t = format!("{} {}", Colour::Red.bold().paint("test"), Colour::White.normal().paint("again"));
|
||||
let after = kill_color(&t);
|
||||
assert_eq!(after, "test again");
|
||||
let t = format!(
|
||||
"{} {}",
|
||||
Colour::Red.bold().paint("test"),
|
||||
Colour::White.normal().paint("again")
|
||||
);
|
||||
let after = kill_color(&t);
|
||||
assert_eq!(after, "test again");
|
||||
}
|
||||
|
||||
@@ -16,108 +16,106 @@
|
||||
|
||||
//! Common log helper functions
|
||||
|
||||
use std::env;
|
||||
use rlog::LevelFilter;
|
||||
use env_logger::Builder as LogBuilder;
|
||||
use arrayvec::ArrayVec;
|
||||
use env_logger::Builder as LogBuilder;
|
||||
use rlog::LevelFilter;
|
||||
use std::env;
|
||||
|
||||
use parking_lot::{RwLock, RwLockReadGuard};
|
||||
|
||||
lazy_static! {
|
||||
static ref LOG_DUMMY: () = {
|
||||
let mut builder = LogBuilder::new();
|
||||
builder.filter(None, LevelFilter::Info);
|
||||
static ref LOG_DUMMY: () = {
|
||||
let mut builder = LogBuilder::new();
|
||||
builder.filter(None, LevelFilter::Info);
|
||||
|
||||
if let Ok(log) = env::var("RUST_LOG") {
|
||||
builder.parse(&log);
|
||||
}
|
||||
if let Ok(log) = env::var("RUST_LOG") {
|
||||
builder.parse(&log);
|
||||
}
|
||||
|
||||
if !builder.try_init().is_ok() {
|
||||
println!("logger initialization failed!");
|
||||
}
|
||||
};
|
||||
if !builder.try_init().is_ok() {
|
||||
println!("logger initialization failed!");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Intialize log with default settings
|
||||
pub fn init_log() {
|
||||
*LOG_DUMMY
|
||||
*LOG_DUMMY
|
||||
}
|
||||
|
||||
const LOG_SIZE : usize = 128;
|
||||
const LOG_SIZE: usize = 128;
|
||||
|
||||
/// Logger implementation that keeps up to `LOG_SIZE` log elements.
|
||||
pub struct RotatingLogger {
|
||||
/// Defined logger levels
|
||||
levels: String,
|
||||
/// Logs array. Latest log is always at index 0
|
||||
logs: RwLock<ArrayVec<[String; LOG_SIZE]>>,
|
||||
/// Defined logger levels
|
||||
levels: String,
|
||||
/// Logs array. Latest log is always at index 0
|
||||
logs: RwLock<ArrayVec<[String; LOG_SIZE]>>,
|
||||
}
|
||||
|
||||
impl RotatingLogger {
|
||||
/// Creates new `RotatingLogger` with given levels.
|
||||
/// It does not enforce levels - it's just read only.
|
||||
pub fn new(levels: String) -> Self {
|
||||
RotatingLogger {
|
||||
levels: levels,
|
||||
logs: RwLock::new(ArrayVec::<[_; LOG_SIZE]>::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new `RotatingLogger` with given levels.
|
||||
/// It does not enforce levels - it's just read only.
|
||||
pub fn new(levels: String) -> Self {
|
||||
RotatingLogger {
|
||||
levels: levels,
|
||||
logs: RwLock::new(ArrayVec::<[_; LOG_SIZE]>::new()),
|
||||
}
|
||||
}
|
||||
/// Append new log entry
|
||||
pub fn append(&self, log: String) {
|
||||
let mut logs = self.logs.write();
|
||||
if logs.is_full() {
|
||||
logs.pop();
|
||||
}
|
||||
logs.insert(0, log);
|
||||
}
|
||||
|
||||
/// Append new log entry
|
||||
pub fn append(&self, log: String) {
|
||||
let mut logs = self.logs.write();
|
||||
if logs.is_full() {
|
||||
logs.pop();
|
||||
}
|
||||
logs.insert(0, log);
|
||||
}
|
||||
|
||||
/// Return levels
|
||||
pub fn levels(&self) -> &str {
|
||||
&self.levels
|
||||
}
|
||||
|
||||
/// Return logs
|
||||
pub fn logs(&self) -> RwLockReadGuard<ArrayVec<[String; LOG_SIZE]>> {
|
||||
self.logs.read()
|
||||
}
|
||||
/// Return levels
|
||||
pub fn levels(&self) -> &str {
|
||||
&self.levels
|
||||
}
|
||||
|
||||
/// Return logs
|
||||
pub fn logs(&self) -> RwLockReadGuard<ArrayVec<[String; LOG_SIZE]>> {
|
||||
self.logs.read()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::RotatingLogger;
|
||||
use super::RotatingLogger;
|
||||
|
||||
fn logger() -> RotatingLogger {
|
||||
RotatingLogger::new("test".to_owned())
|
||||
}
|
||||
fn logger() -> RotatingLogger {
|
||||
RotatingLogger::new("test".to_owned())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_log_levels() {
|
||||
// given
|
||||
let logger = logger();
|
||||
#[test]
|
||||
fn should_return_log_levels() {
|
||||
// given
|
||||
let logger = logger();
|
||||
|
||||
// when
|
||||
let levels = logger.levels();
|
||||
// when
|
||||
let levels = logger.levels();
|
||||
|
||||
// then
|
||||
assert_eq!(levels, "test");
|
||||
}
|
||||
// then
|
||||
assert_eq!(levels, "test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_latest_logs() {
|
||||
// given
|
||||
let logger = logger();
|
||||
#[test]
|
||||
fn should_return_latest_logs() {
|
||||
// given
|
||||
let logger = logger();
|
||||
|
||||
// when
|
||||
logger.append("a".to_owned());
|
||||
logger.append("b".to_owned());
|
||||
// when
|
||||
logger.append("a".to_owned());
|
||||
logger.append("b".to_owned());
|
||||
|
||||
// then
|
||||
let logs = logger.logs();
|
||||
assert_eq!(logs[0], "b".to_owned());
|
||||
assert_eq!(logs[1], "a".to_owned());
|
||||
assert_eq!(logs.len(), 2);
|
||||
}
|
||||
// then
|
||||
let logs = logger.logs();
|
||||
assert_eq!(logs[0], "b".to_owned());
|
||||
assert_eq!(logs[1], "a".to_owned());
|
||||
assert_eq!(logs.len(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
702
parity/main.rs
702
parity/main.rs
@@ -23,30 +23,36 @@ extern crate dir;
|
||||
extern crate fdlimit;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate ansi_term;
|
||||
extern crate panic_hook;
|
||||
extern crate parity_daemonize;
|
||||
extern crate parity_ethereum;
|
||||
extern crate parking_lot;
|
||||
extern crate parity_daemonize;
|
||||
extern crate ansi_term;
|
||||
|
||||
#[cfg(windows)] extern crate winapi;
|
||||
extern crate ethcore_logger;
|
||||
#[cfg(windows)]
|
||||
extern crate winapi;
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{remove_file, metadata, File, create_dir_all};
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::{process, env};
|
||||
use std::{
|
||||
env,
|
||||
ffi::OsString,
|
||||
fs::{create_dir_all, metadata, remove_file, File},
|
||||
io::{Read, Write},
|
||||
path::PathBuf,
|
||||
process,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use ansi_term::Colour;
|
||||
use ctrlc::CtrlC;
|
||||
use dir::default_hypervisor_path;
|
||||
use fdlimit::raise_fd_limit;
|
||||
use ethcore_logger::setup_log;
|
||||
use parity_ethereum::{start, ExecutionAction};
|
||||
use fdlimit::raise_fd_limit;
|
||||
use parity_daemonize::AsHandle;
|
||||
use parity_ethereum::{start, ExecutionAction};
|
||||
use parking_lot::{Condvar, Mutex};
|
||||
|
||||
const PLEASE_RESTART_EXIT_CODE: i32 = 69;
|
||||
@@ -54,72 +60,81 @@ const PARITY_EXECUTABLE_NAME: &str = "parity";
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
BinaryNotFound,
|
||||
ExitCode(i32),
|
||||
Restart,
|
||||
Unknown
|
||||
BinaryNotFound,
|
||||
ExitCode(i32),
|
||||
Restart,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
fn update_path(name: &str) -> PathBuf {
|
||||
let mut dest = default_hypervisor_path();
|
||||
dest.push(name);
|
||||
dest
|
||||
let mut dest = default_hypervisor_path();
|
||||
dest.push(name);
|
||||
dest
|
||||
}
|
||||
|
||||
fn latest_exe_path() -> Result<PathBuf, Error> {
|
||||
File::open(update_path("latest")).and_then(|mut f| {
|
||||
let mut exe_path = String::new();
|
||||
trace!(target: "updater", "latest binary path: {:?}", f);
|
||||
f.read_to_string(&mut exe_path).map(|_| update_path(&exe_path))
|
||||
})
|
||||
.or(Err(Error::BinaryNotFound))
|
||||
File::open(update_path("latest"))
|
||||
.and_then(|mut f| {
|
||||
let mut exe_path = String::new();
|
||||
trace!(target: "updater", "latest binary path: {:?}", f);
|
||||
f.read_to_string(&mut exe_path)
|
||||
.map(|_| update_path(&exe_path))
|
||||
})
|
||||
.or(Err(Error::BinaryNotFound))
|
||||
}
|
||||
|
||||
fn latest_binary_is_newer(current_binary: &Option<PathBuf>, latest_binary: &Option<PathBuf>) -> bool {
|
||||
match (
|
||||
current_binary
|
||||
.as_ref()
|
||||
.and_then(|p| metadata(p.as_path()).ok())
|
||||
.and_then(|m| m.modified().ok()),
|
||||
latest_binary
|
||||
.as_ref()
|
||||
.and_then(|p| metadata(p.as_path()).ok())
|
||||
.and_then(|m| m.modified().ok())
|
||||
) {
|
||||
(Some(latest_exe_time), Some(this_exe_time)) if latest_exe_time > this_exe_time => true,
|
||||
_ => false,
|
||||
}
|
||||
fn latest_binary_is_newer(
|
||||
current_binary: &Option<PathBuf>,
|
||||
latest_binary: &Option<PathBuf>,
|
||||
) -> bool {
|
||||
match (
|
||||
current_binary
|
||||
.as_ref()
|
||||
.and_then(|p| metadata(p.as_path()).ok())
|
||||
.and_then(|m| m.modified().ok()),
|
||||
latest_binary
|
||||
.as_ref()
|
||||
.and_then(|p| metadata(p.as_path()).ok())
|
||||
.and_then(|m| m.modified().ok()),
|
||||
) {
|
||||
(Some(latest_exe_time), Some(this_exe_time)) if latest_exe_time > this_exe_time => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_spec_name_override(spec_name: &str) {
|
||||
if let Err(e) = create_dir_all(default_hypervisor_path())
|
||||
.and_then(|_| File::create(update_path("spec_name_override"))
|
||||
.and_then(|mut f| f.write_all(spec_name.as_bytes())))
|
||||
{
|
||||
warn!("Couldn't override chain spec: {} at {:?}", e, update_path("spec_name_override"));
|
||||
}
|
||||
if let Err(e) = create_dir_all(default_hypervisor_path()).and_then(|_| {
|
||||
File::create(update_path("spec_name_override"))
|
||||
.and_then(|mut f| f.write_all(spec_name.as_bytes()))
|
||||
}) {
|
||||
warn!(
|
||||
"Couldn't override chain spec: {} at {:?}",
|
||||
e,
|
||||
update_path("spec_name_override")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn take_spec_name_override() -> Option<String> {
|
||||
let p = update_path("spec_name_override");
|
||||
let r = File::open(p.clone())
|
||||
.ok()
|
||||
.and_then(|mut f| {
|
||||
let mut spec_name = String::new();
|
||||
f.read_to_string(&mut spec_name).ok().map(|_| spec_name)
|
||||
});
|
||||
let _ = remove_file(p);
|
||||
r
|
||||
let p = update_path("spec_name_override");
|
||||
let r = File::open(p.clone()).ok().and_then(|mut f| {
|
||||
let mut spec_name = String::new();
|
||||
f.read_to_string(&mut spec_name).ok().map(|_| spec_name)
|
||||
});
|
||||
let _ = remove_file(p);
|
||||
r
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn global_cleanup() {
|
||||
// We need to cleanup all sockets before spawning another Parity process. This makes sure everything is cleaned up.
|
||||
// The loop is required because of internal reference counter for winsock dll. We don't know how many crates we use do
|
||||
// initialize it. There's at least 2 now.
|
||||
for _ in 0.. 10 {
|
||||
unsafe { ::winapi::um::winsock2::WSACleanup(); }
|
||||
}
|
||||
// We need to cleanup all sockets before spawning another Parity process. This makes sure everything is cleaned up.
|
||||
// The loop is required because of internal reference counter for winsock dll. We don't know how many crates we use do
|
||||
// initialize it. There's at least 2 now.
|
||||
for _ in 0..10 {
|
||||
unsafe {
|
||||
::winapi::um::winsock2::WSACleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
@@ -127,12 +142,12 @@ fn global_init() {}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn global_init() {
|
||||
// When restarting in the same process this reinits windows sockets.
|
||||
unsafe {
|
||||
const WS_VERSION: u16 = 0x202;
|
||||
let mut wsdata: ::winapi::um::winsock2::WSADATA = ::std::mem::zeroed();
|
||||
::winapi::um::winsock2::WSAStartup(WS_VERSION, &mut wsdata);
|
||||
}
|
||||
// When restarting in the same process this reinits windows sockets.
|
||||
unsafe {
|
||||
const WS_VERSION: u16 = 0x202;
|
||||
let mut wsdata: ::winapi::um::winsock2::WSADATA = ::std::mem::zeroed();
|
||||
::winapi::um::winsock2::WSAStartup(WS_VERSION, &mut wsdata);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
@@ -140,228 +155,246 @@ fn global_cleanup() {}
|
||||
|
||||
// Starts parity binary installed via `parity-updater` and returns the code it exits with.
|
||||
fn run_parity() -> Result<(), Error> {
|
||||
global_init();
|
||||
global_init();
|
||||
|
||||
let prefix = vec![OsString::from("--can-restart"), OsString::from("--force-direct")];
|
||||
let prefix = vec![
|
||||
OsString::from("--can-restart"),
|
||||
OsString::from("--force-direct"),
|
||||
];
|
||||
|
||||
let res: Result<(), Error> = latest_exe_path()
|
||||
.and_then(|exe| process::Command::new(exe)
|
||||
.args(&(env::args_os().skip(1).chain(prefix.into_iter()).collect::<Vec<_>>()))
|
||||
.status()
|
||||
.ok()
|
||||
.map_or(Err(Error::Unknown), |es| {
|
||||
match es.code() {
|
||||
// Process success
|
||||
Some(0) => Ok(()),
|
||||
// Please restart
|
||||
Some(PLEASE_RESTART_EXIT_CODE) => Err(Error::Restart),
|
||||
// Process error code `c`
|
||||
Some(c) => Err(Error::ExitCode(c)),
|
||||
// Unknown error, couldn't determine error code
|
||||
_ => Err(Error::Unknown),
|
||||
}
|
||||
})
|
||||
);
|
||||
let res: Result<(), Error> = latest_exe_path().and_then(|exe| {
|
||||
process::Command::new(exe)
|
||||
.args(
|
||||
&(env::args_os()
|
||||
.skip(1)
|
||||
.chain(prefix.into_iter())
|
||||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.status()
|
||||
.ok()
|
||||
.map_or(Err(Error::Unknown), |es| {
|
||||
match es.code() {
|
||||
// Process success
|
||||
Some(0) => Ok(()),
|
||||
// Please restart
|
||||
Some(PLEASE_RESTART_EXIT_CODE) => Err(Error::Restart),
|
||||
// Process error code `c`
|
||||
Some(c) => Err(Error::ExitCode(c)),
|
||||
// Unknown error, couldn't determine error code
|
||||
_ => Err(Error::Unknown),
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
global_cleanup();
|
||||
res
|
||||
global_cleanup();
|
||||
res
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Status used to exit or restart the program.
|
||||
struct ExitStatus {
|
||||
/// Whether the program panicked.
|
||||
panicking: bool,
|
||||
/// Whether the program should exit.
|
||||
should_exit: bool,
|
||||
/// Whether the program should restart.
|
||||
should_restart: bool,
|
||||
/// If a restart happens, whether a new chain spec should be used.
|
||||
spec_name_override: Option<String>,
|
||||
/// Whether the program panicked.
|
||||
panicking: bool,
|
||||
/// Whether the program should exit.
|
||||
should_exit: bool,
|
||||
/// Whether the program should restart.
|
||||
should_restart: bool,
|
||||
/// If a restart happens, whether a new chain spec should be used.
|
||||
spec_name_override: Option<String>,
|
||||
}
|
||||
|
||||
// Run `locally installed version` of parity (i.e, not installed via `parity-updater`)
|
||||
// Returns the exit error code.
|
||||
fn main_direct(force_can_restart: bool) -> i32 {
|
||||
global_init();
|
||||
global_init();
|
||||
|
||||
let mut conf = {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
parity_ethereum::Configuration::parse_cli(&args).unwrap_or_else(|e| e.exit())
|
||||
};
|
||||
let mut conf = {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
parity_ethereum::Configuration::parse_cli(&args).unwrap_or_else(|e| e.exit())
|
||||
};
|
||||
|
||||
let logger = setup_log(&conf.logger_config()).unwrap_or_else(|e| {
|
||||
eprintln!("{}", e);
|
||||
process::exit(2)
|
||||
});
|
||||
let logger = setup_log(&conf.logger_config()).unwrap_or_else(|e| {
|
||||
eprintln!("{}", e);
|
||||
process::exit(2)
|
||||
});
|
||||
|
||||
if let Some(spec_override) = take_spec_name_override() {
|
||||
conf.args.flag_testnet = false;
|
||||
conf.args.arg_chain = spec_override;
|
||||
}
|
||||
if let Some(spec_override) = take_spec_name_override() {
|
||||
conf.args.flag_testnet = false;
|
||||
conf.args.arg_chain = spec_override;
|
||||
}
|
||||
|
||||
// FIXME: `pid_file` shouldn't need to cloned here
|
||||
// see: `https://github.com/paritytech/parity-daemonize/pull/13` for more info
|
||||
let handle = if let Some(pid) = conf.args.arg_daemon_pid_file.clone() {
|
||||
info!("{}", Colour::Blue.paint("starting in daemon mode").to_string());
|
||||
let _ = std::io::stdout().flush();
|
||||
// FIXME: `pid_file` shouldn't need to cloned here
|
||||
// see: `https://github.com/paritytech/parity-daemonize/pull/13` for more info
|
||||
let handle = if let Some(pid) = conf.args.arg_daemon_pid_file.clone() {
|
||||
info!(
|
||||
"{}",
|
||||
Colour::Blue.paint("starting in daemon mode").to_string()
|
||||
);
|
||||
let _ = std::io::stdout().flush();
|
||||
|
||||
match parity_daemonize::daemonize(pid) {
|
||||
Ok(h) => Some(h),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"{}",
|
||||
Colour::Red.paint(format!("{}", e))
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
match parity_daemonize::daemonize(pid) {
|
||||
Ok(h) => Some(h),
|
||||
Err(e) => {
|
||||
error!("{}", Colour::Red.paint(format!("{}", e)));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let can_restart = force_can_restart || conf.args.flag_can_restart;
|
||||
let can_restart = force_can_restart || conf.args.flag_can_restart;
|
||||
|
||||
// increase max number of open files
|
||||
raise_fd_limit();
|
||||
// increase max number of open files
|
||||
raise_fd_limit();
|
||||
|
||||
let exit = Arc::new((Mutex::new(ExitStatus {
|
||||
panicking: false,
|
||||
should_exit: false,
|
||||
should_restart: false,
|
||||
spec_name_override: None
|
||||
}), Condvar::new()));
|
||||
let exit = Arc::new((
|
||||
Mutex::new(ExitStatus {
|
||||
panicking: false,
|
||||
should_exit: false,
|
||||
should_restart: false,
|
||||
spec_name_override: None,
|
||||
}),
|
||||
Condvar::new(),
|
||||
));
|
||||
|
||||
// Double panic can happen. So when we lock `ExitStatus` after the main thread is notified, it cannot be locked
|
||||
// again.
|
||||
let exiting = Arc::new(AtomicBool::new(false));
|
||||
// Double panic can happen. So when we lock `ExitStatus` after the main thread is notified, it cannot be locked
|
||||
// again.
|
||||
let exiting = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let exec = if can_restart {
|
||||
start(
|
||||
conf,
|
||||
logger,
|
||||
{
|
||||
let e = exit.clone();
|
||||
let exiting = exiting.clone();
|
||||
move |new_chain: String| {
|
||||
if !exiting.swap(true, Ordering::SeqCst) {
|
||||
*e.0.lock() = ExitStatus {
|
||||
panicking: false,
|
||||
should_exit: true,
|
||||
should_restart: true,
|
||||
spec_name_override: Some(new_chain),
|
||||
};
|
||||
e.1.notify_all();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
let e = exit.clone();
|
||||
let exiting = exiting.clone();
|
||||
move || {
|
||||
if !exiting.swap(true, Ordering::SeqCst) {
|
||||
*e.0.lock() = ExitStatus {
|
||||
panicking: false,
|
||||
should_exit: true,
|
||||
should_restart: true,
|
||||
spec_name_override: None,
|
||||
};
|
||||
e.1.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
trace!(target: "mode", "Not hypervised: not setting exit handlers.");
|
||||
start(conf, logger, move |_| {}, move || {})
|
||||
};
|
||||
let exec = if can_restart {
|
||||
start(
|
||||
conf,
|
||||
logger,
|
||||
{
|
||||
let e = exit.clone();
|
||||
let exiting = exiting.clone();
|
||||
move |new_chain: String| {
|
||||
if !exiting.swap(true, Ordering::SeqCst) {
|
||||
*e.0.lock() = ExitStatus {
|
||||
panicking: false,
|
||||
should_exit: true,
|
||||
should_restart: true,
|
||||
spec_name_override: Some(new_chain),
|
||||
};
|
||||
e.1.notify_all();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
let e = exit.clone();
|
||||
let exiting = exiting.clone();
|
||||
move || {
|
||||
if !exiting.swap(true, Ordering::SeqCst) {
|
||||
*e.0.lock() = ExitStatus {
|
||||
panicking: false,
|
||||
should_exit: true,
|
||||
should_restart: true,
|
||||
spec_name_override: None,
|
||||
};
|
||||
e.1.notify_all();
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
trace!(target: "mode", "Not hypervised: not setting exit handlers.");
|
||||
start(conf, logger, move |_| {}, move || {})
|
||||
};
|
||||
|
||||
let res = match exec {
|
||||
Ok(result) => match result {
|
||||
ExecutionAction::Instant(Some(s)) => { println!("{}", s); 0 },
|
||||
ExecutionAction::Instant(None) => 0,
|
||||
ExecutionAction::Running(client) => {
|
||||
panic_hook::set_with({
|
||||
let e = exit.clone();
|
||||
let exiting = exiting.clone();
|
||||
move |panic_msg| {
|
||||
warn!("Panic occured, see stderr for details");
|
||||
eprintln!("{}", panic_msg);
|
||||
if !exiting.swap(true, Ordering::SeqCst) {
|
||||
*e.0.lock() = ExitStatus {
|
||||
panicking: true,
|
||||
should_exit: true,
|
||||
should_restart: false,
|
||||
spec_name_override: None,
|
||||
};
|
||||
e.1.notify_all();
|
||||
}
|
||||
}
|
||||
});
|
||||
let res = match exec {
|
||||
Ok(result) => match result {
|
||||
ExecutionAction::Instant(Some(s)) => {
|
||||
println!("{}", s);
|
||||
0
|
||||
}
|
||||
ExecutionAction::Instant(None) => 0,
|
||||
ExecutionAction::Running(client) => {
|
||||
panic_hook::set_with({
|
||||
let e = exit.clone();
|
||||
let exiting = exiting.clone();
|
||||
move |panic_msg| {
|
||||
warn!("Panic occured, see stderr for details");
|
||||
eprintln!("{}", panic_msg);
|
||||
if !exiting.swap(true, Ordering::SeqCst) {
|
||||
*e.0.lock() = ExitStatus {
|
||||
panicking: true,
|
||||
should_exit: true,
|
||||
should_restart: false,
|
||||
spec_name_override: None,
|
||||
};
|
||||
e.1.notify_all();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
CtrlC::set_handler({
|
||||
let e = exit.clone();
|
||||
let exiting = exiting.clone();
|
||||
move || {
|
||||
if !exiting.swap(true, Ordering::SeqCst) {
|
||||
*e.0.lock() = ExitStatus {
|
||||
panicking: false,
|
||||
should_exit: true,
|
||||
should_restart: false,
|
||||
spec_name_override: None,
|
||||
};
|
||||
e.1.notify_all();
|
||||
}
|
||||
}
|
||||
});
|
||||
CtrlC::set_handler({
|
||||
let e = exit.clone();
|
||||
let exiting = exiting.clone();
|
||||
move || {
|
||||
if !exiting.swap(true, Ordering::SeqCst) {
|
||||
*e.0.lock() = ExitStatus {
|
||||
panicking: false,
|
||||
should_exit: true,
|
||||
should_restart: false,
|
||||
spec_name_override: None,
|
||||
};
|
||||
e.1.notify_all();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// so the client has started successfully
|
||||
// if this is a daemon, detach from the parent process
|
||||
if let Some(mut handle) = handle {
|
||||
handle.detach()
|
||||
}
|
||||
// so the client has started successfully
|
||||
// if this is a daemon, detach from the parent process
|
||||
if let Some(mut handle) = handle {
|
||||
handle.detach()
|
||||
}
|
||||
|
||||
// Wait for signal
|
||||
let mut lock = exit.0.lock();
|
||||
if !lock.should_exit {
|
||||
let _ = exit.1.wait(&mut lock);
|
||||
}
|
||||
// Wait for signal
|
||||
let mut lock = exit.0.lock();
|
||||
if !lock.should_exit {
|
||||
let _ = exit.1.wait(&mut lock);
|
||||
}
|
||||
|
||||
client.shutdown();
|
||||
client.shutdown();
|
||||
|
||||
if lock.should_restart {
|
||||
if let Some(ref spec_name) = lock.spec_name_override {
|
||||
set_spec_name_override(&spec_name.clone());
|
||||
}
|
||||
PLEASE_RESTART_EXIT_CODE
|
||||
} else {
|
||||
if lock.panicking {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
Err(err) => {
|
||||
// error occured during start up
|
||||
// if this is a daemon, detach from the parent process
|
||||
if let Some(mut handle) = handle {
|
||||
handle.detach_with_msg(format!("{}", Colour::Red.paint(&err)))
|
||||
}
|
||||
eprintln!("{}", err);
|
||||
1
|
||||
},
|
||||
};
|
||||
if lock.should_restart {
|
||||
if let Some(ref spec_name) = lock.spec_name_override {
|
||||
set_spec_name_override(&spec_name.clone());
|
||||
}
|
||||
PLEASE_RESTART_EXIT_CODE
|
||||
} else {
|
||||
if lock.panicking {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
// error occured during start up
|
||||
// if this is a daemon, detach from the parent process
|
||||
if let Some(mut handle) = handle {
|
||||
handle.detach_with_msg(format!("{}", Colour::Red.paint(&err)))
|
||||
}
|
||||
eprintln!("{}", err);
|
||||
1
|
||||
}
|
||||
};
|
||||
|
||||
global_cleanup();
|
||||
res
|
||||
global_cleanup();
|
||||
res
|
||||
}
|
||||
|
||||
fn println_trace_main(s: String) {
|
||||
if env::var("RUST_LOG").ok().and_then(|s| s.find("main=trace")).is_some() {
|
||||
println!("{}", s);
|
||||
}
|
||||
if env::var("RUST_LOG")
|
||||
.ok()
|
||||
.and_then(|s| s.find("main=trace"))
|
||||
.is_some()
|
||||
{
|
||||
println!("{}", s);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! trace_main {
|
||||
@@ -370,86 +403,101 @@ macro_rules! trace_main {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
panic_hook::set_abort();
|
||||
panic_hook::set_abort();
|
||||
|
||||
// the user has specified to run its originally installed binary (not via `parity-updater`)
|
||||
let force_direct = std::env::args().any(|arg| arg == "--force-direct");
|
||||
// the user has specified to run its originally installed binary (not via `parity-updater`)
|
||||
let force_direct = std::env::args().any(|arg| arg == "--force-direct");
|
||||
|
||||
// absolute path to the current `binary`
|
||||
let exe_path = std::env::current_exe().ok();
|
||||
// absolute path to the current `binary`
|
||||
let exe_path = std::env::current_exe().ok();
|
||||
|
||||
// the binary is named `target/xx/yy`
|
||||
let development = exe_path
|
||||
.as_ref()
|
||||
.and_then(|p| {
|
||||
p.parent()
|
||||
.and_then(|p| p.parent())
|
||||
.and_then(|p| p.file_name())
|
||||
.map(|n| n == "target")
|
||||
})
|
||||
.unwrap_or(false);
|
||||
// the binary is named `target/xx/yy`
|
||||
let development = exe_path
|
||||
.as_ref()
|
||||
.and_then(|p| {
|
||||
p.parent()
|
||||
.and_then(|p| p.parent())
|
||||
.and_then(|p| p.file_name())
|
||||
.map(|n| n == "target")
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
// the binary is named `parity`
|
||||
let same_name = exe_path
|
||||
.as_ref()
|
||||
.map_or(false, |p| {
|
||||
p.file_stem().map_or(false, |n| n == PARITY_EXECUTABLE_NAME)
|
||||
});
|
||||
// the binary is named `parity`
|
||||
let same_name = exe_path.as_ref().map_or(false, |p| {
|
||||
p.file_stem().map_or(false, |n| n == PARITY_EXECUTABLE_NAME)
|
||||
});
|
||||
|
||||
trace_main!("Starting up {} (force-direct: {}, development: {}, same-name: {})",
|
||||
std::env::current_exe().ok().map_or_else(|| "<unknown>".into(), |x| format!("{}", x.display())),
|
||||
force_direct,
|
||||
development,
|
||||
same_name);
|
||||
trace_main!(
|
||||
"Starting up {} (force-direct: {}, development: {}, same-name: {})",
|
||||
std::env::current_exe()
|
||||
.ok()
|
||||
.map_or_else(|| "<unknown>".into(), |x| format!("{}", x.display())),
|
||||
force_direct,
|
||||
development,
|
||||
same_name
|
||||
);
|
||||
|
||||
if !force_direct && !development && same_name {
|
||||
// Try to run the latest installed version of `parity`,
|
||||
// Upon failure it falls back to the locally installed version of `parity`
|
||||
// Everything run inside a loop, so we'll be able to restart from the child into a new version seamlessly.
|
||||
loop {
|
||||
// `Path` to the latest downloaded binary
|
||||
let latest_exe = latest_exe_path().ok();
|
||||
if !force_direct && !development && same_name {
|
||||
// Try to run the latest installed version of `parity`,
|
||||
// Upon failure it falls back to the locally installed version of `parity`
|
||||
// Everything run inside a loop, so we'll be able to restart from the child into a new version seamlessly.
|
||||
loop {
|
||||
// `Path` to the latest downloaded binary
|
||||
let latest_exe = latest_exe_path().ok();
|
||||
|
||||
// `Latest´ binary exist
|
||||
let have_update = latest_exe.as_ref().map_or(false, |p| p.exists());
|
||||
// `Latest´ binary exist
|
||||
let have_update = latest_exe.as_ref().map_or(false, |p| p.exists());
|
||||
|
||||
// Canonicalized path to the current binary is not the same as to latest binary
|
||||
let canonicalized_path_not_same = exe_path
|
||||
.as_ref()
|
||||
.map_or(false, |exe| latest_exe.as_ref()
|
||||
.map_or(false, |lexe| exe.canonicalize().ok() != lexe.canonicalize().ok()));
|
||||
// Canonicalized path to the current binary is not the same as to latest binary
|
||||
let canonicalized_path_not_same = exe_path.as_ref().map_or(false, |exe| {
|
||||
latest_exe.as_ref().map_or(false, |lexe| {
|
||||
exe.canonicalize().ok() != lexe.canonicalize().ok()
|
||||
})
|
||||
});
|
||||
|
||||
// Downloaded `binary` is newer
|
||||
let update_is_newer = latest_binary_is_newer(&latest_exe, &exe_path);
|
||||
trace_main!("Starting... (have-update: {}, non-updated-current: {}, update-is-newer: {})", have_update, canonicalized_path_not_same, update_is_newer);
|
||||
// Downloaded `binary` is newer
|
||||
let update_is_newer = latest_binary_is_newer(&latest_exe, &exe_path);
|
||||
trace_main!(
|
||||
"Starting... (have-update: {}, non-updated-current: {}, update-is-newer: {})",
|
||||
have_update,
|
||||
canonicalized_path_not_same,
|
||||
update_is_newer
|
||||
);
|
||||
|
||||
let exit_code = if have_update && canonicalized_path_not_same && update_is_newer {
|
||||
trace_main!("Attempting to run latest update ({})...",
|
||||
latest_exe.as_ref().expect("guarded by have_update; latest_exe must exist for have_update; qed").display());
|
||||
match run_parity() {
|
||||
Ok(_) => 0,
|
||||
// Restart parity
|
||||
Err(Error::Restart) => PLEASE_RESTART_EXIT_CODE,
|
||||
// Fall back to local version
|
||||
Err(e) => {
|
||||
error!(target: "updater", "Updated binary could not be executed error: {:?}. Falling back to local version", e);
|
||||
main_direct(true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trace_main!("No latest update. Attempting to direct...");
|
||||
main_direct(true)
|
||||
};
|
||||
trace_main!("Latest binary exited with exit code: {}", exit_code);
|
||||
if exit_code != PLEASE_RESTART_EXIT_CODE {
|
||||
trace_main!("Quitting...");
|
||||
process::exit(exit_code);
|
||||
}
|
||||
trace!(target: "updater", "Re-running updater loop");
|
||||
}
|
||||
} else {
|
||||
trace_main!("Running direct");
|
||||
// Otherwise, we're presumably running the version we want. Just run and fall-through.
|
||||
process::exit(main_direct(false));
|
||||
}
|
||||
let exit_code = if have_update && canonicalized_path_not_same && update_is_newer {
|
||||
trace_main!(
|
||||
"Attempting to run latest update ({})...",
|
||||
latest_exe
|
||||
.as_ref()
|
||||
.expect(
|
||||
"guarded by have_update; latest_exe must exist for have_update; qed"
|
||||
)
|
||||
.display()
|
||||
);
|
||||
match run_parity() {
|
||||
Ok(_) => 0,
|
||||
// Restart parity
|
||||
Err(Error::Restart) => PLEASE_RESTART_EXIT_CODE,
|
||||
// Fall back to local version
|
||||
Err(e) => {
|
||||
error!(target: "updater", "Updated binary could not be executed error: {:?}. Falling back to local version", e);
|
||||
main_direct(true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trace_main!("No latest update. Attempting to direct...");
|
||||
main_direct(true)
|
||||
};
|
||||
trace_main!("Latest binary exited with exit code: {}", exit_code);
|
||||
if exit_code != PLEASE_RESTART_EXIT_CODE {
|
||||
trace_main!("Quitting...");
|
||||
process::exit(exit_code);
|
||||
}
|
||||
trace!(target: "updater", "Re-running updater loop");
|
||||
}
|
||||
} else {
|
||||
trace_main!("Running direct");
|
||||
// Otherwise, we're presumably running the version we want. Just run and fall-through.
|
||||
process::exit(main_direct(false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,50 +14,51 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::{Arc, mpsc};
|
||||
use std::sync::{mpsc, Arc};
|
||||
|
||||
use ethcore::client::BlockChainClient;
|
||||
use sync::{self, AttachedProtocol, SyncConfig, NetworkConfiguration, Params, ConnectionFilter};
|
||||
use ethcore::snapshot::SnapshotService;
|
||||
use ethcore::{client::BlockChainClient, snapshot::SnapshotService};
|
||||
use light::Provider;
|
||||
use sync::{self, AttachedProtocol, ConnectionFilter, NetworkConfiguration, Params, SyncConfig};
|
||||
|
||||
pub use sync::{EthSync, SyncProvider, ManageNetwork, PrivateTxHandler};
|
||||
pub use ethcore::client::ChainNotify;
|
||||
use ethcore_logger::Config as LogConfig;
|
||||
pub use sync::{EthSync, ManageNetwork, PrivateTxHandler, SyncProvider};
|
||||
|
||||
pub type SyncModules = (
|
||||
Arc<SyncProvider>,
|
||||
Arc<ManageNetwork>,
|
||||
Arc<ChainNotify>,
|
||||
mpsc::Sender<sync::PriorityTask>,
|
||||
Arc<SyncProvider>,
|
||||
Arc<ManageNetwork>,
|
||||
Arc<ChainNotify>,
|
||||
mpsc::Sender<sync::PriorityTask>,
|
||||
);
|
||||
|
||||
pub fn sync(
|
||||
config: SyncConfig,
|
||||
network_config: NetworkConfiguration,
|
||||
chain: Arc<BlockChainClient>,
|
||||
snapshot_service: Arc<SnapshotService>,
|
||||
private_tx_handler: Option<Arc<PrivateTxHandler>>,
|
||||
provider: Arc<Provider>,
|
||||
_log_settings: &LogConfig,
|
||||
attached_protos: Vec<AttachedProtocol>,
|
||||
connection_filter: Option<Arc<ConnectionFilter>>,
|
||||
config: SyncConfig,
|
||||
network_config: NetworkConfiguration,
|
||||
chain: Arc<BlockChainClient>,
|
||||
snapshot_service: Arc<SnapshotService>,
|
||||
private_tx_handler: Option<Arc<PrivateTxHandler>>,
|
||||
provider: Arc<Provider>,
|
||||
_log_settings: &LogConfig,
|
||||
attached_protos: Vec<AttachedProtocol>,
|
||||
connection_filter: Option<Arc<ConnectionFilter>>,
|
||||
) -> Result<SyncModules, sync::Error> {
|
||||
let eth_sync = EthSync::new(Params {
|
||||
config,
|
||||
chain,
|
||||
provider,
|
||||
snapshot_service,
|
||||
private_tx_handler,
|
||||
network_config,
|
||||
attached_protos,
|
||||
},
|
||||
connection_filter)?;
|
||||
let eth_sync = EthSync::new(
|
||||
Params {
|
||||
config,
|
||||
chain,
|
||||
provider,
|
||||
snapshot_service,
|
||||
private_tx_handler,
|
||||
network_config,
|
||||
attached_protos,
|
||||
},
|
||||
connection_filter,
|
||||
)?;
|
||||
|
||||
Ok((
|
||||
eth_sync.clone() as Arc<SyncProvider>,
|
||||
eth_sync.clone() as Arc<ManageNetwork>,
|
||||
eth_sync.clone() as Arc<ChainNotify>,
|
||||
eth_sync.priority_tasks()
|
||||
))
|
||||
Ok((
|
||||
eth_sync.clone() as Arc<SyncProvider>,
|
||||
eth_sync.clone() as Arc<ManageNetwork>,
|
||||
eth_sync.clone() as Arc<ChainNotify>,
|
||||
eth_sync.priority_tasks(),
|
||||
))
|
||||
}
|
||||
|
||||
823
parity/params.rs
823
parity/params.rs
@@ -14,20 +14,21 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::time::Duration;
|
||||
use std::{str, fs, fmt};
|
||||
use std::num::NonZeroU32;
|
||||
use std::{collections::HashSet, fmt, fs, num::NonZeroU32, str, time::Duration};
|
||||
|
||||
use ethcore::client::Mode;
|
||||
use ethcore::ethereum;
|
||||
use ethcore::spec::{Spec, SpecParams};
|
||||
use ethereum_types::{U256, Address};
|
||||
use parity_runtime::Executor;
|
||||
use ethcore::{
|
||||
client::Mode,
|
||||
ethereum,
|
||||
spec::{Spec, SpecParams},
|
||||
};
|
||||
use ethereum_types::{Address, U256};
|
||||
use hash_fetch::fetch::Client as FetchClient;
|
||||
use journaldb::Algorithm;
|
||||
use miner::gas_pricer::GasPricer;
|
||||
use miner::gas_price_calibrator::{GasPriceCalibratorOptions, GasPriceCalibrator};
|
||||
use miner::{
|
||||
gas_price_calibrator::{GasPriceCalibrator, GasPriceCalibratorOptions},
|
||||
gas_pricer::GasPricer,
|
||||
};
|
||||
use parity_runtime::Executor;
|
||||
use parity_version::version_data;
|
||||
use user_defaults::UserDefaults;
|
||||
|
||||
@@ -35,475 +36,531 @@ use crate::configuration;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum SpecType {
|
||||
Foundation,
|
||||
Classic,
|
||||
Poanet,
|
||||
Xdai,
|
||||
Volta,
|
||||
Ewc,
|
||||
Expanse,
|
||||
Musicoin,
|
||||
Ellaism,
|
||||
Mix,
|
||||
Callisto,
|
||||
Morden,
|
||||
Mordor,
|
||||
Ropsten,
|
||||
Kovan,
|
||||
Rinkeby,
|
||||
Goerli,
|
||||
Kotti,
|
||||
Sokol,
|
||||
Dev,
|
||||
Custom(String),
|
||||
Foundation,
|
||||
Classic,
|
||||
Poanet,
|
||||
Xdai,
|
||||
Volta,
|
||||
Ewc,
|
||||
Expanse,
|
||||
Musicoin,
|
||||
Ellaism,
|
||||
Mix,
|
||||
Callisto,
|
||||
Morden,
|
||||
Mordor,
|
||||
Ropsten,
|
||||
Kovan,
|
||||
Rinkeby,
|
||||
Goerli,
|
||||
Kotti,
|
||||
Sokol,
|
||||
Dev,
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl Default for SpecType {
|
||||
fn default() -> Self {
|
||||
SpecType::Foundation
|
||||
}
|
||||
fn default() -> Self {
|
||||
SpecType::Foundation
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for SpecType {
|
||||
type Err = String;
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let spec = match s {
|
||||
"eth" | "ethereum" | "foundation" | "mainnet" => SpecType::Foundation,
|
||||
"etc" | "classic" => SpecType::Classic,
|
||||
"poanet" | "poacore" => SpecType::Poanet,
|
||||
"xdai" => SpecType::Xdai,
|
||||
"volta" => SpecType::Volta,
|
||||
"ewc" | "energyweb" => SpecType::Ewc,
|
||||
"expanse" => SpecType::Expanse,
|
||||
"musicoin" => SpecType::Musicoin,
|
||||
"ellaism" => SpecType::Ellaism,
|
||||
"mix" => SpecType::Mix,
|
||||
"callisto" => SpecType::Callisto,
|
||||
"morden" => SpecType::Morden,
|
||||
"mordor" | "classic-testnet" => SpecType::Mordor,
|
||||
"ropsten" => SpecType::Ropsten,
|
||||
"kovan" => SpecType::Kovan,
|
||||
"rinkeby" => SpecType::Rinkeby,
|
||||
"goerli" | "görli" | "testnet" => SpecType::Goerli,
|
||||
"kotti" => SpecType::Kotti,
|
||||
"sokol" | "poasokol" => SpecType::Sokol,
|
||||
"dev" => SpecType::Dev,
|
||||
other => SpecType::Custom(other.into()),
|
||||
};
|
||||
Ok(spec)
|
||||
}
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let spec = match s {
|
||||
"eth" | "ethereum" | "foundation" | "mainnet" => SpecType::Foundation,
|
||||
"etc" | "classic" => SpecType::Classic,
|
||||
"poanet" | "poacore" => SpecType::Poanet,
|
||||
"xdai" => SpecType::Xdai,
|
||||
"volta" => SpecType::Volta,
|
||||
"ewc" | "energyweb" => SpecType::Ewc,
|
||||
"expanse" => SpecType::Expanse,
|
||||
"musicoin" => SpecType::Musicoin,
|
||||
"ellaism" => SpecType::Ellaism,
|
||||
"mix" => SpecType::Mix,
|
||||
"callisto" => SpecType::Callisto,
|
||||
"morden" => SpecType::Morden,
|
||||
"mordor" | "classic-testnet" => SpecType::Mordor,
|
||||
"ropsten" => SpecType::Ropsten,
|
||||
"kovan" => SpecType::Kovan,
|
||||
"rinkeby" => SpecType::Rinkeby,
|
||||
"goerli" | "görli" | "testnet" => SpecType::Goerli,
|
||||
"kotti" => SpecType::Kotti,
|
||||
"sokol" | "poasokol" => SpecType::Sokol,
|
||||
"dev" => SpecType::Dev,
|
||||
other => SpecType::Custom(other.into()),
|
||||
};
|
||||
Ok(spec)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SpecType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(match *self {
|
||||
SpecType::Foundation => "foundation",
|
||||
SpecType::Classic => "classic",
|
||||
SpecType::Poanet => "poanet",
|
||||
SpecType::Xdai => "xdai",
|
||||
SpecType::Volta => "volta",
|
||||
SpecType::Ewc => "energyweb",
|
||||
SpecType::Expanse => "expanse",
|
||||
SpecType::Musicoin => "musicoin",
|
||||
SpecType::Ellaism => "ellaism",
|
||||
SpecType::Mix => "mix",
|
||||
SpecType::Callisto => "callisto",
|
||||
SpecType::Morden => "morden",
|
||||
SpecType::Mordor => "mordor",
|
||||
SpecType::Ropsten => "ropsten",
|
||||
SpecType::Kovan => "kovan",
|
||||
SpecType::Rinkeby => "rinkeby",
|
||||
SpecType::Goerli => "goerli",
|
||||
SpecType::Kotti => "kotti",
|
||||
SpecType::Sokol => "sokol",
|
||||
SpecType::Dev => "dev",
|
||||
SpecType::Custom(ref custom) => custom,
|
||||
})
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(match *self {
|
||||
SpecType::Foundation => "foundation",
|
||||
SpecType::Classic => "classic",
|
||||
SpecType::Poanet => "poanet",
|
||||
SpecType::Xdai => "xdai",
|
||||
SpecType::Volta => "volta",
|
||||
SpecType::Ewc => "energyweb",
|
||||
SpecType::Expanse => "expanse",
|
||||
SpecType::Musicoin => "musicoin",
|
||||
SpecType::Ellaism => "ellaism",
|
||||
SpecType::Mix => "mix",
|
||||
SpecType::Callisto => "callisto",
|
||||
SpecType::Morden => "morden",
|
||||
SpecType::Mordor => "mordor",
|
||||
SpecType::Ropsten => "ropsten",
|
||||
SpecType::Kovan => "kovan",
|
||||
SpecType::Rinkeby => "rinkeby",
|
||||
SpecType::Goerli => "goerli",
|
||||
SpecType::Kotti => "kotti",
|
||||
SpecType::Sokol => "sokol",
|
||||
SpecType::Dev => "dev",
|
||||
SpecType::Custom(ref custom) => custom,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SpecType {
|
||||
pub fn spec<'a, T: Into<SpecParams<'a>>>(&self, params: T) -> Result<Spec, String> {
|
||||
let params = params.into();
|
||||
match *self {
|
||||
SpecType::Foundation => Ok(ethereum::new_foundation(params)),
|
||||
SpecType::Classic => Ok(ethereum::new_classic(params)),
|
||||
SpecType::Poanet => Ok(ethereum::new_poanet(params)),
|
||||
SpecType::Xdai => Ok(ethereum::new_xdai(params)),
|
||||
SpecType::Volta => Ok(ethereum::new_volta(params)),
|
||||
SpecType::Ewc => Ok(ethereum::new_ewc(params)),
|
||||
SpecType::Expanse => Ok(ethereum::new_expanse(params)),
|
||||
SpecType::Musicoin => Ok(ethereum::new_musicoin(params)),
|
||||
SpecType::Ellaism => Ok(ethereum::new_ellaism(params)),
|
||||
SpecType::Mix => Ok(ethereum::new_mix(params)),
|
||||
SpecType::Callisto => Ok(ethereum::new_callisto(params)),
|
||||
SpecType::Morden => Ok(ethereum::new_morden(params)),
|
||||
SpecType::Mordor => Ok(ethereum::new_mordor(params)),
|
||||
SpecType::Ropsten => Ok(ethereum::new_ropsten(params)),
|
||||
SpecType::Kovan => Ok(ethereum::new_kovan(params)),
|
||||
SpecType::Rinkeby => Ok(ethereum::new_rinkeby(params)),
|
||||
SpecType::Goerli => Ok(ethereum::new_goerli(params)),
|
||||
SpecType::Kotti => Ok(ethereum::new_kotti(params)),
|
||||
SpecType::Sokol => Ok(ethereum::new_sokol(params)),
|
||||
SpecType::Dev => Ok(Spec::new_instant()),
|
||||
SpecType::Custom(ref filename) => {
|
||||
let file = fs::File::open(filename).map_err(|e| format!("Could not load specification file at {}: {}", filename, e))?;
|
||||
Spec::load(params, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn spec<'a, T: Into<SpecParams<'a>>>(&self, params: T) -> Result<Spec, String> {
|
||||
let params = params.into();
|
||||
match *self {
|
||||
SpecType::Foundation => Ok(ethereum::new_foundation(params)),
|
||||
SpecType::Classic => Ok(ethereum::new_classic(params)),
|
||||
SpecType::Poanet => Ok(ethereum::new_poanet(params)),
|
||||
SpecType::Xdai => Ok(ethereum::new_xdai(params)),
|
||||
SpecType::Volta => Ok(ethereum::new_volta(params)),
|
||||
SpecType::Ewc => Ok(ethereum::new_ewc(params)),
|
||||
SpecType::Expanse => Ok(ethereum::new_expanse(params)),
|
||||
SpecType::Musicoin => Ok(ethereum::new_musicoin(params)),
|
||||
SpecType::Ellaism => Ok(ethereum::new_ellaism(params)),
|
||||
SpecType::Mix => Ok(ethereum::new_mix(params)),
|
||||
SpecType::Callisto => Ok(ethereum::new_callisto(params)),
|
||||
SpecType::Morden => Ok(ethereum::new_morden(params)),
|
||||
SpecType::Mordor => Ok(ethereum::new_mordor(params)),
|
||||
SpecType::Ropsten => Ok(ethereum::new_ropsten(params)),
|
||||
SpecType::Kovan => Ok(ethereum::new_kovan(params)),
|
||||
SpecType::Rinkeby => Ok(ethereum::new_rinkeby(params)),
|
||||
SpecType::Goerli => Ok(ethereum::new_goerli(params)),
|
||||
SpecType::Kotti => Ok(ethereum::new_kotti(params)),
|
||||
SpecType::Sokol => Ok(ethereum::new_sokol(params)),
|
||||
SpecType::Dev => Ok(Spec::new_instant()),
|
||||
SpecType::Custom(ref filename) => {
|
||||
let file = fs::File::open(filename).map_err(|e| {
|
||||
format!("Could not load specification file at {}: {}", filename, e)
|
||||
})?;
|
||||
Spec::load(params, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn legacy_fork_name(&self) -> Option<String> {
|
||||
match *self {
|
||||
SpecType::Classic => Some("classic".to_owned()),
|
||||
SpecType::Expanse => Some("expanse".to_owned()),
|
||||
SpecType::Musicoin => Some("musicoin".to_owned()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn legacy_fork_name(&self) -> Option<String> {
|
||||
match *self {
|
||||
SpecType::Classic => Some("classic".to_owned()),
|
||||
SpecType::Expanse => Some("expanse".to_owned()),
|
||||
SpecType::Musicoin => Some("musicoin".to_owned()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Pruning {
|
||||
Specific(Algorithm),
|
||||
Auto,
|
||||
Specific(Algorithm),
|
||||
Auto,
|
||||
}
|
||||
|
||||
impl Default for Pruning {
|
||||
fn default() -> Self {
|
||||
Pruning::Auto
|
||||
}
|
||||
fn default() -> Self {
|
||||
Pruning::Auto
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for Pruning {
|
||||
type Err = String;
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"auto" => Ok(Pruning::Auto),
|
||||
other => other.parse().map(Pruning::Specific),
|
||||
}
|
||||
}
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"auto" => Ok(Pruning::Auto),
|
||||
other => other.parse().map(Pruning::Specific),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Pruning {
|
||||
pub fn to_algorithm(&self, user_defaults: &UserDefaults) -> Algorithm {
|
||||
match *self {
|
||||
Pruning::Specific(algo) => algo,
|
||||
Pruning::Auto => user_defaults.pruning,
|
||||
}
|
||||
}
|
||||
pub fn to_algorithm(&self, user_defaults: &UserDefaults) -> Algorithm {
|
||||
match *self {
|
||||
Pruning::Specific(algo) => algo,
|
||||
Pruning::Auto => user_defaults.pruning,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ResealPolicy {
|
||||
pub own: bool,
|
||||
pub external: bool,
|
||||
pub own: bool,
|
||||
pub external: bool,
|
||||
}
|
||||
|
||||
impl Default for ResealPolicy {
|
||||
fn default() -> Self {
|
||||
ResealPolicy {
|
||||
own: true,
|
||||
external: true,
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
ResealPolicy {
|
||||
own: true,
|
||||
external: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for ResealPolicy {
|
||||
type Err = String;
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (own, external) = match s {
|
||||
"none" => (false, false),
|
||||
"own" => (true, false),
|
||||
"ext" => (false, true),
|
||||
"all" => (true, true),
|
||||
x => return Err(format!("Invalid reseal value: {}", x)),
|
||||
};
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (own, external) = match s {
|
||||
"none" => (false, false),
|
||||
"own" => (true, false),
|
||||
"ext" => (false, true),
|
||||
"all" => (true, true),
|
||||
x => return Err(format!("Invalid reseal value: {}", x)),
|
||||
};
|
||||
|
||||
let reseal = ResealPolicy {
|
||||
own: own,
|
||||
external: external,
|
||||
};
|
||||
let reseal = ResealPolicy {
|
||||
own: own,
|
||||
external: external,
|
||||
};
|
||||
|
||||
Ok(reseal)
|
||||
}
|
||||
Ok(reseal)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct AccountsConfig {
|
||||
pub iterations: NonZeroU32,
|
||||
pub refresh_time: u64,
|
||||
pub testnet: bool,
|
||||
pub password_files: Vec<String>,
|
||||
pub unlocked_accounts: Vec<Address>,
|
||||
pub enable_hardware_wallets: bool,
|
||||
pub enable_fast_unlock: bool,
|
||||
pub iterations: NonZeroU32,
|
||||
pub refresh_time: u64,
|
||||
pub testnet: bool,
|
||||
pub password_files: Vec<String>,
|
||||
pub unlocked_accounts: Vec<Address>,
|
||||
pub enable_hardware_wallets: bool,
|
||||
pub enable_fast_unlock: bool,
|
||||
}
|
||||
|
||||
impl Default for AccountsConfig {
|
||||
fn default() -> Self {
|
||||
AccountsConfig {
|
||||
iterations: NonZeroU32::new(10240).expect("10240 > 0; qed"),
|
||||
refresh_time: 5,
|
||||
testnet: false,
|
||||
password_files: Vec::new(),
|
||||
unlocked_accounts: Vec::new(),
|
||||
enable_hardware_wallets: true,
|
||||
enable_fast_unlock: false,
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
AccountsConfig {
|
||||
iterations: NonZeroU32::new(10240).expect("10240 > 0; qed"),
|
||||
refresh_time: 5,
|
||||
testnet: false,
|
||||
password_files: Vec::new(),
|
||||
unlocked_accounts: Vec::new(),
|
||||
enable_hardware_wallets: true,
|
||||
enable_fast_unlock: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum GasPricerConfig {
|
||||
Fixed(U256),
|
||||
Calibrated {
|
||||
usd_per_tx: f32,
|
||||
recalibration_period: Duration,
|
||||
api_endpoint: String
|
||||
}
|
||||
Fixed(U256),
|
||||
Calibrated {
|
||||
usd_per_tx: f32,
|
||||
recalibration_period: Duration,
|
||||
api_endpoint: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for GasPricerConfig {
|
||||
fn default() -> Self {
|
||||
GasPricerConfig::Calibrated {
|
||||
usd_per_tx: 0.0001f32,
|
||||
recalibration_period: Duration::from_secs(3600),
|
||||
api_endpoint: configuration::ETHERSCAN_ETH_PRICE_ENDPOINT.to_string(),
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
GasPricerConfig::Calibrated {
|
||||
usd_per_tx: 0.0001f32,
|
||||
recalibration_period: Duration::from_secs(3600),
|
||||
api_endpoint: configuration::ETHERSCAN_ETH_PRICE_ENDPOINT.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GasPricerConfig {
|
||||
pub fn to_gas_pricer(&self, fetch: FetchClient, p: Executor) -> GasPricer {
|
||||
match *self {
|
||||
GasPricerConfig::Fixed(u) => GasPricer::Fixed(u),
|
||||
GasPricerConfig::Calibrated { usd_per_tx, recalibration_period, ref api_endpoint } => {
|
||||
GasPricer::new_calibrated(
|
||||
GasPriceCalibrator::new(
|
||||
GasPriceCalibratorOptions {
|
||||
usd_per_tx: usd_per_tx,
|
||||
recalibration_period: recalibration_period,
|
||||
},
|
||||
fetch,
|
||||
p,
|
||||
api_endpoint.clone(),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn to_gas_pricer(&self, fetch: FetchClient, p: Executor) -> GasPricer {
|
||||
match *self {
|
||||
GasPricerConfig::Fixed(u) => GasPricer::Fixed(u),
|
||||
GasPricerConfig::Calibrated {
|
||||
usd_per_tx,
|
||||
recalibration_period,
|
||||
ref api_endpoint,
|
||||
} => GasPricer::new_calibrated(GasPriceCalibrator::new(
|
||||
GasPriceCalibratorOptions {
|
||||
usd_per_tx: usd_per_tx,
|
||||
recalibration_period: recalibration_period,
|
||||
},
|
||||
fetch,
|
||||
p,
|
||||
api_endpoint.clone(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct MinerExtras {
|
||||
pub author: Address,
|
||||
pub engine_signer: Address,
|
||||
pub extra_data: Vec<u8>,
|
||||
pub gas_range_target: (U256, U256),
|
||||
pub work_notify: Vec<String>,
|
||||
pub local_accounts: HashSet<Address>,
|
||||
pub author: Address,
|
||||
pub engine_signer: Address,
|
||||
pub extra_data: Vec<u8>,
|
||||
pub gas_range_target: (U256, U256),
|
||||
pub work_notify: Vec<String>,
|
||||
pub local_accounts: HashSet<Address>,
|
||||
}
|
||||
|
||||
impl Default for MinerExtras {
|
||||
fn default() -> Self {
|
||||
MinerExtras {
|
||||
author: Default::default(),
|
||||
engine_signer: Default::default(),
|
||||
extra_data: version_data(),
|
||||
gas_range_target: (8_000_000.into(), 10_000_000.into()),
|
||||
work_notify: Default::default(),
|
||||
local_accounts: Default::default(),
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
MinerExtras {
|
||||
author: Default::default(),
|
||||
engine_signer: Default::default(),
|
||||
extra_data: version_data(),
|
||||
gas_range_target: (8_000_000.into(), 10_000_000.into()),
|
||||
work_notify: Default::default(),
|
||||
local_accounts: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 3-value enum.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Switch {
|
||||
/// True.
|
||||
On,
|
||||
/// False.
|
||||
Off,
|
||||
/// Auto.
|
||||
Auto,
|
||||
/// True.
|
||||
On,
|
||||
/// False.
|
||||
Off,
|
||||
/// Auto.
|
||||
Auto,
|
||||
}
|
||||
|
||||
impl Default for Switch {
|
||||
fn default() -> Self {
|
||||
Switch::Auto
|
||||
}
|
||||
fn default() -> Self {
|
||||
Switch::Auto
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for Switch {
|
||||
type Err = String;
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"on" => Ok(Switch::On),
|
||||
"off" => Ok(Switch::Off),
|
||||
"auto" => Ok(Switch::Auto),
|
||||
other => Err(format!("Invalid switch value: {}", other))
|
||||
}
|
||||
}
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"on" => Ok(Switch::On),
|
||||
"off" => Ok(Switch::Off),
|
||||
"auto" => Ok(Switch::Auto),
|
||||
other => Err(format!("Invalid switch value: {}", other)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tracing_switch_to_bool(switch: Switch, user_defaults: &UserDefaults) -> Result<bool, String> {
|
||||
match (user_defaults.is_first_launch, switch, user_defaults.tracing) {
|
||||
(false, Switch::On, false) => Err("TraceDB resync required".into()),
|
||||
(_, Switch::On, _) => Ok(true),
|
||||
(_, Switch::Off, _) => Ok(false),
|
||||
(_, Switch::Auto, def) => Ok(def),
|
||||
}
|
||||
pub fn tracing_switch_to_bool(
|
||||
switch: Switch,
|
||||
user_defaults: &UserDefaults,
|
||||
) -> Result<bool, String> {
|
||||
match (user_defaults.is_first_launch, switch, user_defaults.tracing) {
|
||||
(false, Switch::On, false) => Err("TraceDB resync required".into()),
|
||||
(_, Switch::On, _) => Ok(true),
|
||||
(_, Switch::Off, _) => Ok(false),
|
||||
(_, Switch::Auto, def) => Ok(def),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fatdb_switch_to_bool(switch: Switch, user_defaults: &UserDefaults, _algorithm: Algorithm) -> Result<bool, String> {
|
||||
let result = match (user_defaults.is_first_launch, switch, user_defaults.fat_db) {
|
||||
(false, Switch::On, false) => Err("FatDB resync required".into()),
|
||||
(_, Switch::On, _) => Ok(true),
|
||||
(_, Switch::Off, _) => Ok(false),
|
||||
(_, Switch::Auto, def) => Ok(def),
|
||||
};
|
||||
result
|
||||
pub fn fatdb_switch_to_bool(
|
||||
switch: Switch,
|
||||
user_defaults: &UserDefaults,
|
||||
_algorithm: Algorithm,
|
||||
) -> Result<bool, String> {
|
||||
let result = match (user_defaults.is_first_launch, switch, user_defaults.fat_db) {
|
||||
(false, Switch::On, false) => Err("FatDB resync required".into()),
|
||||
(_, Switch::On, _) => Ok(true),
|
||||
(_, Switch::Off, _) => Ok(false),
|
||||
(_, Switch::Auto, def) => Ok(def),
|
||||
};
|
||||
result
|
||||
}
|
||||
|
||||
pub fn mode_switch_to_bool(switch: Option<Mode>, user_defaults: &UserDefaults) -> Result<Mode, String> {
|
||||
Ok(switch.unwrap_or(user_defaults.mode().clone()))
|
||||
pub fn mode_switch_to_bool(
|
||||
switch: Option<Mode>,
|
||||
user_defaults: &UserDefaults,
|
||||
) -> Result<Mode, String> {
|
||||
Ok(switch.unwrap_or(user_defaults.mode().clone()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use journaldb::Algorithm;
|
||||
use user_defaults::UserDefaults;
|
||||
use super::{SpecType, Pruning, ResealPolicy, Switch, tracing_switch_to_bool};
|
||||
use super::{tracing_switch_to_bool, Pruning, ResealPolicy, SpecType, Switch};
|
||||
use journaldb::Algorithm;
|
||||
use user_defaults::UserDefaults;
|
||||
|
||||
#[test]
|
||||
fn test_spec_type_parsing() {
|
||||
assert_eq!(SpecType::Foundation, "eth".parse().unwrap());
|
||||
assert_eq!(SpecType::Foundation, "ethereum".parse().unwrap());
|
||||
assert_eq!(SpecType::Foundation, "foundation".parse().unwrap());
|
||||
assert_eq!(SpecType::Foundation, "mainnet".parse().unwrap());
|
||||
assert_eq!(SpecType::Classic, "etc".parse().unwrap());
|
||||
assert_eq!(SpecType::Classic, "classic".parse().unwrap());
|
||||
assert_eq!(SpecType::Poanet, "poanet".parse().unwrap());
|
||||
assert_eq!(SpecType::Poanet, "poacore".parse().unwrap());
|
||||
assert_eq!(SpecType::Xdai, "xdai".parse().unwrap());
|
||||
assert_eq!(SpecType::Volta, "volta".parse().unwrap());
|
||||
assert_eq!(SpecType::Ewc, "ewc".parse().unwrap());
|
||||
assert_eq!(SpecType::Ewc, "energyweb".parse().unwrap());
|
||||
assert_eq!(SpecType::Expanse, "expanse".parse().unwrap());
|
||||
assert_eq!(SpecType::Musicoin, "musicoin".parse().unwrap());
|
||||
assert_eq!(SpecType::Ellaism, "ellaism".parse().unwrap());
|
||||
assert_eq!(SpecType::Mix, "mix".parse().unwrap());
|
||||
assert_eq!(SpecType::Callisto, "callisto".parse().unwrap());
|
||||
assert_eq!(SpecType::Morden, "morden".parse().unwrap());
|
||||
assert_eq!(SpecType::Mordor, "mordor".parse().unwrap());
|
||||
assert_eq!(SpecType::Mordor, "classic-testnet".parse().unwrap());
|
||||
assert_eq!(SpecType::Ropsten, "ropsten".parse().unwrap());
|
||||
assert_eq!(SpecType::Kovan, "kovan".parse().unwrap());
|
||||
assert_eq!(SpecType::Rinkeby, "rinkeby".parse().unwrap());
|
||||
assert_eq!(SpecType::Goerli, "goerli".parse().unwrap());
|
||||
assert_eq!(SpecType::Goerli, "görli".parse().unwrap());
|
||||
assert_eq!(SpecType::Goerli, "testnet".parse().unwrap());
|
||||
assert_eq!(SpecType::Kotti, "kotti".parse().unwrap());
|
||||
assert_eq!(SpecType::Sokol, "sokol".parse().unwrap());
|
||||
assert_eq!(SpecType::Sokol, "poasokol".parse().unwrap());
|
||||
}
|
||||
#[test]
|
||||
fn test_spec_type_parsing() {
|
||||
assert_eq!(SpecType::Foundation, "eth".parse().unwrap());
|
||||
assert_eq!(SpecType::Foundation, "ethereum".parse().unwrap());
|
||||
assert_eq!(SpecType::Foundation, "foundation".parse().unwrap());
|
||||
assert_eq!(SpecType::Foundation, "mainnet".parse().unwrap());
|
||||
assert_eq!(SpecType::Classic, "etc".parse().unwrap());
|
||||
assert_eq!(SpecType::Classic, "classic".parse().unwrap());
|
||||
assert_eq!(SpecType::Poanet, "poanet".parse().unwrap());
|
||||
assert_eq!(SpecType::Poanet, "poacore".parse().unwrap());
|
||||
assert_eq!(SpecType::Xdai, "xdai".parse().unwrap());
|
||||
assert_eq!(SpecType::Volta, "volta".parse().unwrap());
|
||||
assert_eq!(SpecType::Ewc, "ewc".parse().unwrap());
|
||||
assert_eq!(SpecType::Ewc, "energyweb".parse().unwrap());
|
||||
assert_eq!(SpecType::Expanse, "expanse".parse().unwrap());
|
||||
assert_eq!(SpecType::Musicoin, "musicoin".parse().unwrap());
|
||||
assert_eq!(SpecType::Ellaism, "ellaism".parse().unwrap());
|
||||
assert_eq!(SpecType::Mix, "mix".parse().unwrap());
|
||||
assert_eq!(SpecType::Callisto, "callisto".parse().unwrap());
|
||||
assert_eq!(SpecType::Morden, "morden".parse().unwrap());
|
||||
assert_eq!(SpecType::Mordor, "mordor".parse().unwrap());
|
||||
assert_eq!(SpecType::Mordor, "classic-testnet".parse().unwrap());
|
||||
assert_eq!(SpecType::Ropsten, "ropsten".parse().unwrap());
|
||||
assert_eq!(SpecType::Kovan, "kovan".parse().unwrap());
|
||||
assert_eq!(SpecType::Rinkeby, "rinkeby".parse().unwrap());
|
||||
assert_eq!(SpecType::Goerli, "goerli".parse().unwrap());
|
||||
assert_eq!(SpecType::Goerli, "görli".parse().unwrap());
|
||||
assert_eq!(SpecType::Goerli, "testnet".parse().unwrap());
|
||||
assert_eq!(SpecType::Kotti, "kotti".parse().unwrap());
|
||||
assert_eq!(SpecType::Sokol, "sokol".parse().unwrap());
|
||||
assert_eq!(SpecType::Sokol, "poasokol".parse().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_spec_type_default() {
|
||||
assert_eq!(SpecType::Foundation, SpecType::default());
|
||||
}
|
||||
#[test]
|
||||
fn test_spec_type_default() {
|
||||
assert_eq!(SpecType::Foundation, SpecType::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_spec_type_display() {
|
||||
assert_eq!(format!("{}", SpecType::Foundation), "foundation");
|
||||
assert_eq!(format!("{}", SpecType::Classic), "classic");
|
||||
assert_eq!(format!("{}", SpecType::Poanet), "poanet");
|
||||
assert_eq!(format!("{}", SpecType::Xdai), "xdai");
|
||||
assert_eq!(format!("{}", SpecType::Volta), "volta");
|
||||
assert_eq!(format!("{}", SpecType::Ewc), "energyweb");
|
||||
assert_eq!(format!("{}", SpecType::Expanse), "expanse");
|
||||
assert_eq!(format!("{}", SpecType::Musicoin), "musicoin");
|
||||
assert_eq!(format!("{}", SpecType::Ellaism), "ellaism");
|
||||
assert_eq!(format!("{}", SpecType::Mix), "mix");
|
||||
assert_eq!(format!("{}", SpecType::Callisto), "callisto");
|
||||
assert_eq!(format!("{}", SpecType::Morden), "morden");
|
||||
assert_eq!(format!("{}", SpecType::Mordor), "mordor");
|
||||
assert_eq!(format!("{}", SpecType::Ropsten), "ropsten");
|
||||
assert_eq!(format!("{}", SpecType::Kovan), "kovan");
|
||||
assert_eq!(format!("{}", SpecType::Rinkeby), "rinkeby");
|
||||
assert_eq!(format!("{}", SpecType::Goerli), "goerli");
|
||||
assert_eq!(format!("{}", SpecType::Kotti), "kotti");
|
||||
assert_eq!(format!("{}", SpecType::Sokol), "sokol");
|
||||
assert_eq!(format!("{}", SpecType::Dev), "dev");
|
||||
assert_eq!(format!("{}", SpecType::Custom("foo/bar".into())), "foo/bar");
|
||||
}
|
||||
#[test]
|
||||
fn test_spec_type_display() {
|
||||
assert_eq!(format!("{}", SpecType::Foundation), "foundation");
|
||||
assert_eq!(format!("{}", SpecType::Classic), "classic");
|
||||
assert_eq!(format!("{}", SpecType::Poanet), "poanet");
|
||||
assert_eq!(format!("{}", SpecType::Xdai), "xdai");
|
||||
assert_eq!(format!("{}", SpecType::Volta), "volta");
|
||||
assert_eq!(format!("{}", SpecType::Ewc), "energyweb");
|
||||
assert_eq!(format!("{}", SpecType::Expanse), "expanse");
|
||||
assert_eq!(format!("{}", SpecType::Musicoin), "musicoin");
|
||||
assert_eq!(format!("{}", SpecType::Ellaism), "ellaism");
|
||||
assert_eq!(format!("{}", SpecType::Mix), "mix");
|
||||
assert_eq!(format!("{}", SpecType::Callisto), "callisto");
|
||||
assert_eq!(format!("{}", SpecType::Morden), "morden");
|
||||
assert_eq!(format!("{}", SpecType::Mordor), "mordor");
|
||||
assert_eq!(format!("{}", SpecType::Ropsten), "ropsten");
|
||||
assert_eq!(format!("{}", SpecType::Kovan), "kovan");
|
||||
assert_eq!(format!("{}", SpecType::Rinkeby), "rinkeby");
|
||||
assert_eq!(format!("{}", SpecType::Goerli), "goerli");
|
||||
assert_eq!(format!("{}", SpecType::Kotti), "kotti");
|
||||
assert_eq!(format!("{}", SpecType::Sokol), "sokol");
|
||||
assert_eq!(format!("{}", SpecType::Dev), "dev");
|
||||
assert_eq!(format!("{}", SpecType::Custom("foo/bar".into())), "foo/bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pruning_parsing() {
|
||||
assert_eq!(Pruning::Auto, "auto".parse().unwrap());
|
||||
assert_eq!(Pruning::Specific(Algorithm::Archive), "archive".parse().unwrap());
|
||||
assert_eq!(Pruning::Specific(Algorithm::EarlyMerge), "light".parse().unwrap());
|
||||
assert_eq!(Pruning::Specific(Algorithm::OverlayRecent), "fast".parse().unwrap());
|
||||
assert_eq!(Pruning::Specific(Algorithm::RefCounted), "basic".parse().unwrap());
|
||||
}
|
||||
#[test]
|
||||
fn test_pruning_parsing() {
|
||||
assert_eq!(Pruning::Auto, "auto".parse().unwrap());
|
||||
assert_eq!(
|
||||
Pruning::Specific(Algorithm::Archive),
|
||||
"archive".parse().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Pruning::Specific(Algorithm::EarlyMerge),
|
||||
"light".parse().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Pruning::Specific(Algorithm::OverlayRecent),
|
||||
"fast".parse().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Pruning::Specific(Algorithm::RefCounted),
|
||||
"basic".parse().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pruning_default() {
|
||||
assert_eq!(Pruning::Auto, Pruning::default());
|
||||
}
|
||||
#[test]
|
||||
fn test_pruning_default() {
|
||||
assert_eq!(Pruning::Auto, Pruning::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reseal_policy_parsing() {
|
||||
let none = ResealPolicy { own: false, external: false };
|
||||
let own = ResealPolicy { own: true, external: false };
|
||||
let ext = ResealPolicy { own: false, external: true };
|
||||
let all = ResealPolicy { own: true, external: true };
|
||||
assert_eq!(none, "none".parse().unwrap());
|
||||
assert_eq!(own, "own".parse().unwrap());
|
||||
assert_eq!(ext, "ext".parse().unwrap());
|
||||
assert_eq!(all, "all".parse().unwrap());
|
||||
}
|
||||
#[test]
|
||||
fn test_reseal_policy_parsing() {
|
||||
let none = ResealPolicy {
|
||||
own: false,
|
||||
external: false,
|
||||
};
|
||||
let own = ResealPolicy {
|
||||
own: true,
|
||||
external: false,
|
||||
};
|
||||
let ext = ResealPolicy {
|
||||
own: false,
|
||||
external: true,
|
||||
};
|
||||
let all = ResealPolicy {
|
||||
own: true,
|
||||
external: true,
|
||||
};
|
||||
assert_eq!(none, "none".parse().unwrap());
|
||||
assert_eq!(own, "own".parse().unwrap());
|
||||
assert_eq!(ext, "ext".parse().unwrap());
|
||||
assert_eq!(all, "all".parse().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reseal_policy_default() {
|
||||
let all = ResealPolicy { own: true, external: true };
|
||||
assert_eq!(all, ResealPolicy::default());
|
||||
}
|
||||
#[test]
|
||||
fn test_reseal_policy_default() {
|
||||
let all = ResealPolicy {
|
||||
own: true,
|
||||
external: true,
|
||||
};
|
||||
assert_eq!(all, ResealPolicy::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_switch_parsing() {
|
||||
assert_eq!(Switch::On, "on".parse().unwrap());
|
||||
assert_eq!(Switch::Off, "off".parse().unwrap());
|
||||
assert_eq!(Switch::Auto, "auto".parse().unwrap());
|
||||
}
|
||||
#[test]
|
||||
fn test_switch_parsing() {
|
||||
assert_eq!(Switch::On, "on".parse().unwrap());
|
||||
assert_eq!(Switch::Off, "off".parse().unwrap());
|
||||
assert_eq!(Switch::Auto, "auto".parse().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_switch_default() {
|
||||
assert_eq!(Switch::default(), Switch::Auto);
|
||||
}
|
||||
#[test]
|
||||
fn test_switch_default() {
|
||||
assert_eq!(Switch::default(), Switch::Auto);
|
||||
}
|
||||
|
||||
fn user_defaults_with_tracing(first_launch: bool, tracing: bool) -> UserDefaults {
|
||||
let mut ud = UserDefaults::default();
|
||||
ud.is_first_launch = first_launch;
|
||||
ud.tracing = tracing;
|
||||
ud
|
||||
}
|
||||
fn user_defaults_with_tracing(first_launch: bool, tracing: bool) -> UserDefaults {
|
||||
let mut ud = UserDefaults::default();
|
||||
ud.is_first_launch = first_launch;
|
||||
ud.tracing = tracing;
|
||||
ud
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_switch_to_bool() {
|
||||
assert!(!tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(true, true)).unwrap());
|
||||
assert!(!tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(true, false)).unwrap());
|
||||
assert!(!tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(false, true)).unwrap());
|
||||
assert!(!tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(false, false)).unwrap());
|
||||
#[test]
|
||||
fn test_switch_to_bool() {
|
||||
assert!(
|
||||
!tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(true, true)).unwrap()
|
||||
);
|
||||
assert!(
|
||||
!tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(true, false)).unwrap()
|
||||
);
|
||||
assert!(
|
||||
!tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(false, true)).unwrap()
|
||||
);
|
||||
assert!(
|
||||
!tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(false, false))
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
assert!(tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(true, true)).unwrap());
|
||||
assert!(tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(true, false)).unwrap());
|
||||
assert!(tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(false, true)).unwrap());
|
||||
assert!(tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(false, false)).is_err());
|
||||
}
|
||||
assert!(
|
||||
tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(true, true)).unwrap()
|
||||
);
|
||||
assert!(
|
||||
tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(true, false)).unwrap()
|
||||
);
|
||||
assert!(
|
||||
tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(false, true)).unwrap()
|
||||
);
|
||||
assert!(
|
||||
tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(false, false)).is_err()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,45 +14,46 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
use ethkey::Password;
|
||||
use ethstore::PresaleWallet;
|
||||
use helpers::{password_prompt, password_from_file};
|
||||
use helpers::{password_from_file, password_prompt};
|
||||
use params::SpecType;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ImportWallet {
|
||||
pub iterations: NonZeroU32,
|
||||
pub path: String,
|
||||
pub spec: SpecType,
|
||||
pub wallet_path: String,
|
||||
pub password_file: Option<String>,
|
||||
pub iterations: NonZeroU32,
|
||||
pub path: String,
|
||||
pub spec: SpecType,
|
||||
pub wallet_path: String,
|
||||
pub password_file: Option<String>,
|
||||
}
|
||||
|
||||
pub fn execute(cmd: ImportWallet) -> Result<String, String> {
|
||||
let password = match cmd.password_file.clone() {
|
||||
Some(file) => password_from_file(file)?,
|
||||
None => password_prompt()?,
|
||||
};
|
||||
let password = match cmd.password_file.clone() {
|
||||
Some(file) => password_from_file(file)?,
|
||||
None => password_prompt()?,
|
||||
};
|
||||
|
||||
let wallet = PresaleWallet::open(cmd.wallet_path.clone()).map_err(|_| "Unable to open presale wallet.")?;
|
||||
let kp = wallet.decrypt(&password).map_err(|_| "Invalid password.")?;
|
||||
let address = kp.address();
|
||||
import_account(&cmd, kp, password);
|
||||
Ok(format!("{:?}", address))
|
||||
let wallet = PresaleWallet::open(cmd.wallet_path.clone())
|
||||
.map_err(|_| "Unable to open presale wallet.")?;
|
||||
let kp = wallet.decrypt(&password).map_err(|_| "Invalid password.")?;
|
||||
let address = kp.address();
|
||||
import_account(&cmd, kp, password);
|
||||
Ok(format!("{:?}", address))
|
||||
}
|
||||
|
||||
#[cfg(feature = "accounts")]
|
||||
pub fn import_account(cmd: &ImportWallet, kp: ethkey::KeyPair, password: Password) {
|
||||
use accounts::{AccountProvider, AccountProviderSettings};
|
||||
use ethstore::EthStore;
|
||||
use ethstore::accounts_dir::RootDiskDirectory;
|
||||
use accounts::{AccountProvider, AccountProviderSettings};
|
||||
use ethstore::{accounts_dir::RootDiskDirectory, EthStore};
|
||||
|
||||
let dir = Box::new(RootDiskDirectory::create(cmd.path.clone()).unwrap());
|
||||
let secret_store = Box::new(EthStore::open_with_iterations(dir, cmd.iterations).unwrap());
|
||||
let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default());
|
||||
acc_provider.insert_account(kp.secret().clone(), &password).unwrap();
|
||||
let dir = Box::new(RootDiskDirectory::create(cmd.path.clone()).unwrap());
|
||||
let secret_store = Box::new(EthStore::open_with_iterations(dir, cmd.iterations).unwrap());
|
||||
let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default());
|
||||
acc_provider
|
||||
.insert_account(kp.secret().clone(), &password)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "accounts"))]
|
||||
|
||||
463
parity/rpc.rs
463
parity/rpc.rs
@@ -14,186 +14,196 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
use std::path::PathBuf;
|
||||
use std::collections::HashSet;
|
||||
use std::{collections::HashSet, io, path::PathBuf, sync::Arc};
|
||||
|
||||
use dir::default_data_path;
|
||||
use dir::helpers::replace_home;
|
||||
use dir::{default_data_path, helpers::replace_home};
|
||||
use helpers::parity_ipc_path;
|
||||
use jsonrpc_core::MetaIoHandler;
|
||||
use parity_rpc::{
|
||||
self as rpc,
|
||||
informant::{Middleware, RpcStats},
|
||||
DomainsValidation, Metadata,
|
||||
};
|
||||
use parity_runtime::Executor;
|
||||
use parity_rpc::informant::{RpcStats, Middleware};
|
||||
use parity_rpc::{self as rpc, Metadata, DomainsValidation};
|
||||
use rpc_apis::{self, ApiSet};
|
||||
|
||||
pub use parity_rpc::{IpcServer, HttpServer, RequestMiddleware};
|
||||
pub use parity_rpc::{HttpServer, IpcServer, RequestMiddleware};
|
||||
//pub use parity_rpc::ws::Server as WsServer;
|
||||
pub use parity_rpc::ws::{Server as WsServer, ws};
|
||||
pub use parity_rpc::ws::{ws, Server as WsServer};
|
||||
|
||||
pub const DAPPS_DOMAIN: &'static str = "web3.site";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct HttpConfiguration {
|
||||
pub enabled: bool,
|
||||
pub interface: String,
|
||||
pub port: u16,
|
||||
pub apis: ApiSet,
|
||||
pub cors: Option<Vec<String>>,
|
||||
pub hosts: Option<Vec<String>>,
|
||||
pub server_threads: usize,
|
||||
pub processing_threads: usize,
|
||||
pub max_payload: usize,
|
||||
pub keep_alive: bool,
|
||||
pub enabled: bool,
|
||||
pub interface: String,
|
||||
pub port: u16,
|
||||
pub apis: ApiSet,
|
||||
pub cors: Option<Vec<String>>,
|
||||
pub hosts: Option<Vec<String>>,
|
||||
pub server_threads: usize,
|
||||
pub processing_threads: usize,
|
||||
pub max_payload: usize,
|
||||
pub keep_alive: bool,
|
||||
}
|
||||
|
||||
impl Default for HttpConfiguration {
|
||||
fn default() -> Self {
|
||||
HttpConfiguration {
|
||||
enabled: true,
|
||||
interface: "127.0.0.1".into(),
|
||||
port: 8545,
|
||||
apis: ApiSet::UnsafeContext,
|
||||
cors: Some(vec![]),
|
||||
hosts: Some(vec![]),
|
||||
server_threads: 1,
|
||||
processing_threads: 4,
|
||||
max_payload: 5,
|
||||
keep_alive: true,
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
HttpConfiguration {
|
||||
enabled: true,
|
||||
interface: "127.0.0.1".into(),
|
||||
port: 8545,
|
||||
apis: ApiSet::UnsafeContext,
|
||||
cors: Some(vec![]),
|
||||
hosts: Some(vec![]),
|
||||
server_threads: 1,
|
||||
processing_threads: 4,
|
||||
max_payload: 5,
|
||||
keep_alive: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct IpcConfiguration {
|
||||
pub enabled: bool,
|
||||
pub socket_addr: String,
|
||||
pub apis: ApiSet,
|
||||
pub enabled: bool,
|
||||
pub socket_addr: String,
|
||||
pub apis: ApiSet,
|
||||
}
|
||||
|
||||
impl Default for IpcConfiguration {
|
||||
fn default() -> Self {
|
||||
IpcConfiguration {
|
||||
enabled: true,
|
||||
socket_addr: if cfg!(windows) {
|
||||
r"\\.\pipe\jsonrpc.ipc".into()
|
||||
} else {
|
||||
let data_dir = ::dir::default_data_path();
|
||||
parity_ipc_path(&data_dir, "$BASE/jsonrpc.ipc", 0)
|
||||
},
|
||||
apis: ApiSet::IpcContext,
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
IpcConfiguration {
|
||||
enabled: true,
|
||||
socket_addr: if cfg!(windows) {
|
||||
r"\\.\pipe\jsonrpc.ipc".into()
|
||||
} else {
|
||||
let data_dir = ::dir::default_data_path();
|
||||
parity_ipc_path(&data_dir, "$BASE/jsonrpc.ipc", 0)
|
||||
},
|
||||
apis: ApiSet::IpcContext,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct WsConfiguration {
|
||||
pub enabled: bool,
|
||||
pub interface: String,
|
||||
pub port: u16,
|
||||
pub apis: ApiSet,
|
||||
pub max_connections: usize,
|
||||
pub origins: Option<Vec<String>>,
|
||||
pub hosts: Option<Vec<String>>,
|
||||
pub signer_path: PathBuf,
|
||||
pub support_token_api: bool,
|
||||
pub enabled: bool,
|
||||
pub interface: String,
|
||||
pub port: u16,
|
||||
pub apis: ApiSet,
|
||||
pub max_connections: usize,
|
||||
pub origins: Option<Vec<String>>,
|
||||
pub hosts: Option<Vec<String>>,
|
||||
pub signer_path: PathBuf,
|
||||
pub support_token_api: bool,
|
||||
}
|
||||
|
||||
impl Default for WsConfiguration {
|
||||
fn default() -> Self {
|
||||
let data_dir = default_data_path();
|
||||
WsConfiguration {
|
||||
enabled: true,
|
||||
interface: "127.0.0.1".into(),
|
||||
port: 8546,
|
||||
apis: ApiSet::UnsafeContext,
|
||||
max_connections: 100,
|
||||
origins: Some(vec!["parity://*".into(),"chrome-extension://*".into(), "moz-extension://*".into()]),
|
||||
hosts: Some(Vec::new()),
|
||||
signer_path: replace_home(&data_dir, "$BASE/signer").into(),
|
||||
support_token_api: true,
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
let data_dir = default_data_path();
|
||||
WsConfiguration {
|
||||
enabled: true,
|
||||
interface: "127.0.0.1".into(),
|
||||
port: 8546,
|
||||
apis: ApiSet::UnsafeContext,
|
||||
max_connections: 100,
|
||||
origins: Some(vec![
|
||||
"parity://*".into(),
|
||||
"chrome-extension://*".into(),
|
||||
"moz-extension://*".into(),
|
||||
]),
|
||||
hosts: Some(Vec::new()),
|
||||
signer_path: replace_home(&data_dir, "$BASE/signer").into(),
|
||||
support_token_api: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WsConfiguration {
|
||||
pub fn address(&self) -> Option<rpc::Host> {
|
||||
address(self.enabled, &self.interface, self.port, &self.hosts)
|
||||
}
|
||||
pub fn address(&self) -> Option<rpc::Host> {
|
||||
address(self.enabled, &self.interface, self.port, &self.hosts)
|
||||
}
|
||||
}
|
||||
|
||||
fn address(enabled: bool, bind_iface: &str, bind_port: u16, hosts: &Option<Vec<String>>) -> Option<rpc::Host> {
|
||||
if !enabled {
|
||||
return None;
|
||||
}
|
||||
fn address(
|
||||
enabled: bool,
|
||||
bind_iface: &str,
|
||||
bind_port: u16,
|
||||
hosts: &Option<Vec<String>>,
|
||||
) -> Option<rpc::Host> {
|
||||
if !enabled {
|
||||
return None;
|
||||
}
|
||||
|
||||
match *hosts {
|
||||
Some(ref hosts) if !hosts.is_empty() => Some(hosts[0].clone().into()),
|
||||
_ => Some(format!("{}:{}", bind_iface, bind_port).into()),
|
||||
}
|
||||
match *hosts {
|
||||
Some(ref hosts) if !hosts.is_empty() => Some(hosts[0].clone().into()),
|
||||
_ => Some(format!("{}:{}", bind_iface, bind_port).into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dependencies<D: rpc_apis::Dependencies> {
|
||||
pub apis: Arc<D>,
|
||||
pub executor: Executor,
|
||||
pub stats: Arc<RpcStats>,
|
||||
pub apis: Arc<D>,
|
||||
pub executor: Executor,
|
||||
pub stats: Arc<RpcStats>,
|
||||
}
|
||||
|
||||
pub fn new_ws<D: rpc_apis::Dependencies>(
|
||||
conf: WsConfiguration,
|
||||
deps: &Dependencies<D>,
|
||||
conf: WsConfiguration,
|
||||
deps: &Dependencies<D>,
|
||||
) -> Result<Option<WsServer>, String> {
|
||||
if !conf.enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
if !conf.enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let domain = DAPPS_DOMAIN;
|
||||
let url = format!("{}:{}", conf.interface, conf.port);
|
||||
let addr = url.parse().map_err(|_| format!("Invalid WebSockets listen host/port given: {}", url))?;
|
||||
let domain = DAPPS_DOMAIN;
|
||||
let url = format!("{}:{}", conf.interface, conf.port);
|
||||
let addr = url
|
||||
.parse()
|
||||
.map_err(|_| format!("Invalid WebSockets listen host/port given: {}", url))?;
|
||||
|
||||
let full_handler = setup_apis(rpc_apis::ApiSet::All, deps);
|
||||
let handler = {
|
||||
let mut handler = MetaIoHandler::with_middleware((
|
||||
rpc::WsDispatcher::new(full_handler),
|
||||
Middleware::new(deps.stats.clone(), deps.apis.activity_notifier())
|
||||
));
|
||||
let apis = conf.apis.list_apis();
|
||||
deps.apis.extend_with_set(&mut handler, &apis);
|
||||
let full_handler = setup_apis(rpc_apis::ApiSet::All, deps);
|
||||
let handler = {
|
||||
let mut handler = MetaIoHandler::with_middleware((
|
||||
rpc::WsDispatcher::new(full_handler),
|
||||
Middleware::new(deps.stats.clone(), deps.apis.activity_notifier()),
|
||||
));
|
||||
let apis = conf.apis.list_apis();
|
||||
deps.apis.extend_with_set(&mut handler, &apis);
|
||||
|
||||
handler
|
||||
};
|
||||
handler
|
||||
};
|
||||
|
||||
let allowed_origins = into_domains(with_domain(conf.origins, domain, &None));
|
||||
let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into())));
|
||||
let allowed_origins = into_domains(with_domain(conf.origins, domain, &None));
|
||||
let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into())));
|
||||
|
||||
let signer_path;
|
||||
let path = match conf.support_token_api {
|
||||
true => {
|
||||
signer_path = ::signer::codes_path(&conf.signer_path);
|
||||
Some(signer_path.as_path())
|
||||
},
|
||||
false => None
|
||||
};
|
||||
let start_result = rpc::start_ws(
|
||||
&addr,
|
||||
handler,
|
||||
allowed_origins,
|
||||
allowed_hosts,
|
||||
conf.max_connections,
|
||||
rpc::WsExtractor::new(path.clone()),
|
||||
rpc::WsExtractor::new(path.clone()),
|
||||
rpc::WsStats::new(deps.stats.clone()),
|
||||
);
|
||||
let signer_path;
|
||||
let path = match conf.support_token_api {
|
||||
true => {
|
||||
signer_path = ::signer::codes_path(&conf.signer_path);
|
||||
Some(signer_path.as_path())
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
let start_result = rpc::start_ws(
|
||||
&addr,
|
||||
handler,
|
||||
allowed_origins,
|
||||
allowed_hosts,
|
||||
conf.max_connections,
|
||||
rpc::WsExtractor::new(path.clone()),
|
||||
rpc::WsExtractor::new(path.clone()),
|
||||
rpc::WsStats::new(deps.stats.clone()),
|
||||
);
|
||||
|
||||
// match start_result {
|
||||
// Ok(server) => Ok(Some(server)),
|
||||
// Err(rpc::ws::Error::Io(rpc::ws::ErrorKind::Io(ref err), _)) if err.kind() == io::ErrorKind::AddrInUse => Err(
|
||||
// format!("WebSockets address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --ws-port and --ws-interface options.", url)
|
||||
// ),
|
||||
// Err(e) => Err(format!("WebSockets error: {:?}", e)),
|
||||
// }
|
||||
match start_result {
|
||||
// match start_result {
|
||||
// Ok(server) => Ok(Some(server)),
|
||||
// Err(rpc::ws::Error::Io(rpc::ws::ErrorKind::Io(ref err), _)) if err.kind() == io::ErrorKind::AddrInUse => Err(
|
||||
// format!("WebSockets address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --ws-port and --ws-interface options.", url)
|
||||
// ),
|
||||
// Err(e) => Err(format!("WebSockets error: {:?}", e)),
|
||||
// }
|
||||
match start_result {
|
||||
Ok(server) => Ok(Some(server)),
|
||||
Err(rpc::ws::Error::WsError(ws::Error {
|
||||
kind: ws::ErrorKind::Io(ref err), ..
|
||||
@@ -205,35 +215,37 @@ pub fn new_ws<D: rpc_apis::Dependencies>(
|
||||
}
|
||||
|
||||
pub fn new_http<D: rpc_apis::Dependencies>(
|
||||
id: &str,
|
||||
options: &str,
|
||||
conf: HttpConfiguration,
|
||||
deps: &Dependencies<D>,
|
||||
id: &str,
|
||||
options: &str,
|
||||
conf: HttpConfiguration,
|
||||
deps: &Dependencies<D>,
|
||||
) -> Result<Option<HttpServer>, String> {
|
||||
if !conf.enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
if !conf.enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let domain = DAPPS_DOMAIN;
|
||||
let url = format!("{}:{}", conf.interface, conf.port);
|
||||
let addr = url.parse().map_err(|_| format!("Invalid {} listen host/port given: {}", id, url))?;
|
||||
let handler = setup_apis(conf.apis, deps);
|
||||
let domain = DAPPS_DOMAIN;
|
||||
let url = format!("{}:{}", conf.interface, conf.port);
|
||||
let addr = url
|
||||
.parse()
|
||||
.map_err(|_| format!("Invalid {} listen host/port given: {}", id, url))?;
|
||||
let handler = setup_apis(conf.apis, deps);
|
||||
|
||||
let cors_domains = into_domains(conf.cors);
|
||||
let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into())));
|
||||
let cors_domains = into_domains(conf.cors);
|
||||
let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into())));
|
||||
|
||||
let start_result = rpc::start_http(
|
||||
&addr,
|
||||
cors_domains,
|
||||
allowed_hosts,
|
||||
handler,
|
||||
rpc::RpcExtractor,
|
||||
conf.server_threads,
|
||||
conf.max_payload,
|
||||
conf.keep_alive,
|
||||
);
|
||||
let start_result = rpc::start_http(
|
||||
&addr,
|
||||
cors_domains,
|
||||
allowed_hosts,
|
||||
handler,
|
||||
rpc::RpcExtractor,
|
||||
conf.server_threads,
|
||||
conf.max_payload,
|
||||
conf.keep_alive,
|
||||
);
|
||||
|
||||
match start_result {
|
||||
match start_result {
|
||||
Ok(server) => Ok(Some(server)),
|
||||
Err(ref err) if err.kind() == io::ErrorKind::AddrInUse => Err(
|
||||
format!("{} address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --{}-port and --{}-interface options.", id, url, options, options)
|
||||
@@ -243,80 +255,105 @@ pub fn new_http<D: rpc_apis::Dependencies>(
|
||||
}
|
||||
|
||||
pub fn new_ipc<D: rpc_apis::Dependencies>(
|
||||
conf: IpcConfiguration,
|
||||
dependencies: &Dependencies<D>
|
||||
conf: IpcConfiguration,
|
||||
dependencies: &Dependencies<D>,
|
||||
) -> Result<Option<IpcServer>, String> {
|
||||
if !conf.enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
if !conf.enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let handler = setup_apis(conf.apis, dependencies);
|
||||
let path = PathBuf::from(&conf.socket_addr);
|
||||
// Make sure socket file can be created on unix-like OS.
|
||||
// Windows pipe paths are not on the FS.
|
||||
if !cfg!(windows) {
|
||||
if let Some(dir) = path.parent() {
|
||||
::std::fs::create_dir_all(&dir)
|
||||
.map_err(|err| format!("Unable to create IPC directory at {}: {}", dir.display(), err))?;
|
||||
}
|
||||
}
|
||||
let handler = setup_apis(conf.apis, dependencies);
|
||||
let path = PathBuf::from(&conf.socket_addr);
|
||||
// Make sure socket file can be created on unix-like OS.
|
||||
// Windows pipe paths are not on the FS.
|
||||
if !cfg!(windows) {
|
||||
if let Some(dir) = path.parent() {
|
||||
::std::fs::create_dir_all(&dir).map_err(|err| {
|
||||
format!(
|
||||
"Unable to create IPC directory at {}: {}",
|
||||
dir.display(),
|
||||
err
|
||||
)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
match rpc::start_ipc(&conf.socket_addr, handler, rpc::RpcExtractor) {
|
||||
Ok(server) => Ok(Some(server)),
|
||||
Err(io_error) => Err(format!("IPC error: {}", io_error)),
|
||||
}
|
||||
match rpc::start_ipc(&conf.socket_addr, handler, rpc::RpcExtractor) {
|
||||
Ok(server) => Ok(Some(server)),
|
||||
Err(io_error) => Err(format!("IPC error: {}", io_error)),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_domains<T: From<String>>(items: Option<Vec<String>>) -> DomainsValidation<T> {
|
||||
items.map(|vals| vals.into_iter().map(T::from).collect()).into()
|
||||
items
|
||||
.map(|vals| vals.into_iter().map(T::from).collect())
|
||||
.into()
|
||||
}
|
||||
|
||||
fn with_domain(items: Option<Vec<String>>, domain: &str, dapps_address: &Option<rpc::Host>) -> Option<Vec<String>> {
|
||||
fn extract_port(s: &str) -> Option<u16> {
|
||||
s.split(':').nth(1).and_then(|s| s.parse().ok())
|
||||
}
|
||||
fn with_domain(
|
||||
items: Option<Vec<String>>,
|
||||
domain: &str,
|
||||
dapps_address: &Option<rpc::Host>,
|
||||
) -> Option<Vec<String>> {
|
||||
fn extract_port(s: &str) -> Option<u16> {
|
||||
s.split(':').nth(1).and_then(|s| s.parse().ok())
|
||||
}
|
||||
|
||||
items.map(move |items| {
|
||||
let mut items = items.into_iter().collect::<HashSet<_>>();
|
||||
{
|
||||
let mut add_hosts = |address: &Option<rpc::Host>| {
|
||||
if let Some(host) = address.clone() {
|
||||
items.insert(host.to_string());
|
||||
items.insert(host.replace("127.0.0.1", "localhost"));
|
||||
items.insert(format!("http://*.{}", domain)); //proxypac
|
||||
if let Some(port) = extract_port(&*host) {
|
||||
items.insert(format!("http://*.{}:{}", domain, port));
|
||||
}
|
||||
}
|
||||
};
|
||||
items.map(move |items| {
|
||||
let mut items = items.into_iter().collect::<HashSet<_>>();
|
||||
{
|
||||
let mut add_hosts = |address: &Option<rpc::Host>| {
|
||||
if let Some(host) = address.clone() {
|
||||
items.insert(host.to_string());
|
||||
items.insert(host.replace("127.0.0.1", "localhost"));
|
||||
items.insert(format!("http://*.{}", domain)); //proxypac
|
||||
if let Some(port) = extract_port(&*host) {
|
||||
items.insert(format!("http://*.{}:{}", domain, port));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
add_hosts(dapps_address);
|
||||
}
|
||||
items.into_iter().collect()
|
||||
})
|
||||
add_hosts(dapps_address);
|
||||
}
|
||||
items.into_iter().collect()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn setup_apis<D>(apis: ApiSet, deps: &Dependencies<D>) -> MetaIoHandler<Metadata, Middleware<D::Notifier>>
|
||||
where D: rpc_apis::Dependencies
|
||||
pub fn setup_apis<D>(
|
||||
apis: ApiSet,
|
||||
deps: &Dependencies<D>,
|
||||
) -> MetaIoHandler<Metadata, Middleware<D::Notifier>>
|
||||
where
|
||||
D: rpc_apis::Dependencies,
|
||||
{
|
||||
let mut handler = MetaIoHandler::with_middleware(
|
||||
Middleware::new(deps.stats.clone(), deps.apis.activity_notifier())
|
||||
);
|
||||
let apis = apis.list_apis();
|
||||
deps.apis.extend_with_set(&mut handler, &apis);
|
||||
let mut handler = MetaIoHandler::with_middleware(Middleware::new(
|
||||
deps.stats.clone(),
|
||||
deps.apis.activity_notifier(),
|
||||
));
|
||||
let apis = apis.list_apis();
|
||||
deps.apis.extend_with_set(&mut handler, &apis);
|
||||
|
||||
handler
|
||||
handler
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::address;
|
||||
use super::address;
|
||||
|
||||
#[test]
|
||||
fn should_return_proper_address() {
|
||||
assert_eq!(address(false, "localhost", 8180, &None), None);
|
||||
assert_eq!(address(true, "localhost", 8180, &None), Some("localhost:8180".into()));
|
||||
assert_eq!(address(true, "localhost", 8180, &Some(vec!["host:443".into()])), Some("host:443".into()));
|
||||
assert_eq!(address(true, "localhost", 8180, &Some(vec!["host".into()])), Some("host".into()));
|
||||
}
|
||||
#[test]
|
||||
fn should_return_proper_address() {
|
||||
assert_eq!(address(false, "localhost", 8180, &None), None);
|
||||
assert_eq!(
|
||||
address(true, "localhost", 8180, &None),
|
||||
Some("localhost:8180".into())
|
||||
);
|
||||
assert_eq!(
|
||||
address(true, "localhost", 8180, &Some(vec!["host:443".into()])),
|
||||
Some("host:443".into())
|
||||
);
|
||||
assert_eq!(
|
||||
address(true, "localhost", 8180, &Some(vec!["host".into()])),
|
||||
Some("host".into())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
1614
parity/rpc_apis.rs
1614
parity/rpc_apis.rs
File diff suppressed because it is too large
Load Diff
1856
parity/run.rs
1856
parity/run.rs
File diff suppressed because it is too large
Load Diff
@@ -14,236 +14,319 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
use account_utils::AccountProvider;
|
||||
use dir::default_data_path;
|
||||
use dir::helpers::replace_home;
|
||||
use ethcore::client::Client;
|
||||
use ethcore::miner::Miner;
|
||||
use ethkey::{Secret, Public, Password};
|
||||
use sync::SyncProvider;
|
||||
use dir::{default_data_path, helpers::replace_home};
|
||||
use ethcore::{client::Client, miner::Miner};
|
||||
use ethereum_types::Address;
|
||||
use ethkey::{Password, Public, Secret};
|
||||
use parity_runtime::Executor;
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
use sync::SyncProvider;
|
||||
|
||||
/// This node secret key.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum NodeSecretKey {
|
||||
/// Stored as plain text in configuration file.
|
||||
Plain(Secret),
|
||||
/// Stored as account in key store.
|
||||
#[cfg(feature = "accounts")]
|
||||
KeyStore(Address),
|
||||
/// Stored as plain text in configuration file.
|
||||
Plain(Secret),
|
||||
/// Stored as account in key store.
|
||||
#[cfg(feature = "accounts")]
|
||||
KeyStore(Address),
|
||||
}
|
||||
|
||||
/// Secret store service contract address.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum ContractAddress {
|
||||
/// Contract address is read from registry.
|
||||
Registry,
|
||||
/// Contract address is specified.
|
||||
Address(Address),
|
||||
/// Contract address is read from registry.
|
||||
Registry,
|
||||
/// Contract address is specified.
|
||||
Address(Address),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
/// Secret store configuration
|
||||
pub struct Configuration {
|
||||
/// Is secret store functionality enabled?
|
||||
pub enabled: bool,
|
||||
/// Is HTTP API enabled?
|
||||
pub http_enabled: bool,
|
||||
/// Is auto migrate enabled.
|
||||
pub auto_migrate_enabled: bool,
|
||||
/// ACL check contract address.
|
||||
pub acl_check_contract_address: Option<ContractAddress>,
|
||||
/// Service contract address.
|
||||
pub service_contract_address: Option<ContractAddress>,
|
||||
/// Server key generation service contract address.
|
||||
pub service_contract_srv_gen_address: Option<ContractAddress>,
|
||||
/// Server key retrieval service contract address.
|
||||
pub service_contract_srv_retr_address: Option<ContractAddress>,
|
||||
/// Document key store service contract address.
|
||||
pub service_contract_doc_store_address: Option<ContractAddress>,
|
||||
/// Document key shadow retrieval service contract address.
|
||||
pub service_contract_doc_sretr_address: Option<ContractAddress>,
|
||||
/// This node secret.
|
||||
pub self_secret: Option<NodeSecretKey>,
|
||||
/// Other nodes IDs + addresses.
|
||||
pub nodes: BTreeMap<Public, (String, u16)>,
|
||||
/// Key Server Set contract address. If None, 'nodes' map is used.
|
||||
pub key_server_set_contract_address: Option<ContractAddress>,
|
||||
/// Interface to listen to
|
||||
pub interface: String,
|
||||
/// Port to listen to
|
||||
pub port: u16,
|
||||
/// Interface to listen to
|
||||
pub http_interface: String,
|
||||
/// Port to listen to
|
||||
pub http_port: u16,
|
||||
/// Data directory path for secret store
|
||||
pub data_path: String,
|
||||
/// Administrator public key.
|
||||
pub admin_public: Option<Public>,
|
||||
/// Is secret store functionality enabled?
|
||||
pub enabled: bool,
|
||||
/// Is HTTP API enabled?
|
||||
pub http_enabled: bool,
|
||||
/// Is auto migrate enabled.
|
||||
pub auto_migrate_enabled: bool,
|
||||
/// ACL check contract address.
|
||||
pub acl_check_contract_address: Option<ContractAddress>,
|
||||
/// Service contract address.
|
||||
pub service_contract_address: Option<ContractAddress>,
|
||||
/// Server key generation service contract address.
|
||||
pub service_contract_srv_gen_address: Option<ContractAddress>,
|
||||
/// Server key retrieval service contract address.
|
||||
pub service_contract_srv_retr_address: Option<ContractAddress>,
|
||||
/// Document key store service contract address.
|
||||
pub service_contract_doc_store_address: Option<ContractAddress>,
|
||||
/// Document key shadow retrieval service contract address.
|
||||
pub service_contract_doc_sretr_address: Option<ContractAddress>,
|
||||
/// This node secret.
|
||||
pub self_secret: Option<NodeSecretKey>,
|
||||
/// Other nodes IDs + addresses.
|
||||
pub nodes: BTreeMap<Public, (String, u16)>,
|
||||
/// Key Server Set contract address. If None, 'nodes' map is used.
|
||||
pub key_server_set_contract_address: Option<ContractAddress>,
|
||||
/// Interface to listen to
|
||||
pub interface: String,
|
||||
/// Port to listen to
|
||||
pub port: u16,
|
||||
/// Interface to listen to
|
||||
pub http_interface: String,
|
||||
/// Port to listen to
|
||||
pub http_port: u16,
|
||||
/// Data directory path for secret store
|
||||
pub data_path: String,
|
||||
/// Administrator public key.
|
||||
pub admin_public: Option<Public>,
|
||||
}
|
||||
|
||||
/// Secret store dependencies
|
||||
pub struct Dependencies<'a> {
|
||||
/// Blockchain client.
|
||||
pub client: Arc<Client>,
|
||||
/// Sync provider.
|
||||
pub sync: Arc<SyncProvider>,
|
||||
/// Miner service.
|
||||
pub miner: Arc<Miner>,
|
||||
/// Account provider.
|
||||
pub account_provider: Arc<AccountProvider>,
|
||||
/// Passed accounts passwords.
|
||||
pub accounts_passwords: &'a [Password],
|
||||
/// Blockchain client.
|
||||
pub client: Arc<Client>,
|
||||
/// Sync provider.
|
||||
pub sync: Arc<SyncProvider>,
|
||||
/// Miner service.
|
||||
pub miner: Arc<Miner>,
|
||||
/// Account provider.
|
||||
pub account_provider: Arc<AccountProvider>,
|
||||
/// Passed accounts passwords.
|
||||
pub accounts_passwords: &'a [Password],
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "secretstore"))]
|
||||
mod server {
|
||||
use super::{Configuration, Dependencies, Executor};
|
||||
use super::{Configuration, Dependencies, Executor};
|
||||
|
||||
/// Noop key server implementation
|
||||
pub struct KeyServer;
|
||||
/// Noop key server implementation
|
||||
pub struct KeyServer;
|
||||
|
||||
impl KeyServer {
|
||||
/// Create new noop key server
|
||||
pub fn new(_conf: Configuration, _deps: Dependencies, _executor: Executor) -> Result<Self, String> {
|
||||
Ok(KeyServer)
|
||||
}
|
||||
}
|
||||
impl KeyServer {
|
||||
/// Create new noop key server
|
||||
pub fn new(
|
||||
_conf: Configuration,
|
||||
_deps: Dependencies,
|
||||
_executor: Executor,
|
||||
) -> Result<Self, String> {
|
||||
Ok(KeyServer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "secretstore")]
|
||||
mod server {
|
||||
use std::sync::Arc;
|
||||
use ethcore_secretstore;
|
||||
use ethkey::KeyPair;
|
||||
use ansi_term::Colour::{Red, White};
|
||||
use db;
|
||||
use super::{Configuration, Dependencies, NodeSecretKey, ContractAddress, Executor};
|
||||
use super::{Configuration, ContractAddress, Dependencies, Executor, NodeSecretKey};
|
||||
use ansi_term::Colour::{Red, White};
|
||||
use db;
|
||||
use ethcore_secretstore;
|
||||
use ethkey::KeyPair;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn into_service_contract_address(address: ContractAddress) -> ethcore_secretstore::ContractAddress {
|
||||
match address {
|
||||
ContractAddress::Registry => ethcore_secretstore::ContractAddress::Registry,
|
||||
ContractAddress::Address(address) => ethcore_secretstore::ContractAddress::Address(address),
|
||||
}
|
||||
}
|
||||
fn into_service_contract_address(
|
||||
address: ContractAddress,
|
||||
) -> ethcore_secretstore::ContractAddress {
|
||||
match address {
|
||||
ContractAddress::Registry => ethcore_secretstore::ContractAddress::Registry,
|
||||
ContractAddress::Address(address) => {
|
||||
ethcore_secretstore::ContractAddress::Address(address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Key server
|
||||
pub struct KeyServer {
|
||||
_key_server: Box<ethcore_secretstore::KeyServer>,
|
||||
}
|
||||
/// Key server
|
||||
pub struct KeyServer {
|
||||
_key_server: Box<ethcore_secretstore::KeyServer>,
|
||||
}
|
||||
|
||||
impl KeyServer {
|
||||
/// Create new key server
|
||||
pub fn new(mut conf: Configuration, deps: Dependencies, executor: Executor) -> Result<Self, String> {
|
||||
let self_secret: Arc<ethcore_secretstore::NodeKeyPair> = match conf.self_secret.take() {
|
||||
Some(NodeSecretKey::Plain(secret)) => Arc::new(ethcore_secretstore::PlainNodeKeyPair::new(
|
||||
KeyPair::from_secret(secret).map_err(|e| format!("invalid secret: {}", e))?)),
|
||||
#[cfg(feature = "accounts")]
|
||||
Some(NodeSecretKey::KeyStore(account)) => {
|
||||
// Check if account exists
|
||||
if !deps.account_provider.has_account(account.clone()) {
|
||||
return Err(format!("Account {} passed as secret store node key is not found", account));
|
||||
}
|
||||
impl KeyServer {
|
||||
/// Create new key server
|
||||
pub fn new(
|
||||
mut conf: Configuration,
|
||||
deps: Dependencies,
|
||||
executor: Executor,
|
||||
) -> Result<Self, String> {
|
||||
let self_secret: Arc<ethcore_secretstore::NodeKeyPair> = match conf.self_secret.take() {
|
||||
Some(NodeSecretKey::Plain(secret)) => {
|
||||
Arc::new(ethcore_secretstore::PlainNodeKeyPair::new(
|
||||
KeyPair::from_secret(secret)
|
||||
.map_err(|e| format!("invalid secret: {}", e))?,
|
||||
))
|
||||
}
|
||||
#[cfg(feature = "accounts")]
|
||||
Some(NodeSecretKey::KeyStore(account)) => {
|
||||
// Check if account exists
|
||||
if !deps.account_provider.has_account(account.clone()) {
|
||||
return Err(format!(
|
||||
"Account {} passed as secret store node key is not found",
|
||||
account
|
||||
));
|
||||
}
|
||||
|
||||
// Check if any passwords have been read from the password file(s)
|
||||
if deps.accounts_passwords.is_empty() {
|
||||
return Err(format!("No password found for the secret store node account {}", account));
|
||||
}
|
||||
// Check if any passwords have been read from the password file(s)
|
||||
if deps.accounts_passwords.is_empty() {
|
||||
return Err(format!(
|
||||
"No password found for the secret store node account {}",
|
||||
account
|
||||
));
|
||||
}
|
||||
|
||||
// Attempt to sign in the engine signer.
|
||||
let password = deps.accounts_passwords.iter()
|
||||
.find(|p| deps.account_provider.sign(account.clone(), Some((*p).clone()), Default::default()).is_ok())
|
||||
.ok_or_else(|| format!("No valid password for the secret store node account {}", account))?;
|
||||
Arc::new(ethcore_secretstore::KeyStoreNodeKeyPair::new(deps.account_provider, account, password.clone())
|
||||
.map_err(|e| format!("{}", e))?)
|
||||
},
|
||||
None => return Err("self secret is required when using secretstore".into()),
|
||||
};
|
||||
// Attempt to sign in the engine signer.
|
||||
let password = deps
|
||||
.accounts_passwords
|
||||
.iter()
|
||||
.find(|p| {
|
||||
deps.account_provider
|
||||
.sign(account.clone(), Some((*p).clone()), Default::default())
|
||||
.is_ok()
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"No valid password for the secret store node account {}",
|
||||
account
|
||||
)
|
||||
})?;
|
||||
Arc::new(
|
||||
ethcore_secretstore::KeyStoreNodeKeyPair::new(
|
||||
deps.account_provider,
|
||||
account,
|
||||
password.clone(),
|
||||
)
|
||||
.map_err(|e| format!("{}", e))?,
|
||||
)
|
||||
}
|
||||
None => return Err("self secret is required when using secretstore".into()),
|
||||
};
|
||||
|
||||
info!("Starting SecretStore node: {}", White.bold().paint(format!("{:?}", self_secret.public())));
|
||||
if conf.acl_check_contract_address.is_none() {
|
||||
warn!("Running SecretStore with disabled ACL check: {}", Red.bold().paint("everyone has access to stored keys"));
|
||||
}
|
||||
info!(
|
||||
"Starting SecretStore node: {}",
|
||||
White.bold().paint(format!("{:?}", self_secret.public()))
|
||||
);
|
||||
if conf.acl_check_contract_address.is_none() {
|
||||
warn!(
|
||||
"Running SecretStore with disabled ACL check: {}",
|
||||
Red.bold().paint("everyone has access to stored keys")
|
||||
);
|
||||
}
|
||||
|
||||
let key_server_name = format!("{}:{}", conf.interface, conf.port);
|
||||
let mut cconf = ethcore_secretstore::ServiceConfiguration {
|
||||
listener_address: if conf.http_enabled { Some(ethcore_secretstore::NodeAddress {
|
||||
address: conf.http_interface.clone(),
|
||||
port: conf.http_port,
|
||||
}) } else { None },
|
||||
service_contract_address: conf.service_contract_address.map(into_service_contract_address),
|
||||
service_contract_srv_gen_address: conf.service_contract_srv_gen_address.map(into_service_contract_address),
|
||||
service_contract_srv_retr_address: conf.service_contract_srv_retr_address.map(into_service_contract_address),
|
||||
service_contract_doc_store_address: conf.service_contract_doc_store_address.map(into_service_contract_address),
|
||||
service_contract_doc_sretr_address: conf.service_contract_doc_sretr_address.map(into_service_contract_address),
|
||||
acl_check_contract_address: conf.acl_check_contract_address.map(into_service_contract_address),
|
||||
cluster_config: ethcore_secretstore::ClusterConfiguration {
|
||||
listener_address: ethcore_secretstore::NodeAddress {
|
||||
address: conf.interface.clone(),
|
||||
port: conf.port,
|
||||
},
|
||||
nodes: conf.nodes.into_iter().map(|(p, (ip, port))| (p, ethcore_secretstore::NodeAddress {
|
||||
address: ip,
|
||||
port: port,
|
||||
})).collect(),
|
||||
key_server_set_contract_address: conf.key_server_set_contract_address.map(into_service_contract_address),
|
||||
allow_connecting_to_higher_nodes: true,
|
||||
admin_public: conf.admin_public,
|
||||
auto_migrate_enabled: conf.auto_migrate_enabled,
|
||||
},
|
||||
};
|
||||
let key_server_name = format!("{}:{}", conf.interface, conf.port);
|
||||
let mut cconf = ethcore_secretstore::ServiceConfiguration {
|
||||
listener_address: if conf.http_enabled {
|
||||
Some(ethcore_secretstore::NodeAddress {
|
||||
address: conf.http_interface.clone(),
|
||||
port: conf.http_port,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
},
|
||||
service_contract_address: conf
|
||||
.service_contract_address
|
||||
.map(into_service_contract_address),
|
||||
service_contract_srv_gen_address: conf
|
||||
.service_contract_srv_gen_address
|
||||
.map(into_service_contract_address),
|
||||
service_contract_srv_retr_address: conf
|
||||
.service_contract_srv_retr_address
|
||||
.map(into_service_contract_address),
|
||||
service_contract_doc_store_address: conf
|
||||
.service_contract_doc_store_address
|
||||
.map(into_service_contract_address),
|
||||
service_contract_doc_sretr_address: conf
|
||||
.service_contract_doc_sretr_address
|
||||
.map(into_service_contract_address),
|
||||
acl_check_contract_address: conf
|
||||
.acl_check_contract_address
|
||||
.map(into_service_contract_address),
|
||||
cluster_config: ethcore_secretstore::ClusterConfiguration {
|
||||
listener_address: ethcore_secretstore::NodeAddress {
|
||||
address: conf.interface.clone(),
|
||||
port: conf.port,
|
||||
},
|
||||
nodes: conf
|
||||
.nodes
|
||||
.into_iter()
|
||||
.map(|(p, (ip, port))| {
|
||||
(
|
||||
p,
|
||||
ethcore_secretstore::NodeAddress {
|
||||
address: ip,
|
||||
port: port,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
key_server_set_contract_address: conf
|
||||
.key_server_set_contract_address
|
||||
.map(into_service_contract_address),
|
||||
allow_connecting_to_higher_nodes: true,
|
||||
admin_public: conf.admin_public,
|
||||
auto_migrate_enabled: conf.auto_migrate_enabled,
|
||||
},
|
||||
};
|
||||
|
||||
cconf.cluster_config.nodes.insert(self_secret.public().clone(), cconf.cluster_config.listener_address.clone());
|
||||
cconf.cluster_config.nodes.insert(
|
||||
self_secret.public().clone(),
|
||||
cconf.cluster_config.listener_address.clone(),
|
||||
);
|
||||
|
||||
let db = db::open_secretstore_db(&conf.data_path)?;
|
||||
let key_server = ethcore_secretstore::start(deps.client, deps.sync, deps.miner, self_secret, cconf, db, executor)
|
||||
.map_err(|e| format!("Error starting KeyServer {}: {}", key_server_name, e))?;
|
||||
let db = db::open_secretstore_db(&conf.data_path)?;
|
||||
let key_server = ethcore_secretstore::start(
|
||||
deps.client,
|
||||
deps.sync,
|
||||
deps.miner,
|
||||
self_secret,
|
||||
cconf,
|
||||
db,
|
||||
executor,
|
||||
)
|
||||
.map_err(|e| format!("Error starting KeyServer {}: {}", key_server_name, e))?;
|
||||
|
||||
Ok(KeyServer {
|
||||
_key_server: key_server,
|
||||
})
|
||||
}
|
||||
}
|
||||
Ok(KeyServer {
|
||||
_key_server: key_server,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use self::server::KeyServer;
|
||||
|
||||
impl Default for Configuration {
|
||||
fn default() -> Self {
|
||||
let data_dir = default_data_path();
|
||||
Configuration {
|
||||
enabled: true,
|
||||
http_enabled: true,
|
||||
auto_migrate_enabled: true,
|
||||
acl_check_contract_address: Some(ContractAddress::Registry),
|
||||
service_contract_address: None,
|
||||
service_contract_srv_gen_address: None,
|
||||
service_contract_srv_retr_address: None,
|
||||
service_contract_doc_store_address: None,
|
||||
service_contract_doc_sretr_address: None,
|
||||
self_secret: None,
|
||||
admin_public: None,
|
||||
nodes: BTreeMap::new(),
|
||||
key_server_set_contract_address: Some(ContractAddress::Registry),
|
||||
interface: "127.0.0.1".to_owned(),
|
||||
port: 8083,
|
||||
http_interface: "127.0.0.1".to_owned(),
|
||||
http_port: 8082,
|
||||
data_path: replace_home(&data_dir, "$BASE/secretstore"),
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
let data_dir = default_data_path();
|
||||
Configuration {
|
||||
enabled: true,
|
||||
http_enabled: true,
|
||||
auto_migrate_enabled: true,
|
||||
acl_check_contract_address: Some(ContractAddress::Registry),
|
||||
service_contract_address: None,
|
||||
service_contract_srv_gen_address: None,
|
||||
service_contract_srv_retr_address: None,
|
||||
service_contract_doc_store_address: None,
|
||||
service_contract_doc_sretr_address: None,
|
||||
self_secret: None,
|
||||
admin_public: None,
|
||||
nodes: BTreeMap::new(),
|
||||
key_server_set_contract_address: Some(ContractAddress::Registry),
|
||||
interface: "127.0.0.1".to_owned(),
|
||||
port: 8083,
|
||||
http_interface: "127.0.0.1".to_owned(),
|
||||
http_port: 8082,
|
||||
data_path: replace_home(&data_dir, "$BASE/secretstore"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Start secret store-related functionality
|
||||
pub fn start(conf: Configuration, deps: Dependencies, executor: Executor) -> Result<Option<KeyServer>, String> {
|
||||
if !conf.enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
pub fn start(
|
||||
conf: Configuration,
|
||||
deps: Dependencies,
|
||||
executor: Executor,
|
||||
) -> Result<Option<KeyServer>, String> {
|
||||
if !conf.enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
KeyServer::new(conf, deps, executor)
|
||||
.map(|s| Some(s))
|
||||
KeyServer::new(conf, deps, executor).map(|s| Some(s))
|
||||
}
|
||||
|
||||
@@ -14,72 +14,87 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use ansi_term::Colour::White;
|
||||
use ethcore_logger::Config as LogConfig;
|
||||
use rpc;
|
||||
use rpc_apis;
|
||||
use parity_rpc;
|
||||
use path::restrict_permissions_owner;
|
||||
use rpc;
|
||||
use rpc_apis;
|
||||
|
||||
pub const CODES_FILENAME: &'static str = "authcodes";
|
||||
|
||||
pub struct NewToken {
|
||||
pub token: String,
|
||||
pub message: String,
|
||||
pub token: String,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
pub fn new_service(ws_conf: &rpc::WsConfiguration, logger_config: &LogConfig) -> rpc_apis::SignerService {
|
||||
let logger_config_color = logger_config.color;
|
||||
let signer_path = ws_conf.signer_path.clone();
|
||||
let signer_enabled = ws_conf.support_token_api;
|
||||
pub fn new_service(
|
||||
ws_conf: &rpc::WsConfiguration,
|
||||
logger_config: &LogConfig,
|
||||
) -> rpc_apis::SignerService {
|
||||
let logger_config_color = logger_config.color;
|
||||
let signer_path = ws_conf.signer_path.clone();
|
||||
let signer_enabled = ws_conf.support_token_api;
|
||||
|
||||
rpc_apis::SignerService::new(move || {
|
||||
generate_new_token(&signer_path, logger_config_color).map_err(|e| format!("{:?}", e))
|
||||
}, signer_enabled)
|
||||
rpc_apis::SignerService::new(
|
||||
move || {
|
||||
generate_new_token(&signer_path, logger_config_color).map_err(|e| format!("{:?}", e))
|
||||
},
|
||||
signer_enabled,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn codes_path(path: &Path) -> PathBuf {
|
||||
let mut p = path.to_owned();
|
||||
p.push(CODES_FILENAME);
|
||||
let _ = restrict_permissions_owner(&p, true, false);
|
||||
p
|
||||
let mut p = path.to_owned();
|
||||
p.push(CODES_FILENAME);
|
||||
let _ = restrict_permissions_owner(&p, true, false);
|
||||
p
|
||||
}
|
||||
|
||||
pub fn execute(ws_conf: rpc::WsConfiguration, logger_config: LogConfig) -> Result<String, String> {
|
||||
Ok(generate_token_and_url(&ws_conf, &logger_config)?.message)
|
||||
Ok(generate_token_and_url(&ws_conf, &logger_config)?.message)
|
||||
}
|
||||
|
||||
pub fn generate_token_and_url(ws_conf: &rpc::WsConfiguration, logger_config: &LogConfig) -> Result<NewToken, String> {
|
||||
let code = generate_new_token(&ws_conf.signer_path, logger_config.color).map_err(|err| format!("Error generating token: {:?}", err))?;
|
||||
let colored = |s: String| match logger_config.color {
|
||||
true => format!("{}", White.bold().paint(s)),
|
||||
false => s,
|
||||
};
|
||||
pub fn generate_token_and_url(
|
||||
ws_conf: &rpc::WsConfiguration,
|
||||
logger_config: &LogConfig,
|
||||
) -> Result<NewToken, String> {
|
||||
let code = generate_new_token(&ws_conf.signer_path, logger_config.color)
|
||||
.map_err(|err| format!("Error generating token: {:?}", err))?;
|
||||
let colored = |s: String| match logger_config.color {
|
||||
true => format!("{}", White.bold().paint(s)),
|
||||
false => s,
|
||||
};
|
||||
|
||||
Ok(NewToken {
|
||||
token: code.clone(),
|
||||
message: format!(
|
||||
r#"
|
||||
Ok(NewToken {
|
||||
token: code.clone(),
|
||||
message: format!(
|
||||
r#"
|
||||
Generated token:
|
||||
{}
|
||||
"#,
|
||||
colored(code)
|
||||
),
|
||||
})
|
||||
colored(code)
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_new_token(path: &Path, logger_config_color: bool) -> io::Result<String> {
|
||||
let path = codes_path(path);
|
||||
let mut codes = parity_rpc::AuthCodes::from_file(&path)?;
|
||||
codes.clear_garbage();
|
||||
let code = codes.generate_new()?;
|
||||
codes.to_file(&path)?;
|
||||
trace!("New key code created: {}", match logger_config_color {
|
||||
true => format!("{}", White.bold().paint(&code[..])),
|
||||
false => format!("{}", &code[..])
|
||||
});
|
||||
Ok(code)
|
||||
let path = codes_path(path);
|
||||
let mut codes = parity_rpc::AuthCodes::from_file(&path)?;
|
||||
codes.clear_garbage();
|
||||
let code = codes.generate_new()?;
|
||||
codes.to_file(&path)?;
|
||||
trace!(
|
||||
"New key code created: {}",
|
||||
match logger_config_color {
|
||||
true => format!("{}", White.bold().paint(&code[..])),
|
||||
false => format!("{}", &code[..]),
|
||||
}
|
||||
);
|
||||
Ok(code)
|
||||
}
|
||||
|
||||
@@ -16,279 +16,336 @@
|
||||
|
||||
//! Snapshot and restoration commands.
|
||||
|
||||
use std::time::Duration;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use hash::keccak;
|
||||
use ethcore::snapshot::{Progress, RestorationStatus, SnapshotConfiguration, SnapshotService as SS};
|
||||
use ethcore::snapshot::io::{SnapshotReader, PackedReader, PackedWriter};
|
||||
use ethcore::snapshot::service::Service as SnapshotService;
|
||||
use ethcore::client::{Mode, DatabaseCompactionProfile, VMType};
|
||||
use ethcore::miner::Miner;
|
||||
use ethcore::{
|
||||
client::{DatabaseCompactionProfile, Mode, VMType},
|
||||
miner::Miner,
|
||||
snapshot::{
|
||||
io::{PackedReader, PackedWriter, SnapshotReader},
|
||||
service::Service as SnapshotService,
|
||||
Progress, RestorationStatus, SnapshotConfiguration, SnapshotService as SS,
|
||||
},
|
||||
};
|
||||
use ethcore_service::ClientService;
|
||||
use hash::keccak;
|
||||
use types::ids::BlockId;
|
||||
|
||||
use cache::CacheConfig;
|
||||
use params::{SpecType, Pruning, Switch, tracing_switch_to_bool, fatdb_switch_to_bool};
|
||||
use helpers::{to_client_config, execute_upgrades};
|
||||
use dir::Directories;
|
||||
use user_defaults::UserDefaults;
|
||||
use ethcore_private_tx;
|
||||
use db;
|
||||
use dir::Directories;
|
||||
use ethcore_private_tx;
|
||||
use helpers::{execute_upgrades, to_client_config};
|
||||
use params::{fatdb_switch_to_bool, tracing_switch_to_bool, Pruning, SpecType, Switch};
|
||||
use user_defaults::UserDefaults;
|
||||
|
||||
/// Kinds of snapshot commands.
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum Kind {
|
||||
/// Take a snapshot.
|
||||
Take,
|
||||
/// Restore a snapshot.
|
||||
Restore
|
||||
/// Take a snapshot.
|
||||
Take,
|
||||
/// Restore a snapshot.
|
||||
Restore,
|
||||
}
|
||||
|
||||
/// Command for snapshot creation or restoration.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct SnapshotCommand {
|
||||
pub cache_config: CacheConfig,
|
||||
pub dirs: Directories,
|
||||
pub spec: SpecType,
|
||||
pub pruning: Pruning,
|
||||
pub pruning_history: u64,
|
||||
pub pruning_memory: usize,
|
||||
pub tracing: Switch,
|
||||
pub fat_db: Switch,
|
||||
pub compaction: DatabaseCompactionProfile,
|
||||
pub file_path: Option<String>,
|
||||
pub kind: Kind,
|
||||
pub block_at: BlockId,
|
||||
pub max_round_blocks_to_import: usize,
|
||||
pub snapshot_conf: SnapshotConfiguration,
|
||||
pub cache_config: CacheConfig,
|
||||
pub dirs: Directories,
|
||||
pub spec: SpecType,
|
||||
pub pruning: Pruning,
|
||||
pub pruning_history: u64,
|
||||
pub pruning_memory: usize,
|
||||
pub tracing: Switch,
|
||||
pub fat_db: Switch,
|
||||
pub compaction: DatabaseCompactionProfile,
|
||||
pub file_path: Option<String>,
|
||||
pub kind: Kind,
|
||||
pub block_at: BlockId,
|
||||
pub max_round_blocks_to_import: usize,
|
||||
pub snapshot_conf: SnapshotConfiguration,
|
||||
}
|
||||
|
||||
// helper for reading chunks from arbitrary reader and feeding them into the
|
||||
// service.
|
||||
fn restore_using<R: SnapshotReader>(snapshot: Arc<SnapshotService>, reader: &R, recover: bool) -> Result<(), String> {
|
||||
let manifest = reader.manifest();
|
||||
fn restore_using<R: SnapshotReader>(
|
||||
snapshot: Arc<SnapshotService>,
|
||||
reader: &R,
|
||||
recover: bool,
|
||||
) -> Result<(), String> {
|
||||
let manifest = reader.manifest();
|
||||
|
||||
info!("Restoring to block #{} (0x{:?})", manifest.block_number, manifest.block_hash);
|
||||
info!(
|
||||
"Restoring to block #{} (0x{:?})",
|
||||
manifest.block_number, manifest.block_hash
|
||||
);
|
||||
|
||||
snapshot.init_restore(manifest.clone(), recover).map_err(|e| {
|
||||
format!("Failed to begin restoration: {}", e)
|
||||
})?;
|
||||
snapshot
|
||||
.init_restore(manifest.clone(), recover)
|
||||
.map_err(|e| format!("Failed to begin restoration: {}", e))?;
|
||||
|
||||
let (num_state, num_blocks) = (manifest.state_hashes.len(), manifest.block_hashes.len());
|
||||
let (num_state, num_blocks) = (manifest.state_hashes.len(), manifest.block_hashes.len());
|
||||
|
||||
let informant_handle = snapshot.clone();
|
||||
::std::thread::spawn(move || {
|
||||
while let RestorationStatus::Ongoing { state_chunks_done, block_chunks_done, .. } = informant_handle.status() {
|
||||
info!("Processed {}/{} state chunks and {}/{} block chunks.",
|
||||
state_chunks_done, num_state, block_chunks_done, num_blocks);
|
||||
::std::thread::sleep(Duration::from_secs(5));
|
||||
}
|
||||
});
|
||||
let informant_handle = snapshot.clone();
|
||||
::std::thread::spawn(move || {
|
||||
while let RestorationStatus::Ongoing {
|
||||
state_chunks_done,
|
||||
block_chunks_done,
|
||||
..
|
||||
} = informant_handle.status()
|
||||
{
|
||||
info!(
|
||||
"Processed {}/{} state chunks and {}/{} block chunks.",
|
||||
state_chunks_done, num_state, block_chunks_done, num_blocks
|
||||
);
|
||||
::std::thread::sleep(Duration::from_secs(5));
|
||||
}
|
||||
});
|
||||
|
||||
info!("Restoring state");
|
||||
for &state_hash in &manifest.state_hashes {
|
||||
if snapshot.status() == RestorationStatus::Failed {
|
||||
return Err("Restoration failed".into());
|
||||
}
|
||||
info!("Restoring state");
|
||||
for &state_hash in &manifest.state_hashes {
|
||||
if snapshot.status() == RestorationStatus::Failed {
|
||||
return Err("Restoration failed".into());
|
||||
}
|
||||
|
||||
let chunk = reader.chunk(state_hash)
|
||||
.map_err(|e| format!("Encountered error while reading chunk {:?}: {}", state_hash, e))?;
|
||||
let chunk = reader.chunk(state_hash).map_err(|e| {
|
||||
format!(
|
||||
"Encountered error while reading chunk {:?}: {}",
|
||||
state_hash, e
|
||||
)
|
||||
})?;
|
||||
|
||||
let hash = keccak(&chunk);
|
||||
if hash != state_hash {
|
||||
return Err(format!("Mismatched chunk hash. Expected {:?}, got {:?}", state_hash, hash));
|
||||
}
|
||||
let hash = keccak(&chunk);
|
||||
if hash != state_hash {
|
||||
return Err(format!(
|
||||
"Mismatched chunk hash. Expected {:?}, got {:?}",
|
||||
state_hash, hash
|
||||
));
|
||||
}
|
||||
|
||||
snapshot.feed_state_chunk(state_hash, &chunk);
|
||||
}
|
||||
snapshot.feed_state_chunk(state_hash, &chunk);
|
||||
}
|
||||
|
||||
info!("Restoring blocks");
|
||||
for &block_hash in &manifest.block_hashes {
|
||||
if snapshot.status() == RestorationStatus::Failed {
|
||||
return Err("Restoration failed".into());
|
||||
}
|
||||
info!("Restoring blocks");
|
||||
for &block_hash in &manifest.block_hashes {
|
||||
if snapshot.status() == RestorationStatus::Failed {
|
||||
return Err("Restoration failed".into());
|
||||
}
|
||||
|
||||
let chunk = reader.chunk(block_hash)
|
||||
.map_err(|e| format!("Encountered error while reading chunk {:?}: {}", block_hash, e))?;
|
||||
let chunk = reader.chunk(block_hash).map_err(|e| {
|
||||
format!(
|
||||
"Encountered error while reading chunk {:?}: {}",
|
||||
block_hash, e
|
||||
)
|
||||
})?;
|
||||
|
||||
let hash = keccak(&chunk);
|
||||
if hash != block_hash {
|
||||
return Err(format!("Mismatched chunk hash. Expected {:?}, got {:?}", block_hash, hash));
|
||||
}
|
||||
snapshot.feed_block_chunk(block_hash, &chunk);
|
||||
}
|
||||
let hash = keccak(&chunk);
|
||||
if hash != block_hash {
|
||||
return Err(format!(
|
||||
"Mismatched chunk hash. Expected {:?}, got {:?}",
|
||||
block_hash, hash
|
||||
));
|
||||
}
|
||||
snapshot.feed_block_chunk(block_hash, &chunk);
|
||||
}
|
||||
|
||||
match snapshot.status() {
|
||||
RestorationStatus::Ongoing { .. } => Err("Snapshot file is incomplete and missing chunks.".into()),
|
||||
RestorationStatus::Initializing { .. } => Err("Snapshot restoration is still initializing.".into()),
|
||||
RestorationStatus::Failed => Err("Snapshot restoration failed.".into()),
|
||||
RestorationStatus::Inactive => {
|
||||
info!("Restoration complete.");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
match snapshot.status() {
|
||||
RestorationStatus::Ongoing { .. } => {
|
||||
Err("Snapshot file is incomplete and missing chunks.".into())
|
||||
}
|
||||
RestorationStatus::Initializing { .. } => {
|
||||
Err("Snapshot restoration is still initializing.".into())
|
||||
}
|
||||
RestorationStatus::Failed => Err("Snapshot restoration failed.".into()),
|
||||
RestorationStatus::Inactive => {
|
||||
info!("Restoration complete.");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SnapshotCommand {
|
||||
// shared portion of snapshot commands: start the client service
|
||||
fn start_service(self) -> Result<ClientService, String> {
|
||||
// load spec file
|
||||
let spec = self.spec.spec(&self.dirs.cache)?;
|
||||
// shared portion of snapshot commands: start the client service
|
||||
fn start_service(self) -> Result<ClientService, String> {
|
||||
// load spec file
|
||||
let spec = self.spec.spec(&self.dirs.cache)?;
|
||||
|
||||
// load genesis hash
|
||||
let genesis_hash = spec.genesis_header().hash();
|
||||
// load genesis hash
|
||||
let genesis_hash = spec.genesis_header().hash();
|
||||
|
||||
// database paths
|
||||
let db_dirs = self.dirs.database(genesis_hash, None, spec.data_dir.clone());
|
||||
// database paths
|
||||
let db_dirs = self
|
||||
.dirs
|
||||
.database(genesis_hash, None, spec.data_dir.clone());
|
||||
|
||||
// user defaults path
|
||||
let user_defaults_path = db_dirs.user_defaults_path();
|
||||
// user defaults path
|
||||
let user_defaults_path = db_dirs.user_defaults_path();
|
||||
|
||||
// load user defaults
|
||||
let user_defaults = UserDefaults::load(&user_defaults_path)?;
|
||||
// load user defaults
|
||||
let user_defaults = UserDefaults::load(&user_defaults_path)?;
|
||||
|
||||
// select pruning algorithm
|
||||
let algorithm = self.pruning.to_algorithm(&user_defaults);
|
||||
// select pruning algorithm
|
||||
let algorithm = self.pruning.to_algorithm(&user_defaults);
|
||||
|
||||
// check if tracing is on
|
||||
let tracing = tracing_switch_to_bool(self.tracing, &user_defaults)?;
|
||||
// check if tracing is on
|
||||
let tracing = tracing_switch_to_bool(self.tracing, &user_defaults)?;
|
||||
|
||||
// check if fatdb is on
|
||||
let fat_db = fatdb_switch_to_bool(self.fat_db, &user_defaults, algorithm)?;
|
||||
// check if fatdb is on
|
||||
let fat_db = fatdb_switch_to_bool(self.fat_db, &user_defaults, algorithm)?;
|
||||
|
||||
// prepare client and snapshot paths.
|
||||
let client_path = db_dirs.client_path(algorithm);
|
||||
let snapshot_path = db_dirs.snapshot_path();
|
||||
// prepare client and snapshot paths.
|
||||
let client_path = db_dirs.client_path(algorithm);
|
||||
let snapshot_path = db_dirs.snapshot_path();
|
||||
|
||||
// execute upgrades
|
||||
execute_upgrades(&self.dirs.base, &db_dirs, algorithm, &self.compaction)?;
|
||||
// execute upgrades
|
||||
execute_upgrades(&self.dirs.base, &db_dirs, algorithm, &self.compaction)?;
|
||||
|
||||
// prepare client config
|
||||
let mut client_config = to_client_config(
|
||||
&self.cache_config,
|
||||
spec.name.to_lowercase(),
|
||||
Mode::Active,
|
||||
tracing,
|
||||
fat_db,
|
||||
self.compaction,
|
||||
VMType::default(),
|
||||
"".into(),
|
||||
algorithm,
|
||||
self.pruning_history,
|
||||
self.pruning_memory,
|
||||
true,
|
||||
self.max_round_blocks_to_import,
|
||||
);
|
||||
// prepare client config
|
||||
let mut client_config = to_client_config(
|
||||
&self.cache_config,
|
||||
spec.name.to_lowercase(),
|
||||
Mode::Active,
|
||||
tracing,
|
||||
fat_db,
|
||||
self.compaction,
|
||||
VMType::default(),
|
||||
"".into(),
|
||||
algorithm,
|
||||
self.pruning_history,
|
||||
self.pruning_memory,
|
||||
true,
|
||||
self.max_round_blocks_to_import,
|
||||
);
|
||||
|
||||
client_config.snapshot = self.snapshot_conf;
|
||||
client_config.snapshot = self.snapshot_conf;
|
||||
|
||||
let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config);
|
||||
let client_db = restoration_db_handler.open(&client_path)
|
||||
.map_err(|e| format!("Failed to open database {:?}", e))?;
|
||||
let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config);
|
||||
let client_db = restoration_db_handler
|
||||
.open(&client_path)
|
||||
.map_err(|e| format!("Failed to open database {:?}", e))?;
|
||||
|
||||
let service = ClientService::start(
|
||||
client_config,
|
||||
&spec,
|
||||
client_db,
|
||||
&snapshot_path,
|
||||
restoration_db_handler,
|
||||
&self.dirs.ipc_path(),
|
||||
// TODO [ToDr] don't use test miner here
|
||||
// (actually don't require miner at all)
|
||||
Arc::new(Miner::new_for_tests(&spec, None)),
|
||||
Arc::new(ethcore_private_tx::DummySigner),
|
||||
Box::new(ethcore_private_tx::NoopEncryptor),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
).map_err(|e| format!("Client service error: {:?}", e))?;
|
||||
let service = ClientService::start(
|
||||
client_config,
|
||||
&spec,
|
||||
client_db,
|
||||
&snapshot_path,
|
||||
restoration_db_handler,
|
||||
&self.dirs.ipc_path(),
|
||||
// TODO [ToDr] don't use test miner here
|
||||
// (actually don't require miner at all)
|
||||
Arc::new(Miner::new_for_tests(&spec, None)),
|
||||
Arc::new(ethcore_private_tx::DummySigner),
|
||||
Box::new(ethcore_private_tx::NoopEncryptor),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
)
|
||||
.map_err(|e| format!("Client service error: {:?}", e))?;
|
||||
|
||||
Ok(service)
|
||||
}
|
||||
/// restore from a snapshot
|
||||
pub fn restore(self) -> Result<(), String> {
|
||||
let file = self.file_path.clone();
|
||||
let service = self.start_service()?;
|
||||
Ok(service)
|
||||
}
|
||||
/// restore from a snapshot
|
||||
pub fn restore(self) -> Result<(), String> {
|
||||
let file = self.file_path.clone();
|
||||
let service = self.start_service()?;
|
||||
|
||||
warn!("Snapshot restoration is experimental and the format may be subject to change.");
|
||||
warn!("On encountering an unexpected error, please ensure that you have a recent snapshot.");
|
||||
warn!("Snapshot restoration is experimental and the format may be subject to change.");
|
||||
warn!(
|
||||
"On encountering an unexpected error, please ensure that you have a recent snapshot."
|
||||
);
|
||||
|
||||
let snapshot = service.snapshot_service();
|
||||
let snapshot = service.snapshot_service();
|
||||
|
||||
if let Some(file) = file {
|
||||
info!("Attempting to restore from snapshot at '{}'", file);
|
||||
if let Some(file) = file {
|
||||
info!("Attempting to restore from snapshot at '{}'", file);
|
||||
|
||||
let reader = PackedReader::new(Path::new(&file))
|
||||
.map_err(|e| format!("Couldn't open snapshot file: {}", e))
|
||||
.and_then(|x| x.ok_or("Snapshot file has invalid format.".into()));
|
||||
let reader = PackedReader::new(Path::new(&file))
|
||||
.map_err(|e| format!("Couldn't open snapshot file: {}", e))
|
||||
.and_then(|x| x.ok_or("Snapshot file has invalid format.".into()));
|
||||
|
||||
let reader = reader?;
|
||||
restore_using(snapshot, &reader, true)?;
|
||||
} else {
|
||||
info!("Attempting to restore from local snapshot.");
|
||||
let reader = reader?;
|
||||
restore_using(snapshot, &reader, true)?;
|
||||
} else {
|
||||
info!("Attempting to restore from local snapshot.");
|
||||
|
||||
// attempting restoration with recovery will lead to deadlock
|
||||
// as we currently hold a read lock on the service's reader.
|
||||
match *snapshot.reader() {
|
||||
Some(ref reader) => restore_using(snapshot.clone(), reader, false)?,
|
||||
None => return Err("No local snapshot found.".into()),
|
||||
}
|
||||
}
|
||||
// attempting restoration with recovery will lead to deadlock
|
||||
// as we currently hold a read lock on the service's reader.
|
||||
match *snapshot.reader() {
|
||||
Some(ref reader) => restore_using(snapshot.clone(), reader, false)?,
|
||||
None => return Err("No local snapshot found.".into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Take a snapshot from the head of the chain.
|
||||
pub fn take_snapshot(self) -> Result<(), String> {
|
||||
let file_path = self.file_path.clone().ok_or("No file path provided.".to_owned())?;
|
||||
let file_path: PathBuf = file_path.into();
|
||||
let block_at = self.block_at;
|
||||
let service = self.start_service()?;
|
||||
/// Take a snapshot from the head of the chain.
|
||||
pub fn take_snapshot(self) -> Result<(), String> {
|
||||
let file_path = self
|
||||
.file_path
|
||||
.clone()
|
||||
.ok_or("No file path provided.".to_owned())?;
|
||||
let file_path: PathBuf = file_path.into();
|
||||
let block_at = self.block_at;
|
||||
let service = self.start_service()?;
|
||||
|
||||
warn!("Snapshots are currently experimental. File formats may be subject to change.");
|
||||
warn!("Snapshots are currently experimental. File formats may be subject to change.");
|
||||
|
||||
let writer = PackedWriter::new(&file_path)
|
||||
.map_err(|e| format!("Failed to open snapshot writer: {}", e))?;
|
||||
let writer = PackedWriter::new(&file_path)
|
||||
.map_err(|e| format!("Failed to open snapshot writer: {}", e))?;
|
||||
|
||||
let progress = Arc::new(Progress::default());
|
||||
let p = progress.clone();
|
||||
let informant_handle = ::std::thread::spawn(move || {
|
||||
::std::thread::sleep(Duration::from_secs(5));
|
||||
let progress = Arc::new(Progress::default());
|
||||
let p = progress.clone();
|
||||
let informant_handle = ::std::thread::spawn(move || {
|
||||
::std::thread::sleep(Duration::from_secs(5));
|
||||
|
||||
let mut last_size = 0;
|
||||
while !p.done() {
|
||||
let cur_size = p.size();
|
||||
if cur_size != last_size {
|
||||
last_size = cur_size;
|
||||
let bytes = ::informant::format_bytes(cur_size as usize);
|
||||
info!("Snapshot: {} accounts {} blocks {}", p.accounts(), p.blocks(), bytes);
|
||||
}
|
||||
let mut last_size = 0;
|
||||
while !p.done() {
|
||||
let cur_size = p.size();
|
||||
if cur_size != last_size {
|
||||
last_size = cur_size;
|
||||
let bytes = ::informant::format_bytes(cur_size as usize);
|
||||
info!(
|
||||
"Snapshot: {} accounts {} blocks {}",
|
||||
p.accounts(),
|
||||
p.blocks(),
|
||||
bytes
|
||||
);
|
||||
}
|
||||
|
||||
::std::thread::sleep(Duration::from_secs(5));
|
||||
}
|
||||
});
|
||||
::std::thread::sleep(Duration::from_secs(5));
|
||||
}
|
||||
});
|
||||
|
||||
if let Err(e) = service.client().take_snapshot(writer, block_at, &*progress) {
|
||||
let _ = ::std::fs::remove_file(&file_path);
|
||||
return Err(format!("Encountered fatal error while creating snapshot: {}", e));
|
||||
}
|
||||
if let Err(e) = service.client().take_snapshot(writer, block_at, &*progress) {
|
||||
let _ = ::std::fs::remove_file(&file_path);
|
||||
return Err(format!(
|
||||
"Encountered fatal error while creating snapshot: {}",
|
||||
e
|
||||
));
|
||||
}
|
||||
|
||||
info!("snapshot creation complete");
|
||||
info!("snapshot creation complete");
|
||||
|
||||
assert!(progress.done());
|
||||
informant_handle.join().map_err(|_| "failed to join logger thread")?;
|
||||
assert!(progress.done());
|
||||
informant_handle
|
||||
.join()
|
||||
.map_err(|_| "failed to join logger thread")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute this snapshot command.
|
||||
pub fn execute(cmd: SnapshotCommand) -> Result<String, String> {
|
||||
match cmd.kind {
|
||||
Kind::Take => cmd.take_snapshot()?,
|
||||
Kind::Restore => cmd.restore()?,
|
||||
}
|
||||
match cmd.kind {
|
||||
Kind::Take => cmd.take_snapshot()?,
|
||||
Kind::Restore => cmd.restore()?,
|
||||
}
|
||||
|
||||
Ok(String::new())
|
||||
Ok(String::new())
|
||||
}
|
||||
|
||||
@@ -16,202 +16,231 @@
|
||||
|
||||
//! Parity upgrade logic
|
||||
|
||||
use semver::{Version, SemVerError};
|
||||
use std::collections::*;
|
||||
use std::fs::{self, File, create_dir_all};
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::{PathBuf, Path};
|
||||
use dir::{DatabaseDirectories, default_data_path, home_dir};
|
||||
use dir::helpers::replace_home;
|
||||
use dir::{default_data_path, helpers::replace_home, home_dir, DatabaseDirectories};
|
||||
use journaldb::Algorithm;
|
||||
use semver::{SemVerError, Version};
|
||||
use std::{
|
||||
collections::*,
|
||||
fs::{self, create_dir_all, File},
|
||||
io,
|
||||
io::{Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
CannotCreateConfigPath(io::Error),
|
||||
CannotWriteVersionFile(io::Error),
|
||||
CannotUpdateVersionFile(io::Error),
|
||||
SemVer(SemVerError),
|
||||
CannotCreateConfigPath(io::Error),
|
||||
CannotWriteVersionFile(io::Error),
|
||||
CannotUpdateVersionFile(io::Error),
|
||||
SemVer(SemVerError),
|
||||
}
|
||||
|
||||
impl From<SemVerError> for Error {
|
||||
fn from(err: SemVerError) -> Self {
|
||||
Error::SemVer(err)
|
||||
}
|
||||
fn from(err: SemVerError) -> Self {
|
||||
Error::SemVer(err)
|
||||
}
|
||||
}
|
||||
|
||||
const CURRENT_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
struct UpgradeKey {
|
||||
pub old_version: Version,
|
||||
pub new_version: Version,
|
||||
pub old_version: Version,
|
||||
pub new_version: Version,
|
||||
}
|
||||
|
||||
type UpgradeList = HashMap<UpgradeKey, fn() -> Result<(), Error>>;
|
||||
|
||||
impl UpgradeKey {
|
||||
// given the following config exist
|
||||
// ver.lock 1.1 (`previous_version`)
|
||||
//
|
||||
// current_version 1.4 (`current_version`)
|
||||
//
|
||||
//
|
||||
//upgrades (set of `UpgradeKey`)
|
||||
// 1.0 -> 1.1 (u1)
|
||||
// 1.1 -> 1.2 (u2)
|
||||
// 1.2 -> 1.3 (u3)
|
||||
// 1.3 -> 1.4 (u4)
|
||||
// 1.4 -> 1.5 (u5)
|
||||
//
|
||||
// then the following upgrades should be applied:
|
||||
// u2, u3, u4
|
||||
fn is_applicable(&self, previous_version: &Version, current_version: &Version) -> bool {
|
||||
self.old_version >= *previous_version && self.new_version <= *current_version
|
||||
}
|
||||
// given the following config exist
|
||||
// ver.lock 1.1 (`previous_version`)
|
||||
//
|
||||
// current_version 1.4 (`current_version`)
|
||||
//
|
||||
//
|
||||
//upgrades (set of `UpgradeKey`)
|
||||
// 1.0 -> 1.1 (u1)
|
||||
// 1.1 -> 1.2 (u2)
|
||||
// 1.2 -> 1.3 (u3)
|
||||
// 1.3 -> 1.4 (u4)
|
||||
// 1.4 -> 1.5 (u5)
|
||||
//
|
||||
// then the following upgrades should be applied:
|
||||
// u2, u3, u4
|
||||
fn is_applicable(&self, previous_version: &Version, current_version: &Version) -> bool {
|
||||
self.old_version >= *previous_version && self.new_version <= *current_version
|
||||
}
|
||||
}
|
||||
|
||||
// dummy upgrade (remove when the first one is in)
|
||||
fn dummy_upgrade() -> Result<(), Error> {
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_upgrades(upgrades: &mut UpgradeList)
|
||||
{
|
||||
// dummy upgrade (remove when the first one is in)
|
||||
upgrades.insert(
|
||||
UpgradeKey { old_version: Version::new(0, 9, 0), new_version: Version::new(1, 0, 0)},
|
||||
dummy_upgrade);
|
||||
fn push_upgrades(upgrades: &mut UpgradeList) {
|
||||
// dummy upgrade (remove when the first one is in)
|
||||
upgrades.insert(
|
||||
UpgradeKey {
|
||||
old_version: Version::new(0, 9, 0),
|
||||
new_version: Version::new(1, 0, 0),
|
||||
},
|
||||
dummy_upgrade,
|
||||
);
|
||||
}
|
||||
|
||||
fn upgrade_from_version(previous_version: &Version) -> Result<usize, Error> {
|
||||
let mut upgrades = HashMap::new();
|
||||
push_upgrades(&mut upgrades);
|
||||
let mut upgrades = HashMap::new();
|
||||
push_upgrades(&mut upgrades);
|
||||
|
||||
let current_version = Version::parse(CURRENT_VERSION)?;
|
||||
let current_version = Version::parse(CURRENT_VERSION)?;
|
||||
|
||||
let mut count = 0;
|
||||
for upgrade_key in upgrades.keys() {
|
||||
if upgrade_key.is_applicable(previous_version, ¤t_version) {
|
||||
let upgrade_script = upgrades[upgrade_key];
|
||||
upgrade_script()?;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
Ok(count)
|
||||
let mut count = 0;
|
||||
for upgrade_key in upgrades.keys() {
|
||||
if upgrade_key.is_applicable(previous_version, ¤t_version) {
|
||||
let upgrade_script = upgrades[upgrade_key];
|
||||
upgrade_script()?;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
fn with_locked_version<F>(db_path: &str, script: F) -> Result<usize, Error>
|
||||
where F: Fn(&Version) -> Result<usize, Error>
|
||||
where
|
||||
F: Fn(&Version) -> Result<usize, Error>,
|
||||
{
|
||||
let mut path = PathBuf::from(db_path);
|
||||
create_dir_all(&path).map_err(Error::CannotCreateConfigPath)?;
|
||||
path.push("ver.lock");
|
||||
let mut path = PathBuf::from(db_path);
|
||||
create_dir_all(&path).map_err(Error::CannotCreateConfigPath)?;
|
||||
path.push("ver.lock");
|
||||
|
||||
let version =
|
||||
File::open(&path).ok().and_then(|ref mut file|
|
||||
{
|
||||
let mut version_string = String::new();
|
||||
file.read_to_string(&mut version_string)
|
||||
.ok()
|
||||
.and_then(|_| Version::parse(&version_string).ok())
|
||||
})
|
||||
.unwrap_or(Version::new(0, 9, 0));
|
||||
let version = File::open(&path)
|
||||
.ok()
|
||||
.and_then(|ref mut file| {
|
||||
let mut version_string = String::new();
|
||||
file.read_to_string(&mut version_string)
|
||||
.ok()
|
||||
.and_then(|_| Version::parse(&version_string).ok())
|
||||
})
|
||||
.unwrap_or(Version::new(0, 9, 0));
|
||||
|
||||
let mut lock = File::create(&path).map_err(Error::CannotWriteVersionFile)?;
|
||||
let result = script(&version);
|
||||
let mut lock = File::create(&path).map_err(Error::CannotWriteVersionFile)?;
|
||||
let result = script(&version);
|
||||
|
||||
let written_version = Version::parse(CURRENT_VERSION)?;
|
||||
lock.write_all(written_version.to_string().as_bytes()).map_err(Error::CannotUpdateVersionFile)?;
|
||||
result
|
||||
let written_version = Version::parse(CURRENT_VERSION)?;
|
||||
lock.write_all(written_version.to_string().as_bytes())
|
||||
.map_err(Error::CannotUpdateVersionFile)?;
|
||||
result
|
||||
}
|
||||
|
||||
pub fn upgrade(db_path: &str) -> Result<usize, Error> {
|
||||
with_locked_version(db_path, |ver| {
|
||||
upgrade_from_version(ver)
|
||||
})
|
||||
with_locked_version(db_path, |ver| upgrade_from_version(ver))
|
||||
}
|
||||
|
||||
fn file_exists(path: &Path) -> bool {
|
||||
match fs::metadata(&path) {
|
||||
Err(ref e) if e.kind() == io::ErrorKind::NotFound => false,
|
||||
_ => true,
|
||||
}
|
||||
match fs::metadata(&path) {
|
||||
Err(ref e) if e.kind() == io::ErrorKind::NotFound => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "accounts"))]
|
||||
pub fn upgrade_key_location(from: &PathBuf, to: &PathBuf) {
|
||||
match fs::create_dir_all(&to).and_then(|()| fs::read_dir(from)) {
|
||||
Ok(entries) => {
|
||||
let files: Vec<_> = entries.filter_map(|f| f.ok().and_then(|f| if f.file_type().ok().map_or(false, |f| f.is_file()) { f.file_name().to_str().map(|s| s.to_owned()) } else { None })).collect();
|
||||
let mut num: usize = 0;
|
||||
for name in files {
|
||||
let mut from = from.clone();
|
||||
from.push(&name);
|
||||
let mut to = to.clone();
|
||||
to.push(&name);
|
||||
if !file_exists(&to) {
|
||||
if let Err(e) = fs::rename(&from, &to) {
|
||||
debug!("Error upgrading key {:?}: {:?}", from, e);
|
||||
} else {
|
||||
num += 1;
|
||||
}
|
||||
} else {
|
||||
debug!("Skipped upgrading key {:?}", from);
|
||||
}
|
||||
}
|
||||
if num > 0 {
|
||||
info!("Moved {} keys from {} to {}", num, from.to_string_lossy(), to.to_string_lossy());
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
debug!("Error moving keys from {:?} to {:?}: {:?}", from, to, e);
|
||||
}
|
||||
}
|
||||
match fs::create_dir_all(&to).and_then(|()| fs::read_dir(from)) {
|
||||
Ok(entries) => {
|
||||
let files: Vec<_> = entries
|
||||
.filter_map(|f| {
|
||||
f.ok().and_then(|f| {
|
||||
if f.file_type().ok().map_or(false, |f| f.is_file()) {
|
||||
f.file_name().to_str().map(|s| s.to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let mut num: usize = 0;
|
||||
for name in files {
|
||||
let mut from = from.clone();
|
||||
from.push(&name);
|
||||
let mut to = to.clone();
|
||||
to.push(&name);
|
||||
if !file_exists(&to) {
|
||||
if let Err(e) = fs::rename(&from, &to) {
|
||||
debug!("Error upgrading key {:?}: {:?}", from, e);
|
||||
} else {
|
||||
num += 1;
|
||||
}
|
||||
} else {
|
||||
debug!("Skipped upgrading key {:?}", from);
|
||||
}
|
||||
}
|
||||
if num > 0 {
|
||||
info!(
|
||||
"Moved {} keys from {} to {}",
|
||||
num,
|
||||
from.to_string_lossy(),
|
||||
to.to_string_lossy()
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Error moving keys from {:?} to {:?}: {:?}", from, to, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn upgrade_dir_location(source: &PathBuf, dest: &PathBuf) {
|
||||
if file_exists(&source) {
|
||||
if !file_exists(&dest) {
|
||||
let mut parent = dest.clone();
|
||||
parent.pop();
|
||||
if let Err(e) = fs::create_dir_all(&parent).and_then(|()| fs::rename(&source, &dest)) {
|
||||
debug!("Skipped path {:?} -> {:?} :{:?}", source, dest, e);
|
||||
} else {
|
||||
info!("Moved {} to {}", source.to_string_lossy(), dest.to_string_lossy());
|
||||
}
|
||||
} else {
|
||||
debug!("Skipped upgrading directory {:?}, Destination already exists at {:?}", source, dest);
|
||||
}
|
||||
}
|
||||
if file_exists(&source) {
|
||||
if !file_exists(&dest) {
|
||||
let mut parent = dest.clone();
|
||||
parent.pop();
|
||||
if let Err(e) = fs::create_dir_all(&parent).and_then(|()| fs::rename(&source, &dest)) {
|
||||
debug!("Skipped path {:?} -> {:?} :{:?}", source, dest, e);
|
||||
} else {
|
||||
info!(
|
||||
"Moved {} to {}",
|
||||
source.to_string_lossy(),
|
||||
dest.to_string_lossy()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
debug!(
|
||||
"Skipped upgrading directory {:?}, Destination already exists at {:?}",
|
||||
source, dest
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn upgrade_user_defaults(dirs: &DatabaseDirectories) {
|
||||
let source = dirs.legacy_user_defaults_path();
|
||||
let dest = dirs.user_defaults_path();
|
||||
if file_exists(&source) {
|
||||
if !file_exists(&dest) {
|
||||
if let Err(e) = fs::rename(&source, &dest) {
|
||||
debug!("Skipped upgrading user defaults {:?}:{:?}", dest, e);
|
||||
}
|
||||
} else {
|
||||
debug!("Skipped upgrading user defaults {:?}, File exists at {:?}", source, dest);
|
||||
}
|
||||
}
|
||||
let source = dirs.legacy_user_defaults_path();
|
||||
let dest = dirs.user_defaults_path();
|
||||
if file_exists(&source) {
|
||||
if !file_exists(&dest) {
|
||||
if let Err(e) = fs::rename(&source, &dest) {
|
||||
debug!("Skipped upgrading user defaults {:?}:{:?}", dest, e);
|
||||
}
|
||||
} else {
|
||||
debug!(
|
||||
"Skipped upgrading user defaults {:?}, File exists at {:?}",
|
||||
source, dest
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upgrade_data_paths(base_path: &str, dirs: &DatabaseDirectories, pruning: Algorithm) {
|
||||
if home_dir().is_none() {
|
||||
return;
|
||||
}
|
||||
if home_dir().is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let legacy_root_path = replace_home("", "$HOME/.parity");
|
||||
let default_path = default_data_path();
|
||||
if legacy_root_path != base_path && base_path == default_path {
|
||||
upgrade_dir_location(&PathBuf::from(legacy_root_path), &PathBuf::from(&base_path));
|
||||
}
|
||||
upgrade_dir_location(&dirs.legacy_version_path(pruning), &dirs.db_path(pruning));
|
||||
upgrade_dir_location(&dirs.legacy_snapshot_path(), &dirs.snapshot_path());
|
||||
upgrade_dir_location(&dirs.legacy_network_path(), &dirs.network_path());
|
||||
upgrade_user_defaults(&dirs);
|
||||
let legacy_root_path = replace_home("", "$HOME/.parity");
|
||||
let default_path = default_data_path();
|
||||
if legacy_root_path != base_path && base_path == default_path {
|
||||
upgrade_dir_location(&PathBuf::from(legacy_root_path), &PathBuf::from(&base_path));
|
||||
}
|
||||
upgrade_dir_location(&dirs.legacy_version_path(pruning), &dirs.db_path(pruning));
|
||||
upgrade_dir_location(&dirs.legacy_snapshot_path(), &dirs.snapshot_path());
|
||||
upgrade_dir_location(&dirs.legacy_network_path(), &dirs.network_path());
|
||||
upgrade_user_defaults(&dirs);
|
||||
}
|
||||
|
||||
@@ -14,161 +14,175 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_json::de::from_reader;
|
||||
use serde_json::ser::to_string;
|
||||
use ethcore::client::Mode as ClientMode;
|
||||
use journaldb::Algorithm;
|
||||
use ethcore::client::{Mode as ClientMode};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_json::{de::from_reader, ser::to_string};
|
||||
use std::{fs::File, io::Write, path::Path, time::Duration};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Seconds(Duration);
|
||||
|
||||
impl Seconds {
|
||||
pub fn value(&self) -> u64 {
|
||||
self.0.as_secs()
|
||||
}
|
||||
pub fn value(&self) -> u64 {
|
||||
self.0.as_secs()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Seconds {
|
||||
fn from(s: u64) -> Seconds {
|
||||
Seconds(Duration::from_secs(s))
|
||||
}
|
||||
fn from(s: u64) -> Seconds {
|
||||
Seconds(Duration::from_secs(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Duration> for Seconds {
|
||||
fn from(d: Duration) -> Seconds {
|
||||
Seconds(d)
|
||||
}
|
||||
fn from(d: Duration) -> Seconds {
|
||||
Seconds(d)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Duration> for Seconds {
|
||||
fn into(self) -> Duration {
|
||||
self.0
|
||||
}
|
||||
fn into(self) -> Duration {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Seconds {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_u64(self.value())
|
||||
}
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_u64(self.value())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Seconds {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let secs = u64::deserialize(deserializer)?;
|
||||
Ok(Seconds::from(secs))
|
||||
}
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let secs = u64::deserialize(deserializer)?;
|
||||
Ok(Seconds::from(secs))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase", tag = "mode")]
|
||||
pub enum Mode {
|
||||
Active,
|
||||
Passive {
|
||||
#[serde(rename = "mode.timeout")]
|
||||
timeout: Seconds,
|
||||
#[serde(rename = "mode.alarm")]
|
||||
alarm: Seconds,
|
||||
},
|
||||
Dark {
|
||||
#[serde(rename = "mode.timeout")]
|
||||
timeout: Seconds,
|
||||
},
|
||||
Offline,
|
||||
Active,
|
||||
Passive {
|
||||
#[serde(rename = "mode.timeout")]
|
||||
timeout: Seconds,
|
||||
#[serde(rename = "mode.alarm")]
|
||||
alarm: Seconds,
|
||||
},
|
||||
Dark {
|
||||
#[serde(rename = "mode.timeout")]
|
||||
timeout: Seconds,
|
||||
},
|
||||
Offline,
|
||||
}
|
||||
|
||||
impl Into<ClientMode> for Mode {
|
||||
fn into(self) -> ClientMode {
|
||||
match self {
|
||||
Mode::Active => ClientMode::Active,
|
||||
Mode::Passive { timeout, alarm } => ClientMode::Passive(timeout.into(), alarm.into()),
|
||||
Mode::Dark { timeout } => ClientMode::Dark(timeout.into()),
|
||||
Mode::Offline => ClientMode::Off,
|
||||
}
|
||||
}
|
||||
fn into(self) -> ClientMode {
|
||||
match self {
|
||||
Mode::Active => ClientMode::Active,
|
||||
Mode::Passive { timeout, alarm } => ClientMode::Passive(timeout.into(), alarm.into()),
|
||||
Mode::Dark { timeout } => ClientMode::Dark(timeout.into()),
|
||||
Mode::Offline => ClientMode::Off,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ClientMode> for Mode {
|
||||
fn from(mode: ClientMode) -> Mode {
|
||||
match mode {
|
||||
ClientMode::Active => Mode::Active,
|
||||
ClientMode::Passive(timeout, alarm) => Mode::Passive { timeout: timeout.into(), alarm: alarm.into() },
|
||||
ClientMode::Dark(timeout) => Mode::Dark { timeout: timeout.into() },
|
||||
ClientMode::Off => Mode::Offline,
|
||||
}
|
||||
}
|
||||
fn from(mode: ClientMode) -> Mode {
|
||||
match mode {
|
||||
ClientMode::Active => Mode::Active,
|
||||
ClientMode::Passive(timeout, alarm) => Mode::Passive {
|
||||
timeout: timeout.into(),
|
||||
alarm: alarm.into(),
|
||||
},
|
||||
ClientMode::Dark(timeout) => Mode::Dark {
|
||||
timeout: timeout.into(),
|
||||
},
|
||||
ClientMode::Off => Mode::Offline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct UserDefaults {
|
||||
pub is_first_launch: bool,
|
||||
#[serde(with = "algorithm_serde")]
|
||||
pub pruning: Algorithm,
|
||||
pub tracing: bool,
|
||||
pub fat_db: bool,
|
||||
#[serde(flatten)]
|
||||
mode: Mode,
|
||||
pub is_first_launch: bool,
|
||||
#[serde(with = "algorithm_serde")]
|
||||
pub pruning: Algorithm,
|
||||
pub tracing: bool,
|
||||
pub fat_db: bool,
|
||||
#[serde(flatten)]
|
||||
mode: Mode,
|
||||
}
|
||||
|
||||
impl UserDefaults {
|
||||
pub fn mode(&self) -> ClientMode {
|
||||
self.mode.clone().into()
|
||||
}
|
||||
pub fn mode(&self) -> ClientMode {
|
||||
self.mode.clone().into()
|
||||
}
|
||||
|
||||
pub fn set_mode(&mut self, mode: ClientMode) {
|
||||
self.mode = mode.into();
|
||||
}
|
||||
pub fn set_mode(&mut self, mode: ClientMode) {
|
||||
self.mode = mode.into();
|
||||
}
|
||||
}
|
||||
|
||||
mod algorithm_serde {
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde::de::Error;
|
||||
use journaldb::Algorithm;
|
||||
use journaldb::Algorithm;
|
||||
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
pub fn serialize<S>(algorithm: &Algorithm, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer {
|
||||
algorithm.as_str().serialize(serializer)
|
||||
}
|
||||
pub fn serialize<S>(algorithm: &Algorithm, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
algorithm.as_str().serialize(serializer)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Algorithm, D::Error>
|
||||
where D: Deserializer<'de> {
|
||||
let pruning = String::deserialize(deserializer)?;
|
||||
pruning.parse().map_err(|_| Error::custom("invalid pruning method"))
|
||||
}
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Algorithm, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let pruning = String::deserialize(deserializer)?;
|
||||
pruning
|
||||
.parse()
|
||||
.map_err(|_| Error::custom("invalid pruning method"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for UserDefaults {
|
||||
fn default() -> Self {
|
||||
UserDefaults {
|
||||
is_first_launch: true,
|
||||
pruning: Algorithm::OverlayRecent,
|
||||
tracing: false,
|
||||
fat_db: false,
|
||||
mode: Mode::Active,
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
UserDefaults {
|
||||
is_first_launch: true,
|
||||
pruning: Algorithm::OverlayRecent,
|
||||
tracing: false,
|
||||
fat_db: false,
|
||||
mode: Mode::Active,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserDefaults {
|
||||
pub fn load<P>(path: P) -> Result<Self, String> where P: AsRef<Path> {
|
||||
match File::open(path) {
|
||||
Ok(file) => match from_reader(file) {
|
||||
Ok(defaults) => Ok(defaults),
|
||||
Err(e) => {
|
||||
warn!("Error loading user defaults file: {:?}", e);
|
||||
Ok(UserDefaults::default())
|
||||
},
|
||||
},
|
||||
_ => Ok(UserDefaults::default()),
|
||||
}
|
||||
}
|
||||
pub fn load<P>(path: P) -> Result<Self, String>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
match File::open(path) {
|
||||
Ok(file) => match from_reader(file) {
|
||||
Ok(defaults) => Ok(defaults),
|
||||
Err(e) => {
|
||||
warn!("Error loading user defaults file: {:?}", e);
|
||||
Ok(UserDefaults::default())
|
||||
}
|
||||
},
|
||||
_ => Ok(UserDefaults::default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save<P>(&self, path: P) -> Result<(), String> where P: AsRef<Path> {
|
||||
let mut file: File = File::create(path).map_err(|_| "Cannot create user defaults file".to_owned())?;
|
||||
file.write_all(to_string(&self).unwrap().as_bytes()).map_err(|_| "Failed to save user defaults".to_owned())
|
||||
}
|
||||
pub fn save<P>(&self, path: P) -> Result<(), String>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut file: File =
|
||||
File::create(path).map_err(|_| "Cannot create user defaults file".to_owned())?;
|
||||
file.write_all(to_string(&self).unwrap().as_bytes())
|
||||
.map_err(|_| "Failed to save user defaults".to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,101 +14,111 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::io;
|
||||
use std::{io, sync::Arc};
|
||||
|
||||
use sync::{AttachedProtocol, ManageNetwork};
|
||||
use parity_rpc::Metadata;
|
||||
use parity_whisper::message::Message;
|
||||
use parity_whisper::net::{self as whisper_net, Network as WhisperNetwork};
|
||||
use parity_whisper::rpc::{WhisperClient, PoolHandle, FilterManager};
|
||||
use parity_whisper::{
|
||||
message::Message,
|
||||
net::{self as whisper_net, Network as WhisperNetwork},
|
||||
rpc::{FilterManager, PoolHandle, WhisperClient},
|
||||
};
|
||||
use sync::{AttachedProtocol, ManageNetwork};
|
||||
|
||||
/// Whisper config.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Config {
|
||||
pub enabled: bool,
|
||||
pub target_message_pool_size: usize,
|
||||
pub enabled: bool,
|
||||
pub target_message_pool_size: usize,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
enabled: false,
|
||||
target_message_pool_size: 10 * 1024 * 1024,
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
enabled: false,
|
||||
target_message_pool_size: 10 * 1024 * 1024,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Standard pool handle.
|
||||
pub struct NetPoolHandle {
|
||||
/// Pool handle.
|
||||
handle: Arc<WhisperNetwork<Arc<FilterManager>>>,
|
||||
/// Network manager.
|
||||
net: Arc<ManageNetwork>,
|
||||
/// Pool handle.
|
||||
handle: Arc<WhisperNetwork<Arc<FilterManager>>>,
|
||||
/// Network manager.
|
||||
net: Arc<ManageNetwork>,
|
||||
}
|
||||
|
||||
impl PoolHandle for NetPoolHandle {
|
||||
fn relay(&self, message: Message) -> bool {
|
||||
let mut res = false;
|
||||
let mut message = Some(message);
|
||||
self.net.with_proto_context(whisper_net::PROTOCOL_ID, &mut |ctx| {
|
||||
if let Some(message) = message.take() {
|
||||
res = self.handle.post_message(message, ctx);
|
||||
}
|
||||
});
|
||||
res
|
||||
}
|
||||
fn relay(&self, message: Message) -> bool {
|
||||
let mut res = false;
|
||||
let mut message = Some(message);
|
||||
self.net
|
||||
.with_proto_context(whisper_net::PROTOCOL_ID, &mut |ctx| {
|
||||
if let Some(message) = message.take() {
|
||||
res = self.handle.post_message(message, ctx);
|
||||
}
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
fn pool_status(&self) -> whisper_net::PoolStatus {
|
||||
self.handle.pool_status()
|
||||
}
|
||||
fn pool_status(&self) -> whisper_net::PoolStatus {
|
||||
self.handle.pool_status()
|
||||
}
|
||||
}
|
||||
|
||||
/// Factory for standard whisper RPC.
|
||||
pub struct RpcFactory {
|
||||
net: Arc<WhisperNetwork<Arc<FilterManager>>>,
|
||||
manager: Arc<FilterManager>,
|
||||
net: Arc<WhisperNetwork<Arc<FilterManager>>>,
|
||||
manager: Arc<FilterManager>,
|
||||
}
|
||||
|
||||
impl RpcFactory {
|
||||
pub fn make_handler(&self, net: Arc<ManageNetwork>) -> WhisperClient<NetPoolHandle, Metadata> {
|
||||
let handle = NetPoolHandle { handle: self.net.clone(), net: net };
|
||||
WhisperClient::new(handle, self.manager.clone())
|
||||
}
|
||||
pub fn make_handler(&self, net: Arc<ManageNetwork>) -> WhisperClient<NetPoolHandle, Metadata> {
|
||||
let handle = NetPoolHandle {
|
||||
handle: self.net.clone(),
|
||||
net: net,
|
||||
};
|
||||
WhisperClient::new(handle, self.manager.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets up whisper protocol and RPC handler.
|
||||
///
|
||||
/// Will target the given pool size.
|
||||
#[cfg(not(feature = "ipc"))]
|
||||
pub fn setup(target_pool_size: usize, protos: &mut Vec<AttachedProtocol>)
|
||||
-> io::Result<Option<RpcFactory>>
|
||||
{
|
||||
let manager = Arc::new(FilterManager::new()?);
|
||||
let net = Arc::new(WhisperNetwork::new(target_pool_size, manager.clone()));
|
||||
pub fn setup(
|
||||
target_pool_size: usize,
|
||||
protos: &mut Vec<AttachedProtocol>,
|
||||
) -> io::Result<Option<RpcFactory>> {
|
||||
let manager = Arc::new(FilterManager::new()?);
|
||||
let net = Arc::new(WhisperNetwork::new(target_pool_size, manager.clone()));
|
||||
|
||||
protos.push(AttachedProtocol {
|
||||
handler: net.clone() as Arc<_>,
|
||||
versions: whisper_net::SUPPORTED_VERSIONS,
|
||||
protocol_id: whisper_net::PROTOCOL_ID,
|
||||
});
|
||||
protos.push(AttachedProtocol {
|
||||
handler: net.clone() as Arc<_>,
|
||||
versions: whisper_net::SUPPORTED_VERSIONS,
|
||||
protocol_id: whisper_net::PROTOCOL_ID,
|
||||
});
|
||||
|
||||
// parity-only extensions to whisper.
|
||||
protos.push(AttachedProtocol {
|
||||
handler: Arc::new(whisper_net::ParityExtensions),
|
||||
versions: whisper_net::SUPPORTED_VERSIONS,
|
||||
protocol_id: whisper_net::PARITY_PROTOCOL_ID,
|
||||
});
|
||||
// parity-only extensions to whisper.
|
||||
protos.push(AttachedProtocol {
|
||||
handler: Arc::new(whisper_net::ParityExtensions),
|
||||
versions: whisper_net::SUPPORTED_VERSIONS,
|
||||
protocol_id: whisper_net::PARITY_PROTOCOL_ID,
|
||||
});
|
||||
|
||||
let factory = RpcFactory { net: net, manager: manager };
|
||||
let factory = RpcFactory {
|
||||
net: net,
|
||||
manager: manager,
|
||||
};
|
||||
|
||||
Ok(Some(factory))
|
||||
Ok(Some(factory))
|
||||
}
|
||||
|
||||
// TODO: make it possible to attach generic protocols in IPC.
|
||||
#[cfg(feature = "ipc")]
|
||||
pub fn setup(_target_pool_size: usize, _protos: &mut Vec<AttachedProtocol>)
|
||||
-> io::Result<Option<RpcFactory>>
|
||||
{
|
||||
Ok(None)
|
||||
pub fn setup(
|
||||
_target_pool_size: usize,
|
||||
_protos: &mut Vec<AttachedProtocol>,
|
||||
) -> io::Result<Option<RpcFactory>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user