skeleton for proof checking

This commit is contained in:
Robert Habermeier 2017-04-12 18:55:38 +02:00
parent ec922ee5e4
commit 34a1512ff0
8 changed files with 131 additions and 27 deletions

View File

@ -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<Bytes, Error> {
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()?;

View File

@ -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<ethjson::spec::BasicAuthorityParams> 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::<H520>()?;
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<Bytes, Error> {
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<Client>) {
self.validators.register_contract(client);
}

View File

@ -62,6 +62,8 @@ pub enum EngineError {
UnexpectedMessage,
/// Seal field has an unexpected size.
BadSealFieldSize(OutOfBounds<usize>),
/// 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<Bytes>),
}
/// 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<Bytes, 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]>)
fn proof_required(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>)
-> RequiresProof
{
RequiresProof::No

View File

@ -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<Vec<u8>, 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)
}

View File

@ -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<ValidatorSet> {
}
/// 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<Call>;
/// 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<Vec<u8>, 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;

View File

@ -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<Vec<u8>, 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))

View File

@ -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<Vec<u8>, 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

View File

@ -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<Address>) -> Self {
SimpleList {
validators: validators,
}
}
/// Convert into inner representation.
pub fn into_inner(self) -> Vec<Address> {
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<Vec<u8>, String> {
Ok(Vec::new())
}
fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool {
self.validators.contains(address)
}