diff --git a/ethash/Cargo.toml b/ethash/Cargo.toml index 16af6525c..4c0b3d65e 100644 --- a/ethash/Cargo.toml +++ b/ethash/Cargo.toml @@ -6,4 +6,6 @@ authors = ["arkpar ProofOfWork { light_compute(self, header_hash, nonce) } + + pub fn file_path(block_number: u64) -> PathBuf { + let mut home = ::std::env::home_dir().unwrap(); + home.push(".ethash"); + home.push("light"); + let seed_hash = get_seedhash(block_number); + home.push(to_hex(&seed_hash)); + home + } + + pub fn from_file(block_number: u64) -> io::Result { + let path = Light::file_path(block_number); + let mut file = try!(File::open(path)); + + let cache_size = get_cache_size(block_number); + if try!(file.metadata()).len() != cache_size as u64 { + return Err(io::Error::new(io::ErrorKind::Other, "Cache file size mismatch")); + } + let num_nodes = cache_size / NODE_BYTES; + let mut nodes: Vec = Vec::new(); + nodes.resize(num_nodes, unsafe { mem::uninitialized() }); + let buf = unsafe { slice::from_raw_parts_mut(nodes.as_mut_ptr() as *mut u8, cache_size) }; + try!(file.read_exact(buf)); + Ok(Light { + cache: nodes, + block_number: block_number, + }) + } + + pub fn to_file(&self) -> io::Result<()> { + let path = Light::file_path(self.block_number); + try!(fs::create_dir_all(path.parent().unwrap())); + let mut file = try!(File::create(path)); + + let cache_size = self.cache.len() * NODE_BYTES; + let buf = unsafe { slice::from_raw_parts(self.cache.as_ptr() as *const u8, cache_size) }; + try!(file.write(buf)); + Ok(()) + } } #[inline] @@ -249,6 +292,19 @@ fn light_new(block_number: u64) -> Light { } } +static CHARS: &'static[u8] = b"0123456789abcdef"; +fn to_hex(bytes: &[u8]) -> String { + let mut v = Vec::with_capacity(bytes.len() * 2); + for &byte in bytes.iter() { + v.push(CHARS[(byte >> 4) as usize]); + v.push(CHARS[(byte & 0xf) as usize]); + } + + unsafe { + String::from_utf8_unchecked(v) + } +} + #[test] fn test_difficulty_test() { let hash = [0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72]; diff --git a/ethash/src/lib.rs b/ethash/src/lib.rs index e87ee1a03..17853c985 100644 --- a/ethash/src/lib.rs +++ b/ethash/src/lib.rs @@ -1,25 +1,28 @@ //! Ethash implementation //! See https://github.com/ethereum/wiki/wiki/Ethash extern crate sha3; +extern crate lru_cache; +#[macro_use] +extern crate log; mod sizes; mod compute; +use lru_cache::LruCache; use compute::Light; pub use compute::{quick_get_difficulty, H256, ProofOfWork, ETHASH_EPOCH_LENGTH}; -use std::collections::HashMap; -use std::sync::RwLock; +use std::sync::{Arc, Mutex}; /// Lighy/Full cache manager pub struct EthashManager { - lights: RwLock>, + lights: Mutex>> } impl EthashManager { /// Create a new new instance of ethash manager pub fn new() -> EthashManager { EthashManager { - lights: RwLock::new(HashMap::new()) + lights: Mutex::new(LruCache::new(2)) } } @@ -30,15 +33,27 @@ impl EthashManager { /// `nonce` - The nonce to pack into the mix pub fn compute_light(&self, block_number: u64, header_hash: &H256, nonce: u64) -> ProofOfWork { let epoch = block_number / ETHASH_EPOCH_LENGTH; - while !self.lights.read().unwrap().contains_key(&epoch) { - if let Ok(mut lights) = self.lights.try_write() - { - if !lights.contains_key(&epoch) { - let light = Light::new(block_number); - lights.insert(epoch, light); + let light = { + let mut lights = self.lights.lock().unwrap(); + match lights.get_mut(&epoch).map(|l| l.clone()) { + None => { + let light = match Light::from_file(block_number) { + Ok(light) => Arc::new(light), + Err(e) => { + debug!("Light cache file not found for {}:{}", block_number, e); + let light = Light::new(block_number); + if let Err(e) = light.to_file() { + warn!("Light cache file write error: {}", e); + } + Arc::new(light) + } + }; + lights.insert(epoch, light.clone()); + light } + Some(light) => light } - } - self.lights.read().unwrap().get(&epoch).unwrap().compute(header_hash, nonce) + }; + light.compute(header_hash, nonce) } }