mirror of
https://github.com/sigp/lighthouse.git
synced 2026-07-05 13:54:36 +00:00
Add block ban flag --invalid-block-roots (#7042)
This commit is contained in:
@@ -2857,6 +2857,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
chain_segment: Vec<RpcBlock<T::EthSpec>>,
|
||||
notify_execution_layer: NotifyExecutionLayer,
|
||||
) -> ChainSegmentResult {
|
||||
for block in chain_segment.iter() {
|
||||
if let Err(error) = self.check_invalid_block_roots(block.block_root()) {
|
||||
return ChainSegmentResult::Failed {
|
||||
imported_blocks: vec![],
|
||||
error,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let mut imported_blocks = vec![];
|
||||
|
||||
// Filter uninteresting blocks from the chain segment in a blocking task.
|
||||
@@ -3340,6 +3349,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
self.remove_notified(&block_root, r)
|
||||
}
|
||||
|
||||
/// Check for known and configured invalid block roots before processing.
|
||||
pub fn check_invalid_block_roots(&self, block_root: Hash256) -> Result<(), BlockError> {
|
||||
if self.config.invalid_block_roots.contains(&block_root) {
|
||||
Err(BlockError::KnownInvalidExecutionPayload(block_root))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Ok(block_root)` if the given `unverified_block` was successfully verified and
|
||||
/// imported into the chain.
|
||||
///
|
||||
|
||||
@@ -282,6 +282,9 @@ pub enum BlockError {
|
||||
/// problems to worry about than losing peers, and we're doing the network a favour by
|
||||
/// disconnecting.
|
||||
ParentExecutionPayloadInvalid { parent_root: Hash256 },
|
||||
/// This is a known invalid block that was listed in Lighthouses configuration.
|
||||
/// At the moment this error is only relevant as part of the Holesky network recovery efforts.
|
||||
KnownInvalidExecutionPayload(Hash256),
|
||||
/// The block is a slashable equivocation from the proposer.
|
||||
///
|
||||
/// ## Peer scoring
|
||||
@@ -862,6 +865,9 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
|
||||
return Err(BlockError::DuplicateFullyImported(block_root));
|
||||
}
|
||||
|
||||
// Do not process a block that is known to be invalid.
|
||||
chain.check_invalid_block_roots(block_root)?;
|
||||
|
||||
// Do not process a block that doesn't descend from the finalized root.
|
||||
//
|
||||
// We check this *before* we load the parent so that we can return a more detailed error.
|
||||
@@ -1081,6 +1087,9 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
|
||||
.fork_name(&chain.spec)
|
||||
.map_err(BlockError::InconsistentFork)?;
|
||||
|
||||
// Check whether the block is a banned block prior to loading the parent.
|
||||
chain.check_invalid_block_roots(block_root)?;
|
||||
|
||||
let (mut parent, block) = load_parent(block, chain)?;
|
||||
|
||||
let state = cheap_state_advance_to_obtain_committees::<_, BlockError>(
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
pub use proto_array::{DisallowedReOrgOffsets, ReOrgThreshold};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use types::{Checkpoint, Epoch};
|
||||
use std::str::FromStr;
|
||||
use std::{collections::HashSet, sync::LazyLock, time::Duration};
|
||||
use types::{Checkpoint, Epoch, Hash256};
|
||||
|
||||
pub const DEFAULT_RE_ORG_HEAD_THRESHOLD: ReOrgThreshold = ReOrgThreshold(20);
|
||||
pub const DEFAULT_RE_ORG_PARENT_THRESHOLD: ReOrgThreshold = ReOrgThreshold(160);
|
||||
@@ -19,6 +20,12 @@ pub const FORK_CHOICE_LOOKAHEAD_FACTOR: u32 = 24;
|
||||
/// Default sync tolerance epochs.
|
||||
pub const DEFAULT_SYNC_TOLERANCE_EPOCHS: u64 = 2;
|
||||
|
||||
/// Invalid block root to be banned from processing and importing on Holesky network by default.
|
||||
pub static INVALID_HOLESKY_BLOCK_ROOT: LazyLock<Hash256> = LazyLock::new(|| {
|
||||
Hash256::from_str("2db899881ed8546476d0b92c6aa9110bea9a4cd0dbeb5519eb0ea69575f1f359")
|
||||
.expect("valid block root")
|
||||
});
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||
pub struct ChainConfig {
|
||||
/// Maximum number of slots to skip when importing an attestation.
|
||||
@@ -100,6 +107,11 @@ pub struct ChainConfig {
|
||||
/// The max distance between the head block and the current slot at which Lighthouse will
|
||||
/// consider itself synced and still serve validator-related requests.
|
||||
pub sync_tolerance_epochs: u64,
|
||||
/// Block roots of "banned" blocks which Lighthouse will refuse to import.
|
||||
///
|
||||
/// On Holesky there is a block which is added to this set by default but which can be removed
|
||||
/// by using `--invalid-block-roots ""`.
|
||||
pub invalid_block_roots: HashSet<Hash256>,
|
||||
}
|
||||
|
||||
impl Default for ChainConfig {
|
||||
@@ -136,6 +148,7 @@ impl Default for ChainConfig {
|
||||
blob_publication_batches: 4,
|
||||
blob_publication_batch_interval: Duration::from_millis(300),
|
||||
sync_tolerance_epochs: DEFAULT_SYNC_TOLERANCE_EPOCHS,
|
||||
invalid_block_roots: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1457,6 +1457,7 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
|
||||
| Err(e @ BlockError::InconsistentFork(_))
|
||||
| Err(e @ BlockError::ExecutionPayloadError(_))
|
||||
| Err(e @ BlockError::ParentExecutionPayloadInvalid { .. })
|
||||
| Err(e @ BlockError::KnownInvalidExecutionPayload(_))
|
||||
| Err(e @ BlockError::GenesisBlock) => {
|
||||
warn!(self.log, "Could not verify block for gossip. Rejecting the block";
|
||||
"error" => %e);
|
||||
|
||||
@@ -800,6 +800,18 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
|
||||
peer_action: Some(PeerAction::LowToleranceError),
|
||||
})
|
||||
}
|
||||
// Penalise peers for sending us banned blocks.
|
||||
BlockError::KnownInvalidExecutionPayload(block_root) => {
|
||||
warn!(
|
||||
self.log,
|
||||
"Received block known to be invalid";
|
||||
"block_root" => ?block_root,
|
||||
);
|
||||
Err(ChainSegmentFailed {
|
||||
message: format!("Banned block: {block_root:?}"),
|
||||
peer_action: Some(PeerAction::Fatal),
|
||||
})
|
||||
}
|
||||
other => {
|
||||
debug!(
|
||||
self.log, "Invalid block received";
|
||||
|
||||
@@ -1621,5 +1621,13 @@ pub fn cli_app() -> Command {
|
||||
.action(ArgAction::Set)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("invalid-block-roots")
|
||||
.long("invalid-block-roots")
|
||||
.value_name("FILE")
|
||||
.help("Path to a comma separated file containing block roots that should be treated as invalid during block verification.")
|
||||
.action(ArgAction::Set)
|
||||
.hide(true)
|
||||
)
|
||||
.group(ArgGroup::new("enable_http").args(["http", "gui", "staking"]).multiple(true))
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use account_utils::{read_input_from_user, STDIN_INPUTS_FLAG};
|
||||
use beacon_chain::chain_config::{
|
||||
DisallowedReOrgOffsets, ReOrgThreshold, DEFAULT_PREPARE_PAYLOAD_LOOKAHEAD_FACTOR,
|
||||
DEFAULT_RE_ORG_HEAD_THRESHOLD, DEFAULT_RE_ORG_MAX_EPOCHS_SINCE_FINALIZATION,
|
||||
DEFAULT_RE_ORG_PARENT_THRESHOLD,
|
||||
DEFAULT_RE_ORG_PARENT_THRESHOLD, INVALID_HOLESKY_BLOCK_ROOT,
|
||||
};
|
||||
use beacon_chain::graffiti_calculator::GraffitiOrigin;
|
||||
use beacon_chain::TrustedSetup;
|
||||
@@ -20,9 +20,10 @@ use lighthouse_network::{multiaddr::Protocol, Enr, Multiaddr, NetworkConfig, Pee
|
||||
use sensitive_url::SensitiveUrl;
|
||||
use slog::{info, warn, Logger};
|
||||
use std::cmp::max;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::io::IsTerminal;
|
||||
use std::io::{IsTerminal, Read};
|
||||
use std::net::Ipv6Addr;
|
||||
use std::net::{IpAddr, Ipv4Addr, ToSocketAddrs};
|
||||
use std::num::NonZeroU16;
|
||||
@@ -903,6 +904,40 @@ pub fn get_config<E: EthSpec>(
|
||||
.max_gossip_aggregate_batch_size =
|
||||
clap_utils::parse_required(cli_args, "beacon-processor-aggregate-batch-size")?;
|
||||
|
||||
if let Some(invalid_block_roots_file_path) =
|
||||
clap_utils::parse_optional::<String>(cli_args, "invalid-block-roots")?
|
||||
{
|
||||
let mut file = std::fs::File::open(invalid_block_roots_file_path)
|
||||
.map_err(|e| format!("Failed to open invalid-block-roots file: {}", e))?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)
|
||||
.map_err(|e| format!("Failed to read invalid-block-roots file {}", e))?;
|
||||
let invalid_block_roots: HashSet<Hash256> = contents
|
||||
.split(',')
|
||||
.filter_map(
|
||||
|s| match Hash256::from_str(s.strip_prefix("0x").unwrap_or(s).trim()) {
|
||||
Ok(block_root) => Some(block_root),
|
||||
Err(e) => {
|
||||
warn!(
|
||||
log,
|
||||
"Unable to parse invalid block root";
|
||||
"block_root" => s,
|
||||
"error" => ?e,
|
||||
);
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
client_config.chain.invalid_block_roots = invalid_block_roots;
|
||||
} else if spec
|
||||
.config_name
|
||||
.as_ref()
|
||||
.is_some_and(|network_name| network_name == "holesky")
|
||||
{
|
||||
client_config.chain.invalid_block_roots = HashSet::from([*INVALID_HOLESKY_BLOCK_ROOT]);
|
||||
}
|
||||
|
||||
Ok(client_config)
|
||||
}
|
||||
|
||||
|
||||
@@ -2773,3 +2773,43 @@ fn beacon_node_backend_override() {
|
||||
assert_eq!(config.store.backend, BeaconNodeBackend::LevelDb);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_block_roots_flag() {
|
||||
let dir = TempDir::new().expect("Unable to create temporary directory");
|
||||
let mut file =
|
||||
File::create(dir.path().join("invalid-block-roots")).expect("Unable to create file");
|
||||
file.write_all(b"2db899881ed8546476d0b92c6aa9110bea9a4cd0dbeb5519eb0ea69575f1f359, 2db899881ed8546476d0b92c6aa9110bea9a4cd0dbeb5519eb0ea69575f1f358, 0x3db899881ed8546476d0b92c6aa9110bea9a4cd0dbeb5519eb0ea69575f1f358")
|
||||
.expect("Unable to write to file");
|
||||
CommandLineTest::new()
|
||||
.flag(
|
||||
"invalid-block-roots",
|
||||
dir.path().join("invalid-block-roots").as_os_str().to_str(),
|
||||
)
|
||||
.run_with_zero_port()
|
||||
.with_config(|config| assert_eq!(config.chain.invalid_block_roots.len(), 3))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_block_roots_default_holesky() {
|
||||
use beacon_node::beacon_chain::chain_config::INVALID_HOLESKY_BLOCK_ROOT;
|
||||
CommandLineTest::new()
|
||||
.flag("network", Some("holesky"))
|
||||
.run_with_zero_port()
|
||||
.with_config(|config| {
|
||||
assert_eq!(config.chain.invalid_block_roots.len(), 1);
|
||||
assert!(config
|
||||
.chain
|
||||
.invalid_block_roots
|
||||
.contains(&*INVALID_HOLESKY_BLOCK_ROOT));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_block_roots_default_mainnet() {
|
||||
CommandLineTest::new()
|
||||
.run_with_zero_port()
|
||||
.with_config(|config| {
|
||||
assert!(config.chain.invalid_block_roots.is_empty());
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user