Compare commits

...

17 Commits

Author SHA1 Message Date
Paul Hauner
dc320e3faa Bump version 2025-02-26 14:57:58 +11:00
Eitan Seri-Levi
e473500456 discard unused code 2025-02-25 14:20:12 -08:00
Pawan Dhananjay
627c1bac89 take config value 2025-02-25 10:37:29 -08:00
Pawan Dhananjay
0c5580f23f Bump sync-tolerance-epoch and make it a cli param 2025-02-25 09:45:51 -08:00
Michael Sproul
279afb0696 Add vc --disable-attesting flag 2025-02-26 00:36:33 +11:00
Michael Sproul
fd1ca8ef23 Ban peers with banned payloads 2025-02-25 23:30:11 +11:00
Jimmy Chen
df3038d902 Add log 2025-02-25 18:17:15 +11:00
Jimmy Chen
88c0f9d60a Blacklist invalid block root in block verification and blacklist invalid finalized epochs in sync. 2025-02-25 18:10:19 +11:00
Jimmy Chen
c61cf26622 Blacklist invalid block root in block verification and blacklist invalid finalized epochs in sync. 2025-02-25 18:08:44 +11:00
Michael Sproul
2883429f69 MORE 2025-02-25 16:44:42 +11:00
Michael Sproul
19fc31a75b Allow invalidation of "valid" nodes 2025-02-25 16:30:27 +11:00
Michael Sproul
ff2376efec Implement invalidation API 2025-02-25 15:42:25 +11:00
Michael Sproul
d4586ea92d Remove more liveness risks 2025-02-25 15:18:54 +11:00
Michael Sproul
1b9b61bb77 lcli http-sync hacks 2025-02-25 14:00:28 +11:00
Michael Sproul
11f17e52a0 Disable liveness risk 2025-02-25 12:36:13 +11:00
Michael Sproul
d472689fa2 Fix flag 2025-02-25 11:48:41 +11:00
Michael Sproul
bbc1200b2d Add flag to disable attestation APIs 2025-02-25 11:39:48 +11:00
24 changed files with 407 additions and 151 deletions

8
Cargo.lock generated
View File

@@ -860,7 +860,7 @@ dependencies = [
[[package]]
name = "beacon_node"
version = "7.0.0-beta.0"
version = "7.0.0-beta.1"
dependencies = [
"account_utils",
"beacon_chain",
@@ -1108,7 +1108,7 @@ dependencies = [
[[package]]
name = "boot_node"
version = "7.0.0-beta.0"
version = "7.0.0-beta.1"
dependencies = [
"beacon_node",
"bytes",
@@ -4811,7 +4811,7 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "lcli"
version = "7.0.0-beta.0"
version = "7.0.0-beta.1"
dependencies = [
"account_utils",
"beacon_chain",
@@ -5366,7 +5366,7 @@ dependencies = [
[[package]]
name = "lighthouse"
version = "7.0.0-beta.0"
version = "7.0.0-beta.1"
dependencies = [
"account_manager",
"account_utils",

View File

@@ -1,6 +1,6 @@
[package]
name = "beacon_node"
version = "7.0.0-beta.0"
version = "7.0.0-beta.1"
authors = [
"Paul Hauner <paul@paulhauner.com>",
"Age Manning <Age@AgeManning.com",

View File

@@ -4848,7 +4848,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let proposer_index = if let Some(proposer) = cached_proposer {
proposer.index as u64
} else {
if head_epoch + 2 < proposal_epoch {
if 2 + 2 == 5 && head_epoch + 2 < proposal_epoch {
warn!(
self.log,
"Skipping proposer preparation";
@@ -6089,8 +6089,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
//
// This prevents the routine from running during sync.
let head_slot = cached_head.head_slot();
if head_slot + T::EthSpec::slots_per_epoch() * PREPARE_PROPOSER_HISTORIC_EPOCHS
< current_slot
if 2 + 2 == 5
&& head_slot
+ T::EthSpec::slots_per_epoch() * PREPARE_PROPOSER_HISTORIC_EPOCHS
< current_slot
{
debug!(
chain.log,

View File

@@ -90,6 +90,7 @@ use std::borrow::Cow;
use std::fmt::Debug;
use std::fs;
use std::io::Write;
use std::str::FromStr;
use std::sync::Arc;
use store::{Error as DBError, HotStateSummary, KeyValueStore, StoreOp};
use strum::AsRefStr;
@@ -146,7 +147,9 @@ pub enum BlockError {
///
/// It's unclear if this block is valid, but it cannot be processed without already knowing
/// its parent.
ParentUnknown { parent_root: Hash256 },
ParentUnknown {
parent_root: Hash256,
},
/// The block slot is greater than the present slot.
///
/// ## Peer scoring
@@ -161,7 +164,10 @@ pub enum BlockError {
/// ## Peer scoring
///
/// The peer has incompatible state transition logic and is faulty.
StateRootMismatch { block: Hash256, local: Hash256 },
StateRootMismatch {
block: Hash256,
local: Hash256,
},
/// The block was a genesis block, these blocks cannot be re-imported.
GenesisBlock,
/// The slot is finalized, no need to import.
@@ -180,7 +186,9 @@ pub enum BlockError {
///
/// It's unclear if this block is valid, but it conflicts with finality and shouldn't be
/// imported.
NotFinalizedDescendant { block_parent_root: Hash256 },
NotFinalizedDescendant {
block_parent_root: Hash256,
},
/// Block is already known and valid, no need to re-import.
///
/// ## Peer scoring
@@ -207,7 +215,10 @@ pub enum BlockError {
/// ## Peer scoring
///
/// The block is invalid and the peer is faulty.
IncorrectBlockProposer { block: u64, local_shuffling: u64 },
IncorrectBlockProposer {
block: u64,
local_shuffling: u64,
},
/// The `block.proposal_index` is not known.
///
/// ## Peer scoring
@@ -225,7 +236,10 @@ pub enum BlockError {
/// ## Peer scoring
///
/// The block is invalid and the peer is faulty.
BlockIsNotLaterThanParent { block_slot: Slot, parent_slot: Slot },
BlockIsNotLaterThanParent {
block_slot: Slot,
parent_slot: Slot,
},
/// At least one block in the chain segment did not have it's parent root set to the root of
/// the prior block.
///
@@ -281,7 +295,10 @@ pub enum BlockError {
/// If it's actually our fault (e.g. our execution node database is corrupt) we have bigger
/// problems to worry about than losing peers, and we're doing the network a favour by
/// disconnecting.
ParentExecutionPayloadInvalid { parent_root: Hash256 },
ParentExecutionPayloadInvalid {
parent_root: Hash256,
},
KnownInvalidExecutionPayload(Hash256),
/// The block is a slashable equivocation from the proposer.
///
/// ## Peer scoring
@@ -1326,6 +1343,13 @@ impl<T: BeaconChainTypes> ExecutionPendingBlock<T> {
chain: &Arc<BeaconChain<T>>,
notify_execution_layer: NotifyExecutionLayer,
) -> Result<Self, BlockError> {
if block_root
== Hash256::from_str("2db899881ed8546476d0b92c6aa9110bea9a4cd0dbeb5519eb0ea69575f1f359")
.expect("valid hash")
{
return Err(BlockError::KnownInvalidExecutionPayload(block_root));
}
chain
.observed_slashable
.write()

View File

@@ -94,6 +94,8 @@ pub struct ChainConfig {
/// The delay in milliseconds applied by the node between sending each blob or data column batch.
/// This doesn't apply if the node is the block proposer.
pub blob_publication_batch_interval: Duration,
pub disable_attesting: bool,
pub sync_tolerance_epochs: u64,
}
impl Default for ChainConfig {
@@ -129,6 +131,8 @@ impl Default for ChainConfig {
enable_sampling: false,
blob_publication_batches: 4,
blob_publication_batch_interval: Duration::from_millis(300),
disable_attesting: false,
sync_tolerance_epochs: 16,
}
}
}

View File

@@ -46,11 +46,11 @@ tree_hash = { workspace = true }
types = { workspace = true }
warp = { workspace = true }
warp_utils = { workspace = true }
proto_array = { workspace = true }
[dev-dependencies]
genesis = { workspace = true }
logging = { workspace = true }
proto_array = { workspace = true }
serde_json = { workspace = true }
[[test]]

View File

@@ -107,13 +107,6 @@ use warp_utils::{query::multi_key_query, reject::convert_rejection, uor::Unifyin
const API_PREFIX: &str = "eth";
/// If the node is within this many epochs from the head, we declare it to be synced regardless of
/// the network sync state.
///
/// This helps prevent attacks where nodes can convince us that we're syncing some non-existent
/// finalized head.
const SYNC_TOLERANCE_EPOCHS: u64 = 8;
/// A custom type which allows for both unsecured and TLS-enabled HTTP servers.
type HttpServer = (SocketAddr, Pin<Box<dyn Future<Output = ()> + Send>>);
@@ -157,6 +150,7 @@ pub struct Config {
pub duplicate_block_status_code: StatusCode,
pub enable_light_client_server: bool,
pub target_peers: usize,
pub sync_tolerance_epochs: usize,
}
impl Default for Config {
@@ -173,6 +167,7 @@ impl Default for Config {
duplicate_block_status_code: StatusCode::ACCEPTED,
enable_light_client_server: true,
target_peers: 100,
sync_tolerance_epochs: 16,
}
}
}
@@ -473,7 +468,8 @@ pub fn serve<T: BeaconChainTypes>(
)
})?;
let tolerance = SYNC_TOLERANCE_EPOCHS * T::EthSpec::slots_per_epoch();
let tolerance =
chain.config.sync_tolerance_epochs * T::EthSpec::slots_per_epoch();
if head_slot + tolerance >= current_slot {
Ok(())
@@ -1859,6 +1855,13 @@ pub fn serve<T: BeaconChainTypes>(
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
reprocess_tx: Option<Sender<ReprocessQueueMessage>>,
log: Logger| async move {
if chain.config.disable_attesting {
return convert_rejection::<Response<String>>(Err(
warp_utils::reject::custom_bad_request("Attesting disabled".to_string()),
))
.await;
}
let attestations = attestations.into_iter().map(Either::Left).collect();
let result = crate::publish_attestations::publish_attestations(
task_spawner,
@@ -1891,6 +1894,12 @@ pub fn serve<T: BeaconChainTypes>(
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
reprocess_tx: Option<Sender<ReprocessQueueMessage>>,
log: Logger| async move {
if chain.config.disable_attesting {
return convert_rejection::<Response<String>>(Err(
warp_utils::reject::custom_bad_request("Attesting disabled".to_string()),
))
.await;
}
let attestations =
match crate::publish_attestations::deserialize_attestation_payload::<T>(
payload, fork_name, &log,
@@ -1937,49 +1946,66 @@ pub fn serve<T: BeaconChainTypes>(
task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>,
query: api_types::AttestationPoolQuery| {
task_spawner.blocking_response_task(Priority::P1, move || {
let query_filter = |data: &AttestationData| {
query.slot.is_none_or(|slot| slot == data.slot)
&& query
.committee_index
.is_none_or(|index| index == data.index)
};
async move {
if chain.config.disable_attesting {
return convert_rejection::<Response<String>>(Err(
warp_utils::reject::custom_bad_request(
"Attesting disabled".to_string(),
),
))
.await;
}
task_spawner
.blocking_response_task(Priority::P1, move || {
let query_filter = |data: &AttestationData| {
query.slot.is_none_or(|slot| slot == data.slot)
&& query
.committee_index
.is_none_or(|index| index == data.index)
};
let mut attestations = chain.op_pool.get_filtered_attestations(query_filter);
attestations.extend(
chain
.naive_aggregation_pool
.read()
.iter()
.filter(|&att| query_filter(att.data()))
.cloned(),
);
// Use the current slot to find the fork version, and convert all messages to the
// current fork's format. This is to ensure consistent message types matching
// `Eth-Consensus-Version`.
let current_slot =
chain
.slot_clock
.now()
.ok_or(warp_utils::reject::custom_server_error(
"unable to read slot clock".to_string(),
))?;
let fork_name = chain.spec.fork_name_at_slot::<T::EthSpec>(current_slot);
let attestations = attestations
.into_iter()
.filter(|att| {
(fork_name.electra_enabled() && matches!(att, Attestation::Electra(_)))
|| (!fork_name.electra_enabled()
&& matches!(att, Attestation::Base(_)))
let mut attestations =
chain.op_pool.get_filtered_attestations(query_filter);
attestations.extend(
chain
.naive_aggregation_pool
.read()
.iter()
.filter(|&att| query_filter(att.data()))
.cloned(),
);
// Use the current slot to find the fork version, and convert all messages to the
// current fork's format. This is to ensure consistent message types matching
// `Eth-Consensus-Version`.
let current_slot = chain.slot_clock.now().ok_or(
warp_utils::reject::custom_server_error(
"unable to read slot clock".to_string(),
),
)?;
let fork_name =
chain.spec.fork_name_at_slot::<T::EthSpec>(current_slot);
let attestations = attestations
.into_iter()
.filter(|att| {
(fork_name.electra_enabled()
&& matches!(att, Attestation::Electra(_)))
|| (!fork_name.electra_enabled()
&& matches!(att, Attestation::Base(_)))
})
.collect::<Vec<_>>();
let res = fork_versioned_response(
endpoint_version,
fork_name,
&attestations,
)?;
Ok(add_consensus_version_header(
warp::reply::json(&res).into_response(),
fork_name,
))
})
.collect::<Vec<_>>();
let res = fork_versioned_response(endpoint_version, fork_name, &attestations)?;
Ok(add_consensus_version_header(
warp::reply::json(&res).into_response(),
fork_name,
))
})
.await
}
},
);
@@ -2200,12 +2226,24 @@ pub fn serve<T: BeaconChainTypes>(
signatures: Vec<SyncCommitteeMessage>,
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
log: Logger| {
task_spawner.blocking_json_task(Priority::P0, move || {
sync_committees::process_sync_committee_signatures(
signatures, network_tx, &chain, log,
)?;
Ok(api_types::GenericResponse::from(()))
})
async move {
if chain.config.disable_attesting {
return convert_rejection::<Response<String>>(Err(
warp_utils::reject::custom_bad_request(
"Attesting disabled".to_string(),
),
))
.await;
}
task_spawner
.blocking_json_task(Priority::P0, move || {
sync_committees::process_sync_committee_signatures(
signatures, network_tx, &chain, log,
)?;
Ok(api_types::GenericResponse::from(()))
})
.await
}
},
);
@@ -3419,10 +3457,22 @@ pub fn serve<T: BeaconChainTypes>(
indices: api_types::ValidatorIndexData,
task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>| {
task_spawner.blocking_json_task(Priority::P0, move || {
not_synced_filter?;
attester_duties::attester_duties(epoch, &indices.0, &chain)
})
async move {
if chain.config.disable_attesting {
return convert_rejection::<Response<String>>(Err(
warp_utils::reject::custom_bad_request(
"Attesting disabled".to_string(),
),
))
.await;
}
task_spawner
.blocking_json_task(Priority::P0, move || {
not_synced_filter?;
attester_duties::attester_duties(epoch, &indices.0, &chain)
})
.await
}
},
);
@@ -3447,10 +3497,22 @@ pub fn serve<T: BeaconChainTypes>(
indices: api_types::ValidatorIndexData,
task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>| {
task_spawner.blocking_json_task(Priority::P0, move || {
not_synced_filter?;
sync_committees::sync_committee_duties(epoch, &indices.0, &chain)
})
async move {
if chain.config.disable_attesting {
return convert_rejection::<Response<String>>(Err(
warp_utils::reject::custom_bad_request(
"Attesting disabled".to_string(),
),
))
.await;
}
task_spawner
.blocking_json_task(Priority::P0, move || {
not_synced_filter?;
sync_committees::sync_committee_duties(epoch, &indices.0, &chain)
})
.await
}
},
);
@@ -3468,23 +3530,35 @@ pub fn serve<T: BeaconChainTypes>(
not_synced_filter: Result<(), Rejection>,
task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>| {
task_spawner.blocking_json_task(Priority::P0, move || {
not_synced_filter?;
chain
.get_aggregated_sync_committee_contribution(&sync_committee_data)
.map_err(|e| {
warp_utils::reject::custom_bad_request(format!(
"unable to fetch sync contribution: {:?}",
e
))
})?
.map(api_types::GenericResponse::from)
.ok_or_else(|| {
warp_utils::reject::custom_not_found(
"no matching sync contribution found".to_string(),
)
async move {
if chain.config.disable_attesting {
return convert_rejection::<Response<String>>(Err(
warp_utils::reject::custom_bad_request(
"Attesting disabled".to_string(),
),
))
.await;
}
task_spawner
.blocking_json_task(Priority::P0, move || {
not_synced_filter?;
chain
.get_aggregated_sync_committee_contribution(&sync_committee_data)
.map_err(|e| {
warp_utils::reject::custom_bad_request(format!(
"unable to fetch sync contribution: {:?}",
e
))
})?
.map(api_types::GenericResponse::from)
.ok_or_else(|| {
warp_utils::reject::custom_not_found(
"no matching sync contribution found".to_string(),
)
})
})
})
.await
}
},
);
@@ -3509,6 +3583,15 @@ pub fn serve<T: BeaconChainTypes>(
chain: Arc<BeaconChain<T>>,
aggregates: Vec<SignedAggregateAndProof<T::EthSpec>>,
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>, log: Logger| {
async move {
if chain.config.disable_attesting {
return convert_rejection::<Response<String>>(Err(
warp_utils::reject::custom_bad_request(
"Attesting disabled".to_string(),
),
))
.await;
}
task_spawner.blocking_json_task(Priority::P0, move || {
not_synced_filter?;
let seen_timestamp = timestamp_now();
@@ -3604,7 +3687,8 @@ pub fn serve<T: BeaconChainTypes>(
} else {
Ok(())
}
})
}).await
}
},
);
@@ -3625,36 +3709,58 @@ pub fn serve<T: BeaconChainTypes>(
contributions: Vec<SignedContributionAndProof<T::EthSpec>>,
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
log: Logger| {
task_spawner.blocking_json_task(Priority::P0, move || {
not_synced_filter?;
sync_committees::process_signed_contribution_and_proofs(
contributions,
network_tx,
&chain,
log,
)?;
Ok(api_types::GenericResponse::from(()))
})
async move {
if chain.config.disable_attesting {
return convert_rejection::<Response<String>>(Err(
warp_utils::reject::custom_bad_request(
"Attesting disabled".to_string(),
),
))
.await;
}
task_spawner
.blocking_json_task(Priority::P0, move || {
not_synced_filter?;
sync_committees::process_signed_contribution_and_proofs(
contributions,
network_tx,
&chain,
log,
)?;
Ok(api_types::GenericResponse::from(()))
})
.await
}
},
);
// POST validator/beacon_committee_subscriptions
let post_validator_beacon_committee_subscriptions = eth_v1
.and(warp::path("validator"))
.and(warp::path("beacon_committee_subscriptions"))
.and(warp::path::end())
.and(warp_utils::json::json())
.and(validator_subscription_tx_filter.clone())
.and(task_spawner_filter.clone())
.and(chain_filter.clone())
.and(log_filter.clone())
.then(
|subscriptions: Vec<api_types::BeaconCommitteeSubscription>,
validator_subscription_tx: Sender<ValidatorSubscriptionMessage>,
task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>,
log: Logger| {
task_spawner.blocking_json_task(Priority::P0, move || {
let post_validator_beacon_committee_subscriptions =
eth_v1
.and(warp::path("validator"))
.and(warp::path("beacon_committee_subscriptions"))
.and(warp::path::end())
.and(warp_utils::json::json())
.and(validator_subscription_tx_filter.clone())
.and(task_spawner_filter.clone())
.and(chain_filter.clone())
.and(log_filter.clone())
.then(
|subscriptions: Vec<api_types::BeaconCommitteeSubscription>,
validator_subscription_tx: Sender<ValidatorSubscriptionMessage>,
task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>,
log: Logger| {
async move {
if chain.config.disable_attesting {
return convert_rejection::<Response<String>>(Err(
warp_utils::reject::custom_bad_request(
"Attesting disabled".to_string(),
),
))
.await;
}
task_spawner.blocking_json_task(Priority::P0, move || {
let subscriptions: std::collections::BTreeSet<_> = subscriptions
.iter()
.map(|subscription| {
@@ -3686,9 +3792,10 @@ pub fn serve<T: BeaconChainTypes>(
}
Ok(())
})
},
);
}).await
}
},
);
// POST validator/prepare_beacon_proposer
let post_validator_prepare_beacon_proposer = eth_v1
@@ -3945,6 +4052,15 @@ pub fn serve<T: BeaconChainTypes>(
chain: Arc<BeaconChain<T>>,
log: Logger
| {
async move {
if chain.config.disable_attesting {
return convert_rejection::<Response<String>>(Err(
warp_utils::reject::custom_bad_request(
"Attesting disabled".to_string(),
),
))
.await;
}
task_spawner.blocking_json_task(Priority::P0, move || {
for subscription in subscriptions {
chain
@@ -3969,7 +4085,8 @@ pub fn serve<T: BeaconChainTypes>(
}
Ok(())
})
}).await
}
},
);
@@ -4422,6 +4539,34 @@ pub fn serve<T: BeaconChainTypes>(
},
);
let post_lighthouse_fork_choice_invalidate = warp::path("lighthouse")
.and(warp::path("fork_choice"))
.and(warp::path("invalidate"))
.and(warp::path::end())
.and(task_spawner_filter.clone())
.and(chain_filter.clone())
.and(warp_utils::json::json())
.then(
|task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>,
block_root: Hash256| {
task_spawner.blocking_json_task(Priority::P0, move || {
let invalidation =
proto_array::InvalidationOperation::InvalidateOne { block_root };
chain
.canonical_head
.fork_choice_write_lock()
.on_invalid_execution_payload(&invalidation)
.map_err(|e| {
warp_utils::reject::custom_server_error(format!(
"not invalidated due to error: {e:?}"
))
})?;
Ok("invalidated")
})
},
);
// GET lighthouse/analysis/block_rewards
let get_lighthouse_block_rewards = warp::path("lighthouse")
.and(warp::path("analysis"))
@@ -4783,6 +4928,7 @@ pub fn serve<T: BeaconChainTypes>(
.uor(post_validator_liveness_epoch)
.uor(post_lighthouse_liveness)
.uor(post_lighthouse_database_reconstruct)
.uor(post_lighthouse_fork_choice_invalidate)
.uor(post_lighthouse_block_rewards)
.uor(post_lighthouse_ui_validator_metrics)
.uor(post_lighthouse_ui_validator_info)

View File

@@ -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);

View File

@@ -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::LowToleranceError),
})
}
other => {
debug!(
self.log, "Invalid block received";

View File

@@ -553,6 +553,22 @@ pub fn cli_app() -> Command {
.action(ArgAction::Set)
.display_order(0)
)
.arg(
Arg::new("disable-attesting")
.long("disable-attesting")
.help("Turn off attestation related APIs so that we have some hope of producing \
blocks")
.action(ArgAction::SetTrue)
.display_order(0)
)
.arg(
Arg::new("sync-tolerance-epochs")
.long("sync-tolerance-epochs")
.help("If the beacon node is within this many epochs from the head, we declare it to \
be synced regardless of the network sync state")
.action(ArgAction::Set)
.display_order(0)
)
.arg(
Arg::new("http-sse-capacity-multiplier")
.long("http-sse-capacity-multiplier")

View File

@@ -191,6 +191,16 @@ pub fn get_config<E: EthSpec>(
client_config.chain.enable_light_client_server = false;
}
if cli_args.get_flag("disable-attesting") {
client_config.chain.disable_attesting = true;
}
if let Some(sync_tolerance_epochs) =
clap_utils::parse_optional(cli_args, "sync-tolerance-epochs")?
{
client_config.chain.sync_tolerance_epochs = sync_tolerance_epochs;
}
if let Some(cache_size) = clap_utils::parse_optional(cli_args, "shuffling-cache-size")? {
client_config.chain.shuffling_cache_size = cache_size;
}

View File

@@ -1,6 +1,6 @@
[package]
name = "boot_node"
version = "7.0.0-beta.0"
version = "7.0.0-beta.1"
authors = ["Sigma Prime <contact@sigmaprime.io>"]
edition = { workspace = true }

View File

@@ -17,8 +17,8 @@ pub const VERSION: &str = git_version!(
// NOTE: using --match instead of --exclude for compatibility with old Git
"--match=thiswillnevermatchlol"
],
prefix = "Lighthouse/v7.0.0-beta.0-",
fallback = "Lighthouse/v7.0.0-beta.0"
prefix = "Lighthouse/v7.0.0-beta.1-",
fallback = "Lighthouse/v7.0.0-beta.1"
);
/// Returns the first eight characters of the latest commit hash for this build.
@@ -54,7 +54,7 @@ pub fn version_with_platform() -> String {
///
/// `1.5.1`
pub fn version() -> &'static str {
"7.0.0-beta.0"
"7.0.0-beta.1"
}
/// Returns the name of the current client running.

View File

@@ -531,15 +531,7 @@ impl ProtoArray {
|| latest_valid_ancestor_is_descendant
{
match &node.execution_status {
// It's illegal for an execution client to declare that some previously-valid block
// is now invalid. This is a consensus failure on their behalf.
ExecutionStatus::Valid(hash) => {
return Err(Error::ValidExecutionStatusBecameInvalid {
block_root: node.root,
payload_block_hash: *hash,
})
}
ExecutionStatus::Optimistic(hash) => {
ExecutionStatus::Valid(hash) | ExecutionStatus::Optimistic(hash) => {
invalidated_indices.insert(index);
node.execution_status = ExecutionStatus::Invalid(*hash);
@@ -597,13 +589,9 @@ impl ProtoArray {
if let Some(parent_index) = node.parent {
if invalidated_indices.contains(&parent_index) {
match &node.execution_status {
ExecutionStatus::Valid(hash) => {
return Err(Error::ValidExecutionStatusBecameInvalid {
block_root: node.root,
payload_block_hash: *hash,
})
}
ExecutionStatus::Optimistic(hash) | ExecutionStatus::Invalid(hash) => {
ExecutionStatus::Valid(hash)
| ExecutionStatus::Optimistic(hash)
| ExecutionStatus::Invalid(hash) => {
node.execution_status = ExecutionStatus::Invalid(*hash)
}
ExecutionStatus::Irrelevant(_) => {

View File

@@ -1,7 +1,7 @@
[package]
name = "lcli"
description = "Lighthouse CLI (modeled after zcli)"
version = "7.0.0-beta.0"
version = "7.0.0-beta.1"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = { workspace = true }

View File

@@ -2,7 +2,9 @@ use clap::ArgMatches;
use clap_utils::{parse_optional, parse_required};
use environment::Environment;
use eth2::{
types::{BlockId, ChainSpec, ForkName, PublishBlockRequest, SignedBlockContents},
types::{
BlockId, BroadcastValidation, ChainSpec, ForkName, PublishBlockRequest, SignedBlockContents,
},
BeaconNodeHttpClient, Error, SensitiveUrl, Timeouts,
};
use eth2_network_config::Eth2NetworkConfig;
@@ -85,7 +87,10 @@ pub async fn run_async<T: EthSpec>(
// 2. Apply blocks to target.
for (slot, block) in blocks.iter().rev() {
println!("posting block at slot {slot}");
if let Err(e) = target.post_beacon_blocks(block).await {
if let Err(e) = target
.post_beacon_blocks_v2(block, Some(BroadcastValidation::Consensus))
.await
{
if let Error::ServerMessage(ref e) = e {
if e.code == 202 {
println!("duplicate block detected while posting block at slot {slot}");
@@ -115,7 +120,7 @@ async fn get_block_from_source<T: EthSpec>(
let mut f = File::open(&cache_path).unwrap();
let mut bytes = vec![];
f.read_to_end(&mut bytes).unwrap();
PublishBlockRequest::from_ssz_bytes(&bytes, ForkName::Deneb).unwrap()
PublishBlockRequest::from_ssz_bytes(&bytes, ForkName::Electra).unwrap()
} else {
let block_from_source = source
.get_beacon_blocks_ssz::<T>(block_id, spec)

View File

@@ -1,6 +1,6 @@
[package]
name = "lighthouse"
version = "7.0.0-beta.0"
version = "7.0.0-beta.1"
authors = ["Sigma Prime <contact@sigmaprime.io>"]
edition = { workspace = true }
autotests = false

View File

@@ -2579,6 +2579,16 @@ fn light_client_http_server_disabled() {
});
}
#[test]
fn disable_attesting() {
CommandLineTest::new()
.flag("disable-attesting", None)
.run_with_zero_port()
.with_config(|config| {
assert!(config.chain.disable_attesting);
});
}
#[test]
fn gui_flag() {
CommandLineTest::new()

View File

@@ -97,6 +97,14 @@ pub struct ValidatorClient {
)]
pub disable_auto_discover: bool,
#[clap(
long,
help = "Disable everything except block proposals",
display_order = 0,
help_heading = FLAG_HEADER
)]
pub disable_attesting: bool,
#[clap(
long,
help = "If present, the validator client will use longer timeouts for requests \

View File

@@ -85,6 +85,7 @@ pub struct Config {
/// Configuration for the initialized validators
#[serde(flatten)]
pub initialized_validators: InitializedValidatorsConfig,
pub disable_attesting: bool,
}
impl Default for Config {
@@ -126,6 +127,7 @@ impl Default for Config {
validator_registration_batch_size: 500,
distributed: false,
initialized_validators: <_>::default(),
disable_attesting: false,
}
}
}
@@ -379,6 +381,8 @@ impl Config {
true
};
config.disable_attesting = validator_client_config.disable_attesting;
Ok(config)
}
}

View File

@@ -325,7 +325,7 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
get_validator_block: slot_duration / HTTP_GET_VALIDATOR_BLOCK_TIMEOUT_QUOTIENT,
}
} else {
Timeouts::set_all(slot_duration)
Timeouts::set_all(25 * slot_duration)
};
Ok(BeaconNodeHttpClient::from_components(
@@ -478,6 +478,7 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
context: duties_context,
enable_high_validator_count_metrics: config.enable_high_validator_count_metrics,
distributed: config.distributed,
disable_attesting: config.disable_attesting,
});
// Update the metrics server.
@@ -507,6 +508,7 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
.validator_store(validator_store.clone())
.beacon_nodes(beacon_nodes.clone())
.runtime_context(context.service_context("attestation".into()))
.disable(config.disable_attesting)
.build()?;
let preparation_service = PreparationServiceBuilder::new()

View File

@@ -21,6 +21,7 @@ pub struct AttestationServiceBuilder<T: SlotClock + 'static, E: EthSpec> {
slot_clock: Option<T>,
beacon_nodes: Option<Arc<BeaconNodeFallback<T, E>>>,
context: Option<RuntimeContext<E>>,
disable: bool,
}
impl<T: SlotClock + 'static, E: EthSpec> AttestationServiceBuilder<T, E> {
@@ -31,6 +32,7 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationServiceBuilder<T, E> {
slot_clock: None,
beacon_nodes: None,
context: None,
disable: false,
}
}
@@ -59,6 +61,11 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationServiceBuilder<T, E> {
self
}
pub fn disable(mut self, disable: bool) -> Self {
self.disable = disable;
self
}
pub fn build(self) -> Result<AttestationService<T, E>, String> {
Ok(AttestationService {
inner: Arc::new(Inner {
@@ -77,6 +84,7 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationServiceBuilder<T, E> {
context: self
.context
.ok_or("Cannot build AttestationService without runtime_context")?,
disable: self.disable,
}),
})
}
@@ -89,6 +97,7 @@ pub struct Inner<T, E: EthSpec> {
slot_clock: T,
beacon_nodes: Arc<BeaconNodeFallback<T, E>>,
context: RuntimeContext<E>,
disable: bool,
}
/// Attempts to produce attestations for all known validators 1/3rd of the way through each slot.
@@ -120,6 +129,10 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationService<T, E> {
/// Starts the service which periodically produces attestations.
pub fn start_update_service(self, spec: &ChainSpec) -> Result<(), String> {
let log = self.context.log().clone();
if self.disable {
info!(log, "Attestation service disabled");
return Ok(());
}
let slot_duration = Duration::from_secs(spec.seconds_per_slot);
let duration_to_next_slot = self

View File

@@ -230,6 +230,7 @@ pub struct DutiesService<T, E: EthSpec> {
pub enable_high_validator_count_metrics: bool,
/// If this validator is running in distributed mode.
pub distributed: bool,
pub disable_attesting: bool,
}
impl<T: SlotClock + 'static, E: EthSpec> DutiesService<T, E> {
@@ -403,6 +404,11 @@ pub fn start_update_service<T: SlotClock + 'static, E: EthSpec>(
"duties_service_proposers",
);
// Skip starting attestation duties or sync committee services.
if core_duties_service.disable_attesting {
return;
}
/*
* Spawn the task which keeps track of local attestation duties.
*/

View File

@@ -87,6 +87,11 @@ impl<T: SlotClock + 'static, E: EthSpec> SyncCommitteeService<T, E> {
pub fn start_update_service(self, spec: &ChainSpec) -> Result<(), String> {
let log = self.context.log().clone();
if self.duties_service.disable_attesting {
info!(log, "Sync committee service disabled");
return Ok(());
}
let slot_duration = Duration::from_secs(spec.seconds_per_slot);
let duration_to_next_slot = self
.slot_clock