Use memmap for dag cache (#6193)
* Rebase and fix compilation errors (tests not yet fixed) * Use `debug_assert` over `assert` * Fix tests * Assert safety, clean up * Fix up stale cache removal, move one assert to debug_assert * Remove printlns * Add licenses * Fix benches * Inline some no-ops in a hot loop that weren't being inlined * Add spooky comment to make sure no-one removes the inlining annotations * Minor cleanup * Add option to switch between mmap and ram * Flag ethash to use less memory when running light client * Fix tests * Remove todo comment (it's done) * Replace assertion with error return * Fix indentation * Use union instead of `transmute` * Fix benches * Extract to constants * Clean up and fix soundness holes * Fix formatting * Ignore missing-file errors * Make incorrect cache size an error condition instead of a panic, remove dead code * Fix compilation errors from rebase * Fix compilation errors in tests * Fix compilation errors in tests
This commit is contained in:
parent
70be064aa5
commit
5c08698fa0
526
Cargo.lock
generated
526
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,8 @@ hash = { path = "../util/hash" }
|
|||||||
primal = "0.2.3"
|
primal = "0.2.3"
|
||||||
parking_lot = "0.4"
|
parking_lot = "0.4"
|
||||||
crunchy = "0.1.0"
|
crunchy = "0.1.0"
|
||||||
|
memmap = "0.5.2"
|
||||||
|
either = "1.0.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
benches = []
|
benches = []
|
||||||
|
352
ethash/src/cache.rs
Normal file
352
ethash/src/cache.rs
Normal file
@ -0,0 +1,352 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// 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/>.
|
||||||
|
|
||||||
|
use compute::Light;
|
||||||
|
use either::Either;
|
||||||
|
use keccak::{H256, keccak_512};
|
||||||
|
use memmap::{Mmap, Protection};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use seed_compute::SeedHashCompute;
|
||||||
|
|
||||||
|
use shared::{ETHASH_CACHE_ROUNDS, NODE_BYTES, NODE_DWORDS, Node, epoch, get_cache_size, to_hex};
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::fs;
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::slice;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
type Cache = Either<Vec<Node>, Mmap>;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||||
|
pub enum OptimizeFor {
|
||||||
|
Cpu,
|
||||||
|
Memory,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OptimizeFor {
|
||||||
|
fn default() -> Self {
|
||||||
|
OptimizeFor::Cpu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn byte_size(cache: &Cache) -> usize {
|
||||||
|
use self::Either::{Left, Right};
|
||||||
|
|
||||||
|
match *cache {
|
||||||
|
Left(ref vec) => vec.len() * NODE_BYTES,
|
||||||
|
Right(ref mmap) => mmap.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_buffer(path: &Path, num_nodes: usize, ident: &H256, optimize_for: OptimizeFor) -> Cache {
|
||||||
|
let memmap = match optimize_for {
|
||||||
|
OptimizeFor::Cpu => None,
|
||||||
|
OptimizeFor::Memory => make_memmapped_cache(path, num_nodes, ident).ok(),
|
||||||
|
};
|
||||||
|
|
||||||
|
memmap.map(Either::Right).unwrap_or_else(|| {
|
||||||
|
Either::Left(make_memory_cache(num_nodes, ident))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct NodeCacheBuilder {
|
||||||
|
// TODO: Remove this locking and just use an `Rc`?
|
||||||
|
seedhash: Arc<Mutex<SeedHashCompute>>,
|
||||||
|
optimize_for: OptimizeFor,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Abstract the "optimize for" logic
|
||||||
|
pub struct NodeCache {
|
||||||
|
builder: NodeCacheBuilder,
|
||||||
|
cache_dir: Cow<'static, Path>,
|
||||||
|
cache_path: PathBuf,
|
||||||
|
epoch: u64,
|
||||||
|
cache: Cache,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeCacheBuilder {
|
||||||
|
pub fn light(&self, cache_dir: &Path, block_number: u64) -> Light {
|
||||||
|
Light::new_with_builder(self, cache_dir, block_number)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn light_from_file(&self, cache_dir: &Path, block_number: u64) -> io::Result<Light> {
|
||||||
|
Light::from_file_with_builder(self, cache_dir, block_number)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new<T: Into<Option<OptimizeFor>>>(optimize_for: T) -> Self {
|
||||||
|
NodeCacheBuilder {
|
||||||
|
seedhash: Arc::new(Mutex::new(SeedHashCompute::new())),
|
||||||
|
optimize_for: optimize_for.into().unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_number_to_ident(&self, block_number: u64) -> H256 {
|
||||||
|
self.seedhash.lock().hash_block_number(block_number)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn epoch_to_ident(&self, epoch: u64) -> H256 {
|
||||||
|
self.seedhash.lock().hash_epoch(epoch)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_file<P: Into<Cow<'static, Path>>>(
|
||||||
|
&self,
|
||||||
|
cache_dir: P,
|
||||||
|
block_number: u64,
|
||||||
|
) -> io::Result<NodeCache> {
|
||||||
|
let cache_dir = cache_dir.into();
|
||||||
|
let ident = self.block_number_to_ident(block_number);
|
||||||
|
|
||||||
|
let path = cache_path(cache_dir.as_ref(), &ident);
|
||||||
|
|
||||||
|
let cache = cache_from_path(&path, self.optimize_for)?;
|
||||||
|
let expected_cache_size = get_cache_size(block_number);
|
||||||
|
|
||||||
|
if byte_size(&cache) == expected_cache_size {
|
||||||
|
Ok(NodeCache {
|
||||||
|
builder: self.clone(),
|
||||||
|
epoch: epoch(block_number),
|
||||||
|
cache_dir: cache_dir,
|
||||||
|
cache_path: path,
|
||||||
|
cache: cache,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"Node cache is of incorrect size",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_cache<P: Into<Cow<'static, Path>>>(
|
||||||
|
&self,
|
||||||
|
cache_dir: P,
|
||||||
|
block_number: u64,
|
||||||
|
) -> NodeCache {
|
||||||
|
let cache_dir = cache_dir.into();
|
||||||
|
let ident = self.block_number_to_ident(block_number);
|
||||||
|
|
||||||
|
let cache_size = get_cache_size(block_number);
|
||||||
|
|
||||||
|
// We use `debug_assert` since it is impossible for `get_cache_size` to return an unaligned
|
||||||
|
// value with the current implementation. If the implementation changes, CI will catch it.
|
||||||
|
debug_assert!(cache_size % NODE_BYTES == 0, "Unaligned cache size");
|
||||||
|
let num_nodes = cache_size / NODE_BYTES;
|
||||||
|
|
||||||
|
let path = cache_path(cache_dir.as_ref(), &ident);
|
||||||
|
let nodes = new_buffer(&path, num_nodes, &ident, self.optimize_for);
|
||||||
|
|
||||||
|
NodeCache {
|
||||||
|
builder: self.clone(),
|
||||||
|
epoch: epoch(block_number),
|
||||||
|
cache_dir: cache_dir.into(),
|
||||||
|
cache_path: path,
|
||||||
|
cache: nodes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeCache {
|
||||||
|
pub fn cache_path(&self) -> &Path {
|
||||||
|
&self.cache_path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush(&mut self) -> io::Result<()> {
|
||||||
|
if let Some(last) = self.epoch.checked_sub(2).map(|ep| {
|
||||||
|
cache_path(self.cache_dir.as_ref(), &self.builder.epoch_to_ident(ep))
|
||||||
|
})
|
||||||
|
{
|
||||||
|
fs::remove_file(last).unwrap_or_else(|error| match error.kind() {
|
||||||
|
io::ErrorKind::NotFound => (),
|
||||||
|
_ => warn!("Error removing stale DAG cache: {:?}", error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
consume_cache(&mut self.cache, &self.cache_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_memmapped_cache(path: &Path, num_nodes: usize, ident: &H256) -> io::Result<Mmap> {
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
|
||||||
|
let file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&path)?;
|
||||||
|
file.set_len((num_nodes * NODE_BYTES) as _)?;
|
||||||
|
|
||||||
|
let mut memmap = Mmap::open(&file, Protection::ReadWrite)?;
|
||||||
|
|
||||||
|
unsafe { initialize_memory(memmap.mut_ptr() as *mut Node, num_nodes, ident) };
|
||||||
|
|
||||||
|
Ok(memmap)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_memory_cache(num_nodes: usize, ident: &H256) -> Vec<Node> {
|
||||||
|
let mut nodes: Vec<Node> = Vec::with_capacity(num_nodes);
|
||||||
|
// Use uninit instead of unnecessarily writing `size_of::<Node>() * num_nodes` 0s
|
||||||
|
unsafe {
|
||||||
|
initialize_memory(nodes.as_mut_ptr(), num_nodes, ident);
|
||||||
|
nodes.set_len(num_nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cache_path<'a, P: Into<Cow<'a, Path>>>(path: P, ident: &H256) -> PathBuf {
|
||||||
|
let mut buf = path.into().into_owned();
|
||||||
|
buf.push(to_hex(ident));
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume_cache(cache: &mut Cache, path: &Path) -> io::Result<()> {
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
|
||||||
|
match *cache {
|
||||||
|
Either::Left(ref mut vec) => {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&path)?;
|
||||||
|
|
||||||
|
let buf = unsafe {
|
||||||
|
slice::from_raw_parts_mut(vec.as_mut_ptr() as *mut u8, vec.len() * NODE_BYTES)
|
||||||
|
};
|
||||||
|
|
||||||
|
file.write_all(buf).map(|_| ())
|
||||||
|
}
|
||||||
|
Either::Right(ref mmap) => {
|
||||||
|
mmap.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cache_from_path(path: &Path, optimize_for: OptimizeFor) -> io::Result<Cache> {
|
||||||
|
let memmap = match optimize_for {
|
||||||
|
OptimizeFor::Cpu => None,
|
||||||
|
OptimizeFor::Memory => Mmap::open_path(path, Protection::ReadWrite).ok(),
|
||||||
|
};
|
||||||
|
|
||||||
|
memmap.map(Either::Right).ok_or(()).or_else(|_| {
|
||||||
|
read_from_path(path).map(Either::Left)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_from_path(path: &Path) -> io::Result<Vec<Node>> {
|
||||||
|
use std::fs::File;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
let mut file = File::open(path)?;
|
||||||
|
|
||||||
|
let mut nodes: Vec<u8> = Vec::with_capacity(file.metadata().map(|m| m.len() as _).unwrap_or(
|
||||||
|
NODE_BYTES * 1_000_000,
|
||||||
|
));
|
||||||
|
file.read_to_end(&mut nodes)?;
|
||||||
|
|
||||||
|
nodes.shrink_to_fit();
|
||||||
|
|
||||||
|
if nodes.len() % NODE_BYTES != 0 || nodes.capacity() % NODE_BYTES != 0 {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"Node cache is not a multiple of node size",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let out: Vec<Node> = unsafe {
|
||||||
|
Vec::from_raw_parts(
|
||||||
|
nodes.as_mut_ptr() as *mut _,
|
||||||
|
nodes.len() / NODE_BYTES,
|
||||||
|
nodes.capacity() / NODE_BYTES,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
mem::forget(nodes);
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[Node]> for NodeCache {
|
||||||
|
fn as_ref(&self) -> &[Node] {
|
||||||
|
match self.cache {
|
||||||
|
Either::Left(ref vec) => vec,
|
||||||
|
Either::Right(ref mmap) => unsafe {
|
||||||
|
let bytes = mmap.ptr();
|
||||||
|
// This isn't a safety issue, so we can keep this a debug lint. We don't care about
|
||||||
|
// people manually messing with the files unless it can cause unsafety, but if we're
|
||||||
|
// generating incorrect files then we want to catch that in CI.
|
||||||
|
debug_assert_eq!(mmap.len() % NODE_BYTES, 0);
|
||||||
|
slice::from_raw_parts(bytes as _, mmap.len() / NODE_BYTES)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This takes a raw pointer and a counter because `memory` may be uninitialized. `memory` _must_ be
|
||||||
|
// a pointer to the beginning of an allocated but possibly-uninitialized block of
|
||||||
|
// `num_nodes * NODE_BYTES` bytes
|
||||||
|
//
|
||||||
|
// We have to use raw pointers to read/write uninit, using "normal" indexing causes LLVM to freak
|
||||||
|
// out. It counts as a read and causes all writes afterwards to be elided. Yes, really. I know, I
|
||||||
|
// want to refactor this to use less `unsafe` as much as the next rustacean.
|
||||||
|
unsafe fn initialize_memory(memory: *mut Node, num_nodes: usize, ident: &H256) {
|
||||||
|
let dst = memory as *mut u8;
|
||||||
|
|
||||||
|
debug_assert_eq!(ident.len(), 32);
|
||||||
|
keccak_512::unchecked(dst, NODE_BYTES, ident.as_ptr(), ident.len());
|
||||||
|
|
||||||
|
for i in 1..num_nodes {
|
||||||
|
// We use raw pointers here, see above
|
||||||
|
let dst = memory.offset(i as _) as *mut u8;
|
||||||
|
let src = memory.offset(i as isize - 1) as *mut u8;
|
||||||
|
|
||||||
|
keccak_512::unchecked(dst, NODE_BYTES, src, NODE_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now this is initialized, we can treat it as a slice.
|
||||||
|
let nodes: &mut [Node] = slice::from_raw_parts_mut(memory, num_nodes);
|
||||||
|
|
||||||
|
// For `unroll!`, see below. If the literal in `unroll!` is not the same as the RHS here then
|
||||||
|
// these have got out of sync! Don't let this happen!
|
||||||
|
debug_assert_eq!(NODE_DWORDS, 8);
|
||||||
|
|
||||||
|
// This _should_ get unrolled by the compiler, since it's not using the loop variable.
|
||||||
|
for _ in 0..ETHASH_CACHE_ROUNDS {
|
||||||
|
for i in 0..num_nodes {
|
||||||
|
let data_idx = (num_nodes - 1 + i) % num_nodes;
|
||||||
|
let idx = nodes.get_unchecked_mut(i).as_words()[0] as usize % num_nodes;
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
let mut data: Node = nodes.get_unchecked(data_idx).clone();
|
||||||
|
let rhs: &Node = nodes.get_unchecked(idx);
|
||||||
|
|
||||||
|
unroll! {
|
||||||
|
for w in 0..8 {
|
||||||
|
*data.as_dwords_mut().get_unchecked_mut(w) ^=
|
||||||
|
*rhs.as_dwords().get_unchecked(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data
|
||||||
|
};
|
||||||
|
|
||||||
|
keccak_512::write(&data.bytes, &mut nodes.get_unchecked_mut(i).bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -19,30 +19,16 @@
|
|||||||
|
|
||||||
// TODO: fix endianess for big endian
|
// TODO: fix endianess for big endian
|
||||||
|
|
||||||
use primal::is_prime;
|
use keccak::{keccak_512, keccak_256, H256};
|
||||||
use std::cell::Cell;
|
use cache::{NodeCache, NodeCacheBuilder};
|
||||||
|
use seed_compute::SeedHashCompute;
|
||||||
|
use shared::*;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::path::Path;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use hash;
|
|
||||||
use std::slice;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::io::{self, Read, Write};
|
|
||||||
use std::fs::{self, File};
|
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
|
|
||||||
pub const ETHASH_EPOCH_LENGTH: u64 = 30000;
|
|
||||||
pub const ETHASH_CACHE_ROUNDS: usize = 3;
|
|
||||||
pub const ETHASH_MIX_BYTES: usize = 128;
|
|
||||||
pub const ETHASH_ACCESSES: usize = 64;
|
|
||||||
pub const ETHASH_DATASET_PARENTS: u32 = 256;
|
|
||||||
|
|
||||||
const DATASET_BYTES_INIT: u64 = 1 << 30;
|
|
||||||
const DATASET_BYTES_GROWTH: u64 = 1 << 23;
|
|
||||||
const CACHE_BYTES_INIT: u64 = 1 << 24;
|
|
||||||
const CACHE_BYTES_GROWTH: u64 = 1 << 17;
|
|
||||||
const NODE_WORDS: usize = 64 / 4;
|
|
||||||
const NODE_BYTES: usize = 64;
|
|
||||||
const MIX_WORDS: usize = ETHASH_MIX_BYTES / 4;
|
const MIX_WORDS: usize = ETHASH_MIX_BYTES / 4;
|
||||||
const MIX_NODES: usize = MIX_WORDS / NODE_WORDS;
|
const MIX_NODES: usize = MIX_WORDS / NODE_WORDS;
|
||||||
const FNV_PRIME: u32 = 0x01000193;
|
const FNV_PRIME: u32 = 0x01000193;
|
||||||
@ -55,48 +41,24 @@ pub struct ProofOfWork {
|
|||||||
pub mix_hash: H256,
|
pub mix_hash: H256,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Node {
|
|
||||||
bytes: [u8; NODE_BYTES],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Node {
|
|
||||||
fn default() -> Self {
|
|
||||||
Node { bytes: [0u8; NODE_BYTES] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for Node {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Node { bytes: *&self.bytes }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Node {
|
|
||||||
#[inline]
|
|
||||||
fn as_words(&self) -> &[u32; NODE_WORDS] {
|
|
||||||
unsafe { mem::transmute(&self.bytes) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn as_words_mut(&mut self) -> &mut [u32; NODE_WORDS] {
|
|
||||||
unsafe { mem::transmute(&mut self.bytes) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type H256 = [u8; 32];
|
|
||||||
|
|
||||||
pub struct Light {
|
pub struct Light {
|
||||||
cache_dir: PathBuf,
|
|
||||||
block_number: u64,
|
block_number: u64,
|
||||||
cache: Vec<Node>,
|
cache: NodeCache,
|
||||||
seed_compute: Mutex<SeedHashCompute>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Light cache structure
|
/// Light cache structure
|
||||||
impl Light {
|
impl Light {
|
||||||
/// Create a new light cache for a given block number
|
pub fn new_with_builder(
|
||||||
pub fn new<T: AsRef<Path>>(cache_dir: T, block_number: u64) -> Light {
|
builder: &NodeCacheBuilder,
|
||||||
light_new(cache_dir, block_number)
|
cache_dir: &Path,
|
||||||
|
block_number: u64,
|
||||||
|
) -> Self {
|
||||||
|
let cache = builder.new_cache(cache_dir.to_path_buf(), block_number);
|
||||||
|
|
||||||
|
Light {
|
||||||
|
block_number: block_number,
|
||||||
|
cache: cache,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the light boundary data
|
/// Calculate the light boundary data
|
||||||
@ -106,107 +68,25 @@ impl Light {
|
|||||||
light_compute(self, header_hash, nonce)
|
light_compute(self, header_hash, nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_path<T: AsRef<Path>>(cache_dir: T, seed_hash: H256) -> PathBuf {
|
pub fn from_file_with_builder(
|
||||||
let mut cache_dir = cache_dir.as_ref().to_path_buf();
|
builder: &NodeCacheBuilder,
|
||||||
cache_dir.push(to_hex(&seed_hash));
|
cache_dir: &Path,
|
||||||
cache_dir
|
block_number: u64,
|
||||||
}
|
) -> io::Result<Self> {
|
||||||
|
let cache = builder.from_file(cache_dir.to_path_buf(), block_number)?;
|
||||||
pub fn from_file<T: AsRef<Path>>(cache_dir: T, block_number: u64) -> io::Result<Light> {
|
|
||||||
let seed_compute = SeedHashCompute::new();
|
|
||||||
let path = Light::file_path(&cache_dir, seed_compute.get_seedhash(block_number));
|
|
||||||
let mut file = File::open(path)?;
|
|
||||||
|
|
||||||
let cache_size = get_cache_size(block_number);
|
|
||||||
if 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<Node> = Vec::with_capacity(num_nodes);
|
|
||||||
|
|
||||||
unsafe { nodes.set_len(num_nodes) };
|
|
||||||
|
|
||||||
let buf = unsafe { slice::from_raw_parts_mut(nodes.as_mut_ptr() as *mut u8, cache_size) };
|
|
||||||
file.read_exact(buf)?;
|
|
||||||
Ok(Light {
|
Ok(Light {
|
||||||
block_number,
|
block_number: block_number,
|
||||||
cache_dir: cache_dir.as_ref().to_path_buf(),
|
cache: cache,
|
||||||
cache: nodes,
|
|
||||||
seed_compute: Mutex::new(seed_compute),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_file(&self) -> io::Result<PathBuf> {
|
pub fn to_file(&mut self) -> io::Result<&Path> {
|
||||||
let seed_compute = self.seed_compute.lock();
|
self.cache.flush()?;
|
||||||
let path = Light::file_path(&self.cache_dir, seed_compute.get_seedhash(self.block_number));
|
Ok(self.cache.cache_path())
|
||||||
|
|
||||||
if self.block_number >= ETHASH_EPOCH_LENGTH * 2 {
|
|
||||||
let deprecated = Light::file_path(
|
|
||||||
&self.cache_dir,
|
|
||||||
seed_compute.get_seedhash(self.block_number - ETHASH_EPOCH_LENGTH * 2)
|
|
||||||
);
|
|
||||||
|
|
||||||
if deprecated.exists() {
|
|
||||||
debug!(target: "ethash", "removing: {:?}", &deprecated);
|
|
||||||
fs::remove_file(deprecated)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::create_dir_all(path.parent().unwrap())?;
|
|
||||||
let mut file = 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) };
|
|
||||||
file.write(buf)?;
|
|
||||||
Ok(path)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SeedHashCompute {
|
pub fn slow_hash_block_number(block_number: u64) -> H256 {
|
||||||
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 { hash::keccak_256(hash[..].as_mut_ptr(), 32, hash[..].as_ptr(), 32) };
|
|
||||||
}
|
|
||||||
hash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn slow_get_seedhash(block_number: u64) -> H256 {
|
|
||||||
SeedHashCompute::resume_compute_seedhash([0u8; 32], 0, block_number / ETHASH_EPOCH_LENGTH)
|
SeedHashCompute::resume_compute_seedhash([0u8; 32], 0, block_number / ETHASH_EPOCH_LENGTH)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,34 +94,6 @@ fn fnv_hash(x: u32, y: u32) -> u32 {
|
|||||||
return x.wrapping_mul(FNV_PRIME) ^ y;
|
return x.wrapping_mul(FNV_PRIME) ^ y;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keccak_512(input: &[u8], output: &mut [u8]) {
|
|
||||||
unsafe { hash::keccak_512(output.as_mut_ptr(), output.len(), input.as_ptr(), input.len()) };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn keccak_512_inplace(input: &mut [u8]) {
|
|
||||||
// This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This
|
|
||||||
// means that we can reuse the input buffer for both input and output.
|
|
||||||
unsafe { hash::keccak_512(input.as_mut_ptr(), input.len(), input.as_ptr(), input.len()) };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cache_size(block_number: u64) -> usize {
|
|
||||||
let mut sz: u64 = CACHE_BYTES_INIT + CACHE_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH);
|
|
||||||
sz = sz - NODE_BYTES as u64;
|
|
||||||
while !is_prime(sz / NODE_BYTES as u64) {
|
|
||||||
sz = sz - 2 * NODE_BYTES as u64;
|
|
||||||
}
|
|
||||||
sz as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_data_size(block_number: u64) -> usize {
|
|
||||||
let mut sz: u64 = DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH);
|
|
||||||
sz = sz - ETHASH_MIX_BYTES as u64;
|
|
||||||
while !is_prime(sz / ETHASH_MIX_BYTES as u64) {
|
|
||||||
sz = sz - 2 * ETHASH_MIX_BYTES as u64;
|
|
||||||
}
|
|
||||||
sz as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Difficulty quick check for POW preverification
|
/// Difficulty quick check for POW preverification
|
||||||
///
|
///
|
||||||
/// `header_hash` The hash of the header
|
/// `header_hash` The hash of the header
|
||||||
@ -261,12 +113,12 @@ pub fn quick_get_difficulty(header_hash: &H256, nonce: u64, mix_hash: &H256) ->
|
|||||||
ptr::copy_nonoverlapping(header_hash.as_ptr(), buf.as_mut_ptr(), 32);
|
ptr::copy_nonoverlapping(header_hash.as_ptr(), buf.as_mut_ptr(), 32);
|
||||||
ptr::copy_nonoverlapping(mem::transmute(&nonce), buf[32..].as_mut_ptr(), 8);
|
ptr::copy_nonoverlapping(mem::transmute(&nonce), buf[32..].as_mut_ptr(), 8);
|
||||||
|
|
||||||
hash::keccak_512(buf.as_mut_ptr(), 64, buf.as_ptr(), 40);
|
keccak_512::unchecked(buf.as_mut_ptr(), 64, buf.as_ptr(), 40);
|
||||||
ptr::copy_nonoverlapping(mix_hash.as_ptr(), buf[64..].as_mut_ptr(), 32);
|
ptr::copy_nonoverlapping(mix_hash.as_ptr(), buf[64..].as_mut_ptr(), 32);
|
||||||
|
|
||||||
// This is initialized in `keccak_256`
|
// This is initialized in `keccak_256`
|
||||||
let mut hash: [u8; 32] = mem::uninitialized();
|
let mut hash: [u8; 32] = mem::uninitialized();
|
||||||
hash::keccak_256(hash.as_mut_ptr(), hash.len(), buf.as_ptr(), buf.len());
|
keccak_256::unchecked(hash.as_mut_ptr(), hash.len(), buf.as_ptr(), buf.len());
|
||||||
|
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
@ -324,11 +176,7 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
|
|||||||
// leaving it fully initialized.
|
// leaving it fully initialized.
|
||||||
let mut out: [u8; NODE_BYTES] = mem::uninitialized();
|
let mut out: [u8; NODE_BYTES] = mem::uninitialized();
|
||||||
|
|
||||||
ptr::copy_nonoverlapping(
|
ptr::copy_nonoverlapping(header_hash.as_ptr(), out.as_mut_ptr(), header_hash.len());
|
||||||
header_hash.as_ptr(),
|
|
||||||
out.as_mut_ptr(),
|
|
||||||
header_hash.len(),
|
|
||||||
);
|
|
||||||
ptr::copy_nonoverlapping(
|
ptr::copy_nonoverlapping(
|
||||||
mem::transmute(&nonce),
|
mem::transmute(&nonce),
|
||||||
out[header_hash.len()..].as_mut_ptr(),
|
out[header_hash.len()..].as_mut_ptr(),
|
||||||
@ -336,11 +184,11 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
|
|||||||
);
|
);
|
||||||
|
|
||||||
// compute keccak-512 hash and replicate across mix
|
// compute keccak-512 hash and replicate across mix
|
||||||
hash::keccak_512(
|
keccak_512::unchecked(
|
||||||
out.as_mut_ptr(),
|
out.as_mut_ptr(),
|
||||||
NODE_BYTES,
|
NODE_BYTES,
|
||||||
out.as_ptr(),
|
out.as_ptr(),
|
||||||
header_hash.len() + mem::size_of::<u64>()
|
header_hash.len() + mem::size_of::<u64>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Node { bytes: out }
|
Node { bytes: out }
|
||||||
@ -354,7 +202,7 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
|
|||||||
let page_size = 4 * MIX_WORDS;
|
let page_size = 4 * MIX_WORDS;
|
||||||
let num_full_pages = (full_size / page_size) as u32;
|
let num_full_pages = (full_size / page_size) as u32;
|
||||||
// deref once for better performance
|
// deref once for better performance
|
||||||
let cache: &[Node] = &light.cache;
|
let cache: &[Node] = light.cache.as_ref();
|
||||||
let first_val = buf.half_mix.as_words()[0];
|
let first_val = buf.half_mix.as_words()[0];
|
||||||
|
|
||||||
debug_assert_eq!(MIX_NODES, 2);
|
debug_assert_eq!(MIX_NODES, 2);
|
||||||
@ -364,14 +212,10 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
|
|||||||
let index = {
|
let index = {
|
||||||
// This is trivially safe, but does not work on big-endian. The safety of this is
|
// This is trivially safe, but does not work on big-endian. The safety of this is
|
||||||
// asserted in debug builds (see the definition of `make_const_array!`).
|
// asserted in debug builds (see the definition of `make_const_array!`).
|
||||||
let mix_words: &mut [u32; MIX_WORDS] = unsafe {
|
let mix_words: &mut [u32; MIX_WORDS] =
|
||||||
make_const_array!(MIX_WORDS, &mut mix)
|
unsafe { make_const_array!(MIX_WORDS, &mut mix) };
|
||||||
};
|
|
||||||
|
|
||||||
fnv_hash(
|
fnv_hash(first_val ^ i, mix_words[i as usize % MIX_WORDS]) % num_full_pages
|
||||||
first_val ^ i,
|
|
||||||
mix_words[i as usize % MIX_WORDS]
|
|
||||||
) % num_full_pages
|
|
||||||
};
|
};
|
||||||
|
|
||||||
unroll! {
|
unroll! {
|
||||||
@ -403,9 +247,8 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
|
|||||||
// times and set each index, leaving the array fully initialized. THIS ONLY WORKS ON LITTLE-
|
// times and set each index, leaving the array fully initialized. THIS ONLY WORKS ON LITTLE-
|
||||||
// ENDIAN MACHINES. See a future PR to make this and the rest of the code work correctly on
|
// ENDIAN MACHINES. See a future PR to make this and the rest of the code work correctly on
|
||||||
// big-endian arches like mips.
|
// big-endian arches like mips.
|
||||||
let mut compress: &mut [u32; MIX_WORDS / 4] = unsafe {
|
let compress: &mut [u32; MIX_WORDS / 4] =
|
||||||
make_const_array!(MIX_WORDS / 4, &mut buf.compress_bytes)
|
unsafe { make_const_array!(MIX_WORDS / 4, &mut buf.compress_bytes) };
|
||||||
};
|
|
||||||
|
|
||||||
// Compress mix
|
// Compress mix
|
||||||
debug_assert_eq!(MIX_WORDS / 4, 8);
|
debug_assert_eq!(MIX_WORDS / 4, 8);
|
||||||
@ -430,7 +273,7 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
|
|||||||
// We overwrite the second half since `keccak_256` has an internal buffer and so allows
|
// We overwrite the second half since `keccak_256` has an internal buffer and so allows
|
||||||
// overlapping arrays as input.
|
// overlapping arrays as input.
|
||||||
let write_ptr: *mut u8 = mem::transmute(&mut buf.compress_bytes);
|
let write_ptr: *mut u8 = mem::transmute(&mut buf.compress_bytes);
|
||||||
hash::keccak_256(
|
keccak_256::unchecked(
|
||||||
write_ptr,
|
write_ptr,
|
||||||
buf.compress_bytes.len(),
|
buf.compress_bytes.len(),
|
||||||
read_ptr,
|
read_ptr,
|
||||||
@ -439,25 +282,21 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
|
|||||||
buf.compress_bytes
|
buf.compress_bytes
|
||||||
};
|
};
|
||||||
|
|
||||||
ProofOfWork {
|
ProofOfWork { mix_hash: mix_hash, value: value }
|
||||||
mix_hash: mix_hash,
|
|
||||||
value: value,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Use the `simd` crate
|
||||||
fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node {
|
fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node {
|
||||||
let num_parent_nodes = cache.len();
|
let num_parent_nodes = cache.len();
|
||||||
let mut ret = cache[node_index as usize % num_parent_nodes].clone();
|
let mut ret = cache[node_index as usize % num_parent_nodes].clone();
|
||||||
ret.as_words_mut()[0] ^= node_index;
|
ret.as_words_mut()[0] ^= node_index;
|
||||||
|
|
||||||
keccak_512_inplace(&mut ret.bytes);
|
keccak_512::inplace(ret.as_bytes_mut());
|
||||||
|
|
||||||
debug_assert_eq!(NODE_WORDS, 16);
|
debug_assert_eq!(NODE_WORDS, 16);
|
||||||
for i in 0..ETHASH_DATASET_PARENTS as u32 {
|
for i in 0..ETHASH_DATASET_PARENTS as u32 {
|
||||||
let parent_index = fnv_hash(
|
let parent_index = fnv_hash(node_index ^ i, ret.as_words()[i as usize % NODE_WORDS]) %
|
||||||
node_index ^ i,
|
num_parent_nodes as u32;
|
||||||
ret.as_words()[i as usize % NODE_WORDS],
|
|
||||||
) % num_parent_nodes as u32;
|
|
||||||
let parent = &cache[parent_index as usize];
|
let parent = &cache[parent_index as usize];
|
||||||
|
|
||||||
unroll! {
|
unroll! {
|
||||||
@ -467,69 +306,18 @@ fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
keccak_512_inplace(&mut ret.bytes);
|
keccak_512::inplace(ret.as_bytes_mut());
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn light_new<T: AsRef<Path>>(cache_dir: T, block_number: u64) -> Light {
|
#[cfg(test)]
|
||||||
let seed_compute = SeedHashCompute::new();
|
mod test {
|
||||||
let seedhash = seed_compute.get_seedhash(block_number);
|
use super::*;
|
||||||
let cache_size = get_cache_size(block_number);
|
use std::fs;
|
||||||
|
|
||||||
assert!(cache_size % NODE_BYTES == 0, "Unaligned cache size");
|
#[test]
|
||||||
let num_nodes = cache_size / NODE_BYTES;
|
fn test_get_cache_size() {
|
||||||
|
|
||||||
let mut nodes: Vec<Node> = Vec::with_capacity(num_nodes);
|
|
||||||
unsafe {
|
|
||||||
// Use uninit instead of unnecessarily writing `size_of::<Node>() * num_nodes` 0s
|
|
||||||
nodes.set_len(num_nodes);
|
|
||||||
|
|
||||||
keccak_512(&seedhash[0..32], &mut nodes.get_unchecked_mut(0).bytes);
|
|
||||||
for i in 1..num_nodes {
|
|
||||||
hash::keccak_512(nodes.get_unchecked_mut(i).bytes.as_mut_ptr(), NODE_BYTES, nodes.get_unchecked(i - 1).bytes.as_ptr(), NODE_BYTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_assert_eq!(NODE_WORDS, 16);
|
|
||||||
|
|
||||||
// This _should_ get unrolled by the compiler, since it's not using the loop variable.
|
|
||||||
for _ in 0..ETHASH_CACHE_ROUNDS {
|
|
||||||
for i in 0..num_nodes {
|
|
||||||
let idx = *nodes.get_unchecked_mut(i).as_words().get_unchecked(0) as usize % num_nodes;
|
|
||||||
let mut data = nodes.get_unchecked((num_nodes - 1 + i) % num_nodes).clone();
|
|
||||||
|
|
||||||
unroll! {
|
|
||||||
for w in 0..16 {
|
|
||||||
*data.as_words_mut().get_unchecked_mut(w) ^= *nodes.get_unchecked(idx).as_words().get_unchecked(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
keccak_512(&data.bytes, &mut nodes.get_unchecked_mut(i).bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Light {
|
|
||||||
block_number,
|
|
||||||
cache_dir: cache_dir.as_ref().to_path_buf(),
|
|
||||||
cache: nodes,
|
|
||||||
seed_compute: Mutex::new(seed_compute),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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_get_cache_size() {
|
|
||||||
// https://github.com/ethereum/wiki/wiki/Ethash/ef6b93f9596746a088ea95d01ca2778be43ae68f#data-sizes
|
// https://github.com/ethereum/wiki/wiki/Ethash/ef6b93f9596746a088ea95d01ca2778be43ae68f#data-sizes
|
||||||
assert_eq!(16776896usize, get_cache_size(0));
|
assert_eq!(16776896usize, get_cache_size(0));
|
||||||
assert_eq!(16776896usize, get_cache_size(1));
|
assert_eq!(16776896usize, get_cache_size(1));
|
||||||
@ -539,10 +327,10 @@ fn test_get_cache_size() {
|
|||||||
assert_eq!(284950208usize, get_cache_size(2046 * ETHASH_EPOCH_LENGTH));
|
assert_eq!(284950208usize, get_cache_size(2046 * ETHASH_EPOCH_LENGTH));
|
||||||
assert_eq!(285081536usize, get_cache_size(2047 * ETHASH_EPOCH_LENGTH));
|
assert_eq!(285081536usize, get_cache_size(2047 * ETHASH_EPOCH_LENGTH));
|
||||||
assert_eq!(285081536usize, get_cache_size(2048 * ETHASH_EPOCH_LENGTH - 1));
|
assert_eq!(285081536usize, get_cache_size(2048 * ETHASH_EPOCH_LENGTH - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_data_size() {
|
fn test_get_data_size() {
|
||||||
// https://github.com/ethereum/wiki/wiki/Ethash/ef6b93f9596746a088ea95d01ca2778be43ae68f#data-sizes
|
// https://github.com/ethereum/wiki/wiki/Ethash/ef6b93f9596746a088ea95d01ca2778be43ae68f#data-sizes
|
||||||
assert_eq!(1073739904usize, get_data_size(0));
|
assert_eq!(1073739904usize, get_data_size(0));
|
||||||
assert_eq!(1073739904usize, get_data_size(1));
|
assert_eq!(1073739904usize, get_data_size(1));
|
||||||
@ -551,75 +339,74 @@ fn test_get_data_size() {
|
|||||||
assert_eq!(1082130304usize, get_data_size(ETHASH_EPOCH_LENGTH + 1));
|
assert_eq!(1082130304usize, get_data_size(ETHASH_EPOCH_LENGTH + 1));
|
||||||
assert_eq!(18236833408usize, get_data_size(2046 * ETHASH_EPOCH_LENGTH));
|
assert_eq!(18236833408usize, get_data_size(2046 * ETHASH_EPOCH_LENGTH));
|
||||||
assert_eq!(18245220736usize, get_data_size(2047 * ETHASH_EPOCH_LENGTH));
|
assert_eq!(18245220736usize, get_data_size(2047 * ETHASH_EPOCH_LENGTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_difficulty_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];
|
let hash = [
|
||||||
let mix_hash = [0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a, 0x64, 0x31, 0xab, 0x6d];
|
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 mix_hash = [
|
||||||
|
0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce,
|
||||||
|
0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a,
|
||||||
|
0x64, 0x31, 0xab, 0x6d,
|
||||||
|
];
|
||||||
let nonce = 0xd7b3ac70a301a249;
|
let nonce = 0xd7b3ac70a301a249;
|
||||||
let boundary_good = [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, 0xe9, 0x7e, 0x53, 0x84];
|
let boundary_good = [
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2,
|
||||||
|
0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a,
|
||||||
|
0xe9, 0x7e, 0x53, 0x84,
|
||||||
|
];
|
||||||
assert_eq!(quick_get_difficulty(&hash, nonce, &mix_hash)[..], boundary_good[..]);
|
assert_eq!(quick_get_difficulty(&hash, nonce, &mix_hash)[..], boundary_good[..]);
|
||||||
let boundary_bad = [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, 0xe9, 0x7e, 0x53, 0x84];
|
let boundary_bad = [
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2,
|
||||||
|
0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a,
|
||||||
|
0xe9, 0x7e, 0x53, 0x84,
|
||||||
|
];
|
||||||
assert!(quick_get_difficulty(&hash, nonce, &mix_hash)[..] != boundary_bad[..]);
|
assert!(quick_get_difficulty(&hash, nonce, &mix_hash)[..] != boundary_bad[..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_light_compute() {
|
fn test_light_compute() {
|
||||||
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 hash = [
|
||||||
let mix_hash = [0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a, 0x64, 0x31, 0xab, 0x6d];
|
0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3,
|
||||||
let boundary = [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, 0xe9, 0x7e, 0x53, 0x84];
|
0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94,
|
||||||
|
0x05, 0x52, 0x7d, 0x72,
|
||||||
|
];
|
||||||
|
let mix_hash = [
|
||||||
|
0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce,
|
||||||
|
0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a,
|
||||||
|
0x64, 0x31, 0xab, 0x6d,
|
||||||
|
];
|
||||||
|
let boundary = [
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2,
|
||||||
|
0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a,
|
||||||
|
0xe9, 0x7e, 0x53, 0x84,
|
||||||
|
];
|
||||||
let nonce = 0xd7b3ac70a301a249;
|
let nonce = 0xd7b3ac70a301a249;
|
||||||
// difficulty = 0x085657254bd9u64;
|
// difficulty = 0x085657254bd9u64;
|
||||||
let light = Light::new(&::std::env::temp_dir(), 486382);
|
let light = NodeCacheBuilder::new(None).light(&::std::env::temp_dir(), 486382);
|
||||||
let result = light_compute(&light, &hash, nonce);
|
let result = light_compute(&light, &hash, nonce);
|
||||||
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]
|
#[test]
|
||||||
fn test_seed_compute_once() {
|
fn test_drop_old_data() {
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_drop_old_data() {
|
|
||||||
let path = ::std::env::temp_dir();
|
let path = ::std::env::temp_dir();
|
||||||
let first = Light::new(&path, 0).to_file().unwrap();
|
let builder = NodeCacheBuilder::new(None);
|
||||||
|
let first = builder.light(&path, 0).to_file().unwrap().to_owned();
|
||||||
|
|
||||||
let second = Light::new(&path, ETHASH_EPOCH_LENGTH).to_file().unwrap();
|
let second = builder.light(&path, ETHASH_EPOCH_LENGTH).to_file().unwrap().to_owned();
|
||||||
assert!(fs::metadata(&first).is_ok());
|
assert!(fs::metadata(&first).is_ok());
|
||||||
|
|
||||||
let _ = Light::new(&path, ETHASH_EPOCH_LENGTH * 2).to_file();
|
let _ = builder.light(&path, ETHASH_EPOCH_LENGTH * 2).to_file();
|
||||||
assert!(fs::metadata(&first).is_err());
|
assert!(fs::metadata(&first).is_err());
|
||||||
assert!(fs::metadata(&second).is_ok());
|
assert!(fs::metadata(&second).is_ok());
|
||||||
|
|
||||||
let _ = Light::new(&path, ETHASH_EPOCH_LENGTH * 3).to_file();
|
let _ = builder.light(&path, ETHASH_EPOCH_LENGTH * 3).to_file();
|
||||||
assert!(fs::metadata(&second).is_err());
|
assert!(fs::metadata(&second).is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
52
ethash/src/keccak.rs
Normal file
52
ethash/src/keccak.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// 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/>.
|
||||||
|
|
||||||
|
extern crate hash;
|
||||||
|
|
||||||
|
pub type H256 = [u8; 32];
|
||||||
|
|
||||||
|
pub mod keccak_512 {
|
||||||
|
use super::hash;
|
||||||
|
|
||||||
|
pub use self::hash::keccak_512 as unchecked;
|
||||||
|
|
||||||
|
pub fn write(input: &[u8], output: &mut [u8]) {
|
||||||
|
unsafe { hash::keccak_512(output.as_mut_ptr(), output.len(), input.as_ptr(), input.len()) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inplace(input: &mut [u8]) {
|
||||||
|
// This is safe since `sha3_*` uses an internal buffer and copies the result to the output. This
|
||||||
|
// means that we can reuse the input buffer for both input and output.
|
||||||
|
unsafe { hash::keccak_512(input.as_mut_ptr(), input.len(), input.as_ptr(), input.len()) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod keccak_256 {
|
||||||
|
use super::hash;
|
||||||
|
|
||||||
|
pub use self::hash::keccak_256 as unchecked;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn write(input: &[u8], output: &mut [u8]) {
|
||||||
|
unsafe { hash::keccak_256(output.as_mut_ptr(), output.len(), input.as_ptr(), input.len()) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inplace(input: &mut [u8]) {
|
||||||
|
// This is safe since `sha3_*` uses an internal buffer and copies the result to the output. This
|
||||||
|
// means that we can reuse the input buffer for both input and output.
|
||||||
|
unsafe { hash::keccak_256(input.as_mut_ptr(), input.len(), input.as_ptr(), input.len()) };
|
||||||
|
}
|
||||||
|
}
|
@ -14,28 +14,35 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Ethash implementation
|
|
||||||
//! See https://github.com/ethereum/wiki/wiki/Ethash
|
|
||||||
|
|
||||||
#![cfg_attr(feature = "benches", feature(test))]
|
#![cfg_attr(feature = "benches", feature(test))]
|
||||||
|
|
||||||
extern crate primal;
|
extern crate primal;
|
||||||
extern crate hash;
|
|
||||||
extern crate parking_lot;
|
extern crate parking_lot;
|
||||||
|
extern crate either;
|
||||||
|
extern crate memmap;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate crunchy;
|
extern crate crunchy;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
mod compute;
|
|
||||||
|
|
||||||
|
mod compute;
|
||||||
|
mod seed_compute;
|
||||||
|
mod cache;
|
||||||
|
mod keccak;
|
||||||
|
mod shared;
|
||||||
|
|
||||||
|
pub use cache::{NodeCacheBuilder, OptimizeFor};
|
||||||
|
pub use compute::{ProofOfWork, quick_get_difficulty, slow_hash_block_number};
|
||||||
|
use compute::Light;
|
||||||
|
use keccak::H256;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
pub use seed_compute::SeedHashCompute;
|
||||||
|
pub use shared::ETHASH_EPOCH_LENGTH;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use compute::Light;
|
|
||||||
pub use compute::{ETHASH_EPOCH_LENGTH, H256, ProofOfWork, SeedHashCompute, quick_get_difficulty, slow_get_seedhash};
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use parking_lot::Mutex;
|
|
||||||
|
|
||||||
struct LightCache {
|
struct LightCache {
|
||||||
recent_epoch: Option<u64>,
|
recent_epoch: Option<u64>,
|
||||||
@ -46,15 +53,17 @@ struct LightCache {
|
|||||||
|
|
||||||
/// Light/Full cache manager.
|
/// Light/Full cache manager.
|
||||||
pub struct EthashManager {
|
pub struct EthashManager {
|
||||||
|
nodecache_builder: NodeCacheBuilder,
|
||||||
cache: Mutex<LightCache>,
|
cache: Mutex<LightCache>,
|
||||||
cache_dir: PathBuf,
|
cache_dir: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EthashManager {
|
impl EthashManager {
|
||||||
/// Create a new new instance of ethash manager
|
/// Create a new new instance of ethash manager
|
||||||
pub fn new<T: AsRef<Path>>(cache_dir: T) -> EthashManager {
|
pub fn new<T: Into<Option<OptimizeFor>>>(cache_dir: &Path, optimize_for: T) -> EthashManager {
|
||||||
EthashManager {
|
EthashManager {
|
||||||
cache_dir: cache_dir.as_ref().to_path_buf(),
|
cache_dir: cache_dir.to_path_buf(),
|
||||||
|
nodecache_builder: NodeCacheBuilder::new(optimize_for.into().unwrap_or_default()),
|
||||||
cache: Mutex::new(LightCache {
|
cache: Mutex::new(LightCache {
|
||||||
recent_epoch: None,
|
recent_epoch: None,
|
||||||
recent: None,
|
recent: None,
|
||||||
@ -96,11 +105,19 @@ impl EthashManager {
|
|||||||
};
|
};
|
||||||
match light {
|
match light {
|
||||||
None => {
|
None => {
|
||||||
let light = match Light::from_file(&self.cache_dir, block_number) {
|
let light = match Light::from_file_with_builder(
|
||||||
|
&self.nodecache_builder,
|
||||||
|
&self.cache_dir,
|
||||||
|
block_number,
|
||||||
|
) {
|
||||||
Ok(light) => Arc::new(light),
|
Ok(light) => Arc::new(light),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!("Light cache file not found for {}:{}", block_number, e);
|
debug!("Light cache file not found for {}:{}", block_number, e);
|
||||||
let light = Light::new(&self.cache_dir, block_number);
|
let mut light = Light::new_with_builder(
|
||||||
|
&self.nodecache_builder,
|
||||||
|
&self.cache_dir,
|
||||||
|
block_number,
|
||||||
|
);
|
||||||
if let Err(e) = light.to_file() {
|
if let Err(e) = light.to_file() {
|
||||||
warn!("Light cache file write error: {}", e);
|
warn!("Light cache file write error: {}", e);
|
||||||
}
|
}
|
||||||
@ -120,7 +137,7 @@ impl EthashManager {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lru() {
|
fn test_lru() {
|
||||||
let ethash = EthashManager::new(&::std::env::temp_dir());
|
let ethash = EthashManager::new(&::std::env::temp_dir(), None);
|
||||||
let hash = [0u8; 32];
|
let hash = [0u8; 32];
|
||||||
ethash.compute_light(1, &hash, 1);
|
ethash.compute_light(1, &hash, 1);
|
||||||
ethash.compute_light(50000, &hash, 1);
|
ethash.compute_light(50000, &hash, 1);
|
||||||
@ -138,24 +155,89 @@ fn test_lru() {
|
|||||||
mod benchmarks {
|
mod benchmarks {
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
use compute::{Light, light_compute, SeedHashCompute};
|
|
||||||
use self::test::Bencher;
|
use self::test::Bencher;
|
||||||
|
use cache::{NodeCacheBuilder, OptimizeFor};
|
||||||
|
use compute::{Light, light_compute};
|
||||||
|
|
||||||
|
const HASH: [u8; 32] = [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];
|
||||||
|
const NONCE: u64 = 0xd7b3ac70a301a249;
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_light_compute(b: &mut Bencher) {
|
fn bench_light_compute_memmap(b: &mut Bencher) {
|
||||||
use ::std::env;
|
use std::env;
|
||||||
|
|
||||||
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 builder = NodeCacheBuilder::new(OptimizeFor::Memory);
|
||||||
let nonce = 0xd7b3ac70a301a249;
|
let light = Light::new_with_builder(&builder, &env::temp_dir(), 486382);
|
||||||
let light = Light::new(env::temp_dir(), 486382);
|
|
||||||
|
|
||||||
b.iter(|| light_compute(&light, &hash, nonce));
|
b.iter(|| light_compute(&light, &HASH, NONCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_seedhash(b: &mut Bencher) {
|
fn bench_light_compute_memory(b: &mut Bencher) {
|
||||||
let seed_compute = SeedHashCompute::new();
|
use std::env;
|
||||||
|
|
||||||
b.iter(|| seed_compute.get_seedhash(486382));
|
let light = Light::new(&env::temp_dir(), 486382);
|
||||||
|
|
||||||
|
b.iter(|| light_compute(&light, &HASH, NONCE));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
#[ignore]
|
||||||
|
fn bench_light_new_round_trip_memmap(b: &mut Bencher) {
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let builder = NodeCacheBuilder::new(OptimizeFor::Memory);
|
||||||
|
let light = Light::new_with_builder(&builder, &env::temp_dir(), 486382);
|
||||||
|
light_compute(&light, &HASH, NONCE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
#[ignore]
|
||||||
|
fn bench_light_new_round_trip_memory(b: &mut Bencher) {
|
||||||
|
use std::env;
|
||||||
|
b.iter(|| {
|
||||||
|
let light = Light::new(&env::temp_dir(), 486382);
|
||||||
|
light_compute(&light, &HASH, NONCE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_light_from_file_round_trip_memory(b: &mut Bencher) {
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
let dir = env::temp_dir();
|
||||||
|
let height = 486382;
|
||||||
|
{
|
||||||
|
let mut dummy = Light::new(&dir, height);
|
||||||
|
dummy.to_file().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let light = Light::from_file(&dir, 486382).unwrap();
|
||||||
|
light_compute(&light, &HASH, NONCE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_light_from_file_round_trip_memmap(b: &mut Bencher) {
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
let dir = env::temp_dir();
|
||||||
|
let height = 486382;
|
||||||
|
{
|
||||||
|
let builder = NodeCacheBuilder::new(OptimizeFor::Memory);
|
||||||
|
let mut dummy = Light::new_with_builder(&builder, &dir, height);
|
||||||
|
dummy.to_file().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let builder = NodeCacheBuilder::new(OptimizeFor::Memory);
|
||||||
|
let light = Light::from_file_with_builder(&builder, &dir, 486382).unwrap();
|
||||||
|
light_compute(&light, &HASH, NONCE);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
109
ethash/src/seed_compute.rs
Normal file
109
ethash/src/seed_compute.rs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// 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/>.
|
||||||
|
|
||||||
|
use shared;
|
||||||
|
use keccak::{keccak_256, H256};
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
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 hash_block_number(&self, block_number: u64) -> H256 {
|
||||||
|
self.hash_epoch(shared::epoch(block_number))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn hash_epoch(&self, epoch: u64) -> H256 {
|
||||||
|
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 {
|
||||||
|
keccak_256::inplace(&mut hash);
|
||||||
|
}
|
||||||
|
hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::SeedHashCompute;
|
||||||
|
|
||||||
|
#[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.hash_block_number(486382), hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_seed_compute_zero() {
|
||||||
|
let seed_compute = SeedHashCompute::new();
|
||||||
|
assert_eq!(seed_compute.hash_block_number(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.hash_block_number(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.hash_block_number(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.hash_block_number(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.hash_block_number(486382), hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
149
ethash/src/shared.rs
Normal file
149
ethash/src/shared.rs
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// 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/>.
|
||||||
|
|
||||||
|
use primal::is_prime;
|
||||||
|
|
||||||
|
pub const DATASET_BYTES_INIT: u64 = 1 << 30;
|
||||||
|
pub const DATASET_BYTES_GROWTH: u64 = 1 << 23;
|
||||||
|
pub const CACHE_BYTES_INIT: u64 = 1 << 24;
|
||||||
|
pub const CACHE_BYTES_GROWTH: u64 = 1 << 17;
|
||||||
|
|
||||||
|
pub const ETHASH_EPOCH_LENGTH: u64 = 30000;
|
||||||
|
pub const ETHASH_CACHE_ROUNDS: usize = 3;
|
||||||
|
pub const ETHASH_MIX_BYTES: usize = 128;
|
||||||
|
pub const ETHASH_ACCESSES: usize = 64;
|
||||||
|
pub const ETHASH_DATASET_PARENTS: u32 = 256;
|
||||||
|
pub const NODE_DWORDS: usize = NODE_WORDS / 2;
|
||||||
|
pub const NODE_WORDS: usize = NODE_BYTES / 4;
|
||||||
|
pub const NODE_BYTES: usize = 64;
|
||||||
|
|
||||||
|
pub fn epoch(block_number: u64) -> u64 {
|
||||||
|
block_number / ETHASH_EPOCH_LENGTH
|
||||||
|
}
|
||||||
|
|
||||||
|
static CHARS: &'static [u8] = b"0123456789abcdef";
|
||||||
|
pub 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) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cache_size(block_number: u64) -> usize {
|
||||||
|
// TODO: Memoise
|
||||||
|
let mut sz: u64 = CACHE_BYTES_INIT + CACHE_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH);
|
||||||
|
sz = sz - NODE_BYTES as u64;
|
||||||
|
while !is_prime(sz / NODE_BYTES as u64) {
|
||||||
|
sz = sz - 2 * NODE_BYTES as u64;
|
||||||
|
}
|
||||||
|
sz as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_data_size(block_number: u64) -> usize {
|
||||||
|
// TODO: Memoise
|
||||||
|
let mut sz: u64 = DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH);
|
||||||
|
sz = sz - ETHASH_MIX_BYTES as u64;
|
||||||
|
while !is_prime(sz / ETHASH_MIX_BYTES as u64) {
|
||||||
|
sz = sz - 2 * ETHASH_MIX_BYTES as u64;
|
||||||
|
}
|
||||||
|
sz as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type NodeBytes = [u8; NODE_BYTES];
|
||||||
|
pub type NodeWords = [u32; NODE_WORDS];
|
||||||
|
pub type NodeDwords = [u64; NODE_DWORDS];
|
||||||
|
|
||||||
|
macro_rules! static_assert_size_eq {
|
||||||
|
(@inner $a:ty, $b:ty, $($rest:ty),*) => {
|
||||||
|
fn first() {
|
||||||
|
static_assert_size_eq!($a, $b);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn second() {
|
||||||
|
static_assert_size_eq!($b, $($rest),*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(@inner $a:ty, $b:ty) => {
|
||||||
|
unsafe {
|
||||||
|
let val: $b = ::std::mem::uninitialized();
|
||||||
|
let _: $a = ::std::mem::transmute(val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($($rest:ty),*) => {
|
||||||
|
static_assert_size_eq!(size_eq: $($rest),*);
|
||||||
|
};
|
||||||
|
($name:ident : $($rest:ty),*) => {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn $name() {
|
||||||
|
static_assert_size_eq!(@inner $($rest),*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert_size_eq!(Node, NodeBytes, NodeWords, NodeDwords);
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union Node {
|
||||||
|
pub dwords: NodeDwords,
|
||||||
|
pub words: NodeWords,
|
||||||
|
pub bytes: NodeBytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Node {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
unsafe { Node { bytes: *&self.bytes } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use `inline(always)` because I was experiencing an 100% slowdown and `perf` showed that these
|
||||||
|
// calls were taking up ~30% of the runtime. Adding these annotations fixes the issue. Remove at
|
||||||
|
// your peril, if and only if you have benchmarks to prove that this doesn't reintroduce the
|
||||||
|
// performance regression. It's not caused by the `debug_assert_eq!` either, your guess is as good
|
||||||
|
// as mine.
|
||||||
|
impl Node {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_bytes(&self) -> &NodeBytes {
|
||||||
|
unsafe { &self.bytes }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_bytes_mut(&mut self) -> &mut NodeBytes {
|
||||||
|
unsafe { &mut self.bytes }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_words(&self) -> &NodeWords {
|
||||||
|
unsafe { &self.words }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_words_mut(&mut self) -> &mut NodeWords {
|
||||||
|
unsafe { &mut self.words }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_dwords(&self) -> &NodeDwords {
|
||||||
|
unsafe { &self.dwords }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_dwords_mut(&mut self) -> &mut NodeDwords {
|
||||||
|
unsafe { &mut self.dwords }
|
||||||
|
}
|
||||||
|
}
|
@ -133,7 +133,7 @@ mod test {
|
|||||||
fn node_filter() {
|
fn node_filter() {
|
||||||
let contract_addr = Address::from_str("0000000000000000000000000000000000000005").unwrap();
|
let contract_addr = Address::from_str("0000000000000000000000000000000000000005").unwrap();
|
||||||
let data = include_bytes!("../res/node_filter.json");
|
let data = include_bytes!("../res/node_filter.json");
|
||||||
let spec = Spec::load(::std::env::temp_dir(), &data[..]).unwrap();
|
let spec = Spec::load(&::std::env::temp_dir(), &data[..]).unwrap();
|
||||||
let client_db = Arc::new(::util::kvdb::in_memory(::ethcore::db::NUM_COLUMNS.unwrap_or(0)));
|
let client_db = Arc::new(::util::kvdb::in_memory(::ethcore::db::NUM_COLUMNS.unwrap_or(0)));
|
||||||
|
|
||||||
let client = Client::new(
|
let client = Client::new(
|
||||||
|
@ -270,7 +270,7 @@ mod tests {
|
|||||||
/// Create a new test chain spec with `BasicAuthority` consensus engine.
|
/// Create a new test chain spec with `BasicAuthority` consensus engine.
|
||||||
fn new_test_authority() -> Spec {
|
fn new_test_authority() -> Spec {
|
||||||
let bytes: &[u8] = include_bytes!("../../res/basic_authority.json");
|
let bytes: &[u8] = include_bytes!("../../res/basic_authority.json");
|
||||||
Spec::load(::std::env::temp_dir(), bytes).expect("invalid chain spec")
|
Spec::load(&::std::env::temp_dir(), bytes).expect("invalid chain spec")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -19,7 +19,7 @@ use std::cmp;
|
|||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use hash::{KECCAK_EMPTY_LIST_RLP};
|
use hash::{KECCAK_EMPTY_LIST_RLP};
|
||||||
use ethash::{quick_get_difficulty, slow_get_seedhash, EthashManager};
|
use ethash::{quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor};
|
||||||
use bigint::prelude::U256;
|
use bigint::prelude::U256;
|
||||||
use bigint::hash::{H256, H64};
|
use bigint::hash::{H256, H64};
|
||||||
use util::*;
|
use util::*;
|
||||||
@ -161,18 +161,19 @@ pub struct Ethash {
|
|||||||
|
|
||||||
impl Ethash {
|
impl Ethash {
|
||||||
/// Create a new instance of Ethash engine
|
/// Create a new instance of Ethash engine
|
||||||
pub fn new<T: AsRef<Path>>(
|
pub fn new<T: Into<Option<OptimizeFor>>>(
|
||||||
cache_dir: T,
|
cache_dir: &Path,
|
||||||
params: CommonParams,
|
params: CommonParams,
|
||||||
ethash_params: EthashParams,
|
ethash_params: EthashParams,
|
||||||
builtins: BTreeMap<Address, Builtin>,
|
builtins: BTreeMap<Address, Builtin>,
|
||||||
|
optimize_for: T,
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
Arc::new(Ethash {
|
Arc::new(Ethash {
|
||||||
tx_filter: TransactionFilter::from_params(¶ms),
|
tx_filter: TransactionFilter::from_params(¶ms),
|
||||||
params,
|
params,
|
||||||
ethash_params,
|
ethash_params,
|
||||||
builtins,
|
builtins,
|
||||||
pow: EthashManager::new(cache_dir),
|
pow: EthashManager::new(cache_dir.as_ref(), optimize_for.into()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -416,7 +417,7 @@ impl Engine for Arc<Ethash> {
|
|||||||
let result = self.pow.compute_light(header.number() as u64, &header.bare_hash().0, header.nonce().low_u64());
|
let result = self.pow.compute_light(header.number() as u64, &header.bare_hash().0, header.nonce().low_u64());
|
||||||
let mix = H256(result.mix_hash);
|
let mix = H256(result.mix_hash);
|
||||||
let difficulty = Ethash::boundary_to_difficulty(&H256(result.value));
|
let difficulty = Ethash::boundary_to_difficulty(&H256(result.value));
|
||||||
trace!(target: "miner", "num: {}, seed: {}, h: {}, non: {}, mix: {}, res: {}" , header.number() as u64, H256(slow_get_seedhash(header.number() as u64)), header.bare_hash(), header.nonce().low_u64(), H256(result.mix_hash), H256(result.value));
|
trace!(target: "miner", "num: {}, seed: {}, h: {}, non: {}, mix: {}, res: {}" , header.number() as u64, H256(slow_hash_block_number(header.number() as u64)), header.bare_hash(), header.nonce().low_u64(), H256(result.mix_hash), H256(result.value));
|
||||||
if mix != header.mix_hash() {
|
if mix != header.mix_hash() {
|
||||||
return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: header.mix_hash() })));
|
return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: header.mix_hash() })));
|
||||||
}
|
}
|
||||||
@ -849,7 +850,7 @@ mod tests {
|
|||||||
fn difficulty_frontier() {
|
fn difficulty_frontier() {
|
||||||
let spec = new_homestead_test();
|
let spec = new_homestead_test();
|
||||||
let ethparams = get_default_ethash_params();
|
let ethparams = get_default_ethash_params();
|
||||||
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new());
|
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None);
|
||||||
|
|
||||||
let mut parent_header = Header::default();
|
let mut parent_header = Header::default();
|
||||||
parent_header.set_number(1000000);
|
parent_header.set_number(1000000);
|
||||||
@ -867,7 +868,7 @@ mod tests {
|
|||||||
fn difficulty_homestead() {
|
fn difficulty_homestead() {
|
||||||
let spec = new_homestead_test();
|
let spec = new_homestead_test();
|
||||||
let ethparams = get_default_ethash_params();
|
let ethparams = get_default_ethash_params();
|
||||||
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new());
|
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None);
|
||||||
|
|
||||||
let mut parent_header = Header::default();
|
let mut parent_header = Header::default();
|
||||||
parent_header.set_number(1500000);
|
parent_header.set_number(1500000);
|
||||||
@ -920,7 +921,7 @@ mod tests {
|
|||||||
ecip1010_pause_transition: 3000000,
|
ecip1010_pause_transition: 3000000,
|
||||||
..get_default_ethash_params()
|
..get_default_ethash_params()
|
||||||
};
|
};
|
||||||
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new());
|
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None);
|
||||||
|
|
||||||
let mut parent_header = Header::default();
|
let mut parent_header = Header::default();
|
||||||
parent_header.set_number(3500000);
|
parent_header.set_number(3500000);
|
||||||
@ -954,7 +955,7 @@ mod tests {
|
|||||||
ecip1010_continue_transition: 5000000,
|
ecip1010_continue_transition: 5000000,
|
||||||
..get_default_ethash_params()
|
..get_default_ethash_params()
|
||||||
};
|
};
|
||||||
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new());
|
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None);
|
||||||
|
|
||||||
let mut parent_header = Header::default();
|
let mut parent_header = Header::default();
|
||||||
parent_header.set_number(5000102);
|
parent_header.set_number(5000102);
|
||||||
@ -1000,7 +1001,7 @@ mod tests {
|
|||||||
fn gas_limit_is_multiple_of_determinant() {
|
fn gas_limit_is_multiple_of_determinant() {
|
||||||
let spec = new_homestead_test();
|
let spec = new_homestead_test();
|
||||||
let ethparams = get_default_ethash_params();
|
let ethparams = get_default_ethash_params();
|
||||||
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new());
|
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None);
|
||||||
let mut parent = Header::new();
|
let mut parent = Header::new();
|
||||||
let mut header = Header::new();
|
let mut header = Header::new();
|
||||||
header.set_number(1);
|
header.set_number(1);
|
||||||
@ -1044,7 +1045,7 @@ mod tests {
|
|||||||
fn difficulty_max_timestamp() {
|
fn difficulty_max_timestamp() {
|
||||||
let spec = new_homestead_test();
|
let spec = new_homestead_test();
|
||||||
let ethparams = get_default_ethash_params();
|
let ethparams = get_default_ethash_params();
|
||||||
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new());
|
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None);
|
||||||
|
|
||||||
let mut parent_header = Header::default();
|
let mut parent_header = Header::default();
|
||||||
parent_header.set_number(1000000);
|
parent_header.set_number(1000000);
|
||||||
@ -1072,7 +1073,7 @@ mod tests {
|
|||||||
header.set_number(parent_header.number() + 1);
|
header.set_number(parent_header.number() + 1);
|
||||||
header.set_gas_limit(100_001.into());
|
header.set_gas_limit(100_001.into());
|
||||||
header.set_difficulty(ethparams.minimum_difficulty);
|
header.set_difficulty(ethparams.minimum_difficulty);
|
||||||
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new());
|
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None);
|
||||||
assert!(ethash.verify_block_family(&header, &parent_header, None).is_ok());
|
assert!(ethash.verify_block_family(&header, &parent_header, None).is_ok());
|
||||||
|
|
||||||
parent_header.set_number(9);
|
parent_header.set_number(9);
|
||||||
@ -1127,7 +1128,7 @@ mod tests {
|
|||||||
nonce: U256::zero(),
|
nonce: U256::zero(),
|
||||||
}.sign(keypair.secret(), None).into();
|
}.sign(keypair.secret(), None).into();
|
||||||
|
|
||||||
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new());
|
let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new(), None);
|
||||||
assert!(ethash.verify_transaction_basic(&tx1, &header).is_ok());
|
assert!(ethash.verify_transaction_basic(&tx1, &header).is_ok());
|
||||||
assert!(ethash.verify_transaction_basic(&tx2, &header).is_ok());
|
assert!(ethash.verify_transaction_basic(&tx2, &header).is_ok());
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ pub mod denominations;
|
|||||||
pub use self::ethash::{Ethash};
|
pub use self::ethash::{Ethash};
|
||||||
pub use self::denominations::*;
|
pub use self::denominations::*;
|
||||||
|
|
||||||
use std::path::Path;
|
|
||||||
use super::spec::*;
|
use super::spec::*;
|
||||||
|
|
||||||
/// Most recent fork block that we support on Mainnet.
|
/// Most recent fork block that we support on Mainnet.
|
||||||
@ -39,33 +38,47 @@ pub const FORK_SUPPORTED_ROPSTEN: u64 = 10;
|
|||||||
/// Most recent fork block that we support on Kovan.
|
/// Most recent fork block that we support on Kovan.
|
||||||
pub const FORK_SUPPORTED_KOVAN: u64 = 0;
|
pub const FORK_SUPPORTED_KOVAN: u64 = 0;
|
||||||
|
|
||||||
fn load<'a, T: 'a + Into<Option<&'a Path>>>(cache_dir: T, b: &[u8]) -> Spec {
|
fn load<'a, T: Into<Option<SpecParams<'a>>>>(params: T, b: &[u8]) -> Spec {
|
||||||
match cache_dir.into() {
|
match params.into() {
|
||||||
Some(path) => Spec::load(path, b),
|
Some(params) => Spec::load(params, b),
|
||||||
None => Spec::load(&::std::env::temp_dir(), b)
|
None => Spec::load(&::std::env::temp_dir(), b)
|
||||||
}.expect("chain spec is invalid")
|
}.expect("chain spec is invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new Foundation Olympic chain spec.
|
/// Create a new Foundation Olympic chain spec.
|
||||||
pub fn new_olympic(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/olympic.json")) }
|
pub fn new_olympic<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
|
||||||
|
load(params.into(), include_bytes!("../../res/ethereum/olympic.json"))
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Foundation Mainnet chain spec.
|
/// Create a new Foundation Mainnet chain spec.
|
||||||
pub fn new_foundation(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/foundation.json")) }
|
pub fn new_foundation<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
|
||||||
|
load(params.into(), include_bytes!("../../res/ethereum/foundation.json"))
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Classic Mainnet chain spec without the DAO hardfork.
|
/// Create a new Classic Mainnet chain spec without the DAO hardfork.
|
||||||
pub fn new_classic(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/classic.json")) }
|
pub fn new_classic<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
|
||||||
|
load(params.into(), include_bytes!("../../res/ethereum/classic.json"))
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Expanse mainnet chain spec.
|
/// Create a new Expanse mainnet chain spec.
|
||||||
pub fn new_expanse(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/expanse.json")) }
|
pub fn new_expanse<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
|
||||||
|
load(params.into(), include_bytes!("../../res/ethereum/expanse.json"))
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Kovan testnet chain spec.
|
/// Create a new Kovan testnet chain spec.
|
||||||
pub fn new_kovan(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/kovan.json")) }
|
pub fn new_kovan<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
|
||||||
|
load(params.into(), include_bytes!("../../res/ethereum/kovan.json"))
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Foundation Ropsten chain spec.
|
/// Create a new Foundation Ropsten chain spec.
|
||||||
pub fn new_ropsten(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/ropsten.json")) }
|
pub fn new_ropsten<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
|
||||||
|
load(params.into(), include_bytes!("../../res/ethereum/ropsten.json"))
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Morden chain spec.
|
/// Create a new Morden chain spec.
|
||||||
pub fn new_morden(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/morden.json")) }
|
pub fn new_morden<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
|
||||||
|
load(params.into(), include_bytes!("../../res/ethereum/morden.json"))
|
||||||
|
}
|
||||||
|
|
||||||
// For tests
|
// For tests
|
||||||
|
|
||||||
|
@ -52,7 +52,8 @@ pub fn run_test_file(path: &Path, runner: fn (json_data: &[u8]) -> Vec<String>)
|
|||||||
let mut file = File::open(&path).expect("Error opening test file");
|
let mut file = File::open(&path).expect("Error opening test file");
|
||||||
file.read_to_end(&mut data).expect("Error reading test file");
|
file.read_to_end(&mut data).expect("Error reading test file");
|
||||||
let results = runner(&data);
|
let results = runner(&data);
|
||||||
assert!(results.is_empty());
|
let empty: [String; 0] = [];
|
||||||
|
assert_eq!(results, empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! test {
|
macro_rules! test {
|
||||||
|
@ -171,7 +171,7 @@ impl StratumJobDispatcher {
|
|||||||
fn payload(&self, pow_hash: H256, difficulty: U256, number: u64) -> String {
|
fn payload(&self, pow_hash: H256, difficulty: U256, number: u64) -> String {
|
||||||
// TODO: move this to engine
|
// TODO: move this to engine
|
||||||
let target = Ethash::difficulty_to_boundary(&difficulty);
|
let target = Ethash::difficulty_to_boundary(&difficulty);
|
||||||
let seed_hash = &self.seed_compute.lock().get_seedhash(number);
|
let seed_hash = &self.seed_compute.lock().hash_block_number(number);
|
||||||
let seed_hash = H256::from_slice(&seed_hash[..]);
|
let seed_hash = H256::from_slice(&seed_hash[..]);
|
||||||
format!(
|
format!(
|
||||||
r#"["0x", "0x{}","0x{}","0x{}","0x{:x}"]"#,
|
r#"["0x", "0x{}","0x{}","0x{}","0x{:x}"]"#,
|
||||||
|
@ -72,7 +72,7 @@ impl NotifyWork for WorkPoster {
|
|||||||
fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) {
|
fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) {
|
||||||
// TODO: move this to engine
|
// TODO: move this to engine
|
||||||
let target = Ethash::difficulty_to_boundary(&difficulty);
|
let target = Ethash::difficulty_to_boundary(&difficulty);
|
||||||
let seed_hash = &self.seed_compute.lock().get_seedhash(number);
|
let seed_hash = &self.seed_compute.lock().hash_block_number(number);
|
||||||
let seed_hash = H256::from_slice(&seed_hash[..]);
|
let seed_hash = H256::from_slice(&seed_hash[..]);
|
||||||
let body = format!(
|
let body = format!(
|
||||||
r#"{{ "result": ["0x{}","0x{}","0x{}","0x{:x}"] }}"#,
|
r#"{{ "result": ["0x{}","0x{}","0x{}","0x{:x}"] }}"#,
|
||||||
|
@ -16,34 +16,37 @@
|
|||||||
|
|
||||||
//! Parameters for a block chain.
|
//! Parameters for a block chain.
|
||||||
|
|
||||||
use std::io::Read;
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use rustc_hex::FromHex;
|
|
||||||
use hash::{KECCAK_NULL_RLP, keccak};
|
|
||||||
use super::genesis::Genesis;
|
use super::genesis::Genesis;
|
||||||
use super::seal::Generic as GenericSeal;
|
use super::seal::Generic as GenericSeal;
|
||||||
|
use bigint::hash::{H2048, H256};
|
||||||
|
use bigint::prelude::U256;
|
||||||
|
|
||||||
use builtin::Builtin;
|
use builtin::Builtin;
|
||||||
use engines::{Engine, NullEngine, InstantSeal, BasicAuthority, AuthorityRound, Tendermint, DEFAULT_BLOCKHASH_CONTRACT};
|
use bytes::Bytes;
|
||||||
use vm::{EnvInfo, CallType, ActionValue, ActionParams};
|
use engines::{AuthorityRound, BasicAuthority, DEFAULT_BLOCKHASH_CONTRACT, Engine, InstantSeal,
|
||||||
|
NullEngine, Tendermint};
|
||||||
use error::Error;
|
use error::Error;
|
||||||
|
|
||||||
|
pub use ethash::OptimizeFor;
|
||||||
use ethereum;
|
use ethereum;
|
||||||
use ethjson;
|
use ethjson;
|
||||||
use executive::Executive;
|
use executive::Executive;
|
||||||
use factory::Factories;
|
use factory::Factories;
|
||||||
|
use hash::{KECCAK_NULL_RLP, keccak};
|
||||||
use header::{BlockNumber, Header};
|
use header::{BlockNumber, Header};
|
||||||
|
use parking_lot::RwLock;
|
||||||
use pod_state::*;
|
use pod_state::*;
|
||||||
use rlp::{Rlp, RlpStream};
|
use rlp::{Rlp, RlpStream};
|
||||||
|
use rustc_hex::FromHex;
|
||||||
use state::{Backend, State, Substate};
|
use state::{Backend, State, Substate};
|
||||||
use state::backend::Basic as BasicBackend;
|
use state::backend::Basic as BasicBackend;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
use trace::{NoopTracer, NoopVMTracer};
|
use trace::{NoopTracer, NoopVMTracer};
|
||||||
use bigint::prelude::U256;
|
|
||||||
use bigint::hash::{H256, H2048};
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use bytes::Bytes;
|
use vm::{ActionParams, ActionValue, CallType, EnvInfo};
|
||||||
|
|
||||||
/// Parameters common to ethereum-like blockchains.
|
/// Parameters common to ethereum-like blockchains.
|
||||||
/// NOTE: when adding bugfix hard-fork parameters,
|
/// NOTE: when adding bugfix hard-fork parameters,
|
||||||
@ -88,7 +91,8 @@ pub struct CommonParams {
|
|||||||
pub eip210_contract_code: Bytes,
|
pub eip210_contract_code: Bytes,
|
||||||
/// Gas allocated for EIP-210 blockhash update.
|
/// Gas allocated for EIP-210 blockhash update.
|
||||||
pub eip210_contract_gas: U256,
|
pub eip210_contract_gas: U256,
|
||||||
/// Number of first block where EIP-211 (Metropolis: RETURNDATASIZE/RETURNDATACOPY) rules begin.
|
/// Number of first block where EIP-211 (Metropolis: RETURNDATASIZE/RETURNDATACOPY) rules
|
||||||
|
/// begin.
|
||||||
pub eip211_transition: BlockNumber,
|
pub eip211_transition: BlockNumber,
|
||||||
/// Number of first block where EIP-214 rules begin.
|
/// Number of first block where EIP-214 rules begin.
|
||||||
pub eip214_transition: BlockNumber,
|
pub eip214_transition: BlockNumber,
|
||||||
@ -139,14 +143,10 @@ impl CommonParams {
|
|||||||
|
|
||||||
/// Whether these params contain any bug-fix hard forks.
|
/// Whether these params contain any bug-fix hard forks.
|
||||||
pub fn contains_bugfix_hard_fork(&self) -> bool {
|
pub fn contains_bugfix_hard_fork(&self) -> bool {
|
||||||
self.eip98_transition != 0 &&
|
self.eip98_transition != 0 && self.eip155_transition != 0 &&
|
||||||
self.eip155_transition != 0 &&
|
self.validate_receipts_transition != 0 && self.eip86_transition != 0 &&
|
||||||
self.validate_receipts_transition != 0 &&
|
self.eip140_transition != 0 && self.eip210_transition != 0 &&
|
||||||
self.eip86_transition != 0 &&
|
self.eip211_transition != 0 && self.eip214_transition != 0 &&
|
||||||
self.eip140_transition != 0 &&
|
|
||||||
self.eip210_transition != 0 &&
|
|
||||||
self.eip211_transition != 0 &&
|
|
||||||
self.eip214_transition != 0 &&
|
|
||||||
self.dust_protection_transition != 0
|
self.dust_protection_transition != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,25 +157,59 @@ impl From<ethjson::spec::Params> for CommonParams {
|
|||||||
account_start_nonce: p.account_start_nonce.map_or_else(U256::zero, Into::into),
|
account_start_nonce: p.account_start_nonce.map_or_else(U256::zero, Into::into),
|
||||||
maximum_extra_data_size: p.maximum_extra_data_size.into(),
|
maximum_extra_data_size: p.maximum_extra_data_size.into(),
|
||||||
network_id: p.network_id.into(),
|
network_id: p.network_id.into(),
|
||||||
chain_id: if let Some(n) = p.chain_id { n.into() } else { p.network_id.into() },
|
chain_id: if let Some(n) = p.chain_id {
|
||||||
|
n.into()
|
||||||
|
} else {
|
||||||
|
p.network_id.into()
|
||||||
|
},
|
||||||
subprotocol_name: p.subprotocol_name.unwrap_or_else(|| "eth".to_owned()),
|
subprotocol_name: p.subprotocol_name.unwrap_or_else(|| "eth".to_owned()),
|
||||||
min_gas_limit: p.min_gas_limit.into(),
|
min_gas_limit: p.min_gas_limit.into(),
|
||||||
fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { Some((n.into(), h.into())) } else { None },
|
fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) {
|
||||||
|
Some((n.into(), h.into()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
eip98_transition: p.eip98_transition.map_or(0, Into::into),
|
eip98_transition: p.eip98_transition.map_or(0, Into::into),
|
||||||
eip155_transition: p.eip155_transition.map_or(0, Into::into),
|
eip155_transition: p.eip155_transition.map_or(0, Into::into),
|
||||||
validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into),
|
validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into),
|
||||||
eip86_transition: p.eip86_transition.map_or(BlockNumber::max_value(), Into::into),
|
eip86_transition: p.eip86_transition.map_or(
|
||||||
eip140_transition: p.eip140_transition.map_or(BlockNumber::max_value(), Into::into),
|
BlockNumber::max_value(),
|
||||||
eip210_transition: p.eip210_transition.map_or(BlockNumber::max_value(), Into::into),
|
Into::into,
|
||||||
|
),
|
||||||
|
eip140_transition: p.eip140_transition.map_or(
|
||||||
|
BlockNumber::max_value(),
|
||||||
|
Into::into,
|
||||||
|
),
|
||||||
|
eip210_transition: p.eip210_transition.map_or(
|
||||||
|
BlockNumber::max_value(),
|
||||||
|
Into::into,
|
||||||
|
),
|
||||||
eip210_contract_address: p.eip210_contract_address.map_or(0xf0.into(), Into::into),
|
eip210_contract_address: p.eip210_contract_address.map_or(0xf0.into(), Into::into),
|
||||||
eip210_contract_code: p.eip210_contract_code.map_or_else(
|
eip210_contract_code: p.eip210_contract_code.map_or_else(
|
||||||
|| DEFAULT_BLOCKHASH_CONTRACT.from_hex().expect("Default BLOCKHASH contract is valid"),
|
|| {
|
||||||
Into::into),
|
DEFAULT_BLOCKHASH_CONTRACT.from_hex().expect(
|
||||||
|
"Default BLOCKHASH contract is valid",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Into::into,
|
||||||
|
),
|
||||||
eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into),
|
eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into),
|
||||||
eip211_transition: p.eip211_transition.map_or(BlockNumber::max_value(), Into::into),
|
eip211_transition: p.eip211_transition.map_or(
|
||||||
eip214_transition: p.eip214_transition.map_or(BlockNumber::max_value(), Into::into),
|
BlockNumber::max_value(),
|
||||||
eip658_transition: p.eip658_transition.map_or(BlockNumber::max_value(), Into::into),
|
Into::into,
|
||||||
dust_protection_transition: p.dust_protection_transition.map_or(BlockNumber::max_value(), Into::into),
|
),
|
||||||
|
eip214_transition: p.eip214_transition.map_or(
|
||||||
|
BlockNumber::max_value(),
|
||||||
|
Into::into,
|
||||||
|
),
|
||||||
|
eip658_transition: p.eip658_transition.map_or(
|
||||||
|
BlockNumber::max_value(),
|
||||||
|
Into::into,
|
||||||
|
),
|
||||||
|
dust_protection_transition: p.dust_protection_transition.map_or(
|
||||||
|
BlockNumber::max_value(),
|
||||||
|
Into::into,
|
||||||
|
),
|
||||||
nonce_cap_increment: p.nonce_cap_increment.map_or(64, Into::into),
|
nonce_cap_increment: p.nonce_cap_increment.map_or(64, Into::into),
|
||||||
remove_dust_contracts: p.remove_dust_contracts.unwrap_or(false),
|
remove_dust_contracts: p.remove_dust_contracts.unwrap_or(false),
|
||||||
wasm: p.wasm.unwrap_or(false),
|
wasm: p.wasm.unwrap_or(false),
|
||||||
@ -188,6 +222,44 @@ impl From<ethjson::spec::Params> for CommonParams {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runtime parameters for the spec that are related to how the software should run the chain,
|
||||||
|
/// rather than integral properties of the chain itself.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct SpecParams<'a> {
|
||||||
|
/// The path to the folder used to cache nodes. This is typically /tmp/ on Unix-like systems
|
||||||
|
pub cache_dir: &'a Path,
|
||||||
|
/// Whether to run slower at the expense of better memory usage, or run faster while using
|
||||||
|
/// more
|
||||||
|
/// memory. This may get more fine-grained in the future but for now is simply a binary
|
||||||
|
/// option.
|
||||||
|
pub optimization_setting: Option<OptimizeFor>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SpecParams<'a> {
|
||||||
|
/// Create from a cache path, with null values for the other fields
|
||||||
|
pub fn from_path(path: &'a Path) -> Self {
|
||||||
|
SpecParams {
|
||||||
|
cache_dir: path,
|
||||||
|
optimization_setting: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create from a cache path and an optimization setting
|
||||||
|
pub fn new(path: &'a Path, optimization: OptimizeFor) -> Self {
|
||||||
|
SpecParams {
|
||||||
|
cache_dir: path,
|
||||||
|
optimization_setting: Some(optimization),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: AsRef<Path>> From<&'a T> for SpecParams<'a> {
|
||||||
|
fn from(path: &'a T) -> Self {
|
||||||
|
Self::from_path(path.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Parameters for a block chain; includes both those intrinsic to the design of the
|
/// Parameters for a block chain; includes both those intrinsic to the design of the
|
||||||
/// chain and those to be interpreted by the active chain engine.
|
/// chain and those to be interpreted by the active chain engine.
|
||||||
pub struct Spec {
|
pub struct Spec {
|
||||||
@ -258,15 +330,19 @@ impl Clone for Spec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Load from JSON object.
|
/// Load from JSON object.
|
||||||
pub fn load_from<T: AsRef<Path>>(cache_dir: T, s: ethjson::spec::Spec) -> Result<Spec, Error> {
|
fn load_from(spec_params: SpecParams, s: ethjson::spec::Spec) -> Result<Spec, Error> {
|
||||||
let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), From::from(p.1))).collect();
|
let builtins = s.accounts
|
||||||
|
.builtins()
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| (p.0.into(), From::from(p.1)))
|
||||||
|
.collect();
|
||||||
let g = Genesis::from(s.genesis);
|
let g = Genesis::from(s.genesis);
|
||||||
let GenericSeal(seal_rlp) = g.seal.into();
|
let GenericSeal(seal_rlp) = g.seal.into();
|
||||||
let params = CommonParams::from(s.params);
|
let params = CommonParams::from(s.params);
|
||||||
|
|
||||||
let mut s = Spec {
|
let mut s = Spec {
|
||||||
name: s.name.clone().into(),
|
name: s.name.clone().into(),
|
||||||
engine: Spec::engine(cache_dir, s.engine, params, builtins),
|
engine: Spec::engine(spec_params, s.engine, params, builtins),
|
||||||
data_dir: s.data_dir.unwrap_or(s.name).into(),
|
data_dir: s.data_dir.unwrap_or(s.name).into(),
|
||||||
nodes: s.nodes.unwrap_or_else(Vec::new),
|
nodes: s.nodes.unwrap_or_else(Vec::new),
|
||||||
parent_hash: g.parent_hash,
|
parent_hash: g.parent_hash,
|
||||||
@ -279,7 +355,11 @@ pub fn load_from<T: AsRef<Path>>(cache_dir: T, s: ethjson::spec::Spec) -> Result
|
|||||||
timestamp: g.timestamp,
|
timestamp: g.timestamp,
|
||||||
extra_data: g.extra_data,
|
extra_data: g.extra_data,
|
||||||
seal_rlp: seal_rlp,
|
seal_rlp: seal_rlp,
|
||||||
constructors: s.accounts.constructors().into_iter().map(|(a, c)| (a.into(), c.into())).collect(),
|
constructors: s.accounts
|
||||||
|
.constructors()
|
||||||
|
.into_iter()
|
||||||
|
.map(|(a, c)| (a.into(), c.into()))
|
||||||
|
.collect(),
|
||||||
state_root_memo: RwLock::new(Default::default()), // will be overwritten right after.
|
state_root_memo: RwLock::new(Default::default()), // will be overwritten right after.
|
||||||
genesis_state: s.accounts.into(),
|
genesis_state: s.accounts.into(),
|
||||||
};
|
};
|
||||||
@ -287,7 +367,12 @@ pub fn load_from<T: AsRef<Path>>(cache_dir: T, s: ethjson::spec::Spec) -> Result
|
|||||||
// use memoized state root if provided.
|
// use memoized state root if provided.
|
||||||
match g.state_root {
|
match g.state_root {
|
||||||
Some(root) => *s.state_root_memo.get_mut() = root,
|
Some(root) => *s.state_root_memo.get_mut() = root,
|
||||||
None => { let _ = s.run_constructors(&Default::default(), BasicBackend(MemoryDB::new()))?; },
|
None => {
|
||||||
|
let _ = s.run_constructors(
|
||||||
|
&Default::default(),
|
||||||
|
BasicBackend(MemoryDB::new()),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(s)
|
Ok(s)
|
||||||
@ -305,8 +390,8 @@ macro_rules! load_bundled {
|
|||||||
impl Spec {
|
impl Spec {
|
||||||
/// Convert engine spec into a arc'd Engine of the right underlying type.
|
/// Convert engine spec into a arc'd Engine of the right underlying type.
|
||||||
/// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead.
|
/// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead.
|
||||||
fn engine<T: AsRef<Path>>(
|
fn engine(
|
||||||
cache_dir: T,
|
spec_params: SpecParams,
|
||||||
engine_spec: ethjson::spec::Engine,
|
engine_spec: ethjson::spec::Engine,
|
||||||
params: CommonParams,
|
params: CommonParams,
|
||||||
builtins: BTreeMap<Address, Builtin>,
|
builtins: BTreeMap<Address, Builtin>,
|
||||||
@ -314,14 +399,43 @@ impl Spec {
|
|||||||
match engine_spec {
|
match engine_spec {
|
||||||
ethjson::spec::Engine::Null => Arc::new(NullEngine::new(params, builtins)),
|
ethjson::spec::Engine::Null => Arc::new(NullEngine::new(params, builtins)),
|
||||||
ethjson::spec::Engine::InstantSeal => Arc::new(InstantSeal::new(params, builtins)),
|
ethjson::spec::Engine::InstantSeal => Arc::new(InstantSeal::new(params, builtins)),
|
||||||
ethjson::spec::Engine::Ethash(ethash) => Arc::new(ethereum::Ethash::new(cache_dir, params, From::from(ethash.params), builtins)),
|
ethjson::spec::Engine::Ethash(ethash) => Arc::new(ethereum::Ethash::new(
|
||||||
ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(params, From::from(basic_authority.params), builtins)),
|
spec_params.cache_dir,
|
||||||
ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(params, From::from(authority_round.params), builtins).expect("Failed to start AuthorityRound consensus engine."),
|
params,
|
||||||
ethjson::spec::Engine::Tendermint(tendermint) => Tendermint::new(params, From::from(tendermint.params), builtins).expect("Failed to start the Tendermint consensus engine."),
|
From::from(ethash.params),
|
||||||
|
builtins,
|
||||||
|
spec_params.optimization_setting,
|
||||||
|
)),
|
||||||
|
ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(
|
||||||
|
BasicAuthority::new(
|
||||||
|
params,
|
||||||
|
From::from(
|
||||||
|
basic_authority.params,
|
||||||
|
),
|
||||||
|
builtins,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(
|
||||||
|
params,
|
||||||
|
From::from(
|
||||||
|
authority_round.params,
|
||||||
|
),
|
||||||
|
builtins,
|
||||||
|
).expect(
|
||||||
|
"Failed to start AuthorityRound consensus engine.",
|
||||||
|
),
|
||||||
|
ethjson::spec::Engine::Tendermint(tendermint) => Tendermint::new(
|
||||||
|
params,
|
||||||
|
From::from(tendermint.params),
|
||||||
|
builtins,
|
||||||
|
).expect(
|
||||||
|
"Failed to start the Tendermint consensus engine.",
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// given a pre-constructor state, run all the given constructors and produce a new state and state root.
|
// given a pre-constructor state, run all the given constructors and produce a new state and
|
||||||
|
// state root.
|
||||||
fn run_constructors<T: Backend>(&self, factories: &Factories, mut db: T) -> Result<T, Error> {
|
fn run_constructors<T: Backend>(&self, factories: &Factories, mut db: T) -> Result<T, Error> {
|
||||||
let mut root = KECCAK_NULL_RLP;
|
let mut root = KECCAK_NULL_RLP;
|
||||||
|
|
||||||
@ -337,20 +451,18 @@ impl Spec {
|
|||||||
for (address, account) in self.genesis_state.get().iter() {
|
for (address, account) in self.genesis_state.get().iter() {
|
||||||
db.note_non_null_account(address);
|
db.note_non_null_account(address);
|
||||||
account.insert_additional(
|
account.insert_additional(
|
||||||
&mut *factories.accountdb.create(db.as_hashdb_mut(), keccak(address)),
|
&mut *factories.accountdb.create(
|
||||||
&factories.trie
|
db.as_hashdb_mut(),
|
||||||
|
keccak(address),
|
||||||
|
),
|
||||||
|
&factories.trie,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let start_nonce = self.engine.account_start_nonce(0);
|
let start_nonce = self.engine.account_start_nonce(0);
|
||||||
|
|
||||||
let (root, db) = {
|
let (root, db) = {
|
||||||
let mut state = State::from_existing(
|
let mut state = State::from_existing(db, root, start_nonce, factories.clone())?;
|
||||||
db,
|
|
||||||
root,
|
|
||||||
start_nonce,
|
|
||||||
factories.clone(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Execute contract constructors.
|
// Execute contract constructors.
|
||||||
let env_info = EnvInfo {
|
let env_info = EnvInfo {
|
||||||
@ -385,7 +497,14 @@ impl Spec {
|
|||||||
|
|
||||||
{
|
{
|
||||||
let mut exec = Executive::new(&mut state, &env_info, self.engine.as_ref());
|
let mut exec = Executive::new(&mut state, &env_info, self.engine.as_ref());
|
||||||
if let Err(e) = exec.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer) {
|
if let Err(e) = exec.create(
|
||||||
|
params,
|
||||||
|
&mut substate,
|
||||||
|
&mut None,
|
||||||
|
&mut NoopTracer,
|
||||||
|
&mut NoopVMTracer,
|
||||||
|
)
|
||||||
|
{
|
||||||
warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e);
|
warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -410,22 +529,34 @@ impl Spec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get common blockchain parameters.
|
/// Get common blockchain parameters.
|
||||||
pub fn params(&self) -> &CommonParams { &self.engine.params() }
|
pub fn params(&self) -> &CommonParams {
|
||||||
|
&self.engine.params()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the known knodes of the network in enode format.
|
/// Get the known knodes of the network in enode format.
|
||||||
pub fn nodes(&self) -> &[String] { &self.nodes }
|
pub fn nodes(&self) -> &[String] {
|
||||||
|
&self.nodes
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the configured Network ID.
|
/// Get the configured Network ID.
|
||||||
pub fn network_id(&self) -> u64 { self.params().network_id }
|
pub fn network_id(&self) -> u64 {
|
||||||
|
self.params().network_id
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the chain ID used for signing.
|
/// Get the chain ID used for signing.
|
||||||
pub fn chain_id(&self) -> u64 { self.params().chain_id }
|
pub fn chain_id(&self) -> u64 {
|
||||||
|
self.params().chain_id
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the configured subprotocol name.
|
/// Get the configured subprotocol name.
|
||||||
pub fn subprotocol_name(&self) -> String { self.params().subprotocol_name.clone() }
|
pub fn subprotocol_name(&self) -> String {
|
||||||
|
self.params().subprotocol_name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the configured network fork block.
|
/// Get the configured network fork block.
|
||||||
pub fn fork_block(&self) -> Option<(BlockNumber, H256)> { self.params().fork_block }
|
pub fn fork_block(&self) -> Option<(BlockNumber, H256)> {
|
||||||
|
self.params().fork_block
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the header of the genesis block.
|
/// Get the header of the genesis block.
|
||||||
pub fn genesis_header(&self) -> Header {
|
pub fn genesis_header(&self) -> Header {
|
||||||
@ -480,7 +611,10 @@ impl Spec {
|
|||||||
/// Alter the value of the genesis state.
|
/// Alter the value of the genesis state.
|
||||||
pub fn set_genesis_state(&mut self, s: PodState) -> Result<(), Error> {
|
pub fn set_genesis_state(&mut self, s: PodState) -> Result<(), Error> {
|
||||||
self.genesis_state = s;
|
self.genesis_state = s;
|
||||||
let _ = self.run_constructors(&Default::default(), BasicBackend(MemoryDB::new()))?;
|
let _ = self.run_constructors(
|
||||||
|
&Default::default(),
|
||||||
|
BasicBackend(MemoryDB::new()),
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -496,7 +630,7 @@ impl Spec {
|
|||||||
/// Ensure that the given state DB has the trie nodes in for the genesis state.
|
/// Ensure that the given state DB has the trie nodes in for the genesis state.
|
||||||
pub fn ensure_db_good<T: Backend>(&self, db: T, factories: &Factories) -> Result<T, Error> {
|
pub fn ensure_db_good<T: Backend>(&self, db: T, factories: &Factories) -> Result<T, Error> {
|
||||||
if db.as_hashdb().contains(&self.state_root()) {
|
if db.as_hashdb().contains(&self.state_root()) {
|
||||||
return Ok(db)
|
return Ok(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: could optimize so we don't re-run, but `ensure_db_good` is barely ever
|
// TODO: could optimize so we don't re-run, but `ensure_db_good` is barely ever
|
||||||
@ -507,13 +641,19 @@ impl Spec {
|
|||||||
|
|
||||||
/// Loads spec from json file. Provide factories for executing contracts and ensuring
|
/// Loads spec from json file. Provide factories for executing contracts and ensuring
|
||||||
/// storage goes to the right place.
|
/// storage goes to the right place.
|
||||||
pub fn load<T: AsRef<Path>, R>(cache_dir: T, reader: R) -> Result<Self, String> where R: Read {
|
pub fn load<'a, T: Into<SpecParams<'a>>, R>(params: T, reader: R) -> Result<Self, String>
|
||||||
|
where
|
||||||
|
R: Read,
|
||||||
|
{
|
||||||
fn fmt<F: ::std::fmt::Display>(f: F) -> String {
|
fn fmt<F: ::std::fmt::Display>(f: F) -> String {
|
||||||
format!("Spec json is invalid: {}", f)
|
format!("Spec json is invalid: {}", f)
|
||||||
}
|
}
|
||||||
|
|
||||||
ethjson::spec::Spec::load(reader).map_err(fmt)
|
ethjson::spec::Spec::load(reader).map_err(fmt).and_then(
|
||||||
.and_then(|x| load_from(cache_dir, x).map_err(fmt))
|
|x| {
|
||||||
|
load_from(params.into(), x).map_err(fmt)
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// initialize genesis epoch data, using in-memory database for
|
/// initialize genesis epoch data, using in-memory database for
|
||||||
@ -543,7 +683,7 @@ impl Spec {
|
|||||||
difficulty: *genesis.difficulty(),
|
difficulty: *genesis.difficulty(),
|
||||||
gas_limit: *genesis.gas_limit(),
|
gas_limit: *genesis.gas_limit(),
|
||||||
last_hashes: Arc::new(Vec::new()),
|
last_hashes: Arc::new(Vec::new()),
|
||||||
gas_used: 0.into()
|
gas_used: 0.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let from = Address::default();
|
let from = Address::default();
|
||||||
@ -566,86 +706,139 @@ impl Spec {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
res.map(|(out, proof)| (out, proof.into_iter().map(|x| x.into_vec()).collect()))
|
res.map(|(out, proof)| {
|
||||||
.ok_or_else(|| "Failed to prove call: insufficient state".into())
|
(out, proof.into_iter().map(|x| x.into_vec()).collect())
|
||||||
|
}).ok_or_else(|| "Failed to prove call: insufficient state".into())
|
||||||
};
|
};
|
||||||
|
|
||||||
self.engine.genesis_epoch_data(&genesis, &call)
|
self.engine.genesis_epoch_data(&genesis, &call)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus.
|
/// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a
|
||||||
pub fn new_test() -> Spec { load_bundled!("null_morden") }
|
/// NullEngine consensus.
|
||||||
|
pub fn new_test() -> Spec {
|
||||||
|
load_bundled!("null_morden")
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus with applying reward on block close.
|
/// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a
|
||||||
pub fn new_test_with_reward() -> Spec { load_bundled!("null_morden_with_reward") }
|
/// NullEngine consensus with applying reward on block close.
|
||||||
|
pub fn new_test_with_reward() -> Spec {
|
||||||
|
load_bundled!("null_morden_with_reward")
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Spec which is a NullEngine consensus with a premine of address whose secret is keccak('').
|
/// Create a new Spec which is a NullEngine consensus with a premine of address whose
|
||||||
pub fn new_null() -> Spec { load_bundled!("null") }
|
/// secret is keccak('').
|
||||||
|
pub fn new_null() -> Spec {
|
||||||
|
load_bundled!("null")
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Spec which constructs a contract at address 5 with storage at 0 equal to 1.
|
/// Create a new Spec which constructs a contract at address 5 with storage at 0 equal to 1.
|
||||||
pub fn new_test_constructor() -> Spec { load_bundled!("constructor") }
|
pub fn new_test_constructor() -> Spec {
|
||||||
|
load_bundled!("constructor")
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Spec with InstantSeal consensus which does internal sealing (not requiring work).
|
/// Create a new Spec with InstantSeal consensus which does internal sealing (not requiring
|
||||||
pub fn new_instant() -> Spec { load_bundled!("instant_seal") }
|
/// work).
|
||||||
|
pub fn new_instant() -> Spec {
|
||||||
|
load_bundled!("instant_seal")
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Spec with AuthorityRound consensus which does internal sealing (not requiring work).
|
/// Create a new Spec with AuthorityRound consensus which does internal sealing (not
|
||||||
|
/// requiring work).
|
||||||
/// Accounts with secrets keccak("0") and keccak("1") are the validators.
|
/// Accounts with secrets keccak("0") and keccak("1") are the validators.
|
||||||
pub fn new_test_round() -> Self { load_bundled!("authority_round") }
|
pub fn new_test_round() -> Self {
|
||||||
|
load_bundled!("authority_round")
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Spec with Tendermint consensus which does internal sealing (not requiring work).
|
/// Create a new Spec with Tendermint consensus which does internal sealing (not requiring
|
||||||
|
/// work).
|
||||||
/// Account keccak("0") and keccak("1") are a authorities.
|
/// Account keccak("0") and keccak("1") are a authorities.
|
||||||
pub fn new_test_tendermint() -> Self { load_bundled!("tendermint") }
|
pub fn new_test_tendermint() -> Self {
|
||||||
|
load_bundled!("tendermint")
|
||||||
|
}
|
||||||
|
|
||||||
/// TestList.sol used in both specs: https://github.com/paritytech/contracts/pull/30/files
|
/// TestList.sol used in both specs: https://github.com/paritytech/contracts/pull/30/files
|
||||||
/// Accounts with secrets keccak("0") and keccak("1") are initially the validators.
|
/// Accounts with secrets keccak("0") and keccak("1") are initially the validators.
|
||||||
/// Create a new Spec with BasicAuthority which uses a contract at address 5 to determine the current validators using `getValidators`.
|
/// Create a new Spec with BasicAuthority which uses a contract at address 5 to determine
|
||||||
/// Second validator can be removed with "0xbfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1" and added back in using "0x4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".
|
/// the current validators using `getValidators`.
|
||||||
pub fn new_validator_safe_contract() -> Self { load_bundled!("validator_safe_contract") }
|
/// Second validator can be removed with
|
||||||
|
/// "0xbfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1" and added
|
||||||
|
/// back in using
|
||||||
|
/// "0x4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".
|
||||||
|
pub fn new_validator_safe_contract() -> Self {
|
||||||
|
load_bundled!("validator_safe_contract")
|
||||||
|
}
|
||||||
|
|
||||||
/// The same as the `safeContract`, but allows reporting and uses AuthorityRound.
|
/// The same as the `safeContract`, but allows reporting and uses AuthorityRound.
|
||||||
/// Account is marked with `reportBenign` it can be checked as disliked with "0xd8f2e0bf".
|
/// Account is marked with `reportBenign` it can be checked as disliked with "0xd8f2e0bf".
|
||||||
/// Validator can be removed with `reportMalicious`.
|
/// Validator can be removed with `reportMalicious`.
|
||||||
pub fn new_validator_contract() -> Self { load_bundled!("validator_contract") }
|
pub fn new_validator_contract() -> Self {
|
||||||
|
load_bundled!("validator_contract")
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Spec with BasicAuthority which uses multiple validator sets changing with height.
|
/// Create a new Spec with BasicAuthority which uses multiple validator sets changing with
|
||||||
/// Account with secrets keccak("0") is the validator for block 1 and with keccak("1") onwards.
|
/// height.
|
||||||
pub fn new_validator_multi() -> Self { load_bundled!("validator_multi") }
|
/// Account with secrets keccak("0") is the validator for block 1 and with keccak("1")
|
||||||
|
/// onwards.
|
||||||
|
pub fn new_validator_multi() -> Self {
|
||||||
|
load_bundled!("validator_multi")
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new spec for a PoW chain
|
/// Create a new spec for a PoW chain
|
||||||
pub fn new_pow_test_spec() -> Self { load_bundled!("ethereum/olympic") }
|
pub fn new_pow_test_spec() -> Self {
|
||||||
|
load_bundled!("ethereum/olympic")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use state::State;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use tests::helpers::get_temp_state_db;
|
||||||
use util::*;
|
use util::*;
|
||||||
use views::*;
|
use views::*;
|
||||||
use tests::helpers::get_temp_state_db;
|
|
||||||
use state::State;
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
// https://github.com/paritytech/parity/issues/1840
|
// https://github.com/paritytech/parity/issues/1840
|
||||||
#[test]
|
#[test]
|
||||||
fn test_load_empty() {
|
fn test_load_empty() {
|
||||||
assert!(Spec::load(::std::env::temp_dir(), &[] as &[u8]).is_err());
|
assert!(Spec::load(&::std::env::temp_dir(), &[] as &[u8]).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_chain() {
|
fn test_chain() {
|
||||||
let test_spec = Spec::new_test();
|
let test_spec = Spec::new_test();
|
||||||
|
|
||||||
assert_eq!(test_spec.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap());
|
assert_eq!(
|
||||||
|
test_spec.state_root(),
|
||||||
|
H256::from_str(
|
||||||
|
"f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9",
|
||||||
|
).unwrap()
|
||||||
|
);
|
||||||
let genesis = test_spec.genesis_block();
|
let genesis = test_spec.genesis_block();
|
||||||
assert_eq!(BlockView::new(&genesis).header_view().hash(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
|
assert_eq!(
|
||||||
|
BlockView::new(&genesis).header_view().hash(),
|
||||||
|
H256::from_str(
|
||||||
|
"0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303",
|
||||||
|
).unwrap()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn genesis_constructor() {
|
fn genesis_constructor() {
|
||||||
::ethcore_logger::init_log();
|
::ethcore_logger::init_log();
|
||||||
let spec = Spec::new_test_constructor();
|
let spec = Spec::new_test_constructor();
|
||||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default())
|
||||||
let state = State::from_existing(db.boxed_clone(), spec.state_root(), spec.engine.account_start_nonce(0), Default::default()).unwrap();
|
.unwrap();
|
||||||
let expected = H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap();
|
let state = State::from_existing(
|
||||||
|
db.boxed_clone(),
|
||||||
|
spec.state_root(),
|
||||||
|
spec.engine.account_start_nonce(0),
|
||||||
|
Default::default(),
|
||||||
|
).unwrap();
|
||||||
|
let expected = H256::from_str(
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
).unwrap();
|
||||||
let address = Address::from_str("0000000000000000000000000000000000000005").unwrap();
|
let address = Address::from_str("0000000000000000000000000000000000000005").unwrap();
|
||||||
|
|
||||||
assert_eq!(state.storage_at(&address, &H256::zero()).unwrap(), expected);
|
assert_eq!(state.storage_at(&address, &H256::zero()).unwrap(), expected);
|
||||||
|
@ -196,7 +196,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let spec = Spec::load(::std::env::temp_dir(), spec_data.as_bytes()).unwrap();
|
let spec = Spec::load(&::std::env::temp_dir(), spec_data.as_bytes()).unwrap();
|
||||||
let client_db = Arc::new(::util::kvdb::in_memory(::db::NUM_COLUMNS.unwrap_or(0)));
|
let client_db = Arc::new(::util::kvdb::in_memory(::db::NUM_COLUMNS.unwrap_or(0)));
|
||||||
|
|
||||||
let client = Client::new(
|
let client = Client::new(
|
||||||
|
@ -230,7 +230,7 @@ impl Args {
|
|||||||
Ok(match self.flag_chain {
|
Ok(match self.flag_chain {
|
||||||
Some(ref filename) => {
|
Some(ref filename) => {
|
||||||
let file = fs::File::open(filename).map_err(|e| format!("{}", e))?;
|
let file = fs::File::open(filename).map_err(|e| format!("{}", e))?;
|
||||||
spec::Spec::load(::std::env::temp_dir(), file)?
|
spec::Spec::load(&::std::env::temp_dir(), file)?
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
ethcore::ethereum::new_foundation(&::std::env::temp_dir())
|
ethcore::ethereum::new_foundation(&::std::env::temp_dir())
|
||||||
|
@ -14,12 +14,12 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::{str, fs, fmt, path};
|
use std::{str, fs, fmt};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use bigint::prelude::U256;
|
use bigint::prelude::U256;
|
||||||
use util::{Address, version_data};
|
use util::{Address, version_data};
|
||||||
use util::journaldb::Algorithm;
|
use util::journaldb::Algorithm;
|
||||||
use ethcore::spec::Spec;
|
use ethcore::spec::{Spec, SpecParams};
|
||||||
use ethcore::ethereum;
|
use ethcore::ethereum;
|
||||||
use ethcore::client::Mode;
|
use ethcore::client::Mode;
|
||||||
use ethcore::miner::{GasPricer, GasPriceCalibratorOptions};
|
use ethcore::miner::{GasPricer, GasPriceCalibratorOptions};
|
||||||
@ -81,20 +81,20 @@ impl fmt::Display for SpecType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SpecType {
|
impl SpecType {
|
||||||
pub fn spec<T: AsRef<path::Path>>(&self, cache_dir: T) -> Result<Spec, String> {
|
pub fn spec<'a, T: Into<SpecParams<'a>>>(&self, params: T) -> Result<Spec, String> {
|
||||||
let cache_dir = cache_dir.as_ref();
|
let params = params.into();
|
||||||
match *self {
|
match *self {
|
||||||
SpecType::Foundation => Ok(ethereum::new_foundation(cache_dir)),
|
SpecType::Foundation => Ok(ethereum::new_foundation(params)),
|
||||||
SpecType::Morden => Ok(ethereum::new_morden(cache_dir)),
|
SpecType::Morden => Ok(ethereum::new_morden(params)),
|
||||||
SpecType::Ropsten => Ok(ethereum::new_ropsten(cache_dir)),
|
SpecType::Ropsten => Ok(ethereum::new_ropsten(params)),
|
||||||
SpecType::Olympic => Ok(ethereum::new_olympic(cache_dir)),
|
SpecType::Olympic => Ok(ethereum::new_olympic(params)),
|
||||||
SpecType::Classic => Ok(ethereum::new_classic(cache_dir)),
|
SpecType::Classic => Ok(ethereum::new_classic(params)),
|
||||||
SpecType::Expanse => Ok(ethereum::new_expanse(cache_dir)),
|
SpecType::Expanse => Ok(ethereum::new_expanse(params)),
|
||||||
SpecType::Kovan => Ok(ethereum::new_kovan(cache_dir)),
|
SpecType::Kovan => Ok(ethereum::new_kovan(params)),
|
||||||
SpecType::Dev => Ok(Spec::new_instant()),
|
SpecType::Dev => Ok(Spec::new_instant()),
|
||||||
SpecType::Custom(ref filename) => {
|
SpecType::Custom(ref filename) => {
|
||||||
let file = fs::File::open(filename).map_err(|e| format!("Could not load specification file at {}: {}", filename, e))?;
|
let file = fs::File::open(filename).map_err(|e| format!("Could not load specification file at {}: {}", filename, e))?;
|
||||||
Spec::load(cache_dir, file)
|
Spec::load(params, file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ use ethcore::service::ClientService;
|
|||||||
use ethcore::snapshot;
|
use ethcore::snapshot;
|
||||||
use ethcore::verification::queue::VerifierSettings;
|
use ethcore::verification::queue::VerifierSettings;
|
||||||
use ethsync::{self, SyncConfig};
|
use ethsync::{self, SyncConfig};
|
||||||
|
use ethcore::spec::{SpecParams, OptimizeFor};
|
||||||
use fdlimit::raise_fd_limit;
|
use fdlimit::raise_fd_limit;
|
||||||
use hash_fetch::fetch::{Fetch, Client as FetchClient};
|
use hash_fetch::fetch::{Fetch, Client as FetchClient};
|
||||||
use informant::{Informant, LightNodeInformantData, FullNodeInformantData};
|
use informant::{Informant, LightNodeInformantData, FullNodeInformantData};
|
||||||
@ -175,7 +176,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
|
|||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
|
|
||||||
// load spec
|
// load spec
|
||||||
let spec = cmd.spec.spec(&cmd.dirs.cache)?;
|
let spec = cmd.spec.spec(SpecParams::new(cmd.dirs.cache.as_ref(), OptimizeFor::Memory))?;
|
||||||
|
|
||||||
// load genesis hash
|
// load genesis hash
|
||||||
let genesis_hash = spec.genesis_header().hash();
|
let genesis_hash = spec.genesis_header().hash();
|
||||||
|
@ -555,7 +555,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
|
|||||||
self.miner.map_sealing_work(&*self.client, |b| {
|
self.miner.map_sealing_work(&*self.client, |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 = self.seed_compute.lock().get_seedhash(b.block().header().number());
|
let seed_hash = self.seed_compute.lock().hash_block_number(b.block().header().number());
|
||||||
|
|
||||||
if no_new_work_timeout > 0 && b.block().header().timestamp() + no_new_work_timeout < get_time().sec as u64 {
|
if no_new_work_timeout > 0 && b.block().header().timestamp() + no_new_work_timeout < get_time().sec as u64 {
|
||||||
Err(errors::no_new_work())
|
Err(errors::no_new_work())
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
verbose=false
|
verbose=false
|
||||||
max_width=1000
|
max_width=100
|
||||||
comment_width=1000
|
comment_width=100
|
||||||
tab_spaces=4
|
tab_spaces=4
|
||||||
fn_call_width=1000
|
fn_call_width=100
|
||||||
struct_lit_width=32
|
struct_lit_width=32
|
||||||
fn_call_style="Visual"
|
fn_call_style="Visual"
|
||||||
single_line_if_else_max_width=100
|
single_line_if_else_max_width=100
|
||||||
@ -13,3 +13,4 @@ reorder_imports=true
|
|||||||
format_strings=false
|
format_strings=false
|
||||||
hard_tabs=true
|
hard_tabs=true
|
||||||
wrap_match_arms=false
|
wrap_match_arms=false
|
||||||
|
error_on_line_overflow=false
|
Loading…
Reference in New Issue
Block a user