From 34a1512ff07b74993ec45206e4a5af9612425fc0 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 12 Apr 2017 18:55:38 +0200 Subject: [PATCH] skeleton for proof checking --- ethcore/src/engines/authority_round.rs | 14 +++++- ethcore/src/engines/basic_authority.rs | 18 ++++++-- ethcore/src/engines/mod.rs | 17 ++++--- ethcore/src/engines/validator_set/contract.rs | 11 +++++ ethcore/src/engines/validator_set/mod.rs | 23 +++++++++- ethcore/src/engines/validator_set/multi.rs | 44 ++++++++++++------- .../engines/validator_set/safe_contract.rs | 14 ++++++ .../src/engines/validator_set/simple_list.rs | 17 +++++++ 8 files changed, 131 insertions(+), 27 deletions(-) diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index e6cbdb531..e57fb27bc 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -25,7 +25,7 @@ use rlp::{UntrustedRlp, encode}; use account_provider::AccountProvider; use block::*; use spec::CommonParams; -use engines::{Engine, Seal, EngineError}; +use engines::{Call, Engine, Seal, EngineError}; use header::Header; use error::{Error, TransactionError, BlockError}; use evm::Schedule; @@ -358,6 +358,18 @@ impl Engine for AuthorityRound { Ok(()) } + // 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) + .map_err(|e| EngineError::InsufficientProof(e).into()) + } + + fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> super::RequiresProof + { + self.validators.proof_required(header, block, receipts) + } + fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> result::Result<(), Error> { t.check_low_s()?; diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index e5a53d4e9..a785b360a 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, Seal}; +use engines::{Engine, EngineError, Seal, Call, RequiresProof}; use env_info::EnvInfo; use error::{BlockError, Error}; use evm::Schedule; @@ -51,8 +51,7 @@ impl From for BasicAuthorityParams { } } -/// Engine using `BasicAuthority` proof-of-work consensus algorithm, suitable for Ethereum -/// mainnet chains in the Olympic, Frontier and Homestead eras. +/// Engine using `BasicAuthority`, trivial proof-of-authority consensus. pub struct BasicAuthority { params: CommonParams, gas_limit_bound_divisor: U256, @@ -139,6 +138,7 @@ 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())?); @@ -164,6 +164,18 @@ impl Engine for BasicAuthority { Ok(()) } + // 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) + .map_err(|e| EngineError::InsufficientProof(e).into()) + } + + fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> RequiresProof + { + self.validators.proof_required(header, block, receipts) + } + fn register_client(&self, client: Weak) { self.validators.register_contract(client); } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index e4bc514b7..f1e0a4d43 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -62,6 +62,8 @@ pub enum EngineError { UnexpectedMessage, /// Seal field has an unexpected size. BadSealFieldSize(OutOfBounds), + /// Validation proof insufficient. + InsufficientProof(String), } impl fmt::Display for EngineError { @@ -73,6 +75,7 @@ impl fmt::Display for EngineError { NotAuthorized(ref address) => format!("Signer {} is not authorized.", address), UnexpectedMessage => "This Engine should not be fed messages.".into(), BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob), + InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", msg), }; f.write_fmt(format_args!("Engine error ({})", msg)) @@ -99,8 +102,9 @@ pub enum RequiresProof { Unsure(Unsure), /// Validation proof not required. No, - /// Validation proof required. - Yes, + /// Validation proof required, and the expected output + /// if it can be recovered. + Yes(Option), } /// More data required to determine if a validation proof is required. @@ -213,12 +217,15 @@ pub trait Engine : Sync + Send { /// Re-do all verification for a header with the given contract-calling interface /// /// This will be used to generate proofs of validation as well as verify them. - fn verify_with_state(&self, _header: &Header, _call: &Call) -> Result<(), Error> { - Ok(()) + /// 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 { + Ok(Vec::new()) } /// Whether a proof is required for the given header. - fn proof_required(&self, _header: Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>) + fn proof_required(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>) -> RequiresProof { RequiresProof::No diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 12d816246..5c4af4ba7 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -25,6 +25,7 @@ use native_contracts::ValidatorReport as Provider; use client::{Client, BlockChainClient}; use engines::Call; +use header::Header; use super::ValidatorSet; use super::safe_contract::ValidatorSafeContract; @@ -65,6 +66,16 @@ impl ValidatorSet for ValidatorContract { self.validators.default_caller(id) } + fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> ::engines::RequiresProof + { + self.validators.proof_required(header, block, receipts) + } + + fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String> { + self.validators.generate_proof(header, caller) + } + fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { self.validators.contains_with_caller(bh, address, caller) } diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index 707b5adf5..d5abf430d 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -26,6 +26,7 @@ use ids::BlockId; use util::{Address, H256}; use ethjson::spec::ValidatorSet as ValidatorSpec; use client::Client; +use header::Header; use self::simple_list::SimpleList; use self::contract::ValidatorContract; @@ -47,9 +48,10 @@ pub fn new_validator_set(spec: ValidatorSpec) -> Box { } /// A validator set. -// TODO [keorn]: remove internal callers. pub trait ValidatorSet: Send + Sync { /// Get the default "Call" helper, for use in general operation. + // TODO [keorn]: this is a hack intended to migrate off of + // a strict dependency on state always being available. fn default_caller(&self, block_id: BlockId) -> Box; /// Checks if a given address is a validator, @@ -63,12 +65,31 @@ pub trait ValidatorSet: Send + Sync { let default = self.default_caller(BlockId::Hash(*parent)); self.get_with_caller(parent, nonce, &*default) } + /// Returns the current number of validators. fn count(&self, parent: &H256) -> usize { let default = self.default_caller(BlockId::Hash(*parent)); self.count_with_caller(parent, &*default) } + /// Whether a validation proof is required at the given block. + /// 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, + /// 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; + + /// Generate a validation proof at the given header. + /// 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>; + /// 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 f098ca343..78270c4af 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -18,10 +18,10 @@ use std::collections::BTreeMap; use std::sync::Weak; -use engines::Call; +use engines::{Call, RequiresProof}; use util::{H256, Address, RwLock}; use ids::BlockId; -use header::BlockNumber; +use header::{BlockNumber, Header}; use client::{Client, BlockChainClient}; use super::ValidatorSet; @@ -42,21 +42,9 @@ impl Multi { } fn correct_set(&self, id: BlockId) -> Option<&ValidatorSet> { - match self - .block_number - .read()(id) - .map(|parent_block| self - .sets - .iter() - .rev() - .find(|&(block, _)| *block <= parent_block + 1) - .expect("constructor validation ensures that there is at least one validator set for block 0; - block 0 is less than any uint; - qed") - ) { - Ok((block, set)) => { - trace!(target: "engine", "Multi ValidatorSet retrieved for block {}.", block); - Some(&**set) + match self.block_number.read()(id).map(|parent_block| self.correct_set_by_number(parent_block)) { + Ok(set) => { + Some(set) }, Err(e) => { debug!(target: "engine", "ValidatorSet could not be recovered: {}", e); @@ -64,6 +52,18 @@ impl Multi { }, } } + + fn correct_set_by_number(&self, parent_block: BlockNumber) -> &ValidatorSet { + let (block, set) = self.sets.iter() + .rev() + .find(|&(block, _)| *block <= parent_block + 1) + .expect("constructor validation ensures that there is at least one validator set for block 0; + block 0 is less than any uint; + qed"); + + trace!(target: "engine", "Multi ValidatorSet retrieved for block {}.", block); + &**set + } } impl ValidatorSet for Multi { @@ -72,6 +72,16 @@ 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 + { + self.correct_set_by_number(header.number()).proof_required(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 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 8a0599dde..61e16e198 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -26,6 +26,7 @@ use util::cache::MemoryLruCache; use engines::Call; use types::ids::BlockId; use client::{Client, BlockChainClient}; +use header::Header; use super::ValidatorSet; use super::simple_list::SimpleList; @@ -74,6 +75,19 @@ 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 + { + // TODO: check blooms first and then logs for the + // ValidatorsChanged([parent_hash, nonce], new_validators) log event. + ::engines::RequiresProof::No + } + + fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String> { + self.get_list(caller).map(|list| ::rlp::encode_list(&list.into_inner()).to_vec()) + .ok_or_else(|| "Caller insufficient to get validator list.".into()) + } + 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 ac82cf76f..e513212a6 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/src/engines/validator_set/simple_list.rs @@ -19,6 +19,7 @@ use util::{H256, Address, HeapSizeOf}; use engines::Call; +use header::Header; use super::ValidatorSet; #[derive(Debug, PartialEq, Eq, Default)] @@ -27,11 +28,17 @@ pub struct SimpleList { } impl SimpleList { + /// Create a new `SimpleList`. pub fn new(validators: Vec
) -> Self { SimpleList { validators: validators, } } + + /// Convert into inner representation. + pub fn into_inner(self) -> Vec
{ + self.validators + } } impl HeapSizeOf for SimpleList { @@ -45,6 +52,16 @@ 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 + { + ::engines::RequiresProof::No + } + + fn generate_proof(&self, _header: &Header, _caller: &Call) -> Result, String> { + Ok(Vec::new()) + } + fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool { self.validators.contains(address) }