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:
committed by
Gav Wood
parent
d8af9f4e7b
commit
bc167a211b
@@ -19,19 +19,16 @@
|
||||
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::{Weak, Arc};
|
||||
use std::time::{UNIX_EPOCH, Duration};
|
||||
use std::collections::{BTreeMap, HashSet, HashMap};
|
||||
use std::cmp;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use account_provider::AccountProvider;
|
||||
use block::*;
|
||||
use builtin::Builtin;
|
||||
use client::EngineClient;
|
||||
use engines::{Call, Engine, Seal, EngineError, ConstructedVerifier};
|
||||
use error::{Error, TransactionError, BlockError};
|
||||
use engines::{Engine, Seal, EngineError, ConstructedVerifier};
|
||||
use error::{Error, BlockError};
|
||||
use ethjson;
|
||||
use machine::{AuxiliaryData, Call, EthereumMachine};
|
||||
use header::{Header, BlockNumber};
|
||||
use spec::CommonParams;
|
||||
use transaction::UnverifiedTransaction;
|
||||
|
||||
use super::signer::EngineSigner;
|
||||
use super::validator_set::{ValidatorSet, SimpleList, new_validator_set};
|
||||
@@ -66,6 +63,8 @@ pub struct AuthorityRoundParams {
|
||||
pub validate_step_transition: u64,
|
||||
/// Immediate transitions.
|
||||
pub immediate_transitions: bool,
|
||||
/// Block reward in base units.
|
||||
pub block_reward: U256,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
||||
@@ -77,6 +76,7 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
||||
validate_score_transition: p.validate_score_transition.map_or(0, Into::into),
|
||||
validate_step_transition: p.validate_step_transition.map_or(0, Into::into),
|
||||
immediate_transitions: p.immediate_transitions.unwrap_or(false),
|
||||
block_reward: p.block_reward.map_or_else(Default::default, Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,7 +138,7 @@ impl EpochManager {
|
||||
}
|
||||
|
||||
// zoom to epoch for given header. returns true if succeeded, false otherwise.
|
||||
fn zoom_to(&mut self, client: &EngineClient, engine: &Engine, validators: &ValidatorSet, header: &Header) -> bool {
|
||||
fn zoom_to(&mut self, client: &EngineClient, machine: &EthereumMachine, validators: &ValidatorSet, header: &Header) -> bool {
|
||||
let last_was_parent = self.finality_checker.subchain_head() == Some(header.parent_hash().clone());
|
||||
|
||||
// early exit for current target == chain head, but only if the epochs are
|
||||
@@ -164,7 +164,6 @@ impl EpochManager {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// extract other epoch set if it's not the same as the last.
|
||||
if last_transition.block_hash != self.epoch_transition_hash {
|
||||
let (signal_number, set_proof, _) = destructure_proofs(&last_transition.proof)
|
||||
@@ -176,7 +175,7 @@ impl EpochManager {
|
||||
let first = signal_number == 0;
|
||||
let epoch_set = validators.epoch_set(
|
||||
first,
|
||||
engine,
|
||||
machine,
|
||||
signal_number, // use signal number so multi-set first calculation is correct.
|
||||
set_proof,
|
||||
)
|
||||
@@ -208,8 +207,6 @@ impl EpochManager {
|
||||
|
||||
/// Engine using `AuthorityRound` proof-of-authority BFT consensus.
|
||||
pub struct AuthorityRound {
|
||||
params: CommonParams,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
transition_service: IoService<()>,
|
||||
step: Arc<Step>,
|
||||
can_propose: AtomicBool,
|
||||
@@ -220,6 +217,8 @@ pub struct AuthorityRound {
|
||||
validate_step_transition: u64,
|
||||
epoch_manager: Mutex<EpochManager>,
|
||||
immediate_transitions: bool,
|
||||
block_reward: U256,
|
||||
machine: EthereumMachine,
|
||||
}
|
||||
|
||||
// header-chain validator.
|
||||
@@ -228,7 +227,7 @@ struct EpochVerifier {
|
||||
subchain_validators: SimpleList,
|
||||
}
|
||||
|
||||
impl super::EpochVerifier for EpochVerifier {
|
||||
impl super::EpochVerifier<EthereumMachine> for EpochVerifier {
|
||||
fn verify_light(&self, header: &Header) -> Result<(), Error> {
|
||||
// always check the seal since it's fast.
|
||||
// nothing heavier to do.
|
||||
@@ -250,7 +249,6 @@ impl super::EpochVerifier for EpochVerifier {
|
||||
|
||||
let headers: Vec<Header> = otry!(UntrustedRlp::new(proof).as_list().ok());
|
||||
|
||||
|
||||
for header in &headers {
|
||||
// ensure all headers have correct number of seal fields so we can `verify_external`
|
||||
// without panic.
|
||||
@@ -347,13 +345,11 @@ impl AsMillis for Duration {
|
||||
|
||||
impl AuthorityRound {
|
||||
/// Create a new instance of AuthorityRound engine.
|
||||
pub fn new(params: CommonParams, our_params: AuthorityRoundParams, builtins: BTreeMap<Address, Builtin>) -> Result<Arc<Self>, Error> {
|
||||
pub fn new(our_params: AuthorityRoundParams, machine: EthereumMachine) -> Result<Arc<Self>, Error> {
|
||||
let should_timeout = our_params.start_step.is_none();
|
||||
let initial_step = our_params.start_step.unwrap_or_else(|| (unix_now().as_secs() / our_params.step_duration.as_secs())) as usize;
|
||||
let engine = Arc::new(
|
||||
AuthorityRound {
|
||||
params: params,
|
||||
builtins: builtins,
|
||||
transition_service: IoService::<()>::start()?,
|
||||
step: Arc::new(Step {
|
||||
inner: AtomicUsize::new(initial_step),
|
||||
@@ -368,6 +364,8 @@ impl AuthorityRound {
|
||||
validate_step_transition: our_params.validate_step_transition,
|
||||
epoch_manager: Mutex::new(EpochManager::blank()),
|
||||
immediate_transitions: our_params.immediate_transitions,
|
||||
block_reward: our_params.block_reward,
|
||||
machine: machine,
|
||||
});
|
||||
|
||||
// Do not initialize timeouts for tests.
|
||||
@@ -410,22 +408,16 @@ impl IoHandler<()> for TransitionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine for AuthorityRound {
|
||||
impl Engine<EthereumMachine> for AuthorityRound {
|
||||
fn name(&self) -> &str { "AuthorityRound" }
|
||||
|
||||
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
||||
|
||||
fn machine(&self) -> &EthereumMachine { &self.machine }
|
||||
|
||||
/// Two fields - consensus step and the corresponding proposer signature.
|
||||
fn seal_fields(&self) -> usize { 2 }
|
||||
|
||||
fn params(&self) -> &CommonParams { &self.params }
|
||||
|
||||
fn additional_params(&self) -> HashMap<String, String> {
|
||||
hash_map!["registrar".to_owned() => self.params().registrar.hex()]
|
||||
}
|
||||
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins }
|
||||
|
||||
fn step(&self) {
|
||||
self.step.increment();
|
||||
self.can_propose.store(true, AtomicOrdering::SeqCst);
|
||||
@@ -444,19 +436,10 @@ impl Engine for AuthorityRound {
|
||||
]
|
||||
}
|
||||
|
||||
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
|
||||
fn populate_from_parent(&self, header: &mut Header, parent: &Header) {
|
||||
// Chain scoring: total weight is sqrt(U256::max_value())*height - step
|
||||
let new_difficulty = U256::from(U128::max_value()) + header_step(parent).expect("Header has been verified; qed").into() - self.step.load().into();
|
||||
header.set_difficulty(new_difficulty);
|
||||
header.set_gas_limit({
|
||||
let gas_limit = parent.gas_limit().clone();
|
||||
let bound_divisor = self.params().gas_limit_bound_divisor;
|
||||
if gas_limit < gas_floor_target {
|
||||
cmp::min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
||||
} else {
|
||||
cmp::max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into())
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn seals_internally(&self) -> Option<bool> {
|
||||
@@ -491,7 +474,7 @@ impl Engine for AuthorityRound {
|
||||
}
|
||||
};
|
||||
|
||||
if !epoch_manager.zoom_to(&*client, self, &*self.validators, header) {
|
||||
if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, header) {
|
||||
debug!(target: "engine", "Unable to zoom to epoch.");
|
||||
return Seal::None;
|
||||
}
|
||||
@@ -518,15 +501,15 @@ impl Engine for AuthorityRound {
|
||||
Seal::None
|
||||
}
|
||||
|
||||
fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_new_block(
|
||||
&self,
|
||||
block: &mut ExecutedBlock,
|
||||
last_hashes: Arc<::vm::LastHashes>,
|
||||
epoch_begin: bool,
|
||||
) -> Result<(), Error> {
|
||||
let parent_hash = block.fields().header.parent_hash().clone();
|
||||
::engines::common::push_last_hash(block, last_hashes.clone(), self, &parent_hash)?;
|
||||
|
||||
// with immediate transitions, we don't use the epoch mechanism anyway.
|
||||
// the genesis is always considered an epoch, but we ignore it intentionally.
|
||||
if self.immediate_transitions || !epoch_begin { return Ok(()) }
|
||||
@@ -536,10 +519,8 @@ impl Engine for AuthorityRound {
|
||||
let first = header.number() == 0;
|
||||
|
||||
let mut call = |to, data| {
|
||||
let result = ::engines::common::execute_as_system(
|
||||
let result = self.machine.execute_as_system(
|
||||
block,
|
||||
last_hashes.clone(),
|
||||
self,
|
||||
to,
|
||||
U256::max_value(), // unbounded gas? maybe make configurable.
|
||||
Some(data),
|
||||
@@ -553,17 +534,13 @@ impl Engine for AuthorityRound {
|
||||
|
||||
/// Apply the block reward on finalisation of the block.
|
||||
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
|
||||
::engines::common::bestow_block_reward(block, self)
|
||||
// TODO: move to "machine::WithBalances" trait.
|
||||
::engines::common::bestow_block_reward(block, self.block_reward)
|
||||
}
|
||||
|
||||
/// Check the number of seal fields.
|
||||
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
if header.seal().len() != self.seal_fields() {
|
||||
trace!(target: "engine", "verify_block_basic: wrong number of seal fields");
|
||||
Err(From::from(BlockError::InvalidSealArity(
|
||||
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
|
||||
)))
|
||||
} else if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) {
|
||||
fn verify_block_basic(&self, header: &Header,) -> Result<(), Error> {
|
||||
if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) {
|
||||
Err(From::from(BlockError::DifficultyOutOfBounds(
|
||||
OutOfBounds { min: None, max: Some(U256::from(U128::max_value())), found: *header.difficulty() }
|
||||
)))
|
||||
@@ -572,19 +549,10 @@ impl Engine for AuthorityRound {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Do the step and gas limit validation.
|
||||
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> {
|
||||
let step = header_step(header)?;
|
||||
|
||||
// Do not calculate difficulty for genesis blocks.
|
||||
if header.number() == 0 {
|
||||
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
|
||||
}
|
||||
|
||||
let parent_step = header_step(parent)?;
|
||||
|
||||
// Ensure header is from the step after parent.
|
||||
@@ -595,6 +563,7 @@ impl Engine for AuthorityRound {
|
||||
self.validators.report_malicious(header.author(), header.number(), header.number(), Default::default());
|
||||
Err(EngineError::DoubleVote(header.author().clone()))?;
|
||||
}
|
||||
|
||||
// Report skipped primaries.
|
||||
if let (true, Some(me)) = (step > parent_step + 1, self.signer.read().address()) {
|
||||
debug!(target: "engine", "Author {} built block with step gap. current step: {}, parent step: {}",
|
||||
@@ -611,17 +580,11 @@ impl Engine for AuthorityRound {
|
||||
}
|
||||
}
|
||||
|
||||
let gas_limit_divisor = self.params().gas_limit_bound_divisor;
|
||||
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
|
||||
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
|
||||
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() })));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Check the validators.
|
||||
fn verify_block_external(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
fn verify_block_external(&self, header: &Header) -> Result<(), Error> {
|
||||
// fetch correct validator set for current epoch, taking into account
|
||||
// finality of previous transitions.
|
||||
let active_set;
|
||||
@@ -639,7 +602,7 @@ impl Engine for AuthorityRound {
|
||||
};
|
||||
|
||||
let mut epoch_manager = self.epoch_manager.lock();
|
||||
if !epoch_manager.zoom_to(&*client, self, &*self.validators, header) {
|
||||
if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, header) {
|
||||
debug!(target: "engine", "Unable to zoom to epoch.");
|
||||
return Err(EngineError::RequiresClient.into())
|
||||
}
|
||||
@@ -667,19 +630,19 @@ impl Engine for AuthorityRound {
|
||||
.map(|set_proof| combine_proofs(0, &set_proof, &[]))
|
||||
}
|
||||
|
||||
fn signals_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
|
||||
-> super::EpochChange
|
||||
fn signals_epoch_end(&self, header: &Header, aux: AuxiliaryData)
|
||||
-> super::EpochChange<EthereumMachine>
|
||||
{
|
||||
if self.immediate_transitions { return super::EpochChange::No }
|
||||
|
||||
let first = header.number() == 0;
|
||||
self.validators.signals_epoch_end(first, header, block, receipts)
|
||||
self.validators.signals_epoch_end(first, header, aux)
|
||||
}
|
||||
|
||||
fn is_epoch_end(
|
||||
&self,
|
||||
chain_head: &Header,
|
||||
chain: &super::Headers,
|
||||
chain: &super::Headers<Header>,
|
||||
transition_store: &super::PendingTransitionStore,
|
||||
) -> Option<Vec<u8>> {
|
||||
// epochs only matter if we want to support light clients.
|
||||
@@ -703,7 +666,7 @@ impl Engine for AuthorityRound {
|
||||
|
||||
// find most recently finalized blocks, then check transition store for pending transitions.
|
||||
let mut epoch_manager = self.epoch_manager.lock();
|
||||
if !epoch_manager.zoom_to(&*client, self, &*self.validators, chain_head) {
|
||||
if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, chain_head) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -782,14 +745,14 @@ impl Engine for AuthorityRound {
|
||||
None
|
||||
}
|
||||
|
||||
fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a> {
|
||||
fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a, EthereumMachine> {
|
||||
let (signal_number, set_proof, finality_proof) = match destructure_proofs(proof) {
|
||||
Ok(x) => x,
|
||||
Err(e) => return ConstructedVerifier::Err(e),
|
||||
};
|
||||
|
||||
let first = signal_number == 0;
|
||||
match self.validators.epoch_set(first, self, signal_number, set_proof) {
|
||||
match self.validators.epoch_set(first, &self.machine, signal_number, set_proof) {
|
||||
Ok((list, finalize)) => {
|
||||
let verifier = Box::new(EpochVerifier {
|
||||
step: self.step.clone(),
|
||||
@@ -805,18 +768,6 @@ impl Engine for AuthorityRound {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), Error> {
|
||||
t.check_low_s()?;
|
||||
|
||||
if let Some(n) = t.chain_id() {
|
||||
if header.number() >= self.params().eip155_transition && n != self.params().chain_id {
|
||||
return Err(TransactionError::InvalidChainId.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_client(&self, client: Weak<EngineClient>) {
|
||||
*self.client.write() = Some(client.clone());
|
||||
self.validators.register_client(client);
|
||||
@@ -847,7 +798,6 @@ mod tests {
|
||||
use bigint::prelude::U256;
|
||||
use bigint::hash::H520;
|
||||
use header::Header;
|
||||
use error::{Error, BlockError};
|
||||
use rlp::encode;
|
||||
use block::*;
|
||||
use tests::helpers::*;
|
||||
@@ -872,27 +822,13 @@ mod tests {
|
||||
assert!(schedule.stack_limit > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verification_fails_on_short_seal() {
|
||||
let engine = Spec::new_test_round().engine;
|
||||
let header: Header = Header::default();
|
||||
|
||||
let verify_result = engine.verify_block_basic(&header, None);
|
||||
|
||||
match verify_result {
|
||||
Err(Error::Block(BlockError::InvalidSealArity(_))) => {},
|
||||
Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); },
|
||||
_ => { panic!("Should be error, got Ok"); },
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_do_signature_verification_fail() {
|
||||
let engine = Spec::new_test_round().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![encode(&H520::default()).into_vec()]);
|
||||
|
||||
let verify_result = engine.verify_block_external(&header, None);
|
||||
let verify_result = engine.verify_block_external(&header);
|
||||
assert!(verify_result.is_err());
|
||||
}
|
||||
|
||||
@@ -946,11 +882,11 @@ mod tests {
|
||||
// Two validators.
|
||||
// Spec starts with step 2.
|
||||
header.set_seal(vec![encode(&2usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
assert!(engine.verify_block_external(&header, None).is_err());
|
||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||
assert!(engine.verify_block_external(&header).is_err());
|
||||
header.set_seal(vec![encode(&1usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
assert!(engine.verify_block_external(&header, None).is_ok());
|
||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||
assert!(engine.verify_block_external(&header).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -972,11 +908,11 @@ mod tests {
|
||||
// Two validators.
|
||||
// Spec starts with step 2.
|
||||
header.set_seal(vec![encode(&1usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
assert!(engine.verify_block_external(&header, None).is_ok());
|
||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||
assert!(engine.verify_block_external(&header).is_ok());
|
||||
header.set_seal(vec![encode(&5usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
assert!(engine.verify_block_external(&header, None).is_err());
|
||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||
assert!(engine.verify_block_external(&header).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -998,9 +934,9 @@ mod tests {
|
||||
// Two validators.
|
||||
// Spec starts with step 2.
|
||||
header.set_seal(vec![encode(&5usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||
header.set_seal(vec![encode(&3usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_err());
|
||||
assert!(engine.verify_block_family(&header, &parent_header).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1013,12 +949,14 @@ mod tests {
|
||||
validate_score_transition: 0,
|
||||
validate_step_transition: 0,
|
||||
immediate_transitions: true,
|
||||
block_reward: Default::default(),
|
||||
};
|
||||
|
||||
let aura = {
|
||||
let mut c_params = ::spec::CommonParams::default();
|
||||
c_params.gas_limit_bound_divisor = 5.into();
|
||||
AuthorityRound::new(c_params, params, Default::default()).unwrap()
|
||||
let machine = ::machine::EthereumMachine::regular(c_params, Default::default());
|
||||
AuthorityRound::new(params, machine).unwrap()
|
||||
};
|
||||
|
||||
let mut parent_header: Header = Header::default();
|
||||
@@ -1030,12 +968,12 @@ mod tests {
|
||||
header.set_seal(vec![encode(&3usize).into_vec()]);
|
||||
|
||||
// Do not report when signer not present.
|
||||
assert!(aura.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
assert!(aura.verify_block_family(&header, &parent_header).is_ok());
|
||||
assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 0);
|
||||
|
||||
aura.set_signer(Arc::new(AccountProvider::transient_provider()), Default::default(), Default::default());
|
||||
|
||||
assert!(aura.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
assert!(aura.verify_block_family(&header, &parent_header).is_ok());
|
||||
assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,24 +17,18 @@
|
||||
//! A blockchain engine that supports a basic, non-BFT proof-of-authority.
|
||||
|
||||
use std::sync::{Weak, Arc};
|
||||
use std::collections::BTreeMap;
|
||||
use std::cmp;
|
||||
use bigint::prelude::U256;
|
||||
use bigint::hash::{H256, H520};
|
||||
use parking_lot::RwLock;
|
||||
use util::*;
|
||||
use unexpected::{Mismatch, OutOfBounds};
|
||||
use ethkey::{recover, public_to_address, Signature};
|
||||
use account_provider::AccountProvider;
|
||||
use block::*;
|
||||
use builtin::Builtin;
|
||||
use spec::CommonParams;
|
||||
use engines::{Engine, Seal, Call, ConstructedVerifier, EngineError};
|
||||
use engines::{Engine, Seal, ConstructedVerifier, EngineError};
|
||||
use error::{BlockError, Error};
|
||||
use evm::Schedule;
|
||||
use ethjson;
|
||||
use header::{Header, BlockNumber};
|
||||
use header::Header;
|
||||
use client::EngineClient;
|
||||
use machine::{AuxiliaryData, Call, EthereumMachine};
|
||||
use semantic_version::SemanticVersion;
|
||||
use super::signer::EngineSigner;
|
||||
use super::validator_set::{ValidatorSet, SimpleList, new_validator_set};
|
||||
@@ -58,7 +52,7 @@ struct EpochVerifier {
|
||||
list: SimpleList,
|
||||
}
|
||||
|
||||
impl super::EpochVerifier for EpochVerifier {
|
||||
impl super::EpochVerifier<EthereumMachine> for EpochVerifier {
|
||||
fn verify_light(&self, header: &Header) -> Result<(), Error> {
|
||||
verify_external(header, &self.list)
|
||||
}
|
||||
@@ -83,53 +77,31 @@ fn verify_external(header: &Header, validators: &ValidatorSet) -> Result<(), Err
|
||||
|
||||
/// Engine using `BasicAuthority`, trivial proof-of-authority consensus.
|
||||
pub struct BasicAuthority {
|
||||
params: CommonParams,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
machine: EthereumMachine,
|
||||
signer: RwLock<EngineSigner>,
|
||||
validators: Box<ValidatorSet>,
|
||||
}
|
||||
|
||||
impl BasicAuthority {
|
||||
/// Create a new instance of BasicAuthority engine
|
||||
pub fn new(params: CommonParams, our_params: BasicAuthorityParams, builtins: BTreeMap<Address, Builtin>) -> Self {
|
||||
pub fn new(our_params: BasicAuthorityParams, machine: EthereumMachine) -> Self {
|
||||
BasicAuthority {
|
||||
params: params,
|
||||
builtins: builtins,
|
||||
validators: new_validator_set(our_params.validators),
|
||||
machine: machine,
|
||||
signer: Default::default(),
|
||||
validators: new_validator_set(our_params.validators),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine for BasicAuthority {
|
||||
impl Engine<EthereumMachine> for BasicAuthority {
|
||||
fn name(&self) -> &str { "BasicAuthority" }
|
||||
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
||||
|
||||
fn machine(&self) -> &EthereumMachine { &self.machine }
|
||||
|
||||
// One field - the signature
|
||||
fn seal_fields(&self) -> usize { 1 }
|
||||
|
||||
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) -> BTreeMap<String, String> { map!["signature".to_owned() => "TODO".to_owned()] }
|
||||
|
||||
fn schedule(&self, _block_number: BlockNumber) -> Schedule {
|
||||
Schedule::new_homestead()
|
||||
}
|
||||
|
||||
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
|
||||
header.set_difficulty(parent.difficulty().clone());
|
||||
header.set_gas_limit({
|
||||
let gas_limit = parent.gas_limit().clone();
|
||||
let bound_divisor = self.params().gas_limit_bound_divisor;
|
||||
if gas_limit < gas_floor_target {
|
||||
cmp::min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
||||
} else {
|
||||
cmp::max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into())
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn seals_internally(&self) -> Option<bool> {
|
||||
Some(self.signer.read().is_some())
|
||||
}
|
||||
@@ -149,41 +121,11 @@ impl Engine for BasicAuthority {
|
||||
Seal::None
|
||||
}
|
||||
|
||||
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
// check the seal fields.
|
||||
// TODO: pull this out into common code.
|
||||
if header.seal().len() != self.seal_fields() {
|
||||
return Err(From::from(BlockError::InvalidSealArity(
|
||||
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
|
||||
)));
|
||||
}
|
||||
fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
// Do not calculate difficulty for genesis blocks.
|
||||
if header.number() == 0 {
|
||||
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
|
||||
}
|
||||
|
||||
// Check difficulty is correct given the two timestamps.
|
||||
if header.difficulty() != parent.difficulty() {
|
||||
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() })))
|
||||
}
|
||||
let gas_limit_divisor = self.params().gas_limit_bound_divisor;
|
||||
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
|
||||
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
|
||||
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() })));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_block_external(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
fn verify_block_external(&self, header: &Header) -> Result<(), Error> {
|
||||
verify_external(header, &*self.validators)
|
||||
}
|
||||
|
||||
@@ -192,26 +134,26 @@ impl Engine for BasicAuthority {
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
fn signals_epoch_end(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[::receipt::Receipt]>)
|
||||
-> super::EpochChange
|
||||
fn signals_epoch_end(&self, _header: &Header, _auxiliary: AuxiliaryData)
|
||||
-> super::EpochChange<EthereumMachine>
|
||||
{
|
||||
// don't bother signalling even though a contract might try.
|
||||
super::EpochChange::No
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn signals_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
|
||||
-> super::EpochChange
|
||||
fn signals_epoch_end(&self, header: &Header, auxiliary: AuxiliaryData)
|
||||
-> super::EpochChange<EthereumMachine>
|
||||
{
|
||||
// in test mode, always signal even though they don't be finalized.
|
||||
let first = header.number() == 0;
|
||||
self.validators.signals_epoch_end(first, header, block, receipts)
|
||||
self.validators.signals_epoch_end(first, header, auxiliary)
|
||||
}
|
||||
|
||||
fn is_epoch_end(
|
||||
&self,
|
||||
chain_head: &Header,
|
||||
_chain: &super::Headers,
|
||||
_chain: &super::Headers<Header>,
|
||||
_transition_store: &super::PendingTransitionStore,
|
||||
) -> Option<Vec<u8>> {
|
||||
let first = chain_head.number() == 0;
|
||||
@@ -220,10 +162,10 @@ impl Engine for BasicAuthority {
|
||||
self.validators.is_epoch_end(first, chain_head)
|
||||
}
|
||||
|
||||
fn epoch_verifier<'a>(&self, header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a> {
|
||||
fn epoch_verifier<'a>(&self, header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a, EthereumMachine> {
|
||||
let first = header.number() == 0;
|
||||
|
||||
match self.validators.epoch_set(first, self, header.number(), proof) {
|
||||
match self.validators.epoch_set(first, &self.machine, header.number(), proof) {
|
||||
Ok((list, finalize)) => {
|
||||
let verifier = Box::new(EpochVerifier { list: list });
|
||||
|
||||
@@ -260,7 +202,6 @@ mod tests {
|
||||
use hash::keccak;
|
||||
use bigint::hash::H520;
|
||||
use block::*;
|
||||
use error::{BlockError, Error};
|
||||
use tests::helpers::*;
|
||||
use account_provider::AccountProvider;
|
||||
use header::Header;
|
||||
@@ -287,27 +228,13 @@ mod tests {
|
||||
assert!(schedule.stack_limit > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_do_seal_verification_fail() {
|
||||
let engine = new_test_authority().engine;
|
||||
let header: Header = Header::default();
|
||||
|
||||
let verify_result = engine.verify_block_basic(&header, None);
|
||||
|
||||
match verify_result {
|
||||
Err(Error::Block(BlockError::InvalidSealArity(_))) => {},
|
||||
Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); },
|
||||
_ => { panic!("Should be error, got Ok"); },
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_do_signature_verification_fail() {
|
||||
let engine = new_test_authority().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![::rlp::encode(&H520::default()).into_vec()]);
|
||||
|
||||
let verify_result = engine.verify_block_family(&header, &Default::default(), None);
|
||||
let verify_result = engine.verify_block_external(&header);
|
||||
assert!(verify_result.is_err());
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
//! Epoch verifiers and transitions.
|
||||
|
||||
use bigint::hash::H256;
|
||||
use error::Error;
|
||||
use header::Header;
|
||||
|
||||
/// A full epoch transition.
|
||||
#[derive(Debug, Clone, RlpEncodable, RlpDecodable)]
|
||||
@@ -40,15 +38,13 @@ pub struct PendingTransition {
|
||||
}
|
||||
|
||||
/// Verifier for all blocks within an epoch with self-contained state.
|
||||
///
|
||||
/// See docs on `Engine` relating to proving functions for more details.
|
||||
pub trait EpochVerifier: Send + Sync {
|
||||
pub trait EpochVerifier<M: ::parity_machine::Machine>: Send + Sync {
|
||||
/// Lightly verify the next block header.
|
||||
/// This may not be a header belonging to a different epoch.
|
||||
fn verify_light(&self, header: &Header) -> Result<(), Error>;
|
||||
fn verify_light(&self, header: &M::Header) -> Result<(), M::Error>;
|
||||
|
||||
/// Perform potentially heavier checks on the next block header.
|
||||
fn verify_heavy(&self, header: &Header) -> Result<(), Error> {
|
||||
fn verify_heavy(&self, header: &M::Header) -> Result<(), M::Error> {
|
||||
self.verify_light(header)
|
||||
}
|
||||
|
||||
@@ -63,6 +59,6 @@ pub trait EpochVerifier: Send + Sync {
|
||||
/// Special "no-op" verifier for stateless, epoch-less engines.
|
||||
pub struct NoOp;
|
||||
|
||||
impl EpochVerifier for NoOp {
|
||||
fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) }
|
||||
impl<M: ::parity_machine::Machine> EpochVerifier<M> for NoOp {
|
||||
fn verify_light(&self, _header: &M::Header) -> Result<(), M::Error> { Ok(()) }
|
||||
}
|
||||
|
||||
@@ -14,51 +14,42 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use util::Address;
|
||||
use builtin::Builtin;
|
||||
use engines::{Engine, Seal};
|
||||
use spec::CommonParams;
|
||||
use block::{ExecutedBlock, IsBlock};
|
||||
use parity_machine::{Machine, Transactions};
|
||||
|
||||
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
|
||||
pub struct InstantSeal {
|
||||
params: CommonParams,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
/// Only seals blocks which have transactions.
|
||||
pub struct InstantSeal<M> {
|
||||
machine: M,
|
||||
}
|
||||
|
||||
impl InstantSeal {
|
||||
/// Returns new instance of InstantSeal with default VM Factory
|
||||
pub fn new(params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Self {
|
||||
impl<M> InstantSeal<M> {
|
||||
/// Returns new instance of InstantSeal over the given state machine.
|
||||
pub fn new(machine: M) -> Self {
|
||||
InstantSeal {
|
||||
params: params,
|
||||
builtins: builtins,
|
||||
machine: machine,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine for InstantSeal {
|
||||
impl<M: Machine> Engine<M> for InstantSeal<M>
|
||||
where M::LiveBlock: Transactions
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
"InstantSeal"
|
||||
}
|
||||
|
||||
fn params(&self) -> &CommonParams {
|
||||
&self.params
|
||||
}
|
||||
|
||||
fn additional_params(&self) -> HashMap<String, String> {
|
||||
hash_map!["registrar".to_owned() => self.params().registrar.hex()]
|
||||
}
|
||||
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
||||
&self.builtins
|
||||
}
|
||||
fn machine(&self) -> &M { &self.machine }
|
||||
|
||||
fn seals_internally(&self) -> Option<bool> { Some(true) }
|
||||
|
||||
fn generate_seal(&self, block: &ExecutedBlock) -> Seal {
|
||||
fn generate_seal(&self, block: &M::LiveBlock) -> Seal {
|
||||
if block.transactions().is_empty() { Seal::None } else { Seal::Regular(Vec::new()) }
|
||||
}
|
||||
|
||||
fn verify_local_seal(&self, _header: &M::Header) -> Result<(), M::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -91,10 +82,10 @@ mod tests {
|
||||
let engine = Spec::new_instant().engine;
|
||||
let mut header: Header = Header::default();
|
||||
|
||||
assert!(engine.verify_block_basic(&header, None).is_ok());
|
||||
assert!(engine.verify_block_basic(&header).is_ok());
|
||||
|
||||
header.set_seal(vec![::rlp::encode(&H520::default()).into_vec()]);
|
||||
|
||||
assert!(engine.verify_block_unordered(&header, None).is_ok());
|
||||
assert!(engine.verify_block_unordered(&header).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,18 +42,16 @@ use std::fmt;
|
||||
use self::epoch::PendingTransition;
|
||||
|
||||
use account_provider::AccountProvider;
|
||||
use block::ExecutedBlock;
|
||||
use builtin::Builtin;
|
||||
use client::EngineClient;
|
||||
use vm::{EnvInfo, LastHashes, Schedule, CreateContractAddress};
|
||||
use vm::{EnvInfo, Schedule, CreateContractAddress};
|
||||
use error::Error;
|
||||
use header::{Header, BlockNumber};
|
||||
use receipt::Receipt;
|
||||
use snapshot::SnapshotComponents;
|
||||
use spec::CommonParams;
|
||||
use transaction::{UnverifiedTransaction, SignedTransaction};
|
||||
|
||||
use ethkey::Signature;
|
||||
use parity_machine::{Machine, LocalizedMachine as Localized};
|
||||
use bigint::prelude::U256;
|
||||
use bigint::hash::H256;
|
||||
use semantic_version::SemanticVersion;
|
||||
@@ -82,6 +80,8 @@ pub enum EngineError {
|
||||
InsufficientProof(String),
|
||||
/// Failed system call.
|
||||
FailedSystemCall(String),
|
||||
/// Malformed consensus message.
|
||||
MalformedMessage(String),
|
||||
/// Requires client ref, but none registered.
|
||||
RequiresClient,
|
||||
}
|
||||
@@ -97,6 +97,7 @@ impl fmt::Display for EngineError {
|
||||
BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob),
|
||||
InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", msg),
|
||||
FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg),
|
||||
MalformedMessage(ref msg) => format!("Received malformed consensus message: {}", msg),
|
||||
RequiresClient => format!("Call requires client but none registered"),
|
||||
};
|
||||
|
||||
@@ -115,49 +116,46 @@ pub enum Seal {
|
||||
None,
|
||||
}
|
||||
|
||||
/// Type alias for a function we can make calls through synchronously.
|
||||
/// Returns the call result and state proof for each call.
|
||||
pub type Call<'a> = Fn(Address, Bytes) -> Result<(Bytes, Vec<Vec<u8>>), String> + 'a;
|
||||
|
||||
/// Type alias for a function we can get headers by hash through.
|
||||
pub type Headers<'a> = Fn(H256) -> Option<Header> + 'a;
|
||||
pub type Headers<'a, H> = Fn(H256) -> Option<H> + 'a;
|
||||
|
||||
/// Type alias for a function we can query pending transitions by block hash through.
|
||||
pub type PendingTransitionStore<'a> = Fn(H256) -> Option<PendingTransition> + 'a;
|
||||
|
||||
/// Proof dependent on state.
|
||||
pub trait StateDependentProof: Send + Sync {
|
||||
pub trait StateDependentProof<M: Machine>: Send + Sync {
|
||||
/// Generate a proof, given the state.
|
||||
fn generate_proof(&self, caller: &Call) -> Result<Vec<u8>, String>;
|
||||
// TODO: make this into an &M::StateContext
|
||||
fn generate_proof<'a>(&self, state: &<M as Localized<'a>>::StateContext) -> Result<Vec<u8>, String>;
|
||||
/// Check a proof generated elsewhere (potentially by a peer).
|
||||
// `engine` needed to check state proofs, while really this should
|
||||
// just be state machine params.
|
||||
fn check_proof(&self, engine: &Engine, proof: &[u8]) -> Result<(), String>;
|
||||
fn check_proof(&self, machine: &M, proof: &[u8]) -> Result<(), String>;
|
||||
}
|
||||
|
||||
/// Proof generated on epoch change.
|
||||
pub enum Proof {
|
||||
pub enum Proof<M: Machine> {
|
||||
/// Known proof (extracted from signal)
|
||||
Known(Vec<u8>),
|
||||
/// State dependent proof.
|
||||
WithState(Arc<StateDependentProof>),
|
||||
WithState(Arc<StateDependentProof<M>>),
|
||||
}
|
||||
|
||||
/// Generated epoch verifier.
|
||||
pub enum ConstructedVerifier<'a> {
|
||||
pub enum ConstructedVerifier<'a, M: Machine> {
|
||||
/// Fully trusted verifier.
|
||||
Trusted(Box<EpochVerifier>),
|
||||
Trusted(Box<EpochVerifier<M>>),
|
||||
/// Verifier unconfirmed. Check whether given finality proof finalizes given hash
|
||||
/// under previous epoch.
|
||||
Unconfirmed(Box<EpochVerifier>, &'a [u8], H256),
|
||||
Unconfirmed(Box<EpochVerifier<M>>, &'a [u8], H256),
|
||||
/// Error constructing verifier.
|
||||
Err(Error),
|
||||
}
|
||||
|
||||
impl<'a> ConstructedVerifier<'a> {
|
||||
impl<'a, M: Machine> ConstructedVerifier<'a, M> {
|
||||
/// Convert to a result, indicating that any necessary confirmation has been done
|
||||
/// already.
|
||||
pub fn known_confirmed(self) -> Result<Box<EpochVerifier>, Error> {
|
||||
pub fn known_confirmed(self) -> Result<Box<EpochVerifier<M>>, Error> {
|
||||
match self {
|
||||
ConstructedVerifier::Trusted(v) | ConstructedVerifier::Unconfirmed(v, _, _) => Ok(v),
|
||||
ConstructedVerifier::Err(e) => Err(e),
|
||||
@@ -166,84 +164,53 @@ impl<'a> ConstructedVerifier<'a> {
|
||||
}
|
||||
|
||||
/// Results of a query of whether an epoch change occurred at the given block.
|
||||
pub enum EpochChange {
|
||||
pub enum EpochChange<M: Machine> {
|
||||
/// Cannot determine until more data is passed.
|
||||
Unsure(Unsure),
|
||||
Unsure(M::AuxiliaryRequest),
|
||||
/// No epoch change.
|
||||
No,
|
||||
/// The epoch will change, with proof.
|
||||
Yes(Proof),
|
||||
}
|
||||
|
||||
/// More data required to determine if an epoch change occurred at a given block.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Unsure {
|
||||
/// Needs the body.
|
||||
NeedsBody,
|
||||
/// Needs the receipts.
|
||||
NeedsReceipts,
|
||||
/// Needs both body and receipts.
|
||||
NeedsBoth,
|
||||
Yes(Proof<M>),
|
||||
}
|
||||
|
||||
/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based.
|
||||
/// Provides hooks into each of the major parts of block import.
|
||||
pub trait Engine : Sync + Send {
|
||||
pub trait Engine<M: Machine>: Sync + Send {
|
||||
/// The name of this engine.
|
||||
fn name(&self) -> &str;
|
||||
/// The version of this engine. Should be of the form
|
||||
fn version(&self) -> SemanticVersion { SemanticVersion::new(0, 0, 0) }
|
||||
|
||||
/// Get access to the underlying state machine.
|
||||
// TODO: decouple.
|
||||
fn machine(&self) -> &M;
|
||||
|
||||
/// The number of additional header fields required for this engine.
|
||||
fn seal_fields(&self) -> usize { 0 }
|
||||
|
||||
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||
fn extra_info(&self, _header: &Header) -> BTreeMap<String, String> { BTreeMap::new() }
|
||||
fn extra_info(&self, _header: &M::Header) -> BTreeMap<String, String> { BTreeMap::new() }
|
||||
|
||||
/// Additional information.
|
||||
fn additional_params(&self) -> HashMap<String, String> { HashMap::new() }
|
||||
|
||||
/// Get the general parameters of the chain.
|
||||
fn params(&self) -> &CommonParams;
|
||||
|
||||
/// Get the EVM schedule for the given `block_number`.
|
||||
fn schedule(&self, block_number: BlockNumber) -> Schedule {
|
||||
self.params().schedule(block_number)
|
||||
}
|
||||
|
||||
/// Builtin-contracts we would like to see in the chain.
|
||||
/// (In principle these are just hints for the engine since that has the last word on them.)
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin>;
|
||||
|
||||
/// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`.
|
||||
fn maximum_extra_data_size(&self) -> usize { self.params().maximum_extra_data_size }
|
||||
/// Maximum number of uncles a block is allowed to declare.
|
||||
fn maximum_uncle_count(&self) -> usize { 2 }
|
||||
/// The number of generations back that uncles can be.
|
||||
fn maximum_uncle_age(&self) -> usize { 6 }
|
||||
/// The nonce with which accounts begin at given block.
|
||||
fn account_start_nonce(&self, block: u64) -> U256 {
|
||||
if block >= self.params().dust_protection_transition {
|
||||
U256::from(self.params().nonce_cap_increment) * U256::from(block)
|
||||
} else {
|
||||
self.params().account_start_nonce
|
||||
}
|
||||
}
|
||||
|
||||
/// Block transformation functions, before the transactions.
|
||||
/// `epoch_begin` set to true if this block kicks off an epoch.
|
||||
fn on_new_block(
|
||||
&self,
|
||||
block: &mut ExecutedBlock,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
_block: &mut M::LiveBlock,
|
||||
_epoch_begin: bool,
|
||||
) -> Result<(), Error> {
|
||||
let parent_hash = block.fields().header.parent_hash().clone();
|
||||
common::push_last_hash(block, last_hashes, self, &parent_hash)
|
||||
) -> Result<(), M::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Block transformation functions, after the transactions.
|
||||
fn on_close_block(&self, _block: &mut ExecutedBlock) -> Result<(), Error> {
|
||||
fn on_close_block(&self, _block: &mut M::LiveBlock) -> Result<(), M::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -251,68 +218,57 @@ pub trait Engine : Sync + Send {
|
||||
/// Some(true) means the engine is currently prime for seal generation (i.e. node is the current validator).
|
||||
/// Some(false) means that the node might seal internally but is not qualified now.
|
||||
fn seals_internally(&self) -> Option<bool> { None }
|
||||
|
||||
/// Attempt to seal the block internally.
|
||||
///
|
||||
/// If `Some` is returned, then you get a valid seal.
|
||||
///
|
||||
/// This operation is synchronous and may (quite reasonably) not be available, in which None will
|
||||
/// be returned.
|
||||
fn generate_seal(&self, _block: &ExecutedBlock) -> Seal { Seal::None }
|
||||
///
|
||||
/// It is fine to require access to state or a full client for this function, since
|
||||
/// light clients do not generate seals.
|
||||
fn generate_seal(&self, _block: &M::LiveBlock) -> Seal { Seal::None }
|
||||
|
||||
/// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block)
|
||||
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
|
||||
fn verify_block_basic(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
|
||||
/// Verify a locally-generated seal of a header.
|
||||
///
|
||||
/// If this engine seals internally,
|
||||
/// no checks have to be done here, since all internally generated seals
|
||||
/// should be valid.
|
||||
///
|
||||
/// Externally-generated seals (e.g. PoW) will need to be checked for validity.
|
||||
///
|
||||
/// It is fine to require access to state or a full client for this function, since
|
||||
/// light clients do not generate seals.
|
||||
fn verify_local_seal(&self, header: &M::Header) -> Result<(), M::Error>;
|
||||
|
||||
/// Phase 2 verification. Perform costly checks such as transaction signatures. `block` (the header's full block)
|
||||
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
|
||||
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
|
||||
/// Phase 1 quick block verification. Only does checks that are cheap. Returns either a null `Ok` or a general error detailing the problem with import.
|
||||
fn verify_block_basic(&self, _header: &M::Header) -> Result<(), M::Error> { Ok(()) }
|
||||
|
||||
/// Phase 3 verification. Check block information against parent and uncles. `block` (the header's full block)
|
||||
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
|
||||
fn verify_block_family(&self, _header: &Header, _parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
|
||||
/// Phase 2 verification. Perform costly checks such as transaction signatures. Returns either a null `Ok` or a general error detailing the problem with import.
|
||||
fn verify_block_unordered(&self, _header: &M::Header) -> Result<(), M::Error> { Ok(()) }
|
||||
|
||||
/// Phase 3 verification. Check block information against parent. Returns either a null `Ok` or a general error detailing the problem with import.
|
||||
fn verify_block_family(&self, _header: &M::Header, _parent: &M::Header) -> Result<(), Error> { Ok(()) }
|
||||
|
||||
/// Phase 4 verification. Verify block header against potentially external data.
|
||||
fn verify_block_external(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
|
||||
|
||||
/// Additional verification for transactions in blocks.
|
||||
// TODO: Add flags for which bits of the transaction to check.
|
||||
// TODO: consider including State in the params.
|
||||
fn verify_transaction_basic(&self, t: &UnverifiedTransaction, _header: &Header) -> Result<(), Error> {
|
||||
t.verify_basic(true, Some(self.params().chain_id), true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify a particular transaction is valid.
|
||||
fn verify_transaction(&self, t: UnverifiedTransaction, _header: &Header) -> Result<SignedTransaction, Error> {
|
||||
SignedTransaction::new(t)
|
||||
}
|
||||
|
||||
/// The network ID that transactions should be signed with.
|
||||
fn signing_chain_id(&self, _env_info: &EnvInfo) -> Option<u64> {
|
||||
Some(self.params().chain_id)
|
||||
}
|
||||
|
||||
/// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods
|
||||
/// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer
|
||||
/// methods are needed for an Engine, this may be overridden.
|
||||
fn verify_block_seal(&self, header: &Header) -> Result<(), Error> {
|
||||
self.verify_block_basic(header, None).and_then(|_| self.verify_block_unordered(header, None))
|
||||
}
|
||||
/// Should only be called when `register_client` has been called previously.
|
||||
fn verify_block_external(&self, _header: &M::Header) -> Result<(), Error> { Ok(()) }
|
||||
|
||||
/// Genesis epoch data.
|
||||
fn genesis_epoch_data(&self, _header: &Header, _call: &Call) -> Result<Vec<u8>, String> { Ok(Vec::new()) }
|
||||
fn genesis_epoch_data<'a>(&self, _header: &M::Header, _state: &<M as Localized<'a>>::StateContext) -> Result<Vec<u8>, String> { Ok(Vec::new()) }
|
||||
|
||||
/// Whether an epoch change is signalled at the given header but will require finality.
|
||||
/// If a change can be enacted immediately then return `No` from this function but
|
||||
/// `Yes` from `is_epoch_end`.
|
||||
///
|
||||
/// If the block or receipts are required, return `Unsure` and the function will be
|
||||
/// If auxiliary data of the block is required, return an auxiliary request and the function will be
|
||||
/// called again with them.
|
||||
/// Return `Yes` or `No` when the answer is definitively known.
|
||||
///
|
||||
/// Should not interact with state.
|
||||
fn signals_epoch_end(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>)
|
||||
-> EpochChange
|
||||
fn signals_epoch_end<'a>(&self, _header: &M::Header, _aux: <M as Localized<'a>>::AuxiliaryData)
|
||||
-> EpochChange<M>
|
||||
{
|
||||
EpochChange::No
|
||||
}
|
||||
@@ -326,8 +282,8 @@ pub trait Engine : Sync + Send {
|
||||
/// Return optional transition proof.
|
||||
fn is_epoch_end(
|
||||
&self,
|
||||
_chain_head: &Header,
|
||||
_chain: &Headers,
|
||||
_chain_head: &M::Header,
|
||||
_chain: &Headers<M::Header>,
|
||||
_transition_store: &PendingTransitionStore,
|
||||
) -> Option<Vec<u8>> {
|
||||
None
|
||||
@@ -335,35 +291,21 @@ pub trait Engine : Sync + Send {
|
||||
|
||||
/// Create an epoch verifier from validation proof and a flag indicating
|
||||
/// whether finality is required.
|
||||
fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> ConstructedVerifier<'a> {
|
||||
fn epoch_verifier<'a>(&self, _header: &M::Header, _proof: &'a [u8]) -> ConstructedVerifier<'a, M> {
|
||||
ConstructedVerifier::Trusted(Box::new(self::epoch::NoOp))
|
||||
}
|
||||
|
||||
/// Populate a header's fields based on its parent's header.
|
||||
/// Usually implements the chain scoring rule based on weight.
|
||||
/// The gas floor target must not be lower than the engine's minimum gas limit.
|
||||
fn populate_from_parent(&self, header: &mut Header, parent: &Header, _gas_floor_target: U256, _gas_ceil_target: U256) {
|
||||
header.set_difficulty(parent.difficulty().clone());
|
||||
header.set_gas_limit(parent.gas_limit().clone());
|
||||
}
|
||||
fn populate_from_parent(&self, _header: &mut M::Header, _parent: &M::Header) { }
|
||||
|
||||
/// Handle any potential consensus messages;
|
||||
/// updating consensus state and potentially issuing a new one.
|
||||
fn handle_message(&self, _message: &[u8]) -> Result<(), Error> { Err(EngineError::UnexpectedMessage.into()) }
|
||||
|
||||
/// Attempt to get a handle to a built-in contract.
|
||||
/// Only returns references to activated built-ins.
|
||||
// TODO: builtin contract routing - to do this properly, it will require removing the built-in configuration-reading logic
|
||||
// from Spec into here and removing the Spec::builtins field.
|
||||
fn builtin(&self, a: &Address, block_number: ::header::BlockNumber) -> Option<&Builtin> {
|
||||
self.builtins()
|
||||
.get(a)
|
||||
.and_then(|b| if b.is_active(block_number) { Some(b) } else { None })
|
||||
}
|
||||
fn handle_message(&self, _message: &[u8]) -> Result<(), EngineError> { Err(EngineError::UnexpectedMessage) }
|
||||
|
||||
/// Find out if the block is a proposal block and should not be inserted into the DB.
|
||||
/// Takes a header of a fully verified block.
|
||||
fn is_proposal(&self, _verified_header: &Header) -> bool { false }
|
||||
fn is_proposal(&self, _verified_header: &M::Header) -> bool { false }
|
||||
|
||||
/// Register an account which signs consensus messages.
|
||||
fn set_signer(&self, _account_provider: Arc<AccountProvider>, _address: Address, _password: String) {}
|
||||
@@ -371,8 +313,8 @@ pub trait Engine : Sync + Send {
|
||||
/// Sign using the EngineSigner, to be used for consensus tx signing.
|
||||
fn sign(&self, _hash: H256) -> Result<Signature, Error> { unimplemented!() }
|
||||
|
||||
/// Add Client which can be used for sealing, querying the state and sending messages.
|
||||
fn register_client(&self, _client: Weak<EngineClient>) {}
|
||||
/// Add Client which can be used for sealing, potentially querying the state and sending messages.
|
||||
fn register_client(&self, _client: Weak<M::EngineClient>) {}
|
||||
|
||||
/// Trigger next step of the consensus engine.
|
||||
fn step(&self) {}
|
||||
@@ -390,118 +332,96 @@ pub trait Engine : Sync + Send {
|
||||
fn supports_warp(&self) -> bool {
|
||||
self.snapshot_components().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
/// If this engine supports wasm contracts.
|
||||
fn supports_wasm(&self) -> bool {
|
||||
self.params().wasm
|
||||
/// Common type alias for an engine coupled with an Ethereum-like state machine.
|
||||
// TODO: make this a _trait_ alias when those exist.
|
||||
// fortunately the effect is largely the same since engines are mostly used
|
||||
// via trait objects.
|
||||
pub trait EthEngine: Engine<::machine::EthereumMachine> {
|
||||
/// Get the general parameters of the chain.
|
||||
fn params(&self) -> &CommonParams {
|
||||
self.machine().params()
|
||||
}
|
||||
|
||||
/// Get the EVM schedule for the given block number.
|
||||
fn schedule(&self, block_number: BlockNumber) -> Schedule {
|
||||
self.machine().schedule(block_number)
|
||||
}
|
||||
|
||||
/// Builtin-contracts for the chain..
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
||||
self.machine().builtins()
|
||||
}
|
||||
|
||||
/// Attempt to get a handle to a built-in contract.
|
||||
/// Only returns references to activated built-ins.
|
||||
fn builtin(&self, a: &Address, block_number: BlockNumber) -> Option<&Builtin> {
|
||||
self.machine().builtin(a, block_number)
|
||||
}
|
||||
|
||||
/// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`.
|
||||
fn maximum_extra_data_size(&self) -> usize {
|
||||
self.machine().maximum_extra_data_size()
|
||||
}
|
||||
|
||||
/// The nonce with which accounts begin at given block.
|
||||
fn account_start_nonce(&self, block: u64) -> U256 {
|
||||
self.machine().account_start_nonce(block)
|
||||
}
|
||||
|
||||
/// The network ID that transactions should be signed with.
|
||||
fn signing_chain_id(&self, env_info: &EnvInfo) -> Option<u64> {
|
||||
self.machine().signing_chain_id(env_info)
|
||||
}
|
||||
|
||||
/// Returns new contract address generation scheme at given block number.
|
||||
fn create_address_scheme(&self, number: BlockNumber) -> CreateContractAddress {
|
||||
if number >= self.params().eip86_transition {
|
||||
CreateContractAddress::FromCodeHash
|
||||
} else {
|
||||
CreateContractAddress::FromSenderAndNonce
|
||||
}
|
||||
self.machine().create_address_scheme(number)
|
||||
}
|
||||
|
||||
/// Verify a particular transaction is valid.
|
||||
fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result<SignedTransaction, Error> {
|
||||
self.machine().verify_transaction_unordered(t, header)
|
||||
}
|
||||
|
||||
/// Additional verification for transactions in blocks.
|
||||
// TODO: Add flags for which bits of the transaction to check.
|
||||
// TODO: consider including State in the params.
|
||||
fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), Error> {
|
||||
self.machine().verify_transaction_basic(t, header)
|
||||
}
|
||||
|
||||
/// If this machine supports wasm.
|
||||
fn supports_wasm(&self) -> bool {
|
||||
self.machine().supports_wasm()
|
||||
}
|
||||
}
|
||||
|
||||
// convenience wrappers for existing functions.
|
||||
impl<T> EthEngine for T where T: Engine<::machine::EthereumMachine> { }
|
||||
|
||||
/// Common engine utilities
|
||||
pub mod common {
|
||||
use std::sync::Arc;
|
||||
use block::ExecutedBlock;
|
||||
use error::Error;
|
||||
use transaction::SYSTEM_ADDRESS;
|
||||
use executive::Executive;
|
||||
use vm::{CallType, ActionParams, ActionValue, EnvInfo, LastHashes};
|
||||
use trace::{NoopTracer, NoopVMTracer, Tracer, ExecutiveTracer, RewardType};
|
||||
use state::Substate;
|
||||
use trace::{Tracer, ExecutiveTracer, RewardType};
|
||||
use state::CleanupMode;
|
||||
|
||||
use bigint::prelude::U256;
|
||||
use bigint::hash::H256;
|
||||
use util::*;
|
||||
use bytes::{Bytes, BytesRef};
|
||||
use super::Engine;
|
||||
|
||||
/// Execute a call as the system address.
|
||||
pub fn execute_as_system<E: Engine + ?Sized>(
|
||||
block: &mut ExecutedBlock,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
engine: &E,
|
||||
contract_address: Address,
|
||||
gas: U256,
|
||||
data: Option<Bytes>,
|
||||
) -> Result<Bytes, Error> {
|
||||
let env_info = {
|
||||
let header = block.fields().header;
|
||||
EnvInfo {
|
||||
number: header.number(),
|
||||
author: header.author().clone(),
|
||||
timestamp: header.timestamp(),
|
||||
difficulty: header.difficulty().clone(),
|
||||
last_hashes: last_hashes,
|
||||
gas_used: U256::zero(),
|
||||
gas_limit: gas,
|
||||
}
|
||||
};
|
||||
|
||||
let mut state = block.fields_mut().state;
|
||||
let params = ActionParams {
|
||||
code_address: contract_address.clone(),
|
||||
address: contract_address.clone(),
|
||||
sender: SYSTEM_ADDRESS.clone(),
|
||||
origin: SYSTEM_ADDRESS.clone(),
|
||||
gas: gas,
|
||||
gas_price: 0.into(),
|
||||
value: ActionValue::Transfer(0.into()),
|
||||
code: state.code(&contract_address)?,
|
||||
code_hash: Some(state.code_hash(&contract_address)?),
|
||||
data: data,
|
||||
call_type: CallType::Call,
|
||||
};
|
||||
let mut ex = Executive::new(&mut state, &env_info, engine);
|
||||
let mut substate = Substate::new();
|
||||
let mut output = Vec::new();
|
||||
if let Err(e) = ex.call(params, &mut substate, BytesRef::Flexible(&mut output), &mut NoopTracer, &mut NoopVMTracer) {
|
||||
warn!("Encountered error on making system call: {}", e);
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Push last known block hash to the state.
|
||||
pub fn push_last_hash<E: Engine + ?Sized>(block: &mut ExecutedBlock, last_hashes: Arc<LastHashes>, engine: &E, hash: &H256) -> Result<(), Error> {
|
||||
if block.fields().header.number() == engine.params().eip210_transition {
|
||||
let state = block.fields_mut().state;
|
||||
state.init_code(&engine.params().eip210_contract_address, engine.params().eip210_contract_code.clone())?;
|
||||
}
|
||||
if block.fields().header.number() >= engine.params().eip210_transition {
|
||||
let _ = execute_as_system(
|
||||
block,
|
||||
last_hashes,
|
||||
engine,
|
||||
engine.params().eip210_contract_address,
|
||||
engine.params().eip210_contract_gas,
|
||||
Some(hash.to_vec()),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Trace rewards on closing block
|
||||
pub fn bestow_block_reward<E: Engine + ?Sized>(block: &mut ExecutedBlock, engine: &E) -> Result<(), Error> {
|
||||
/// Give reward and trace.
|
||||
pub fn bestow_block_reward(block: &mut ExecutedBlock, reward: U256) -> Result<(), Error> {
|
||||
let fields = block.fields_mut();
|
||||
// Bestow block reward
|
||||
let reward = engine.params().block_reward;
|
||||
let res = fields.state.add_balance(fields.header.author(), &reward, CleanupMode::NoEmpty)
|
||||
.map_err(::error::Error::from)
|
||||
.and_then(|_| fields.state.commit());
|
||||
|
||||
let block_author = fields.header.author().clone();
|
||||
fields.traces.as_mut().map(|mut traces| {
|
||||
fields.traces.as_mut().map(move |mut traces| {
|
||||
let mut tracer = ExecutiveTracer::default();
|
||||
tracer.trace_reward(block_author, engine.params().block_reward, RewardType::Block);
|
||||
tracer.trace_reward(block_author, reward, RewardType::Block);
|
||||
traces.push(tracer.drain())
|
||||
});
|
||||
|
||||
|
||||
@@ -14,103 +14,92 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use util::Address;
|
||||
use builtin::Builtin;
|
||||
use block::{ExecutedBlock, IsBlock};
|
||||
use bigint::prelude::U256;
|
||||
use engines::Engine;
|
||||
use spec::CommonParams;
|
||||
use evm::Schedule;
|
||||
use header::BlockNumber;
|
||||
use error::Error;
|
||||
use state::CleanupMode;
|
||||
use trace::{Tracer, ExecutiveTracer, RewardType};
|
||||
use parity_machine::{Header, LiveBlock, WithBalances};
|
||||
|
||||
/// An engine which does not provide any consensus mechanism and does not seal blocks.
|
||||
pub struct NullEngine {
|
||||
params: CommonParams,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
/// Params for a null engine.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct NullEngineParams {
|
||||
/// base reward for a block.
|
||||
pub block_reward: U256,
|
||||
}
|
||||
|
||||
impl NullEngine {
|
||||
/// Returns new instance of NullEngine with default VM Factory
|
||||
pub fn new(params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Self {
|
||||
NullEngine{
|
||||
params: params,
|
||||
builtins: builtins,
|
||||
impl From<::ethjson::spec::NullEngineParams> for NullEngineParams {
|
||||
fn from(p: ::ethjson::spec::NullEngineParams) -> Self {
|
||||
NullEngineParams {
|
||||
block_reward: p.block_reward.map_or_else(Default::default, Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NullEngine {
|
||||
/// An engine which does not provide any consensus mechanism and does not seal blocks.
|
||||
pub struct NullEngine<M> {
|
||||
params: NullEngineParams,
|
||||
machine: M,
|
||||
}
|
||||
|
||||
impl<M> NullEngine<M> {
|
||||
/// Returns new instance of NullEngine with default VM Factory
|
||||
pub fn new(params: NullEngineParams, machine: M) -> Self {
|
||||
NullEngine {
|
||||
params: params,
|
||||
machine: machine,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Default> Default for NullEngine<M> {
|
||||
fn default() -> Self {
|
||||
Self::new(Default::default(), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine for NullEngine {
|
||||
impl<M: WithBalances> Engine<M> for NullEngine<M> {
|
||||
fn name(&self) -> &str {
|
||||
"NullEngine"
|
||||
}
|
||||
|
||||
fn params(&self) -> &CommonParams {
|
||||
&self.params
|
||||
fn machine(&self) -> &M { &self.machine }
|
||||
|
||||
fn on_close_block(&self, block: &mut M::LiveBlock) -> Result<(), M::Error> {
|
||||
use std::ops::Shr;
|
||||
|
||||
let author = *LiveBlock::header(&*block).author();
|
||||
let number = LiveBlock::header(&*block).number();
|
||||
|
||||
let reward = self.params.block_reward;
|
||||
if reward == U256::zero() { return Ok(()) }
|
||||
|
||||
let n_uncles = LiveBlock::uncles(&*block).len();
|
||||
|
||||
// Bestow block reward
|
||||
let result_block_reward = reward + reward.shr(5) * U256::from(n_uncles);
|
||||
let mut uncle_rewards = Vec::with_capacity(n_uncles);
|
||||
|
||||
self.machine.add_balance(block, &author, &result_block_reward)?;
|
||||
|
||||
// bestow uncle rewards.
|
||||
for u in LiveBlock::uncles(&*block) {
|
||||
let uncle_author = u.author();
|
||||
let result_uncle_reward = (reward * U256::from(8 + u.number() - number)).shr(3);
|
||||
|
||||
uncle_rewards.push((*uncle_author, result_uncle_reward));
|
||||
}
|
||||
|
||||
for &(ref a, ref reward) in &uncle_rewards {
|
||||
self.machine.add_balance(block, a, reward)?;
|
||||
}
|
||||
|
||||
// note and trace.
|
||||
self.machine.note_rewards(block, &[(author, result_block_reward)], &uncle_rewards)
|
||||
}
|
||||
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
||||
&self.builtins
|
||||
}
|
||||
|
||||
fn schedule(&self, _block_number: BlockNumber) -> Schedule {
|
||||
Schedule::new_homestead()
|
||||
fn verify_local_seal(&self, _header: &M::Header) -> Result<(), M::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {
|
||||
Some(Box::new(::snapshot::PowSnapshot::new(10000, 10000)))
|
||||
}
|
||||
|
||||
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
|
||||
if self.params.block_reward == U256::zero() {
|
||||
// we don't have to apply reward in this case
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
/// Block reward
|
||||
let tracing_enabled = block.tracing_enabled();
|
||||
let fields = block.fields_mut();
|
||||
let mut tracer = ExecutiveTracer::default();
|
||||
|
||||
let result_block_reward = U256::from(1000000000);
|
||||
fields.state.add_balance(
|
||||
fields.header.author(),
|
||||
&result_block_reward,
|
||||
CleanupMode::NoEmpty
|
||||
)?;
|
||||
|
||||
if tracing_enabled {
|
||||
let block_author = fields.header.author().clone();
|
||||
tracer.trace_reward(block_author, result_block_reward, RewardType::Block);
|
||||
}
|
||||
|
||||
/// Uncle rewards
|
||||
let result_uncle_reward = U256::from(10000000);
|
||||
for u in fields.uncles.iter() {
|
||||
let uncle_author = u.author().clone();
|
||||
fields.state.add_balance(
|
||||
u.author(),
|
||||
&(result_uncle_reward),
|
||||
CleanupMode::NoEmpty
|
||||
)?;
|
||||
if tracing_enabled {
|
||||
tracer.trace_reward(uncle_author, result_uncle_reward, RewardType::Uncle);
|
||||
}
|
||||
}
|
||||
|
||||
fields.state.commit()?;
|
||||
if tracing_enabled {
|
||||
fields.traces.as_mut().map(|mut traces| traces.push(tracer.drain()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,8 @@ mod params;
|
||||
|
||||
use std::sync::{Weak, Arc};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
|
||||
use std::collections::{HashSet, BTreeMap, HashMap};
|
||||
use std::collections::{HashSet, BTreeMap};
|
||||
use hash::keccak;
|
||||
use std::cmp;
|
||||
use bigint::prelude::{U128, U256};
|
||||
use bigint::hash::{H256, H520};
|
||||
use parking_lot::RwLock;
|
||||
@@ -39,12 +38,10 @@ use client::EngineClient;
|
||||
use bytes::Bytes;
|
||||
use error::{Error, BlockError};
|
||||
use header::{Header, BlockNumber};
|
||||
use builtin::Builtin;
|
||||
use rlp::UntrustedRlp;
|
||||
use ethkey::{Message, public_to_address, recover, Signature};
|
||||
use account_provider::AccountProvider;
|
||||
use block::*;
|
||||
use spec::CommonParams;
|
||||
use engines::{Engine, Seal, EngineError, ConstructedVerifier};
|
||||
use io::IoService;
|
||||
use super::signer::EngineSigner;
|
||||
@@ -54,6 +51,7 @@ use super::vote_collector::VoteCollector;
|
||||
use self::message::*;
|
||||
use self::params::TendermintParams;
|
||||
use semantic_version::SemanticVersion;
|
||||
use machine::{AuxiliaryData, EthereumMachine};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
pub enum Step {
|
||||
@@ -78,8 +76,6 @@ pub type BlockHash = H256;
|
||||
|
||||
/// Engine using `Tendermint` consensus algorithm, suitable for EVM chain.
|
||||
pub struct Tendermint {
|
||||
params: CommonParams,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
step_service: IoService<Step>,
|
||||
client: RwLock<Option<Weak<EngineClient>>>,
|
||||
/// Blockchain height.
|
||||
@@ -104,6 +100,10 @@ pub struct Tendermint {
|
||||
last_proposed: RwLock<H256>,
|
||||
/// Set used to determine the current validators.
|
||||
validators: Box<ValidatorSet>,
|
||||
/// Reward per block, in base units.
|
||||
block_reward: U256,
|
||||
/// ethereum machine descriptor
|
||||
machine: EthereumMachine,
|
||||
}
|
||||
|
||||
struct EpochVerifier<F>
|
||||
@@ -113,7 +113,7 @@ struct EpochVerifier<F>
|
||||
recover: F
|
||||
}
|
||||
|
||||
impl <F> super::EpochVerifier for EpochVerifier<F>
|
||||
impl <F> super::EpochVerifier<EthereumMachine> for EpochVerifier<F>
|
||||
where F: Fn(&Signature, &Message) -> Result<Address, Error> + Send + Sync
|
||||
{
|
||||
fn verify_light(&self, header: &Header) -> Result<(), Error> {
|
||||
@@ -167,11 +167,9 @@ fn destructure_proofs(combined: &[u8]) -> Result<(BlockNumber, &[u8], &[u8]), Er
|
||||
|
||||
impl Tendermint {
|
||||
/// Create a new instance of Tendermint engine
|
||||
pub fn new(params: CommonParams, our_params: TendermintParams, builtins: BTreeMap<Address, Builtin>) -> Result<Arc<Self>, Error> {
|
||||
pub fn new(our_params: TendermintParams, machine: EthereumMachine) -> Result<Arc<Self>, Error> {
|
||||
let engine = Arc::new(
|
||||
Tendermint {
|
||||
params: params,
|
||||
builtins: builtins,
|
||||
client: RwLock::new(None),
|
||||
step_service: IoService::<Step>::start()?,
|
||||
height: AtomicUsize::new(1),
|
||||
@@ -185,9 +183,13 @@ impl Tendermint {
|
||||
proposal_parent: Default::default(),
|
||||
last_proposed: Default::default(),
|
||||
validators: our_params.validators,
|
||||
block_reward: our_params.block_reward,
|
||||
machine: machine,
|
||||
});
|
||||
let handler = TransitionHandler::new(Arc::downgrade(&engine) as Weak<Engine>, Box::new(our_params.timeouts));
|
||||
|
||||
let handler = TransitionHandler::new(Arc::downgrade(&engine) as Weak<Engine<_>>, Box::new(our_params.timeouts));
|
||||
engine.step_service.register_handler(Arc::new(handler))?;
|
||||
|
||||
Ok(engine)
|
||||
}
|
||||
|
||||
@@ -438,7 +440,7 @@ impl Tendermint {
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine for Tendermint {
|
||||
impl Engine<EthereumMachine> for Tendermint {
|
||||
fn name(&self) -> &str { "Tendermint" }
|
||||
|
||||
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
||||
@@ -446,13 +448,7 @@ impl Engine for Tendermint {
|
||||
/// (consensus view, proposal signature, authority signatures)
|
||||
fn seal_fields(&self) -> usize { 3 }
|
||||
|
||||
fn params(&self) -> &CommonParams { &self.params }
|
||||
|
||||
fn additional_params(&self) -> HashMap<String, String> {
|
||||
hash_map!["registrar".to_owned() => self.params().registrar.hex()]
|
||||
}
|
||||
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins }
|
||||
fn machine(&self) -> &EthereumMachine { &self.machine }
|
||||
|
||||
fn maximum_uncle_count(&self) -> usize { 0 }
|
||||
|
||||
@@ -469,19 +465,13 @@ impl Engine for Tendermint {
|
||||
]
|
||||
}
|
||||
|
||||
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
|
||||
fn populate_from_parent(&self, header: &mut Header, parent: &Header) {
|
||||
// Chain scoring: total weight is sqrt(U256::max_value())*height - view
|
||||
let new_difficulty = U256::from(U128::max_value()) + consensus_view(parent).expect("Header has been verified; qed").into() - self.view.load(AtomicOrdering::SeqCst).into();
|
||||
let new_difficulty = U256::from(U128::max_value())
|
||||
+ consensus_view(parent).expect("Header has been verified; qed").into()
|
||||
- self.view.load(AtomicOrdering::SeqCst).into();
|
||||
|
||||
header.set_difficulty(new_difficulty);
|
||||
header.set_gas_limit({
|
||||
let gas_limit = parent.gas_limit().clone();
|
||||
let bound_divisor = self.params().gas_limit_bound_divisor;
|
||||
if gas_limit < gas_floor_target {
|
||||
cmp::min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
||||
} else {
|
||||
cmp::max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into())
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Should this node participate.
|
||||
@@ -525,19 +515,27 @@ impl Engine for Tendermint {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_message(&self, rlp: &[u8]) -> Result<(), Error> {
|
||||
fn handle_message(&self, rlp: &[u8]) -> Result<(), EngineError> {
|
||||
fn fmt_err<T: ::std::fmt::Debug>(x: T) -> EngineError {
|
||||
EngineError::MalformedMessage(format!("{:?}", x))
|
||||
}
|
||||
|
||||
let rlp = UntrustedRlp::new(rlp);
|
||||
let message: ConsensusMessage = rlp.as_val()?;
|
||||
let message: ConsensusMessage = rlp.as_val().map_err(fmt_err)?;
|
||||
if !self.votes.is_old_or_known(&message) {
|
||||
let sender = public_to_address(&recover(&message.signature.into(), &keccak(rlp.at(1)?.as_raw()))?);
|
||||
let msg_hash = keccak(rlp.at(1).map_err(fmt_err)?.as_raw());
|
||||
let sender = public_to_address(
|
||||
&recover(&message.signature.into(), &msg_hash).map_err(fmt_err)?
|
||||
);
|
||||
|
||||
if !self.is_authority(&sender) {
|
||||
return Err(EngineError::NotAuthorized(sender).into());
|
||||
return Err(EngineError::NotAuthorized(sender));
|
||||
}
|
||||
self.broadcast_message(rlp.as_raw().to_vec());
|
||||
if let Some(double) = self.votes.vote(message.clone(), &sender) {
|
||||
let height = message.vote_step.height as BlockNumber;
|
||||
self.validators.report_malicious(&sender, height, height, ::rlp::encode(&double).into_vec());
|
||||
return Err(EngineError::DoubleVote(sender).into());
|
||||
return Err(EngineError::DoubleVote(sender));
|
||||
}
|
||||
trace!(target: "engine", "Handling a valid {:?} from {}.", message, sender);
|
||||
self.handle_valid_message(&message);
|
||||
@@ -545,12 +543,37 @@ impl Engine for Tendermint {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Apply the block reward on finalisation of the block.
|
||||
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error>{
|
||||
::engines::common::bestow_block_reward(block, self)
|
||||
fn on_new_block(&self, block: &mut ExecutedBlock, epoch_begin: bool) -> Result<(), Error> {
|
||||
if !epoch_begin { return Ok(()) }
|
||||
|
||||
// genesis is never a new block, but might as well check.
|
||||
let header = block.fields().header.clone();
|
||||
let first = header.number() == 0;
|
||||
|
||||
let mut call = |to, data| {
|
||||
let result = self.machine.execute_as_system(
|
||||
block,
|
||||
to,
|
||||
U256::max_value(), // unbounded gas? maybe make configurable.
|
||||
Some(data),
|
||||
);
|
||||
|
||||
result.map_err(|e| format!("{}", e))
|
||||
};
|
||||
|
||||
self.validators.on_epoch_begin(first, &header, &mut call)
|
||||
}
|
||||
|
||||
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
/// Apply the block reward on finalisation of the block.
|
||||
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error>{
|
||||
::engines::common::bestow_block_reward(block, self.block_reward)
|
||||
}
|
||||
|
||||
fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_block_basic(&self, header: &Header) -> Result<(), Error> {
|
||||
let seal_length = header.seal().len();
|
||||
if seal_length == self.seal_fields() {
|
||||
// Either proposal or commit.
|
||||
@@ -568,28 +591,7 @@ impl Engine for Tendermint {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify gas limit.
|
||||
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
if header.number() == 0 {
|
||||
return Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }).into());
|
||||
}
|
||||
|
||||
let gas_limit_divisor = self.params().gas_limit_bound_divisor;
|
||||
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
|
||||
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
|
||||
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||
self.validators.report_malicious(header.author(), header.number(), header.number(), Default::default());
|
||||
return Err(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }).into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_block_external(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
fn verify_block_external(&self, header: &Header) -> Result<(), Error> {
|
||||
if let Ok(proposal) = ConsensusMessage::new_proposal(header) {
|
||||
let proposer = proposal.verify()?;
|
||||
if !self.is_authority(&proposer) {
|
||||
@@ -630,17 +632,17 @@ impl Engine for Tendermint {
|
||||
}
|
||||
}
|
||||
|
||||
fn signals_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
|
||||
-> super::EpochChange
|
||||
fn signals_epoch_end(&self, header: &Header, aux: AuxiliaryData)
|
||||
-> super::EpochChange<EthereumMachine>
|
||||
{
|
||||
let first = header.number() == 0;
|
||||
self.validators.signals_epoch_end(first, header, block, receipts)
|
||||
self.validators.signals_epoch_end(first, header, aux)
|
||||
}
|
||||
|
||||
fn is_epoch_end(
|
||||
&self,
|
||||
chain_head: &Header,
|
||||
_chain: &super::Headers,
|
||||
_chain: &super::Headers<Header>,
|
||||
transition_store: &super::PendingTransitionStore,
|
||||
) -> Option<Vec<u8>> {
|
||||
let first = chain_head.number() == 0;
|
||||
@@ -657,14 +659,14 @@ impl Engine for Tendermint {
|
||||
None
|
||||
}
|
||||
|
||||
fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a> {
|
||||
fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a, EthereumMachine> {
|
||||
let (signal_number, set_proof, finality_proof) = match destructure_proofs(proof) {
|
||||
Ok(x) => x,
|
||||
Err(e) => return ConstructedVerifier::Err(e),
|
||||
};
|
||||
|
||||
let first = signal_number == 0;
|
||||
match self.validators.epoch_set(first, self, signal_number, set_proof) {
|
||||
match self.validators.epoch_set(first, &self.machine, signal_number, set_proof) {
|
||||
Ok((list, finalize)) => {
|
||||
let verifier = Box::new(EpochVerifier {
|
||||
subchain_validators: list,
|
||||
@@ -785,7 +787,7 @@ mod tests {
|
||||
use tests::helpers::*;
|
||||
use account_provider::AccountProvider;
|
||||
use spec::Spec;
|
||||
use engines::{Engine, EngineError, Seal};
|
||||
use engines::{EthEngine, EngineError, Seal};
|
||||
use engines::epoch::EpochVerifier;
|
||||
use super::*;
|
||||
|
||||
@@ -810,7 +812,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn vote<F>(engine: &Engine, signer: F, height: usize, view: usize, step: Step, block_hash: Option<H256>) -> Bytes where F: FnOnce(H256) -> Result<H520, ::account_provider::SignError> {
|
||||
fn vote<F>(engine: &EthEngine, signer: F, height: usize, view: usize, step: Step, block_hash: Option<H256>) -> Bytes where F: FnOnce(H256) -> Result<H520, ::account_provider::SignError> {
|
||||
let mi = message_info_rlp(&VoteStep::new(height, view, step), block_hash);
|
||||
let m = message_full_rlp(&signer(keccak(&mi)).unwrap().into(), &mi);
|
||||
engine.handle_message(&m).unwrap();
|
||||
@@ -834,7 +836,7 @@ mod tests {
|
||||
addr
|
||||
}
|
||||
|
||||
fn insert_and_register(tap: &Arc<AccountProvider>, engine: &Engine, acc: &str) -> Address {
|
||||
fn insert_and_register(tap: &Arc<AccountProvider>, engine: &EthEngine, acc: &str) -> Address {
|
||||
let addr = insert_and_unlock(tap, acc);
|
||||
engine.set_signer(tap.clone(), addr.clone(), acc.into());
|
||||
addr
|
||||
@@ -871,7 +873,7 @@ mod tests {
|
||||
let engine = Spec::new_test_tendermint().engine;
|
||||
let header = Header::default();
|
||||
|
||||
let verify_result = engine.verify_block_basic(&header, None);
|
||||
let verify_result = engine.verify_block_basic(&header);
|
||||
|
||||
match verify_result {
|
||||
Err(Error::Block(BlockError::InvalidSealArity(_))) => {},
|
||||
@@ -896,14 +898,14 @@ mod tests {
|
||||
let seal = proposal_seal(&tap, &header, 0);
|
||||
header.set_seal(seal);
|
||||
// Good proposer.
|
||||
assert!(engine.verify_block_external(&header, None).is_ok());
|
||||
assert!(engine.verify_block_external(&header).is_ok());
|
||||
|
||||
let validator = insert_and_unlock(&tap, "0");
|
||||
header.set_author(validator);
|
||||
let seal = proposal_seal(&tap, &header, 0);
|
||||
header.set_seal(seal);
|
||||
// Bad proposer.
|
||||
match engine.verify_block_external(&header, None) {
|
||||
match engine.verify_block_external(&header) {
|
||||
Err(Error::Engine(EngineError::NotProposer(_))) => {},
|
||||
_ => panic!(),
|
||||
}
|
||||
@@ -913,7 +915,7 @@ mod tests {
|
||||
let seal = proposal_seal(&tap, &header, 0);
|
||||
header.set_seal(seal);
|
||||
// Not authority.
|
||||
match engine.verify_block_external(&header, None) {
|
||||
match engine.verify_block_external(&header) {
|
||||
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
|
||||
_ => panic!(),
|
||||
};
|
||||
@@ -943,7 +945,7 @@ mod tests {
|
||||
header.set_seal(seal.clone());
|
||||
|
||||
// One good signature is not enough.
|
||||
match engine.verify_block_external(&header, None) {
|
||||
match engine.verify_block_external(&header) {
|
||||
Err(Error::Engine(EngineError::BadSealFieldSize(_))) => {},
|
||||
_ => panic!(),
|
||||
}
|
||||
@@ -954,7 +956,7 @@ mod tests {
|
||||
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1.clone()), H520::from(signature0.clone())]).into_vec();
|
||||
header.set_seal(seal.clone());
|
||||
|
||||
assert!(engine.verify_block_external(&header, None).is_ok());
|
||||
assert!(engine.verify_block_external(&header).is_ok());
|
||||
|
||||
let bad_voter = insert_and_unlock(&tap, "101");
|
||||
let bad_signature = tap.sign(bad_voter, None, keccak(vote_info)).unwrap();
|
||||
@@ -963,7 +965,7 @@ mod tests {
|
||||
header.set_seal(seal);
|
||||
|
||||
// One good and one bad signature.
|
||||
match engine.verify_block_external(&header, None) {
|
||||
match engine.verify_block_external(&header) {
|
||||
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
use ethjson;
|
||||
use time::Duration;
|
||||
use bigint::prelude::U256;
|
||||
use super::super::validator_set::{ValidatorSet, new_validator_set};
|
||||
use super::super::transition::Timeouts;
|
||||
use super::Step;
|
||||
@@ -28,6 +29,8 @@ pub struct TendermintParams {
|
||||
pub validators: Box<ValidatorSet>,
|
||||
/// Timeout durations for different steps.
|
||||
pub timeouts: TendermintTimeouts,
|
||||
/// Reward per block in base units.
|
||||
pub block_reward: U256,
|
||||
}
|
||||
|
||||
/// Base timeout of each step in ms.
|
||||
@@ -81,6 +84,7 @@ impl From<ethjson::spec::TendermintParams> for TendermintParams {
|
||||
precommit: p.timeout_precommit.map_or(dt.precommit, to_duration),
|
||||
commit: p.timeout_commit.map_or(dt.commit, to_duration),
|
||||
},
|
||||
block_reward: p.block_reward.map_or(U256::default(), Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use std::sync::Weak;
|
||||
use time::Duration;
|
||||
use io::{IoContext, IoHandler, TimerToken};
|
||||
use engines::Engine;
|
||||
use parity_machine::Machine;
|
||||
|
||||
/// Timeouts lookup
|
||||
pub trait Timeouts<S: Sync + Send + Clone>: Send + Sync {
|
||||
@@ -31,14 +32,14 @@ pub trait Timeouts<S: Sync + Send + Clone>: Send + Sync {
|
||||
}
|
||||
|
||||
/// Timeout transition handling.
|
||||
pub struct TransitionHandler<S: Sync + Send + Clone> {
|
||||
engine: Weak<Engine>,
|
||||
pub struct TransitionHandler<S: Sync + Send + Clone, M: Machine> {
|
||||
engine: Weak<Engine<M>>,
|
||||
timeouts: Box<Timeouts<S>>,
|
||||
}
|
||||
|
||||
impl<S> TransitionHandler<S> where S: Sync + Send + Clone {
|
||||
impl<S, M: Machine> TransitionHandler<S, M> where S: Sync + Send + Clone {
|
||||
/// New step caller by timeouts.
|
||||
pub fn new(engine: Weak<Engine>, timeouts: Box<Timeouts<S>>) -> Self {
|
||||
pub fn new(engine: Weak<Engine<M>>, timeouts: Box<Timeouts<S>>) -> Self {
|
||||
TransitionHandler {
|
||||
engine: engine,
|
||||
timeouts: timeouts,
|
||||
@@ -54,7 +55,9 @@ fn set_timeout<S: Sync + Send + Clone>(io: &IoContext<S>, timeout: Duration) {
|
||||
.unwrap_or_else(|e| warn!(target: "engine", "Failed to set consensus step timeout: {}.", e))
|
||||
}
|
||||
|
||||
impl<S> IoHandler<S> for TransitionHandler<S> where S: Sync + Send + Clone + 'static {
|
||||
impl<S, M> IoHandler<S> for TransitionHandler<S, M>
|
||||
where S: Sync + Send + Clone + 'static, M: Machine
|
||||
{
|
||||
fn initialize(&self, io: &IoContext<S>) {
|
||||
let initial = self.timeouts.initial();
|
||||
trace!(target: "engine", "Setting the initial timeout to {}.", initial);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."),
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
};
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user