diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 66abfcee3d..09aa28e86b 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -9,7 +9,7 @@ use lmd_ghost::LmdGhost; use operation_pool::DepositInsertStatus; use operation_pool::{OperationPool, PersistedOperationPool}; use parking_lot::RwLock; -use slog::{crit, debug, error, info, trace, warn, Logger}; +use slog::{debug, error, info, trace, warn, Logger}; use slot_clock::SlotClock; use ssz::Encode; use state_processing::per_block_processing::{ @@ -25,6 +25,7 @@ use state_processing::{ use std::fs; use std::io::prelude::*; use std::sync::Arc; +use std::time::{Duration, Instant}; use store::iter::{ BlockRootsIterator, ReverseBlockRootIterator, ReverseStateRootIterator, StateRootsIterator, }; @@ -44,9 +45,6 @@ pub const GRAFFITI: &str = "sigp/lighthouse-0.0.0-prerelease"; /// Only useful for testing. const WRITE_BLOCK_PROCESSING_SSZ: bool = cfg!(feature = "write_ssz_files"); -const BLOCK_SKIPPING_LOGGING_THRESHOLD: u64 = 3; -const BLOCK_SKIPPING_FAILURE_THRESHOLD: u64 = 128; - #[derive(Debug, PartialEq)] pub enum BlockProcessingOutcome { /// Block was valid and imported into the block graph. @@ -343,34 +341,34 @@ impl BeaconChain { if slot == head_state.slot { Ok(head_state) } else if slot > head_state.slot { - // It is presently very resource intensive (lots of hashing) to skip slots. - // - // We log warnings or simply fail if there are too many skip slots. This is a - // protection against DoS attacks. - if slot > head_state.slot + BLOCK_SKIPPING_FAILURE_THRESHOLD { - crit!( - self.log, - "Refusing to skip more than {} blocks", BLOCK_SKIPPING_LOGGING_THRESHOLD; - "head_slot" => head_state.slot, - "request_slot" => slot - ); - - return Err(Error::StateSkipTooLarge { - head_slot: head_state.slot, - requested_slot: slot, - }); - } else if slot > head_state.slot + BLOCK_SKIPPING_LOGGING_THRESHOLD { + if slot > head_state.slot + T::EthSpec::slots_per_epoch() { warn!( self.log, - "Skipping more than {} blocks", BLOCK_SKIPPING_LOGGING_THRESHOLD; + "Skipping more than an epoch"; "head_slot" => head_state.slot, "request_slot" => slot ) } + let start_slot = head_state.slot; + let task_start = Instant::now(); + let max_task_runtime = Duration::from_millis(self.spec.milliseconds_per_slot); + let head_state_slot = head_state.slot; let mut state = head_state; while state.slot < slot { + // Do not allow and forward state skip that takes longer than the maximum task duration. + // + // This is a protection against nodes doing too much work when they're not synced + // to a chain. + if task_start + max_task_runtime < Instant::now() { + return Err(Error::StateSkipTooLarge { + start_slot, + requested_slot: slot, + max_task_runtime, + }); + } + match per_slot_processing(&mut state, &self.spec) { Ok(()) => (), Err(e) => {