openethereum/ethash/src/lib.rs

162 lines
4.7 KiB
Rust
Raw Normal View History

// Copyright 2015-2017 Parity Technologies (UK) Ltd.
2016-02-05 13:40:41 +01:00
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
2016-01-17 12:00:34 +01:00
//! Ethash implementation
//! See https://github.com/ethereum/wiki/wiki/Ethash
2017-07-22 14:49:02 +02:00
#![cfg_attr(feature = "benches", feature(test))]
extern crate primal;
2016-01-18 12:49:39 +01:00
extern crate sha3;
extern crate parking_lot;
2017-07-18 15:38:10 +02:00
#[macro_use]
extern crate crunchy;
2016-01-30 23:02:55 +01:00
#[macro_use]
extern crate log;
2016-01-17 12:00:34 +01:00
mod compute;
2016-02-18 22:35:14 +01:00
use std::mem;
use std::path::{Path, PathBuf};
2016-01-17 12:00:34 +01:00
use compute::Light;
pub use compute::{ETHASH_EPOCH_LENGTH, H256, ProofOfWork, SeedHashCompute, quick_get_difficulty, slow_get_seedhash};
2016-01-17 12:00:34 +01:00
use std::sync::Arc;
use parking_lot::Mutex;
2016-01-17 12:00:34 +01:00
2016-02-18 22:35:14 +01:00
struct LightCache {
recent_epoch: Option<u64>,
recent: Option<Arc<Light>>,
prev_epoch: Option<u64>,
prev: Option<Arc<Light>>,
}
2016-02-29 19:30:13 +01:00
/// Light/Full cache manager.
2016-01-17 12:00:34 +01:00
pub struct EthashManager {
2016-02-18 22:35:14 +01:00
cache: Mutex<LightCache>,
cache_dir: PathBuf,
2016-01-17 12:00:34 +01:00
}
impl EthashManager {
/// Create a new new instance of ethash manager
pub fn new<T: AsRef<Path>>(cache_dir: T) -> EthashManager {
EthashManager {
cache_dir: cache_dir.as_ref().to_path_buf(),
2016-02-18 22:35:14 +01:00
cache: Mutex::new(LightCache {
recent_epoch: None,
recent: None,
prev_epoch: None,
prev: None,
}),
2016-01-17 12:00:34 +01:00
}
}
/// Calculate the light client data
/// `block_number` - Block number to check
/// `light` - The light client handler
/// `header_hash` - The header hash to pack into the mix
/// `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;
2016-01-30 23:02:55 +01:00
let light = {
let mut lights = self.cache.lock();
2016-02-18 22:35:14 +01:00
let light = match lights.recent_epoch.clone() {
Some(ref e) if *e == epoch => lights.recent.clone(),
_ => match lights.prev_epoch.clone() {
Some(e) if e == epoch => {
// don't swap if recent is newer.
if lights.recent_epoch > lights.prev_epoch {
None
} else {
// swap
let t = lights.prev_epoch;
lights.prev_epoch = lights.recent_epoch;
lights.recent_epoch = t;
let t = lights.prev.clone();
lights.prev = lights.recent.clone();
lights.recent = t;
lights.recent.clone()
}
2016-02-18 22:35:14 +01:00
}
_ => None,
2016-04-13 00:06:02 +02:00
},
2016-02-18 22:35:14 +01:00
};
match light {
2016-01-30 23:02:55 +01:00
None => {
let light = match Light::from_file(&self.cache_dir, block_number) {
2016-01-30 23:02:55 +01:00
Ok(light) => Arc::new(light),
Err(e) => {
2016-01-30 23:02:55 +01:00
debug!("Light cache file not found for {}:{}", block_number, e);
let light = Light::new(&self.cache_dir, block_number);
2016-01-30 23:02:55 +01:00
if let Err(e) = light.to_file() {
warn!("Light cache file write error: {}", e);
}
Arc::new(light)
}
};
2016-02-18 22:35:14 +01:00
lights.prev_epoch = mem::replace(&mut lights.recent_epoch, Some(epoch));
lights.prev = mem::replace(&mut lights.recent, Some(light.clone()));
2016-01-30 23:02:55 +01:00
light
2016-01-21 16:48:37 +01:00
}
2016-04-13 00:06:02 +02:00
Some(light) => light,
2016-01-17 12:00:34 +01:00
}
2016-01-30 23:02:55 +01:00
};
light.compute(header_hash, nonce)
2016-01-17 12:00:34 +01:00
}
}
2016-02-18 22:35:14 +01:00
#[test]
fn test_lru() {
let ethash = EthashManager::new(&::std::env::temp_dir());
2016-02-18 22:35:14 +01:00
let hash = [0u8; 32];
ethash.compute_light(1, &hash, 1);
ethash.compute_light(50000, &hash, 1);
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 1);
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
2016-02-18 22:35:14 +01:00
ethash.compute_light(1, &hash, 1);
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 0);
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 1);
2016-02-18 22:35:14 +01:00
ethash.compute_light(70000, &hash, 1);
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 2);
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
2016-02-18 22:35:14 +01:00
}
2017-07-18 14:14:42 +02:00
#[cfg(feature = "benches")]
mod benchmarks {
extern crate test;
use compute::{Light, light_compute, SeedHashCompute};
use self::test::Bencher;
#[bench]
fn bench_light_compute(b: &mut Bencher) {
use ::std::env;
2017-07-18 14:14:42 +02:00
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];
let nonce = 0xd7b3ac70a301a249;
let light = Light::new(env::temp_dir(), 486382);
2017-07-18 14:14:42 +02:00
b.iter(|| light_compute(&light, &hash, nonce));
}
#[bench]
fn bench_seedhash(b: &mut Bencher) {
let seed_compute = SeedHashCompute::new();
b.iter(|| seed_compute.get_seedhash(486382));
}
}