Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Properly handle check_epoch_end_signal errors #10015

Merged
merged 8 commits into from
Feb 7, 2019
Merged
64 changes: 37 additions & 27 deletions ethcore/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,8 @@ impl Importer {
continue;
}

match self.check_and_lock_block(block, client) {
Ok(closed_block) => {
match self.check_and_lock_block(&bytes, block, client) {
Ok((closed_block, pending)) => {
if self.engine.is_proposal(&header) {
self.block_queue.mark_as_good(&[hash]);
proposed_blocks.push(bytes);
Expand All @@ -301,7 +301,7 @@ impl Importer {

let transactions_len = closed_block.transactions().len();

let route = self.commit_block(closed_block, &header, encoded::Block::new(bytes), client);
let route = self.commit_block(closed_block, &header, encoded::Block::new(bytes), pending, client);
import_results.push(route);

client.report.write().accrue_block(&header, transactions_len);
Expand Down Expand Up @@ -353,7 +353,7 @@ impl Importer {
imported
}

fn check_and_lock_block(&self, block: PreverifiedBlock, client: &Client) -> EthcoreResult<LockedBlock> {
fn check_and_lock_block(&self, bytes: &[u8], block: PreverifiedBlock, client: &Client) -> EthcoreResult<(LockedBlock, Option<PendingTransition>)> {
let engine = &*self.engine;
let header = block.header.clone();

Expand Down Expand Up @@ -437,7 +437,15 @@ impl Importer {
bail!(e);
}

Ok(locked_block)
let pending = self.check_epoch_end_signal(
&header,
bytes,
locked_block.receipts(),
locked_block.state().db(),
client
)?;

Ok((locked_block, pending))
}

/// Import a block with transaction receipts.
Expand Down Expand Up @@ -469,7 +477,7 @@ impl Importer {
// it is for reconstructing the state transition.
//
// The header passed is from the original block data and is sealed.
fn commit_block<B>(&self, block: B, header: &Header, block_data: encoded::Block, client: &Client) -> ImportRoute where B: Drain {
fn commit_block<B>(&self, block: B, header: &Header, block_data: encoded::Block, pending: Option<PendingTransition>, client: &Client) -> ImportRoute where B: Drain {
let hash = &header.hash();
let number = header.number();
let parent = header.parent_hash();
Expand Down Expand Up @@ -524,15 +532,9 @@ impl Importer {

// check epoch end signal, potentially generating a proof on the current
// state.
self.check_epoch_end_signal(
&header,
block_data.raw(),
&receipts,
&state,
&chain,
&mut batch,
client
);
if let Some(pending) = pending {
chain.insert_pending_transition(&mut batch, header.hash(), pending);
}

state.journal_under(&mut batch, number, hash).expect("DB commit failed");

Expand Down Expand Up @@ -587,10 +589,8 @@ impl Importer {
block_bytes: &[u8],
receipts: &[Receipt],
state_db: &StateDB,
chain: &BlockChain,
batch: &mut DBTransaction,
client: &Client,
) {
) -> EthcoreResult<Option<PendingTransition>> {
use engines::EpochChange;

let hash = header.hash();
Expand All @@ -601,7 +601,6 @@ impl Importer {

match self.engine.signals_epoch_end(header, auxiliary) {
EpochChange::Yes(proof) => {
use engines::epoch::PendingTransition;
use engines::Proof;

let proof = match proof {
Expand Down Expand Up @@ -638,11 +637,9 @@ impl Importer {
.transact(&transaction, options);

let res = match res {
Err(ExecutionError::Internal(e)) =>
Err(format!("Internal error: {}", e)),
Err(e) => {
trace!(target: "client", "Proved call failed: {}", e);
Ok((Vec::new(), state.drop().1.extract_proof()))
Err(e.to_string())
}
Ok(res) => Ok((res.output, state.drop().1.extract_proof())),
};
Expand All @@ -655,21 +652,21 @@ impl Importer {
Err(e) => {
warn!(target: "client", "Failed to generate transition proof for block {}: {}", hash, e);
warn!(target: "client", "Snapshots produced by this client may be incomplete");
Vec::new()
return Err(EngineError::FailedSystemCall(e).into())
}
}
}
};

debug!(target: "client", "Block {} signals epoch end.", hash);

let pending = PendingTransition { proof: proof };
chain.insert_pending_transition(batch, hash, pending);
Ok(Some(PendingTransition { proof: proof }))
},
EpochChange::No => {},
EpochChange::No => Ok(None),
EpochChange::Unsure(_) => {
warn!(target: "client", "Detected invalid engine implementation.");
warn!(target: "client", "Engine claims to require more block data, but everything provided.");
Err(EngineError::InvalidEngine.into())
}
}
}
Expand Down Expand Up @@ -2327,7 +2324,20 @@ impl ImportSealedBlock for Client {

let block_data = block.rlp_bytes();

let route = self.importer.commit_block(block, &header, encoded::Block::new(block_data), self);
let pending = self.importer.check_epoch_end_signal(
&header,
&block_data,
block.receipts(),
block.state().db(),
self
)?;
let route = self.importer.commit_block(
block,
&header,
encoded::Block::new(block_data),
pending,
self
);
trace!(target: "client", "Imported sealed block #{} ({})", header.number(), hash);
self.state_db.write().sync_cache(&route.enacted, &route.retracted, false);
route
Expand Down
3 changes: 3 additions & 0 deletions ethcore/src/engines/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ pub enum EngineError {
MalformedMessage(String),
/// Requires client ref, but none registered.
RequiresClient,
/// Invalid engine specification or implementation.
InvalidEngine,
}

impl fmt::Display for EngineError {
Expand All @@ -96,6 +98,7 @@ impl fmt::Display for EngineError {
FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg),
MalformedMessage(ref msg) => format!("Received malformed consensus message: {}", msg),
RequiresClient => format!("Call requires client but none registered"),
InvalidEngine => format!("Invalid engine specification or implementation"),
};

f.write_fmt(format_args!("Engine error ({})", msg))
Expand Down
7 changes: 7 additions & 0 deletions ethcore/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,13 @@ impl<B: Backend> fmt::Debug for State<B> {
}
}

impl State<StateDB> {
/// Get a reference to the underlying state DB.
pub fn db(&self) -> &StateDB {
&self.db
}
}

// TODO: cloning for `State` shouldn't be possible in general; Remove this and use
// checkpoints where possible.
impl Clone for State<StateDB> {
Expand Down