more useful Engine::verify_seal

This commit is contained in:
Robert Habermeier 2017-04-11 17:07:04 +02:00
parent 4f8e61dce9
commit a254b2098f
8 changed files with 46 additions and 15 deletions

View File

@ -26,7 +26,7 @@ use util::error::{Mismatch, OutOfBounds};
use basic_types::{LogBloom, Seal};
use env_info::{EnvInfo, LastHashes};
use engines::Engine;
use engines::{Engine, ValidationProof};
use error::{Error, BlockError, TransactionError};
use factory::Factories;
use header::Header;
@ -484,10 +484,15 @@ impl LockedBlock {
/// Provide a valid seal in order to turn this into a `SealedBlock`.
/// This does check the validity of `seal` with the engine.
/// Returns the `ClosedBlock` back again if the seal is no good.
pub fn try_seal(self, engine: &Engine, seal: Vec<Bytes>) -> Result<SealedBlock, (Error, LockedBlock)> {
pub fn try_seal(
self,
engine: &Engine,
seal: Vec<Bytes>,
proof: Option<ValidationProof>,
) -> Result<SealedBlock, (Error, LockedBlock)> {
let mut s = self;
s.block.header.set_seal(seal);
match engine.verify_block_seal(&s.block.header) {
match engine.verify_block_seal(&s.block.header, proof) {
Err(e) => Err((e, s)),
_ => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }),
}

View File

@ -464,14 +464,14 @@ mod tests {
engine.set_signer(tap.clone(), addr1, "1".into());
if let Seal::Regular(seal) = engine.generate_seal(b1.block()) {
assert!(b1.clone().try_seal(engine, seal).is_ok());
assert!(b1.clone().try_seal(engine, seal, None).is_ok());
// Second proposal is forbidden.
assert!(engine.generate_seal(b1.block()) == Seal::None);
}
engine.set_signer(tap, addr2, "2".into());
if let Seal::Regular(seal) = engine.generate_seal(b2.block()) {
assert!(b2.clone().try_seal(engine, seal).is_ok());
assert!(b2.clone().try_seal(engine, seal, None).is_ok());
// Second proposal is forbidden.
assert!(engine.generate_seal(b2.block()) == Seal::None);
}

View File

@ -258,7 +258,7 @@ mod tests {
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock();
if let Seal::Regular(seal) = engine.generate_seal(b.block()) {
assert!(b.try_seal(engine, seal).is_ok());
assert!(b.try_seal(engine, seal, None).is_ok());
}
}

View File

@ -89,7 +89,7 @@ mod tests {
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock();
if let Seal::Regular(seal) = engine.generate_seal(b.block()) {
assert!(b.try_seal(engine, seal).is_ok());
assert!(b.try_seal(engine, seal, None).is_ok());
}
}

View File

@ -33,18 +33,20 @@ pub use self::authority_round::AuthorityRound;
pub use self::tendermint::Tendermint;
use std::sync::Weak;
use util::*;
use ethkey::Signature;
use account_provider::AccountProvider;
use block::ExecutedBlock;
use builtin::Builtin;
use client::Client;
use env_info::EnvInfo;
use error::{Error, TransactionError};
use spec::CommonParams;
use evm::Schedule;
use header::Header;
use spec::CommonParams;
use transaction::{UnverifiedTransaction, SignedTransaction};
use client::Client;
use ethkey::Signature;
use util::*;
/// Voting errors.
#[derive(Debug)]
@ -59,6 +61,8 @@ pub enum EngineError {
UnexpectedMessage,
/// Seal field has an unexpected size.
BadSealFieldSize(OutOfBounds<usize>),
/// Needs a validation proof for the given block hash before verification can continue.
NeedsValidationProof(H256),
}
impl fmt::Display for EngineError {
@ -70,6 +74,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),
NeedsValidationProof(ref hash) => format!("Needs validation proof of block {} to verify seal.", hash),
};
f.write_fmt(format_args!("Engine error ({})", msg))
@ -87,6 +92,12 @@ pub enum Seal {
None,
}
/// A validation proof, required for validation of a block header.
pub type ValidationProof = Vec<DBValue>;
/// Type alias for a function we can make calls through synchronously.
pub type Call = Fn(Address, Bytes) -> Result<Bytes, String>;
/// 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 {
@ -180,10 +191,25 @@ pub trait Engine : Sync + Send {
/// 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> {
fn verify_block_seal(&self, header: &Header, _proof: Option<ValidationProof>) -> Result<(), Error> {
self.verify_block_basic(header, None).and_then(|_| self.verify_block_unordered(header, None))
}
/// Generate a validation proof for the given block header.
///
/// All values queried during execution of given will go into the proof.
/// This may only be called for blocks indicated in "needs validation proof"
/// errors.
///
/// Engines which don't draw consensus information from the state (e.g. PoW)
/// don't need to change anything here.
///
/// Engines which do draw consensus information from the state may only do so
/// here.
fn generate_validation_proof(&self, _call: &Call) -> ValidationProof {
ValidationProof::default()
}
/// 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.

View File

@ -864,7 +864,7 @@ mod tests {
let proposer = insert_and_register(&tap, spec.engine.as_ref(), "1");
let (b, seal) = propose_default(&spec, proposer);
assert!(b.lock().try_seal(spec.engine.as_ref(), seal).is_ok());
assert!(b.lock().try_seal(spec.engine.as_ref(), seal, None).is_ok());
}
#[test]

View File

@ -1133,7 +1133,7 @@ impl MinerService for Miner {
|b| &b.hash() == &block_hash
) {
trace!(target: "miner", "Submitted block {}={}={} with seal {:?}", block_hash, b.hash(), b.header().bare_hash(), seal);
b.lock().try_seal(&*self.engine, seal).or_else(|(e, _)| {
b.lock().try_seal(&*self.engine, seal, None).or_else(|(e, _)| {
warn!(target: "miner", "Mined solution rejected: {}", e);
Err(Error::PowInvalid)
})

View File

@ -567,7 +567,7 @@ pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &Engine, chain
if always || rng.gen::<f32>() <= POW_VERIFY_RATE {
match chain.block_header(header.parent_hash()) {
Some(parent) => engine.verify_block_family(header, &parent, body),
None => engine.verify_block_seal(header),
None => engine.verify_block_seal(header, None), // TODO: fetch validation proof as necessary.
}
} else {
engine.verify_block_basic(header, body)