spec loading cleanup (#858)

* spec loading cleanup in progress

* changed engine field in json spec

* refactored engine params

* polishing spec loading refactor

* fixed compiling json tests

* fixed compiling parity

* removed warnings

* removed commented out code

* fixed failing test

* bringing back removed TODO in spec.
This commit is contained in:
Marek Kotewicz
2016-04-09 19:20:35 +02:00
committed by Gav Wood
parent d823fd7685
commit 373284ca0a
46 changed files with 957 additions and 704 deletions

View File

@@ -19,42 +19,63 @@ extern crate ethash;
use self::ethash::{quick_get_difficulty, EthashManager, H256 as EH256};
use common::*;
use block::*;
use spec::*;
use spec::CommonParams;
use engine::*;
use evm::Schedule;
use evm::Factory;
use evm::{Schedule, Factory};
use ethjson;
/// Ethash params.
#[derive(Debug, PartialEq)]
pub struct EthashParams {
/// Tie breaking gas.
pub tie_breaking_gas: bool,
/// Gas limit divisor.
pub gas_limit_bound_divisor: U256,
/// Minimum difficulty.
pub minimum_difficulty: U256,
/// Difficulty bound divisor.
pub difficulty_bound_divisor: U256,
/// Block duration.
pub duration_limit: u64,
/// Block reward.
pub block_reward: U256,
/// Namereg contract address.
pub registrar: Address,
}
impl From<ethjson::spec::EthashParams> for EthashParams {
fn from(p: ethjson::spec::EthashParams) -> Self {
EthashParams {
tie_breaking_gas: p.tie_breaking_gas,
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
minimum_difficulty: p.minimum_difficulty.into(),
difficulty_bound_divisor: p.difficulty_bound_divisor.into(),
duration_limit: p.duration_limit.into(),
block_reward: p.block_reward.into(),
registrar: p.registrar.into(),
}
}
}
/// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum
/// mainnet chains in the Olympic, Frontier and Homestead eras.
pub struct Ethash {
spec: Spec,
params: CommonParams,
ethash_params: EthashParams,
builtins: BTreeMap<Address, Builtin>,
pow: EthashManager,
factory: Factory,
u64_params: RwLock<HashMap<String, u64>>,
u256_params: RwLock<HashMap<String, U256>>,
}
impl Ethash {
/// Create a new boxed instance of Ethash engine
pub fn new_boxed(spec: Spec) -> Box<Engine> {
Box::new(Ethash {
spec: spec,
pow: EthashManager::new(),
// TODO [todr] should this return any specific factory?
factory: Factory::default(),
u64_params: RwLock::new(HashMap::new()),
u256_params: RwLock::new(HashMap::new())
})
}
#[cfg(test)]
fn new_test(spec: Spec) -> Ethash {
/// Create a new instance of Ethash engine
pub fn new(params: CommonParams, ethash_params: EthashParams, builtins: BTreeMap<Address, Builtin>) -> Self {
Ethash {
spec: spec,
params: params,
ethash_params: ethash_params,
builtins: builtins,
pow: EthashManager::new(),
factory: Factory::default(),
u64_params: RwLock::new(HashMap::new()),
u256_params: RwLock::new(HashMap::new())
}
}
}
@@ -65,17 +86,23 @@ impl Engine for Ethash {
// Two fields - mix
fn seal_fields(&self) -> usize { 2 }
fn params(&self) -> &CommonParams { &self.params }
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
&self.builtins
}
/// Additional engine-specific information for the user/developer concerning `header`.
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
fn spec(&self) -> &Spec { &self.spec }
fn vm_factory(&self) -> &Factory {
&self.factory
}
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
trace!(target: "client", "Creating schedule. param={:?}, fCML={}", self.spec().engine_params.get("frontierCompatibilityModeLimit"), self.u64_param("frontierCompatibilityModeLimit"));
if env_info.number < self.u64_param("frontierCompatibilityModeLimit") {
trace!(target: "client", "Creating schedule. fCML={}", self.params.frontier_compatibility_mode_limit);
if env_info.number < self.params.frontier_compatibility_mode_limit {
Schedule::new_frontier()
} else {
Schedule::new_homestead()
@@ -86,7 +113,7 @@ impl Engine for Ethash {
header.difficulty = self.calculate_difficuty(header, parent);
header.gas_limit = {
let gas_limit = parent.gas_limit;
let bound_divisor = self.u256_param("gasLimitBoundDivisor");
let bound_divisor = self.ethash_params.gas_limit_bound_divisor;
if gas_limit < gas_floor_target {
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - x!(1))
} else {
@@ -100,7 +127,7 @@ impl Engine for Ethash {
/// Apply the block reward on finalisation of the block.
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
fn on_close_block(&self, block: &mut ExecutedBlock) {
let reward = self.spec().engine_params.get("blockReward").map_or(U256::from(0u64), |a| decode(&a));
let reward = self.ethash_params.block_reward;
let fields = block.fields_mut();
// Bestow block reward
@@ -125,7 +152,7 @@ impl Engine for Ethash {
try!(UntrustedRlp::new(&header.seal[1]).as_val::<H64>());
// TODO: consider removing these lines.
let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap());
let min_difficulty = self.ethash_params.minimum_difficulty;
if header.difficulty < min_difficulty {
return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty })))
}
@@ -170,7 +197,7 @@ impl Engine for Ethash {
if header.difficulty != expected_difficulty {
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty })))
}
let gas_limit_divisor = decode(self.spec().engine_params.get("gasLimitBoundDivisor").unwrap());
let gas_limit_divisor = self.ethash_params.gas_limit_bound_divisor;
let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor;
let max_gas = parent.gas_limit + parent.gas_limit / gas_limit_divisor;
if header.gas_limit <= min_gas || header.gas_limit >= max_gas {
@@ -180,7 +207,7 @@ impl Engine for Ethash {
}
fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
if header.number() >= self.u64_param("frontierCompatibilityModeLimit") {
if header.number() >= self.params.frontier_compatibility_mode_limit {
try!(t.check_low_s());
}
Ok(())
@@ -189,16 +216,6 @@ impl Engine for Ethash {
fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
t.sender().map(|_|()) // Perform EC recovery and cache sender
}
fn u64_param(&self, name: &str) -> u64 {
*self.u64_params.write().unwrap().entry(name.to_owned()).or_insert_with(||
self.spec().engine_params.get(name).map_or(0u64, |a| decode(&a)))
}
fn u256_param(&self, name: &str) -> U256 {
*self.u256_params.write().unwrap().entry(name.to_owned()).or_insert_with(||
self.spec().engine_params.get(name).map_or(x!(0), |a| decode(&a)))
}
}
#[cfg_attr(feature="dev", allow(wrong_self_convention))] // to_ethash should take self
@@ -209,10 +226,11 @@ impl Ethash {
panic!("Can't calculate genesis block difficulty");
}
let min_difficulty = self.u256_param("minimumDifficulty");
let difficulty_bound_divisor = self.u256_param("difficultyBoundDivisor");
let duration_limit = self.u64_param("durationLimit");
let frontier_limit = self.u64_param("frontierCompatibilityModeLimit");
let min_difficulty = self.ethash_params.minimum_difficulty;
let difficulty_bound_divisor = self.ethash_params.difficulty_bound_divisor;
let duration_limit = self.ethash_params.duration_limit;
let frontier_limit = self.params.frontier_compatibility_mode_limit;
let mut target = if header.number < frontier_limit {
if header.timestamp >= parent.timestamp + duration_limit {
parent.difficulty - (parent.difficulty / difficulty_bound_divisor)
@@ -283,16 +301,16 @@ mod tests {
use block::*;
use engine::*;
use tests::helpers::*;
use super::{Ethash};
use super::super::new_morden;
#[test]
fn on_close_block() {
let engine = new_morden().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let spec = new_morden();
let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
engine.spec().ensure_db_good(db.as_hashdb_mut());
spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()];
let b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]);
let b = b.close();
@@ -301,11 +319,12 @@ mod tests {
#[test]
fn on_close_block_with_uncle() {
let engine = new_morden().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let spec = new_morden();
let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
engine.spec().ensure_db_good(db.as_hashdb_mut());
spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()];
let mut b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]);
let mut uncle = Header::new();
@@ -320,27 +339,20 @@ mod tests {
#[test]
fn has_valid_metadata() {
let engine = Ethash::new_boxed(new_morden());
let engine = new_morden().engine;
assert!(!engine.name().is_empty());
assert!(engine.version().major >= 1);
}
#[test]
fn can_return_params() {
let engine = Ethash::new_test(new_morden());
assert!(engine.u64_param("durationLimit") > 0);
assert!(engine.u256_param("minimumDifficulty") > U256::zero());
}
#[test]
fn can_return_factory() {
let engine = Ethash::new_test(new_morden());
let engine = new_morden().engine;
engine.vm_factory();
}
#[test]
fn can_return_schedule() {
let engine = Ethash::new_test(new_morden());
let engine = new_morden().engine;
let schedule = engine.schedule(&EnvInfo {
number: 10000000,
author: x!(0),
@@ -368,7 +380,8 @@ mod tests {
#[test]
fn can_do_seal_verification_fail() {
let engine = Ethash::new_test(new_morden());
let engine = new_morden().engine;
//let engine = Ethash::new_test(new_morden());
let header: Header = Header::default();
let verify_result = engine.verify_block_basic(&header, None);
@@ -382,7 +395,7 @@ mod tests {
#[test]
fn can_do_difficulty_verification_fail() {
let engine = Ethash::new_test(new_morden());
let engine = new_morden().engine;
let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
@@ -397,7 +410,7 @@ mod tests {
#[test]
fn can_do_proof_of_work_verification_fail() {
let engine = Ethash::new_test(new_morden());
let engine = new_morden().engine;
let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
@@ -413,7 +426,7 @@ mod tests {
#[test]
fn can_do_seal_unordered_verification_fail() {
let engine = Ethash::new_test(new_morden());
let engine = new_morden().engine;
let header: Header = Header::default();
let verify_result = engine.verify_block_unordered(&header, None);
@@ -427,7 +440,7 @@ mod tests {
#[test]
fn can_do_seal256_verification_fail() {
let engine = Ethash::new_test(new_morden());
let engine = new_morden().engine;
let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
let verify_result = engine.verify_block_unordered(&header, None);
@@ -441,7 +454,7 @@ mod tests {
#[test]
fn can_do_proof_of_work_unordered_verification_fail() {
let engine = Ethash::new_test(new_morden());
let engine = new_morden().engine;
let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
@@ -457,7 +470,7 @@ mod tests {
#[test]
fn can_verify_block_family_genesis_fail() {
let engine = Ethash::new_test(new_morden());
let engine = new_morden().engine;
let header: Header = Header::default();
let parent_header: Header = Header::default();
@@ -472,7 +485,7 @@ mod tests {
#[test]
fn can_verify_block_family_difficulty_fail() {
let engine = Ethash::new_test(new_morden());
let engine = new_morden().engine;
let mut header: Header = Header::default();
header.set_number(2);
let mut parent_header: Header = Header::default();
@@ -489,7 +502,7 @@ mod tests {
#[test]
fn can_verify_block_family_gas_fail() {
let engine = Ethash::new_test(new_morden());
let engine = new_morden().engine;
let mut header: Header = Header::default();
header.set_number(2);
header.set_difficulty(U256::from_str("0000000000000000000000000000000000000000000000000000000000020000").unwrap());

View File

@@ -30,22 +30,22 @@ pub use self::denominations::*;
use super::spec::*;
/// Create a new Olympic chain spec.
pub fn new_olympic() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/olympic.json")) }
pub fn new_olympic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/olympic.json")) }
/// Create a new Frontier mainnet chain spec.
pub fn new_frontier() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier.json")) }
pub fn new_frontier() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier.json")) }
/// Create a new Frontier chain spec as though it never changes to Homestead.
pub fn new_frontier_test() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier_test.json")) }
pub fn new_frontier_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier_test.json")) }
/// Create a new Homestead chain spec as though it never changed from Frontier.
pub fn new_homestead_test() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/homestead_test.json")) }
pub fn new_homestead_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/homestead_test.json")) }
/// Create a new Frontier main net chain spec without genesis accounts.
pub fn new_mainnet_like() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
pub fn new_mainnet_like() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
/// Create a new Morden chain spec.
pub fn new_morden() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/morden.json")) }
pub fn new_morden() -> Spec { Spec::load(include_bytes!("../../res/ethereum/morden.json")) }
#[cfg(test)]
mod tests {
@@ -57,11 +57,12 @@ mod tests {
#[test]
fn ensure_db_good() {
let engine = new_morden().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let spec = new_morden();
let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
engine.spec().ensure_db_good(db.as_hashdb_mut());
spec.ensure_db_good(db.as_hashdb_mut());
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce());
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64));
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), U256::from(1u64));
@@ -79,7 +80,7 @@ mod tests {
let genesis = morden.genesis_block();
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
let _ = morden.to_engine();
let _ = morden.engine;
}
#[test]
@@ -90,6 +91,6 @@ mod tests {
let genesis = frontier.genesis_block();
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap());
let _ = frontier.to_engine();
let _ = frontier.engine;
}
}