Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure validator has the voting snapshot state before sealing #38

Merged
merged 3 commits into from
Jan 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 66 additions & 39 deletions ethcore/src/engines/clique/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ use account_provider::AccountProvider;
use builtin::Builtin;
use vm::{EnvInfo, Schedule, CreateContractAddress, CallType, ActionValue};
use error::Error;
use header::{Header, BlockNumber, ExtendedHeader};
use types::header::{Header, ExtendedHeader};
use types::BlockNumber;
use snapshot::SnapshotComponents;
use spec::CommonParams;
use transaction::{self, UnverifiedTransaction, SignedTransaction};
use super::transaction::{UnverifiedTransaction, SignedTransaction};
use parking_lot::RwLock;
use block::*;
use io::IoService;
Expand All @@ -54,7 +55,7 @@ use types::ancestry_action::AncestryAction;
use engines::{Engine, Seal, EngineError, ConstructedVerifier, Headers, PendingTransitionStore};
use super::signer::EngineSigner;
use machine::{Call, AuxiliaryData, EthereumMachine};
use self::signer_snapshot::{CliqueState, SignerAuthorization, NONCE_AUTH_VOTE, NONCE_DROP_VOTE, NULL_AUTHOR, DIFF_INTURN, DIFF_NOT_INTURN};
use self::signer_snapshot::{CliqueState, SignerAuthorization, SnapshotState, NONCE_AUTH_VOTE, NONCE_DROP_VOTE, NULL_AUTHOR, DIFF_INTURN, DIFF_NOT_INTURN};

pub const SIGNER_VANITY_LENGTH: u32 = 32;
// Fixed number of extra-data prefix bytes reserved for signer vanity
Expand Down Expand Up @@ -148,6 +149,61 @@ impl Clique {
Err(e) => { Err(From::from("failed to sign header")) }
}
}

fn state(&self, parent: &Header) -> Result<SnapshotState, Error> {
let mut state = self.state.write();

match state.state(&parent.hash()) {
Some(st) => Ok(st),
None => {
let client = self.client.read();
if let Some(c) = client.as_ref().and_then(|w|{ w.upgrade()}) {
let last_checkpoint_number = (parent.number() / self.epoch_length as u64) * self.epoch_length;
let mut chain: &mut Vec<Header> = &mut Vec::new();
chain.push(parent.clone());

// populate chain to last checkpoint
let mut last = chain.last().unwrap().clone();

while last.number() != last_checkpoint_number +1 {
if let Some(next) = c.block_header(BlockId::Hash(*last.parent_hash())) {
chain.push(next.decode().unwrap().clone());
last = chain.last().unwrap().clone();
} else {
return Err(From::from(format!("parent state could not be recovered for block {}", &parent.hash())));
}
}

// Get the last checkpoint header
if let Some(last_checkpoint_header) = c.block_header(BlockId::Hash(*chain.last().unwrap().parent_hash())) {
if let Err(_) = state.apply_checkpoint(&last_checkpoint_header.decode().unwrap()) {
return Err(From::from("failed to apply checkpoint"));
}
}

// Catching up state.
chain.reverse();

trace!(target: "engine",
"verify_block_family backfilling state. last_checkpoint: {}, chain: {:?}.",
last_checkpoint_number, chain);

for item in chain {
if let Err(_) = state.apply(item) {
return Err(From::from("failed to apply item"));
}
}

match state.state(&parent.hash()) {
Some(st) => { return Ok(st); },
None => { panic!("Parent state should exist after being recovered. block {}", &parent.hash()); }
}
} else {
return Err(From::from("failed to upgrade client reference"));
}
}
}
}
}

impl Engine<EthereumMachine> for Clique {
Expand Down Expand Up @@ -284,6 +340,9 @@ impl Engine<EthereumMachine> for Clique {
return Seal::None;
}

//ensure the voting state exists
self.state(&_parent).unwrap();

let mut state = self.state.write();

match state.proposer_authorization(&block.header) {
Expand Down Expand Up @@ -411,44 +470,12 @@ impl Engine<EthereumMachine> for Clique {
header.number(), *self.state.read());
*/

let mut state = self.state.write();

// see if we have parent state
if state.state(&parent.hash()).is_none() {
let client = self.client.read();
if let Some(c) = client.as_ref().and_then(|w|{ w.upgrade()}) {
let last_checkpoint_number = (parent.number() / self.epoch_length as u64) * self.epoch_length;
let mut chain: &mut Vec<Header> = &mut Vec::new();
chain.push(parent.clone());

// populate chain to last checkpoint
let mut last = chain.last().unwrap().clone();

while last.number() != last_checkpoint_number +1 {
if let Some(next) = c.block_header(BlockId::Hash(*last.parent_hash())) {
chain.push(next.decode().unwrap().clone());
last = chain.last().unwrap().clone();
} else {
return Err(From::from("No parent state exist."));
}
}
if let Err(e) = self.state(&parent) {
return Err(e);
}

// Get the last checkpoint header
if let Some(last_checkpoint_header) = c.block_header(BlockId::Hash(*chain.last().unwrap().parent_hash())) {
state.apply_checkpoint(&last_checkpoint_header.decode().unwrap())?;
}
// Catching up state.
chain.reverse();

trace!(target: "engine",
"verify_block_family backfilling state. last_checkpoint: {}, chain: {:?}.",
last_checkpoint_number, chain);
let mut state = self.state.write();

for item in chain {
state.apply(item)?;
}
}
}
if (header.number() % self.epoch_length == 0) {
// TODO: we may still need to validate checkpoint state
state.apply_checkpoint(header);
Expand Down
9 changes: 1 addition & 8 deletions ethcore/src/engines/clique/signer_snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use ethereum_types::{Address, H256, U256};
use std::collections::{HashMap, VecDeque};
use engines::clique::{SIGNER_SIG_LENGTH, SIGNER_VANITY_LENGTH, recover};
use error::Error;
use header::{Header, ExtendedHeader};
use types::header::{Header, ExtendedHeader};
use super::super::signer::EngineSigner;
use parking_lot::{RwLock, Mutex};
use account_provider::AccountProvider;
Expand Down Expand Up @@ -124,7 +124,6 @@ impl CliqueState {
/// Apply an new header
pub fn apply(&mut self, header: &Header) -> Result<(), Error> {
let db = self.states_by_hash.borrow_mut();
trace!(target: "engine", "applying header {}", header.hash());

// make sure current hash is not in the db
match db.get_mut(header.parent_hash()).cloned() {
Expand All @@ -144,7 +143,6 @@ impl CliqueState {
new_state.recent_signers.pop_back();
}

trace!(target: "engine", "inserting {} {:?}", header.hash(), &new_state);
db.insert(header.hash(), new_state.clone());
Ok(())
}
Expand Down Expand Up @@ -220,7 +218,6 @@ fn extract_signers(header: &Header) -> Result<Vec<Address>, Error> {
// NOTE: base on geth implmentation , signers list area always sorted to ascending order.
signers_list.sort();

trace!(target: "engine", "extracted signers {:?}", &signers_list);
Ok(signers_list)
}

Expand Down Expand Up @@ -274,14 +271,10 @@ fn clique_hash(h: &Header) -> U256 {
/// Apply header to the state, used in block sealing and external block import
fn process_header(header: &Header, state: &mut SnapshotState, epoch_length: u64) -> Result<Address, Error> {

trace!(target: "engine", "called process_header for {}", header.number());

if header.extra_data().len() < SIGNER_VANITY_LENGTH as usize + SIGNER_SIG_LENGTH as usize {
return Err(From::from(format!("header extra data was too small: {}", header.extra_data().len())));
}

trace!(target: "engine", "extra_data field has valid length: {:?}", header.extra_data());

let creator = public_to_address(&recover(header).unwrap()).clone();

match state.get_signer_authorization(header.number(), &creator) {
Expand Down