Extract spec to own crate (#10978)

* Add client-traits crate
Move the BlockInfo trait to new crate

* New crate `machine`
Contains code extracted from ethcore that defines `Machine`, `Externalities` and other execution related code.

* Use new machine and client-traits crates in ethcore

* Use new crates machine and client-traits instead of ethcore where appropriate

* Fix tests

* Don't re-export so many types from ethcore::client

* Fixing more fallout from removing re-export

* fix test

* More fallout from not re-exporting types

* Add some docs

* cleanup

* import the macro edition style

* Tweak docs

* Add missing import

* remove unused ethabi_derive imports

* Use latest ethabi-contract

* Move many traits from ethcore/client/traits to client-traits crate
Initial version of extracted Engine trait

* Move snapshot related traits to the engine crate (eew)

* Move a few snapshot related types to common_types
Cleanup Executed as exported from machine crate

* fix warning

* Gradually introduce new engine crate: snapshot

* ethcore typechecks with new engine crate

* Sort out types outside ethcore

* Add an EpochVerifier to ethash and use that in Engine.epoch_verifier()
Cleanup

* Document pub members

* Sort out tests
Sort out default impls for EpochVerifier

* Add test-helpers feature and move EngineSigner impl to the right place

* Sort out tests

* Sort out tests and refactor verification types

* Fix missing traits

* More missing traits
Fix Histogram

* Fix tests and cleanup

* cleanup

* Put back needed logger import

* Don't rexport common_types from ethcore/src/client
Don't export ethcore::client::*

* Remove files no longer used
Use types from the engine crate
Explicit exports from engine::engine

* Get rid of itertools

* Move a few more traits from ethcore to client-traits: BlockChainReset, ScheduleInfo, StateClient

* Move ProvingBlockChainClient to client-traits

* Don't re-export ForkChoice and Transition from ethcore

* Address grumbles: sort imports, remove commented out code

* Fix merge resolution error

* Extract the Clique engine to own crate

* Extract NullEngine and the block_reward module from ethcore

* Extract InstantSeal engine to own crate

* Extract remaining engines

* Extract executive_state to own crate so it can be used by engine crates

* Remove snapshot stuff from the engine crate

* Put snapshot traits back in ethcore

* cleanup

* Remove stuff from ethcore

* Don't use itertools

* itertools in aura is legit-ish

* More post-merge fixes

* Re-export less types in client

* cleanup

* Extract spec to own crate

* Put back the test-helpers from basic-authority

* Fix ethcore benchmarks

* Reduce the public api of ethcore/verification

* Update ethcore/block-reward/Cargo.toml

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update ethcore/engines/basic-authority/Cargo.toml

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update ethcore/engines/ethash/Cargo.toml

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update ethcore/engines/clique/src/lib.rs

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* signers is already a ref

* Add an EngineType enum to tighten up Engine.name()

* Introduce Snapshotting enum to distinguish the type of snapshots a chain uses

* Rename supports_warp to snapshot_mode

* Missing import

* Update ethcore/src/snapshot/consensus/mod.rs

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* missing import

* Fix import

* double semi
This commit is contained in:
David
2019-08-23 15:32:58 +02:00
committed by Wei Tang
parent 79b671f6c7
commit fbf425c4e2
80 changed files with 341 additions and 204 deletions

View File

@@ -500,7 +500,7 @@ mod tests {
verification::Unverified,
};
use hash_db::EMPTY_PREFIX;
use crate::spec;
use spec;
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
fn enact_bytes(

View File

@@ -58,6 +58,7 @@ use types::{
client_types::Mode,
blockchain_info::BlockChainInfo,
block_status::BlockStatus,
verification::VerificationQueueInfo as BlockQueueInfo,
};
use vm::{Schedule, LastHashes};
@@ -80,7 +81,6 @@ use spec::{Spec, self};
use account_state::state::StateInfo;
use state_db::StateDB;
use trace::LocalizedTrace;
use verification::queue::QueueInfo as BlockQueueInfo;
/// Test client.
pub struct TestBlockChainClient {

View File

@@ -39,7 +39,7 @@ use ethtrie;
use rlp::RlpStream;
use hash::keccak;
use ethereum_types::BigEndianHash;
use crate::spec;
use spec;
use super::HookType;

View File

@@ -55,32 +55,23 @@
extern crate account_db;
extern crate account_state;
extern crate authority_round;
extern crate ansi_term;
extern crate basic_authority;
extern crate client_traits;
extern crate common_types as types;
extern crate clique;
extern crate crossbeam_utils;
extern crate engine;
extern crate ethabi;
extern crate ethash;
extern crate ethash_engine;
extern crate ethcore_blockchain as blockchain;
extern crate ethcore_bloom_journal as bloom_journal;
extern crate ethcore_builtin as builtin;
extern crate ethcore_call_contract as call_contract;
extern crate ethcore_db as db;
extern crate ethcore_io as io;
extern crate ethcore_miner;
extern crate ethereum_types;
extern crate ethjson;
extern crate ethkey;
extern crate executive_state;
extern crate trie_vm_factories;
extern crate futures;
extern crate hash_db;
extern crate instant_seal;
extern crate itertools;
extern crate journaldb;
extern crate keccak_hash as hash;
@@ -92,12 +83,10 @@ extern crate kvdb_memorydb;
extern crate len_caching_lock;
extern crate machine;
extern crate memory_cache;
extern crate null_engine;
extern crate num_cpus;
extern crate parity_bytes as bytes;
extern crate parity_snappy as snappy;
extern crate parking_lot;
extern crate pod;
extern crate trie_db as trie;
extern crate patricia_trie_ethereum as ethtrie;
extern crate rand;
@@ -108,6 +97,7 @@ extern crate parity_util_mem as malloc_size_of;
#[cfg(any(test, feature = "test-helpers"))]
extern crate rustc_hex;
extern crate serde;
extern crate spec;
extern crate state_db;
extern crate time_utils;
extern crate trace;
@@ -122,6 +112,13 @@ extern crate rand_xorshift;
extern crate ethcore_accounts as accounts;
#[cfg(feature = "stratum")]
extern crate ethcore_stratum;
#[cfg(any(test, feature = "stratum"))]
extern crate ethash;
#[cfg(any(test, feature = "test-helpers"))]
extern crate ethkey;
#[cfg(any(test, feature = "test-helpers"))]
extern crate ethjson;
#[cfg(any(test, feature = "tempdir"))]
extern crate tempdir;
#[cfg(any(test, feature = "kvdb-rocksdb"))]
@@ -129,9 +126,13 @@ extern crate kvdb_rocksdb;
#[cfg(any(test, feature = "json-tests"))]
#[macro_use]
extern crate lazy_static;
#[cfg(any(test, feature = "json-tests", feature = "test-helpers", feature = "parity"))]
#[cfg(any(test, feature = "test-helpers"))]
#[macro_use]
extern crate macros;
#[cfg(test)]
extern crate null_engine;
#[cfg(any(test, feature = "test-helpers"))]
extern crate pod;
#[cfg(any(test, feature = "blooms-db"))]
extern crate blooms_db;
#[cfg(any(test, feature = "env_logger"))]
@@ -161,7 +162,6 @@ pub mod block;
pub mod client;
pub mod miner;
pub mod snapshot;
pub mod spec;
pub mod verification;
#[cfg(test)]

View File

@@ -1498,7 +1498,7 @@ mod tests {
BlockNumber,
transaction::Transaction
};
use crate::spec;
use spec;
#[test]
fn should_prepare_block_to_seal() {

View File

@@ -907,7 +907,7 @@ impl Drop for Service {
mod tests {
use client::ClientIoMessage;
use io::{IoService};
use crate::spec;
use spec;
use journaldb::Algorithm;
use snapshot::SnapshotService;
use super::*;

View File

@@ -33,7 +33,7 @@ use parking_lot::Mutex;
use snappy;
use kvdb::DBTransaction;
use test_helpers;
use crate::spec;
use spec;
const SNAPSHOT_MODE: ::snapshot::PowSnapshot = ::snapshot::PowSnapshot { blocks: 30000, max_restore_blocks: 30000 };

View File

@@ -32,7 +32,7 @@ use types::{
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
use snapshot::service::{Service, ServiceParams};
use snapshot::{chunk_state, chunk_secondary, SnapshotService};
use crate::spec;
use spec;
use test_helpers::{new_db, new_temp_db, generate_dummy_client_with_spec_and_data, restoration_db_handler};
use parking_lot::Mutex;

View File

@@ -1,164 +0,0 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! Load chain specifications for all chains supported by the parity-ethereum client.
macro_rules! bundle_release_spec {
($($path: expr => $name: ident), *) => {
$(
/// Bundled release spec
pub fn $name<'a, T: Into<crate::spec::SpecParams<'a>>>(params: T) -> crate::spec::Spec {
let params = params.into();
crate::spec::Spec::load(
params,
include_bytes!(concat!("../../res/", $path, ".json")) as &[u8]
).expect(concat!("Chain spec ", $path, " is invalid."))
}
)*
}
}
macro_rules! bundle_test_spec {
($($path: expr => $name: ident), *) => {
$(
/// Bundled test spec
pub fn $name() -> crate::spec::Spec {
crate::spec::Spec::load(
&::std::env::temp_dir(),
include_bytes!(concat!("../../res/", $path, ".json")) as &[u8]
).expect(concat!("Chain spec ", $path, " is invalid."))
}
)*
}
}
macro_rules! bundle_test_machine {
($($path: expr => $name: ident), *) => {
$(
/// Bundled test spec
pub fn $name() -> crate::machine::Machine {
crate::spec::Spec::load_machine(
include_bytes!(concat!("../../res/", $path, ".json")) as &[u8]
).expect(concat!("Chain spec ", $path, " is invalid."))
}
)*
}
}
bundle_release_spec! {
"ethereum/callisto" => new_callisto,
"ethereum/classic" => new_classic,
"ethereum/ellaism" => new_ellaism,
"ethereum/expanse" => new_expanse,
"ethereum/foundation" => new_foundation,
"ethereum/goerli" => new_goerli,
"ethereum/kotti" => new_kotti,
"ethereum/kovan" => new_kovan,
"ethereum/mix" => new_mix,
"ethereum/morden" => new_morden,
"ethereum/musicoin" => new_musicoin,
"ethereum/poacore" => new_poanet,
"ethereum/poasokol" => new_sokol,
"ethereum/rinkeby" => new_rinkeby,
"ethereum/ropsten" => new_ropsten,
"ethereum/volta" => new_volta,
"ethereum/ewc" => new_ewc
}
bundle_test_spec! {
"authority_round" => new_test_round,
"authority_round_block_reward_contract" => new_test_round_block_reward_contract,
"authority_round_empty_steps" => new_test_round_empty_steps,
"constructor" => new_test_constructor,
"ethereum/byzantium_test" => new_byzantium_test,
"ethereum/constantinople_test" => new_constantinople_test,
"ethereum/eip150_test" => new_eip150_test,
"ethereum/eip161_test" => new_eip161_test,
"ethereum/eip210_test" => new_eip210_test,
"ethereum/frontier_like_test" => new_mainnet_like,
"ethereum/frontier_test" => new_frontier_test,
"ethereum/homestead_test" => new_homestead_test,
"ethereum/kovan_wasm_test" => new_kovan_wasm_test,
"ethereum/mcip3_test" => new_mcip3_test,
"ethereum/morden" => new_morden_test,
"ethereum/ropsten" => new_ropsten_test,
"ethereum/st_peters_test" => new_constantinople_fix_test,
"ethereum/transition_test" => new_transition_test,
"instant_seal" => new_instant,
"null" => new_null,
"null_morden" => new_test,
"null_morden_with_reward" => new_test_with_reward,
"validator_contract" => new_validator_contract,
"validator_multi" => new_validator_multi,
"validator_safe_contract" => new_validator_safe_contract
}
bundle_test_machine! {
"ethereum/byzantium_test" => new_byzantium_test_machine,
"ethereum/constantinople_test" => new_constantinople_test_machine,
"ethereum/eip210_test" => new_eip210_test_machine,
"ethereum/frontier_test" => new_frontier_test_machine,
"ethereum/homestead_test" => new_homestead_test_machine,
"ethereum/kovan_wasm_test" => new_kovan_wasm_test_machine,
"null_morden" => new_test_machine
}
#[cfg(test)]
mod tests {
use account_state::State;
use ethereum_types::U256;
use tempdir::TempDir;
use test_helpers::get_temp_state_db;
use types::{view, views::BlockView};
use super::{new_morden, new_foundation};
#[test]
fn ensure_db_good() {
let tempdir = TempDir::new("").unwrap();
let spec = new_morden(&tempdir.path());
let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
let s = State::from_existing(db, genesis_header.state_root().clone(), engine.account_start_nonce(0), Default::default()).unwrap();
assert_eq!(s.balance(&"0000000000000000000000000000000000000001".parse().unwrap()).unwrap(), 1u64.into());
assert_eq!(s.balance(&"0000000000000000000000000000000000000002".parse().unwrap()).unwrap(), 1u64.into());
assert_eq!(s.balance(&"0000000000000000000000000000000000000003".parse().unwrap()).unwrap(), 1u64.into());
assert_eq!(s.balance(&"0000000000000000000000000000000000000004".parse().unwrap()).unwrap(), 1u64.into());
assert_eq!(s.balance(&"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c".parse().unwrap()).unwrap(), U256::from(1u64) << 200);
assert_eq!(s.balance(&"0000000000000000000000000000000000000000".parse().unwrap()).unwrap(), 0u64.into());
}
#[test]
fn morden() {
let tempdir = TempDir::new("").unwrap();
let morden = new_morden(&tempdir.path());
assert_eq!(morden.state_root, "f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9".parse().unwrap());
let genesis = morden.genesis_block();
assert_eq!(view!(BlockView, &genesis).header_view().hash(), "0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303".parse().unwrap());
}
#[test]
fn frontier() {
let tempdir = TempDir::new("").unwrap();
let frontier = new_foundation(&tempdir.path());
assert_eq!(frontier.state_root, "d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544".parse().unwrap());
let genesis = frontier.genesis_block();
assert_eq!(view!(BlockView, &genesis).header_view().hash(), "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3".parse().unwrap());
}
}

View File

@@ -1,64 +0,0 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
use ethereum_types::{H256, U256, Address};
use ethjson;
use hash::KECCAK_NULL_RLP;
use spec::seal::Seal;
/// Genesis components.
pub struct Genesis {
/// Seal.
pub seal: Seal,
/// Difficulty.
pub difficulty: U256,
/// Author.
pub author: Address,
/// Timestamp.
pub timestamp: u64,
/// Parent hash.
pub parent_hash: H256,
/// Gas limit.
pub gas_limit: U256,
/// Transactions root.
pub transactions_root: H256,
/// Receipts root.
pub receipts_root: H256,
/// State root.
pub state_root: Option<H256>,
/// Gas used.
pub gas_used: U256,
/// Extra data.
pub extra_data: Vec<u8>,
}
impl From<ethjson::spec::Genesis> for Genesis {
fn from(g: ethjson::spec::Genesis) -> Self {
Genesis {
seal: From::from(g.seal),
difficulty: g.difficulty.into(),
author: g.author.map_or_else(Address::zero, Into::into),
timestamp: g.timestamp.map_or(0, Into::into),
parent_hash: g.parent_hash.map_or_else(H256::zero, Into::into),
gas_limit: g.gas_limit.into(),
transactions_root: g.transactions_root.map_or_else(|| KECCAK_NULL_RLP.clone(), Into::into),
receipts_root: g.receipts_root.map_or_else(|| KECCAK_NULL_RLP.clone(), Into::into),
state_root: g.state_root.map(Into::into),
gas_used: g.gas_used.map_or_else(U256::zero, Into::into),
extra_data: g.extra_data.map_or_else(Vec::new, Into::into),
}
}
}

View File

@@ -1,26 +0,0 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! Blockchain params.
mod chain;
mod genesis;
mod seal;
mod spec;
pub use self::chain::*;
pub use self::genesis::Genesis;
pub use self::spec::{Spec, SpecHardcodedSync, SpecParams, OptimizeFor};

View File

@@ -1,120 +0,0 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! Spec seal.
use rlp::RlpStream;
use ethereum_types::{H64, H256, H520};
use ethjson;
/// Classic ethereum seal.
pub struct Ethereum {
/// Seal nonce.
pub nonce: H64,
/// Seal mix hash.
pub mix_hash: H256,
}
impl Into<Generic> for Ethereum {
fn into(self) -> Generic {
let mut s = RlpStream::new_list(2);
s.append(&self.mix_hash).append(&self.nonce);
Generic(s.out())
}
}
/// AuthorityRound seal.
pub struct AuthorityRound {
/// Seal step.
pub step: usize,
/// Seal signature.
pub signature: H520,
}
/// Tendermint seal.
pub struct Tendermint {
/// Seal round.
pub round: usize,
/// Proposal seal signature.
pub proposal: H520,
/// Precommit seal signatures.
pub precommits: Vec<H520>,
}
impl Into<Generic> for AuthorityRound {
fn into(self) -> Generic {
let mut s = RlpStream::new_list(2);
s.append(&self.step).append(&self.signature);
Generic(s.out())
}
}
impl Into<Generic> for Tendermint {
fn into(self) -> Generic {
let mut stream = RlpStream::new_list(3);
stream
.append(&self.round)
.append(&self.proposal)
.append_list(&self.precommits);
Generic(stream.out())
}
}
pub struct Generic(pub Vec<u8>);
/// Genesis seal type.
pub enum Seal {
/// Classic ethereum seal.
Ethereum(Ethereum),
/// AuthorityRound seal.
AuthorityRound(AuthorityRound),
/// Tendermint seal.
Tendermint(Tendermint),
/// Generic RLP seal.
Generic(Generic),
}
impl From<ethjson::spec::Seal> for Seal {
fn from(s: ethjson::spec::Seal) -> Self {
match s {
ethjson::spec::Seal::Ethereum(eth) => Seal::Ethereum(Ethereum {
nonce: eth.nonce.into(),
mix_hash: eth.mix_hash.into()
}),
ethjson::spec::Seal::AuthorityRound(ar) => Seal::AuthorityRound(AuthorityRound {
step: ar.step.into(),
signature: ar.signature.into()
}),
ethjson::spec::Seal::Tendermint(tender) => Seal::Tendermint(Tendermint {
round: tender.round.into(),
proposal: tender.proposal.into(),
precommits: tender.precommits.into_iter().map(Into::into).collect()
}),
ethjson::spec::Seal::Generic(g) => Seal::Generic(Generic(g.into())),
}
}
}
impl Into<Generic> for Seal {
fn into(self) -> Generic {
match self {
Seal::Generic(generic) => generic,
Seal::Ethereum(eth) => eth.into(),
Seal::AuthorityRound(ar) => ar.into(),
Seal::Tendermint(tender) => tender.into(),
}
}
}

View File

@@ -1,596 +0,0 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! Parameters for a block chain.
use std::{
collections::BTreeMap,
fmt,
io::Read,
path::Path,
sync::Arc,
};
use bytes::Bytes;
use ethereum_types::{H256, Bloom, U256, Address};
use ethjson;
use hash::{KECCAK_NULL_RLP, keccak};
use rlp::{Rlp, RlpStream};
use types::{
BlockNumber,
header::Header,
encoded,
engines::params::CommonParams,
errors::EthcoreError as Error,
};
use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType};
use builtin::Builtin;
use engine::Engine;
use clique::Clique;
use null_engine::NullEngine;
use instant_seal::{InstantSeal, InstantSealParams};
use authority_round::AuthorityRound;
use basic_authority::BasicAuthority;
use ethash_engine::Ethash;
use machine::{
executive::Executive,
machine::Machine,
substate::Substate,
};
use trie_vm_factories::Factories;
use pod::PodState;
use spec::Genesis;
use spec::seal::Generic as GenericSeal;
use account_state::{Backend, State, backend::Basic as BasicBackend};
use trace::{NoopTracer, NoopVMTracer};
pub use ethash::OptimizeFor;
/// Runtime parameters for the spec that are related to how the software should run the chain,
/// rather than integral properties of the chain itself.
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())
}
}
/// given a pre-constructor state, run all the given constructors and produce a new state and
/// state root.
fn run_constructors<T: Backend>(
genesis_state: &PodState,
constructors: &[(Address, Bytes)],
engine: &dyn Engine,
author: Address,
timestamp: u64,
difficulty: U256,
factories: &Factories,
mut db: T
) -> Result<(H256, T), Error> {
let mut root = KECCAK_NULL_RLP;
// basic accounts in spec.
{
let mut t = factories.trie.create(db.as_hash_db_mut(), &mut root);
for (address, account) in genesis_state.get().iter() {
t.insert(address.as_bytes(), &account.rlp())?;
}
}
for (address, account) in genesis_state.get().iter() {
db.note_non_null_account(address);
account.insert_additional(
&mut *factories.accountdb.create(
db.as_hash_db_mut(),
keccak(address),
),
&factories.trie,
);
}
let start_nonce = engine.account_start_nonce(0);
let mut state = State::from_existing(db, root, start_nonce, factories.clone())?;
// Execute contract constructors.
let env_info = EnvInfo {
number: 0,
author,
timestamp,
difficulty,
last_hashes: Default::default(),
gas_used: U256::zero(),
gas_limit: U256::max_value(),
};
let from = Address::zero();
for &(ref address, ref constructor) in constructors.iter() {
trace!(target: "spec", "run_constructors: Creating a contract at {}.", address);
trace!(target: "spec", " .. root before = {}", state.root());
let params = ActionParams {
code_address: address.clone(),
code_hash: Some(keccak(constructor)),
code_version: U256::zero(),
address: address.clone(),
sender: from.clone(),
origin: from.clone(),
gas: U256::max_value(),
gas_price: Default::default(),
value: ActionValue::Transfer(Default::default()),
code: Some(Arc::new(constructor.clone())),
data: None,
call_type: CallType::None,
params_type: ParamsType::Embedded,
};
let mut substate = Substate::new();
{
let machine = engine.machine();
let schedule = machine.schedule(env_info.number);
let mut exec = Executive::new(&mut state, &env_info, &machine, &schedule);
// failing create is not a bug
if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) {
warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e);
}
}
let _ = state.commit()?;
}
Ok(state.drop())
}
/// 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.
pub struct Spec {
/// User friendly spec name.
pub name: String,
/// Engine specified by json file.
pub engine: Arc<dyn Engine>,
/// Name of the subdir inside the main data dir to use for chain data and settings.
pub data_dir: String,
/// Known nodes on the network in enode format.
pub nodes: Vec<String>,
/// The genesis block's parent hash field.
pub parent_hash: H256,
/// The genesis block's author field.
pub author: Address,
/// The genesis block's difficulty field.
pub difficulty: U256,
/// The genesis block's gas limit field.
pub gas_limit: U256,
/// The genesis block's gas used field.
pub gas_used: U256,
/// The genesis block's timestamp field.
pub timestamp: u64,
/// Transactions root of the genesis block. Should be KECCAK_NULL_RLP.
pub transactions_root: H256,
/// Receipts root of the genesis block. Should be KECCAK_NULL_RLP.
pub receipts_root: H256,
/// The genesis block's extra data field.
pub extra_data: Bytes,
/// Each seal field, expressed as RLP, concatenated.
pub seal_rlp: Bytes,
/// Hardcoded synchronization. Allows the light client to immediately jump to a specific block.
pub hardcoded_sync: Option<SpecHardcodedSync>,
/// Contract constructors to be executed on genesis.
pub constructors: Vec<(Address, Bytes)>,
/// May be prepopulated if we know this in advance.
pub state_root: H256,
/// Genesis state as plain old data.
pub genesis_state: PodState,
}
/// Part of `Spec`. Describes the hardcoded synchronization parameters.
pub struct SpecHardcodedSync {
/// Header of the block to jump to for hardcoded sync, and total difficulty.
pub header: encoded::Header,
/// Total difficulty of the block to jump to.
pub total_difficulty: U256,
/// List of hardcoded CHTs, in order. If `hardcoded_sync` is set, the CHTs should include the
/// header of `hardcoded_sync`.
pub chts: Vec<H256>,
}
impl From<ethjson::spec::HardcodedSync> for SpecHardcodedSync {
fn from(sync: ethjson::spec::HardcodedSync) -> Self {
SpecHardcodedSync {
header: encoded::Header::new(sync.header.into()),
total_difficulty: sync.total_difficulty.into(),
chts: sync.chts.into_iter().map(Into::into).collect(),
}
}
}
impl fmt::Display for SpecHardcodedSync {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "{{")?;
writeln!(f, r#"header": "{:?},"#, self.header)?;
writeln!(f, r#"total_difficulty": "{:?},"#, self.total_difficulty)?;
writeln!(f, r#"chts": {:#?}"#, self.chts.iter().map(|x| format!(r#"{}"#, x)).collect::<Vec<_>>())?;
writeln!(f, "}}")
}
}
/// Load from JSON object.
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 g = Genesis::from(s.genesis);
let GenericSeal(seal_rlp) = g.seal.into();
let params = CommonParams::from(s.params);
let hardcoded_sync = s.hardcoded_sync.map(Into::into);
let engine = Spec::engine(spec_params, s.engine, params, builtins);
let author = g.author;
let timestamp = g.timestamp;
let difficulty = g.difficulty;
let constructors: Vec<_> = s.accounts
.constructors()
.into_iter()
.map(|(a, c)| (a.into(), c.into()))
.collect();
let genesis_state: PodState = s.accounts.into();
let (state_root, _) = run_constructors(
&genesis_state,
&constructors,
&*engine,
author,
timestamp,
difficulty,
&Default::default(),
BasicBackend(journaldb::new_memory_db()),
)?;
let s = Spec {
engine,
name: s.name.clone().into(),
data_dir: s.data_dir.unwrap_or(s.name).into(),
nodes: s.nodes.unwrap_or_else(Vec::new),
parent_hash: g.parent_hash,
transactions_root: g.transactions_root,
receipts_root: g.receipts_root,
author,
difficulty,
gas_limit: g.gas_limit,
gas_used: g.gas_used,
timestamp,
extra_data: g.extra_data,
seal_rlp,
hardcoded_sync,
constructors,
genesis_state,
state_root,
};
Ok(s)
}
impl Spec {
// create an instance of an Ethereum state machine, minus consensus logic.
fn machine(
engine_spec: &ethjson::spec::Engine,
params: CommonParams,
builtins: BTreeMap<Address, Builtin>,
) -> Machine {
if let ethjson::spec::Engine::Ethash(ref ethash) = *engine_spec {
Machine::with_ethash_extensions(params, builtins, ethash.params.clone().into())
} else {
Machine::regular(params, builtins)
}
}
/// 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.
fn engine(
spec_params: SpecParams,
engine_spec: ethjson::spec::Engine,
params: CommonParams,
builtins: BTreeMap<Address, Builtin>,
) -> Arc<dyn Engine> {
let machine = Self::machine(&engine_spec, params, builtins);
match engine_spec {
ethjson::spec::Engine::Null(null) => Arc::new(NullEngine::new(null.params.into(), machine)),
ethjson::spec::Engine::Ethash(ethash) => Arc::new(Ethash::new(spec_params.cache_dir, ethash.params.into(), machine, spec_params.optimization_setting)),
ethjson::spec::Engine::InstantSeal(Some(instant_seal)) => Arc::new(InstantSeal::new(instant_seal.params.into(), machine)),
ethjson::spec::Engine::InstantSeal(None) => Arc::new(InstantSeal::new(InstantSealParams::default(), machine)),
ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(basic_authority.params.into(), machine)),
ethjson::spec::Engine::Clique(clique) => Clique::new(clique.params.into(), machine)
.expect("Failed to start Clique consensus engine."),
ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(authority_round.params.into(), machine)
.expect("Failed to start AuthorityRound consensus engine."),
}
}
/// Get common blockchain parameters.
pub fn params(&self) -> &CommonParams {
&self.engine.params()
}
/// Get the configured Network ID.
pub fn network_id(&self) -> u64 {
self.params().network_id
}
/// Get the chain ID used for signing.
pub fn chain_id(&self) -> u64 {
self.params().chain_id
}
/// Get the configured subprotocol name.
pub fn subprotocol_name(&self) -> String {
self.params().subprotocol_name.clone()
}
/// Get the configured network fork block.
pub fn fork_block(&self) -> Option<(BlockNumber, H256)> {
self.params().fork_block
}
/// Get the header of the genesis block.
pub fn genesis_header(&self) -> Header {
let mut header: Header = Default::default();
header.set_parent_hash(self.parent_hash.clone());
header.set_timestamp(self.timestamp);
header.set_number(0);
header.set_author(self.author.clone());
header.set_transactions_root(self.transactions_root.clone());
header.set_uncles_hash(keccak(RlpStream::new_list(0).out()));
header.set_extra_data(self.extra_data.clone());
header.set_state_root(self.state_root);
header.set_receipts_root(self.receipts_root.clone());
header.set_log_bloom(Bloom::default());
header.set_gas_used(self.gas_used.clone());
header.set_gas_limit(self.gas_limit.clone());
header.set_difficulty(self.difficulty.clone());
header.set_seal({
let r = Rlp::new(&self.seal_rlp);
r.iter().map(|f| f.as_raw().to_vec()).collect()
});
trace!(target: "spec", "Header hash is {}", header.hash());
header
}
/// Compose the genesis block for this chain.
pub fn genesis_block(&self) -> Bytes {
let empty_list = RlpStream::new_list(0).out();
let header = self.genesis_header();
let mut ret = RlpStream::new_list(3);
ret.append(&header);
ret.append_raw(&empty_list, 1);
ret.append_raw(&empty_list, 1);
ret.out()
}
/// Overwrite the genesis components.
pub fn overwrite_genesis_params(&mut self, g: Genesis) {
let GenericSeal(seal_rlp) = g.seal.into();
self.parent_hash = g.parent_hash;
self.transactions_root = g.transactions_root;
self.receipts_root = g.receipts_root;
self.author = g.author;
self.difficulty = g.difficulty;
self.gas_limit = g.gas_limit;
self.gas_used = g.gas_used;
self.timestamp = g.timestamp;
self.extra_data = g.extra_data;
self.seal_rlp = seal_rlp;
}
/// Alter the value of the genesis state.
pub fn set_genesis_state(&mut self, s: PodState) -> Result<(), Error> {
self.genesis_state = s;
let (root, _) = run_constructors(
&self.genesis_state,
&self.constructors,
&*self.engine,
self.author,
self.timestamp,
self.difficulty,
&Default::default(),
BasicBackend(journaldb::new_memory_db()),
)?;
self.state_root = root;
Ok(())
}
/// 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> {
if db.as_hash_db().contains(&self.state_root, hash_db::EMPTY_PREFIX) {
return Ok(db);
}
// TODO: could optimize so we don't re-run, but `ensure_db_good` is barely ever
// called anyway.
let (root, db) = run_constructors(
&self.genesis_state,
&self.constructors,
&*self.engine,
self.author,
self.timestamp,
self.difficulty,
factories,
db
)?;
assert_eq!(root, self.state_root, "Spec's state root has not been precomputed correctly.");
Ok(db)
}
/// Loads just the state machine from a json file.
pub fn load_machine<R: Read>(reader: R) -> Result<Machine, Error> {
ethjson::spec::Spec::load(reader)
.map_err(|e| Error::Msg(e.to_string()))
.map(|s| {
let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), From::from(p.1))).collect();
let params = CommonParams::from(s.params);
Spec::machine(&s.engine, params, builtins)
})
}
/// Loads spec from json file. Provide factories for executing contracts and ensuring
/// storage goes to the right place.
pub fn load<'a, T: Into<SpecParams<'a>>, R: Read>(params: T, reader: R) -> Result<Self, Error> {
ethjson::spec::Spec::load(reader)
.map_err(|e| Error::Msg(e.to_string()))
.and_then(|x| load_from(params.into(), x))
}
/// initialize genesis epoch data, using in-memory database for
/// constructor.
pub fn genesis_epoch_data(&self) -> Result<Vec<u8>, String> {
use types::transaction::{Action, Transaction};
let genesis = self.genesis_header();
let factories = Default::default();
let mut db = journaldb::new(
Arc::new(kvdb_memorydb::create(0)),
journaldb::Algorithm::Archive,
None,
);
self.ensure_db_good(BasicBackend(db.as_hash_db_mut()), &factories)
.map_err(|e| format!("Unable to initialize genesis state: {}", e))?;
let call = |a, d| {
let mut db = db.boxed_clone();
let env_info = ::evm::EnvInfo {
number: 0,
author: *genesis.author(),
timestamp: genesis.timestamp(),
difficulty: *genesis.difficulty(),
gas_limit: U256::max_value(),
last_hashes: Arc::new(Vec::new()),
gas_used: 0.into(),
};
let from = Address::zero();
let tx = Transaction {
nonce: self.engine.account_start_nonce(0),
action: Action::Call(a),
gas: U256::max_value(),
gas_price: U256::default(),
value: U256::default(),
data: d,
}.fake_sign(from);
let res = ::executive_state::prove_transaction_virtual(
db.as_hash_db_mut(),
*genesis.state_root(),
&tx,
self.engine.machine(),
&env_info,
factories.clone(),
);
res.map(|(out, proof)| {
(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)
}
}
#[cfg(test)]
mod tests {
use super::*;
use account_state::State;
use test_helpers::get_temp_state_db;
use tempdir::TempDir;
use types::view;
use types::views::BlockView;
use std::str::FromStr;
use crate::spec;
#[test]
fn test_load_empty() {
let tempdir = TempDir::new("").unwrap();
assert!(Spec::load(&tempdir.path(), &[] as &[u8]).is_err());
}
#[test]
fn test_chain() {
let test_spec = spec::new_test();
assert_eq!(
test_spec.state_root,
H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap()
);
let genesis = test_spec.genesis_block();
assert_eq!(
view!(BlockView, &genesis).header_view().hash(),
H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap()
);
}
#[test]
fn genesis_constructor() {
let _ = ::env_logger::try_init();
let spec = spec::new_test_constructor();
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default())
.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("0000000000000000000000000000000000001337").unwrap();
assert_eq!(state.storage_at(&address, &H256::zero()).unwrap(), expected);
assert_eq!(state.balance(&address).unwrap(), 1.into());
}
}

View File

@@ -33,7 +33,7 @@ use types::{
use client::{Client, ClientConfig, PrepareOpenBlock, ImportSealedBlock};
use client_traits::{BlockInfo, BlockChainClient, BlockChainReset, ChainInfo, ImportBlock};
use crate::spec;
use spec;
use machine::executive::{Executive, TransactOptions};
use miner::{Miner, PendingOrdering, MinerService};
use account_state::{State, CleanupMode, backend};

View File

@@ -21,7 +21,7 @@ use hash::keccak;
use block::*;
use ethereum_types::{U256, Address};
use io::*;
use crate::spec;
use spec;
use test_helpers::get_temp_state_db;
use client::{Client, ClientConfig};
use client_traits::{BlockChainClient, ImportBlock};

View File

@@ -16,20 +16,26 @@
//! Block verification utilities.
use call_contract::CallContract;
use client_traits::BlockInfo;
mod verification;
mod verifier;
pub mod queue;
mod canon_verifier;
mod noop_verifier;
pub use self::verification::*;
pub use self::verification::FullFamilyParams;
pub use self::verifier::Verifier;
pub use self::canon_verifier::CanonVerifier;
pub use self::noop_verifier::NoopVerifier;
pub use self::queue::{BlockQueue, Config as QueueConfig, VerificationQueue, QueueInfo};
pub use self::queue::{BlockQueue, Config as QueueConfig};
use call_contract::CallContract;
use client_traits::BlockInfo;
use self::verification::{
verify_block_basic,
verify_block_unordered,
verify_header_params,
};
use self::canon_verifier::CanonVerifier;
use self::noop_verifier::NoopVerifier;
/// Verifier type.
#[derive(Debug, PartialEq, Clone)]

View File

@@ -29,12 +29,13 @@ use io::*;
use engine::Engine;
use client::ClientIoMessage;
use len_caching_lock::LenCachingMutex;
use types::errors::{BlockError, EthcoreError as Error, ImportError};
use types::{
errors::{BlockError, EthcoreError as Error, ImportError},
verification::VerificationQueueInfo as QueueInfo,
};
use self::kind::{BlockLike, Kind};
pub use types::verification::VerificationQueueInfo as QueueInfo;
pub mod kind;
const MIN_MEM_LIMIT: usize = 16384;
@@ -743,7 +744,7 @@ mod tests {
view,
views::BlockView,
};
use crate::spec;
use spec;
// create a test block queue.
// auto_scaling enables verifier adjustment.

View File

@@ -372,7 +372,7 @@ mod tests {
use hash::keccak;
use engine::Engine;
use ethkey::{Random, Generator};
use crate::spec;
use spec;
use test_helpers::{create_test_block_with_data, create_test_block};
use types::{
encoded,