Merge pull request #701 from ethcore/sstore-key-exp
management of account expiration & memory
This commit is contained in:
commit
97fe4fcab4
@ -523,6 +523,7 @@ impl Configuration {
|
|||||||
client: service.client(),
|
client: service.client(),
|
||||||
info: Default::default(),
|
info: Default::default(),
|
||||||
sync: sync.clone(),
|
sync: sync.clone(),
|
||||||
|
accounts: account_service.clone(),
|
||||||
});
|
});
|
||||||
service.io().register_handler(io_handler).expect("Error registering IO handler");
|
service.io().register_handler(io_handler).expect("Error registering IO handler");
|
||||||
|
|
||||||
@ -618,20 +619,28 @@ impl Informant {
|
|||||||
|
|
||||||
const INFO_TIMER: TimerToken = 0;
|
const INFO_TIMER: TimerToken = 0;
|
||||||
|
|
||||||
|
const ACCOUNT_TICK_TIMER: TimerToken = 10;
|
||||||
|
const ACCOUNT_TICK_MS: u64 = 60000;
|
||||||
|
|
||||||
struct ClientIoHandler {
|
struct ClientIoHandler {
|
||||||
client: Arc<Client>,
|
client: Arc<Client>,
|
||||||
sync: Arc<EthSync>,
|
sync: Arc<EthSync>,
|
||||||
|
accounts: Arc<AccountService>,
|
||||||
info: Informant,
|
info: Informant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IoHandler<NetSyncMessage> for ClientIoHandler {
|
impl IoHandler<NetSyncMessage> for ClientIoHandler {
|
||||||
fn initialize(&self, io: &IoContext<NetSyncMessage>) {
|
fn initialize(&self, io: &IoContext<NetSyncMessage>) {
|
||||||
io.register_timer(INFO_TIMER, 5000).expect("Error registering timer");
|
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) {
|
fn timeout(&self, _io: &IoContext<NetSyncMessage>, timer: TimerToken) {
|
||||||
if INFO_TIMER == timer {
|
match timer {
|
||||||
self.info.tick(&self.client, &self.sync);
|
INFO_TIMER => { self.info.tick(&self.client, &self.sync); }
|
||||||
|
ACCOUNT_TICK_TIMER => { self.accounts.tick(); },
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -542,6 +542,8 @@ impl KeyDirectory {
|
|||||||
if removes.is_empty() { return; }
|
if removes.is_empty() { return; }
|
||||||
let mut cache = self.cache.write().unwrap();
|
let mut cache = self.cache.write().unwrap();
|
||||||
for key in removes { cache.remove(&key); }
|
for key in removes { cache.remove(&key); }
|
||||||
|
|
||||||
|
cache.shrink_to_fit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reports how many keys are currently cached.
|
/// Reports how many keys are currently cached.
|
||||||
|
@ -135,8 +135,22 @@ impl AccountService {
|
|||||||
secret_store: secret_store
|
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 {
|
impl Default for SecretStore {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
SecretStore::new()
|
SecretStore::new()
|
||||||
@ -256,6 +270,20 @@ impl SecretStore {
|
|||||||
let unlock = try!(read_lock.get(account).ok_or(SigningError::AccountNotUnlocked));
|
let unlock = try!(read_lock.get(account).ok_or(SigningError::AccountNotUnlocked));
|
||||||
Ok(unlock.secret as crypto::Secret)
|
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) {
|
fn derive_key_iterations(password: &str, salt: &H256, c: u32) -> (Bytes, Bytes) {
|
||||||
@ -362,14 +390,12 @@ impl EncryptedHashMap<H128> for SecretStore {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature="heavy-tests"))]
|
||||||
mod vector_tests {
|
mod vector_tests {
|
||||||
use super::{derive_mac,derive_key_iterations};
|
use super::{derive_mac,derive_key_iterations};
|
||||||
use common::*;
|
use common::*;
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature="heavy-tests")]
|
|
||||||
fn mac_vector() {
|
fn mac_vector() {
|
||||||
let password = "testpassword";
|
let password = "testpassword";
|
||||||
let salt = H256::from_str("ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd").unwrap();
|
let salt = H256::from_str("ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd").unwrap();
|
||||||
@ -395,6 +421,7 @@ mod tests {
|
|||||||
use devtools::*;
|
use devtools::*;
|
||||||
use common::*;
|
use common::*;
|
||||||
use crypto::KeyPair;
|
use crypto::KeyPair;
|
||||||
|
use chrono::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_insert() {
|
fn can_insert() {
|
||||||
@ -581,4 +608,32 @@ mod tests {
|
|||||||
let kp = KeyPair::from_secret(secret).unwrap();
|
let kp = KeyPair::from_secret(secret).unwrap();
|
||||||
assert_eq!(Address::from(kp.public().sha3()), addr);
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user