ChainVerifier for memoizing validator sets
This commit is contained in:
parent
b4f3e30cd6
commit
715d5daafe
@ -1 +1 @@
|
||||
Subproject commit 9028c4801fd39fbb71a9796979182549a24e81c8
|
||||
Subproject commit d520593078fa0849dcd1f907e44ed0a616892e33
|
@ -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 {
|
||||
|
@ -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<ethjson::spec::AuthorityRoundParams> 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<Address, Builtin>,
|
||||
transition_service: IoService<()>,
|
||||
step: AtomicUsize,
|
||||
step: Arc<Step>,
|
||||
proposed: AtomicBool,
|
||||
client: RwLock<Option<Weak<EngineClient>>>,
|
||||
signer: EngineSigner,
|
||||
validators: Box<ValidatorSet>,
|
||||
/// 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<Step>,
|
||||
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<usize, ::rlp::DecoderError> {
|
||||
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<Signature, ::rlp::DecoderError> {
|
||||
UntrustedRlp::new(&header.seal().get(1).expect("was checked with verify_block_basic; has 2 fields; qed")).as_val::<H520>().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<Address, Builtin> { &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<Bytes, Error> {
|
||||
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<Box<super::ChainVerifier>, 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());
|
||||
}
|
||||
}
|
||||
|
@ -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<ethjson::spec::BasicAuthorityParams> 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::<H520>()?;
|
||||
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::<H520>()?;
|
||||
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<Bytes, Error> {
|
||||
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<Box<super::ChainVerifier>, 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<Client>) {
|
||||
self.validators.register_contract(client);
|
||||
}
|
||||
|
42
ethcore/src/engines/chain_verifier.rs
Normal file
42
ethcore/src/engines/chain_verifier.rs
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// 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(()) }
|
||||
}
|
@ -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<Bytes, Error> {
|
||||
/// 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<Vec<u8>, 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<Box<ChainVerifier>, 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.
|
||||
|
@ -76,6 +76,10 @@ impl ValidatorSet for ValidatorContract {
|
||||
self.validators.generate_proof(header, caller)
|
||||
}
|
||||
|
||||
fn chain_verifier(&self, header: &Header, proof: Vec<u8>) -> Result<super::SimpleList, ::error::Error> {
|
||||
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);
|
||||
|
@ -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<Vec<u8>, String>;
|
||||
|
||||
/// Create a fully self-contained validator set from the given proof.
|
||||
fn chain_verifier(&self, header: &Header, proof: Vec<u8>) -> Result<SimpleList, ::error::Error>;
|
||||
|
||||
/// 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;
|
||||
|
@ -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<u8>) -> Result<super::SimpleList, ::error::Error> {
|
||||
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))
|
||||
|
@ -202,6 +202,15 @@ impl ValidatorSet for ValidatorSafeContract {
|
||||
}
|
||||
}
|
||||
|
||||
fn chain_verifier(&self, _header: &Header, proof: Vec<u8>) -> Result<SimpleList, ::error::Error> {
|
||||
use rlp::UntrustedRlp;
|
||||
|
||||
let rlp = UntrustedRlp::new(&proof);
|
||||
let validators: Vec<Address> = 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
|
||||
|
@ -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<Address>,
|
||||
}
|
||||
@ -62,6 +63,10 @@ impl ValidatorSet for SimpleList {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
fn chain_verifier(&self, _header: &Header, _: Vec<u8>) -> Result<SimpleList, ::error::Error> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
|
||||
fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool {
|
||||
self.validators.contains(address)
|
||||
}
|
||||
|
@ -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<Address, Builtin>) -> Self {
|
||||
Ethash {
|
||||
pub fn new(params: CommonParams, ethash_params: EthashParams, builtins: BTreeMap<Address, Builtin>) -> Arc<Self> {
|
||||
Arc::new(Ethash {
|
||||
params: params,
|
||||
ethash_params: ethash_params,
|
||||
builtins: builtins,
|
||||
pow: EthashManager::new(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine for Ethash {
|
||||
impl ::engines::ChainVerifier for Arc<Ethash> {
|
||||
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<Ethash> {
|
||||
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<u8>) -> Result<Box<::engines::ChainVerifier>, Error> {
|
||||
Ok(Box::new(self.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
// Try to round gas_limit a bit so that:
|
||||
|
Loading…
Reference in New Issue
Block a user