Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
Merge pull request #4583 from ethcore/sstore-opt
Browse files Browse the repository at this point in the history
Optimize key directory reloads
  • Loading branch information
rphmeier authored Feb 17, 2017
2 parents 32023f1 + 92d8edc commit 998cb0d
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 16 deletions.
67 changes: 54 additions & 13 deletions ethstore/src/dir/disk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,26 +90,43 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
}
}

/// all accounts found in keys directory
fn files(&self) -> Result<HashMap<PathBuf, SafeAccount>, Error> {
// it's not done using one iterator cause
// there is an issue with rustc and it takes tooo much time to compile
let paths = fs::read_dir(&self.path)?
fn files(&self) -> Result<Vec<PathBuf>, Error> {
Ok(fs::read_dir(&self.path)?
.flat_map(Result::ok)
.filter(|entry| {
let metadata = entry.metadata().ok();
let file_name = entry.file_name();
let name = file_name.to_string_lossy();
// filter directories
metadata.map_or(false, |m| !m.is_dir()) &&
// hidden files
!name.starts_with(".") &&
// other ignored files
!IGNORED_FILES.contains(&&*name)
// hidden files
!name.starts_with(".") &&
// other ignored files
!IGNORED_FILES.contains(&&*name)
})
.map(|entry| entry.path())
.collect::<Vec<PathBuf>>();
.collect::<Vec<PathBuf>>()
)
}

pub fn files_hash(&self) -> Result<u64, Error> {
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;

let mut hasher = DefaultHasher::new();
let files = self.files()?;
for file in files {
hasher.write(file.to_str().unwrap_or("").as_bytes())
}

Ok(hasher.finish())
}

/// all accounts found in keys directory
fn files_content(&self) -> Result<HashMap<PathBuf, SafeAccount>, Error> {
// it's not done using one iterator cause
// there is an issue with rustc and it takes tooo much time to compile
let paths = self.files()?;
Ok(paths
.into_iter()
.filter_map(|path| {
Expand Down Expand Up @@ -166,7 +183,7 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {

impl<T> KeyDirectory for DiskDirectory<T> where T: KeyFileManager {
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
let accounts = self.files()?
let accounts = self.files_content()?
.into_iter()
.map(|(_, account)| account)
.collect();
Expand All @@ -191,7 +208,7 @@ impl<T> KeyDirectory for DiskDirectory<T> where T: KeyFileManager {
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
// enumerate all entries in keystore
// and find entry with given address
let to_remove = self.files()?
let to_remove = self.files_content()?
.into_iter()
.find(|&(_, ref acc)| acc.id == account.id && acc.address == account.address);

Expand All @@ -207,6 +224,10 @@ impl<T> KeyDirectory for DiskDirectory<T> where T: KeyFileManager {
fn as_vault_provider(&self) -> Option<&VaultKeyDirectoryProvider> {
Some(self)
}

fn unique_repr(&self) -> Result<u64, Error> {
self.files_hash()
}
}

impl<T> VaultKeyDirectoryProvider for DiskDirectory<T> where T: KeyFileManager {
Expand Down Expand Up @@ -279,7 +300,6 @@ mod test {
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned());
let res = directory.insert(account);


// then
assert!(res.is_ok(), "Should save account succesfuly.");
assert!(res.unwrap().filename.is_some(), "Filename has been assigned.");
Expand Down Expand Up @@ -336,4 +356,25 @@ mod test {
assert!(vaults.iter().any(|v| &*v == "vault1"));
assert!(vaults.iter().any(|v| &*v == "vault2"));
}

#[test]
fn hash_of_files() {
let temp_path = RandomTempPath::new();
let directory = RootDiskDirectory::create(&temp_path).unwrap();

let hash = directory.files_hash().expect("Files hash should be calculated ok");
assert_eq!(
hash,
15130871412783076140
);

let keypair = Random.generate().unwrap();
let password = "test pass";
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned());
directory.insert(account).expect("Account should be inserted ok");

let new_hash = directory.files_hash().expect("New files hash should be calculated ok");

assert!(new_hash != hash, "hash of the file list should change once directory content changed");
}
}
4 changes: 4 additions & 0 deletions ethstore/src/dir/geth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,8 @@ impl KeyDirectory for GethDirectory {
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
self.dir.remove(account)
}

fn unique_repr(&self) -> Result<u64, Error> {
self.dir.unique_repr()
}
}
7 changes: 7 additions & 0 deletions ethstore/src/dir/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,12 @@ impl KeyDirectory for MemoryDirectory {
}
Ok(())
}

fn unique_repr(&self) -> Result<u64, Error> {
let mut val = 0u64;
let accounts = self.accounts.read();
for acc in accounts.keys() { val = val ^ ::util::FixedHash::low_u64(acc) }
Ok(val)
}
}

2 changes: 2 additions & 0 deletions ethstore/src/dir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ pub trait KeyDirectory: Send + Sync {
fn path(&self) -> Option<&PathBuf> { None }
/// Return vault provider, if available
fn as_vault_provider(&self) -> Option<&VaultKeyDirectoryProvider> { None }
/// Unique representation of directory account collection
fn unique_repr(&self) -> Result<u64, Error>;
}

/// Vaults provider
Expand Down
4 changes: 4 additions & 0 deletions ethstore/src/dir/parity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,8 @@ impl KeyDirectory for ParityDirectory {
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
self.dir.remove(account)
}

fn unique_repr(&self) -> Result<u64, Error> {
self.dir.unique_repr()
}
}
19 changes: 16 additions & 3 deletions ethstore/src/ethstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ pub struct EthMultiStore {
// order lock: cache, then vaults
cache: RwLock<BTreeMap<StoreAccountRef, Vec<SafeAccount>>>,
vaults: Mutex<HashMap<String, Box<VaultKeyDirectory>>>,
dir_hash: Mutex<Option<u64>>,
}

impl EthMultiStore {
Expand All @@ -244,11 +245,23 @@ impl EthMultiStore {
vaults: Mutex::new(HashMap::new()),
iterations: iterations,
cache: Default::default(),
dir_hash: Default::default(),
};
store.reload_accounts()?;
Ok(store)
}

fn reload_if_changed(&self) -> Result<(), Error> {
let mut last_dir_hash = self.dir_hash.lock();
let dir_hash = Some(self.dir.unique_repr()?);
if *last_dir_hash == dir_hash {
return Ok(())
}
self.reload_accounts()?;
*last_dir_hash = dir_hash;
Ok(())
}

fn reload_accounts(&self) -> Result<(), Error> {
let mut cache = self.cache.write();

Expand Down Expand Up @@ -284,7 +297,7 @@ impl EthMultiStore {
}
}

self.reload_accounts()?;
self.reload_if_changed()?;
let cache = self.cache.read();
let accounts = cache.get(account).ok_or(Error::InvalidAccount)?;
if accounts.is_empty() {
Expand Down Expand Up @@ -431,15 +444,15 @@ impl SimpleSecretStore for EthMultiStore {
}

fn account_ref(&self, address: &Address) -> Result<StoreAccountRef, Error> {
self.reload_accounts()?;
self.reload_if_changed()?;
self.cache.read().keys()
.find(|r| &r.address == address)
.cloned()
.ok_or(Error::InvalidAccount)
}

fn accounts(&self) -> Result<Vec<StoreAccountRef>, Error> {
self.reload_accounts()?;
self.reload_if_changed()?;
Ok(self.cache.read().keys().cloned().collect())
}

Expand Down
4 changes: 4 additions & 0 deletions ethstore/tests/util/transient_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,8 @@ impl KeyDirectory for TransientDir {
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
self.dir.remove(account)
}

fn unique_repr(&self) -> Result<u64, Error> {
self.dir.unique_repr()
}
}

0 comments on commit 998cb0d

Please sign in to comment.