Refactor block processing results, some sync logic
This commit is contained in:
parent
b9e832216b
commit
4c0724fba6
@ -15,7 +15,7 @@ use state_processing::per_block_processing::errors::{
|
||||
};
|
||||
use state_processing::{
|
||||
per_block_processing, per_block_processing_without_verifying_block_signature,
|
||||
per_slot_processing, BlockProcessingError, SlotProcessingError,
|
||||
per_slot_processing, BlockProcessingError,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use store::{Error as DBError, Store};
|
||||
@ -23,15 +23,11 @@ use tree_hash::TreeHash;
|
||||
use types::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ValidBlock {
|
||||
/// The block was successfully processed.
|
||||
pub enum BlockProcessingOutcome {
|
||||
/// Block was valid and imported into the block graph.
|
||||
Processed,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum InvalidBlock {
|
||||
/// Don't re-process the genesis block.
|
||||
GenesisBlock,
|
||||
/// The blocks parent_root is unknown.
|
||||
ParentUnknown { parent: Hash256 },
|
||||
/// The block slot is greater than the present slot.
|
||||
FutureSlot {
|
||||
present_slot: Slot,
|
||||
@ -39,53 +35,16 @@ pub enum InvalidBlock {
|
||||
},
|
||||
/// The block state_root does not match the generated state.
|
||||
StateRootMismatch,
|
||||
/// The blocks parent_root is unknown.
|
||||
ParentUnknown { parent: Hash256 },
|
||||
/// There was an error whilst advancing the parent state to the present slot. This condition
|
||||
/// should not occur, it likely represents an internal error.
|
||||
SlotProcessingError(SlotProcessingError),
|
||||
/// The block was a genesis block, these blocks cannot be re-imported.
|
||||
GenesisBlock,
|
||||
/// The slot is finalized, no need to import.
|
||||
FinalizedSlot,
|
||||
/// Block is already known, no need to re-import.
|
||||
BlockIsAlreadyKnown,
|
||||
/// The block could not be applied to the state, it is invalid.
|
||||
PerBlockProcessingError(BlockProcessingError),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BlockProcessingOutcome {
|
||||
/// The block was successfully validated.
|
||||
ValidBlock(ValidBlock),
|
||||
/// The block was not successfully validated.
|
||||
InvalidBlock(InvalidBlock),
|
||||
}
|
||||
|
||||
impl BlockProcessingOutcome {
|
||||
/// Returns `true` if the block was objectively invalid and we should disregard the peer who
|
||||
/// sent it.
|
||||
pub fn is_invalid(&self) -> bool {
|
||||
match self {
|
||||
BlockProcessingOutcome::ValidBlock(_) => false,
|
||||
BlockProcessingOutcome::InvalidBlock(r) => match r {
|
||||
InvalidBlock::GenesisBlock { .. } => true,
|
||||
InvalidBlock::FutureSlot { .. } => true,
|
||||
InvalidBlock::StateRootMismatch => true,
|
||||
InvalidBlock::ParentUnknown { .. } => false,
|
||||
InvalidBlock::SlotProcessingError(_) => false,
|
||||
InvalidBlock::PerBlockProcessingError(e) => match e {
|
||||
BlockProcessingError::Invalid(_) => true,
|
||||
BlockProcessingError::BeaconStateError(_) => false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the block was successfully processed and can be removed from any import
|
||||
/// queues or temporary storage.
|
||||
pub fn sucessfully_processed(&self) -> bool {
|
||||
match self {
|
||||
BlockProcessingOutcome::ValidBlock(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BeaconChainTypes {
|
||||
type Store: store::Store;
|
||||
type SlotClock: slot_clock::SlotClock;
|
||||
@ -257,88 +216,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
BlockRootsIterator::new(self.store.clone(), self.state.read().clone(), slot)
|
||||
}
|
||||
|
||||
/*
|
||||
/// Returns `count `beacon block roots, starting from `start_slot` with an
|
||||
/// interval of `skip` slots between each root.
|
||||
///
|
||||
/// ## Errors:
|
||||
///
|
||||
/// - `SlotOutOfBounds`: Unable to return the full specified range.
|
||||
/// - `SlotOutOfBounds`: Unable to load a state from the DB.
|
||||
/// - `SlotOutOfBounds`: Start slot is higher than the first slot.
|
||||
/// - Other: BeaconState` is inconsistent.
|
||||
pub fn get_block_roots(
|
||||
&self,
|
||||
earliest_slot: Slot,
|
||||
count: usize,
|
||||
skip: usize,
|
||||
) -> Result<Vec<Hash256>, Error> {
|
||||
let step_by = Slot::from(skip + 1);
|
||||
|
||||
let mut roots: Vec<Hash256> = vec![];
|
||||
|
||||
// The state for reading block roots. Will be updated with an older state if slots go too
|
||||
// far back in history.
|
||||
let mut state = self.state.read().clone();
|
||||
|
||||
// The final slot in this series, will be reduced by `skip` each loop iteration.
|
||||
let mut slot = earliest_slot + Slot::from(count * (skip + 1)) - 1;
|
||||
|
||||
// If the highest slot requested is that of the current state insert the root of the
|
||||
// head block, unless the head block's slot is not matching.
|
||||
if slot == state.slot && self.head().beacon_block.slot == slot {
|
||||
roots.push(self.head().beacon_block_root);
|
||||
|
||||
slot -= step_by;
|
||||
} else if slot >= state.slot {
|
||||
return Err(BeaconStateError::SlotOutOfBounds.into());
|
||||
}
|
||||
|
||||
loop {
|
||||
// If the slot is within the range of the current state's block roots, append the root
|
||||
// to the output vec.
|
||||
//
|
||||
// If we get `SlotOutOfBounds` error, load the oldest available historic
|
||||
// state from the DB.
|
||||
match state.get_block_root(slot) {
|
||||
Ok(root) => {
|
||||
if slot < earliest_slot {
|
||||
break;
|
||||
} else {
|
||||
roots.push(*root);
|
||||
slot -= step_by;
|
||||
}
|
||||
}
|
||||
Err(BeaconStateError::SlotOutOfBounds) => {
|
||||
// Read the earliest historic state in the current slot.
|
||||
let earliest_historic_slot =
|
||||
state.slot - Slot::from(T::EthSpec::slots_per_historical_root());
|
||||
// Load the earlier state from disk.
|
||||
let new_state_root = state.get_state_root(earliest_historic_slot)?;
|
||||
|
||||
// Break if the DB is unable to load the state.
|
||||
state = match self.store.get(&new_state_root) {
|
||||
Ok(Some(state)) => state,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
}
|
||||
|
||||
// Return the results if they pass a sanity check.
|
||||
if (slot <= earliest_slot) && (roots.len() == count) {
|
||||
// Reverse the ordering of the roots. We extracted them in reverse order to make it
|
||||
// simpler to lookup historic states.
|
||||
//
|
||||
// This is a potential optimisation target.
|
||||
Ok(roots.iter().rev().cloned().collect())
|
||||
} else {
|
||||
Err(BeaconStateError::SlotOutOfBounds.into())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/// Returns the block at the given root, if any.
|
||||
///
|
||||
/// ## Errors
|
||||
@ -649,31 +526,39 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
///
|
||||
/// Will accept blocks from prior slots, however it will reject any block from a future slot.
|
||||
pub fn process_block(&self, block: BeaconBlock) -> Result<BlockProcessingOutcome, Error> {
|
||||
debug!("Processing block with slot {}...", block.slot);
|
||||
self.metrics.block_processing_requests.inc();
|
||||
let timer = self.metrics.block_processing_times.start_timer();
|
||||
|
||||
let finalized_slot = self
|
||||
.state
|
||||
.read()
|
||||
.finalized_epoch
|
||||
.start_slot(T::EthSpec::slots_per_epoch());
|
||||
if block.slot <= finalized_slot {
|
||||
return Ok(BlockProcessingOutcome::FinalizedSlot);
|
||||
}
|
||||
|
||||
if block.slot == 0 {
|
||||
return Ok(BlockProcessingOutcome::InvalidBlock(
|
||||
InvalidBlock::GenesisBlock,
|
||||
));
|
||||
return Ok(BlockProcessingOutcome::GenesisBlock);
|
||||
}
|
||||
|
||||
let block_root = block.block_header().canonical_root();
|
||||
|
||||
if block_root == self.genesis_block_root {
|
||||
return Ok(BlockProcessingOutcome::ValidBlock(ValidBlock::Processed));
|
||||
return Ok(BlockProcessingOutcome::GenesisBlock);
|
||||
}
|
||||
|
||||
let present_slot = self.present_slot();
|
||||
|
||||
if block.slot > present_slot {
|
||||
return Ok(BlockProcessingOutcome::InvalidBlock(
|
||||
InvalidBlock::FutureSlot {
|
||||
present_slot,
|
||||
block_slot: block.slot,
|
||||
},
|
||||
));
|
||||
return Ok(BlockProcessingOutcome::FutureSlot {
|
||||
present_slot,
|
||||
block_slot: block.slot,
|
||||
});
|
||||
}
|
||||
|
||||
if self.store.exists::<BeaconBlock>(&block_root)? {
|
||||
return Ok(BlockProcessingOutcome::BlockIsAlreadyKnown);
|
||||
}
|
||||
|
||||
// Load the blocks parent block from the database, returning invalid if that block is not
|
||||
@ -682,11 +567,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let parent_block: BeaconBlock = match self.store.get(&parent_block_root)? {
|
||||
Some(previous_block_root) => previous_block_root,
|
||||
None => {
|
||||
return Ok(BlockProcessingOutcome::InvalidBlock(
|
||||
InvalidBlock::ParentUnknown {
|
||||
parent: parent_block_root,
|
||||
},
|
||||
));
|
||||
return Ok(BlockProcessingOutcome::ParentUnknown {
|
||||
parent: parent_block_root,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -704,29 +587,25 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// Transition the parent state to the block slot.
|
||||
let mut state: BeaconState<T::EthSpec> = parent_state;
|
||||
for _ in state.slot.as_u64()..block.slot.as_u64() {
|
||||
if let Err(e) = per_slot_processing(&mut state, &self.spec) {
|
||||
return Ok(BlockProcessingOutcome::InvalidBlock(
|
||||
InvalidBlock::SlotProcessingError(e),
|
||||
));
|
||||
}
|
||||
per_slot_processing(&mut state, &self.spec)?;
|
||||
}
|
||||
|
||||
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?;
|
||||
|
||||
// Apply the received block to its parent state (which has been transitioned into this
|
||||
// slot).
|
||||
if let Err(e) = per_block_processing(&mut state, &block, &self.spec) {
|
||||
return Ok(BlockProcessingOutcome::InvalidBlock(
|
||||
InvalidBlock::PerBlockProcessingError(e),
|
||||
));
|
||||
match per_block_processing(&mut state, &block, &self.spec) {
|
||||
Err(BlockProcessingError::BeaconStateError(e)) => {
|
||||
return Err(Error::BeaconStateError(e))
|
||||
}
|
||||
Err(e) => return Ok(BlockProcessingOutcome::PerBlockProcessingError(e)),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let state_root = state.canonical_root();
|
||||
|
||||
if block.state_root != state_root {
|
||||
return Ok(BlockProcessingOutcome::InvalidBlock(
|
||||
InvalidBlock::StateRootMismatch,
|
||||
));
|
||||
return Ok(BlockProcessingOutcome::StateRootMismatch);
|
||||
}
|
||||
|
||||
// Store the block and state.
|
||||
@ -750,7 +629,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.observe(block.body.attestations.len() as f64);
|
||||
timer.observe_duration();
|
||||
|
||||
Ok(BlockProcessingOutcome::ValidBlock(ValidBlock::Processed))
|
||||
Ok(BlockProcessingOutcome::Processed)
|
||||
}
|
||||
|
||||
/// Produce a new block at the present slot.
|
||||
|
@ -5,9 +5,7 @@ pub mod iter;
|
||||
mod metrics;
|
||||
mod persisted_beacon_chain;
|
||||
|
||||
pub use self::beacon_chain::{
|
||||
BeaconChain, BeaconChainTypes, BlockProcessingOutcome, InvalidBlock, ValidBlock,
|
||||
};
|
||||
pub use self::beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome};
|
||||
pub use self::checkpoint::CheckPoint;
|
||||
pub use self::errors::{BeaconChainError, BlockProductionError};
|
||||
pub use fork_choice;
|
||||
|
@ -212,7 +212,7 @@ impl<T: BeaconChainTypes> ImportQueue<T> {
|
||||
// Case 2: there was no partial with a matching block root.
|
||||
//
|
||||
// A new partial is added. This case permits adding a header without already known the
|
||||
// root -- this is not possible in the wire protocol however we support it anyway.
|
||||
// root.
|
||||
self.partials.push(PartialBeaconBlock {
|
||||
slot: header.slot,
|
||||
block_root,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::import_queue::ImportQueue;
|
||||
use crate::message_handler::NetworkContext;
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome, InvalidBlock};
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome};
|
||||
use eth2_libp2p::rpc::methods::*;
|
||||
use eth2_libp2p::rpc::{RPCRequest, RPCResponse, RequestId};
|
||||
use eth2_libp2p::PeerId;
|
||||
@ -9,7 +9,6 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use store::Store;
|
||||
use tree_hash::TreeHash;
|
||||
use types::{
|
||||
Attestation, BeaconBlock, BeaconBlockBody, BeaconBlockHeader, Epoch, EthSpec, Hash256, Slot,
|
||||
};
|
||||
@ -24,6 +23,9 @@ const QUEUE_STALE_SECS: u64 = 600;
|
||||
/// Otherwise we queue it.
|
||||
const FUTURE_SLOT_TOLERANCE: u64 = 1;
|
||||
|
||||
const SHOULD_FORWARD_GOSSIP_BLOCK: bool = true;
|
||||
const SHOULD_NOT_FORWARD_GOSSIP_BLOCK: bool = false;
|
||||
|
||||
/// Keeps track of syncing information for known connected peers.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PeerSyncInfo {
|
||||
@ -393,6 +395,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
.collect();
|
||||
|
||||
roots.reverse();
|
||||
roots.dedup();
|
||||
|
||||
let headers: Vec<BeaconBlockHeader> = roots
|
||||
.into_iter()
|
||||
@ -509,6 +512,8 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
|
||||
/// Process a gossip message declaring a new block.
|
||||
///
|
||||
/// Attempts to apply to block to the beacon chain. May queue the block for later processing.
|
||||
///
|
||||
/// Returns a `bool` which, if `true`, indicates we should forward the block to our peers.
|
||||
pub fn on_block_gossip(
|
||||
&mut self,
|
||||
@ -516,133 +521,35 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
block: BeaconBlock,
|
||||
network: &mut NetworkContext,
|
||||
) -> bool {
|
||||
// Ignore any block from a finalized slot.
|
||||
if self.slot_is_finalized(block.slot) {
|
||||
debug!(
|
||||
self.log, "IgnoredFinalizedBlock";
|
||||
"source" => "gossip",
|
||||
"msg" => "chain is finalized at block slot",
|
||||
"block_slot" => block.slot,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if let Some(outcome) =
|
||||
self.process_block(peer_id.clone(), block.clone(), network, &"gossip")
|
||||
{
|
||||
match outcome {
|
||||
BlockProcessingOutcome::Processed => SHOULD_FORWARD_GOSSIP_BLOCK,
|
||||
BlockProcessingOutcome::ParentUnknown { .. } => {
|
||||
self.import_queue
|
||||
.enqueue_full_blocks(vec![block], peer_id.clone());
|
||||
|
||||
let block_root = Hash256::from_slice(&block.tree_hash_root());
|
||||
|
||||
// Ignore any block that the chain already knows about.
|
||||
if self.chain_has_seen_block(&block_root) {
|
||||
// TODO: Age confirm that we shouldn't forward a block if we already know of it.
|
||||
return false;
|
||||
}
|
||||
|
||||
match self.chain.process_block(block.clone()) {
|
||||
Ok(BlockProcessingOutcome::InvalidBlock(InvalidBlock::ParentUnknown { .. })) => {
|
||||
// The block was valid and we processed it successfully.
|
||||
debug!(
|
||||
self.log, "ParentBlockUnknown";
|
||||
"source" => "gossip",
|
||||
"parent_root" => format!("{}", block.previous_block_root),
|
||||
"peer" => format!("{:?}", peer_id),
|
||||
);
|
||||
// Queue the block for later processing.
|
||||
self.import_queue
|
||||
.enqueue_full_blocks(vec![block], peer_id.clone());
|
||||
// Send a hello to learn of the clients best slot so we can then sync the require
|
||||
// parent(s).
|
||||
network.send_rpc_request(
|
||||
peer_id.clone(),
|
||||
RPCRequest::Hello(hello_message(&self.chain)),
|
||||
);
|
||||
// Forward the block onto our peers.
|
||||
//
|
||||
// Note: this may need to be changed if we decide to only forward blocks if we have
|
||||
// all required info.
|
||||
true
|
||||
}
|
||||
Ok(BlockProcessingOutcome::InvalidBlock(InvalidBlock::FutureSlot {
|
||||
present_slot,
|
||||
block_slot,
|
||||
})) => {
|
||||
if block_slot - present_slot > FUTURE_SLOT_TOLERANCE {
|
||||
// The block is too far in the future, drop it.
|
||||
warn!(
|
||||
self.log, "FutureBlock";
|
||||
"source" => "gossip",
|
||||
"msg" => "block for future slot rejected, check your time",
|
||||
"present_slot" => present_slot,
|
||||
"block_slot" => block_slot,
|
||||
"FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE,
|
||||
"peer" => format!("{:?}", peer_id),
|
||||
);
|
||||
// Do not forward the block around to peers.
|
||||
false
|
||||
} else {
|
||||
// The block is in the future, but not too far.
|
||||
warn!(
|
||||
self.log, "QueuedFutureBlock";
|
||||
"source" => "gossip",
|
||||
"msg" => "queuing future block, check your time",
|
||||
"present_slot" => present_slot,
|
||||
"block_slot" => block_slot,
|
||||
"FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE,
|
||||
"peer" => format!("{:?}", peer_id),
|
||||
);
|
||||
// Queue the block for later processing.
|
||||
self.import_queue.enqueue_full_blocks(vec![block], peer_id);
|
||||
// Forward the block around to peers.
|
||||
true
|
||||
SHOULD_FORWARD_GOSSIP_BLOCK
|
||||
}
|
||||
}
|
||||
Ok(outcome) => {
|
||||
if outcome.is_invalid() {
|
||||
// The peer has sent a block which is fundamentally invalid.
|
||||
warn!(
|
||||
self.log, "InvalidBlock";
|
||||
"source" => "gossip",
|
||||
"msg" => "peer sent objectively invalid block",
|
||||
"outcome" => format!("{:?}", outcome),
|
||||
"peer" => format!("{:?}", peer_id),
|
||||
);
|
||||
// Disconnect the peer
|
||||
network.disconnect(peer_id, GoodbyeReason::Fault);
|
||||
// Do not forward the block to peers.
|
||||
false
|
||||
} else if outcome.sucessfully_processed() {
|
||||
// The block was valid and we processed it successfully.
|
||||
info!(
|
||||
self.log, "ImportedBlock";
|
||||
"source" => "gossip",
|
||||
"peer" => format!("{:?}", peer_id),
|
||||
);
|
||||
// Forward the block to peers
|
||||
true
|
||||
} else {
|
||||
// The block wasn't necessarily invalid but we didn't process it successfully.
|
||||
// This condition shouldn't be reached.
|
||||
error!(
|
||||
self.log, "BlockProcessingFailure";
|
||||
"source" => "gossip",
|
||||
"msg" => "unexpected condition in processing block.",
|
||||
"outcome" => format!("{:?}", outcome),
|
||||
);
|
||||
// Do not forward the block on.
|
||||
false
|
||||
BlockProcessingOutcome::FutureSlot {
|
||||
present_slot,
|
||||
block_slot,
|
||||
} if present_slot + FUTURE_SLOT_TOLERANCE >= block_slot => {
|
||||
self.import_queue
|
||||
.enqueue_full_blocks(vec![block], peer_id.clone());
|
||||
|
||||
SHOULD_FORWARD_GOSSIP_BLOCK
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
// We encountered an error whilst processing the block.
|
||||
// Note: known blocks are forwarded on the gossip network.
|
||||
//
|
||||
// Blocks should not be able to trigger errors, instead they should be flagged as
|
||||
// invalid.
|
||||
error!(
|
||||
self.log, "BlockProcessingError";
|
||||
"msg" => "internal error in processing block.",
|
||||
"source" => "gossip",
|
||||
"error" => format!("{:?}", e),
|
||||
);
|
||||
// Do not forward the block to peers.
|
||||
false
|
||||
// We rely upon the lower layers (libp2p) to stop loops occuring from re-gossiped
|
||||
// blocks.
|
||||
BlockProcessingOutcome::BlockIsAlreadyKnown => SHOULD_FORWARD_GOSSIP_BLOCK,
|
||||
_ => SHOULD_NOT_FORWARD_GOSSIP_BLOCK,
|
||||
}
|
||||
} else {
|
||||
SHOULD_NOT_FORWARD_GOSSIP_BLOCK
|
||||
}
|
||||
}
|
||||
|
||||
@ -669,57 +576,32 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
/// the queue.
|
||||
pub fn process_import_queue(&mut self, network: &mut NetworkContext) {
|
||||
let mut successful = 0;
|
||||
let mut invalid = 0;
|
||||
let mut errored = 0;
|
||||
|
||||
// Loop through all of the complete blocks in the queue.
|
||||
for (block_root, block, sender) in self.import_queue.complete_blocks() {
|
||||
let slot = block.slot;
|
||||
let parent_root = block.previous_block_root;
|
||||
let processing_result = self.process_block(sender, block.clone(), network, &"gossip");
|
||||
|
||||
match self.chain.process_block(block) {
|
||||
Ok(outcome) => {
|
||||
if outcome.is_invalid() {
|
||||
invalid += 1;
|
||||
warn!(
|
||||
self.log,
|
||||
"InvalidBlock";
|
||||
"sender_peer_id" => format!("{:?}", sender.clone()),
|
||||
"block_root" => format!("{}", block_root),
|
||||
"reason" => format!("{:?}", outcome),
|
||||
);
|
||||
network.disconnect(sender, GoodbyeReason::Fault);
|
||||
} else if outcome.sucessfully_processed() {
|
||||
successful += 1;
|
||||
self.import_queue.remove(block_root);
|
||||
} else {
|
||||
debug!(
|
||||
self.log,
|
||||
"ProcessImportQueue";
|
||||
"msg" => "Block not imported",
|
||||
"outcome" => format!("{:?}", outcome),
|
||||
"block_slot" => format!("{:?}", slot),
|
||||
"parent_root" => format!("{}", parent_root),
|
||||
"peer" => format!("{:?}", sender),
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
errored += 1;
|
||||
error!(self.log, "BlockProcessingError"; "error" => format!("{:?}", e));
|
||||
}
|
||||
let should_dequeue = match processing_result {
|
||||
Some(BlockProcessingOutcome::ParentUnknown { .. }) => false,
|
||||
Some(BlockProcessingOutcome::FutureSlot {
|
||||
present_slot,
|
||||
block_slot,
|
||||
}) if present_slot + FUTURE_SLOT_TOLERANCE >= block_slot => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if processing_result == Some(BlockProcessingOutcome::Processed) {
|
||||
successful += 1;
|
||||
}
|
||||
|
||||
if should_dequeue {
|
||||
self.import_queue.remove(block_root);
|
||||
}
|
||||
}
|
||||
|
||||
if successful > 0 {
|
||||
info!(self.log, "Imported {} blocks", successful)
|
||||
}
|
||||
if invalid > 0 {
|
||||
warn!(self.log, "Rejected {} invalid blocks", invalid)
|
||||
}
|
||||
if errored > 0 {
|
||||
warn!(self.log, "Failed to process {} blocks", errored)
|
||||
}
|
||||
}
|
||||
|
||||
/// Request some `BeaconBlockRoots` from the remote peer.
|
||||
@ -791,17 +673,128 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `true` if the given slot is finalized in our chain.
|
||||
fn slot_is_finalized(&self, slot: Slot) -> bool {
|
||||
slot <= hello_message(&self.chain)
|
||||
.latest_finalized_epoch
|
||||
.start_slot(T::EthSpec::slots_per_epoch())
|
||||
}
|
||||
|
||||
/// Generates our current state in the form of a HELLO RPC message.
|
||||
pub fn generate_hello(&self) -> HelloMessage {
|
||||
hello_message(&self.chain)
|
||||
}
|
||||
|
||||
/// Processes the `block` that was received from `peer_id`.
|
||||
///
|
||||
/// If the block was submitted to the beacon chain without internal error, `Some(outcome)` is
|
||||
/// returned, otherwise `None` is returned. Note: `Some(_)` does not necessarily indicate that
|
||||
/// the block was successfully processed or valid.
|
||||
///
|
||||
/// This function performs the following duties:
|
||||
///
|
||||
/// - Attempting to import the block into the beacon chain.
|
||||
/// - Logging
|
||||
/// - Requesting unavailable blocks (e.g., if parent is unknown).
|
||||
/// - Disconnecting faulty nodes.
|
||||
///
|
||||
/// This function does not remove processed blocks from the import queue.
|
||||
fn process_block(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
block: BeaconBlock,
|
||||
network: &mut NetworkContext,
|
||||
source: &str,
|
||||
) -> Option<BlockProcessingOutcome> {
|
||||
let processing_result = self.chain.process_block(block.clone());
|
||||
|
||||
if let Ok(outcome) = processing_result {
|
||||
match outcome {
|
||||
BlockProcessingOutcome::Processed => {
|
||||
info!(
|
||||
self.log, "Imported block from network";
|
||||
"source" => source,
|
||||
"slot" => block.slot,
|
||||
"peer" => format!("{:?}", peer_id),
|
||||
);
|
||||
}
|
||||
BlockProcessingOutcome::ParentUnknown { parent } => {
|
||||
// The block was valid and we processed it successfully.
|
||||
debug!(
|
||||
self.log, "ParentBlockUnknown";
|
||||
"source" => source,
|
||||
"parent_root" => format!("{}", parent),
|
||||
"peer" => format!("{:?}", peer_id),
|
||||
);
|
||||
|
||||
// Send a hello to learn of the clients best slot so we can then sync the require
|
||||
// parent(s).
|
||||
network.send_rpc_request(
|
||||
peer_id.clone(),
|
||||
RPCRequest::Hello(hello_message(&self.chain)),
|
||||
);
|
||||
|
||||
// Explicitly request the parent block from the peer.
|
||||
//
|
||||
// It is likely that this is duplicate work, given we already send a hello
|
||||
// request. However, I believe there are some edge-cases where the hello
|
||||
// message doesn't suffice, so we perform this request as well.
|
||||
self.request_block_headers(
|
||||
peer_id,
|
||||
BeaconBlockHeadersRequest {
|
||||
start_root: parent,
|
||||
start_slot: block.slot - 1,
|
||||
max_headers: 1,
|
||||
skip_slots: 0,
|
||||
},
|
||||
network,
|
||||
)
|
||||
}
|
||||
BlockProcessingOutcome::FutureSlot {
|
||||
present_slot,
|
||||
block_slot,
|
||||
} => {
|
||||
if present_slot + FUTURE_SLOT_TOLERANCE >= block_slot {
|
||||
// The block is too far in the future, drop it.
|
||||
warn!(
|
||||
self.log, "FutureBlock";
|
||||
"source" => source,
|
||||
"msg" => "block for future slot rejected, check your time",
|
||||
"present_slot" => present_slot,
|
||||
"block_slot" => block_slot,
|
||||
"FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE,
|
||||
"peer" => format!("{:?}", peer_id),
|
||||
);
|
||||
network.disconnect(peer_id, GoodbyeReason::Fault);
|
||||
} else {
|
||||
// The block is in the future, but not too far.
|
||||
debug!(
|
||||
self.log, "QueuedFutureBlock";
|
||||
"source" => source,
|
||||
"msg" => "queuing future block, check your time",
|
||||
"present_slot" => present_slot,
|
||||
"block_slot" => block_slot,
|
||||
"FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE,
|
||||
"peer" => format!("{:?}", peer_id),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
debug!(
|
||||
self.log, "InvalidBlock";
|
||||
"source" => source,
|
||||
"msg" => "peer sent invalid block",
|
||||
"outcome" => format!("{:?}", outcome),
|
||||
"peer" => format!("{:?}", peer_id),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Some(outcome)
|
||||
} else {
|
||||
error!(
|
||||
self.log, "BlockProcessingFailure";
|
||||
"source" => source,
|
||||
"msg" => "unexpected condition in processing block.",
|
||||
"outcome" => format!("{:?}", processing_result)
|
||||
);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a `HelloMessage` representing the state of the given `beacon_chain`.
|
||||
|
@ -1,4 +1,4 @@
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome};
|
||||
use crossbeam_channel;
|
||||
use eth2_libp2p::PubsubMessage;
|
||||
use futures::Future;
|
||||
@ -95,14 +95,12 @@ impl<T: BeaconChainTypes> BeaconBlockService for BeaconBlockServiceInstance<T> {
|
||||
Ok(block) => {
|
||||
match self.chain.process_block(block.clone()) {
|
||||
Ok(outcome) => {
|
||||
if outcome.sucessfully_processed() {
|
||||
if outcome == BlockProcessingOutcome::Processed {
|
||||
// Block was successfully processed.
|
||||
info!(
|
||||
self.log,
|
||||
"PublishBeaconBlock";
|
||||
"type" => "valid_block",
|
||||
"Valid block from RPC";
|
||||
"block_slot" => block.slot,
|
||||
"outcome" => format!("{:?}", outcome)
|
||||
);
|
||||
|
||||
// TODO: Obtain topics from the network service properly.
|
||||
@ -126,12 +124,11 @@ impl<T: BeaconChainTypes> BeaconBlockService for BeaconBlockServiceInstance<T> {
|
||||
});
|
||||
|
||||
resp.set_success(true);
|
||||
} else if outcome.is_invalid() {
|
||||
// Block was invalid.
|
||||
} else {
|
||||
// Block was not successfully processed.
|
||||
warn!(
|
||||
self.log,
|
||||
"PublishBeaconBlock";
|
||||
"type" => "invalid_block",
|
||||
"Invalid block from RPC";
|
||||
"outcome" => format!("{:?}", outcome)
|
||||
);
|
||||
|
||||
@ -139,17 +136,6 @@ impl<T: BeaconChainTypes> BeaconBlockService for BeaconBlockServiceInstance<T> {
|
||||
resp.set_msg(
|
||||
format!("InvalidBlock: {:?}", outcome).as_bytes().to_vec(),
|
||||
);
|
||||
} else {
|
||||
// Some failure during processing.
|
||||
warn!(
|
||||
self.log,
|
||||
"PublishBeaconBlock";
|
||||
"type" => "unable_to_import",
|
||||
"outcome" => format!("{:?}", outcome)
|
||||
);
|
||||
|
||||
resp.set_success(false);
|
||||
resp.set_msg(format!("other: {:?}", outcome).as_bytes().to_vec());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
Loading…
Reference in New Issue
Block a user