mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-18 22:49:34 +00:00
Merge branch 'unstable' into progressive-list-tests
This commit is contained in:
@@ -28,7 +28,7 @@ pub fn get_base_reward(
|
||||
validator_effective_balance: u64,
|
||||
base_reward_per_increment: BaseRewardPerIncrement,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<u64, Error> {
|
||||
) -> Result<u64, BeaconStateError> {
|
||||
validator_effective_balance
|
||||
.safe_div(spec.effective_balance_increment)?
|
||||
.safe_mul(base_reward_per_increment.as_u64())
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use integer_sqrt::IntegerSquareRoot;
|
||||
use safe_arith::SafeArith;
|
||||
use smallvec::SmallVec;
|
||||
use types::{AttestationData, BeaconState, ChainSpec, EthSpec};
|
||||
use types::{
|
||||
BeaconStateError as Error,
|
||||
AttestationData, BeaconState, BeaconStateError as Error, ChainSpec, EthSpec,
|
||||
consts::altair::{
|
||||
NUM_FLAG_INDICES, TIMELY_HEAD_FLAG_INDEX, TIMELY_SOURCE_FLAG_INDEX,
|
||||
TIMELY_TARGET_FLAG_INDEX,
|
||||
@@ -16,6 +16,8 @@ use types::{
|
||||
///
|
||||
/// This function will return an error if the source of the attestation doesn't match the
|
||||
/// state's relevant justified checkpoint.
|
||||
///
|
||||
/// This function has been abstracted to work for all forks from Altair to Gloas.
|
||||
pub fn get_attestation_participation_flag_indices<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
data: &AttestationData,
|
||||
@@ -27,13 +29,43 @@ pub fn get_attestation_participation_flag_indices<E: EthSpec>(
|
||||
} else {
|
||||
state.previous_justified_checkpoint()
|
||||
};
|
||||
|
||||
// Matching roots.
|
||||
let is_matching_source = data.source == justified_checkpoint;
|
||||
|
||||
// Matching target.
|
||||
let is_matching_target = is_matching_source
|
||||
&& data.target.root == *state.get_block_root_at_epoch(data.target.epoch)?;
|
||||
let is_matching_head =
|
||||
is_matching_target && data.beacon_block_root == *state.get_block_root(data.slot)?;
|
||||
|
||||
// [New in Gloas:EIP7732]
|
||||
let payload_matches = if state.fork_name_unchecked().gloas_enabled() {
|
||||
if state.is_attestation_same_slot(data)? {
|
||||
// For same-slot attestations, data.index must be 0
|
||||
if data.index != 0 {
|
||||
return Err(Error::BadOverloadedDataIndex(data.index));
|
||||
}
|
||||
true
|
||||
} else {
|
||||
// For non same-slot attestations, check execution payload availability
|
||||
let slot_index = data
|
||||
.slot
|
||||
.as_usize()
|
||||
.safe_rem(E::slots_per_historical_root())?;
|
||||
let payload_index = state
|
||||
.execution_payload_availability()?
|
||||
.get(slot_index)
|
||||
.map(|avail| if avail { 1 } else { 0 })
|
||||
.map_err(|_| Error::InvalidExecutionPayloadAvailabilityIndex(slot_index))?;
|
||||
data.index == payload_index
|
||||
}
|
||||
} else {
|
||||
// Essentially `payload_matches` is always true pre-Gloas (it is not considered for matching
|
||||
// head).
|
||||
true
|
||||
};
|
||||
|
||||
// Matching head.
|
||||
let is_matching_head = is_matching_target
|
||||
&& data.beacon_block_root == *state.get_block_root(data.slot)?
|
||||
&& payload_matches;
|
||||
|
||||
if !is_matching_source {
|
||||
return Err(Error::IncorrectAttestationSource);
|
||||
|
||||
@@ -107,7 +107,7 @@ pub mod attesting_indices_electra {
|
||||
for committee_index in committee_indices {
|
||||
let beacon_committee = committees
|
||||
.get(committee_index as usize)
|
||||
.ok_or(Error::NoCommitteeFound(committee_index))?;
|
||||
.ok_or(BeaconStateError::NoCommitteeFound(committee_index))?;
|
||||
|
||||
// This check is new to the spec's `process_attestation` in Electra.
|
||||
if committee_index >= committee_count_per_slot {
|
||||
|
||||
@@ -42,8 +42,7 @@ pub fn slash_validator<E: EthSpec>(
|
||||
decrease_balance(
|
||||
state,
|
||||
slashed_index,
|
||||
validator_effective_balance
|
||||
.safe_div(spec.min_slashing_penalty_quotient_for_state(state))?,
|
||||
validator_effective_balance.safe_div(state.get_min_slashing_penalty_quotient(spec))?,
|
||||
)?;
|
||||
|
||||
update_progressive_balances_on_slashing(state, slashed_index, validator_effective_balance)?;
|
||||
@@ -54,8 +53,8 @@ pub fn slash_validator<E: EthSpec>(
|
||||
// Apply proposer and whistleblower rewards
|
||||
let proposer_index = ctxt.get_proposer_index(state, spec)? as usize;
|
||||
let whistleblower_index = opt_whistleblower_index.unwrap_or(proposer_index);
|
||||
let whistleblower_reward = validator_effective_balance
|
||||
.safe_div(spec.whistleblower_reward_quotient_for_state(state))?;
|
||||
let whistleblower_reward =
|
||||
validator_effective_balance.safe_div(state.get_whistleblower_reward_quotient(spec))?;
|
||||
let proposer_reward = if state.fork_name_unchecked().altair_enabled() {
|
||||
whistleblower_reward
|
||||
.safe_mul(PROPOSER_WEIGHT)?
|
||||
|
||||
278
consensus/state_processing/src/envelope_processing.rs
Normal file
278
consensus/state_processing/src/envelope_processing.rs
Normal file
@@ -0,0 +1,278 @@
|
||||
use crate::BlockProcessingError;
|
||||
use crate::VerifySignatures;
|
||||
use crate::per_block_processing::compute_timestamp_at_slot;
|
||||
use crate::per_block_processing::process_operations::{
|
||||
process_consolidation_requests, process_deposit_requests_post_gloas,
|
||||
process_withdrawal_requests,
|
||||
};
|
||||
use safe_arith::{ArithError, SafeArith};
|
||||
use tree_hash::TreeHash;
|
||||
use types::{
|
||||
BeaconState, BeaconStateError, BuilderIndex, BuilderPendingPayment, ChainSpec, EthSpec,
|
||||
ExecutionBlockHash, Hash256, SignedExecutionPayloadEnvelope, Slot,
|
||||
};
|
||||
|
||||
macro_rules! envelope_verify {
|
||||
($condition: expr, $result: expr) => {
|
||||
if !$condition {
|
||||
return Err($result);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum EnvelopeProcessingError {
|
||||
/// Bad Signature
|
||||
BadSignature,
|
||||
BeaconStateError(BeaconStateError),
|
||||
BlockProcessingError(BlockProcessingError),
|
||||
ArithError(ArithError),
|
||||
/// Envelope doesn't match latest beacon block header
|
||||
LatestBlockHeaderMismatch {
|
||||
envelope_root: Hash256,
|
||||
block_header_root: Hash256,
|
||||
},
|
||||
/// Envelope doesn't match latest beacon block slot
|
||||
SlotMismatch {
|
||||
envelope_slot: Slot,
|
||||
parent_state_slot: Slot,
|
||||
},
|
||||
/// The payload withdrawals don't match the state's payload withdrawals.
|
||||
WithdrawalsRootMismatch {
|
||||
state: Hash256,
|
||||
payload: Hash256,
|
||||
},
|
||||
// The builder index doesn't match the committed bid.
|
||||
BuilderIndexMismatch {
|
||||
committed_bid: BuilderIndex,
|
||||
envelope: BuilderIndex,
|
||||
},
|
||||
// The gas limit doesn't match the committed bid
|
||||
GasLimitMismatch {
|
||||
committed_bid: u64,
|
||||
envelope: u64,
|
||||
},
|
||||
// The block hash doesn't match the committed bid
|
||||
BlockHashMismatch {
|
||||
committed_bid: ExecutionBlockHash,
|
||||
envelope: ExecutionBlockHash,
|
||||
},
|
||||
// The parent hash doesn't match the previous execution payload
|
||||
ParentHashMismatch {
|
||||
state: ExecutionBlockHash,
|
||||
envelope: ExecutionBlockHash,
|
||||
},
|
||||
// The previous randao didn't match the payload
|
||||
PrevRandaoMismatch {
|
||||
committed_bid: Hash256,
|
||||
envelope: Hash256,
|
||||
},
|
||||
// The timestamp didn't match the payload
|
||||
TimestampMismatch {
|
||||
state: u64,
|
||||
envelope: u64,
|
||||
},
|
||||
// Invalid state root
|
||||
InvalidStateRoot {
|
||||
state: Hash256,
|
||||
envelope: Hash256,
|
||||
},
|
||||
// BitFieldError
|
||||
BitFieldError(ssz::BitfieldError),
|
||||
// Some kind of error calculating the builder payment index
|
||||
BuilderPaymentIndexOutOfBounds(usize),
|
||||
/// The envelope was deemed invalid by the execution engine.
|
||||
ExecutionInvalid,
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for EnvelopeProcessingError {
|
||||
fn from(e: BeaconStateError) -> Self {
|
||||
EnvelopeProcessingError::BeaconStateError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockProcessingError> for EnvelopeProcessingError {
|
||||
fn from(e: BlockProcessingError) -> Self {
|
||||
EnvelopeProcessingError::BlockProcessingError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArithError> for EnvelopeProcessingError {
|
||||
fn from(e: ArithError) -> Self {
|
||||
EnvelopeProcessingError::ArithError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes a `SignedExecutionPayloadEnvelope`
|
||||
///
|
||||
/// This function does all the state modifications inside `process_execution_payload()`
|
||||
pub fn process_execution_payload_envelope<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
parent_state_root: Option<Hash256>,
|
||||
signed_envelope: &SignedExecutionPayloadEnvelope<E>,
|
||||
verify_signatures: VerifySignatures,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), EnvelopeProcessingError> {
|
||||
if verify_signatures.is_true() {
|
||||
// Verify Signed Envelope Signature
|
||||
if !signed_envelope.verify_signature_with_state(state, spec)? {
|
||||
return Err(EnvelopeProcessingError::BadSignature);
|
||||
}
|
||||
}
|
||||
|
||||
let envelope = &signed_envelope.message;
|
||||
let payload = &envelope.payload;
|
||||
let execution_requests = &envelope.execution_requests;
|
||||
|
||||
// Cache latest block header state root
|
||||
if state.latest_block_header().state_root == Hash256::default() {
|
||||
let previous_state_root = parent_state_root
|
||||
.map(Ok)
|
||||
.unwrap_or_else(|| state.canonical_root())?;
|
||||
state.latest_block_header_mut().state_root = previous_state_root;
|
||||
}
|
||||
|
||||
// Verify consistency with the beacon block
|
||||
let latest_block_header_root = state.latest_block_header().tree_hash_root();
|
||||
envelope_verify!(
|
||||
envelope.beacon_block_root == latest_block_header_root,
|
||||
EnvelopeProcessingError::LatestBlockHeaderMismatch {
|
||||
envelope_root: envelope.beacon_block_root,
|
||||
block_header_root: latest_block_header_root,
|
||||
}
|
||||
);
|
||||
envelope_verify!(
|
||||
envelope.slot == state.slot(),
|
||||
EnvelopeProcessingError::SlotMismatch {
|
||||
envelope_slot: envelope.slot,
|
||||
parent_state_slot: state.slot(),
|
||||
}
|
||||
);
|
||||
|
||||
// Verify consistency with the committed bid
|
||||
let committed_bid = state.latest_execution_payload_bid()?;
|
||||
envelope_verify!(
|
||||
envelope.builder_index == committed_bid.builder_index,
|
||||
EnvelopeProcessingError::BuilderIndexMismatch {
|
||||
committed_bid: committed_bid.builder_index,
|
||||
envelope: envelope.builder_index,
|
||||
}
|
||||
);
|
||||
envelope_verify!(
|
||||
committed_bid.prev_randao == payload.prev_randao,
|
||||
EnvelopeProcessingError::PrevRandaoMismatch {
|
||||
committed_bid: committed_bid.prev_randao,
|
||||
envelope: payload.prev_randao,
|
||||
}
|
||||
);
|
||||
|
||||
// Verify consistency with expected withdrawals
|
||||
// NOTE: we don't bother hashing here except in case of error, because we can just compare for
|
||||
// equality directly. This equality check could be more straight-forward if the types were
|
||||
// changed to match (currently we are comparing VariableList to List). This could happen
|
||||
// coincidentally when we adopt ProgressiveList.
|
||||
envelope_verify!(
|
||||
payload.withdrawals.len() == state.payload_expected_withdrawals()?.len()
|
||||
&& payload
|
||||
.withdrawals
|
||||
.iter()
|
||||
.eq(state.payload_expected_withdrawals()?.iter()),
|
||||
EnvelopeProcessingError::WithdrawalsRootMismatch {
|
||||
state: state.payload_expected_withdrawals()?.tree_hash_root(),
|
||||
payload: payload.withdrawals.tree_hash_root(),
|
||||
}
|
||||
);
|
||||
|
||||
// Verify the gas limit
|
||||
envelope_verify!(
|
||||
committed_bid.gas_limit == payload.gas_limit,
|
||||
EnvelopeProcessingError::GasLimitMismatch {
|
||||
committed_bid: committed_bid.gas_limit,
|
||||
envelope: payload.gas_limit,
|
||||
}
|
||||
);
|
||||
|
||||
// Verify the block hash
|
||||
envelope_verify!(
|
||||
committed_bid.block_hash == payload.block_hash,
|
||||
EnvelopeProcessingError::BlockHashMismatch {
|
||||
committed_bid: committed_bid.block_hash,
|
||||
envelope: payload.block_hash,
|
||||
}
|
||||
);
|
||||
|
||||
// Verify consistency of the parent hash with respect to the previous execution payload
|
||||
envelope_verify!(
|
||||
payload.parent_hash == *state.latest_block_hash()?,
|
||||
EnvelopeProcessingError::ParentHashMismatch {
|
||||
state: *state.latest_block_hash()?,
|
||||
envelope: payload.parent_hash,
|
||||
}
|
||||
);
|
||||
|
||||
// Verify timestamp
|
||||
let state_timestamp = compute_timestamp_at_slot(state, state.slot(), spec)?;
|
||||
envelope_verify!(
|
||||
payload.timestamp == state_timestamp,
|
||||
EnvelopeProcessingError::TimestampMismatch {
|
||||
state: state_timestamp,
|
||||
envelope: payload.timestamp,
|
||||
}
|
||||
);
|
||||
|
||||
// TODO(gloas): newPayload happens here in the spec, ensure we wire that up correctly
|
||||
|
||||
process_deposit_requests_post_gloas(state, &execution_requests.deposits, spec)?;
|
||||
|
||||
// TODO(gloas): gotta update these
|
||||
process_withdrawal_requests(state, &execution_requests.withdrawals, spec)?;
|
||||
process_consolidation_requests(state, &execution_requests.consolidations, spec)?;
|
||||
|
||||
// Queue the builder payment
|
||||
let payment_index = E::slots_per_epoch()
|
||||
.safe_add(state.slot().as_u64().safe_rem(E::slots_per_epoch())?)?
|
||||
as usize;
|
||||
let payment_mut = state
|
||||
.builder_pending_payments_mut()?
|
||||
.get_mut(payment_index)
|
||||
.ok_or(EnvelopeProcessingError::BuilderPaymentIndexOutOfBounds(
|
||||
payment_index,
|
||||
))?;
|
||||
|
||||
// We have re-ordered the blanking out of the pending payment to avoid a double-lookup.
|
||||
// This is semantically equivalent to the ordering used by the spec because we have taken a
|
||||
// clone of the payment prior to doing the write.
|
||||
let payment_withdrawal = payment_mut.withdrawal.clone();
|
||||
*payment_mut = BuilderPendingPayment::default();
|
||||
|
||||
let amount = payment_withdrawal.amount;
|
||||
if amount > 0 {
|
||||
state
|
||||
.builder_pending_withdrawals_mut()?
|
||||
.push(payment_withdrawal)
|
||||
.map_err(|e| EnvelopeProcessingError::BeaconStateError(e.into()))?;
|
||||
}
|
||||
|
||||
// Cache the execution payload hash
|
||||
let availability_index = state
|
||||
.slot()
|
||||
.as_usize()
|
||||
.safe_rem(E::slots_per_historical_root())?;
|
||||
state
|
||||
.execution_payload_availability_mut()?
|
||||
.set(availability_index, true)
|
||||
.map_err(EnvelopeProcessingError::BitFieldError)?;
|
||||
*state.latest_block_hash_mut()? = payload.block_hash;
|
||||
|
||||
// Verify the state root
|
||||
let state_root = state.canonical_root()?;
|
||||
envelope_verify!(
|
||||
envelope.state_root == state_root,
|
||||
EnvelopeProcessingError::InvalidStateRoot {
|
||||
state: state_root,
|
||||
envelope: envelope.state_root,
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -5,7 +5,7 @@ use crate::metrics;
|
||||
use fixed_bytes::FixedBytesExtended;
|
||||
use safe_arith::SafeArith;
|
||||
use tracing::instrument;
|
||||
use types::epoch_cache::{EpochCache, EpochCacheError, EpochCacheKey};
|
||||
use types::state::{EpochCache, EpochCacheError, EpochCacheKey};
|
||||
use types::{ActivationQueue, BeaconState, ChainSpec, EthSpec, ForkName, Hash256};
|
||||
|
||||
/// Precursor to an `EpochCache`.
|
||||
|
||||
@@ -195,7 +195,7 @@ pub fn is_valid_genesis_state<E: EthSpec>(state: &BeaconState<E>, spec: &ChainSp
|
||||
pub fn process_activations<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<(), BeaconStateError> {
|
||||
let (validators, balances, _) = state.validators_and_balances_and_progressive_balances_mut();
|
||||
let mut validators_iter = validators.iter_cow();
|
||||
while let Some((index, validator)) = validators_iter.next_cow() {
|
||||
@@ -203,7 +203,7 @@ pub fn process_activations<E: EthSpec>(
|
||||
let balance = balances
|
||||
.get(index)
|
||||
.copied()
|
||||
.ok_or(Error::BalancesOutOfBounds(index))?;
|
||||
.ok_or(BeaconStateError::BalancesOutOfBounds(index))?;
|
||||
validator.effective_balance = std::cmp::min(
|
||||
balance.safe_sub(balance.safe_rem(spec.effective_balance_increment)?)?,
|
||||
spec.max_effective_balance,
|
||||
|
||||
@@ -20,6 +20,7 @@ pub mod all_caches;
|
||||
pub mod block_replayer;
|
||||
pub mod common;
|
||||
pub mod consensus_context;
|
||||
pub mod envelope_processing;
|
||||
pub mod epoch_cache;
|
||||
pub mod genesis;
|
||||
pub mod per_block_processing;
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
use crate::consensus_context::ConsensusContext;
|
||||
use errors::{BlockOperationError, BlockProcessingError, HeaderInvalid};
|
||||
use errors::{
|
||||
BlockOperationError, BlockProcessingError, ExecutionPayloadBidInvalid, HeaderInvalid,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use safe_arith::{ArithError, SafeArith, SafeArithIter};
|
||||
use signature_sets::{block_proposal_signature_set, get_pubkey_from_state, randao_signature_set};
|
||||
use safe_arith::{ArithError, SafeArith};
|
||||
use signature_sets::{
|
||||
block_proposal_signature_set, execution_payload_bid_signature_set,
|
||||
get_builder_pubkey_from_state, get_pubkey_from_state, randao_signature_set,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use tree_hash::TreeHash;
|
||||
use typenum::Unsigned;
|
||||
use types::*;
|
||||
use types::{consts::gloas::BUILDER_INDEX_SELF_BUILD, *};
|
||||
|
||||
pub use self::verify_attester_slashing::{
|
||||
get_slashable_indices, get_slashable_indices_modular, verify_attester_slashing,
|
||||
@@ -24,9 +29,11 @@ pub use verify_deposit::{
|
||||
get_existing_validator_index, is_valid_deposit_signature, verify_deposit_merkle_proof,
|
||||
};
|
||||
pub use verify_exit::verify_exit;
|
||||
pub use withdrawals::get_expected_withdrawals;
|
||||
|
||||
pub mod altair;
|
||||
pub mod block_signature_verifier;
|
||||
pub mod builder;
|
||||
pub mod deneb;
|
||||
pub mod errors;
|
||||
mod is_valid_indexed_attestation;
|
||||
@@ -39,8 +46,8 @@ mod verify_bls_to_execution_change;
|
||||
mod verify_deposit;
|
||||
mod verify_exit;
|
||||
mod verify_proposer_slashing;
|
||||
pub mod withdrawals;
|
||||
|
||||
use crate::common::decrease_balance;
|
||||
use crate::common::update_progressive_balances_cache::{
|
||||
initialize_progressive_balances_cache, update_progressive_balances_metrics,
|
||||
};
|
||||
@@ -172,14 +179,21 @@ pub fn per_block_processing<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||
// previous block.
|
||||
if is_execution_enabled(state, block.body()) {
|
||||
let body = block.body();
|
||||
// TODO(EIP-7732): build out process_withdrawals variant for gloas
|
||||
process_withdrawals::<E, Payload>(state, body.execution_payload()?, spec)?;
|
||||
process_execution_payload::<E, Payload>(state, body, spec)?;
|
||||
if state.fork_name_unchecked().gloas_enabled() {
|
||||
withdrawals::gloas::process_withdrawals::<E>(state, spec)?;
|
||||
// TODO(EIP-7732): process execution payload bid
|
||||
} else {
|
||||
if state.fork_name_unchecked().capella_enabled() {
|
||||
withdrawals::capella_electra::process_withdrawals::<E, Payload>(
|
||||
state,
|
||||
body.execution_payload()?,
|
||||
spec,
|
||||
)?;
|
||||
}
|
||||
process_execution_payload::<E, Payload>(state, body, spec)?;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(EIP-7732): build out process_execution_bid
|
||||
// process_execution_bid(state, block, verify_signatures, spec)?;
|
||||
|
||||
process_randao(state, block, verify_randao, ctxt, spec)?;
|
||||
process_eth1_data(state, block.body().eth1_data())?;
|
||||
process_operations(state, block.body(), verify_signatures, ctxt, spec)?;
|
||||
@@ -322,7 +336,7 @@ pub fn process_randao<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||
pub fn process_eth1_data<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
eth1_data: &Eth1Data,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<(), BeaconStateError> {
|
||||
if let Some(new_eth1_data) = get_new_eth1_data(state, eth1_data)? {
|
||||
*state.eth1_data_mut() = new_eth1_data;
|
||||
}
|
||||
@@ -510,193 +524,165 @@ pub fn compute_timestamp_at_slot<E: EthSpec>(
|
||||
) -> Result<u64, ArithError> {
|
||||
let slots_since_genesis = block_slot.as_u64().safe_sub(spec.genesis_slot.as_u64())?;
|
||||
slots_since_genesis
|
||||
.safe_mul(spec.seconds_per_slot)
|
||||
.safe_mul(spec.get_slot_duration().as_secs())
|
||||
.and_then(|since_genesis| state.genesis_time().safe_add(since_genesis))
|
||||
}
|
||||
|
||||
/// Compute the next batch of withdrawals which should be included in a block.
|
||||
///
|
||||
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/electra/beacon-chain.md#new-get_expected_withdrawals
|
||||
pub fn get_expected_withdrawals<E: EthSpec>(
|
||||
pub fn can_builder_cover_bid<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
builder_index: BuilderIndex,
|
||||
builder: &Builder,
|
||||
bid_amount: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(Withdrawals<E>, Option<usize>), BlockProcessingError> {
|
||||
let epoch = state.current_epoch();
|
||||
let mut withdrawal_index = state.next_withdrawal_index()?;
|
||||
let mut validator_index = state.next_withdrawal_validator_index()?;
|
||||
let mut withdrawals = Vec::<Withdrawal>::with_capacity(E::max_withdrawals_per_payload());
|
||||
let fork_name = state.fork_name_unchecked();
|
||||
|
||||
// [New in Electra:EIP7251]
|
||||
// Consume pending partial withdrawals
|
||||
let processed_partial_withdrawals_count =
|
||||
if let Ok(pending_partial_withdrawals) = state.pending_partial_withdrawals() {
|
||||
let mut processed_partial_withdrawals_count = 0;
|
||||
for withdrawal in pending_partial_withdrawals {
|
||||
if withdrawal.withdrawable_epoch > epoch
|
||||
|| withdrawals.len() == spec.max_pending_partials_per_withdrawals_sweep as usize
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
let validator = state.get_validator(withdrawal.validator_index as usize)?;
|
||||
|
||||
let has_sufficient_effective_balance =
|
||||
validator.effective_balance >= spec.min_activation_balance;
|
||||
let total_withdrawn = withdrawals
|
||||
.iter()
|
||||
.filter_map(|w| {
|
||||
(w.validator_index == withdrawal.validator_index).then_some(w.amount)
|
||||
})
|
||||
.safe_sum()?;
|
||||
let balance = state
|
||||
.get_balance(withdrawal.validator_index as usize)?
|
||||
.safe_sub(total_withdrawn)?;
|
||||
let has_excess_balance = balance > spec.min_activation_balance;
|
||||
|
||||
if validator.exit_epoch == spec.far_future_epoch
|
||||
&& has_sufficient_effective_balance
|
||||
&& has_excess_balance
|
||||
{
|
||||
let withdrawable_balance = std::cmp::min(
|
||||
balance.safe_sub(spec.min_activation_balance)?,
|
||||
withdrawal.amount,
|
||||
);
|
||||
withdrawals.push(Withdrawal {
|
||||
index: withdrawal_index,
|
||||
validator_index: withdrawal.validator_index,
|
||||
address: validator
|
||||
.get_execution_withdrawal_address(spec)
|
||||
.ok_or(BeaconStateError::NonExecutionAddressWithdrawalCredential)?,
|
||||
amount: withdrawable_balance,
|
||||
});
|
||||
withdrawal_index.safe_add_assign(1)?;
|
||||
}
|
||||
processed_partial_withdrawals_count.safe_add_assign(1)?;
|
||||
}
|
||||
Some(processed_partial_withdrawals_count)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let bound = std::cmp::min(
|
||||
state.validators().len() as u64,
|
||||
spec.max_validators_per_withdrawals_sweep,
|
||||
);
|
||||
for _ in 0..bound {
|
||||
let validator = state.get_validator(validator_index as usize)?;
|
||||
let partially_withdrawn_balance = withdrawals
|
||||
.iter()
|
||||
.filter_map(|withdrawal| {
|
||||
(withdrawal.validator_index == validator_index).then_some(withdrawal.amount)
|
||||
})
|
||||
.safe_sum()?;
|
||||
let balance = state
|
||||
.balances()
|
||||
.get(validator_index as usize)
|
||||
.ok_or(BeaconStateError::BalancesOutOfBounds(
|
||||
validator_index as usize,
|
||||
))?
|
||||
.safe_sub(partially_withdrawn_balance)?;
|
||||
if validator.is_fully_withdrawable_validator(balance, epoch, spec, fork_name) {
|
||||
withdrawals.push(Withdrawal {
|
||||
index: withdrawal_index,
|
||||
validator_index,
|
||||
address: validator
|
||||
.get_execution_withdrawal_address(spec)
|
||||
.ok_or(BlockProcessingError::WithdrawalCredentialsInvalid)?,
|
||||
amount: balance,
|
||||
});
|
||||
withdrawal_index.safe_add_assign(1)?;
|
||||
} else if validator.is_partially_withdrawable_validator(balance, spec, fork_name) {
|
||||
withdrawals.push(Withdrawal {
|
||||
index: withdrawal_index,
|
||||
validator_index,
|
||||
address: validator
|
||||
.get_execution_withdrawal_address(spec)
|
||||
.ok_or(BlockProcessingError::WithdrawalCredentialsInvalid)?,
|
||||
amount: balance.safe_sub(validator.get_max_effective_balance(spec, fork_name))?,
|
||||
});
|
||||
withdrawal_index.safe_add_assign(1)?;
|
||||
}
|
||||
if withdrawals.len() == E::max_withdrawals_per_payload() {
|
||||
break;
|
||||
}
|
||||
validator_index = validator_index
|
||||
.safe_add(1)?
|
||||
.safe_rem(state.validators().len() as u64)?;
|
||||
) -> Result<bool, BlockProcessingError> {
|
||||
let builder_balance = builder.balance;
|
||||
let pending_withdrawals_amount =
|
||||
state.get_pending_balance_to_withdraw_for_builder(builder_index)?;
|
||||
let min_balance = spec
|
||||
.min_deposit_amount
|
||||
.safe_add(pending_withdrawals_amount)?;
|
||||
if builder_balance < min_balance {
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(builder_balance.safe_sub(min_balance)? >= bid_amount)
|
||||
}
|
||||
|
||||
Ok((
|
||||
withdrawals
|
||||
.try_into()
|
||||
.map_err(BlockProcessingError::SszTypesError)?,
|
||||
processed_partial_withdrawals_count,
|
||||
))
|
||||
}
|
||||
|
||||
/// Apply withdrawals to the state.
|
||||
/// TODO(EIP-7732): abstract this out and create gloas variant
|
||||
pub fn process_withdrawals<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||
pub fn process_execution_payload_bid<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||
state: &mut BeaconState<E>,
|
||||
payload: Payload::Ref<'_>,
|
||||
block: BeaconBlockRef<'_, E, Payload>,
|
||||
verify_signatures: VerifySignatures,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
if state.fork_name_unchecked().capella_enabled() {
|
||||
let (expected_withdrawals, processed_partial_withdrawals_count) =
|
||||
get_expected_withdrawals(state, spec)?;
|
||||
let expected_root = expected_withdrawals.tree_hash_root();
|
||||
let withdrawals_root = payload.withdrawals_root()?;
|
||||
// Verify the bid signature
|
||||
let signed_bid = block.body().signed_execution_payload_bid()?;
|
||||
|
||||
if expected_root != withdrawals_root {
|
||||
return Err(BlockProcessingError::WithdrawalsRootMismatch {
|
||||
expected: expected_root,
|
||||
found: withdrawals_root,
|
||||
});
|
||||
}
|
||||
let bid = &signed_bid.message;
|
||||
let amount = bid.value;
|
||||
let builder_index = bid.builder_index;
|
||||
|
||||
for withdrawal in expected_withdrawals.iter() {
|
||||
decrease_balance(
|
||||
state,
|
||||
withdrawal.validator_index as usize,
|
||||
withdrawal.amount,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Update pending partial withdrawals [New in Electra:EIP7251]
|
||||
if let Some(processed_partial_withdrawals_count) = processed_partial_withdrawals_count {
|
||||
state
|
||||
.pending_partial_withdrawals_mut()?
|
||||
.pop_front(processed_partial_withdrawals_count)?;
|
||||
}
|
||||
|
||||
// Update the next withdrawal index if this block contained withdrawals
|
||||
if let Some(latest_withdrawal) = expected_withdrawals.last() {
|
||||
*state.next_withdrawal_index_mut()? = latest_withdrawal.index.safe_add(1)?;
|
||||
|
||||
// Update the next validator index to start the next withdrawal sweep
|
||||
if expected_withdrawals.len() == E::max_withdrawals_per_payload() {
|
||||
// Next sweep starts after the latest withdrawal's validator index
|
||||
let next_validator_index = latest_withdrawal
|
||||
.validator_index
|
||||
.safe_add(1)?
|
||||
.safe_rem(state.validators().len() as u64)?;
|
||||
*state.next_withdrawal_validator_index_mut()? = next_validator_index;
|
||||
}
|
||||
}
|
||||
|
||||
// Advance sweep by the max length of the sweep if there was not a full set of withdrawals
|
||||
if expected_withdrawals.len() != E::max_withdrawals_per_payload() {
|
||||
let next_validator_index = state
|
||||
.next_withdrawal_validator_index()?
|
||||
.safe_add(spec.max_validators_per_withdrawals_sweep)?
|
||||
.safe_rem(state.validators().len() as u64)?;
|
||||
*state.next_withdrawal_validator_index_mut()? = next_validator_index;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
// For self-builds, amount must be zero regardless of withdrawal credential prefix
|
||||
if builder_index == BUILDER_INDEX_SELF_BUILD {
|
||||
block_verify!(
|
||||
amount == 0,
|
||||
ExecutionPayloadBidInvalid::SelfBuildNonZeroAmount.into()
|
||||
);
|
||||
block_verify!(
|
||||
signed_bid.signature.is_infinity(),
|
||||
ExecutionPayloadBidInvalid::BadSignature.into()
|
||||
);
|
||||
} else {
|
||||
// these shouldn't even be encountered but they're here for completeness
|
||||
Ok(())
|
||||
let builder = state.get_builder(builder_index)?;
|
||||
|
||||
// Verify that the builder is active
|
||||
block_verify!(
|
||||
builder.is_active_at_finalized_epoch(state.finalized_checkpoint().epoch, spec),
|
||||
ExecutionPayloadBidInvalid::BuilderNotActive(builder_index).into()
|
||||
);
|
||||
|
||||
// Verify that the builder has funds to cover the bid
|
||||
block_verify!(
|
||||
can_builder_cover_bid(state, builder_index, builder, amount, spec)?,
|
||||
ExecutionPayloadBidInvalid::InsufficientBalance {
|
||||
builder_index,
|
||||
builder_balance: builder.balance,
|
||||
bid_value: amount,
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
if verify_signatures.is_true() {
|
||||
block_verify!(
|
||||
// We know this is NOT a self-build, so there MUST be a signature set (func does not
|
||||
// return None).
|
||||
execution_payload_bid_signature_set(
|
||||
state,
|
||||
|i| get_builder_pubkey_from_state(state, i),
|
||||
signed_bid,
|
||||
spec
|
||||
)?
|
||||
.ok_or(ExecutionPayloadBidInvalid::BadSignature)?
|
||||
.verify(),
|
||||
ExecutionPayloadBidInvalid::BadSignature.into()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify commitments are under limit
|
||||
let max_blobs_per_block = spec.max_blobs_per_block(state.current_epoch()) as usize;
|
||||
block_verify!(
|
||||
bid.blob_kzg_commitments.len() <= max_blobs_per_block,
|
||||
ExecutionPayloadBidInvalid::ExcessBlobCommitments {
|
||||
max: max_blobs_per_block,
|
||||
bid: bid.blob_kzg_commitments.len(),
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Verify that the bid is for the current slot
|
||||
block_verify!(
|
||||
bid.slot == block.slot(),
|
||||
ExecutionPayloadBidInvalid::SlotMismatch {
|
||||
bid_slot: bid.slot,
|
||||
block_slot: block.slot(),
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Verify that the bid is for the right parent block
|
||||
let latest_block_hash = state.latest_block_hash()?;
|
||||
block_verify!(
|
||||
bid.parent_block_hash == *latest_block_hash,
|
||||
ExecutionPayloadBidInvalid::ParentBlockHashMismatch {
|
||||
state_block_hash: *latest_block_hash,
|
||||
bid_parent_hash: bid.parent_block_hash,
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
block_verify!(
|
||||
bid.parent_block_root == block.parent_root(),
|
||||
ExecutionPayloadBidInvalid::ParentBlockRootMismatch {
|
||||
block_parent_root: block.parent_root(),
|
||||
bid_parent_root: bid.parent_block_root,
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
let expected_randao = *state.get_randao_mix(state.current_epoch())?;
|
||||
block_verify!(
|
||||
bid.prev_randao == expected_randao,
|
||||
ExecutionPayloadBidInvalid::PrevRandaoMismatch {
|
||||
expected: expected_randao,
|
||||
bid: bid.prev_randao,
|
||||
}
|
||||
.into()
|
||||
);
|
||||
|
||||
// Record the pending payment if there is some payment
|
||||
if amount > 0 {
|
||||
let pending_payment = BuilderPendingPayment {
|
||||
weight: 0,
|
||||
withdrawal: BuilderPendingWithdrawal {
|
||||
fee_recipient: bid.fee_recipient,
|
||||
amount,
|
||||
builder_index,
|
||||
},
|
||||
};
|
||||
|
||||
let payment_index = E::SlotsPerEpoch::to_usize()
|
||||
.safe_add(bid.slot.as_usize().safe_rem(E::SlotsPerEpoch::to_usize())?)?;
|
||||
|
||||
*state
|
||||
.builder_pending_payments_mut()?
|
||||
.get_mut(payment_index)
|
||||
.ok_or(BlockProcessingError::BeaconStateError(
|
||||
BeaconStateError::InvalidBuilderPendingPaymentsIndex(payment_index),
|
||||
))? = pending_payment;
|
||||
}
|
||||
|
||||
// Cache the execution bid
|
||||
*state.latest_execution_payload_bid_mut()? = bid.clone();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
use types::{builder::BuilderIndex, consts::gloas::BUILDER_INDEX_FLAG};
|
||||
|
||||
pub fn is_builder_index(validator_index: u64) -> bool {
|
||||
validator_index & BUILDER_INDEX_FLAG != 0
|
||||
}
|
||||
|
||||
pub fn convert_builder_index_to_validator_index(builder_index: BuilderIndex) -> u64 {
|
||||
builder_index | BUILDER_INDEX_FLAG
|
||||
}
|
||||
|
||||
pub fn convert_validator_index_to_builder_index(validator_index: u64) -> BuilderIndex {
|
||||
validator_index & !BUILDER_INDEX_FLAG
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use ethereum_hashing::hash_fixed;
|
||||
use types::{KzgCommitment, VERSIONED_HASH_VERSION_KZG, VersionedHash};
|
||||
use types::{VersionedHash, kzg_ext::KzgCommitment, kzg_ext::consts::VERSIONED_HASH_VERSION_KZG};
|
||||
|
||||
pub fn kzg_commitment_to_versioned_hash(kzg_commitment: &KzgCommitment) -> VersionedHash {
|
||||
let mut hashed_commitment = hash_fixed(&kzg_commitment.0);
|
||||
|
||||
@@ -90,7 +90,20 @@ pub enum BlockProcessingError {
|
||||
found: Hash256,
|
||||
},
|
||||
WithdrawalCredentialsInvalid,
|
||||
/// This should be unreachable unless there's a logical flaw in the spec for withdrawals.
|
||||
WithdrawalsLimitExceeded {
|
||||
limit: usize,
|
||||
prior_withdrawals: usize,
|
||||
},
|
||||
/// Unreachable unless there's a logic error in LH.
|
||||
IncorrectExpectedWithdrawalsVariant,
|
||||
MissingLastWithdrawal,
|
||||
PendingAttestationInElectra,
|
||||
ExecutionPayloadBidInvalid {
|
||||
reason: ExecutionPayloadBidInvalid,
|
||||
},
|
||||
/// Builder payment index out of bounds (Gloas)
|
||||
BuilderPaymentIndexOutOfBounds(usize),
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for BlockProcessingError {
|
||||
@@ -147,6 +160,12 @@ impl From<milhouse::Error> for BlockProcessingError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExecutionPayloadBidInvalid> for BlockProcessingError {
|
||||
fn from(reason: ExecutionPayloadBidInvalid) -> Self {
|
||||
Self::ExecutionPayloadBidInvalid { reason }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockOperationError<HeaderInvalid>> for BlockProcessingError {
|
||||
fn from(e: BlockOperationError<HeaderInvalid>) -> BlockProcessingError {
|
||||
match e {
|
||||
@@ -364,6 +383,8 @@ pub enum AttestationInvalid {
|
||||
BadSignature,
|
||||
/// The indexed attestation created from this attestation was found to be invalid.
|
||||
BadIndexedAttestation(IndexedAttestationInvalid),
|
||||
/// The overloaded "data.index" field is invalid (post-Gloas).
|
||||
BadOverloadedDataIndex,
|
||||
}
|
||||
|
||||
impl From<BlockOperationError<IndexedAttestationInvalid>>
|
||||
@@ -440,6 +461,38 @@ pub enum ExitInvalid {
|
||||
PendingWithdrawalInQueue(u64),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum ExecutionPayloadBidInvalid {
|
||||
/// The validator set a non-zero amount for a self-build.
|
||||
SelfBuildNonZeroAmount,
|
||||
/// The signature is invalid.
|
||||
BadSignature,
|
||||
/// The builder is not active.
|
||||
BuilderNotActive(u64),
|
||||
/// The builder has insufficient balance to cover the bid
|
||||
InsufficientBalance {
|
||||
builder_index: u64,
|
||||
builder_balance: u64,
|
||||
bid_value: u64,
|
||||
},
|
||||
/// Bid slot doesn't match block slot
|
||||
SlotMismatch { bid_slot: Slot, block_slot: Slot },
|
||||
/// The bid's parent block hash doesn't match the state's latest block hash
|
||||
ParentBlockHashMismatch {
|
||||
state_block_hash: ExecutionBlockHash,
|
||||
bid_parent_hash: ExecutionBlockHash,
|
||||
},
|
||||
/// The bid's parent block root doesn't match the block's parent root
|
||||
ParentBlockRootMismatch {
|
||||
block_parent_root: Hash256,
|
||||
bid_parent_root: Hash256,
|
||||
},
|
||||
/// The bid's prev randao doesn't match the state.
|
||||
PrevRandaoMismatch { expected: Hash256, bid: Hash256 },
|
||||
/// The bid contains more than the maximum number of kzg blob commitments.
|
||||
ExcessBlobCommitments { max: usize, bid: usize },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum BlsExecutionChangeInvalid {
|
||||
/// The specified validator is not in the state's validator registry.
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::common::{
|
||||
slash_validator,
|
||||
};
|
||||
use crate::per_block_processing::errors::{BlockProcessingError, IntoWithIndex};
|
||||
use bls::{PublicKeyBytes, SignatureBytes};
|
||||
use ssz_types::FixedVector;
|
||||
use typenum::U33;
|
||||
use types::consts::altair::{PARTICIPATION_FLAG_WEIGHTS, PROPOSER_WEIGHT, WEIGHT_DENOMINATOR};
|
||||
@@ -38,9 +39,14 @@ pub fn process_operations<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||
process_bls_to_execution_changes(state, bls_to_execution_changes, verify_signatures, spec)?;
|
||||
}
|
||||
|
||||
if state.fork_name_unchecked().electra_enabled() {
|
||||
if state.fork_name_unchecked().electra_enabled() && !state.fork_name_unchecked().gloas_enabled()
|
||||
{
|
||||
state.update_pubkey_cache()?;
|
||||
process_deposit_requests(state, &block_body.execution_requests()?.deposits, spec)?;
|
||||
process_deposit_requests_pre_gloas(
|
||||
state,
|
||||
&block_body.execution_requests()?.deposits,
|
||||
spec,
|
||||
)?;
|
||||
process_withdrawal_requests(state, &block_body.execution_requests()?.withdrawals, spec)?;
|
||||
process_consolidation_requests(
|
||||
state,
|
||||
@@ -212,6 +218,148 @@ pub mod altair_deneb {
|
||||
}
|
||||
}
|
||||
|
||||
pub mod gloas {
|
||||
use super::*;
|
||||
use crate::common::update_progressive_balances_cache::update_progressive_balances_on_attestation;
|
||||
|
||||
pub fn process_attestations<'a, E: EthSpec, I>(
|
||||
state: &mut BeaconState<E>,
|
||||
attestations: I,
|
||||
verify_signatures: VerifySignatures,
|
||||
ctxt: &mut ConsensusContext<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError>
|
||||
where
|
||||
I: Iterator<Item = AttestationRef<'a, E>>,
|
||||
{
|
||||
attestations.enumerate().try_for_each(|(i, attestation)| {
|
||||
process_attestation(state, attestation, i, ctxt, verify_signatures, spec)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn process_attestation<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
attestation: AttestationRef<E>,
|
||||
att_index: usize,
|
||||
ctxt: &mut ConsensusContext<E>,
|
||||
verify_signatures: VerifySignatures,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
let proposer_index = ctxt.get_proposer_index(state, spec)?;
|
||||
let previous_epoch = ctxt.previous_epoch;
|
||||
let current_epoch = ctxt.current_epoch;
|
||||
|
||||
let indexed_att = verify_attestation_for_block_inclusion(
|
||||
state,
|
||||
attestation,
|
||||
ctxt,
|
||||
verify_signatures,
|
||||
spec,
|
||||
)
|
||||
.map_err(|e| e.into_with_index(att_index))?;
|
||||
|
||||
// Matching roots, participation flag indices
|
||||
let data = attestation.data();
|
||||
let inclusion_delay = state.slot().safe_sub(data.slot)?.as_u64();
|
||||
let participation_flag_indices =
|
||||
get_attestation_participation_flag_indices(state, data, inclusion_delay, spec)?;
|
||||
|
||||
// [New in EIP-7732]
|
||||
let current_epoch_target = data.target.epoch == state.current_epoch();
|
||||
let slot_mod = data
|
||||
.slot
|
||||
.as_usize()
|
||||
.safe_rem(E::slots_per_epoch() as usize)?;
|
||||
let payment_index = if current_epoch_target {
|
||||
(E::slots_per_epoch() as usize).safe_add(slot_mod)?
|
||||
} else {
|
||||
slot_mod
|
||||
};
|
||||
// Cached here to avoid repeat lookups. The withdrawal amount is immutable throughout
|
||||
// this whole function.
|
||||
let payment_withdrawal_amount = state
|
||||
.builder_pending_payments()?
|
||||
.get(payment_index)
|
||||
.ok_or(BlockProcessingError::BuilderPaymentIndexOutOfBounds(
|
||||
payment_index,
|
||||
))?
|
||||
.withdrawal
|
||||
.amount;
|
||||
|
||||
// Update epoch participation flags.
|
||||
let mut proposer_reward_numerator = 0;
|
||||
for index in indexed_att.attesting_indices_iter() {
|
||||
let index = *index as usize;
|
||||
|
||||
let validator_effective_balance = state.epoch_cache().get_effective_balance(index)?;
|
||||
let validator_slashed = state.slashings_cache().is_slashed(index);
|
||||
|
||||
// [New in Gloas:EIP7732]
|
||||
// For same-slot attestations, check if we're setting any new flags
|
||||
// If we are, this validator hasn't contributed to this slot's quorum yet
|
||||
let mut will_set_new_flag = false;
|
||||
|
||||
for (flag_index, &weight) in PARTICIPATION_FLAG_WEIGHTS.iter().enumerate() {
|
||||
let epoch_participation = state.get_epoch_participation_mut(
|
||||
data.target.epoch,
|
||||
previous_epoch,
|
||||
current_epoch,
|
||||
)?;
|
||||
|
||||
if participation_flag_indices.contains(&flag_index) {
|
||||
let validator_participation = epoch_participation
|
||||
.get_mut(index)
|
||||
.ok_or(BeaconStateError::ParticipationOutOfBounds(index))?;
|
||||
|
||||
if !validator_participation.has_flag(flag_index)? {
|
||||
validator_participation.add_flag(flag_index)?;
|
||||
proposer_reward_numerator
|
||||
.safe_add_assign(state.get_base_reward(index)?.safe_mul(weight)?)?;
|
||||
will_set_new_flag = true;
|
||||
|
||||
update_progressive_balances_on_attestation(
|
||||
state,
|
||||
data.target.epoch,
|
||||
flag_index,
|
||||
validator_effective_balance,
|
||||
validator_slashed,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [New in Gloas:EIP7732]
|
||||
// Add weight for same-slot attestations when any new flag is set.
|
||||
// This ensures each validator contributes exactly once per slot.
|
||||
if will_set_new_flag
|
||||
&& state.is_attestation_same_slot(data)?
|
||||
&& payment_withdrawal_amount > 0
|
||||
{
|
||||
let builder_payments = state.builder_pending_payments_mut()?;
|
||||
let payment = builder_payments.get_mut(payment_index).ok_or(
|
||||
BlockProcessingError::BuilderPaymentIndexOutOfBounds(payment_index),
|
||||
)?;
|
||||
payment
|
||||
.weight
|
||||
.safe_add_assign(validator_effective_balance)?;
|
||||
}
|
||||
}
|
||||
|
||||
let proposer_reward_denominator = WEIGHT_DENOMINATOR
|
||||
.safe_sub(PROPOSER_WEIGHT)?
|
||||
.safe_mul(WEIGHT_DENOMINATOR)?
|
||||
.safe_div(PROPOSER_WEIGHT)?;
|
||||
let proposer_reward = proposer_reward_numerator.safe_div(proposer_reward_denominator)?;
|
||||
increase_balance(state, proposer_index as usize, proposer_reward)?;
|
||||
|
||||
// [New in Gloas:EIP7732]
|
||||
// Update builder payment weight
|
||||
// No-op, this is done inline above.
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates each `ProposerSlashing` and updates the state, short-circuiting on an invalid object.
|
||||
///
|
||||
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
|
||||
@@ -235,6 +383,31 @@ pub fn process_proposer_slashings<E: EthSpec>(
|
||||
verify_proposer_slashing(proposer_slashing, state, verify_signatures, spec)
|
||||
.map_err(|e| e.into_with_index(i))?;
|
||||
|
||||
// [New in Gloas:EIP7732]
|
||||
// Remove the BuilderPendingPayment corresponding to this proposal
|
||||
// if it is still in the 2-epoch window.
|
||||
if state.fork_name_unchecked().gloas_enabled() {
|
||||
let slot = proposer_slashing.signed_header_1.message.slot;
|
||||
let proposal_epoch = slot.epoch(E::slots_per_epoch());
|
||||
let slot_in_epoch = slot.as_usize().safe_rem(E::SlotsPerEpoch::to_usize())?;
|
||||
|
||||
let payment_index = if proposal_epoch == state.current_epoch() {
|
||||
Some(E::SlotsPerEpoch::to_usize().safe_add(slot_in_epoch)?)
|
||||
} else if proposal_epoch == state.previous_epoch() {
|
||||
Some(slot_in_epoch)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(index) = payment_index {
|
||||
let payment = state
|
||||
.builder_pending_payments_mut()?
|
||||
.get_mut(index)
|
||||
.ok_or(BlockProcessingError::BuilderPaymentIndexOutOfBounds(index))?;
|
||||
*payment = BuilderPendingPayment::default();
|
||||
}
|
||||
}
|
||||
|
||||
slash_validator(
|
||||
state,
|
||||
proposer_slashing.signed_header_1.message.proposer_index as usize,
|
||||
@@ -285,7 +458,15 @@ pub fn process_attestations<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||
ctxt: &mut ConsensusContext<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
if state.fork_name_unchecked().altair_enabled() {
|
||||
if state.fork_name_unchecked().gloas_enabled() {
|
||||
gloas::process_attestations(
|
||||
state,
|
||||
block_body.attestations(),
|
||||
verify_signatures,
|
||||
ctxt,
|
||||
spec,
|
||||
)?;
|
||||
} else if state.fork_name_unchecked().altair_enabled() {
|
||||
altair_deneb::process_attestations(
|
||||
state,
|
||||
block_body.attestations(),
|
||||
@@ -586,7 +767,7 @@ pub fn process_withdrawal_requests<E: EthSpec>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn process_deposit_requests<E: EthSpec>(
|
||||
pub fn process_deposit_requests_pre_gloas<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
deposit_requests: &[DepositRequest],
|
||||
spec: &ChainSpec,
|
||||
@@ -613,6 +794,112 @@ pub fn process_deposit_requests<E: EthSpec>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn process_deposit_requests_post_gloas<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
deposit_requests: &[DepositRequest],
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
for request in deposit_requests {
|
||||
process_deposit_request_post_gloas(state, request, spec)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn process_deposit_request_post_gloas<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
deposit_request: &DepositRequest,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
// [New in Gloas:EIP7732]
|
||||
// Regardless of the withdrawal credentials prefix, if a builder/validator
|
||||
// already exists with this pubkey, apply the deposit to their balance
|
||||
// TODO(gloas): this could be more efficient in the builder case, see:
|
||||
// https://github.com/sigp/lighthouse/issues/8783
|
||||
let builder_index = state
|
||||
.builders()?
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, builder)| builder.pubkey == deposit_request.pubkey)
|
||||
.map(|(i, _)| i as u64);
|
||||
let is_builder = builder_index.is_some();
|
||||
|
||||
let validator_index = state.get_validator_index(&deposit_request.pubkey)?;
|
||||
let is_validator = validator_index.is_some();
|
||||
|
||||
let is_builder_prefix =
|
||||
is_builder_withdrawal_credential(deposit_request.withdrawal_credentials, spec);
|
||||
|
||||
if is_builder || (is_builder_prefix && !is_validator) {
|
||||
// Apply builder deposits immediately
|
||||
apply_deposit_for_builder(
|
||||
state,
|
||||
builder_index,
|
||||
deposit_request.pubkey,
|
||||
deposit_request.withdrawal_credentials,
|
||||
deposit_request.amount,
|
||||
deposit_request.signature.clone(),
|
||||
state.slot(),
|
||||
spec,
|
||||
)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Add validator deposits to the queue
|
||||
let slot = state.slot();
|
||||
state.pending_deposits_mut()?.push(PendingDeposit {
|
||||
pubkey: deposit_request.pubkey,
|
||||
withdrawal_credentials: deposit_request.withdrawal_credentials,
|
||||
amount: deposit_request.amount,
|
||||
signature: deposit_request.signature.clone(),
|
||||
slot,
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn apply_deposit_for_builder<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
builder_index_opt: Option<BuilderIndex>,
|
||||
pubkey: PublicKeyBytes,
|
||||
withdrawal_credentials: Hash256,
|
||||
amount: u64,
|
||||
signature: SignatureBytes,
|
||||
slot: Slot,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
match builder_index_opt {
|
||||
None => {
|
||||
// Verify the deposit signature (proof of possession) which is not checked by the deposit contract
|
||||
let deposit_data = DepositData {
|
||||
pubkey,
|
||||
withdrawal_credentials,
|
||||
amount,
|
||||
signature,
|
||||
};
|
||||
if is_valid_deposit_signature(&deposit_data, spec).is_ok() {
|
||||
state.add_builder_to_registry(
|
||||
pubkey,
|
||||
withdrawal_credentials,
|
||||
amount,
|
||||
slot,
|
||||
spec,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Some(builder_index) => {
|
||||
state
|
||||
.builders_mut()?
|
||||
.get_mut(builder_index as usize)
|
||||
.ok_or(BeaconStateError::UnknownBuilder(builder_index))?
|
||||
.balance
|
||||
.safe_add_assign(amount)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Make sure to build the pubkey cache before calling this function
|
||||
pub fn process_consolidation_requests<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
|
||||
@@ -9,11 +9,12 @@ use tree_hash::TreeHash;
|
||||
use typenum::Unsigned;
|
||||
use types::{
|
||||
AbstractExecPayload, AttesterSlashingRef, BeaconBlockRef, BeaconState, BeaconStateError,
|
||||
ChainSpec, DepositData, Domain, Epoch, EthSpec, Fork, Hash256, InconsistentFork,
|
||||
BuilderIndex, ChainSpec, DepositData, Domain, Epoch, EthSpec, Fork, Hash256, InconsistentFork,
|
||||
IndexedAttestation, IndexedAttestationRef, ProposerSlashing, SignedAggregateAndProof,
|
||||
SignedBeaconBlock, SignedBeaconBlockHeader, SignedBlsToExecutionChange,
|
||||
SignedContributionAndProof, SignedRoot, SignedVoluntaryExit, SigningData, Slot, SyncAggregate,
|
||||
SyncAggregatorSelectionData,
|
||||
SignedContributionAndProof, SignedExecutionPayloadBid, SignedRoot, SignedVoluntaryExit,
|
||||
SigningData, Slot, SyncAggregate, SyncAggregatorSelectionData,
|
||||
consts::gloas::BUILDER_INDEX_SELF_BUILD,
|
||||
};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -28,6 +29,9 @@ pub enum Error {
|
||||
/// Attempted to find the public key of a validator that does not exist. You cannot distinguish
|
||||
/// between an error and an invalid block in this case.
|
||||
ValidatorUnknown(u64),
|
||||
/// Attempted to find the public key of a builder that does not exist. You cannot distinguish
|
||||
/// between an error and an invalid block in this case.
|
||||
BuilderUnknown(BuilderIndex),
|
||||
/// Attempted to find the public key of a validator that does not exist. You cannot distinguish
|
||||
/// between an error and an invalid block in this case.
|
||||
ValidatorPubkeyUnknown(PublicKeyBytes),
|
||||
@@ -53,7 +57,7 @@ impl From<BeaconStateError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to get a public key from a `state`.
|
||||
/// Helper function to get a validator public key from a `state`.
|
||||
pub fn get_pubkey_from_state<E>(
|
||||
state: &BeaconState<E>,
|
||||
validator_index: usize,
|
||||
@@ -71,6 +75,25 @@ where
|
||||
.map(Cow::Owned)
|
||||
}
|
||||
|
||||
/// Helper function to get a builder public key from a `state`.
|
||||
pub fn get_builder_pubkey_from_state<E>(
|
||||
state: &BeaconState<E>,
|
||||
builder_index: BuilderIndex,
|
||||
) -> Option<Cow<'_, PublicKey>>
|
||||
where
|
||||
E: EthSpec,
|
||||
{
|
||||
state
|
||||
.builders()
|
||||
.ok()?
|
||||
.get(builder_index as usize)
|
||||
.and_then(|b| {
|
||||
let pk: Option<PublicKey> = b.pubkey.decompress().ok();
|
||||
pk
|
||||
})
|
||||
.map(Cow::Owned)
|
||||
}
|
||||
|
||||
/// A signature set that is valid if a block was signed by the expected block producer.
|
||||
pub fn block_proposal_signature_set<'a, E, F, Payload: AbstractExecPayload<E>>(
|
||||
state: &'a BeaconState<E>,
|
||||
@@ -332,6 +355,41 @@ where
|
||||
Ok(SignatureSet::multiple_pubkeys(signature, pubkeys, message))
|
||||
}
|
||||
|
||||
pub fn execution_payload_bid_signature_set<'a, E, F>(
|
||||
state: &'a BeaconState<E>,
|
||||
get_builder_pubkey: F,
|
||||
signed_execution_payload_bid: &'a SignedExecutionPayloadBid<E>,
|
||||
spec: &'a ChainSpec,
|
||||
) -> Result<Option<SignatureSet<'a>>>
|
||||
where
|
||||
E: EthSpec,
|
||||
F: Fn(BuilderIndex) -> Option<Cow<'a, PublicKey>>,
|
||||
{
|
||||
let execution_payload_bid = &signed_execution_payload_bid.message;
|
||||
let builder_index = execution_payload_bid.builder_index;
|
||||
if builder_index == BUILDER_INDEX_SELF_BUILD {
|
||||
// No signatures to verify in case of a self-build, but consensus code MUST check that
|
||||
// the signature is the point at infinity.
|
||||
// See `process_execution_payload_bid`.
|
||||
return Ok(None);
|
||||
}
|
||||
let domain = spec.get_domain(
|
||||
state.current_epoch(),
|
||||
Domain::BeaconBuilder,
|
||||
&state.fork(),
|
||||
state.genesis_validators_root(),
|
||||
);
|
||||
|
||||
let pubkey = get_builder_pubkey(builder_index).ok_or(Error::BuilderUnknown(builder_index))?;
|
||||
let message = execution_payload_bid.signing_root(domain);
|
||||
|
||||
Ok(Some(SignatureSet::single_pubkey(
|
||||
&signed_execution_payload_bid.signature,
|
||||
pubkey,
|
||||
message,
|
||||
)))
|
||||
}
|
||||
|
||||
/// Returns the signature set for the given `attester_slashing` and corresponding `pubkeys`.
|
||||
pub fn attester_slashing_signature_sets<'a, E, F>(
|
||||
state: &'a BeaconState<E>,
|
||||
|
||||
@@ -52,8 +52,6 @@ pub fn verify_attestation_for_block_inclusion<'ctxt, E: EthSpec>(
|
||||
///
|
||||
/// Returns a descriptive `Err` if the attestation is malformed or does not accurately reflect the
|
||||
/// prior blocks in `state`.
|
||||
///
|
||||
/// Spec v0.12.1
|
||||
pub fn verify_attestation_for_state<'ctxt, E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
attestation: AttestationRef<'ctxt, E>,
|
||||
@@ -74,7 +72,12 @@ pub fn verify_attestation_for_state<'ctxt, E: EthSpec>(
|
||||
);
|
||||
}
|
||||
AttestationRef::Electra(_) => {
|
||||
verify!(data.index == 0, Invalid::BadCommitteeIndex);
|
||||
let fork_at_attestation_slot = spec.fork_name_at_slot::<E>(data.slot);
|
||||
if fork_at_attestation_slot.gloas_enabled() {
|
||||
verify!(data.index < 2, Invalid::BadOverloadedDataIndex);
|
||||
} else {
|
||||
verify!(data.index == 0, Invalid::BadCommitteeIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,8 +92,6 @@ pub fn verify_attestation_for_state<'ctxt, E: EthSpec>(
|
||||
}
|
||||
|
||||
/// Check target epoch and source checkpoint.
|
||||
///
|
||||
/// Spec v0.12.1
|
||||
fn verify_casper_ffg_vote<E: EthSpec>(
|
||||
attestation: AttestationRef<E>,
|
||||
state: &BeaconState<E>,
|
||||
|
||||
@@ -0,0 +1,535 @@
|
||||
use crate::common::decrease_balance;
|
||||
use crate::per_block_processing::builder::{
|
||||
convert_builder_index_to_validator_index, convert_validator_index_to_builder_index,
|
||||
is_builder_index,
|
||||
};
|
||||
use crate::per_block_processing::errors::BlockProcessingError;
|
||||
use milhouse::List;
|
||||
use safe_arith::{SafeArith, SafeArithIter};
|
||||
use tree_hash::TreeHash;
|
||||
use types::{
|
||||
AbstractExecPayload, BeaconState, BeaconStateError, ChainSpec, EthSpec, ExecPayload,
|
||||
ExpectedWithdrawals, ExpectedWithdrawalsCapella, ExpectedWithdrawalsElectra,
|
||||
ExpectedWithdrawalsGloas, Validator, Withdrawal, Withdrawals,
|
||||
};
|
||||
|
||||
/// Compute the next batch of withdrawals which should be included in a block.
|
||||
///
|
||||
/// https://ethereum.github.io/consensus-specs/specs/gloas/beacon-chain/#modified-get_expected_withdrawals
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn get_expected_withdrawals<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<ExpectedWithdrawals<E>, BlockProcessingError> {
|
||||
let mut withdrawal_index = state.next_withdrawal_index()?;
|
||||
let mut withdrawals = Vec::<Withdrawal>::with_capacity(E::max_withdrawals_per_payload());
|
||||
|
||||
// [New in Gloas:EIP7732]
|
||||
// Get builder withdrawals
|
||||
let processed_builder_withdrawals_count =
|
||||
get_builder_withdrawals(state, &mut withdrawal_index, &mut withdrawals)?;
|
||||
|
||||
// [New in Electra:EIP7251]
|
||||
// Get partial withdrawals.
|
||||
let processed_partial_withdrawals_count =
|
||||
get_pending_partial_withdrawals(state, &mut withdrawal_index, &mut withdrawals, spec)?;
|
||||
|
||||
// [New in Gloas:EIP7732]
|
||||
// Get builders sweep withdrawals
|
||||
let processed_builders_sweep_count =
|
||||
get_builders_sweep_withdrawals(state, &mut withdrawal_index, &mut withdrawals)?;
|
||||
|
||||
// Get validators sweep withdrawals
|
||||
let processed_sweep_withdrawals_count =
|
||||
get_validators_sweep_withdrawals(state, &mut withdrawal_index, &mut withdrawals, spec)?;
|
||||
|
||||
let withdrawals = withdrawals
|
||||
.try_into()
|
||||
.map_err(BlockProcessingError::SszTypesError)?;
|
||||
|
||||
let fork_name = state.fork_name_unchecked();
|
||||
if fork_name.gloas_enabled() {
|
||||
Ok(ExpectedWithdrawals::Gloas(ExpectedWithdrawalsGloas {
|
||||
withdrawals,
|
||||
processed_builder_withdrawals_count: processed_builder_withdrawals_count
|
||||
.ok_or(BlockProcessingError::IncorrectExpectedWithdrawalsVariant)?,
|
||||
processed_partial_withdrawals_count: processed_partial_withdrawals_count
|
||||
.ok_or(BlockProcessingError::IncorrectExpectedWithdrawalsVariant)?,
|
||||
processed_builders_sweep_count: processed_builders_sweep_count
|
||||
.ok_or(BlockProcessingError::IncorrectExpectedWithdrawalsVariant)?,
|
||||
processed_sweep_withdrawals_count,
|
||||
}))
|
||||
} else if fork_name.electra_enabled() {
|
||||
Ok(ExpectedWithdrawals::Electra(ExpectedWithdrawalsElectra {
|
||||
withdrawals,
|
||||
processed_partial_withdrawals_count: processed_partial_withdrawals_count
|
||||
.ok_or(BlockProcessingError::IncorrectExpectedWithdrawalsVariant)?,
|
||||
processed_sweep_withdrawals_count,
|
||||
}))
|
||||
} else {
|
||||
Ok(ExpectedWithdrawals::Capella(ExpectedWithdrawalsCapella {
|
||||
withdrawals,
|
||||
processed_sweep_withdrawals_count,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_builder_withdrawals<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
withdrawal_index: &mut u64,
|
||||
withdrawals: &mut Vec<Withdrawal>,
|
||||
) -> Result<Option<u64>, BlockProcessingError> {
|
||||
let Ok(builder_pending_withdrawals) = state.builder_pending_withdrawals() else {
|
||||
// Pre-Gloas, nothing to do.
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let withdrawals_limit = E::max_withdrawals_per_payload().safe_sub(1)?;
|
||||
|
||||
block_verify!(
|
||||
withdrawals.len() <= withdrawals_limit,
|
||||
BlockProcessingError::WithdrawalsLimitExceeded {
|
||||
limit: withdrawals_limit,
|
||||
prior_withdrawals: withdrawals.len()
|
||||
}
|
||||
);
|
||||
|
||||
let mut processed_count = 0;
|
||||
for withdrawal in builder_pending_withdrawals {
|
||||
let has_reached_limit = withdrawals.len() == withdrawals_limit;
|
||||
|
||||
if has_reached_limit {
|
||||
break;
|
||||
}
|
||||
|
||||
let builder_index = withdrawal.builder_index;
|
||||
|
||||
withdrawals.push(Withdrawal {
|
||||
index: *withdrawal_index,
|
||||
validator_index: convert_builder_index_to_validator_index(builder_index),
|
||||
address: withdrawal.fee_recipient,
|
||||
amount: withdrawal.amount,
|
||||
});
|
||||
withdrawal_index.safe_add_assign(1)?;
|
||||
processed_count.safe_add_assign(1)?;
|
||||
}
|
||||
Ok(Some(processed_count))
|
||||
}
|
||||
|
||||
pub fn get_pending_partial_withdrawals<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
withdrawal_index: &mut u64,
|
||||
withdrawals: &mut Vec<Withdrawal>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Option<u64>, BlockProcessingError> {
|
||||
let Ok(pending_partial_withdrawals) = state.pending_partial_withdrawals() else {
|
||||
// Pre-Electra nothing to do.
|
||||
return Ok(None);
|
||||
};
|
||||
let epoch = state.current_epoch();
|
||||
|
||||
let withdrawals_limit = std::cmp::min(
|
||||
withdrawals
|
||||
.len()
|
||||
.safe_add(spec.max_pending_partials_per_withdrawals_sweep as usize)?,
|
||||
E::max_withdrawals_per_payload().safe_sub(1)?,
|
||||
);
|
||||
|
||||
block_verify!(
|
||||
withdrawals.len() <= withdrawals_limit,
|
||||
BlockProcessingError::WithdrawalsLimitExceeded {
|
||||
limit: withdrawals_limit,
|
||||
prior_withdrawals: withdrawals.len()
|
||||
}
|
||||
);
|
||||
|
||||
let mut processed_count = 0;
|
||||
for withdrawal in pending_partial_withdrawals {
|
||||
let is_withdrawable = withdrawal.withdrawable_epoch <= epoch;
|
||||
let has_reached_limit = withdrawals.len() >= withdrawals_limit;
|
||||
|
||||
if !is_withdrawable || has_reached_limit {
|
||||
break;
|
||||
}
|
||||
|
||||
let validator_index = withdrawal.validator_index;
|
||||
let validator = state.get_validator(validator_index as usize)?;
|
||||
let balance = get_balance_after_withdrawals(state, validator_index, withdrawals)?;
|
||||
|
||||
if is_eligible_for_partial_withdrawals(validator, balance, spec) {
|
||||
let withdrawal_amount = std::cmp::min(
|
||||
balance.safe_sub(spec.min_activation_balance)?,
|
||||
withdrawal.amount,
|
||||
);
|
||||
withdrawals.push(Withdrawal {
|
||||
index: *withdrawal_index,
|
||||
validator_index,
|
||||
address: validator
|
||||
.get_execution_withdrawal_address(spec)
|
||||
.ok_or(BeaconStateError::NonExecutionAddressWithdrawalCredential)?,
|
||||
amount: withdrawal_amount,
|
||||
});
|
||||
withdrawal_index.safe_add_assign(1)?;
|
||||
}
|
||||
processed_count.safe_add_assign(1)?;
|
||||
}
|
||||
|
||||
Ok(Some(processed_count))
|
||||
}
|
||||
|
||||
/// Get withdrawals from the builders sweep.
|
||||
///
|
||||
/// This function iterates through builders starting from `next_withdrawal_builder_index`
|
||||
/// and adds withdrawals for builders whose withdrawable_epoch has been reached and have balance.
|
||||
///
|
||||
/// https://ethereum.github.io/consensus-specs/specs/gloas/beacon-chain/#new-get_builders_sweep_withdrawals
|
||||
pub fn get_builders_sweep_withdrawals<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
withdrawal_index: &mut u64,
|
||||
withdrawals: &mut Vec<Withdrawal>,
|
||||
) -> Result<Option<u64>, BlockProcessingError> {
|
||||
let Ok(builders) = state.builders() else {
|
||||
// Pre-Gloas, nothing to do.
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
if builders.is_empty() {
|
||||
return Ok(Some(0));
|
||||
}
|
||||
|
||||
let epoch = state.current_epoch();
|
||||
let builders_limit = std::cmp::min(builders.len(), E::max_builders_per_withdrawals_sweep());
|
||||
|
||||
let withdrawals_limit = E::max_withdrawals_per_payload().safe_sub(1)?;
|
||||
|
||||
block_verify!(
|
||||
withdrawals.len() <= withdrawals_limit,
|
||||
BlockProcessingError::WithdrawalsLimitExceeded {
|
||||
limit: withdrawals_limit,
|
||||
prior_withdrawals: withdrawals.len()
|
||||
}
|
||||
);
|
||||
|
||||
let mut processed_count: u64 = 0;
|
||||
let mut builder_index = state.next_withdrawal_builder_index()?;
|
||||
|
||||
for _ in 0..builders_limit {
|
||||
if withdrawals.len() >= withdrawals_limit {
|
||||
break;
|
||||
}
|
||||
|
||||
let builder = builders
|
||||
.get(builder_index as usize)
|
||||
.ok_or(BeaconStateError::UnknownBuilder(builder_index))?;
|
||||
|
||||
if builder.withdrawable_epoch <= epoch && builder.balance > 0 {
|
||||
withdrawals.push(Withdrawal {
|
||||
index: *withdrawal_index,
|
||||
validator_index: convert_builder_index_to_validator_index(builder_index),
|
||||
address: builder.execution_address,
|
||||
amount: builder.balance,
|
||||
});
|
||||
withdrawal_index.safe_add_assign(1)?;
|
||||
}
|
||||
|
||||
builder_index = builder_index.safe_add(1)?.safe_rem(builders.len() as u64)?;
|
||||
processed_count.safe_add_assign(1)?;
|
||||
}
|
||||
|
||||
Ok(Some(processed_count))
|
||||
}
|
||||
|
||||
/// Get withdrawals from the validator sweep.
|
||||
///
|
||||
/// This function iterates through validators starting from `next_withdrawal_validator_index`
|
||||
/// and adds full or partial withdrawals for eligible validators.
|
||||
///
|
||||
/// https://ethereum.github.io/consensus-specs/specs/capella/beacon-chain/#new-get_validators_sweep_withdrawals
|
||||
pub fn get_validators_sweep_withdrawals<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
withdrawal_index: &mut u64,
|
||||
withdrawals: &mut Vec<Withdrawal>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<u64, BlockProcessingError> {
|
||||
let epoch = state.current_epoch();
|
||||
let fork_name = state.fork_name_unchecked();
|
||||
let mut validator_index = state.next_withdrawal_validator_index()?;
|
||||
let validators_limit = std::cmp::min(
|
||||
state.validators().len() as u64,
|
||||
spec.max_validators_per_withdrawals_sweep,
|
||||
);
|
||||
let withdrawals_limit = E::max_withdrawals_per_payload();
|
||||
|
||||
// There must be at least one space reserved for validator sweep withdrawals
|
||||
block_verify!(
|
||||
withdrawals.len() < withdrawals_limit,
|
||||
BlockProcessingError::WithdrawalsLimitExceeded {
|
||||
limit: withdrawals_limit,
|
||||
prior_withdrawals: withdrawals.len()
|
||||
}
|
||||
);
|
||||
|
||||
let mut processed_count: u64 = 0;
|
||||
|
||||
for _ in 0..validators_limit {
|
||||
if withdrawals.len() >= withdrawals_limit {
|
||||
break;
|
||||
}
|
||||
|
||||
let validator = state.get_validator(validator_index as usize)?;
|
||||
let balance = get_balance_after_withdrawals(state, validator_index, withdrawals)?;
|
||||
|
||||
if validator.is_fully_withdrawable_validator(balance, epoch, spec, fork_name) {
|
||||
withdrawals.push(Withdrawal {
|
||||
index: *withdrawal_index,
|
||||
validator_index,
|
||||
address: validator
|
||||
.get_execution_withdrawal_address(spec)
|
||||
.ok_or(BlockProcessingError::WithdrawalCredentialsInvalid)?,
|
||||
amount: balance,
|
||||
});
|
||||
withdrawal_index.safe_add_assign(1)?;
|
||||
} else if validator.is_partially_withdrawable_validator(balance, spec, fork_name) {
|
||||
withdrawals.push(Withdrawal {
|
||||
index: *withdrawal_index,
|
||||
validator_index,
|
||||
address: validator
|
||||
.get_execution_withdrawal_address(spec)
|
||||
.ok_or(BlockProcessingError::WithdrawalCredentialsInvalid)?,
|
||||
amount: balance.safe_sub(validator.get_max_effective_balance(spec, fork_name))?,
|
||||
});
|
||||
withdrawal_index.safe_add_assign(1)?;
|
||||
}
|
||||
|
||||
validator_index = validator_index
|
||||
.safe_add(1)?
|
||||
.safe_rem(state.validators().len() as u64)?;
|
||||
processed_count.safe_add_assign(1)?;
|
||||
}
|
||||
|
||||
Ok(processed_count)
|
||||
}
|
||||
|
||||
pub fn get_balance_after_withdrawals<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
validator_index: u64,
|
||||
withdrawals: &[Withdrawal],
|
||||
) -> Result<u64, BeaconStateError> {
|
||||
let withdrawn = withdrawals
|
||||
.iter()
|
||||
.filter(|withdrawal| withdrawal.validator_index == validator_index)
|
||||
.map(|withdrawal| withdrawal.amount)
|
||||
.safe_sum()?;
|
||||
state
|
||||
.get_balance(validator_index as usize)?
|
||||
.safe_sub(withdrawn)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn is_eligible_for_partial_withdrawals(
|
||||
validator: &Validator,
|
||||
balance: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> bool {
|
||||
let has_sufficient_effective_balance =
|
||||
validator.effective_balance >= spec.min_activation_balance;
|
||||
let has_excess_balance = balance > spec.min_activation_balance;
|
||||
validator.exit_epoch == spec.far_future_epoch
|
||||
&& has_sufficient_effective_balance
|
||||
&& has_excess_balance
|
||||
}
|
||||
|
||||
fn update_next_withdrawal_index<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
withdrawals: &Withdrawals<E>,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
// Update the next withdrawal index if this block contained withdrawals
|
||||
if let Some(latest_withdrawal) = withdrawals.last() {
|
||||
*state.next_withdrawal_index_mut()? = latest_withdrawal.index.safe_add(1)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_payload_expected_withdrawals<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
withdrawals: &Withdrawals<E>,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
*state.payload_expected_withdrawals_mut()? = List::new(withdrawals.to_vec())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_builder_pending_withdrawals<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
processed_builder_withdrawals_count: u64,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
state
|
||||
.builder_pending_withdrawals_mut()?
|
||||
.pop_front(processed_builder_withdrawals_count as usize)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_pending_partial_withdrawals<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
processed_partial_withdrawals_count: u64,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
state
|
||||
.pending_partial_withdrawals_mut()?
|
||||
.pop_front(processed_partial_withdrawals_count as usize)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_next_withdrawal_builder_index<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
processed_builders_sweep_count: u64,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
if !state.builders()?.is_empty() {
|
||||
// Update the next builder index to start the next withdrawal sweep
|
||||
let next_index = state
|
||||
.next_withdrawal_builder_index()?
|
||||
.safe_add(processed_builders_sweep_count)?;
|
||||
let next_builder_index = next_index.safe_rem(state.builders()?.len() as u64)?;
|
||||
*state.next_withdrawal_builder_index_mut()? = next_builder_index;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_next_withdrawal_validator_index<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
withdrawals: &Withdrawals<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
// Update the next validator index to start the next withdrawal sweep
|
||||
if withdrawals.len() == E::max_withdrawals_per_payload() {
|
||||
// Next sweep starts after the latest withdrawal's validator index
|
||||
let latest_withdrawal = withdrawals
|
||||
.last()
|
||||
.ok_or(BlockProcessingError::MissingLastWithdrawal)?;
|
||||
let next_validator_index = latest_withdrawal
|
||||
.validator_index
|
||||
.safe_add(1)?
|
||||
.safe_rem(state.validators().len() as u64)?;
|
||||
*state.next_withdrawal_validator_index_mut()? = next_validator_index;
|
||||
} else {
|
||||
// Advance sweep by the max length of the sweep if there was not a full set of withdrawals
|
||||
let next_validator_index = state
|
||||
.next_withdrawal_validator_index()?
|
||||
.safe_add(spec.max_validators_per_withdrawals_sweep)?
|
||||
.safe_rem(state.validators().len() as u64)?;
|
||||
*state.next_withdrawal_validator_index_mut()? = next_validator_index;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn apply_withdrawals<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
withdrawals: &Withdrawals<E>,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
for withdrawal in withdrawals {
|
||||
if state.fork_name_unchecked().gloas_enabled()
|
||||
&& is_builder_index(withdrawal.validator_index)
|
||||
{
|
||||
let builder_index =
|
||||
convert_validator_index_to_builder_index(withdrawal.validator_index);
|
||||
let builder = state
|
||||
.builders_mut()?
|
||||
.get_mut(builder_index as usize)
|
||||
.ok_or(BeaconStateError::UnknownBuilder(builder_index))?;
|
||||
builder.balance = builder.balance.saturating_sub(withdrawal.amount);
|
||||
} else {
|
||||
decrease_balance(
|
||||
state,
|
||||
withdrawal.validator_index as usize,
|
||||
withdrawal.amount,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub mod capella_electra {
|
||||
use super::*;
|
||||
|
||||
/// Apply withdrawals to the state.
|
||||
pub fn process_withdrawals<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||
state: &mut BeaconState<E>,
|
||||
payload: Payload::Ref<'_>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
let expected_withdrawals = get_expected_withdrawals(state, spec)?;
|
||||
|
||||
let expected_root = expected_withdrawals.withdrawals().tree_hash_root();
|
||||
let withdrawals_root = payload.withdrawals_root()?;
|
||||
if expected_root != withdrawals_root {
|
||||
return Err(BlockProcessingError::WithdrawalsRootMismatch {
|
||||
expected: expected_root,
|
||||
found: withdrawals_root,
|
||||
});
|
||||
}
|
||||
|
||||
// Apply expected withdrawals.
|
||||
apply_withdrawals(state, expected_withdrawals.withdrawals())?;
|
||||
|
||||
// [Common] Update withdrawals fields in the state
|
||||
update_next_withdrawal_index(state, expected_withdrawals.withdrawals())?;
|
||||
|
||||
// [New in Electra:EIP7251]
|
||||
if let Ok(processed_partial_withdrawals_count) =
|
||||
expected_withdrawals.processed_partial_withdrawals_count()
|
||||
{
|
||||
update_pending_partial_withdrawals(state, processed_partial_withdrawals_count)?;
|
||||
}
|
||||
|
||||
// [Common from Capella]
|
||||
update_next_withdrawal_validator_index(state, expected_withdrawals.withdrawals(), spec)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub mod gloas {
|
||||
use super::*;
|
||||
|
||||
/// Apply withdrawals to the state.
|
||||
pub fn process_withdrawals<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
if !state.is_parent_block_full() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let ExpectedWithdrawals::Gloas(ExpectedWithdrawalsGloas {
|
||||
withdrawals,
|
||||
processed_builder_withdrawals_count,
|
||||
processed_partial_withdrawals_count,
|
||||
processed_builders_sweep_count,
|
||||
processed_sweep_withdrawals_count: _,
|
||||
}) = get_expected_withdrawals(state, spec)?
|
||||
else {
|
||||
return Err(BlockProcessingError::IncorrectExpectedWithdrawalsVariant);
|
||||
};
|
||||
|
||||
// Apply expected withdrawals.
|
||||
apply_withdrawals(state, &withdrawals)?;
|
||||
|
||||
// [Common] Update withdrawals fields in the state
|
||||
update_next_withdrawal_index(state, &withdrawals)?;
|
||||
|
||||
// [New in Gloas:EIP7732]
|
||||
update_payload_expected_withdrawals(state, &withdrawals)?;
|
||||
|
||||
// [New in Gloas:EIP7732]
|
||||
update_builder_pending_withdrawals(state, processed_builder_withdrawals_count)?;
|
||||
|
||||
// [Common from Electra]
|
||||
update_pending_partial_withdrawals(state, processed_partial_withdrawals_count)?;
|
||||
|
||||
// [New in Gloas:EIP7732]
|
||||
update_next_withdrawal_builder_index(state, processed_builders_sweep_count)?;
|
||||
|
||||
// [Common from Capella]
|
||||
update_next_withdrawal_validator_index(state, &withdrawals, spec)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
use crate::EpochProcessingError;
|
||||
use crate::per_epoch_processing::single_pass::{SinglePassConfig, process_epoch_single_pass};
|
||||
use types::beacon_state::BeaconState;
|
||||
use types::chain_spec::ChainSpec;
|
||||
use types::eth_spec::EthSpec;
|
||||
use types::core::{ChainSpec, EthSpec};
|
||||
use types::state::BeaconState;
|
||||
|
||||
/// Slow version of `process_inactivity_updates` that runs a subset of single-pass processing.
|
||||
///
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::EpochProcessingError;
|
||||
use milhouse::List;
|
||||
use types::beacon_state::BeaconState;
|
||||
use types::eth_spec::EthSpec;
|
||||
use types::participation_flags::ParticipationFlags;
|
||||
use types::attestation::ParticipationFlags;
|
||||
use types::core::EthSpec;
|
||||
use types::state::BeaconState;
|
||||
|
||||
pub fn process_participation_flag_updates<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use crate::EpochProcessingError;
|
||||
use safe_arith::SafeArith;
|
||||
use std::sync::Arc;
|
||||
use types::beacon_state::BeaconState;
|
||||
use types::chain_spec::ChainSpec;
|
||||
use types::eth_spec::EthSpec;
|
||||
use types::core::{ChainSpec, EthSpec};
|
||||
use types::state::BeaconState;
|
||||
|
||||
pub fn process_sync_committee_updates<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::EpochProcessingError;
|
||||
use types::beacon_state::BeaconState;
|
||||
use types::eth_spec::EthSpec;
|
||||
use types::core::EthSpec;
|
||||
use types::state::BeaconState;
|
||||
|
||||
pub fn process_participation_record_updates<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::EpochProcessingError;
|
||||
use safe_arith::SafeArith;
|
||||
use types::historical_summary::HistoricalSummary;
|
||||
use types::state::HistoricalSummary;
|
||||
use types::{BeaconState, EthSpec};
|
||||
|
||||
pub fn process_historical_summaries_update<E: EthSpec>(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::errors::EpochProcessingError;
|
||||
use crate::per_epoch_processing::single_pass::{SinglePassConfig, process_epoch_single_pass};
|
||||
use safe_arith::SafeArith;
|
||||
use types::beacon_state::BeaconState;
|
||||
use types::chain_spec::ChainSpec;
|
||||
use types::core::ChainSpec;
|
||||
use types::state::BeaconState;
|
||||
use types::{BeaconStateError, EthSpec};
|
||||
|
||||
/// This implementation is now only used in phase0. Later hard forks use single-pass.
|
||||
|
||||
@@ -2,8 +2,8 @@ use super::errors::EpochProcessingError;
|
||||
use safe_arith::SafeArith;
|
||||
use tree_hash::TreeHash;
|
||||
use typenum::Unsigned;
|
||||
use types::beacon_state::BeaconState;
|
||||
use types::eth_spec::EthSpec;
|
||||
use types::core::EthSpec;
|
||||
use types::state::BeaconState;
|
||||
|
||||
pub fn process_historical_roots_update<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
|
||||
@@ -2,8 +2,8 @@ use super::errors::EpochProcessingError;
|
||||
use milhouse::List;
|
||||
use safe_arith::SafeArith;
|
||||
use typenum::Unsigned;
|
||||
use types::beacon_state::BeaconState;
|
||||
use types::eth_spec::EthSpec;
|
||||
use types::core::EthSpec;
|
||||
use types::state::BeaconState;
|
||||
|
||||
pub fn process_eth1_data_reset<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
|
||||
@@ -886,7 +886,7 @@ impl SlashingsContext {
|
||||
) -> Result<Self, Error> {
|
||||
let sum_slashings = state.get_all_slashings().iter().copied().safe_sum()?;
|
||||
let adjusted_total_slashing_balance = min(
|
||||
sum_slashings.safe_mul(spec.proportional_slashing_multiplier_for_state(state))?,
|
||||
sum_slashings.safe_mul(state.get_proportional_slashing_multiplier(spec))?,
|
||||
state_ctxt.total_active_balance,
|
||||
);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn process_slashings<E: EthSpec>(
|
||||
let sum_slashings = state.get_all_slashings().iter().copied().safe_sum()?;
|
||||
|
||||
let adjusted_total_slashing_balance = std::cmp::min(
|
||||
sum_slashings.safe_mul(spec.proportional_slashing_multiplier_for_state(state))?,
|
||||
sum_slashings.safe_mul(state.get_proportional_slashing_multiplier(spec))?,
|
||||
total_balance,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use bls::Hash256;
|
||||
use milhouse::{List, Vector};
|
||||
use ssz_types::BitVector;
|
||||
use std::mem;
|
||||
use typenum::Unsigned;
|
||||
use types::{
|
||||
BeaconState, BeaconStateError as Error, BeaconStateGloas, BuilderPendingPayment, ChainSpec,
|
||||
EthSpec, ExecutionPayloadBid, Fork,
|
||||
@@ -88,15 +88,23 @@ pub fn upgrade_state_to_gloas<E: EthSpec>(
|
||||
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
|
||||
execution_payload_availability: BitVector::default(), // All bits set to false initially
|
||||
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,
|
||||
latest_withdrawals_root: Hash256::default(),
|
||||
payload_expected_withdrawals: List::default(),
|
||||
// Caches
|
||||
total_active_balance: pre.total_active_balance,
|
||||
progressive_balances_cache: mem::take(&mut pre.progressive_balances_cache),
|
||||
@@ -105,7 +113,6 @@ pub fn upgrade_state_to_gloas<E: EthSpec>(
|
||||
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: mem::take(&mut pre.proposer_lookahead),
|
||||
});
|
||||
Ok(post)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user