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

@@ -19,8 +19,9 @@
use std::sync::Arc;
use blockchain::BlockChain;
use engines::{Engine, EpochVerifier};
use engines::{EthEngine, EpochVerifier};
use header::Header;
use machine::EthereumMachine;
use rand::Rng;
use parking_lot::RwLock;
@@ -31,13 +32,13 @@ const HEAVY_VERIFY_RATE: f32 = 0.02;
/// Ancient block verifier: import an ancient sequence of blocks in order from a starting
/// epoch.
pub struct AncientVerifier {
cur_verifier: RwLock<Box<EpochVerifier>>,
engine: Arc<Engine>,
cur_verifier: RwLock<Box<EpochVerifier<EthereumMachine>>>,
engine: Arc<EthEngine>,
}
impl AncientVerifier {
/// Create a new ancient block verifier with the given engine and initial verifier.
pub fn new(engine: Arc<Engine>, start_verifier: Box<EpochVerifier>) -> Self {
pub fn new(engine: Arc<EthEngine>, start_verifier: Box<EpochVerifier<EthereumMachine>>) -> Self {
AncientVerifier {
cur_verifier: RwLock::new(start_verifier),
engine: engine,

View File

@@ -46,7 +46,7 @@ use client::{
ChainNotify, PruningInfo, ProvingBlockChainClient,
};
use encoded;
use engines::{Engine, EpochTransition};
use engines::{EthEngine, EpochTransition};
use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError};
use vm::{EnvInfo, LastHashes};
use evm::{Factory as EvmFactory, Schedule};
@@ -147,7 +147,7 @@ pub struct Client {
mode: Mutex<Mode>,
chain: RwLock<Arc<BlockChain>>,
tracedb: RwLock<TraceDB<BlockChain>>,
engine: Arc<Engine>,
engine: Arc<EthEngine>,
config: ClientConfig,
pruning: journaldb::Algorithm,
db: RwLock<Arc<KeyValueDB>>,
@@ -332,7 +332,7 @@ impl Client {
}
/// Returns engine reference.
pub fn engine(&self) -> &Engine {
pub fn engine(&self) -> &EthEngine {
&*self.engine
}
@@ -421,55 +421,63 @@ impl Client {
return Err(());
}
// Check if parent is in chain
let parent = match chain.block_header(header.parent_hash()) {
Some(h) => h,
None => {
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash());
return Err(());
}
};
// Verify Block Family
let verify_family_result = self.verifier.verify_block_family(header, &block.bytes, engine, &**chain);
let verify_family_result = self.verifier.verify_block_family(
header,
&parent,
engine,
Some((&block.bytes, &block.transactions, &**chain, self)),
);
if let Err(e) = verify_family_result {
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
};
let verify_external_result = self.verifier.verify_block_external(header, &block.bytes, engine);
let verify_external_result = self.verifier.verify_block_external(header, engine);
if let Err(e) = verify_external_result {
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
};
// Check if Parent is in chain
let chain_has_parent = chain.block_header(header.parent_hash());
if let Some(parent) = chain_has_parent {
// Enact Verified Block
let last_hashes = self.build_last_hashes(header.parent_hash().clone());
let db = self.state_db.lock().boxed_clone_canon(header.parent_hash());
// Enact Verified Block
let last_hashes = self.build_last_hashes(header.parent_hash().clone());
let db = self.state_db.lock().boxed_clone_canon(header.parent_hash());
let is_epoch_begin = chain.epoch_transition(parent.number(), *header.parent_hash()).is_some();
let enact_result = enact_verified(block,
engine,
self.tracedb.read().tracing_enabled(),
db,
&parent,
last_hashes,
self.factories.clone(),
is_epoch_begin,
);
let mut locked_block = enact_result.map_err(|e| {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
})?;
let is_epoch_begin = chain.epoch_transition(parent.number(), *header.parent_hash()).is_some();
let enact_result = enact_verified(block,
engine,
self.tracedb.read().tracing_enabled(),
db,
&parent,
last_hashes,
self.factories.clone(),
is_epoch_begin,
);
let mut locked_block = enact_result.map_err(|e| {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
})?;
if header.number() < self.engine().params().validate_receipts_transition && header.receipts_root() != locked_block.block().header().receipts_root() {
locked_block = locked_block.strip_receipts();
}
// Final Verification
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
warn!(target: "client", "Stage 5 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
}
Ok(locked_block)
} else {
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash());
Err(())
if header.number() < self.engine().params().validate_receipts_transition && header.receipts_root() != locked_block.block().header().receipts_root() {
locked_block = locked_block.strip_receipts();
}
// Final Verification
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
warn!(target: "client", "Stage 5 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
}
Ok(locked_block)
}
fn calculate_enacted_retracted(&self, import_results: &[ImportRoute]) -> (Vec<H256>, Vec<H256>) {
@@ -721,7 +729,12 @@ impl Client {
use engines::EpochChange;
let hash = header.hash();
match self.engine.signals_epoch_end(header, Some(block_bytes), Some(&receipts)) {
let auxiliary = ::machine::AuxiliaryData {
bytes: Some(block_bytes),
receipts: Some(&receipts),
};
match self.engine.signals_epoch_end(header, auxiliary) {
EpochChange::Yes(proof) => {
use engines::epoch::PendingTransition;
use engines::Proof;
@@ -754,7 +767,7 @@ impl Client {
).expect("state known to be available for just-imported block; qed");
let options = TransactOptions::with_no_tracing().dont_check_nonce();
let res = Executive::new(&mut state, &env_info, &*self.engine)
let res = Executive::new(&mut state, &env_info, self.engine.machine())
.transact(&transaction, options);
let res = match res {
@@ -821,7 +834,7 @@ impl Client {
// use a state-proving closure for the given block.
fn with_proving_caller<F, T>(&self, id: BlockId, with_call: F) -> T
where F: FnOnce(&::engines::Call) -> T
where F: FnOnce(&::machine::Call) -> T
{
let call = |a, d| {
let tx = self.contract_call_tx(id, a, d);
@@ -1119,15 +1132,14 @@ impl Client {
}
fn do_virtual_call(&self, env_info: &EnvInfo, state: &mut State<StateDB>, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
fn call<E, V, T>(
fn call<V, T>(
state: &mut State<StateDB>,
env_info: &EnvInfo,
engine: &E,
machine: &::machine::EthereumMachine,
state_diff: bool,
transaction: &SignedTransaction,
options: TransactOptions<T, V>,
) -> Result<Executed, CallError> where
E: Engine + ?Sized,
T: trace::Tracer,
V: trace::VMTracer,
{
@@ -1136,7 +1148,7 @@ impl Client {
.save_output_from_contract();
let original_state = if state_diff { Some(state.clone()) } else { None };
let mut ret = Executive::new(state, env_info, engine).transact_virtual(transaction, options)?;
let mut ret = Executive::new(state, env_info, machine).transact_virtual(transaction, options)?;
if let Some(original) = original_state {
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?);
@@ -1145,13 +1157,13 @@ impl Client {
}
let state_diff = analytics.state_diffing;
let engine = &*self.engine;
let machine = self.engine.machine();
match (analytics.transaction_tracing, analytics.vm_tracing) {
(true, true) => call(state, env_info, engine, state_diff, t, TransactOptions::with_tracing_and_vm_tracing()),
(true, false) => call(state, env_info, engine, state_diff, t, TransactOptions::with_tracing()),
(false, true) => call(state, env_info, engine, state_diff, t, TransactOptions::with_vm_tracing()),
(false, false) => call(state, env_info, engine, state_diff, t, TransactOptions::with_no_tracing()),
(true, true) => call(state, env_info, machine, state_diff, t, TransactOptions::with_tracing_and_vm_tracing()),
(true, false) => call(state, env_info, machine, state_diff, t, TransactOptions::with_tracing()),
(false, true) => call(state, env_info, machine, state_diff, t, TransactOptions::with_vm_tracing()),
(false, false) => call(state, env_info, machine, state_diff, t, TransactOptions::with_no_tracing()),
}
}
@@ -1235,7 +1247,7 @@ impl BlockChainClient for Client {
let tx = tx.fake_sign(sender);
let mut state = original_state.clone();
Ok(Executive::new(&mut state, &env_info, &*self.engine)
Ok(Executive::new(&mut state, &env_info, self.engine.machine())
.transact_virtual(&tx, options())
.map(|r| r.exception.is_none())
.unwrap_or(false))
@@ -1296,7 +1308,7 @@ impl BlockChainClient for Client {
let rest = txs.split_off(address.index);
for t in txs {
let t = SignedTransaction::new(t).expect(PROOF);
let x = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, TransactOptions::with_no_tracing())?;
let x = Executive::new(&mut state, &env_info, self.engine.machine()).transact(&t, TransactOptions::with_no_tracing())?;
env_info.gas_used = env_info.gas_used + x.gas_used;
}
let first = rest.into_iter().next().expect("We split off < `address.index`; Length is checked earlier; qed");
@@ -1575,7 +1587,7 @@ impl BlockChainClient for Client {
.collect();
match (transaction, previous_receipts) {
(Some(transaction), Some(previous_receipts)) => {
Some(transaction_receipt(self.engine(), transaction, previous_receipts))
Some(transaction_receipt(self.engine().machine(), transaction, previous_receipts))
},
_ => None,
}
@@ -1995,7 +2007,7 @@ impl ProvingBlockChainClient for Client {
jdb.as_hashdb_mut(),
header.state_root().clone(),
&transaction,
&*self.engine,
self.engine.machine(),
&env_info,
self.factories.clone(),
false,
@@ -2018,7 +2030,7 @@ impl Drop for Client {
/// Returns `LocalizedReceipt` given `LocalizedTransaction`
/// and a vector of receipts from given block up to transaction index.
fn transaction_receipt(engine: &Engine, mut tx: LocalizedTransaction, mut receipts: Vec<Receipt>) -> LocalizedReceipt {
fn transaction_receipt(machine: &::machine::EthereumMachine, mut tx: LocalizedTransaction, mut receipts: Vec<Receipt>) -> LocalizedReceipt {
assert_eq!(receipts.len(), tx.transaction_index + 1, "All previous receipts are provided.");
let sender = tx.sender();
@@ -2042,7 +2054,7 @@ fn transaction_receipt(engine: &Engine, mut tx: LocalizedTransaction, mut receip
gas_used: receipt.gas_used - prior_gas_used,
contract_address: match tx.action {
Action::Call(_) => None,
Action::Create => Some(contract_address(engine.create_address_scheme(block_number), &sender, &tx.nonce, &tx.data).0)
Action::Create => Some(contract_address(machine.create_address_scheme(block_number), &sender, &tx.nonce, &tx.data).0)
},
logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry {
entry: log,
@@ -2102,12 +2114,11 @@ mod tests {
use log_entry::{LogEntry, LocalizedLogEntry};
use receipt::{Receipt, LocalizedReceipt, TransactionOutcome};
use transaction::{Transaction, LocalizedTransaction, Action};
use tests::helpers::TestEngine;
// given
let key = KeyPair::from_secret_slice(&keccak("test")).unwrap();
let secret = key.secret();
let engine = TestEngine::new(0);
let machine = ::ethereum::new_frontier_test_machine();
let block_number = 1;
let block_hash = 5.into();
@@ -2151,7 +2162,7 @@ mod tests {
}];
// when
let receipt = transaction_receipt(&engine, transaction, receipts);
let receipt = transaction_receipt(&machine, transaction, receipts);
// then
assert_eq!(receipt, LocalizedReceipt {

View File

@@ -181,7 +181,7 @@ impl<'a> EvmTestClient<'a> {
let mut substate = state::Substate::new();
let mut tracer = trace::NoopTracer;
let mut output = vec![];
let mut executive = executive::Executive::new(&mut self.state, &info, &*self.spec.engine);
let mut executive = executive::Executive::new(&mut self.state, &info, self.spec.engine.machine());
executive.call(
params,
&mut substate,
@@ -211,7 +211,7 @@ impl<'a> EvmTestClient<'a> {
// Apply transaction
let tracer = trace::NoopTracer;
let result = self.state.apply_with_tracing(&env_info, &*self.spec.engine, &transaction, tracer, vm_tracer);
let result = self.state.apply_with_tracing(&env_info, self.spec.engine.machine(), &transaction, tracer, vm_tracer);
match result {
Ok(result) => {