|
|
|
|
@@ -29,6 +29,7 @@ use ethstore::{SimpleSecretStore, SecretStore, Error as SSError, EthStore, EthMu
|
|
|
|
|
use ethstore::dir::MemoryDirectory;
|
|
|
|
|
use ethstore::ethkey::{Address, Message, Public, Secret, Random, Generator};
|
|
|
|
|
use ethjson::misc::AccountMeta;
|
|
|
|
|
use hardware_wallet::{Error as HardwareError, HardwareWalletManager, KeyPath};
|
|
|
|
|
pub use ethstore::ethkey::Signature;
|
|
|
|
|
|
|
|
|
|
/// Type of unlock.
|
|
|
|
|
@@ -55,6 +56,10 @@ struct AccountData {
|
|
|
|
|
pub enum SignError {
|
|
|
|
|
/// Account is not unlocked
|
|
|
|
|
NotUnlocked,
|
|
|
|
|
/// Account does not exist.
|
|
|
|
|
NotFound,
|
|
|
|
|
/// Low-level hardware device error.
|
|
|
|
|
Hardware(HardwareError),
|
|
|
|
|
/// Low-level error from store
|
|
|
|
|
SStore(SSError)
|
|
|
|
|
}
|
|
|
|
|
@@ -63,11 +68,19 @@ impl fmt::Display for SignError {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
|
|
|
match *self {
|
|
|
|
|
SignError::NotUnlocked => write!(f, "Account is locked"),
|
|
|
|
|
SignError::NotFound => write!(f, "Account does not exist"),
|
|
|
|
|
SignError::Hardware(ref e) => write!(f, "{}", e),
|
|
|
|
|
SignError::SStore(ref e) => write!(f, "{}", e),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<HardwareError> for SignError {
|
|
|
|
|
fn from(e: HardwareError) -> Self {
|
|
|
|
|
SignError::Hardware(e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<SSError> for SignError {
|
|
|
|
|
fn from(e: SSError) -> Self {
|
|
|
|
|
SignError::SStore(e)
|
|
|
|
|
@@ -107,17 +120,47 @@ pub struct AccountProvider {
|
|
|
|
|
sstore: Box<SecretStore>,
|
|
|
|
|
/// Accounts unlocked with rolling tokens
|
|
|
|
|
transient_sstore: EthMultiStore,
|
|
|
|
|
/// Accounts in hardware wallets.
|
|
|
|
|
hardware_store: Option<HardwareWalletManager>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Account management settings.
|
|
|
|
|
pub struct AccountProviderSettings {
|
|
|
|
|
/// Enable hardware wallet support.
|
|
|
|
|
pub enable_hardware_wallets: bool,
|
|
|
|
|
/// Use the classic chain key on the hardware wallet.
|
|
|
|
|
pub hardware_wallet_classic_key: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for AccountProviderSettings {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
AccountProviderSettings {
|
|
|
|
|
enable_hardware_wallets: false,
|
|
|
|
|
hardware_wallet_classic_key: false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AccountProvider {
|
|
|
|
|
/// Creates new account provider.
|
|
|
|
|
pub fn new(sstore: Box<SecretStore>) -> Self {
|
|
|
|
|
pub fn new(sstore: Box<SecretStore>, settings: AccountProviderSettings) -> Self {
|
|
|
|
|
let mut hardware_store = None;
|
|
|
|
|
if settings.enable_hardware_wallets {
|
|
|
|
|
match HardwareWalletManager::new() {
|
|
|
|
|
Ok(manager) => {
|
|
|
|
|
manager.set_key_path(if settings.hardware_wallet_classic_key { KeyPath::EthereumClassic } else { KeyPath::Ethereum });
|
|
|
|
|
hardware_store = Some(manager)
|
|
|
|
|
},
|
|
|
|
|
Err(e) => warn!("Error initializing hardware wallets: {}", e),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
AccountProvider {
|
|
|
|
|
unlocked: RwLock::new(HashMap::new()),
|
|
|
|
|
address_book: RwLock::new(AddressBook::new(&sstore.local_path())),
|
|
|
|
|
dapps_settings: RwLock::new(DappsSettingsStore::new(&sstore.local_path())),
|
|
|
|
|
sstore: sstore,
|
|
|
|
|
transient_sstore: transient_sstore(),
|
|
|
|
|
hardware_store: hardware_store,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -129,6 +172,7 @@ impl AccountProvider {
|
|
|
|
|
dapps_settings: RwLock::new(DappsSettingsStore::transient()),
|
|
|
|
|
sstore: Box::new(EthStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")),
|
|
|
|
|
transient_sstore: transient_sstore(),
|
|
|
|
|
hardware_store: None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -176,6 +220,12 @@ impl AccountProvider {
|
|
|
|
|
Ok(accounts.into_iter().map(|a| a.address).collect())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns addresses of hardware accounts.
|
|
|
|
|
pub fn hardware_accounts(&self) -> Result<Vec<Address>, Error> {
|
|
|
|
|
let accounts = self.hardware_store.as_ref().map_or(Vec::new(), |h| h.list_wallets());
|
|
|
|
|
Ok(accounts.into_iter().map(|a| a.address).collect())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Sets a whitelist of accounts exposed for unknown dapps.
|
|
|
|
|
/// `None` means that all accounts will be visible.
|
|
|
|
|
pub fn set_new_dapps_whitelist(&self, accounts: Option<Vec<Address>>) -> Result<(), Error> {
|
|
|
|
|
@@ -271,21 +321,43 @@ impl AccountProvider {
|
|
|
|
|
|
|
|
|
|
/// Returns each account along with name and meta.
|
|
|
|
|
pub fn accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
|
|
|
|
|
let r: HashMap<Address, AccountMeta> = self.sstore.accounts()?
|
|
|
|
|
let r = self.sstore.accounts()?
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|a| (a.address.clone(), self.account_meta(a.address).ok().unwrap_or_default()))
|
|
|
|
|
.collect();
|
|
|
|
|
Ok(r)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns each hardware account along with name and meta.
|
|
|
|
|
pub fn hardware_accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
|
|
|
|
|
let r = self.hardware_accounts()?
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|address| (address.clone(), self.account_meta(address).ok().unwrap_or_default()))
|
|
|
|
|
.collect();
|
|
|
|
|
Ok(r)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns each hardware account along with name and meta.
|
|
|
|
|
pub fn is_hardware_address(&self, address: Address) -> bool {
|
|
|
|
|
self.hardware_store.as_ref().and_then(|s| s.wallet_info(&address)).is_some()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns each account along with name and meta.
|
|
|
|
|
pub fn account_meta(&self, address: Address) -> Result<AccountMeta, Error> {
|
|
|
|
|
let account = self.sstore.account_ref(&address)?;
|
|
|
|
|
Ok(AccountMeta {
|
|
|
|
|
name: self.sstore.name(&account)?,
|
|
|
|
|
meta: self.sstore.meta(&account)?,
|
|
|
|
|
uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid
|
|
|
|
|
})
|
|
|
|
|
if let Some(info) = self.hardware_store.as_ref().and_then(|s| s.wallet_info(&address)) {
|
|
|
|
|
Ok(AccountMeta {
|
|
|
|
|
name: info.name,
|
|
|
|
|
meta: info.manufacturer,
|
|
|
|
|
uuid: None,
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
let account = self.sstore.account_ref(&address)?;
|
|
|
|
|
Ok(AccountMeta {
|
|
|
|
|
name: self.sstore.name(&account)?,
|
|
|
|
|
meta: self.sstore.meta(&account)?,
|
|
|
|
|
uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns each account along with name and meta.
|
|
|
|
|
@@ -505,6 +577,15 @@ impl AccountProvider {
|
|
|
|
|
self.sstore.set_vault_meta(name, meta)
|
|
|
|
|
.map_err(Into::into)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Sign transaction with hardware wallet.
|
|
|
|
|
pub fn sign_with_hardware(&self, address: Address, transaction: &[u8]) -> Result<Signature, SignError> {
|
|
|
|
|
match self.hardware_store.as_ref().map(|s| s.sign_transaction(&address, transaction)) {
|
|
|
|
|
None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound),
|
|
|
|
|
Some(Err(e)) => Err(From::from(e)),
|
|
|
|
|
Some(Ok(s)) => Ok(s),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|