Fork boilerplate

This commit is contained in:
Eitan Seri-Levi
2025-04-29 23:42:37 -07:00
parent 7bd50a6fe8
commit c464a54ba2
63 changed files with 1654 additions and 217 deletions

View File

@@ -2,6 +2,7 @@ use super::per_block_processing::{
errors::BlockProcessingError, process_operations::apply_deposit,
};
use crate::common::DepositDataTree;
use crate::upgrade::eip7805::upgrade_state_to_eip7805;
use crate::upgrade::electra::upgrade_state_to_electra;
use crate::upgrade::{
upgrade_to_altair, upgrade_to_bellatrix, upgrade_to_capella, upgrade_to_deneb, upgrade_to_fulu,
@@ -140,6 +141,34 @@ pub fn initialize_beacon_state_from_eth1<E: EthSpec>(
}
}
// Upgrade to eip7805 if configured from genesis.
if spec
.eip7805_fork_epoch
.is_some_and(|fork_epoch| fork_epoch == E::genesis_epoch())
{
let post = upgrade_state_to_eip7805(&mut state, Epoch::new(0), Epoch::new(0), spec)?;
state = post;
// Remove intermediate Deneb fork from `state.fork`.
state.fork_mut().previous_version = spec.electra_fork_version;
// TODO(electra): think about this more and determine the best way to
// do this. The spec tests will expect that the sync committees are
// calculated using the electra value for MAX_EFFECTIVE_BALANCE when
// calling `initialize_beacon_state_from_eth1()`. But the sync committees
// are actually calcuated back in `upgrade_to_altair()`. We need to
// re-calculate the sync committees here now that the state is `Electra`
let sync_committee = Arc::new(state.get_next_sync_committee(spec)?);
*state.current_sync_committee_mut()? = sync_committee.clone();
*state.next_sync_committee_mut()? = sync_committee;
// Override latest execution payload header.
// See https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#testing
if let Some(ExecutionPayloadHeader::Eip7805(ref header)) = execution_payload_header {
*state.latest_execution_payload_header_eip7805_mut()? = header.clone();
}
}
// Upgrade to fulu if configured from genesis.
if spec
.fulu_fork_epoch

View File

@@ -444,6 +444,12 @@ pub fn process_execution_payload<E: EthSpec, Payload: AbstractExecPayload<E>>(
_ => return Err(BlockProcessingError::IncorrectStateType),
}
}
ExecutionPayloadHeaderRefMut::Eip7805(header_mut) => {
match payload.to_execution_payload_header() {
ExecutionPayloadHeader::Eip7805(header) => *header_mut = header,
_ => return Err(BlockProcessingError::IncorrectStateType),
}
}
ExecutionPayloadHeaderRefMut::Fulu(header_mut) => {
match payload.to_execution_payload_header() {
ExecutionPayloadHeader::Fulu(header) => *header_mut = header,

View File

@@ -1,6 +1,6 @@
use crate::upgrade::{
upgrade_to_altair, upgrade_to_bellatrix, upgrade_to_capella, upgrade_to_deneb,
upgrade_to_electra, upgrade_to_fulu,
upgrade_to_eip7805, upgrade_to_electra, upgrade_to_fulu,
};
use crate::{per_epoch_processing::EpochProcessingSummary, *};
use safe_arith::{ArithError, SafeArith};
@@ -70,6 +70,10 @@ pub fn per_slot_processing<E: EthSpec>(
if spec.electra_fork_epoch == Some(state.current_epoch()) {
upgrade_to_electra(state, spec)?;
}
// Eip7805.
if spec.eip7805_fork_epoch == Some(state.current_epoch()) {
upgrade_to_eip7805(state, spec)?;
}
// Fulu.
if spec.fulu_fork_epoch == Some(state.current_epoch()) {

View File

@@ -2,6 +2,7 @@ pub mod altair;
pub mod bellatrix;
pub mod capella;
pub mod deneb;
pub mod eip7805;
pub mod electra;
pub mod fulu;
@@ -9,5 +10,6 @@ pub use altair::upgrade_to_altair;
pub use bellatrix::upgrade_to_bellatrix;
pub use capella::upgrade_to_capella;
pub use deneb::upgrade_to_deneb;
pub use eip7805::upgrade_to_eip7805;
pub use electra::upgrade_to_electra;
pub use fulu::upgrade_to_fulu;

View File

@@ -0,0 +1,173 @@
use bls::Signature;
use itertools::Itertools;
use safe_arith::SafeArith;
use std::mem;
use types::{
BeaconState, BeaconStateEip7805, BeaconStateError as Error, ChainSpec, Epoch, EpochCache,
EthSpec, Fork, PendingDeposit,
};
/// Transform a `Deneb` state into an `Electra` state.
pub fn upgrade_to_eip7805<E: EthSpec>(
pre_state: &mut BeaconState<E>,
spec: &ChainSpec,
) -> Result<(), Error> {
let epoch = pre_state.current_epoch();
let activation_exit_epoch = spec.compute_activation_exit_epoch(epoch)?;
let earliest_exit_epoch = pre_state
.validators()
.iter()
.filter(|v| v.exit_epoch != spec.far_future_epoch)
.map(|v| v.exit_epoch)
.max()
.unwrap_or(activation_exit_epoch)
.max(activation_exit_epoch)
.safe_add(1)?;
// The total active balance cache must be built before the consolidation churn limit
// is calculated.
pre_state.build_total_active_balance_cache(spec)?;
let earliest_consolidation_epoch = spec.compute_activation_exit_epoch(epoch)?;
let mut post = upgrade_state_to_eip7805(
pre_state,
earliest_exit_epoch,
earliest_consolidation_epoch,
spec,
)?;
*post.exit_balance_to_consume_mut()? = post.get_activation_exit_churn_limit(spec)?;
*post.consolidation_balance_to_consume_mut()? = post.get_consolidation_churn_limit(spec)?;
// Add validators that are not yet active to pending balance deposits
let validators = post.validators().clone();
let pre_activation = validators
.iter()
.enumerate()
.filter(|(_, validator)| validator.activation_epoch == spec.far_future_epoch)
.sorted_by_key(|(index, validator)| (validator.activation_eligibility_epoch, *index))
.map(|(index, _)| index)
.collect::<Vec<_>>();
// Process validators to queue entire balance and reset them
for index in pre_activation {
let balance = post
.balances_mut()
.get_mut(index)
.ok_or(Error::UnknownValidator(index))?;
let balance_copy = *balance;
*balance = 0_u64;
let validator = post
.validators_mut()
.get_mut(index)
.ok_or(Error::UnknownValidator(index))?;
validator.effective_balance = 0;
validator.activation_eligibility_epoch = spec.far_future_epoch;
let pubkey = validator.pubkey;
let withdrawal_credentials = validator.withdrawal_credentials;
post.pending_deposits_mut()?
.push(PendingDeposit {
pubkey,
withdrawal_credentials,
amount: balance_copy,
signature: Signature::infinity()?.into(),
slot: spec.genesis_slot,
})
.map_err(Error::MilhouseError)?;
}
// Ensure early adopters of compounding credentials go through the activation churn
let validators = post.validators().clone();
for (index, validator) in validators.iter().enumerate() {
if validator.has_compounding_withdrawal_credential(spec) {
post.queue_excess_active_balance(index, spec)?;
}
}
*pre_state = post;
Ok(())
}
pub fn upgrade_state_to_eip7805<E: EthSpec>(
pre_state: &mut BeaconState<E>,
earliest_exit_epoch: Epoch,
earliest_consolidation_epoch: Epoch,
spec: &ChainSpec,
) -> Result<BeaconState<E>, Error> {
let epoch = pre_state.current_epoch();
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`.
//
// Fixed size vectors get cloned because replacing them would require the same size
// allocation as cloning.
let post = BeaconState::Eip7805(BeaconStateEip7805 {
// Versioning
genesis_time: pre.genesis_time,
genesis_validators_root: pre.genesis_validators_root,
slot: pre.slot,
fork: Fork {
previous_version: pre.fork.current_version,
current_version: spec.electra_fork_version,
epoch,
},
// History
latest_block_header: pre.latest_block_header.clone(),
block_roots: pre.block_roots.clone(),
state_roots: pre.state_roots.clone(),
historical_roots: mem::take(&mut pre.historical_roots),
// Eth1
eth1_data: pre.eth1_data.clone(),
eth1_data_votes: mem::take(&mut pre.eth1_data_votes),
eth1_deposit_index: pre.eth1_deposit_index,
// Registry
validators: mem::take(&mut pre.validators),
balances: mem::take(&mut pre.balances),
// Randomness
randao_mixes: pre.randao_mixes.clone(),
// Slashings
slashings: pre.slashings.clone(),
// `Participation
previous_epoch_participation: mem::take(&mut pre.previous_epoch_participation),
current_epoch_participation: mem::take(&mut pre.current_epoch_participation),
// Finality
justification_bits: pre.justification_bits.clone(),
previous_justified_checkpoint: pre.previous_justified_checkpoint,
current_justified_checkpoint: pre.current_justified_checkpoint,
finalized_checkpoint: pre.finalized_checkpoint,
// Inactivity
inactivity_scores: mem::take(&mut pre.inactivity_scores),
// Sync committees
current_sync_committee: pre.current_sync_committee.clone(),
next_sync_committee: pre.next_sync_committee.clone(),
// Execution
latest_execution_payload_header: pre.latest_execution_payload_header.upgrade_to_eip7805(),
// Capella
next_withdrawal_index: pre.next_withdrawal_index,
next_withdrawal_validator_index: pre.next_withdrawal_validator_index,
historical_summaries: pre.historical_summaries.clone(),
// Electra
deposit_requests_start_index: spec.unset_deposit_requests_start_index,
deposit_balance_to_consume: 0,
exit_balance_to_consume: 0,
earliest_exit_epoch,
consolidation_balance_to_consume: 0,
earliest_consolidation_epoch,
pending_deposits: Default::default(),
pending_partial_withdrawals: Default::default(),
pending_consolidations: Default::default(),
// Caches
total_active_balance: pre.total_active_balance,
progressive_balances_cache: mem::take(&mut pre.progressive_balances_cache),
committee_caches: mem::take(&mut pre.committee_caches),
pubkey_cache: mem::take(&mut pre.pubkey_cache),
exit_cache: mem::take(&mut pre.exit_cache),
slashings_cache: mem::take(&mut pre.slashings_cache),
epoch_cache: EpochCache::default(),
});
Ok(post)
}

View File

@@ -20,7 +20,7 @@ pub fn upgrade_state_to_fulu<E: EthSpec>(
spec: &ChainSpec,
) -> Result<BeaconState<E>, Error> {
let epoch = pre_state.current_epoch();
let pre = pre_state.as_electra_mut()?;
let pre = pre_state.as_eip7805_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`.
//