Skip to content

Commit de7e258

Browse files
authored
Merge of #7042
2 parents 9db29b0 + c405dfb commit de7e258

File tree

8 files changed

+140
-4
lines changed

8 files changed

+140
-4
lines changed

beacon_node/beacon_chain/src/beacon_chain.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2857,6 +2857,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
28572857
chain_segment: Vec<RpcBlock<T::EthSpec>>,
28582858
notify_execution_layer: NotifyExecutionLayer,
28592859
) -> ChainSegmentResult {
2860+
for block in chain_segment.iter() {
2861+
if let Err(error) = self.check_invalid_block_roots(block.block_root()) {
2862+
return ChainSegmentResult::Failed {
2863+
imported_blocks: vec![],
2864+
error,
2865+
};
2866+
}
2867+
}
2868+
28602869
let mut imported_blocks = vec![];
28612870

28622871
// Filter uninteresting blocks from the chain segment in a blocking task.
@@ -3340,6 +3349,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
33403349
self.remove_notified(&block_root, r)
33413350
}
33423351

3352+
/// Check for known and configured invalid block roots before processing.
3353+
pub fn check_invalid_block_roots(&self, block_root: Hash256) -> Result<(), BlockError> {
3354+
if self.config.invalid_block_roots.contains(&block_root) {
3355+
Err(BlockError::KnownInvalidExecutionPayload(block_root))
3356+
} else {
3357+
Ok(())
3358+
}
3359+
}
3360+
33433361
/// Returns `Ok(block_root)` if the given `unverified_block` was successfully verified and
33443362
/// imported into the chain.
33453363
///

beacon_node/beacon_chain/src/block_verification.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,9 @@ pub enum BlockError {
282282
/// problems to worry about than losing peers, and we're doing the network a favour by
283283
/// disconnecting.
284284
ParentExecutionPayloadInvalid { parent_root: Hash256 },
285+
/// This is a known invalid block that was listed in Lighthouses configuration.
286+
/// At the moment this error is only relevant as part of the Holesky network recovery efforts.
287+
KnownInvalidExecutionPayload(Hash256),
285288
/// The block is a slashable equivocation from the proposer.
286289
///
287290
/// ## Peer scoring
@@ -862,6 +865,9 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
862865
return Err(BlockError::DuplicateFullyImported(block_root));
863866
}
864867

868+
// Do not process a block that is known to be invalid.
869+
chain.check_invalid_block_roots(block_root)?;
870+
865871
// Do not process a block that doesn't descend from the finalized root.
866872
//
867873
// 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> {
10811087
.fork_name(&chain.spec)
10821088
.map_err(BlockError::InconsistentFork)?;
10831089

1090+
// Check whether the block is a banned block prior to loading the parent.
1091+
chain.check_invalid_block_roots(block_root)?;
1092+
10841093
let (mut parent, block) = load_parent(block, chain)?;
10851094

10861095
let state = cheap_state_advance_to_obtain_committees::<_, BlockError>(

beacon_node/beacon_chain/src/chain_config.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
pub use proto_array::{DisallowedReOrgOffsets, ReOrgThreshold};
22
use serde::{Deserialize, Serialize};
3-
use std::time::Duration;
4-
use types::{Checkpoint, Epoch};
3+
use std::str::FromStr;
4+
use std::{collections::HashSet, sync::LazyLock, time::Duration};
5+
use types::{Checkpoint, Epoch, Hash256};
56

67
pub const DEFAULT_RE_ORG_HEAD_THRESHOLD: ReOrgThreshold = ReOrgThreshold(20);
78
pub const DEFAULT_RE_ORG_PARENT_THRESHOLD: ReOrgThreshold = ReOrgThreshold(160);
@@ -19,6 +20,12 @@ pub const FORK_CHOICE_LOOKAHEAD_FACTOR: u32 = 24;
1920
/// Default sync tolerance epochs.
2021
pub const DEFAULT_SYNC_TOLERANCE_EPOCHS: u64 = 2;
2122

23+
/// Invalid block root to be banned from processing and importing on Holesky network by default.
24+
pub static INVALID_HOLESKY_BLOCK_ROOT: LazyLock<Hash256> = LazyLock::new(|| {
25+
Hash256::from_str("2db899881ed8546476d0b92c6aa9110bea9a4cd0dbeb5519eb0ea69575f1f359")
26+
.expect("valid block root")
27+
});
28+
2229
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2330
pub struct ChainConfig {
2431
/// Maximum number of slots to skip when importing an attestation.
@@ -100,6 +107,11 @@ pub struct ChainConfig {
100107
/// The max distance between the head block and the current slot at which Lighthouse will
101108
/// consider itself synced and still serve validator-related requests.
102109
pub sync_tolerance_epochs: u64,
110+
/// Block roots of "banned" blocks which Lighthouse will refuse to import.
111+
///
112+
/// On Holesky there is a block which is added to this set by default but which can be removed
113+
/// by using `--invalid-block-roots ""`.
114+
pub invalid_block_roots: HashSet<Hash256>,
103115
}
104116

105117
impl Default for ChainConfig {
@@ -136,6 +148,7 @@ impl Default for ChainConfig {
136148
blob_publication_batches: 4,
137149
blob_publication_batch_interval: Duration::from_millis(300),
138150
sync_tolerance_epochs: DEFAULT_SYNC_TOLERANCE_EPOCHS,
151+
invalid_block_roots: HashSet::new(),
139152
}
140153
}
141154
}

beacon_node/network/src/network_beacon_processor/gossip_methods.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,6 +1457,7 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
14571457
| Err(e @ BlockError::InconsistentFork(_))
14581458
| Err(e @ BlockError::ExecutionPayloadError(_))
14591459
| Err(e @ BlockError::ParentExecutionPayloadInvalid { .. })
1460+
| Err(e @ BlockError::KnownInvalidExecutionPayload(_))
14601461
| Err(e @ BlockError::GenesisBlock) => {
14611462
warn!(self.log, "Could not verify block for gossip. Rejecting the block";
14621463
"error" => %e);

beacon_node/network/src/network_beacon_processor/sync_methods.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,18 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
800800
peer_action: Some(PeerAction::LowToleranceError),
801801
})
802802
}
803+
// Penalise peers for sending us banned blocks.
804+
BlockError::KnownInvalidExecutionPayload(block_root) => {
805+
warn!(
806+
self.log,
807+
"Received block known to be invalid";
808+
"block_root" => ?block_root,
809+
);
810+
Err(ChainSegmentFailed {
811+
message: format!("Banned block: {block_root:?}"),
812+
peer_action: Some(PeerAction::Fatal),
813+
})
814+
}
803815
other => {
804816
debug!(
805817
self.log, "Invalid block received";

beacon_node/src/cli.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,5 +1621,13 @@ pub fn cli_app() -> Command {
16211621
.action(ArgAction::Set)
16221622
.display_order(0)
16231623
)
1624+
.arg(
1625+
Arg::new("invalid-block-roots")
1626+
.long("invalid-block-roots")
1627+
.value_name("FILE")
1628+
.help("Path to a comma separated file containing block roots that should be treated as invalid during block verification.")
1629+
.action(ArgAction::Set)
1630+
.hide(true)
1631+
)
16241632
.group(ArgGroup::new("enable_http").args(["http", "gui", "staking"]).multiple(true))
16251633
}

beacon_node/src/config.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use account_utils::{read_input_from_user, STDIN_INPUTS_FLAG};
22
use beacon_chain::chain_config::{
33
DisallowedReOrgOffsets, ReOrgThreshold, DEFAULT_PREPARE_PAYLOAD_LOOKAHEAD_FACTOR,
44
DEFAULT_RE_ORG_HEAD_THRESHOLD, DEFAULT_RE_ORG_MAX_EPOCHS_SINCE_FINALIZATION,
5-
DEFAULT_RE_ORG_PARENT_THRESHOLD,
5+
DEFAULT_RE_ORG_PARENT_THRESHOLD, INVALID_HOLESKY_BLOCK_ROOT,
66
};
77
use beacon_chain::graffiti_calculator::GraffitiOrigin;
88
use beacon_chain::TrustedSetup;
@@ -20,9 +20,10 @@ use lighthouse_network::{multiaddr::Protocol, Enr, Multiaddr, NetworkConfig, Pee
2020
use sensitive_url::SensitiveUrl;
2121
use slog::{info, warn, Logger};
2222
use std::cmp::max;
23+
use std::collections::HashSet;
2324
use std::fmt::Debug;
2425
use std::fs;
25-
use std::io::IsTerminal;
26+
use std::io::{IsTerminal, Read};
2627
use std::net::Ipv6Addr;
2728
use std::net::{IpAddr, Ipv4Addr, ToSocketAddrs};
2829
use std::num::NonZeroU16;
@@ -903,6 +904,40 @@ pub fn get_config<E: EthSpec>(
903904
.max_gossip_aggregate_batch_size =
904905
clap_utils::parse_required(cli_args, "beacon-processor-aggregate-batch-size")?;
905906

907+
if let Some(invalid_block_roots_file_path) =
908+
clap_utils::parse_optional::<String>(cli_args, "invalid-block-roots")?
909+
{
910+
let mut file = std::fs::File::open(invalid_block_roots_file_path)
911+
.map_err(|e| format!("Failed to open invalid-block-roots file: {}", e))?;
912+
let mut contents = String::new();
913+
file.read_to_string(&mut contents)
914+
.map_err(|e| format!("Failed to read invalid-block-roots file {}", e))?;
915+
let invalid_block_roots: HashSet<Hash256> = contents
916+
.split(',')
917+
.filter_map(
918+
|s| match Hash256::from_str(s.strip_prefix("0x").unwrap_or(s).trim()) {
919+
Ok(block_root) => Some(block_root),
920+
Err(e) => {
921+
warn!(
922+
log,
923+
"Unable to parse invalid block root";
924+
"block_root" => s,
925+
"error" => ?e,
926+
);
927+
None
928+
}
929+
},
930+
)
931+
.collect();
932+
client_config.chain.invalid_block_roots = invalid_block_roots;
933+
} else if spec
934+
.config_name
935+
.as_ref()
936+
.is_some_and(|network_name| network_name == "holesky")
937+
{
938+
client_config.chain.invalid_block_roots = HashSet::from([*INVALID_HOLESKY_BLOCK_ROOT]);
939+
}
940+
906941
Ok(client_config)
907942
}
908943

lighthouse/tests/beacon_node.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2773,3 +2773,43 @@ fn beacon_node_backend_override() {
27732773
assert_eq!(config.store.backend, BeaconNodeBackend::LevelDb);
27742774
});
27752775
}
2776+
2777+
#[test]
2778+
fn invalid_block_roots_flag() {
2779+
let dir = TempDir::new().expect("Unable to create temporary directory");
2780+
let mut file =
2781+
File::create(dir.path().join("invalid-block-roots")).expect("Unable to create file");
2782+
file.write_all(b"2db899881ed8546476d0b92c6aa9110bea9a4cd0dbeb5519eb0ea69575f1f359, 2db899881ed8546476d0b92c6aa9110bea9a4cd0dbeb5519eb0ea69575f1f358, 0x3db899881ed8546476d0b92c6aa9110bea9a4cd0dbeb5519eb0ea69575f1f358")
2783+
.expect("Unable to write to file");
2784+
CommandLineTest::new()
2785+
.flag(
2786+
"invalid-block-roots",
2787+
dir.path().join("invalid-block-roots").as_os_str().to_str(),
2788+
)
2789+
.run_with_zero_port()
2790+
.with_config(|config| assert_eq!(config.chain.invalid_block_roots.len(), 3))
2791+
}
2792+
2793+
#[test]
2794+
fn invalid_block_roots_default_holesky() {
2795+
use beacon_node::beacon_chain::chain_config::INVALID_HOLESKY_BLOCK_ROOT;
2796+
CommandLineTest::new()
2797+
.flag("network", Some("holesky"))
2798+
.run_with_zero_port()
2799+
.with_config(|config| {
2800+
assert_eq!(config.chain.invalid_block_roots.len(), 1);
2801+
assert!(config
2802+
.chain
2803+
.invalid_block_roots
2804+
.contains(&*INVALID_HOLESKY_BLOCK_ROOT));
2805+
})
2806+
}
2807+
2808+
#[test]
2809+
fn invalid_block_roots_default_mainnet() {
2810+
CommandLineTest::new()
2811+
.run_with_zero_port()
2812+
.with_config(|config| {
2813+
assert!(config.chain.invalid_block_roots.is_empty());
2814+
})
2815+
}

0 commit comments

Comments
 (0)