Caching for computing seed hashes (#541)

Code review changes
This commit is contained in:
Peter 2016-03-26 17:51:27 +00:00
parent 1700b6a087
commit 9655ce8dbf
4 changed files with 96 additions and 23 deletions

View File

@ -20,6 +20,8 @@
// TODO: fix endianess for big endian // TODO: fix endianess for big endian
use primal::is_prime; use primal::is_prime;
use std::cell::Cell;
use std::sync::Mutex;
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use sha3; use sha3;
@ -85,6 +87,7 @@ pub type H256 = [u8; 32];
pub struct Light { pub struct Light {
block_number: u64, block_number: u64,
cache: Vec<Node>, cache: Vec<Node>,
seed_compute: Mutex<SeedHashCompute>,
} }
/// Light cache structur /// Light cache structur
@ -101,17 +104,17 @@ impl Light {
light_compute(self, header_hash, nonce) 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(); let mut home = ::std::env::home_dir().unwrap();
home.push(".ethash"); home.push(".ethash");
home.push("light"); home.push("light");
let seed_hash = get_seedhash(block_number);
home.push(to_hex(&seed_hash)); home.push(to_hex(&seed_hash));
home home
} }
pub fn from_file(block_number: u64) -> io::Result<Light> { pub fn from_file(block_number: u64) -> io::Result<Light> {
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 mut file = try!(File::open(path));
let cache_size = get_cache_size(block_number); let cache_size = get_cache_size(block_number);
@ -126,11 +129,13 @@ impl Light {
Ok(Light { Ok(Light {
cache: nodes, cache: nodes,
block_number: block_number, block_number: block_number,
seed_compute: Mutex::new(seed_compute),
}) })
} }
pub fn to_file(&self) -> io::Result<()> { 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())); try!(fs::create_dir_all(path.parent().unwrap()));
let mut file = try!(File::create(path)); let mut file = try!(File::create(path));
@ -141,6 +146,49 @@ impl Light {
} }
} }
pub struct SeedHashCompute {
prev_epoch: Cell<u64>,
prev_seedhash: Cell<H256>
}
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] #[inline]
fn fnv_hash(x: u32, y: u32) -> u32 { fn fnv_hash(x: u32, y: u32) -> u32 {
return x.wrapping_mul(FNV_PRIME) ^ y; return x.wrapping_mul(FNV_PRIME) ^ y;
@ -171,16 +219,6 @@ fn get_data_size(block_number: u64) -> usize {
sz as 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 /// 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 { 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); let cache_size = get_cache_size(block_number);
if cache_size % NODE_BYTES != 0 { if cache_size % NODE_BYTES != 0 {
@ -318,6 +358,7 @@ fn light_new(block_number: u64) -> Light {
Light { Light {
cache: nodes, cache: nodes,
block_number: block_number, 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.mix_hash[..], mix_hash[..]);
assert_eq!(result.value[..], boundary[..]); 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);
}

View File

@ -24,7 +24,7 @@ mod compute;
use std::mem; use std::mem;
use compute::Light; 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}; use std::sync::{Arc, Mutex};

View File

@ -249,11 +249,6 @@ impl Ethash {
x!(U256::from((U512::one() << 256) / x!(difficulty))) 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 { fn to_ethash(hash: H256) -> EH256 {
unsafe { mem::transmute(hash) } unsafe { mem::transmute(hash) }
} }
@ -512,4 +507,3 @@ mod tests {
// TODO: difficulty test // TODO: difficulty test
} }

View File

@ -15,6 +15,9 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Eth rpc implementation. //! Eth rpc implementation.
extern crate ethash;
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::{Arc, Weak, Mutex}; use std::sync::{Arc, Weak, Mutex};
use std::ops::Deref; use std::ops::Deref;
@ -31,6 +34,7 @@ use ethcore::views::*;
use ethcore::ethereum::Ethash; use ethcore::ethereum::Ethash;
use ethcore::ethereum::denominations::shannon; use ethcore::ethereum::denominations::shannon;
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
use self::ethash::SeedHashCompute;
use v1::traits::{Eth, EthFilter}; use v1::traits::{Eth, EthFilter};
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, CallRequest, OptionalValue, Index, Filter, Log, Receipt}; use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, CallRequest, OptionalValue, Index, Filter, Log, Receipt};
use v1::helpers::{PollFilter, PollManager, ExternalMinerService, ExternalMiner}; use v1::helpers::{PollFilter, PollManager, ExternalMinerService, ExternalMiner};
@ -56,6 +60,7 @@ pub struct EthClient<C, S, A, M, EM = ExternalMiner>
accounts: Weak<A>, accounts: Weak<A>,
miner: Weak<M>, miner: Weak<M>,
external_miner: EM, external_miner: EM,
seed_compute: Mutex<SeedHashCompute>,
} }
impl<C, S, A, M> EthClient<C, S, A, M, ExternalMiner> impl<C, S, A, M> EthClient<C, S, A, M, ExternalMiner>
@ -86,6 +91,7 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM>
miner: Arc::downgrade(miner), miner: Arc::downgrade(miner),
accounts: Arc::downgrade(accounts), accounts: Arc::downgrade(accounts),
external_miner: em, external_miner: em,
seed_compute: Mutex::new(SeedHashCompute::new()),
} }
} }
@ -413,7 +419,8 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
Some(ref b) => { Some(ref b) => {
let pow_hash = b.hash(); let pow_hash = b.hash();
let target = Ethash::difficulty_to_boundary(b.block().header().difficulty()); 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)) to_value(&(pow_hash, seed_hash, target))
} }
_ => Err(Error::internal_error()) _ => Err(Error::internal_error())