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

@@ -27,8 +27,8 @@ use futures::Future;
use native_contracts::ValidatorReport as Provider;
use client::EngineClient;
use engines::{Call, Engine};
use header::{Header, BlockNumber};
use machine::{AuxiliaryData, Call, EthereumMachine};
use super::{ValidatorSet, SimpleList, SystemCall};
use super::safe_contract::ValidatorSafeContract;
@@ -91,14 +91,13 @@ impl ValidatorSet for ValidatorContract {
&self,
first: bool,
header: &Header,
block: Option<&[u8]>,
receipts: Option<&[::receipt::Receipt]>,
) -> ::engines::EpochChange {
self.validators.signals_epoch_end(first, header, block, receipts)
aux: AuxiliaryData,
) -> ::engines::EpochChange<EthereumMachine> {
self.validators.signals_epoch_end(first, header, aux)
}
fn epoch_set(&self, first: bool, engine: &Engine, number: BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option<H256>), ::error::Error> {
self.validators.epoch_set(first, engine, number, proof)
fn epoch_set(&self, first: bool, machine: &EthereumMachine, number: BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option<H256>), ::error::Error> {
self.validators.epoch_set(first, machine, number, proof)
}
fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool {
@@ -182,7 +181,7 @@ mod tests {
header.set_parent_hash(client.chain_info().best_block_hash);
// `reportBenign` when the designated proposer releases block from the future (bad clock).
assert!(client.engine().verify_block_external(&header, None).is_err());
assert!(client.engine().verify_block_external(&header).is_err());
// Seal a block.
client.engine().step();
assert_eq!(client.chain_info().best_block_number, 1);
@@ -190,7 +189,7 @@ mod tests {
assert_eq!(client.call_contract(BlockId::Latest, validator_contract, "d8f2e0bf".from_hex().unwrap()).unwrap().to_hex(), "0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e");
// Simulate a misbehaving validator by handling a double proposal.
let header = client.best_block_header().decode();
assert!(client.engine().verify_block_family(&header, &header, None).is_err());
assert!(client.engine().verify_block_family(&header, &header).is_err());
// Seal a block.
client.engine().step();
client.engine().step();

View File

@@ -31,6 +31,7 @@ use bytes::Bytes;
use ethjson::spec::ValidatorSet as ValidatorSpec;
use client::EngineClient;
use header::{Header, BlockNumber};
use machine::{AuxiliaryData, Call, EthereumMachine};
#[cfg(test)]
pub use self::test::TestSet;
@@ -39,8 +40,6 @@ use self::contract::ValidatorContract;
use self::safe_contract::ValidatorSafeContract;
use self::multi::Multi;
use super::{Call, Engine};
/// A system-calling closure. Enacts calls on a block's state from the system address.
pub type SystemCall<'a> = FnMut(Address, Bytes) -> Result<Bytes, String> + 'a;
@@ -113,9 +112,8 @@ pub trait ValidatorSet: Send + Sync {
&self,
first: bool,
header: &Header,
block: Option<&[u8]>,
receipts: Option<&[::receipt::Receipt]>,
) -> ::engines::EpochChange;
aux: AuxiliaryData,
) -> ::engines::EpochChange<EthereumMachine>;
/// Recover the validator set from the given proof, the block number, and
/// whether this header is first in its set.
@@ -125,7 +123,7 @@ pub trait ValidatorSet: Send + Sync {
///
/// Returns the set, along with a flag indicating whether finality of a specific
/// hash should be proven.
fn epoch_set(&self, first: bool, engine: &Engine, number: BlockNumber, proof: &[u8])
fn epoch_set(&self, first: bool, machine: &EthereumMachine, number: BlockNumber, proof: &[u8])
-> Result<(SimpleList, Option<H256>), ::error::Error>;
/// Checks if a given address is a validator, with the given function

View File

@@ -18,7 +18,6 @@
use std::collections::BTreeMap;
use std::sync::Weak;
use engines::{Call, Engine};
use bigint::hash::H256;
use parking_lot::RwLock;
use util::Address;
@@ -26,6 +25,7 @@ use bytes::Bytes;
use ids::BlockId;
use header::{BlockNumber, Header};
use client::EngineClient;
use machine::{AuxiliaryData, Call, EthereumMachine};
use super::{SystemCall, ValidatorSet};
type BlockNumberLookup = Box<Fn(BlockId) -> Result<BlockNumber, String> + Send + Sync + 'static>;
@@ -93,20 +93,20 @@ impl ValidatorSet for Multi {
set.is_epoch_end(first, chain_head)
}
fn signals_epoch_end(&self, _first: bool, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
-> ::engines::EpochChange
fn signals_epoch_end(&self, _first: bool, header: &Header, aux: AuxiliaryData)
-> ::engines::EpochChange<EthereumMachine>
{
let (set_block, set) = self.correct_set_by_number(header.number());
let first = set_block == header.number();
set.signals_epoch_end(first, header, block, receipts)
set.signals_epoch_end(first, header, aux)
}
fn epoch_set(&self, _first: bool, engine: &Engine, number: BlockNumber, proof: &[u8]) -> Result<(super::SimpleList, Option<H256>), ::error::Error> {
fn epoch_set(&self, _first: bool, machine: &EthereumMachine, number: BlockNumber, proof: &[u8]) -> Result<(super::SimpleList, Option<H256>), ::error::Error> {
let (set_block, set) = self.correct_set_by_number(number);
let first = set_block == number;
set.epoch_set(first, engine, number, proof)
set.epoch_set(first, machine, number, proof)
}
fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool {
@@ -227,7 +227,7 @@ mod tests {
let mut header = Header::new();
header.set_number(499);
match multi.signals_epoch_end(false, &header, None, None) {
match multi.signals_epoch_end(false, &header, Default::default()) {
EpochChange::No => {},
_ => panic!("Expected no epoch signal change."),
}
@@ -235,7 +235,7 @@ mod tests {
header.set_number(500);
match multi.signals_epoch_end(false, &header, None, None) {
match multi.signals_epoch_end(false, &header, Default::default()) {
EpochChange::No => {},
_ => panic!("Expected no epoch signal change."),
}

View File

@@ -33,7 +33,7 @@ use rlp::{UntrustedRlp, RlpStream};
use basic_types::LogBloom;
use client::EngineClient;
use engines::{Call, Engine};
use machine::{AuxiliaryData, Call, EthereumMachine, AuxiliaryRequest};
use header::Header;
use ids::BlockId;
use log_entry::LogEntry;
@@ -58,19 +58,19 @@ struct StateProof {
provider: Provider,
}
impl ::engines::StateDependentProof for StateProof {
impl ::engines::StateDependentProof<EthereumMachine> for StateProof {
fn generate_proof(&self, caller: &Call) -> Result<Vec<u8>, String> {
prove_initial(&self.provider, &*self.header.lock(), caller)
}
fn check_proof(&self, engine: &Engine, proof: &[u8]) -> Result<(), String> {
fn check_proof(&self, machine: &EthereumMachine, proof: &[u8]) -> Result<(), String> {
let (header, state_items) = decode_first_proof(&UntrustedRlp::new(proof))
.map_err(|e| format!("proof incorrectly encoded: {}", e))?;
if &header != &*self.header.lock(){
return Err("wrong header in proof".into());
}
check_first_proof(engine, &self.provider, header, &state_items).map(|_| ())
check_first_proof(machine, &self.provider, header, &state_items).map(|_| ())
}
}
@@ -94,7 +94,7 @@ fn encode_first_proof(header: &Header, state_items: &[Vec<u8>]) -> Bytes {
}
// check a first proof: fetch the validator set at the given block.
fn check_first_proof(engine: &Engine, provider: &Provider, old_header: Header, state_items: &[DBValue])
fn check_first_proof(machine: &EthereumMachine, provider: &Provider, old_header: Header, state_items: &[DBValue])
-> Result<Vec<Address>, String>
{
use transaction::{Action, Transaction};
@@ -117,12 +117,12 @@ fn check_first_proof(engine: &Engine, provider: &Provider, old_header: Header, s
gas_used: 0.into(),
};
// check state proof using given engine.
// check state proof using given machine.
let number = old_header.number();
provider.get_validators(move |a, d| {
let from = Address::default();
let tx = Transaction {
nonce: engine.account_start_nonce(number),
nonce: machine.account_start_nonce(number),
action: Action::Call(a),
gas: PROVIDED_GAS.into(),
gas_price: U256::default(),
@@ -134,7 +134,7 @@ fn check_first_proof(engine: &Engine, provider: &Provider, old_header: Header, s
state_items,
*old_header.state_root(),
&tx,
engine,
machine,
&env_info,
);
@@ -336,9 +336,11 @@ impl ValidatorSet for ValidatorSafeContract {
None // no immediate transitions to contract.
}
fn signals_epoch_end(&self, first: bool, header: &Header, _block: Option<&[u8]>, receipts: Option<&[Receipt]>)
-> ::engines::EpochChange
fn signals_epoch_end(&self, first: bool, header: &Header, aux: AuxiliaryData)
-> ::engines::EpochChange<EthereumMachine>
{
let receipts = aux.receipts;
// transition to the first block of a contract requires finality but has no log event.
if first {
debug!(target: "engine", "signalling transition to fresh contract.");
@@ -358,7 +360,7 @@ impl ValidatorSet for ValidatorSafeContract {
trace!(target: "engine", "detected epoch change event bloom");
match receipts {
None => ::engines::EpochChange::Unsure(::engines::Unsure::NeedsReceipts),
None => ::engines::EpochChange::Unsure(AuxiliaryRequest::Receipts),
Some(receipts) => match self.extract_from_event(bloom, header, receipts) {
None => ::engines::EpochChange::No,
Some(list) => {
@@ -372,7 +374,7 @@ impl ValidatorSet for ValidatorSafeContract {
}
}
fn epoch_set(&self, first: bool, engine: &Engine, _number: ::header::BlockNumber, proof: &[u8])
fn epoch_set(&self, first: bool, machine: &EthereumMachine, _number: ::header::BlockNumber, proof: &[u8])
-> Result<(SimpleList, Option<H256>), ::error::Error>
{
let rlp = UntrustedRlp::new(proof);
@@ -383,7 +385,7 @@ impl ValidatorSet for ValidatorSafeContract {
let (old_header, state_items) = decode_first_proof(&rlp)?;
let number = old_header.number();
let old_hash = old_header.hash();
let addresses = check_first_proof(engine, &self.provider, old_header, &state_items)
let addresses = check_first_proof(machine, &self.provider, old_header, &state_items)
.map_err(::engines::EngineError::InsufficientProof)?;
trace!(target: "engine", "extracted epoch set at #{}: {} addresses",
@@ -561,7 +563,8 @@ mod tests {
#[test]
fn detects_bloom() {
use header::Header;
use engines::{EpochChange, Unsure};
use engines::EpochChange;
use machine::AuxiliaryRequest;
use log_entry::LogEntry;
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, None);
@@ -581,7 +584,7 @@ mod tests {
};
new_header.set_log_bloom(event.bloom());
match engine.signals_epoch_end(&new_header, None, None) {
match engine.signals_epoch_end(&new_header, Default::default()) {
EpochChange::No => {},
_ => panic!("Expected bloom to be unrecognized."),
};
@@ -590,8 +593,8 @@ mod tests {
event.topics.push(last_hash);
new_header.set_log_bloom(event.bloom());
match engine.signals_epoch_end(&new_header, None, None) {
EpochChange::Unsure(Unsure::NeedsReceipts) => {},
match engine.signals_epoch_end(&new_header, Default::default()) {
EpochChange::Unsure(AuxiliaryRequest::Receipts) => {},
_ => panic!("Expected bloom to be recognized."),
};
}
@@ -607,7 +610,7 @@ mod tests {
let mut new_header = Header::default();
new_header.set_number(0); // so the validator set doesn't look for a log
match engine.signals_epoch_end(&new_header, None, None) {
match engine.signals_epoch_end(&new_header, Default::default()) {
EpochChange::Yes(Proof::WithState(_)) => {},
_ => panic!("Expected state to be required to prove initial signal"),
};

View File

@@ -20,7 +20,7 @@ use heapsize::HeapSizeOf;
use bigint::hash::H256;
use util::Address;
use engines::{Call, Engine};
use machine::{AuxiliaryData, Call, EthereumMachine};
use header::{BlockNumber, Header};
use super::ValidatorSet;
@@ -76,13 +76,13 @@ impl ValidatorSet for SimpleList {
}
}
fn signals_epoch_end(&self, _: bool, _: &Header, _: Option<&[u8]>, _: Option<&[::receipt::Receipt]>)
-> ::engines::EpochChange
fn signals_epoch_end(&self, _: bool, _: &Header, _: AuxiliaryData)
-> ::engines::EpochChange<EthereumMachine>
{
::engines::EpochChange::No
}
fn epoch_set(&self, _first: bool, _: &Engine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option<H256>), ::error::Error> {
fn epoch_set(&self, _first: bool, _: &EthereumMachine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option<H256>), ::error::Error> {
Ok((self.clone(), None))
}

View File

@@ -24,7 +24,7 @@ use bigint::hash::H256;
use util::Address;
use bytes::Bytes;
use engines::{Call, Engine};
use machine::{AuxiliaryData, Call, EthereumMachine};
use header::{Header, BlockNumber};
use super::{ValidatorSet, SimpleList};
@@ -58,13 +58,13 @@ impl ValidatorSet for TestSet {
fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option<Vec<u8>> { None }
fn signals_epoch_end(&self, _: bool, _: &Header, _: Option<&[u8]>, _: Option<&[::receipt::Receipt]>)
-> ::engines::EpochChange
fn signals_epoch_end(&self, _: bool, _: &Header, _: AuxiliaryData)
-> ::engines::EpochChange<EthereumMachine>
{
::engines::EpochChange::No
}
fn epoch_set(&self, _: bool, _: &Engine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option<H256>), ::error::Error> {
fn epoch_set(&self, _: bool, _: &EthereumMachine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option<H256>), ::error::Error> {
Ok((self.validator.clone(), None))
}