mirror of
https://github.com/sigp/lighthouse.git
synced 2026-07-02 04:14:33 +00:00
Mallory - Single commit
This commit is contained in:
@@ -843,6 +843,7 @@ impl<'de, E: EthSpec, Payload: AbstractExecPayload<E>> ContextDeserialize<'de, F
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum BlockImportSource {
|
||||
Gossip,
|
||||
Lookup,
|
||||
|
||||
@@ -25,6 +25,7 @@ pub struct ForkVersionedResponse<T, M = EmptyMetadata> {
|
||||
/// `Deserialize`.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize)]
|
||||
pub struct UnversionedResponse<T, M = EmptyMetadata> {
|
||||
#[serde(flatten)]
|
||||
pub metadata: M,
|
||||
pub data: T,
|
||||
}
|
||||
@@ -195,9 +196,10 @@ impl<T, M> From<UnversionedResponse<T, M>> for BeaconResponse<T, M> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod fork_version_response_tests {
|
||||
use crate::beacon_response::ExecutionOptimisticFinalizedMetadata;
|
||||
use crate::{
|
||||
ExecutionPayload, ExecutionPayloadBellatrix, ForkName, ForkVersionedResponse,
|
||||
MainnetEthSpec,
|
||||
MainnetEthSpec, UnversionedResponse,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
@@ -236,4 +238,24 @@ mod fork_version_response_tests {
|
||||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// The following test should only pass by having the attribute #[serde(flatten)] on the metadata
|
||||
#[test]
|
||||
fn unversioned_response_serialize_dezerialize_round_trip_test() {
|
||||
// Create an UnversionedResponse with some data
|
||||
let data = UnversionedResponse {
|
||||
metadata: ExecutionOptimisticFinalizedMetadata {
|
||||
execution_optimistic: Some(false),
|
||||
finalized: Some(false),
|
||||
},
|
||||
data: "some_test_data".to_string(),
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&data);
|
||||
|
||||
let deserialized =
|
||||
serde_json::from_str(&serialized.unwrap()).expect("Failed to deserialize");
|
||||
|
||||
assert_eq!(data, deserialized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +173,21 @@ pub enum Error {
|
||||
AggregatorNotInCommittee {
|
||||
aggregator_index: u64,
|
||||
},
|
||||
PleaseNotifyTheDevs(String),
|
||||
ComputeProposerIndicesPastEpoch {
|
||||
current_epoch: Epoch,
|
||||
request_epoch: Epoch,
|
||||
},
|
||||
ComputeProposerIndicesInsufficientLookahead {
|
||||
current_epoch: Epoch,
|
||||
request_epoch: Epoch,
|
||||
},
|
||||
ComputeProposerIndicesExcessiveLookahead {
|
||||
current_epoch: Epoch,
|
||||
request_epoch: Epoch,
|
||||
},
|
||||
ProposerLookaheadOutOfBounds {
|
||||
i: usize,
|
||||
},
|
||||
}
|
||||
|
||||
/// Control whether an epoch-indexed field can be indexed at the next epoch or not.
|
||||
@@ -578,6 +592,7 @@ where
|
||||
#[compare_fields(as_iter)]
|
||||
#[test_random(default)]
|
||||
#[superstruct(only(Fulu, Gloas))]
|
||||
#[serde(with = "ssz_types::serde_utils::quoted_u64_fixed_vec")]
|
||||
pub proposer_lookahead: Vector<u64, E::ProposerLookaheadSlots>,
|
||||
|
||||
// Gloas
|
||||
@@ -886,8 +901,9 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
block_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Hash256, Error> {
|
||||
let decision_slot = self.proposer_shuffling_decision_slot(epoch);
|
||||
let decision_slot = spec.proposer_shuffling_decision_slot::<E>(epoch);
|
||||
if self.slot() <= decision_slot {
|
||||
Ok(block_root)
|
||||
} else {
|
||||
@@ -902,19 +918,18 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
///
|
||||
/// The `block_root` covers the one-off scenario where the genesis block decides its own
|
||||
/// shuffling. It should be set to the latest block applied to `self` or the genesis block root.
|
||||
pub fn proposer_shuffling_decision_root(&self, block_root: Hash256) -> Result<Hash256, Error> {
|
||||
let decision_slot = self.proposer_shuffling_decision_slot(self.current_epoch());
|
||||
if self.slot() == decision_slot {
|
||||
Ok(block_root)
|
||||
} else {
|
||||
self.get_block_root(decision_slot).copied()
|
||||
}
|
||||
pub fn proposer_shuffling_decision_root(
|
||||
&self,
|
||||
block_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Hash256, Error> {
|
||||
self.proposer_shuffling_decision_root_at_epoch(self.current_epoch(), block_root, spec)
|
||||
}
|
||||
|
||||
/// Returns the slot at which the proposer shuffling was decided. The block root at this slot
|
||||
/// can be used to key the proposer shuffling for the given epoch.
|
||||
fn proposer_shuffling_decision_slot(&self, epoch: Epoch) -> Slot {
|
||||
epoch.start_slot(E::slots_per_epoch()).saturating_sub(1_u64)
|
||||
pub fn epoch_cache_decision_root(&self, block_root: Hash256) -> Result<Hash256, Error> {
|
||||
// Epoch cache decision root for the current epoch (N) is the block root at the end of epoch
|
||||
// N - 1. This is the same as the root that determines the next epoch attester shuffling.
|
||||
self.attester_shuffling_decision_root(block_root, RelativeEpoch::Next)
|
||||
}
|
||||
|
||||
/// Returns the block root which decided the attester shuffling for the given `relative_epoch`.
|
||||
@@ -998,6 +1013,45 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
indices: &[usize],
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Vec<usize>, Error> {
|
||||
// Regardless of fork, we never support computing proposer indices for past epochs.
|
||||
let current_epoch = self.current_epoch();
|
||||
if epoch < current_epoch {
|
||||
return Err(Error::ComputeProposerIndicesPastEpoch {
|
||||
current_epoch,
|
||||
request_epoch: epoch,
|
||||
});
|
||||
}
|
||||
|
||||
if spec.fork_name_at_epoch(epoch).fulu_enabled() {
|
||||
// Post-Fulu we must never compute proposer indices using insufficient lookahead. This
|
||||
// would be very dangerous as it would lead to conflicts between the *true* proposer as
|
||||
// defined by `self.proposer_lookahead` and the output of this function.
|
||||
// With MIN_SEED_LOOKAHEAD=1 (common config), this is equivalent to checking that the
|
||||
// requested epoch is not the current epoch.
|
||||
//
|
||||
// We do not run this check if this function is called from `upgrade_to_fulu`,
|
||||
// which runs *after* the slot is incremented, and needs to compute the proposer
|
||||
// shuffling for the epoch that was just transitioned into.
|
||||
if self.fork_name_unchecked().fulu_enabled()
|
||||
&& epoch < current_epoch.safe_add(spec.min_seed_lookahead)?
|
||||
{
|
||||
return Err(Error::ComputeProposerIndicesInsufficientLookahead {
|
||||
current_epoch,
|
||||
request_epoch: epoch,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Pre-Fulu the situation is reversed, we *should not* compute proposer indices using
|
||||
// too much lookahead. To do so would make us vulnerable to changes in the proposer
|
||||
// indices caused by effective balance changes.
|
||||
if epoch >= current_epoch.safe_add(spec.min_seed_lookahead)? {
|
||||
return Err(Error::ComputeProposerIndicesExcessiveLookahead {
|
||||
current_epoch,
|
||||
request_epoch: epoch,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
epoch
|
||||
.slot_iter(E::slots_per_epoch())
|
||||
.map(|slot| {
|
||||
@@ -1146,10 +1200,7 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
let index = slot.as_usize().safe_rem(E::slots_per_epoch() as usize)?;
|
||||
proposer_lookahead
|
||||
.get(index)
|
||||
.ok_or(Error::PleaseNotifyTheDevs(format!(
|
||||
"Proposer lookahead out of bounds: {} for slot: {}",
|
||||
index, slot
|
||||
)))
|
||||
.ok_or(Error::ProposerLookaheadOutOfBounds { i: index })
|
||||
.map(|index| *index as usize)
|
||||
} else {
|
||||
// Pre-Fulu
|
||||
@@ -1168,6 +1219,25 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Vec<usize>, Error> {
|
||||
// This isn't in the spec, but we remove the footgun that is requesting the current epoch
|
||||
// for a Fulu state.
|
||||
if let Ok(proposer_lookahead) = self.proposer_lookahead()
|
||||
&& epoch >= self.current_epoch()
|
||||
&& epoch <= self.next_epoch()?
|
||||
{
|
||||
let slots_per_epoch = E::slots_per_epoch() as usize;
|
||||
let start_offset = if epoch == self.current_epoch() {
|
||||
0
|
||||
} else {
|
||||
slots_per_epoch
|
||||
};
|
||||
return Ok(proposer_lookahead
|
||||
.iter_from(start_offset)?
|
||||
.take(slots_per_epoch)
|
||||
.map(|x| *x as usize)
|
||||
.collect());
|
||||
}
|
||||
|
||||
// Not using the cached validator indices since they are shuffled.
|
||||
let indices = self.get_active_validator_indices(epoch, spec)?;
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ pub struct ChainSpec {
|
||||
pub ttfb_timeout: u64,
|
||||
pub resp_timeout: u64,
|
||||
pub attestation_propagation_slot_range: u64,
|
||||
pub maximum_gossip_clock_disparity_millis: u64,
|
||||
pub maximum_gossip_clock_disparity: u64,
|
||||
pub message_domain_invalid_snappy: [u8; 4],
|
||||
pub message_domain_valid_snappy: [u8; 4],
|
||||
pub subnets_per_node: u8,
|
||||
@@ -670,7 +670,7 @@ impl ChainSpec {
|
||||
}
|
||||
|
||||
pub fn maximum_gossip_clock_disparity(&self) -> Duration {
|
||||
Duration::from_millis(self.maximum_gossip_clock_disparity_millis)
|
||||
Duration::from_millis(self.maximum_gossip_clock_disparity)
|
||||
}
|
||||
|
||||
pub fn ttfb_timeout(&self) -> Duration {
|
||||
@@ -865,6 +865,34 @@ impl ChainSpec {
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the slot at which the proposer shuffling was decided.
|
||||
///
|
||||
/// The block root at this slot can be used to key the proposer shuffling for the given epoch.
|
||||
pub fn proposer_shuffling_decision_slot<E: EthSpec>(&self, epoch: Epoch) -> Slot {
|
||||
// At the Fulu fork epoch itself, the shuffling is computed "the old way" with no lookahead.
|
||||
// Therefore for `epoch == fulu_fork_epoch` we must take the `else` branch. Checking if Fulu
|
||||
// is enabled at `epoch - 1` accomplishes this neatly.
|
||||
if self
|
||||
.fork_name_at_epoch(epoch.saturating_sub(1_u64))
|
||||
.fulu_enabled()
|
||||
{
|
||||
// Post-Fulu the proposer shuffling decision slot for epoch N is the slot at the end
|
||||
// of epoch N - 2 (note: min_seed_lookahead=1 in all current configs).
|
||||
epoch
|
||||
.saturating_sub(self.min_seed_lookahead)
|
||||
.start_slot(E::slots_per_epoch())
|
||||
.saturating_sub(1_u64)
|
||||
} else {
|
||||
// Pre-Fulu the proposer shuffling decision slot for epoch N is the slot at the end of
|
||||
// epoch N - 1 (note: +1 -1 for min_seed_lookahead=1 in all current configs).
|
||||
epoch
|
||||
.saturating_add(Epoch::new(1))
|
||||
.saturating_sub(self.min_seed_lookahead)
|
||||
.start_slot(E::slots_per_epoch())
|
||||
.saturating_sub(1_u64)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a `ChainSpec` compatible with the Ethereum Foundation specification.
|
||||
pub fn mainnet() -> Self {
|
||||
Self {
|
||||
@@ -1084,7 +1112,7 @@ impl ChainSpec {
|
||||
attestation_propagation_slot_range: default_attestation_propagation_slot_range(),
|
||||
attestation_subnet_count: 64,
|
||||
subnets_per_node: 2,
|
||||
maximum_gossip_clock_disparity_millis: default_maximum_gossip_clock_disparity_millis(),
|
||||
maximum_gossip_clock_disparity: default_maximum_gossip_clock_disparity(),
|
||||
target_aggregators_per_committee: 16,
|
||||
max_payload_size: default_max_payload_size(),
|
||||
min_epochs_for_block_requests: default_min_epochs_for_block_requests(),
|
||||
@@ -1430,7 +1458,7 @@ impl ChainSpec {
|
||||
attestation_propagation_slot_range: default_attestation_propagation_slot_range(),
|
||||
attestation_subnet_count: 64,
|
||||
subnets_per_node: 4, // Make this larger than usual to avoid network damage
|
||||
maximum_gossip_clock_disparity_millis: default_maximum_gossip_clock_disparity_millis(),
|
||||
maximum_gossip_clock_disparity: default_maximum_gossip_clock_disparity(),
|
||||
target_aggregators_per_committee: 16,
|
||||
max_payload_size: default_max_payload_size(),
|
||||
min_epochs_for_block_requests: 33024,
|
||||
@@ -1751,9 +1779,9 @@ pub struct Config {
|
||||
#[serde(default = "default_attestation_propagation_slot_range")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
attestation_propagation_slot_range: u64,
|
||||
#[serde(default = "default_maximum_gossip_clock_disparity_millis")]
|
||||
#[serde(default = "default_maximum_gossip_clock_disparity")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
maximum_gossip_clock_disparity_millis: u64,
|
||||
maximum_gossip_clock_disparity: u64,
|
||||
#[serde(default = "default_message_domain_invalid_snappy")]
|
||||
#[serde(with = "serde_utils::bytes_4_hex")]
|
||||
message_domain_invalid_snappy: [u8; 4],
|
||||
@@ -1967,7 +1995,7 @@ const fn default_attestation_propagation_slot_range() -> u64 {
|
||||
32
|
||||
}
|
||||
|
||||
const fn default_maximum_gossip_clock_disparity_millis() -> u64 {
|
||||
const fn default_maximum_gossip_clock_disparity() -> u64 {
|
||||
500
|
||||
}
|
||||
|
||||
@@ -2186,7 +2214,7 @@ impl Config {
|
||||
ttfb_timeout: spec.ttfb_timeout,
|
||||
resp_timeout: spec.resp_timeout,
|
||||
attestation_propagation_slot_range: spec.attestation_propagation_slot_range,
|
||||
maximum_gossip_clock_disparity_millis: spec.maximum_gossip_clock_disparity_millis,
|
||||
maximum_gossip_clock_disparity: spec.maximum_gossip_clock_disparity,
|
||||
message_domain_invalid_snappy: spec.message_domain_invalid_snappy,
|
||||
message_domain_valid_snappy: spec.message_domain_valid_snappy,
|
||||
max_request_blocks_deneb: spec.max_request_blocks_deneb,
|
||||
@@ -2274,7 +2302,7 @@ impl Config {
|
||||
message_domain_valid_snappy,
|
||||
max_request_blocks,
|
||||
attestation_propagation_slot_range,
|
||||
maximum_gossip_clock_disparity_millis,
|
||||
maximum_gossip_clock_disparity,
|
||||
max_request_blocks_deneb,
|
||||
max_request_blob_sidecars,
|
||||
max_request_data_column_sidecars,
|
||||
@@ -2350,7 +2378,7 @@ impl Config {
|
||||
attestation_subnet_prefix_bits,
|
||||
max_request_blocks,
|
||||
attestation_propagation_slot_range,
|
||||
maximum_gossip_clock_disparity_millis,
|
||||
maximum_gossip_clock_disparity,
|
||||
max_request_blocks_deneb,
|
||||
max_request_blob_sidecars,
|
||||
max_request_data_column_sidecars,
|
||||
@@ -2977,4 +3005,32 @@ mod yaml_tests {
|
||||
spec.min_epoch_data_availability_boundary(current_epoch)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proposer_shuffling_decision_root_around_epoch_boundary() {
|
||||
type E = MainnetEthSpec;
|
||||
let fulu_fork_epoch = 5;
|
||||
let spec = {
|
||||
let mut spec = ForkName::Electra.make_genesis_spec(E::default_spec());
|
||||
spec.fulu_fork_epoch = Some(Epoch::new(fulu_fork_epoch));
|
||||
Arc::new(spec)
|
||||
};
|
||||
|
||||
// For epochs prior to AND including the Fulu fork epoch, the decision slot is the end
|
||||
// of the previous epoch (i.e. only 1 slot lookahead).
|
||||
for epoch in (0..=fulu_fork_epoch).map(Epoch::new) {
|
||||
assert_eq!(
|
||||
spec.proposer_shuffling_decision_slot::<E>(epoch),
|
||||
epoch.start_slot(E::slots_per_epoch()) - 1
|
||||
);
|
||||
}
|
||||
|
||||
// For epochs after Fulu, the decision slot is the end of the epoch two epochs prior.
|
||||
for epoch in ((fulu_fork_epoch + 1)..(fulu_fork_epoch + 10)).map(Epoch::new) {
|
||||
assert_eq!(
|
||||
spec.proposer_shuffling_decision_slot::<E>(epoch),
|
||||
(epoch - 1).start_slot(E::slots_per_epoch()) - 1
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,7 @@ pub enum DataColumnSidecarError {
|
||||
PreDeneb,
|
||||
SszError(SszError),
|
||||
BuildSidecarFailed(String),
|
||||
InvalidCellProofLength { expected: usize, actual: usize },
|
||||
}
|
||||
|
||||
impl From<ArithError> for DataColumnSidecarError {
|
||||
|
||||
@@ -5,9 +5,13 @@ use std::sync::Arc;
|
||||
/// Cache of values which are uniquely determined at the start of an epoch.
|
||||
///
|
||||
/// The values are fixed with respect to the last block of the _prior_ epoch, which we refer
|
||||
/// to as the "decision block". This cache is very similar to the `BeaconProposerCache` in that
|
||||
/// beacon proposers are determined at exactly the same time as the values in this cache, so
|
||||
/// the keys for the two caches are identical.
|
||||
/// to as the "decision block".
|
||||
///
|
||||
/// Prior to Fulu this cache was similar to the `BeaconProposerCache` in that beacon proposers were
|
||||
/// determined at exactly the same time as the values in this cache, so the keys for the two caches
|
||||
/// were identical.
|
||||
///
|
||||
/// Post-Fulu, we use a different key (the proposers have more lookahead).
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Default)]
|
||||
pub struct EpochCache {
|
||||
|
||||
@@ -51,7 +51,7 @@ impl ForkName {
|
||||
/// This fork serves as the baseline for many tests, and the goal
|
||||
/// is to ensure features are passing on this fork.
|
||||
pub fn latest_stable() -> ForkName {
|
||||
ForkName::Electra
|
||||
ForkName::Fulu
|
||||
}
|
||||
|
||||
/// Set the activation slots in the given `ChainSpec` so that the fork named by `self`
|
||||
@@ -201,6 +201,46 @@ impl ForkName {
|
||||
pub fn gloas_enabled(self) -> bool {
|
||||
self >= ForkName::Gloas
|
||||
}
|
||||
|
||||
pub fn fork_ascii(self) {
|
||||
if self == ForkName::Fulu {
|
||||
println!(
|
||||
r#"
|
||||
╔═══════════════════════════════════════╗
|
||||
║ ║
|
||||
║ TO FULU, MOAR BLOBS TO ETHEREUM ║
|
||||
║ ║
|
||||
║ III DECEMBER MMXXV ║
|
||||
║ ║
|
||||
╚═══════════════════════════════════════╝
|
||||
|
||||
=============================================================================
|
||||
|||| ||||
|
||||
|---------------------------------------------------------------------------|
|
||||
|___-----___-----___-----___-----___-----___-----___-----___-----___-----___|
|
||||
/ _ \===/ _ \ / _ \===/ _ \ / _ \===/ _ \ / _ \===/ _ \
|
||||
( (.\ oOo /.) ) ( (.\ oOo /.) ) ( (.\ oOo /.) ) ( (.\ oOo /.) )
|
||||
\__/=====\__/ \__/=====\__/ \__/=====\__/ \__/=====\__/
|
||||
||||||| ||||||| ||||||| |||||||
|
||||
||||||| ||||||| \\/), ||||||| |||||||
|
||||
||||||| ||||||| ,'.' /, ||||||| |||||||
|
||||
||||||| ||||||| (_)- / /, ||||||| |||||||
|
||||
||||||| ||||||| /\_/ |__..--, * ||||||| |||||||
|
||||
||||||| ||||||| (\___/\ \ \ / ).' ||||||| |||||||
|
||||
||||||| ||||||| \____/ / (_ // ||||||| |||||||
|
||||
||||||| ||||||| \\_ ,'--'\_( ||||||| |||||||
|
||||
(oOoOo) (oOoOo) )_)_/ )_/ )_) (oOoOo) (oOoOo)
|
||||
J%%%%%L J%%%%%L (_(_.'(_.'(_.' J%%%%%L J%%%%%L
|
||||
ZZZZZZZZZ ZZZZZZZZZ ZZZZZZZZZ ZZZZZZZZZ
|
||||
===========================================================================
|
||||
|_________________________________________________________________________|
|
||||
|___________________________________________________________________________|
|
||||
|_____________________________________________________________________________|
|
||||
|_______________________________________________________________________________|
|
||||
"#
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Map a fork name into a fork-versioned superstruct type like `BeaconBlock`.
|
||||
|
||||
@@ -208,6 +208,8 @@ pub struct DenebPreset {
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub max_blob_commitments_per_block: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub kzg_commitment_inclusion_proof_depth: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub field_elements_per_blob: u64,
|
||||
}
|
||||
|
||||
@@ -215,6 +217,7 @@ impl DenebPreset {
|
||||
pub fn from_chain_spec<E: EthSpec>(_spec: &ChainSpec) -> Self {
|
||||
Self {
|
||||
max_blob_commitments_per_block: E::max_blob_commitments_per_block() as u64,
|
||||
kzg_commitment_inclusion_proof_depth: E::KzgCommitmentInclusionProofDepth::to_u64(),
|
||||
field_elements_per_blob: E::field_elements_per_blob() as u64,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user