EthMultiStore
This commit is contained in:
parent
79e79473bf
commit
ad440a12bd
@ -163,12 +163,19 @@ impl AddressBook {
|
||||
}
|
||||
}
|
||||
|
||||
fn transient_sstore() -> Box<SecretStore> {
|
||||
Box::new(EthStore::open(Box::new(NullDir::default())).expect("NullDir load always succeeds; qed")))
|
||||
}
|
||||
|
||||
/// Account management.
|
||||
/// Responsible for unlocking accounts.
|
||||
pub struct AccountProvider {
|
||||
unlocked: Mutex<HashMap<Address, AccountData>>,
|
||||
sstore: Box<SecretStore>,
|
||||
address_book: Mutex<AddressBook>,
|
||||
unlocked: Mutex<HashMap<Address, AccountData>>,
|
||||
/// Accounts on disk
|
||||
sstore: Box<SecretStore>,
|
||||
/// Accounts unlocked with rolling tokens
|
||||
transient_sstore: Box<SecretStore>,
|
||||
}
|
||||
|
||||
impl AccountProvider {
|
||||
@ -178,6 +185,7 @@ impl AccountProvider {
|
||||
unlocked: Mutex::new(HashMap::new()),
|
||||
address_book: Mutex::new(AddressBook::new(sstore.local_path().into())),
|
||||
sstore: sstore,
|
||||
transient_sstore: transient_sstore(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,8 +194,8 @@ impl AccountProvider {
|
||||
AccountProvider {
|
||||
unlocked: Mutex::new(HashMap::new()),
|
||||
address_book: Mutex::new(AddressBook::transient()),
|
||||
sstore: Box::new(EthStore::open(Box::new(NullDir::default()))
|
||||
.expect("NullDir load always succeeds; qed"))
|
||||
sstore: transient_sstore(),
|
||||
transient_sstore: transient_sstore(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -433,4 +441,15 @@ mod tests {
|
||||
ap.unlocked.lock().get_mut(&kp.address()).unwrap().unlock = Unlock::Timed(Instant::now());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_sign_and_return_token() {
|
||||
let kp = Random.generate().unwrap();
|
||||
// given
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
||||
|
||||
// when
|
||||
let (_signature, token) = ap.sign_with_token(kp.address(), "test", Default::default()).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ use std::{fs, io};
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::collections::HashMap;
|
||||
use time;
|
||||
use ethkey::Address;
|
||||
use {json, SafeAccount, Error};
|
||||
use json::UUID;
|
||||
use super::KeyDirectory;
|
||||
@ -138,12 +137,12 @@ impl KeyDirectory for DiskDirectory {
|
||||
Ok(account)
|
||||
}
|
||||
|
||||
fn remove(&self, address: &Address) -> Result<(), Error> {
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
|
||||
// enumerate all entries in keystore
|
||||
// and find entry with given address
|
||||
let to_remove = try!(self.files())
|
||||
.into_iter()
|
||||
.find(|&(_, ref account)| &account.address == address);
|
||||
.find(|&(_, ref acc)| acc == account);
|
||||
|
||||
// remove it
|
||||
match to_remove {
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use ethkey::Address;
|
||||
use {SafeAccount, Error};
|
||||
use super::{KeyDirectory, DiskDirectory, DirectoryType};
|
||||
|
||||
@ -89,7 +88,7 @@ impl KeyDirectory for GethDirectory {
|
||||
self.dir.insert(account)
|
||||
}
|
||||
|
||||
fn remove(&self, address: &Address) -> Result<(), Error> {
|
||||
self.dir.remove(address)
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
|
||||
self.dir.remove(account)
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use ethkey::Address;
|
||||
use std::path::{PathBuf};
|
||||
use {SafeAccount, Error};
|
||||
|
||||
@ -30,7 +29,7 @@ pub enum DirectoryType {
|
||||
pub trait KeyDirectory: Send + Sync {
|
||||
fn load(&self) -> Result<Vec<SafeAccount>, Error>;
|
||||
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error>;
|
||||
fn remove(&self, address: &Address) -> Result<(), Error>;
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error>;
|
||||
fn path(&self) -> Option<&PathBuf> { None }
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use ethkey::Address;
|
||||
use {SafeAccount, Error};
|
||||
use super::{KeyDirectory, DiskDirectory, DirectoryType};
|
||||
|
||||
@ -68,7 +67,7 @@ impl KeyDirectory for ParityDirectory {
|
||||
self.dir.insert(account)
|
||||
}
|
||||
|
||||
fn remove(&self, address: &Address) -> Result<(), Error> {
|
||||
self.dir.remove(address)
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
|
||||
self.dir.remove(account)
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ impl EthStore {
|
||||
|
||||
fn save(&self, account: SafeAccount) -> Result<(), Error> {
|
||||
// save to file
|
||||
let account = try!(self.dir.insert(account.clone()));
|
||||
let account = try!(self.dir.insert(account));
|
||||
|
||||
// update cache
|
||||
let mut cache = self.cache.write();
|
||||
@ -124,13 +124,11 @@ impl SecretStore for EthStore {
|
||||
}
|
||||
|
||||
fn remove_account(&self, address: &Address, password: &str) -> Result<(), Error> {
|
||||
let can_remove = {
|
||||
let account = try!(self.get(address));
|
||||
account.check_password(password)
|
||||
};
|
||||
let account = try!(self.get(address));
|
||||
let can_remove = account.check_password(password);
|
||||
|
||||
if can_remove {
|
||||
try!(self.dir.remove(address));
|
||||
try!(self.dir.remove(&account));
|
||||
let mut cache = self.cache.write();
|
||||
cache.remove(address);
|
||||
Ok(())
|
||||
@ -197,3 +195,136 @@ impl SecretStore for EthStore {
|
||||
import::import_geth_accounts(&*self.dir, desired.into_iter().collect(), testnet)
|
||||
}
|
||||
}
|
||||
|
||||
/// Similar to `EthStore` but may store many accounts (with different passwords) for the same `Address`
|
||||
pub struct EthMultiStore {
|
||||
dir: Box<KeyDirectory>,
|
||||
iterations: u32,
|
||||
cache: RwLock<BTreeMap<Address, Vec<SafeAccount>>>,
|
||||
}
|
||||
|
||||
impl EthMultiStore {
|
||||
|
||||
pub fn open(directory: Box<KeyDirectory>) -> Result<Self, Error> {
|
||||
Self::open_with_iterations(directory, KEY_ITERATIONS as u32)
|
||||
}
|
||||
|
||||
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: u32) -> Result<Self, Error> {
|
||||
let mut store = EthMultiStore {
|
||||
dir: directory,
|
||||
iterations: iterations,
|
||||
cache: Default::default(),
|
||||
};
|
||||
try!(store.reload_accounts());
|
||||
Ok(store)
|
||||
}
|
||||
|
||||
fn reload_accounts(&self) -> Result<(), Error> {
|
||||
let mut cache = self.cache.write();
|
||||
let accounts = try!(self.dir.load());
|
||||
|
||||
let mut new_accounts = BTreeMap::new();
|
||||
for account in accounts {
|
||||
let mut entry = new_accounts.entry(account.address.clone()).or_insert_with(Vec::new);
|
||||
entry.push(account);
|
||||
}
|
||||
mem::replace(&mut *cache, new_accounts);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get(&self, address: &Address) -> Result<Vec<SafeAccount>, Error> {
|
||||
{
|
||||
let cache = self.cache.read();
|
||||
if let Some(accounts) = cache.get(address) {
|
||||
if !accounts.is_empty() {
|
||||
return Ok(accounts.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try!(self.reload_accounts());
|
||||
let cache = self.cache.read();
|
||||
let accounts = try!(cache.get(address).cloned().ok_or(Error::InvalidAccount));
|
||||
if accounts.is_empty() {
|
||||
Err(Error::InvalidAccount)
|
||||
} else {
|
||||
Ok(accounts)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_account(&self, account: SafeAccount) -> Result<(), Error> {
|
||||
//save to file
|
||||
let account = try!(self.dir.insert(account));
|
||||
|
||||
// update cache
|
||||
let mut cache = self.cache.write();
|
||||
let mut accounts = cache.entry(account.address.clone()).or_insert_with(Vec::new);
|
||||
accounts.push(account);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_account(&self, address: &Address, password: &str) -> Result<(), Error> {
|
||||
let accounts = try!(self.get(address));
|
||||
|
||||
for account in accounts {
|
||||
// Skip if password is invalid
|
||||
if !account.check_password(password) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove from dir
|
||||
try!(self.dir.remove(&account));
|
||||
|
||||
// Remove from cache
|
||||
let mut cache = self.cache.write();
|
||||
let is_empty = {
|
||||
let mut accounts = cache.get_mut(address).expect("Entry exists, because it was returned by `get`; qed");
|
||||
if let Some(position) = accounts.iter().position(|acc| acc == &account) {
|
||||
accounts.remove(position);
|
||||
}
|
||||
accounts.is_empty()
|
||||
};
|
||||
|
||||
if is_empty {
|
||||
cache.remove(address);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
Err(Error::InvalidPassword)
|
||||
}
|
||||
|
||||
pub fn change_password(&self, address: &Address, old_password: &str, new_password: &str) -> Result<(), Error> {
|
||||
let accounts = try!(self.get(address));
|
||||
for account in accounts {
|
||||
// First remove
|
||||
try!(self.remove_account(&address, old_password));
|
||||
// Then insert back with new password
|
||||
let new_account = try!(account.change_password(old_password, new_password, self.iterations));
|
||||
try!(self.insert_account(new_account));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sign(&self, address: &Address, password: &str, message: &Message) -> Result<Signature, Error> {
|
||||
let accounts = try!(self.get(address));
|
||||
for account in accounts {
|
||||
if account.check_password(password) {
|
||||
return account.sign(password, message);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::InvalidPassword)
|
||||
}
|
||||
|
||||
pub fn decrypt(&self, account: &Address, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let accounts = try!(self.get(account));
|
||||
for account in accounts {
|
||||
if account.check_password(password) {
|
||||
return account.decrypt(password, shared_mac, message);
|
||||
}
|
||||
}
|
||||
Err(Error::InvalidPassword)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@ use std::path::PathBuf;
|
||||
use std::{env, fs};
|
||||
use rand::{Rng, OsRng};
|
||||
use ethstore::dir::{KeyDirectory, DiskDirectory};
|
||||
use ethstore::ethkey::Address;
|
||||
use ethstore::{Error, SafeAccount};
|
||||
|
||||
pub fn random_dir() -> PathBuf {
|
||||
@ -68,7 +67,7 @@ impl KeyDirectory for TransientDir {
|
||||
self.dir.insert(account)
|
||||
}
|
||||
|
||||
fn remove(&self, address: &Address) -> Result<(), Error> {
|
||||
self.dir.remove(address)
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
|
||||
self.dir.remove(account)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user