diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 03353a2a4..a91294e27 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -579,11 +579,11 @@ impl Client { // generate validation proof if the engine requires them. // TODO: make conditional? let generate_proof = { - use engines::RequiresProof; - match self.engine.proof_required(block.header(), Some(block_data), Some(&receipts)) { - RequiresProof::Yes(_) => true, - RequiresProof::No => false, - RequiresProof::Unsure(_) => { + use engines::EpochChange; + match self.engine.is_epoch_end(block.header(), Some(block_data), Some(&receipts)) { + EpochChange::Yes(_) => true, + EpochChange::No => false, + EpochChange::Unsure(_) => { warn!(target: "client", "Detected invalid engine implementation."); warn!(target: "client", "Engine claims to require more block data, but everything provided."); false diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 8e8450768..1f3edf57c 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -132,12 +132,14 @@ pub struct AuthorityRound { } // header-chain validator. -struct ChainVerifier { +struct EpochVerifier { + epoch_number: U256, step: Arc, subchain_validators: SimpleList, } -impl super::ChainVerifier for ChainVerifier { +impl super::EpochVerifier for EpochVerifier { + fn epoch_number(&self) -> U256 { self.epoch_number.clone() } fn verify_light(&self, header: &Header) -> Result<(), Error> { // always check the seal since it's fast. // nothing heavier to do. @@ -398,22 +400,23 @@ impl Engine for AuthorityRound { } // the proofs we need just allow us to get the full validator set. - fn prove_with_caller(&self, header: &Header, caller: &Call) -> Result { - self.validators.generate_proof(header, caller) + fn epoch_proof(&self, header: &Header, caller: &Call) -> Result { + self.validators.epoch_proof(header, caller) .map_err(|e| EngineError::InsufficientProof(e).into()) } - fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> super::RequiresProof + fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> super::EpochChange { - self.validators.proof_required(header, block, receipts) + self.validators.is_epoch_end(header, block, receipts) } - fn chain_verifier(&self, header: &Header, proof: Bytes) -> Result, Error> { + fn epoch_verifier(&self, header: &Header, proof: &[u8]) -> Result, Error> { // extract a simple list from the proof. - let simple_list = self.validators.chain_verifier(header, proof)?; + let (num, simple_list) = self.validators.epoch_set(header, proof)?; - Ok(Box::new(ChainVerifier { + Ok(Box::new(EpochVerifier { + epoch_number: num, step: self.step.clone(), subchain_validators: simple_list, })) diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 25cdd765f..94c3fe578 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -23,7 +23,7 @@ use account_provider::AccountProvider; use block::*; use builtin::Builtin; use spec::CommonParams; -use engines::{Engine, EngineError, Seal, Call, RequiresProof}; +use engines::{Engine, EngineError, Seal, Call, EpochChange}; use env_info::EnvInfo; use error::{BlockError, Error}; use evm::Schedule; @@ -51,11 +51,15 @@ impl From for BasicAuthorityParams { } } -struct ChainVerifier(SimpleList); +struct EpochVerifier { + epoch_number: U256, + list: SimpleList, +} -impl super::ChainVerifier for ChainVerifier { +impl super::EpochVerifier for EpochVerifier { + fn epoch_number(&self) -> U256 { self.epoch_number.clone() } fn verify_light(&self, header: &Header) -> Result<(), Error> { - verify_external(header, &self.0) + verify_external(header, &self.list) } } @@ -181,22 +185,25 @@ impl Engine for BasicAuthority { } // the proofs we need just allow us to get the full validator set. - fn prove_with_caller(&self, header: &Header, caller: &Call) -> Result { - self.validators.generate_proof(header, caller) + fn epoch_proof(&self, header: &Header, caller: &Call) -> Result { + self.validators.epoch_proof(header, caller) .map_err(|e| EngineError::InsufficientProof(e).into()) } - fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> RequiresProof + fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> EpochChange { - self.validators.proof_required(header, block, receipts) + self.validators.is_epoch_end(header, block, receipts) } - fn chain_verifier(&self, header: &Header, proof: Bytes) -> Result, Error> { + fn epoch_verifier(&self, header: &Header, proof: &[u8]) -> Result, Error> { // extract a simple list from the proof. - let simple_list = self.validators.chain_verifier(header, proof)?; + let (num, simple_list) = self.validators.epoch_set(header, proof)?; - Ok(Box::new(ChainVerifier(simple_list))) + Ok(Box::new(EpochVerifier { + epoch_number: num, + list: simple_list, + })) } fn register_client(&self, client: Weak) { diff --git a/ethcore/src/engines/chain_verifier.rs b/ethcore/src/engines/epoch_verifier.rs similarity index 75% rename from ethcore/src/engines/chain_verifier.rs rename to ethcore/src/engines/epoch_verifier.rs index 399832fe9..fd0847e76 100644 --- a/ethcore/src/engines/chain_verifier.rs +++ b/ethcore/src/engines/epoch_verifier.rs @@ -14,18 +14,21 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -// Chain verifiers. +// Epoch verifiers. use error::Error; use header::Header; +use util::U256; -/// Sequential chain verifier. +/// Verifier for all blocks within an epoch without accessing /// /// See docs on `Engine` relating to proving functions for more details. -/// Headers here will have already passed "basic" verification. -pub trait ChainVerifier { +pub trait EpochVerifier: Sync { + /// Get the epoch number. + fn epoch_number(&self) -> U256; + /// Lightly verify the next block header. - /// This may not be a header that requires a proof. + /// This may not be a header belonging to a different epoch. fn verify_light(&self, header: &Header) -> Result<(), Error>; /// Perform potentially heavier checks on the next block header. @@ -34,9 +37,10 @@ pub trait ChainVerifier { } } -/// No-op chain verifier. +/// Special "no-op" verifier for stateless, epoch-less engines. pub struct NoOp; -impl ChainVerifier for NoOp { +impl EpochVerifier for NoOp { + fn epoch_number(&self) -> U256 { 0.into() } fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 7d4906dd2..b67afdf2f 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -18,7 +18,7 @@ mod authority_round; mod basic_authority; -mod chain_verifier; +mod epoch_verifier; mod instant_seal; mod null_engine; mod signer; @@ -29,7 +29,7 @@ mod vote_collector; pub use self::authority_round::AuthorityRound; pub use self::basic_authority::BasicAuthority; -pub use self::chain_verifier::ChainVerifier; +pub use self::epoch_verifier::EpochVerifier; pub use self::instant_seal::InstantSeal; pub use self::null_engine::NullEngine; pub use self::tendermint::Tendermint; @@ -98,18 +98,19 @@ pub enum Seal { /// Type alias for a function we can make calls through synchronously. pub type Call = Fn(Address, Bytes) -> Result; -/// Results of a query of whether a validation proof is necessary at a block. -pub enum RequiresProof { +/// Results of a query of whether an epoch change occurred at the given block. +#[derive(Debug, Clone, PartialEq)] +pub enum EpochChange { /// Cannot determine until more data is passed. Unsure(Unsure), - /// Validation proof not required. + /// No epoch change. No, /// Validation proof required, and the expected output - /// if it can be recovered. - Yes(Option), + Yes(Bytes), } -/// More data required to determine if a validation proof is required. +/// 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, @@ -219,37 +220,38 @@ pub trait Engine : Sync + Send { self.verify_block_basic(header, None).and_then(|_| self.verify_block_unordered(header, None)) } - /// Generate validation proof. + /// Generate epoch change proof. /// - /// This will be used to generate proofs of validation as well as verify them. + /// This will be used to generate proofs of epoch change as well as verify them. /// Must be called on blocks that have already passed basic verification. /// - /// Return the "validation proof" generated. - /// This must be usable to generate a `ChainVerifier` for verifying all blocks + /// Return the "epoch proof" generated. + /// This must be usable to generate a `EpochVerifier` for verifying all blocks /// from the supplied header up to the next one where proof is required. /// /// For example, for PoA chains the proof will be a validator set, - /// and the corresponding `ChainVerifier` can be used to correctly validate + /// and the corresponding `EpochVerifier` can be used to correctly validate /// all blocks produced under that `ValidatorSet` - fn prove_with_caller(&self, _header: &Header, _caller: &Call) + fn epoch_proof(&self, _header: &Header, _caller: &Call) -> Result, Error> { Ok(Vec::new()) } - /// Whether a proof is required for the given header. - fn proof_required(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>) - -> RequiresProof + /// Whether an epoch change occurred at the given header. + /// Should not interact with state. + fn is_epoch_end(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>) + -> EpochChange { - RequiresProof::No + EpochChange::No } - /// Create a subchain verifier from validation proof. + /// Create an epoch verifier from validation proof. /// - /// The proof should be one generated by `prove_with_caller`. - /// See docs of `prove_with_caller` for description. - fn chain_verifier(&self, _header: &Header, _proof: Bytes) -> Result, Error> { - Ok(Box::new(self::chain_verifier::NoOp)) + /// The proof should be one generated by `epoch_proof`. + /// See docs of `epoch_proof` for description. + fn epoch_verifier(&self, _header: &Header, _proof: &[u8]) -> Result, Error> { + Ok(Box::new(self::epoch_verifier::NoOp)) } /// Populate a header's fields based on its parent's header. diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index db6e8ed2c..0fdef84a3 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -66,18 +66,18 @@ impl ValidatorSet for ValidatorContract { self.validators.default_caller(id) } - fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> ::engines::RequiresProof + fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> ::engines::EpochChange { - self.validators.proof_required(header, block, receipts) + self.validators.is_epoch_end(header, block, receipts) } - fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String> { - self.validators.generate_proof(header, caller) + fn epoch_proof(&self, header: &Header, caller: &Call) -> Result, String> { + self.validators.epoch_proof(header, caller) } - fn chain_verifier(&self, header: &Header, proof: Vec) -> Result { - self.validators.chain_verifier(header, proof) + fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(U256, super::SimpleList), ::error::Error> { + self.validators.epoch_set(header, proof) } fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index 48269e20f..e9dc333c5 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -23,7 +23,7 @@ mod multi; use std::sync::Weak; use ids::BlockId; -use util::{Address, H256}; +use util::{Address, H256, U256}; use ethjson::spec::ValidatorSet as ValidatorSpec; use client::Client; use header::Header; @@ -72,26 +72,32 @@ pub trait ValidatorSet: Send + Sync { self.count_with_caller(parent, &*default) } - /// Whether a validation proof is required at the given block. + /// Whether this block is the last one in its epoch. /// Usually indicates that the validator set changed at the given block. /// /// Should not inspect state! This is used in situations where /// state is not generally available. /// - /// Return `Ok` with a flag indicating whether it changed at the given header, + /// Return `Yes` or `No` indicating whether it changed at the given header, /// or `Unsure` indicating a need for more information. /// - /// This may or may not be called in a loop. - fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> super::RequiresProof; + /// If block or receipts are provided, do not return `Unsure` indicating + /// need for them. + fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> super::EpochChange; - /// Generate a validation proof at the given header. + /// Generate epoch proof. /// Must interact with state only through the given caller! /// Otherwise, generated proofs may be wrong. - fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String>; + fn epoch_proof(&self, header: &Header, caller: &Call) -> Result, String>; - /// Create a fully self-contained validator set from the given proof. - fn chain_verifier(&self, header: &Header, proof: Vec) -> Result; + /// Recover the validator set for all + /// + /// May fail if the given header doesn't kick off an epoch or + /// the proof is invalid. + /// + /// Returns the epoch number and proof. + fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(U256, SimpleList), ::error::Error>; /// Checks if a given address is a validator, with the given function /// for executing synchronous calls to contracts. diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index b7a13c5e3..eecf88432 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -18,8 +18,8 @@ use std::collections::BTreeMap; use std::sync::Weak; -use engines::{Call, RequiresProof}; -use util::{H256, Address, RwLock}; +use engines::{Call, EpochChange}; +use util::{H256, Address, RwLock, U256}; use ids::BlockId; use header::{BlockNumber, Header}; use client::{Client, BlockChainClient}; @@ -43,9 +43,7 @@ impl Multi { fn correct_set(&self, id: BlockId) -> Option<&ValidatorSet> { match self.block_number.read()(id).map(|parent_block| self.correct_set_by_number(parent_block)) { - Ok(set) => { - Some(set) - }, + Ok((_, set)) => Some(set), Err(e) => { debug!(target: "engine", "ValidatorSet could not be recovered: {}", e); None @@ -53,7 +51,9 @@ impl Multi { } } - fn correct_set_by_number(&self, parent_block: BlockNumber) -> &ValidatorSet { + // get correct set by block number, along with block number at which + // this set was activated. + fn correct_set_by_number(&self, parent_block: BlockNumber) -> (BlockNumber, &ValidatorSet) { let (block, set) = self.sets.iter() .rev() .find(|&(block, _)| *block <= parent_block + 1) @@ -62,7 +62,7 @@ impl Multi { qed"); trace!(target: "engine", "Multi ValidatorSet retrieved for block {}.", block); - &**set + (*block, &**set) } } @@ -72,18 +72,22 @@ impl ValidatorSet for Multi { .unwrap_or(Box::new(|_, _| Err("No validator set for given ID.".into()))) } - fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> RequiresProof + fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> EpochChange { - self.correct_set_by_number(header.number()).proof_required(header, block, receipts) + self.correct_set_by_number(header.number()).1.is_epoch_end(header, block, receipts) } - fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String> { - self.correct_set_by_number(header.number()).generate_proof(header, caller) + fn epoch_proof(&self, header: &Header, caller: &Call) -> Result, String> { + self.correct_set_by_number(header.number()).1.epoch_proof(header, caller) } - fn chain_verifier(&self, header: &Header, proof: Vec) -> Result { - self.correct_set_by_number(header.number()).chain_verifier(header, proof) + fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(U256, super::SimpleList), ::error::Error> { + // "multi" epoch is the inner set's epoch plus the transition block to that set. + // ensures epoch increases monotonically. + let (set_block, set) = self.correct_set_by_number(header.number()); + let (inner_epoch, list) = set.epoch_set(header, proof)?; + Ok((U256::from(set_block) + inner_epoch, list)) } fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 2af389484..3284ec5ab 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -126,16 +126,16 @@ impl ValidatorSet for ValidatorSafeContract { .and_then(|c| c.call_contract(id, addr, data))) } - fn proof_required(&self, header: &Header, _block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> ::engines::RequiresProof + fn is_epoch_end(&self, header: &Header, _block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> ::engines::EpochChange { let bloom = self.expected_bloom(header); let header_bloom = header.log_bloom(); - if &bloom & header_bloom != bloom { return ::engines::RequiresProof::No } + if &bloom & header_bloom != bloom { return ::engines::EpochChange::No } match receipts { - None => ::engines::RequiresProof::Unsure(::engines::Unsure::NeedsReceipts), + None => ::engines::EpochChange::Unsure(::engines::Unsure::NeedsReceipts), Some(receipts) => { let check_log = |log: &LogEntry| { log.address == self.address && @@ -149,7 +149,11 @@ impl ValidatorSet for ValidatorSafeContract { .event("ValidatorsChanged".into()) .expect("Contract known ahead of time to have `ValidatorsChanged` event; qed"); + // iterate in reverse because only the _last_ change in a given + // block actually has any effect. + // the contract should only increment the nonce once. let mut decoded_events = receipts.iter() + .rev() .filter(|r| &bloom & &r.log_bloom == bloom) .flat_map(|r| r.logs.iter()) .filter(move |l| check_log(l)) @@ -164,7 +168,7 @@ impl ValidatorSet for ValidatorSafeContract { // TODO: are multiple transitions per block possible? match decoded_events.next() { - None => ::engines::RequiresProof::No, + None => ::engines::EpochChange::No, Some(matched_event) => { // decode log manually until the native contract generator is // good enough to do it for us. @@ -181,10 +185,10 @@ impl ValidatorSet for ValidatorSafeContract { match (nonce, validators) { (Some(nonce), Some(validators)) => - ::engines::RequiresProof::Yes(Some(encode_proof(nonce, &validators))), + ::engines::EpochChange::Yes(encode_proof(nonce, &validators)), _ => { debug!(target: "engine", "Successfully decoded log turned out to be bad."); - ::engines::RequiresProof::No + ::engines::EpochChange::No } } } @@ -195,20 +199,21 @@ impl ValidatorSet for ValidatorSafeContract { // the proof we generate is an RLP list containing two parts. // (nonce, validators) - fn generate_proof(&self, _header: &Header, caller: &Call) -> Result, String> { + fn epoch_proof(&self, _header: &Header, caller: &Call) -> Result, String> { match (self.get_nonce(caller), self.get_list(caller)) { (Some(nonce), Some(list)) => Ok(encode_proof(nonce, &list.into_inner())), _ => Err("Caller insufficient to generate validator proof.".into()), } } - fn chain_verifier(&self, _header: &Header, proof: Vec) -> Result { + fn epoch_set(&self, _header: &Header, proof: &[u8]) -> Result<(U256, SimpleList), ::error::Error> { use rlp::UntrustedRlp; - let rlp = UntrustedRlp::new(&proof); + let rlp = UntrustedRlp::new(proof); + let nonce: U256 = rlp.val_at(0)?; let validators: Vec
= rlp.list_at(1)?; - Ok(SimpleList::new(validators)) + Ok((nonce, SimpleList::new(validators))) } fn contains_with_caller(&self, block_hash: &H256, address: &Address, caller: &Call) -> bool { diff --git a/ethcore/src/engines/validator_set/simple_list.rs b/ethcore/src/engines/validator_set/simple_list.rs index 339585d7d..59209fac0 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/src/engines/validator_set/simple_list.rs @@ -16,7 +16,7 @@ /// Preconfigured validator list. -use util::{H256, Address, HeapSizeOf}; +use util::{H256, Address, HeapSizeOf, U256}; use engines::Call; use header::Header; @@ -53,18 +53,18 @@ impl ValidatorSet for SimpleList { Box::new(|_, _| Err("Simple list doesn't require calls.".into())) } - fn proof_required(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[::receipt::Receipt]>) - -> ::engines::RequiresProof + fn is_epoch_end(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[::receipt::Receipt]>) + -> ::engines::EpochChange { - ::engines::RequiresProof::No + ::engines::EpochChange::No } - fn generate_proof(&self, _header: &Header, _caller: &Call) -> Result, String> { + fn epoch_proof(&self, _header: &Header, _caller: &Call) -> Result, String> { Ok(Vec::new()) } - fn chain_verifier(&self, _header: &Header, _: Vec) -> Result { - Ok(self.clone()) + fn epoch_set(&self, _header: &Header, _: &[u8]) -> Result<(U256, SimpleList), ::error::Error> { + Ok((0.into(), self.clone())) } fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool { diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 585fd29aa..867d4f775 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -149,7 +149,16 @@ impl Ethash { } } -impl ::engines::ChainVerifier for Arc { +// TODO [rphmeier] +// +// for now, this is different than Ethash's own epochs, and signal +// "consensus epochs". +// in this sense, `Ethash` is epochless: the same `EpochVerifier` can be used +// for any block in the chain. +// in the future, we might move the Ethash epoch +// caching onto this mechanism as well. +impl ::engines::EpochVerifier for Arc { + fn epoch_number(&self) -> U256 { 0.into() } fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } fn verify_heavy(&self, header: &Header) -> Result<(), Error> { self.verify_block_unordered(header, None) @@ -393,7 +402,7 @@ impl Engine for Arc { Ok(()) } - fn chain_verifier(&self, _header: &Header, _proof: Vec) -> Result, Error> { + fn epoch_verifier(&self, _header: &Header, _proof: &[u8]) -> Result, Error> { Ok(Box::new(self.clone())) } }