Skip to content

Commit

Permalink
clique: ensure validator restores state before trying to seal
Browse files Browse the repository at this point in the history
  • Loading branch information
jwasinger committed Jan 7, 2019
1 parent e92ceb3 commit 30cad03
Showing 1 changed file with 65 additions and 37 deletions.
102 changes: 65 additions & 37 deletions ethcore/src/engines/clique/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,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 @@ -132,6 +132,62 @@ impl Clique {
Err(e) => { Err(From::from("failed to sign header")) }
}
}

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

match state.state(&parent.hash()) {
Some(st) => Some(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 {
trace!(target: "engine", "no parent state for {}", &parent.hash());
return None;
}
}

// 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()) {
trace!(target: "engine", "failed to apply checkpoint");
return None;
}
}

// 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) {
trace!(target: "engine", "failed to apply item");
return None;
}
}

return state.state(&parent.hash());
} else {
trace!(target: "engine", "failed to upgrade client");
return None;
}
}
}
}
}

impl Engine<EthereumMachine> for Clique {
Expand Down Expand Up @@ -269,6 +325,10 @@ impl Engine<EthereumMachine> for Clique {
return Seal::None;
}

if self.state(&_parent).is_none() {
panic!("could not recover voting state when sealing on block {}", _parent.number() + 1 );
}

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

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

if self.state(&parent).is_none() {
return Err(From::from(format!("could not parse parent state for {}", &parent.hash())));
}

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."));
}
}

// 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);

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

0 comments on commit 30cad03

Please sign in to comment.