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

management of account expiration & memory #701

Merged
merged 5 commits into from
Mar 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 11 additions & 2 deletions parity/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ impl Configuration {
client: service.client(),
info: Default::default(),
sync: sync.clone(),
accounts: account_service.clone(),
});
service.io().register_handler(io_handler).expect("Error registering IO handler");

Expand Down Expand Up @@ -559,20 +560,28 @@ impl Informant {

const INFO_TIMER: TimerToken = 0;

const ACCOUNT_TICK_TIMER: TimerToken = 10;
const ACCOUNT_TICK_MS: u64 = 60000;

struct ClientIoHandler {
client: Arc<Client>,
sync: Arc<EthSync>,
accounts: Arc<AccountService>,
info: Informant,
}

impl IoHandler<NetSyncMessage> for ClientIoHandler {
fn initialize(&self, io: &IoContext<NetSyncMessage>) {
io.register_timer(INFO_TIMER, 5000).expect("Error registering timer");
io.register_timer(ACCOUNT_TICK_TIMER, ACCOUNT_TICK_MS).expect("Error registering account timer");

}

fn timeout(&self, _io: &IoContext<NetSyncMessage>, timer: TimerToken) {
if INFO_TIMER == timer {
self.info.tick(&self.client, &self.sync);
match timer {
INFO_TIMER => { self.info.tick(&self.client, &self.sync); }
ACCOUNT_TICK_TIMER => { self.accounts.tick(); },
_ => {}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions util/src/keys/directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,8 @@ impl KeyDirectory {
if removes.is_empty() { return; }
let mut cache = self.cache.write().unwrap();
for key in removes { cache.remove(&key); }

cache.shrink_to_fit();
}

/// Reports how many keys are currently cached.
Expand Down
61 changes: 58 additions & 3 deletions util/src/keys/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,22 @@ impl AccountService {
secret_store: secret_store
}
}

#[cfg(test)]
fn new_test(temp: &::devtools::RandomTempPath) -> Self {
let secret_store = RwLock::new(SecretStore::new_test(temp));
AccountService {
secret_store: secret_store
}
}

/// Ticks the account service
pub fn tick(&self) {
self.secret_store.write().unwrap().collect_garbage();
}
}


impl Default for SecretStore {
fn default() -> Self {
SecretStore::new()
Expand Down Expand Up @@ -256,6 +270,20 @@ impl SecretStore {
let unlock = try!(read_lock.get(account).ok_or(SigningError::AccountNotUnlocked));
Ok(unlock.secret as crypto::Secret)
}

/// Makes account unlocks expire and removes unused key files from memory
pub fn collect_garbage(&mut self) {
let mut garbage_lock = self.unlocks.write().unwrap();
self.directory.collect_garbage();
let utc = UTC::now();
let expired_addresses = garbage_lock.iter()
.filter(|&(_, unlock)| unlock.expires < utc)
.map(|(address, _)| address.clone()).collect::<Vec<Address>>();

for expired in expired_addresses { garbage_lock.remove(&expired); }

garbage_lock.shrink_to_fit();
}
}

fn derive_key_iterations(password: &str, salt: &H256, c: u32) -> (Bytes, Bytes) {
Expand Down Expand Up @@ -362,14 +390,12 @@ impl EncryptedHashMap<H128> for SecretStore {

}

#[cfg(test)]
#[cfg(all(test, feature="heavy-tests"))]
mod vector_tests {
use super::{derive_mac,derive_key_iterations};
use common::*;


#[test]
#[cfg(feature="heavy-tests")]
fn mac_vector() {
let password = "testpassword";
let salt = H256::from_str("ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd").unwrap();
Expand All @@ -395,6 +421,7 @@ mod tests {
use devtools::*;
use common::*;
use crypto::KeyPair;
use chrono::*;

#[test]
fn can_insert() {
Expand Down Expand Up @@ -581,4 +608,32 @@ mod tests {
let kp = KeyPair::from_secret(secret).unwrap();
assert_eq!(Address::from(kp.public().sha3()), addr);
}

#[test]
fn can_create_service() {
let temp = RandomTempPath::create_dir();
let svc = AccountService::new_test(&temp);
assert!(svc.accounts().unwrap().is_empty());
}

#[test]
fn accounts_expire() {
use std::collections::hash_map::*;

let temp = RandomTempPath::create_dir();
let svc = AccountService::new_test(&temp);
let address = svc.new_account("pass").unwrap();
svc.unlock_account(&address, "pass").unwrap();
assert!(svc.account_secret(&address).is_ok());
{
let ss_rw = svc.secret_store.write().unwrap();
let mut ua_rw = ss_rw.unlocks.write().unwrap();
let entry = ua_rw.entry(address);
if let Entry::Occupied(mut occupied) = entry { occupied.get_mut().expires = UTC::now() - Duration::minutes(1); }
}

svc.tick();

assert!(svc.account_secret(&address).is_err());
}
}