From b08d49c4cb340eacd8d77a7a04df006e145c21a5 Mon Sep 17 00:00:00 2001 From: ethDreamer <37123614+ethDreamer@users.noreply.github.com> Date: Mon, 9 Jun 2025 11:10:08 +0200 Subject: [PATCH] Changes for `fusaka-devnet-1` (#7559) Changes for [fusaka-devnet-1](https://notes.ethereum.org/@ethpandaops/fusaka-devnet-1) [Consensus Specs v1.6.0-alpha.1](https://github.com/ethereum/consensus-specs/pull/4346) * [EIP-7917: Deterministic Proposer Lookahead](https://eips.ethereum.org/EIPS/eip-7917) * [EIP-7892: Blob Parameter Only Hardforks](https://eips.ethereum.org/EIPS/eip-7892) --- .../beacon_chain/src/beacon_proposer_cache.rs | 2 +- .../beacon_chain/src/blob_verification.rs | 3 +- .../beacon_chain/src/block_verification.rs | 3 +- .../src/data_column_verification.rs | 3 +- .../beacon_chain/src/state_advance_timer.rs | 2 +- beacon_node/beacon_chain/tests/tests.rs | 23 ++++-- .../beacon_chain/tests/validator_monitor.rs | 14 ++-- beacon_node/http_api/src/proposer_duties.rs | 2 +- beacon_node/store/src/partial_beacon_state.rs | 5 +- .../src/per_epoch_processing/errors.rs | 1 + .../src/per_epoch_processing/single_pass.rs | 39 +++++++++- .../state_processing/src/upgrade/fulu.rs | 27 ++++++- consensus/types/src/beacon_state.rs | 71 ++++++++++++++----- consensus/types/src/eth_spec.rs | 13 ++++ testing/ef_tests/Makefile | 2 +- .../ef_tests/src/cases/epoch_processing.rs | 20 +++++- testing/ef_tests/src/lib.rs | 4 +- testing/ef_tests/tests/tests.rs | 6 ++ 18 files changed, 201 insertions(+), 39 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_proposer_cache.rs b/beacon_node/beacon_chain/src/beacon_proposer_cache.rs index 56b13b0b77..12970214c6 100644 --- a/beacon_node/beacon_chain/src/beacon_proposer_cache.rs +++ b/beacon_node/beacon_chain/src/beacon_proposer_cache.rs @@ -181,7 +181,7 @@ pub fn compute_proposer_duties_from_head( ensure_state_is_in_epoch(&mut state, head_state_root, request_epoch, &chain.spec)?; let indices = state - .get_beacon_proposer_indices(&chain.spec) + .get_beacon_proposer_indices(request_epoch, &chain.spec) .map_err(BeaconChainError::from)?; let dependent_root = state diff --git a/beacon_node/beacon_chain/src/blob_verification.rs b/beacon_node/beacon_chain/src/blob_verification.rs index d7acb78408..a78224fb70 100644 --- a/beacon_node/beacon_chain/src/blob_verification.rs +++ b/beacon_node/beacon_chain/src/blob_verification.rs @@ -523,7 +523,8 @@ pub fn validate_blob_sidecar_for_gossip GossipVerifiedBlock { &chain.spec, )?; - let proposers = state.get_beacon_proposer_indices(&chain.spec)?; + let epoch = state.current_epoch(); + let proposers = state.get_beacon_proposer_indices(epoch, &chain.spec)?; let proposer_index = *proposers .get(block.slot().as_usize() % T::EthSpec::slots_per_epoch() as usize) .ok_or_else(|| BeaconChainError::NoProposerForSlot(block.slot()))?; diff --git a/beacon_node/beacon_chain/src/data_column_verification.rs b/beacon_node/beacon_chain/src/data_column_verification.rs index 4e847d9f9f..3e54ee9199 100644 --- a/beacon_node/beacon_chain/src/data_column_verification.rs +++ b/beacon_node/beacon_chain/src/data_column_verification.rs @@ -669,7 +669,8 @@ fn verify_proposer_and_signature( &chain.spec, )?; - let proposers = state.get_beacon_proposer_indices(&chain.spec)?; + let epoch = state.current_epoch(); + let proposers = state.get_beacon_proposer_indices(epoch, &chain.spec)?; // Prime the proposer shuffling cache with the newly-learned value. Ok::<_, GossipDataColumnError>(EpochBlockProposers { epoch: column_epoch, diff --git a/beacon_node/beacon_chain/src/state_advance_timer.rs b/beacon_node/beacon_chain/src/state_advance_timer.rs index f206405f67..ad7e31a8f0 100644 --- a/beacon_node/beacon_chain/src/state_advance_timer.rs +++ b/beacon_node/beacon_chain/src/state_advance_timer.rs @@ -377,7 +377,7 @@ fn advance_head(beacon_chain: &Arc>) -> Resu state.current_epoch(), head_block_root, state - .get_beacon_proposer_indices(&beacon_chain.spec) + .get_beacon_proposer_indices(state.current_epoch(), &beacon_chain.spec) .map_err(BeaconChainError::from)?, state.fork(), ) diff --git a/beacon_node/beacon_chain/tests/tests.rs b/beacon_node/beacon_chain/tests/tests.rs index c801361fd5..be34248d95 100644 --- a/beacon_node/beacon_chain/tests/tests.rs +++ b/beacon_node/beacon_chain/tests/tests.rs @@ -9,6 +9,7 @@ use beacon_chain::{ BeaconChain, ChainConfig, NotifyExecutionLayer, StateSkipConfig, WhenSlotSkipped, }; use operation_pool::PersistedOperationPool; +use state_processing::EpochProcessingError; use state_processing::{per_slot_processing, per_slot_processing::Error as SlotProcessingError}; use std::sync::LazyLock; use types::{ @@ -67,11 +68,23 @@ fn massive_skips() { }; assert!(state.slot() > 1, "the state should skip at least one slot"); - assert_eq!( - error, - SlotProcessingError::BeaconStateError(BeaconStateError::InsufficientValidators), - "should return error indicating that validators have been slashed out" - ) + + if state.fork_name_unchecked().fulu_enabled() { + // post-fulu this is done in per_epoch_processing + assert_eq!( + error, + SlotProcessingError::EpochProcessingError(EpochProcessingError::BeaconStateError( + BeaconStateError::InsufficientValidators + )), + "should return error indicating that validators have been slashed out" + ) + } else { + assert_eq!( + error, + SlotProcessingError::BeaconStateError(BeaconStateError::InsufficientValidators), + "should return error indicating that validators have been slashed out" + ) + } } #[tokio::test] diff --git a/beacon_node/beacon_chain/tests/validator_monitor.rs b/beacon_node/beacon_chain/tests/validator_monitor.rs index bca37b4e6d..5b861d1a4a 100644 --- a/beacon_node/beacon_chain/tests/validator_monitor.rs +++ b/beacon_node/beacon_chain/tests/validator_monitor.rs @@ -81,7 +81,7 @@ async fn missed_blocks_across_epochs() { epoch, decision_root, state - .get_beacon_proposer_indices(&harness.chain.spec) + .get_beacon_proposer_indices(epoch, &harness.chain.spec) .unwrap(), state.fork(), ) @@ -147,7 +147,9 @@ async fn missed_blocks_basic() { let mut slot_in_epoch = slot % slots_per_epoch; let mut prev_slot = Slot::new(idx - 1); let mut duplicate_block_root = *_state.block_roots().get(idx as usize).unwrap(); - let mut validator_indexes = _state.get_beacon_proposer_indices(&harness1.spec).unwrap(); + let mut validator_indexes = _state + .get_beacon_proposer_indices(epoch, &harness1.spec) + .unwrap(); let mut missed_block_proposer = validator_indexes[slot_in_epoch.as_usize()]; let mut proposer_shuffling_decision_root = _state .proposer_shuffling_decision_root(duplicate_block_root) @@ -219,7 +221,9 @@ async fn missed_blocks_basic() { prev_slot = Slot::new(idx - 1); slot_in_epoch = slot % slots_per_epoch; duplicate_block_root = *_state2.block_roots().get(idx as usize).unwrap(); - validator_indexes = _state2.get_beacon_proposer_indices(&harness2.spec).unwrap(); + validator_indexes = _state2 + .get_beacon_proposer_indices(epoch, &harness2.spec) + .unwrap(); missed_block_proposer = validator_indexes[slot_in_epoch.as_usize()]; let beacon_proposer_cache = harness2 @@ -317,7 +321,9 @@ async fn missed_blocks_basic() { slot_in_epoch = slot % slots_per_epoch; prev_slot = Slot::new(idx - 1); duplicate_block_root = *_state3.block_roots().get(idx as usize).unwrap(); - validator_indexes = _state3.get_beacon_proposer_indices(&harness3.spec).unwrap(); + validator_indexes = _state3 + .get_beacon_proposer_indices(epoch, &harness3.spec) + .unwrap(); missed_block_proposer = validator_indexes[slot_in_epoch.as_usize()]; proposer_shuffling_decision_root = _state3 .proposer_shuffling_decision_root_at_epoch(epoch, duplicate_block_root) diff --git a/beacon_node/http_api/src/proposer_duties.rs b/beacon_node/http_api/src/proposer_duties.rs index 971571f487..44286736f3 100644 --- a/beacon_node/http_api/src/proposer_duties.rs +++ b/beacon_node/http_api/src/proposer_duties.rs @@ -227,7 +227,7 @@ fn compute_historic_proposer_duties( } let indices = state - .get_beacon_proposer_indices(&chain.spec) + .get_beacon_proposer_indices(epoch, &chain.spec) .map_err(BeaconChainError::from) .map_err(warp_utils::reject::unhandled_error)?; diff --git a/beacon_node/store/src/partial_beacon_state.rs b/beacon_node/store/src/partial_beacon_state.rs index d209512159..fdd1880f55 100644 --- a/beacon_node/store/src/partial_beacon_state.rs +++ b/beacon_node/store/src/partial_beacon_state.rs @@ -147,6 +147,8 @@ where List, #[superstruct(only(Electra, Fulu))] pub pending_consolidations: List, + #[superstruct(only(Fulu))] + pub proposer_lookahead: Vector, } impl PartialBeaconState { @@ -444,7 +446,8 @@ impl TryInto> for PartialBeaconState { earliest_consolidation_epoch, pending_deposits, pending_partial_withdrawals, - pending_consolidations + pending_consolidations, + proposer_lookahead ], [historical_summaries] ), diff --git a/consensus/state_processing/src/per_epoch_processing/errors.rs b/consensus/state_processing/src/per_epoch_processing/errors.rs index 7485e365ec..9db2ff3096 100644 --- a/consensus/state_processing/src/per_epoch_processing/errors.rs +++ b/consensus/state_processing/src/per_epoch_processing/errors.rs @@ -30,6 +30,7 @@ pub enum EpochProcessingError { MissingEarliestExitEpoch, MissingExitBalanceToConsume, PendingDepositsLogicError, + ProposerLookaheadOutOfBounds(usize), } impl From for EpochProcessingError { diff --git a/consensus/state_processing/src/per_epoch_processing/single_pass.rs b/consensus/state_processing/src/per_epoch_processing/single_pass.rs index af6a0936e2..ae1e330043 100644 --- a/consensus/state_processing/src/per_epoch_processing/single_pass.rs +++ b/consensus/state_processing/src/per_epoch_processing/single_pass.rs @@ -19,7 +19,7 @@ use types::{ milhouse::Cow, ActivationQueue, BeaconState, BeaconStateError, ChainSpec, Checkpoint, DepositData, Epoch, EthSpec, ExitCache, ForkName, List, ParticipationFlags, PendingDeposit, - ProgressiveBalancesCache, RelativeEpoch, Unsigned, Validator, + ProgressiveBalancesCache, RelativeEpoch, Unsigned, Validator, Vector, }; pub struct SinglePassConfig { @@ -30,6 +30,7 @@ pub struct SinglePassConfig { pub pending_deposits: bool, pub pending_consolidations: bool, pub effective_balance_updates: bool, + pub proposer_lookahead: bool, } impl Default for SinglePassConfig { @@ -48,6 +49,7 @@ impl SinglePassConfig { pending_deposits: true, pending_consolidations: true, effective_balance_updates: true, + proposer_lookahead: true, } } @@ -60,6 +62,7 @@ impl SinglePassConfig { pending_deposits: false, pending_consolidations: false, effective_balance_updates: false, + proposer_lookahead: false, } } } @@ -460,9 +463,43 @@ pub fn process_epoch_single_pass( next_epoch_cache.into_epoch_cache(next_epoch_activation_queue, spec)?; } + if conf.proposer_lookahead && fork_name.fulu_enabled() { + process_proposer_lookahead(state, spec)?; + } + Ok(summary) } +// TOOO(EIP-7917): use balances cache +pub fn process_proposer_lookahead( + state: &mut BeaconState, + spec: &ChainSpec, +) -> Result<(), Error> { + let mut lookahead = state.proposer_lookahead()?.clone().to_vec(); + + // Shift out proposers in the first epoch + lookahead.copy_within((E::slots_per_epoch() as usize).., 0); + + let next_epoch = state + .current_epoch() + .safe_add(spec.min_seed_lookahead.as_u64())? + .safe_add(1)?; + let last_epoch_proposers = state.get_beacon_proposer_indices(next_epoch, spec)?; + + // Fill in the last epoch with new proposer indices + let last_epoch_start = E::proposer_lookahead_slots().safe_sub(E::slots_per_epoch() as usize)?; + for (i, proposer) in last_epoch_proposers.into_iter().enumerate() { + let index = last_epoch_start.safe_add(i)?; + *lookahead + .get_mut(index) + .ok_or(Error::ProposerLookaheadOutOfBounds(index))? = proposer as u64; + } + + *state.proposer_lookahead_mut()? = Vector::new(lookahead)?; + + Ok(()) +} + fn process_single_inactivity_update( inactivity_score: &mut Cow, validator_info: &ValidatorInfo, diff --git a/consensus/state_processing/src/upgrade/fulu.rs b/consensus/state_processing/src/upgrade/fulu.rs index 6e0cd3fa9d..6b038ad73a 100644 --- a/consensus/state_processing/src/upgrade/fulu.rs +++ b/consensus/state_processing/src/upgrade/fulu.rs @@ -1,5 +1,8 @@ +use safe_arith::SafeArith; use std::mem; -use types::{BeaconState, BeaconStateError as Error, BeaconStateFulu, ChainSpec, EthSpec, Fork}; +use types::{ + BeaconState, BeaconStateError as Error, BeaconStateFulu, ChainSpec, EthSpec, Fork, Vector, +}; /// Transform a `Electra` state into an `Fulu` state. pub fn upgrade_to_fulu( @@ -15,11 +18,32 @@ pub fn upgrade_to_fulu( Ok(()) } +fn initialize_proposer_lookahead( + state: &BeaconState, + spec: &ChainSpec, +) -> Result, Error> { + let current_epoch = state.current_epoch(); + let mut lookahead = Vec::with_capacity(E::proposer_lookahead_slots()); + for i in 0..(spec.min_seed_lookahead.safe_add(1)?.as_u64()) { + let target_epoch = current_epoch.safe_add(i)?; + lookahead.extend( + state + .get_beacon_proposer_indices(target_epoch, spec) + .map(|vec| vec.into_iter().map(|x| x as u64))?, + ); + } + + Vector::new(lookahead).map_err(|e| { + Error::PleaseNotifyTheDevs(format!("Failed to initialize proposer lookahead: {:?}", e)) + }) +} + pub fn upgrade_state_to_fulu( pre_state: &mut BeaconState, spec: &ChainSpec, ) -> Result, Error> { let epoch = pre_state.current_epoch(); + let proposer_lookahead = initialize_proposer_lookahead(pre_state, spec)?; let pre = pre_state.as_electra_mut()?; // Where possible, use something like `mem::take` to move fields from behind the &mut // reference. For other fields that don't have a good default value, use `clone`. @@ -89,6 +113,7 @@ pub fn upgrade_state_to_fulu( exit_cache: mem::take(&mut pre.exit_cache), slashings_cache: mem::take(&mut pre.slashings_cache), epoch_cache: mem::take(&mut pre.epoch_cache), + proposer_lookahead, }); Ok(post) } diff --git a/consensus/types/src/beacon_state.rs b/consensus/types/src/beacon_state.rs index ce41eddc17..599d0fe8e9 100644 --- a/consensus/types/src/beacon_state.rs +++ b/consensus/types/src/beacon_state.rs @@ -172,6 +172,7 @@ pub enum Error { AggregatorNotInCommittee { aggregator_index: u64, }, + PleaseNotifyTheDevs(String), } /// Control whether an epoch-indexed field can be indexed at the next epoch or not. @@ -544,6 +545,12 @@ where #[superstruct(only(Electra, Fulu))] pub pending_consolidations: List, + // Fulu + #[compare_fields(as_iter)] + #[test_random(default)] + #[superstruct(only(Fulu))] + pub proposer_lookahead: Vector, + // Caching (not in the spec) #[serde(skip_serializing, skip_deserializing)] #[ssz(skip_serializing, skip_deserializing)] @@ -948,6 +955,25 @@ impl BeaconState { } } + // Vec is just much easier to work with here + fn compute_proposer_indices( + &self, + epoch: Epoch, + seed: &[u8], + indices: &[usize], + spec: &ChainSpec, + ) -> Result, Error> { + epoch + .slot_iter(E::slots_per_epoch()) + .map(|slot| { + let mut preimage = seed.to_vec(); + preimage.append(&mut int_to_bytes8(slot.as_u64())); + let seed = hash(&preimage); + self.compute_proposer_index(indices, &seed, spec) + }) + .collect() + } + /// Fork-aware abstraction for the shuffling. /// /// In Electra and later, the random value is a 16-bit integer stored in a `u64`. @@ -1062,37 +1088,48 @@ impl BeaconState { /// Returns the beacon proposer index for the `slot` in `self.current_epoch()`. /// - /// Spec v0.12.1 + /// Spec v1.6.0-alpha.1 pub fn get_beacon_proposer_index(&self, slot: Slot, spec: &ChainSpec) -> Result { // Proposer indices are only known for the current epoch, due to the dependence on the // effective balances of validators, which change at every epoch transition. let epoch = slot.epoch(E::slots_per_epoch()); + // TODO(EIP-7917): Explore allowing this function to be called with a slot one epoch in the future. if epoch != self.current_epoch() { return Err(Error::SlotOutOfBounds); } - let seed = self.get_beacon_proposer_seed(slot, spec)?; - let indices = self.get_active_validator_indices(epoch, spec)?; + if let Ok(proposer_lookahead) = self.proposer_lookahead() { + // Post-Fulu + 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 + ))) + .map(|index| *index as usize) + } else { + // Pre-Fulu + let seed = self.get_beacon_proposer_seed(slot, spec)?; + let indices = self.get_active_validator_indices(epoch, spec)?; - self.compute_proposer_index(&indices, &seed, spec) + self.compute_proposer_index(&indices, &seed, spec) + } } - /// Returns the beacon proposer index for each `slot` in `self.current_epoch()`. + /// Returns the beacon proposer index for each `slot` in `epoch`. /// - /// The returned `Vec` contains one proposer index for each slot. For example, if - /// `state.current_epoch() == 1`, then `vec[0]` refers to slot `32` and `vec[1]` refers to slot - /// `33`. It will always be the case that `vec.len() == SLOTS_PER_EPOCH`. - pub fn get_beacon_proposer_indices(&self, spec: &ChainSpec) -> Result, Error> { + /// The returned `Vec` contains one proposer index for each slot in the epoch. + pub fn get_beacon_proposer_indices( + &self, + epoch: Epoch, + spec: &ChainSpec, + ) -> Result, Error> { // Not using the cached validator indices since they are shuffled. - let indices = self.get_active_validator_indices(self.current_epoch(), spec)?; + let indices = self.get_active_validator_indices(epoch, spec)?; - self.current_epoch() - .slot_iter(E::slots_per_epoch()) - .map(|slot| { - let seed = self.get_beacon_proposer_seed(slot, spec)?; - self.compute_proposer_index(&indices, &seed, spec) - }) - .collect() + let preimage = self.get_seed(epoch, Domain::BeaconProposer, spec)?; + self.compute_proposer_indices(epoch, preimage.as_slice(), &indices, spec) } /// Compute the seed to use for the beacon proposer selection at the given `slot`. diff --git a/consensus/types/src/eth_spec.rs b/consensus/types/src/eth_spec.rs index 1cde9c2e48..bc87a4bd80 100644 --- a/consensus/types/src/eth_spec.rs +++ b/consensus/types/src/eth_spec.rs @@ -118,6 +118,7 @@ pub trait EthSpec: type FieldElementsPerCell: Unsigned + Clone + Sync + Send + Debug + PartialEq; type FieldElementsPerExtBlob: Unsigned + Clone + Sync + Send + Debug + PartialEq; type KzgCommitmentsInclusionProofDepth: Unsigned + Clone + Sync + Send + Debug + PartialEq; + type ProposerLookaheadSlots: Unsigned + Clone + Sync + Send + Debug + PartialEq; /* * Derived values (set these CAREFULLY) */ @@ -378,6 +379,10 @@ pub trait EthSpec: fn kzg_commitments_inclusion_proof_depth() -> usize { Self::KzgCommitmentsInclusionProofDepth::to_usize() } + + fn proposer_lookahead_slots() -> usize { + Self::ProposerLookaheadSlots::to_usize() + } } /// Macro to inherit some type values from another EthSpec. @@ -429,6 +434,7 @@ impl EthSpec for MainnetEthSpec { type MaxCellsPerBlock = U33554432; type KzgCommitmentInclusionProofDepth = U17; type KzgCommitmentsInclusionProofDepth = U4; // inclusion of the whole list of commitments + type ProposerLookaheadSlots = U64; // Derived from (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH type SyncSubcommitteeSize = U128; // 512 committee size / 4 sync committee subnet count type MaxPendingAttestations = U4096; // 128 max attestations * 32 slots per epoch type SlotsPerEth1VotingPeriod = U2048; // 64 epochs * 32 slots per epoch @@ -481,6 +487,7 @@ impl EthSpec for MinimalEthSpec { type MaxCellsPerBlock = U33554432; type BytesPerCell = U2048; type KzgCommitmentsInclusionProofDepth = U4; + type ProposerLookaheadSlots = U16; // Derived from (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH params_from_eth_spec!(MainnetEthSpec { JustificationBitsLength, @@ -576,6 +583,7 @@ impl EthSpec for GnosisEthSpec { type MaxCellsPerBlock = U33554432; type BytesPerCell = U2048; type KzgCommitmentsInclusionProofDepth = U4; + type ProposerLookaheadSlots = U32; // Derived from (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH fn default_spec() -> ChainSpec { ChainSpec::gnosis() @@ -592,9 +600,14 @@ mod test { use ssz_types::typenum::Unsigned; fn assert_valid_spec() { + let spec = E::default_spec(); E::kzg_commitments_tree_depth(); E::block_body_tree_depth(); assert!(E::MaxValidatorsPerSlot::to_i32() >= E::MaxValidatorsPerCommittee::to_i32()); + assert_eq!( + E::proposer_lookahead_slots(), + (spec.min_seed_lookahead.as_usize() + 1) * E::slots_per_epoch() as usize + ); } #[test] diff --git a/testing/ef_tests/Makefile b/testing/ef_tests/Makefile index 6b780b1f92..48afcae4b2 100644 --- a/testing/ef_tests/Makefile +++ b/testing/ef_tests/Makefile @@ -1,6 +1,6 @@ # To download/extract nightly tests, run: # CONSENSUS_SPECS_TEST_VERSION=nightly make -CONSENSUS_SPECS_TEST_VERSION ?= v1.6.0-alpha.0 +CONSENSUS_SPECS_TEST_VERSION ?= v1.6.0-alpha.1 REPO_NAME := consensus-spec-tests OUTPUT_DIR := ./$(REPO_NAME) diff --git a/testing/ef_tests/src/cases/epoch_processing.rs b/testing/ef_tests/src/cases/epoch_processing.rs index e05225c171..0dc5e7ab11 100644 --- a/testing/ef_tests/src/cases/epoch_processing.rs +++ b/testing/ef_tests/src/cases/epoch_processing.rs @@ -11,7 +11,7 @@ use state_processing::per_epoch_processing::effective_balance_updates::{ process_effective_balance_updates, process_effective_balance_updates_slow, }; use state_processing::per_epoch_processing::single_pass::{ - process_epoch_single_pass, SinglePassConfig, + process_epoch_single_pass, process_proposer_lookahead, SinglePassConfig, }; use state_processing::per_epoch_processing::{ altair, base, @@ -77,6 +77,8 @@ pub struct SyncCommitteeUpdates; pub struct InactivityUpdates; #[derive(Debug)] pub struct ParticipationFlagUpdates; +#[derive(Debug)] +pub struct ProposerLookahead; type_name!( JustificationAndFinalization, @@ -97,6 +99,7 @@ type_name!(ParticipationRecordUpdates, "participation_record_updates"); type_name!(SyncCommitteeUpdates, "sync_committee_updates"); type_name!(InactivityUpdates, "inactivity_updates"); type_name!(ParticipationFlagUpdates, "participation_flag_updates"); +type_name!(ProposerLookahead, "proposer_lookahead"); impl EpochTransition for JustificationAndFinalization { fn run(state: &mut BeaconState, spec: &ChainSpec) -> Result<(), EpochProcessingError> { @@ -280,6 +283,16 @@ impl EpochTransition for ParticipationFlagUpdates { } } +impl EpochTransition for ProposerLookahead { + fn run(state: &mut BeaconState, spec: &ChainSpec) -> Result<(), EpochProcessingError> { + if state.fork_name_unchecked().fulu_enabled() { + process_proposer_lookahead(state, spec) + } else { + Ok(()) + } + } +} + impl> LoadCase for EpochProcessing { fn load_from_dir(path: &Path, fork_name: ForkName) -> Result { let spec = &testing_spec::(fork_name); @@ -338,6 +351,11 @@ impl> Case for EpochProcessing { { return false; } + + if !fork_name.fulu_enabled() && T::name() == "proposer_lookahead" { + return false; + } + true } diff --git a/testing/ef_tests/src/lib.rs b/testing/ef_tests/src/lib.rs index de255c2c73..a2d905738e 100644 --- a/testing/ef_tests/src/lib.rs +++ b/testing/ef_tests/src/lib.rs @@ -4,8 +4,8 @@ pub use cases::{ Case, DataColumnsByRootIdentifierWrapper, EffectiveBalanceUpdates, Eth1DataReset, FeatureName, HistoricalRootsUpdate, HistoricalSummariesUpdate, InactivityUpdates, JustificationAndFinalization, ParticipationFlagUpdates, ParticipationRecordUpdates, - PendingBalanceDeposits, PendingConsolidations, RandaoMixesReset, RegistryUpdates, - RewardsAndPenalties, Slashings, SlashingsReset, SyncCommitteeUpdates, + PendingBalanceDeposits, PendingConsolidations, ProposerLookahead, RandaoMixesReset, + RegistryUpdates, RewardsAndPenalties, Slashings, SlashingsReset, SyncCommitteeUpdates, }; pub use decode::log_file_access; pub use error::Error; diff --git a/testing/ef_tests/tests/tests.rs b/testing/ef_tests/tests/tests.rs index 8842ec2852..b6264f2e08 100644 --- a/testing/ef_tests/tests/tests.rs +++ b/testing/ef_tests/tests/tests.rs @@ -833,6 +833,12 @@ fn epoch_processing_participation_flag_updates() { EpochProcessingHandler::::default().run(); } +#[test] +fn epoch_processing_proposer_lookahead() { + EpochProcessingHandler::::default().run(); + EpochProcessingHandler::::default().run(); +} + #[test] fn fork_upgrade() { ForkHandler::::default().run();