From 9655ce8dbf024391128b5c778119e05bedabb47e Mon Sep 17 00:00:00 2001 From: Peter Date: Sat, 26 Mar 2016 17:51:27 +0000 Subject: [PATCH] Caching for computing seed hashes (#541) Code review changes --- ethash/src/compute.rs | 102 ++++++++++++++++++++++++++++----- ethash/src/lib.rs | 2 +- ethcore/src/ethereum/ethash.rs | 6 -- rpc/src/v1/impls/eth.rs | 9 ++- 4 files changed, 96 insertions(+), 23 deletions(-) diff --git a/ethash/src/compute.rs b/ethash/src/compute.rs index eb64151d0..f2230673a 100644 --- a/ethash/src/compute.rs +++ b/ethash/src/compute.rs @@ -20,6 +20,8 @@ // TODO: fix endianess for big endian use primal::is_prime; +use std::cell::Cell; +use std::sync::Mutex; use std::mem; use std::ptr; use sha3; @@ -85,6 +87,7 @@ pub type H256 = [u8; 32]; pub struct Light { block_number: u64, cache: Vec, + seed_compute: Mutex, } /// Light cache structur @@ -101,17 +104,17 @@ impl Light { light_compute(self, header_hash, nonce) } - pub fn file_path(block_number: u64) -> PathBuf { + pub fn file_path(seed_hash: H256) -> 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 seed_compute = SeedHashCompute::new(); + let path = Light::file_path(seed_compute.get_seedhash(block_number)); let mut file = try!(File::open(path)); let cache_size = get_cache_size(block_number); @@ -126,11 +129,13 @@ impl Light { Ok(Light { cache: nodes, block_number: block_number, + seed_compute: Mutex::new(seed_compute), }) } pub fn to_file(&self) -> io::Result<()> { - let path = Light::file_path(self.block_number); + let seed_compute = self.seed_compute.lock().unwrap(); + let path = Light::file_path(seed_compute.get_seedhash(self.block_number)); try!(fs::create_dir_all(path.parent().unwrap())); let mut file = try!(File::create(path)); @@ -141,6 +146,49 @@ impl Light { } } +pub struct SeedHashCompute { + prev_epoch: Cell, + prev_seedhash: Cell +} + +impl SeedHashCompute { + + #[inline] + pub fn new() -> SeedHashCompute { + SeedHashCompute { prev_epoch: Cell::new(0), prev_seedhash: Cell::new([0u8; 32]) } + } + + #[inline] + fn reset_cache(&self) { + self.prev_epoch.set(0); + self.prev_seedhash.set([0u8; 32]); + } + + #[inline] + pub fn get_seedhash(&self, block_number: u64) -> H256 { + let epoch = block_number / ETHASH_EPOCH_LENGTH; + if epoch < self.prev_epoch.get() { + // can't build on previous hash if requesting an older block + self.reset_cache(); + } + if epoch > self.prev_epoch.get() { + let seed_hash = SeedHashCompute::resume_compute_seedhash(self.prev_seedhash.get(), self.prev_epoch.get(), epoch); + self.prev_seedhash.set(seed_hash); + self.prev_epoch.set(epoch); + } + self.prev_seedhash.get() + } + + #[inline] + pub fn resume_compute_seedhash(mut hash: H256, start_epoch: u64, end_epoch: u64) -> H256 { + for _ in start_epoch .. end_epoch { + unsafe { sha3::sha3_256(hash[..].as_mut_ptr(), 32, hash[..].as_ptr(), 32) }; + } + hash + } +} + + #[inline] fn fnv_hash(x: u32, y: u32) -> u32 { return x.wrapping_mul(FNV_PRIME) ^ y; @@ -171,16 +219,6 @@ fn get_data_size(block_number: u64) -> usize { sz as usize } -#[inline] -/// Given the `block_number`, determine the seed hash for Ethash. -pub fn get_seedhash(block_number: u64) -> H256 { - let epochs = block_number / ETHASH_EPOCH_LENGTH; - let mut ret: H256 = [0u8; 32]; - for _ in 0..epochs { - unsafe { sha3::sha3_256(ret[..].as_mut_ptr(), 32, ret[..].as_ptr(), 32) }; - } - ret -} /// Difficulty quick check for POW preverification /// @@ -287,7 +325,9 @@ fn calculate_dag_item(node_index: u32, light: &Light) -> Node { } fn light_new(block_number: u64) -> Light { - let seedhash = get_seedhash(block_number); + + let seed_compute = SeedHashCompute::new(); + let seedhash = seed_compute.get_seedhash(block_number); let cache_size = get_cache_size(block_number); if cache_size % NODE_BYTES != 0 { @@ -318,6 +358,7 @@ fn light_new(block_number: u64) -> Light { Light { cache: nodes, block_number: block_number, + seed_compute: Mutex::new(seed_compute), } } @@ -382,3 +423,34 @@ fn test_light_compute() { assert_eq!(result.mix_hash[..], mix_hash[..]); assert_eq!(result.value[..], boundary[..]); } + +#[test] +fn test_seed_compute_once() { + let seed_compute = SeedHashCompute::new(); + let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162]; + assert_eq!(seed_compute.get_seedhash(486382), hash); +} + +#[test] +fn test_seed_compute_zero() { + let seed_compute = SeedHashCompute::new(); + assert_eq!(seed_compute.get_seedhash(0), [0u8; 32]); +} + +#[test] +fn test_seed_compute_after_older() { + let seed_compute = SeedHashCompute::new(); + // calculating an older value first shouldn't affect the result + let _ = seed_compute.get_seedhash(50000); + let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162]; + assert_eq!(seed_compute.get_seedhash(486382), hash); +} + +#[test] +fn test_seed_compute_after_newer() { + let seed_compute = SeedHashCompute::new(); + // calculating an newer value first shouldn't affect the result + let _ = seed_compute.get_seedhash(972764); + let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162]; + assert_eq!(seed_compute.get_seedhash(486382), hash); +} diff --git a/ethash/src/lib.rs b/ethash/src/lib.rs index 982f7129b..582b59d9c 100644 --- a/ethash/src/lib.rs +++ b/ethash/src/lib.rs @@ -24,7 +24,7 @@ mod compute; use std::mem; use compute::Light; -pub use compute::{get_seedhash, quick_get_difficulty, H256, ProofOfWork, ETHASH_EPOCH_LENGTH}; +pub use compute::{SeedHashCompute, quick_get_difficulty, H256, ProofOfWork, ETHASH_EPOCH_LENGTH}; use std::sync::{Arc, Mutex}; diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index d2c56ebf1..6474bf8e5 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -249,11 +249,6 @@ impl Ethash { x!(U256::from((U512::one() << 256) / x!(difficulty))) } - /// Given the `block_number`, determine the seed hash for Ethash. - pub fn get_seedhash(number: BlockNumber) -> H256 { - Self::from_ethash(ethash::get_seedhash(number)) - } - fn to_ethash(hash: H256) -> EH256 { unsafe { mem::transmute(hash) } } @@ -512,4 +507,3 @@ mod tests { // TODO: difficulty test } - diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index a065392d5..893bc66c8 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -15,6 +15,9 @@ // along with Parity. If not, see . //! Eth rpc implementation. + +extern crate ethash; + use std::collections::HashSet; use std::sync::{Arc, Weak, Mutex}; use std::ops::Deref; @@ -31,6 +34,7 @@ use ethcore::views::*; use ethcore::ethereum::Ethash; use ethcore::ethereum::denominations::shannon; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; +use self::ethash::SeedHashCompute; use v1::traits::{Eth, EthFilter}; use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, CallRequest, OptionalValue, Index, Filter, Log, Receipt}; use v1::helpers::{PollFilter, PollManager, ExternalMinerService, ExternalMiner}; @@ -56,6 +60,7 @@ pub struct EthClient accounts: Weak, miner: Weak, external_miner: EM, + seed_compute: Mutex, } impl EthClient @@ -86,6 +91,7 @@ impl EthClient miner: Arc::downgrade(miner), accounts: Arc::downgrade(accounts), external_miner: em, + seed_compute: Mutex::new(SeedHashCompute::new()), } } @@ -413,7 +419,8 @@ impl Eth for EthClient Some(ref b) => { let pow_hash = b.hash(); let target = Ethash::difficulty_to_boundary(b.block().header().difficulty()); - let seed_hash = Ethash::get_seedhash(b.block().header().number()); + let seed_compute = self.seed_compute.lock().unwrap(); + let seed_hash = seed_compute.get_seedhash(b.block().header().number()); to_value(&(pow_hash, seed_hash, target)) } _ => Err(Error::internal_error())