mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-22 06:14:38 +00:00
- Implement and optimise `upgrade_to_gloas` - Enable EF tests for `fork_ugprade` Co-Authored-By: Michael Sproul <michael@sigmaprime.io>
199 lines
8.2 KiB
Rust
199 lines
8.2 KiB
Rust
use crate::per_block_processing::{
|
|
is_valid_deposit_signature, process_operations::apply_deposit_for_builder,
|
|
};
|
|
use milhouse::{List, Vector};
|
|
use ssz_types::BitVector;
|
|
use std::collections::HashSet;
|
|
use std::mem;
|
|
use typenum::Unsigned;
|
|
use types::{
|
|
BeaconState, BeaconStateError as Error, BeaconStateGloas, BuilderPendingPayment, ChainSpec,
|
|
DepositData, EthSpec, ExecutionPayloadBid, Fork, is_builder_withdrawal_credential,
|
|
};
|
|
|
|
/// Transform a `Fulu` state into a `Gloas` state.
|
|
pub fn upgrade_to_gloas<E: EthSpec>(
|
|
pre_state: &mut BeaconState<E>,
|
|
spec: &ChainSpec,
|
|
) -> Result<(), Error> {
|
|
let post = upgrade_state_to_gloas(pre_state, spec)?;
|
|
|
|
*pre_state = post;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn upgrade_state_to_gloas<E: EthSpec>(
|
|
pre_state: &mut BeaconState<E>,
|
|
spec: &ChainSpec,
|
|
) -> Result<BeaconState<E>, Error> {
|
|
let epoch = pre_state.current_epoch();
|
|
let pre = pre_state.as_fulu_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 mut post = BeaconState::Gloas(BeaconStateGloas {
|
|
// 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.gloas_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 Bid
|
|
latest_execution_payload_bid: ExecutionPayloadBid {
|
|
block_hash: pre.latest_execution_payload_header.block_hash,
|
|
..Default::default()
|
|
},
|
|
// 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: pre.deposit_requests_start_index,
|
|
deposit_balance_to_consume: pre.deposit_balance_to_consume,
|
|
exit_balance_to_consume: pre.exit_balance_to_consume,
|
|
earliest_exit_epoch: pre.earliest_exit_epoch,
|
|
consolidation_balance_to_consume: pre.consolidation_balance_to_consume,
|
|
earliest_consolidation_epoch: pre.earliest_consolidation_epoch,
|
|
pending_deposits: pre.pending_deposits.clone(),
|
|
pending_partial_withdrawals: pre.pending_partial_withdrawals.clone(),
|
|
pending_consolidations: pre.pending_consolidations.clone(),
|
|
proposer_lookahead: mem::take(&mut pre.proposer_lookahead),
|
|
// Gloas
|
|
builders: List::default(),
|
|
next_withdrawal_builder_index: 0,
|
|
// All bits set to true per spec:
|
|
// execution_payload_availability = [0b1 for _ in range(SLOTS_PER_HISTORICAL_ROOT)]
|
|
execution_payload_availability: BitVector::from_bytes(
|
|
vec![0xFFu8; E::SlotsPerHistoricalRoot::to_usize() / 8].into(),
|
|
)
|
|
.map_err(|_| Error::InvalidBitfield)?,
|
|
builder_pending_payments: Vector::new(vec![
|
|
BuilderPendingPayment::default();
|
|
E::builder_pending_payments_limit()
|
|
])?,
|
|
builder_pending_withdrawals: List::default(), // Empty list initially,
|
|
latest_block_hash: pre.latest_execution_payload_header.block_hash,
|
|
payload_expected_withdrawals: List::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: mem::take(&mut pre.epoch_cache),
|
|
});
|
|
// [New in Gloas:EIP7732]
|
|
onboard_builders_from_pending_deposits(&mut post, spec)?;
|
|
|
|
Ok(post)
|
|
}
|
|
|
|
/// Applies any pending deposit for builders, effectively onboarding builders at the fork.
|
|
fn onboard_builders_from_pending_deposits<E: EthSpec>(
|
|
state: &mut BeaconState<E>,
|
|
spec: &ChainSpec,
|
|
) -> Result<(), Error> {
|
|
// Rather than tracking all `validator_pubkeys` in one place as the spec does, we keep a
|
|
// hashset for *just* the new validator pubkeys, and use the state's efficient
|
|
// `get_validator_index` function instead of an O(n) iteration over the full validator list.
|
|
let mut new_validator_pubkeys = HashSet::new();
|
|
|
|
// Clone pending deposits to avoid borrow conflicts when mutating state.
|
|
let current_pending_deposits = state.pending_deposits()?.clone();
|
|
|
|
let mut pending_deposits = List::empty();
|
|
|
|
for deposit in ¤t_pending_deposits {
|
|
// Deposits for existing validators stay in the pending queue.
|
|
if new_validator_pubkeys.contains(&deposit.pubkey)
|
|
|| state.get_validator_index(&deposit.pubkey)?.is_some()
|
|
{
|
|
pending_deposits.push(deposit.clone())?;
|
|
continue;
|
|
}
|
|
|
|
// Re-scan builder list each iteration because `apply_deposit_for_builder` may add
|
|
// new builders to the registry.
|
|
// TODO(gloas): this linear scan could be optimized, see:
|
|
// https://github.com/sigp/lighthouse/issues/8783
|
|
let builder_index = state
|
|
.builders()?
|
|
.iter()
|
|
.position(|b| b.pubkey == deposit.pubkey);
|
|
|
|
let has_builder_credentials =
|
|
is_builder_withdrawal_credential(deposit.withdrawal_credentials, spec);
|
|
|
|
if builder_index.is_some() || has_builder_credentials {
|
|
let builder_index_opt = builder_index.map(|i| i as u64);
|
|
apply_deposit_for_builder(
|
|
state,
|
|
builder_index_opt,
|
|
deposit.pubkey,
|
|
deposit.withdrawal_credentials,
|
|
deposit.amount,
|
|
deposit.signature.clone(),
|
|
deposit.slot,
|
|
spec,
|
|
)?;
|
|
continue;
|
|
}
|
|
|
|
// If there is a pending deposit for a new validator that has a valid signature,
|
|
// track the pubkey so that subsequent builder deposits for the same pubkey stay
|
|
// in pending (applied to the validator later) rather than creating a builder.
|
|
// Deposits with invalid signatures are dropped since they would fail in
|
|
// apply_pending_deposit anyway.
|
|
let deposit_data = DepositData {
|
|
pubkey: deposit.pubkey,
|
|
withdrawal_credentials: deposit.withdrawal_credentials,
|
|
amount: deposit.amount,
|
|
signature: deposit.signature.clone(),
|
|
};
|
|
if is_valid_deposit_signature(&deposit_data, spec).is_ok() {
|
|
new_validator_pubkeys.insert(deposit.pubkey);
|
|
pending_deposits.push(deposit.clone())?;
|
|
}
|
|
}
|
|
|
|
*state.pending_deposits_mut()? = pending_deposits;
|
|
|
|
Ok(())
|
|
}
|