Generalize engine trait (#6591)

* move common forks and parameters to common params

* port specs over to new format

* fix RPC tests

* parity-machine skeleton

* remove block type

* extract out ethereum-specific methods into EthereumMachine

* beginning to integrate Machine into engines. dealing with stale transitions in Ethash

* initial porting to machine

* move block reward back into engine

* abstract block reward logic

* move last hash and DAO HF logic into machine

* begin making engine function parameters generic

* abstract epoch verifier and ethash block reward logic

* instantiate special ethereummachine for ethash in spec

* optional full verification in verify_block_family

* re-instate tx_filter in a way that works for all engines

* fix warnings

* fix most tests, further generalize engine trait

* uncomment nullengine, get ethcore tests compiling

* fix warnings

* update a bunch of specs

* re-enable engine signer, validator set, and transition handler

* migrate basic_authority engine

* move last hashes into executedblock

* port tendermint

* make all ethcore tests pass

* json-tests compilation

* fix RPC tests: change in gas limit for new block changed PoW hash

* fix minor grumbles

* validate chainspecs

* fix broken import

* fix transaction verification for pre-homestead
This commit is contained in:
Robert Habermeier
2017-09-26 14:19:08 +02:00
committed by Gav Wood
parent d8af9f4e7b
commit bc167a211b
85 changed files with 2233 additions and 1923 deletions

View File

@@ -31,7 +31,7 @@ use unexpected::{Mismatch, OutOfBounds};
use basic_types::{LogBloom, Seal};
use vm::{EnvInfo, LastHashes};
use engines::Engine;
use engines::EthEngine;
use error::{Error, BlockError, TransactionError};
use factory::Factories;
use header::Header;
@@ -97,6 +97,7 @@ pub struct ExecutedBlock {
transactions_set: HashSet<H256>,
state: State<StateDB>,
traces: Option<Vec<Vec<FlatTrace>>>,
last_hashes: Arc<LastHashes>,
}
/// A set of references to `ExecutedBlock` fields that are publicly accessible.
@@ -115,6 +116,17 @@ pub struct BlockRefMut<'a> {
pub traces: &'a mut Option<Vec<Vec<FlatTrace>>>,
}
impl<'a> BlockRefMut<'a> {
/// Add traces if tracing is enabled.
pub fn push_traces(&mut self, tracer: ::trace::ExecutiveTracer) {
use trace::Tracer;
if let Some(ref mut traces) = self.traces.as_mut() {
traces.push(tracer.drain())
}
}
}
/// A set of immutable references to `ExecutedBlock` fields that are publicly accessible.
pub struct BlockRef<'a> {
/// Block header.
@@ -133,7 +145,7 @@ pub struct BlockRef<'a> {
impl ExecutedBlock {
/// Create a new block from the given `state`.
fn new(state: State<StateDB>, tracing: bool) -> ExecutedBlock {
fn new(state: State<StateDB>, last_hashes: Arc<LastHashes>, tracing: bool) -> ExecutedBlock {
ExecutedBlock {
header: Default::default(),
transactions: Default::default(),
@@ -142,6 +154,7 @@ impl ExecutedBlock {
transactions_set: Default::default(),
state: state,
traces: if tracing {Some(Vec::new())} else {None},
last_hashes: last_hashes,
}
}
@@ -168,6 +181,20 @@ impl ExecutedBlock {
traces: &self.traces,
}
}
/// Get the environment info concerning this block.
pub fn env_info(&self) -> EnvInfo {
// TODO: memoise.
EnvInfo {
number: self.header.number(),
author: self.header.author().clone(),
timestamp: self.header.timestamp(),
difficulty: self.header.difficulty().clone(),
last_hashes: self.last_hashes.clone(),
gas_used: self.receipts.last().map_or(U256::zero(), |r| r.gas_used),
gas_limit: self.header.gas_limit().clone(),
}
}
}
/// Trait for a object that is a `ExecutedBlock`.
@@ -216,14 +243,33 @@ impl IsBlock for ExecutedBlock {
fn block(&self) -> &ExecutedBlock { self }
}
impl ::parity_machine::LiveBlock for ExecutedBlock {
type Header = Header;
fn header(&self) -> &Header {
&self.header
}
fn uncles(&self) -> &[Header] {
&self.uncles
}
}
impl ::parity_machine::Transactions for ExecutedBlock {
type Transaction = SignedTransaction;
fn transactions(&self) -> &[SignedTransaction] {
&self.transactions
}
}
/// Block that is ready for transactions to be added.
///
/// It's a bit like a Vec<Transaction>, except that whenever a transaction is pushed, we execute it and
/// maintain the system `state()`. We also archive execution receipts in preparation for later block creation.
pub struct OpenBlock<'x> {
block: ExecutedBlock,
engine: &'x Engine,
last_hashes: Arc<LastHashes>,
engine: &'x EthEngine,
}
/// Just like `OpenBlock`, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields,
@@ -234,7 +280,6 @@ pub struct OpenBlock<'x> {
pub struct ClosedBlock {
block: ExecutedBlock,
uncle_bytes: Bytes,
last_hashes: Arc<LastHashes>,
unclosed_state: State<StateDB>,
}
@@ -259,7 +304,7 @@ impl<'x> OpenBlock<'x> {
#[cfg_attr(feature="dev", allow(too_many_arguments))]
/// Create a new `OpenBlock` ready for transaction pushing.
pub fn new(
engine: &'x Engine,
engine: &'x EthEngine,
factories: Factories,
tracing: bool,
db: StateDB,
@@ -273,9 +318,8 @@ impl<'x> OpenBlock<'x> {
let number = parent.number() + 1;
let state = State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(number), factories)?;
let mut r = OpenBlock {
block: ExecutedBlock::new(state, tracing),
block: ExecutedBlock::new(state, last_hashes, tracing),
engine: engine,
last_hashes: last_hashes.clone(),
};
r.block.header.set_parent_hash(parent.hash());
@@ -287,8 +331,12 @@ impl<'x> OpenBlock<'x> {
let gas_floor_target = cmp::max(gas_range_target.0, engine.params().min_gas_limit);
let gas_ceil_target = cmp::max(gas_range_target.1, gas_floor_target);
engine.populate_from_parent(&mut r.block.header, parent, gas_floor_target, gas_ceil_target);
engine.on_new_block(&mut r.block, last_hashes, is_epoch_begin)?;
engine.machine().populate_from_parent(&mut r.block.header, parent, gas_floor_target, gas_ceil_target);
engine.populate_from_parent(&mut r.block.header, parent);
engine.machine().on_new_block(&mut r.block)?;
engine.on_new_block(&mut r.block, is_epoch_begin)?;
Ok(r)
}
@@ -343,16 +391,7 @@ impl<'x> OpenBlock<'x> {
/// Get the environment info concerning this block.
pub fn env_info(&self) -> EnvInfo {
// TODO: memoise.
EnvInfo {
number: self.block.header.number(),
author: self.block.header.author().clone(),
timestamp: self.block.header.timestamp(),
difficulty: self.block.header.difficulty().clone(),
last_hashes: self.last_hashes.clone(),
gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used),
gas_limit: self.block.header.gas_limit().clone(),
}
self.block.env_info()
}
/// Push a transaction into the block.
@@ -365,7 +404,7 @@ impl<'x> OpenBlock<'x> {
let env_info = self.env_info();
// info!("env_info says gas_used={}", env_info.gas_used);
match self.block.state.apply(&env_info, self.engine, &t, self.block.traces.is_some()) {
match self.block.state.apply(&env_info, self.engine.machine(), &t, self.block.traces.is_some()) {
Ok(outcome) => {
self.block.transactions_set.insert(h.unwrap_or_else(||t.hash()));
self.block.transactions.push(t.into());
@@ -418,7 +457,6 @@ impl<'x> OpenBlock<'x> {
ClosedBlock {
block: s.block,
uncle_bytes: uncle_bytes,
last_hashes: s.last_hashes,
unclosed_state: unclosed_state,
}
}
@@ -485,14 +523,13 @@ impl ClosedBlock {
}
/// Given an engine reference, reopen the `ClosedBlock` into an `OpenBlock`.
pub fn reopen(self, engine: &Engine) -> OpenBlock {
pub fn reopen(self, engine: &EthEngine) -> OpenBlock {
// revert rewards (i.e. set state back at last transaction's state).
let mut block = self.block;
block.state = self.unclosed_state;
OpenBlock {
block: block,
engine: engine,
last_hashes: self.last_hashes,
}
}
}
@@ -504,7 +541,7 @@ impl LockedBlock {
/// Provide a valid seal in order to turn this into a `SealedBlock`.
///
/// NOTE: This does not check the validity of `seal` with the engine.
pub fn seal(self, engine: &Engine, seal: Vec<Bytes>) -> Result<SealedBlock, BlockError> {
pub fn seal(self, engine: &EthEngine, seal: Vec<Bytes>) -> Result<SealedBlock, BlockError> {
let mut s = self;
if seal.len() != engine.seal_fields() {
return Err(BlockError::InvalidSealArity(Mismatch{expected: engine.seal_fields(), found: seal.len()}));
@@ -518,12 +555,14 @@ impl LockedBlock {
/// Returns the `ClosedBlock` back again if the seal is no good.
pub fn try_seal(
self,
engine: &Engine,
engine: &EthEngine,
seal: Vec<Bytes>,
) -> Result<SealedBlock, (Error, LockedBlock)> {
let mut s = self;
s.block.header.set_seal(seal);
match engine.verify_block_seal(&s.block.header) {
// TODO: passing state context to avoid engines owning it?
match engine.verify_local_seal(&s.block.header) {
Err(e) => Err((e, s)),
_ => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }),
}
@@ -575,7 +614,7 @@ pub fn enact(
header: &Header,
transactions: &[SignedTransaction],
uncles: &[Header],
engine: &Engine,
engine: &EthEngine,
tracing: bool,
db: StateDB,
parent: &Header,
@@ -647,7 +686,7 @@ fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction])
#[cfg_attr(feature="dev", allow(too_many_arguments))]
pub fn enact_verified(
block: &PreverifiedBlock,
engine: &Engine,
engine: &EthEngine,
tracing: bool,
db: StateDB,
parent: &Header,
@@ -675,7 +714,7 @@ pub fn enact_verified(
mod tests {
use tests::helpers::*;
use super::*;
use engines::Engine;
use engines::EthEngine;
use vm::LastHashes;
use error::Error;
use header::Header;
@@ -690,7 +729,7 @@ mod tests {
#[cfg_attr(feature="dev", allow(too_many_arguments))]
fn enact_bytes(
block_bytes: &[u8],
engine: &Engine,
engine: &EthEngine,
tracing: bool,
db: StateDB,
parent: &Header,
@@ -737,7 +776,7 @@ mod tests {
#[cfg_attr(feature="dev", allow(too_many_arguments))]
fn enact_and_seal(
block_bytes: &[u8],
engine: &Engine,
engine: &EthEngine,
tracing: bool,
db: StateDB,
parent: &Header,