openethereum/parity/account_utils.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

324 lines
11 KiB
Rust
Raw Normal View History

// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use dir::Directories;
use ethereum_types::Address;
use ethkey::Password;
use params::{AccountsConfig, SpecType};
#[cfg(not(feature = "accounts"))]
mod accounts {
use super::*;
2020-08-05 06:08:03 +02:00
/// Dummy AccountProvider
pub struct AccountProvider;
2020-08-05 06:08:03 +02:00
impl ::ethcore::miner::LocalAccounts for AccountProvider {
fn is_local(&self, _address: &Address) -> bool {
false
2020-08-05 06:08:03 +02:00
}
}
2020-08-05 06:08:03 +02:00
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)
}
2020-08-05 06:08:03 +02:00
pub fn miner_local_accounts(_: Arc<AccountProvider>) -> AccountProvider {
AccountProvider
}
2020-08-05 06:08:03 +02:00
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)
}
2020-08-05 06:08:03 +02:00
pub fn private_tx_signer(
_account_provider: Arc<AccountProvider>,
_passwords: &[Password],
2020-07-29 10:36:15 +02:00
) -> Result<Arc<dyn ethcore_private_tx::Signer>, String> {
Ok(Arc::new(::ethcore_private_tx::DummySigner))
}
2020-08-05 06:08:03 +02:00
pub fn accounts_list(
_account_provider: Arc<AccountProvider>,
2020-07-29 10:36:15 +02:00
) -> Arc<dyn Fn() -> Vec<Address> + Send + Sync> {
Arc::new(|| vec![])
}
}
#[cfg(feature = "accounts")]
mod accounts {
use super::*;
use upgrade::upgrade_key_location;
2020-08-05 06:08:03 +02:00
pub use accounts::AccountProvider;
2020-08-05 06:08:03 +02:00
/// 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.";
2020-08-05 06:08:03 +02:00
/// 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};
2020-08-05 06:08:03 +02:00
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 {
unlock_keep_secret: cfg.enable_fast_unlock,
blacklisted_accounts: match *spec {
v2.5.10 stable (#11239) * ropsten #6631425 foundation #8798209 (#11201) * [stable] builtin, istanbul and mordor testnet backports (#11234) * ethcore-builtin (#10850) * [builtin]: support `multiple prices and activations` in chain spec (#11039) * [chain specs]: activate `Istanbul` on mainnet (#11228) * ethcore/res: add mordor testnet configuration (#11200) * Update list of bootnodes for xDai chain (#11236) * ethcore: remove `test-helper feat` from build (#11047) * Secret store: fix Instant::now() related race in net_keep_alive (#11155) (#11159) * [stable]: backport #10691 and #10683 (#11143) * Fix compiler warning (that will become an error) (#10683) * Refactor Clique stepping (#10691) * Add Constantinople eips to the dev (instant_seal) config (#10809) * Add cargo-remote dir to .gitignore (?) * Insert explicit warning into the panic hook (#11225) * Fix docker centos build (#11226) * Update MIX bootnodes. (#11203) * Use provided usd-per-eth value if an endpoint is specified (#11209) * Add new line after writing block to hex file. (#10984) * Type annotation for next_key() matching of json filter options (#11192) (but no `FilterOption` in 2.5 so…) * Upgrade jsonrpc to latest (#11206) * [CI] check evmbin build (#11096) * Correct EIP-712 encoding (#11092) * [client]: Fix for incorrectly dropped consensus messages (#11086) * Fix block detail updating (#11015) * Switching sccache from local to Redis (#10971) * Made ecrecover implementation trait public (#11188) * [dependencies]: jsonrpc `14.0.1` (#11183) * [receipt]: add `sender` & `receiver` to `RichReceipts` (#11179) * [ethcore/builtin]: do not panic in blake2pricer on short input (#11180) * util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175) * ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172) * Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127) * Cleanup stratum a bit (#11161) * Upgrade to jsonrpc v14 (#11151) * SecretStore: expose restore_key_public in HTTP API (#10241)
2019-11-11 21:57:38 +01:00
SpecType::Morden
| SpecType::Mordor
| SpecType::Ropsten
| SpecType::Kovan
| SpecType::Goerli
| SpecType::Kotti
| SpecType::Sokol
| SpecType::Dev => vec![],
_ => vec!["00a329c0648769a73afac7f9381e08fb43dbea72".into()],
},
};
2020-08-05 06:08:03 +02:00
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);
2020-08-05 06:08:03 +02:00
// Add development account if running dev chain:
if let SpecType::Dev = *spec {
insert_dev_account(&account_provider);
}
2020-08-05 06:08:03 +02:00
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)
));
}
2020-08-05 06:08:03 +02:00
// 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
));
}
2020-08-05 06:08:03 +02:00
if !passwords.iter().any(|p| {
account_provider
.unlock_account_permanently(a, (*p).clone())
.is_ok()
2020-08-05 06:08:03 +02:00
}) {
return Err(format!(
"No valid password to unlock account {}. {}",
a, VERIFY_PASSWORD_HINT
));
}
2020-08-05 06:08:03 +02:00
}
Ok(account_provider)
}
2020-08-05 06:08:03 +02:00
pub struct LocalAccounts(Arc<AccountProvider>);
impl ::ethcore::miner::LocalAccounts for LocalAccounts {
fn is_local(&self, address: &Address) -> bool {
self.0.has_account(*address)
2020-08-05 06:08:03 +02:00
}
}
2020-08-05 06:08:03 +02:00
pub fn miner_local_accounts(account_provider: Arc<AccountProvider>) -> LocalAccounts {
LocalAccounts(account_provider)
}
2020-08-05 06:08:03 +02:00
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;
2020-08-05 06:08:03 +02:00
// 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)
));
}
2020-08-05 06:08:03 +02:00
// 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
));
}
2020-08-05 06:08:03 +02:00
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)));
2020-08-05 06:08:03 +02:00
}
}
if author.is_none() {
return Err(format!(
"No valid password for the consensus signer {}. {}",
engine_signer, VERIFY_PASSWORD_HINT
));
}
2020-08-05 06:08:03 +02:00
Ok(author)
}
2020-08-05 06:08:03 +02:00
mod private_tx {
use super::*;
use ethcore_private_tx::Error;
use ethkey::{Message, Signature};
2020-08-05 06:08:03 +02:00
pub struct AccountSigner {
pub accounts: Arc<AccountProvider>,
pub passwords: Vec<Password>,
}
2020-08-05 06:08:03 +02:00
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())?)
}
2020-08-05 06:08:03 +02:00
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())?)
}
}
2020-08-05 06:08:03 +02:00
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());
2020-08-05 06:08:03 +02:00
}
}
None
2020-08-05 06:08:03 +02:00
}
}
}
2020-08-05 06:08:03 +02:00
pub fn private_tx_signer(
accounts: Arc<AccountProvider>,
passwords: &[Password],
2020-07-29 10:36:15 +02:00
) -> Result<Arc<dyn crate::ethcore_private_tx::Signer>, String> {
Ok(Arc::new(self::private_tx::AccountSigner {
accounts,
passwords: passwords.to_vec(),
}))
}
2020-08-05 06:08:03 +02:00
pub fn accounts_list(
account_provider: Arc<AccountProvider>,
2020-07-29 10:36:15 +02:00
) -> Arc<dyn Fn() -> Vec<Address> + Send + Sync> {
Arc::new(move || account_provider.accounts().unwrap_or_default())
}
2020-08-05 06:08:03 +02:00
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<_, _>>()),
2020-08-05 06:08:03 +02:00
)
.expect("Serialization of hashmap does not fail."),
);
}
}
2020-08-05 06:08:03 +02:00
}
}
// 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::{
accounts_list, miner_author, miner_local_accounts, prepare_account_provider, private_tx_signer,
AccountProvider,
};