diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests index 9028c4801..d52059307 160000 --- a/ethcore/res/ethereum/tests +++ b/ethcore/res/ethereum/tests @@ -1 +1 @@ -Subproject commit 9028c4801fd39fbb71a9796979182549a24e81c8 +Subproject commit d520593078fa0849dcd1f907e44ed0a616892e33 diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 4bd29d100..13cb95afe 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -569,11 +569,33 @@ impl Client { //let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new)); let mut batch = DBTransaction::new(); + + // 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(_) => { + warn!(target: "client", "Detected invalid engine implementation."); + warn!(target: "client", "Engine claims to require more block data, but everything provided."); + false + } + } + }; + // CHECK! I *think* this is fine, even if the state_root is equal to another // already-imported block of the same number. // TODO: Prove it with a test. let mut state = block.drain(); + if generate_proof { + debug!(target: "client", "Generating validation proof for block {}", hash); + + // TODO + } + state.journal_under(&mut batch, number, hash).expect("DB commit failed"); let route = chain.insert_block(&mut batch, block_data, receipts); self.tracedb.read().import(&mut batch, TraceImportRequest { diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index e57fb27bc..8e8450768 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -37,7 +37,7 @@ use transaction::UnverifiedTransaction; use client::{Client, EngineClient}; use state::CleanupMode; use super::signer::EngineSigner; -use super::validator_set::{ValidatorSet, new_validator_set}; +use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; /// `AuthorityRound` params. #[derive(Debug, PartialEq)] @@ -75,27 +75,76 @@ impl From for AuthorityRoundParams { } } -/// Engine using `AuthorityRound` proof-of-work consensus algorithm, suitable for Ethereum -/// mainnet chains in the Olympic, Frontier and Homestead eras. +// Helper for managing the step. +#[derive(Debug)] +struct Step { + calibrate: bool, // whether calibration is enabled. + inner: AtomicUsize, + duration: Duration, +} + +impl Step { + fn load(&self) -> usize { self.inner.load(AtomicOrdering::SeqCst) } + fn duration_remaining(&self) -> Duration { + let now = unix_now(); + let step_end = self.duration * (self.load() as u32 + 1); + if step_end > now { + step_end - now + } else { + Duration::from_secs(0) + } + } + fn increment(&self) { + self.inner.fetch_add(1, AtomicOrdering::SeqCst); + } + fn calibrate(&self) { + if self.calibrate { + let new_step = unix_now().as_secs() / self.duration.as_secs(); + self.inner.store(new_step as usize, AtomicOrdering::SeqCst); + } + } + fn is_future(&self, given: usize) -> bool { + if given > self.load() + 1 { + // Make absolutely sure that the given step is correct. + self.calibrate(); + given > self.load() + 1 + } else { + false + } + } +} + +/// Engine using `AuthorityRound` proof-of-authority BFT consensus. pub struct AuthorityRound { params: CommonParams, gas_limit_bound_divisor: U256, block_reward: U256, registrar: Address, - step_duration: Duration, builtins: BTreeMap, transition_service: IoService<()>, - step: AtomicUsize, + step: Arc, proposed: AtomicBool, client: RwLock>>, signer: EngineSigner, validators: Box, - /// Is this Engine just for testing (prevents step calibration). - calibrate_step: bool, validate_score_transition: u64, eip155_transition: u64, } +// header-chain validator. +struct ChainVerifier { + step: Arc, + subchain_validators: SimpleList, +} + +impl super::ChainVerifier for ChainVerifier { + fn verify_light(&self, header: &Header) -> Result<(), Error> { + // always check the seal since it's fast. + // nothing heavier to do. + verify_external(header, &self.subchain_validators, &*self.step) + } +} + fn header_step(header: &Header) -> Result { UntrustedRlp::new(&header.seal().get(0).expect("was either checked with verify_block_basic or is genesis; has 2 fields; qed (Make sure the spec file has a correct genesis seal)")).as_val() } @@ -104,6 +153,26 @@ fn header_signature(header: &Header) -> Result { UntrustedRlp::new(&header.seal().get(1).expect("was checked with verify_block_basic; has 2 fields; qed")).as_val::().map(Into::into) } +fn verify_external(header: &Header, validators: &ValidatorSet, step: &Step) -> Result<(), Error> { + let header_step = header_step(header)?; + + // Give one step slack if step is lagging, double vote is still not possible. + if step.is_future(header_step) { + trace!(target: "engine", "verify_block_unordered: block from the future"); + validators.report_benign(header.author()); + Err(BlockError::InvalidSeal)? + } else { + let proposer_signature = header_signature(header)?; + let correct_proposer = validators.get(header.parent_hash(), header_step); + if !verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())? { + trace!(target: "engine", "verify_block_unordered: bad proposer for step: {}", header_step); + Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))? + } else { + Ok(()) + } + } +} + trait AsMillis { fn as_millis(&self) -> u64; } @@ -125,15 +194,17 @@ impl AuthorityRound { gas_limit_bound_divisor: our_params.gas_limit_bound_divisor, block_reward: our_params.block_reward, registrar: our_params.registrar, - step_duration: our_params.step_duration, builtins: builtins, transition_service: IoService::<()>::start()?, - step: AtomicUsize::new(initial_step), + step: Arc::new(Step { + inner: AtomicUsize::new(initial_step), + calibrate: our_params.start_step.is_none(), + duration: our_params.step_duration, + }), proposed: AtomicBool::new(false), client: RwLock::new(None), signer: Default::default(), validators: new_validator_set(our_params.validators), - calibrate_step: our_params.start_step.is_none(), validate_score_transition: our_params.validate_score_transition, eip155_transition: our_params.eip155_transition, }); @@ -145,22 +216,6 @@ impl AuthorityRound { Ok(engine) } - fn calibrate_step(&self) { - if self.calibrate_step { - self.step.store((unix_now().as_secs() / self.step_duration.as_secs()) as usize, AtomicOrdering::SeqCst); - } - } - - fn remaining_step_duration(&self) -> Duration { - let now = unix_now(); - let step_end = self.step_duration * (self.step.load(AtomicOrdering::SeqCst) as u32 + 1); - if step_end > now { - step_end - now - } else { - Duration::from_secs(0) - } - } - fn step_proposer(&self, bh: &H256, step: usize) -> Address { self.validators.get(bh, step) } @@ -168,16 +223,6 @@ impl AuthorityRound { fn is_step_proposer(&self, bh: &H256, step: usize, address: &Address) -> bool { self.step_proposer(bh, step) == *address } - - fn is_future_step(&self, step: usize) -> bool { - if step > self.step.load(AtomicOrdering::SeqCst) + 1 { - // Make absolutely sure that the step is correct. - self.calibrate_step(); - step > self.step.load(AtomicOrdering::SeqCst) + 1 - } else { - false - } - } } fn unix_now() -> Duration { @@ -193,7 +238,8 @@ const ENGINE_TIMEOUT_TOKEN: TimerToken = 23; impl IoHandler<()> for TransitionHandler { fn initialize(&self, io: &IoContext<()>) { if let Some(engine) = self.engine.upgrade() { - io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.remaining_step_duration().as_millis()) + let remaining = engine.step.duration_remaining(); + io.register_timer_once(ENGINE_TIMEOUT_TOKEN, remaining.as_millis()) .unwrap_or_else(|e| warn!(target: "engine", "Failed to start consensus step timer: {}.", e)) } } @@ -202,7 +248,8 @@ impl IoHandler<()> for TransitionHandler { if timer == ENGINE_TIMEOUT_TOKEN { if let Some(engine) = self.engine.upgrade() { engine.step(); - io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.remaining_step_duration().as_millis()) + let remaining = engine.step.duration_remaining(); + io.register_timer_once(ENGINE_TIMEOUT_TOKEN, remaining.as_millis()) .unwrap_or_else(|e| warn!(target: "engine", "Failed to restart consensus step timer: {}.", e)) } } @@ -224,7 +271,7 @@ impl Engine for AuthorityRound { fn builtins(&self) -> &BTreeMap { &self.builtins } fn step(&self) { - self.step.fetch_add(1, AtomicOrdering::SeqCst); + self.step.increment(); self.proposed.store(false, AtomicOrdering::SeqCst); if let Some(ref weak) = *self.client.read() { if let Some(c) = weak.upgrade() { @@ -247,7 +294,7 @@ impl Engine for AuthorityRound { fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { // 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(AtomicOrdering::SeqCst).into(); + 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(); @@ -271,7 +318,7 @@ impl Engine for AuthorityRound { fn generate_seal(&self, block: &ExecutedBlock) -> Seal { if self.proposed.load(AtomicOrdering::SeqCst) { return Seal::None; } let header = block.header(); - let step = self.step.load(AtomicOrdering::SeqCst); + let step = self.step.load(); if self.is_step_proposer(header.parent_hash(), step, header.author()) { if let Ok(signature) = self.signer.sign(header.bare_hash()) { trace!(target: "engine", "generate_seal: Issuing a block for step {}.", step); @@ -319,32 +366,19 @@ impl Engine for AuthorityRound { Ok(()) } - /// Do the validator and gas limit validation. + /// Do the step and gas limit validation. fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { let step = header_step(header)?; - // Give one step slack if step is lagging, double vote is still not possible. - if self.is_future_step(step) { - trace!(target: "engine", "verify_block_unordered: block from the future"); - self.validators.report_benign(header.author()); - Err(BlockError::InvalidSeal)? - } else { - let proposer_signature = header_signature(header)?; - let correct_proposer = self.step_proposer(header.parent_hash(), step); - if !verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())? { - trace!(target: "engine", "verify_block_unordered: bad proposer for step: {}", step); - Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))? - } - } // 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 if parent is from a previous step. + // Ensure header is from the step after parent. let parent_step = header_step(parent)?; - if step == parent_step { - trace!(target: "engine", "Multiple blocks proposed for step {}.", step); + if step <= parent_step { + trace!(target: "engine", "Multiple blocks proposed for step {}.", parent_step); self.validators.report_malicious(header.author()); Err(EngineError::DoubleVote(header.author().clone()))?; } @@ -358,6 +392,11 @@ impl Engine for AuthorityRound { Ok(()) } + // Check the validators. + fn verify_block_external(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + verify_external(header, &*self.validators, &*self.step) + } + // 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) @@ -370,6 +409,16 @@ impl Engine for AuthorityRound { self.validators.proof_required(header, block, receipts) } + fn chain_verifier(&self, header: &Header, proof: Bytes) -> Result, Error> { + // extract a simple list from the proof. + let simple_list = self.validators.chain_verifier(header, proof)?; + + Ok(Box::new(ChainVerifier { + step: self.step.clone(), + subchain_validators: simple_list, + })) + } + fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> result::Result<(), Error> { t.check_low_s()?; @@ -453,7 +502,7 @@ mod tests { let mut header: Header = Header::default(); header.set_seal(vec![encode(&H520::default()).to_vec()]); - let verify_result = engine.verify_block_family(&header, &Default::default(), None); + let verify_result = engine.verify_block_external(&header, None); assert!(verify_result.is_err()); } @@ -507,9 +556,11 @@ mod tests { // Two validators. // Spec starts with step 2. header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); - assert!(engine.verify_block_family(&header, &parent_header, None).is_err()); + assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(engine.verify_block_external(&header, None).is_err()); header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(engine.verify_block_external(&header, None).is_ok()); } #[test] @@ -532,7 +583,33 @@ mod tests { // Spec starts with step 2. header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(engine.verify_block_external(&header, None).is_ok()); header.set_seal(vec![encode(&5usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); + assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(engine.verify_block_external(&header, None).is_err()); + } + + #[test] + fn rejects_step_backwards() { + let tap = AccountProvider::transient_provider(); + let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap(); + + let mut parent_header: Header = Header::default(); + parent_header.set_seal(vec![encode(&4usize).to_vec()]); + parent_header.set_gas_limit(U256::from_str("222222").unwrap()); + let mut header: Header = Header::default(); + header.set_number(1); + header.set_gas_limit(U256::from_str("222222").unwrap()); + header.set_author(addr); + + let engine = Spec::new_test_round().engine; + + let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); + // Two validators. + // Spec starts with step 2. + header.set_seal(vec![encode(&5usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); + assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + header.set_seal(vec![encode(&3usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); assert!(engine.verify_block_family(&header, &parent_header, None).is_err()); } } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index a785b360a..25cdd765f 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -31,7 +31,7 @@ use ethjson; use header::Header; use client::Client; use super::signer::EngineSigner; -use super::validator_set::{ValidatorSet, new_validator_set}; +use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; /// `BasicAuthority` params. #[derive(Debug, PartialEq)] @@ -51,6 +51,27 @@ impl From for BasicAuthorityParams { } } +struct ChainVerifier(SimpleList); + +impl super::ChainVerifier for ChainVerifier { + fn verify_light(&self, header: &Header) -> Result<(), Error> { + verify_external(header, &self.0) + } +} + +fn verify_external(header: &Header, validators: &ValidatorSet) -> Result<(), Error> { + use rlp::UntrustedRlp; + + // Check if the signature belongs to a validator, can depend on parent state. + let sig = UntrustedRlp::new(&header.seal()[0]).as_val::()?; + let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?); + + match validators.contains(header.parent_hash(), &signer) { + false => Err(BlockError::InvalidSeal.into()), + true => Ok(()) + } +} + /// Engine using `BasicAuthority`, trivial proof-of-authority consensus. pub struct BasicAuthority { params: CommonParams, @@ -137,15 +158,6 @@ impl Engine for BasicAuthority { } fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { - use rlp::UntrustedRlp; - - // Check if the signature belongs to a validator, can depend on parent state. - let sig = UntrustedRlp::new(&header.seal()[0]).as_val::()?; - let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?); - if !self.validators.contains(header.parent_hash(), &signer) { - return Err(BlockError::InvalidSeal)?; - } - // 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() }))); @@ -164,6 +176,10 @@ impl Engine for BasicAuthority { Ok(()) } + fn verify_block_external(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + verify_external(header, &*self.validators) + } + // 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) @@ -176,6 +192,13 @@ impl Engine for BasicAuthority { self.validators.proof_required(header, block, receipts) } + fn chain_verifier(&self, header: &Header, proof: Bytes) -> Result, Error> { + // extract a simple list from the proof. + let simple_list = self.validators.chain_verifier(header, proof)?; + + Ok(Box::new(ChainVerifier(simple_list))) + } + fn register_client(&self, client: Weak) { self.validators.register_contract(client); } diff --git a/ethcore/src/engines/chain_verifier.rs b/ethcore/src/engines/chain_verifier.rs new file mode 100644 index 000000000..399832fe9 --- /dev/null +++ b/ethcore/src/engines/chain_verifier.rs @@ -0,0 +1,42 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +// Chain verifiers. + +use error::Error; +use header::Header; + +/// Sequential chain verifier. +/// +/// See docs on `Engine` relating to proving functions for more details. +/// Headers here will have already passed "basic" verification. +pub trait ChainVerifier { + /// Lightly verify the next block header. + /// This may not be a header that requires a proof. + fn verify_light(&self, header: &Header) -> Result<(), Error>; + + /// Perform potentially heavier checks on the next block header. + fn verify_heavy(&self, header: &Header) -> Result<(), Error> { + self.verify_light(header) + } +} + +/// No-op chain verifier. +pub struct NoOp; + +impl ChainVerifier for NoOp { + fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } +} diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index f1e0a4d43..7d4906dd2 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -16,20 +16,22 @@ //! Consensus engine specification and basic implementations. -mod transition; -mod vote_collector; -mod null_engine; -mod instant_seal; -mod basic_authority; mod authority_round; -mod tendermint; -mod validator_set; +mod basic_authority; +mod chain_verifier; +mod instant_seal; +mod null_engine; mod signer; +mod tendermint; +mod transition; +mod validator_set; +mod vote_collector; -pub use self::null_engine::NullEngine; -pub use self::instant_seal::InstantSeal; -pub use self::basic_authority::BasicAuthority; pub use self::authority_round::AuthorityRound; +pub use self::basic_authority::BasicAuthority; +pub use self::chain_verifier::ChainVerifier; +pub use self::instant_seal::InstantSeal; +pub use self::null_engine::NullEngine; pub use self::tendermint::Tendermint; use std::sync::Weak; @@ -182,6 +184,9 @@ pub trait Engine : Sync + Send { /// 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 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. @@ -214,13 +219,21 @@ pub trait Engine : Sync + Send { self.verify_block_basic(header, None).and_then(|_| self.verify_block_unordered(header, None)) } - /// Re-do all verification for a header with the given contract-calling interface + /// Generate validation proof. /// /// This will be used to generate proofs of validation as well as verify them. /// Must be called on blocks that have already passed basic verification. /// /// Return the "validation proof" generated. - fn prove_with_caller(&self, _header: &Header, _caller: &Call) -> Result { + /// This must be usable to generate a `ChainVerifier` 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 + /// all blocks produced under that `ValidatorSet` + fn prove_with_caller(&self, _header: &Header, _caller: &Call) + -> Result, Error> + { Ok(Vec::new()) } @@ -231,6 +244,14 @@ pub trait Engine : Sync + Send { RequiresProof::No } + /// Create a subchain 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)) + } + /// 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. diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 5c4af4ba7..db6e8ed2c 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -76,6 +76,10 @@ impl ValidatorSet for ValidatorContract { self.validators.generate_proof(header, caller) } + fn chain_verifier(&self, header: &Header, proof: Vec) -> Result { + self.validators.chain_verifier(header, proof) + } + fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { self.validators.contains_with_caller(bh, address, caller) } @@ -153,7 +157,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_family(&header, &header, None).is_err()); + assert!(client.engine().verify_block_external(&header, None).is_err()); // Seal a block. client.engine().step(); assert_eq!(client.chain_info().best_block_number, 1); diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index d5abf430d..48269e20f 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -28,7 +28,7 @@ use ethjson::spec::ValidatorSet as ValidatorSpec; use client::Client; use header::Header; -use self::simple_list::SimpleList; +pub use self::simple_list::SimpleList; use self::contract::ValidatorContract; use self::safe_contract::ValidatorSafeContract; use self::multi::Multi; @@ -90,6 +90,9 @@ pub trait ValidatorSet: Send + Sync { /// Otherwise, generated proofs may be wrong. fn generate_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; + /// Checks if a given address is a validator, with the given function /// for executing synchronous calls to contracts. fn contains_with_caller(&self, parent_block_hash: &H256, address: &Address, caller: &Call) -> bool; diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index 78270c4af..b7a13c5e3 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -82,6 +82,10 @@ impl ValidatorSet for Multi { self.correct_set_by_number(header.number()).generate_proof(header, caller) } + fn chain_verifier(&self, header: &Header, proof: Vec) -> Result { + self.correct_set_by_number(header.number()).chain_verifier(header, proof) + } + fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { self.correct_set(BlockId::Hash(*bh)) .map_or(false, |set| set.contains_with_caller(bh, address, caller)) diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index de7ecb3a2..2af389484 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -202,6 +202,15 @@ impl ValidatorSet for ValidatorSafeContract { } } + fn chain_verifier(&self, _header: &Header, proof: Vec) -> Result { + use rlp::UntrustedRlp; + + let rlp = UntrustedRlp::new(&proof); + let validators: Vec
= rlp.list_at(1)?; + + Ok(SimpleList::new(validators)) + } + fn contains_with_caller(&self, block_hash: &H256, address: &Address, caller: &Call) -> bool { let mut guard = self.validators.write(); let maybe_existing = guard diff --git a/ethcore/src/engines/validator_set/simple_list.rs b/ethcore/src/engines/validator_set/simple_list.rs index e513212a6..339585d7d 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/src/engines/validator_set/simple_list.rs @@ -22,7 +22,8 @@ use engines::Call; use header::Header; use super::ValidatorSet; -#[derive(Debug, PartialEq, Eq, Default)] +/// Validator set containing a known set of addresses. +#[derive(Clone, Debug, PartialEq, Eq, Default)] pub struct SimpleList { validators: Vec
, } @@ -62,6 +63,10 @@ impl ValidatorSet for SimpleList { Ok(Vec::new()) } + fn chain_verifier(&self, _header: &Header, _: Vec) -> Result { + Ok(self.clone()) + } + fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool { self.validators.contains(address) } diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 5bc31c5d9..585fd29aa 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -139,17 +139,24 @@ pub struct Ethash { impl Ethash { /// Create a new instance of Ethash engine - pub fn new(params: CommonParams, ethash_params: EthashParams, builtins: BTreeMap) -> Self { - Ethash { + pub fn new(params: CommonParams, ethash_params: EthashParams, builtins: BTreeMap) -> Arc { + Arc::new(Ethash { params: params, ethash_params: ethash_params, builtins: builtins, pow: EthashManager::new(), - } + }) } } -impl Engine for Ethash { +impl ::engines::ChainVerifier for Arc { + fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } + fn verify_heavy(&self, header: &Header) -> Result<(), Error> { + self.verify_block_unordered(header, None) + } +} + +impl Engine for Arc { fn name(&self) -> &str { "Ethash" } fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) } // Two fields - mix @@ -385,6 +392,10 @@ impl Engine for Ethash { Ok(()) } + + fn chain_verifier(&self, _header: &Header, _proof: Vec) -> Result, Error> { + Ok(Box::new(self.clone())) + } } // Try to round gas_limit a bit so that: