Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4794,7 +4794,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let proposal_epoch = proposal_slot.epoch(T::EthSpec::slots_per_epoch());
if head_state.current_epoch() == proposal_epoch {
return get_expected_withdrawals(&unadvanced_state, &self.spec)
.map(|(withdrawals, _)| withdrawals)
.map(|(withdrawals, _, _, _, _)| withdrawals)
.map_err(Error::PrepareProposerFailed);
}

Expand All @@ -4812,7 +4812,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
&self.spec,
)?;
get_expected_withdrawals(&advanced_state, &self.spec)
.map(|(withdrawals, _)| withdrawals)
.map(|(withdrawals, _, _, _, _)| withdrawals)
.map_err(Error::PrepareProposerFailed)
}

Expand Down
5 changes: 3 additions & 2 deletions beacon_node/beacon_chain/tests/store_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,7 @@ async fn proposer_shuffling_root_consistency_at_fork_boundary() {
}

#[tokio::test]
#[allow(clippy::large_stack_frames)]
async fn proposer_shuffling_changing_with_lookahead() {
let initial_blocks = E::slots_per_epoch() * 4 - 1;

Expand Down Expand Up @@ -1412,7 +1413,7 @@ async fn proposer_shuffling_changing_with_lookahead() {

let consolidation_request: ConsolidationRequest = ConsolidationRequest {
source_address: validator_to_topup
.get_execution_withdrawal_address(spec)
.get_execution_withdrawal_address(spec, ForkName::Fulu)
.unwrap(),
source_pubkey: validator_to_topup.pubkey,
target_pubkey: validator_to_topup.pubkey,
Expand Down Expand Up @@ -1491,7 +1492,7 @@ async fn proposer_shuffling_changing_with_lookahead() {
let validator = current_epoch_state
.get_validator(validator_to_topup_index)
.unwrap();
assert!(validator.has_compounding_withdrawal_credential(spec));
assert!(validator.has_compounding_withdrawal_credential(spec, ForkName::Fulu));
assert_eq!(validator.effective_balance, 95_000_000_000);

// The shuffling for the current epoch from `prev_epoch_state` should match the shuffling
Expand Down
2 changes: 1 addition & 1 deletion beacon_node/http_api/src/builder_states.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub fn get_next_withdrawals<T: BeaconChainTypes>(
}

match get_expected_withdrawals(&state, &chain.spec) {
Ok((withdrawals, _)) => Ok(withdrawals),
Ok((withdrawals, _, _, _, _)) => Ok(withdrawals),
Err(e) => Err(warp_utils::reject::custom_server_error(format!(
"failed to get expected withdrawal: {:?}",
e
Expand Down
22 changes: 12 additions & 10 deletions beacon_node/operation_pool/src/bls_to_execution_changes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,16 +113,18 @@ impl<E: EthSpec> BlsToExecutionChanges<E> {
.validators()
.get(validator_index as usize)
.is_none_or(|validator| {
let prune = validator.has_execution_withdrawal_credential(spec)
&& head_block
.message()
.body()
.bls_to_execution_changes()
.map_or(true, |recent_changes| {
!recent_changes
.iter()
.any(|c| c.message.validator_index == validator_index)
});
let prune = validator.has_execution_withdrawal_credential(
spec,
head_state.fork_name_unchecked(),
) && head_block
.message()
.body()
.bls_to_execution_changes()
.map_or(true, |recent_changes| {
!recent_changes
.iter()
.any(|c| c.message.validator_index == validator_index)
});
if prune {
validator_indices_pruned.push(validator_index);
}
Expand Down
7 changes: 6 additions & 1 deletion beacon_node/operation_pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,12 @@ impl<E: EthSpec> OperationPool<E> {
address_change.signature_is_still_valid(&state.fork())
&& state
.get_validator(address_change.as_inner().message.validator_index as usize)
.is_ok_and(|validator| !validator.has_execution_withdrawal_credential(spec))
.is_ok_and(|validator| {
!validator.has_execution_withdrawal_credential(
spec,
state.fork_name_unchecked(),
)
})
},
|address_change| address_change.as_inner().clone(),
E::MaxBlsToExecutionChanges::to_usize(),
Expand Down
2 changes: 2 additions & 0 deletions beacon_node/store/src/consensus_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ impl<E: EthSpec> OnDiskConsensusContext<E> {
proposer_index,
current_block_root,
indexed_attestations,
indexed_payload_attestations: _,
// TODO(EIP-7732): add indexed_payload_attestations to the on-disk format.
} = ctxt;
OnDiskConsensusContext {
slot,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615

# Genesis
# ---------------------------------------------------------------
# `2**14` (= 16,384)
# 2**14 (= 16,384) validators
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 16384
# Dec 1, 2020, 12pm UTC
MIN_GENESIS_TIME: 1606824000
# Mainnet initial fork version, recommend altering for testnets
# Initial fork version for mainnet
GENESIS_FORK_VERSION: 0x00000000
# 604800 seconds (7 days)
# 7 * 24 * 3,600 (= 604,800) seconds, 7 days
GENESIS_DELAY: 604800

# Forking
Expand All @@ -39,25 +39,34 @@ GENESIS_DELAY: 604800

# Altair
ALTAIR_FORK_VERSION: 0x01000000
ALTAIR_FORK_EPOCH: 74240 # Oct 27, 2021, 10:56:23am UTC
ALTAIR_FORK_EPOCH: 74240 # Oct 27, 2021, 10:56:23am UTC
# Bellatrix
BELLATRIX_FORK_VERSION: 0x02000000
BELLATRIX_FORK_EPOCH: 144896 # Sept 6, 2022, 11:34:47am UTC
BELLATRIX_FORK_EPOCH: 144896 # Sept 6, 2022, 11:34:47am UTC
# Capella
CAPELLA_FORK_VERSION: 0x03000000
CAPELLA_FORK_EPOCH: 194048 # April 12, 2023, 10:27:35pm UTC
CAPELLA_FORK_EPOCH: 194048 # April 12, 2023, 10:27:35pm UTC
# Deneb
DENEB_FORK_VERSION: 0x04000000
DENEB_FORK_EPOCH: 269568 # March 13, 2024, 01:55:35pm UTC
DENEB_FORK_EPOCH: 269568 # March 13, 2024, 01:55:35pm UTC
# Electra
ELECTRA_FORK_VERSION: 0x05000000
ELECTRA_FORK_EPOCH: 364032 # May 7, 2025, 10:05:11am UTC
ELECTRA_FORK_EPOCH: 364032 # May 7, 2025, 10:05:11am UTC
# Fulu
FULU_FORK_VERSION: 0x06000000
FULU_FORK_EPOCH: 411392 # December 3, 2025, 09:49:11pm UTC
FULU_FORK_EPOCH: 411392 # December 3, 2025, 09:49:11pm UTC
# Gloas
GLOAS_FORK_VERSION: 0x07000000
GLOAS_FORK_VERSION: 0x07000000 # temporary stub
GLOAS_FORK_EPOCH: 18446744073709551615
# EIP7441
EIP7441_FORK_VERSION: 0x08000000 # temporary stub
EIP7441_FORK_EPOCH: 18446744073709551615
# EIP7805
EIP7805_FORK_VERSION: 0x0a000000 # temporary stub
EIP7805_FORK_EPOCH: 18446744073709551615
# EIP7928
EIP7928_FORK_VERSION: 0x0b000000 # temporary stub
EIP7928_FORK_EPOCH: 18446744073709551615

# Time parameters
# ---------------------------------------------------------------
Expand Down Expand Up @@ -86,6 +95,28 @@ SYNC_MESSAGE_DUE_BPS: 3333
# 6667 basis points, ~67% of SLOT_DURATION_MS
CONTRIBUTION_DUE_BPS: 6667

# Gloas
# 2**12 (= 4,096) epochs
MIN_BUILDER_WITHDRAWABILITY_DELAY: 4096
# 2500 basis points, 25% of SLOT_DURATION_MS
ATTESTATION_DUE_BPS_GLOAS: 2500
# 5000 basis points, 50% of SLOT_DURATION_MS
AGGREGATE_DUE_BPS_GLOAS: 5000
# 2500 basis points, 25% of SLOT_DURATION_MS
SYNC_MESSAGE_DUE_BPS_GLOAS: 2500
# 5000 basis points, 50% of SLOT_DURATION_MS
CONTRIBUTION_DUE_BPS_GLOAS: 5000
# 7500 basis points, 75% of SLOT_DURATION_MS
PAYLOAD_ATTESTATION_DUE_BPS: 7500

# EIP7805
# 7500 basis points, 75% of SLOT_DURATION_MS
VIEW_FREEZE_CUTOFF_BPS: 7500
# 6667 basis points, ~67% of SLOT_DURATION_MS
INCLUSION_LIST_SUBMISSION_DUE_BPS: 6667
# 9167 basis points, ~92% of SLOT_DURATION_MS
PROPOSER_INCLUSION_LIST_CUTOFF_BPS: 9167

# Validator cycle
# ---------------------------------------------------------------
# 2**2 (= 4)
Expand Down Expand Up @@ -137,10 +168,6 @@ MAX_REQUEST_BLOCKS: 1024
EPOCHS_PER_SUBNET_SUBSCRIPTION: 256
# MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2 (= 33,024) epochs
MIN_EPOCHS_FOR_BLOCK_REQUESTS: 33024
# 5s
TTFB_TIMEOUT: 5
# 10s
RESP_TIMEOUT: 10
# 2**5 (= 32) slots
ATTESTATION_PROPAGATION_SLOT_RANGE: 32
# 500ms
Expand All @@ -154,9 +181,7 @@ ATTESTATION_SUBNET_COUNT: 64
# 0 bits
ATTESTATION_SUBNET_EXTRA_BITS: 0
# ceillog2(ATTESTATION_SUBNET_COUNT) + ATTESTATION_SUBNET_EXTRA_BITS (= 6 + 0) bits
# computed at runtime
ATTESTATION_SUBNET_PREFIX_BITS: 6
ATTESTATION_SUBNET_SHUFFLING_PREFIX_BITS: 3

# Deneb
# 2**7 (= 128) blocks
Expand Down Expand Up @@ -196,6 +221,22 @@ BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: 32000000000
# 2**12 (= 4,096) epochs
MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096

# Gloas
# 2**7 (= 128) payloads
MAX_REQUEST_PAYLOADS: 128

# EIP7441
# 2**8 (= 256) epochs
EPOCHS_PER_SHUFFLING_PHASE: 256
# 2**1 (= 2) epochs
PROPOSER_SELECTION_GAP: 2

# EIP7805
# 2**4 (= 16) inclusion lists
MAX_REQUEST_INCLUSION_LIST: 16
# 2**13 (= 8,192) bytes
MAX_BYTES_PER_INCLUSION_LIST: 8192

# Blob Scheduling
# ---------------------------------------------------------------

Expand All @@ -204,5 +245,3 @@ BLOB_SCHEDULE:
MAX_BLOBS_PER_BLOCK: 15
- EPOCH: 419072 # January 7, 2026, 01:01:11am UTC
MAX_BLOBS_PER_BLOCK: 21

# Gloas
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use integer_sqrt::IntegerSquareRoot;
use safe_arith::SafeArith;
use smallvec::SmallVec;
use types::{AttestationData, BeaconState, ChainSpec, EthSpec};
use types::{
Expand All @@ -22,18 +23,47 @@ pub fn get_attestation_participation_flag_indices<E: EthSpec>(
inclusion_delay: u64,
spec: &ChainSpec,
) -> Result<SmallVec<[usize; NUM_FLAG_INDICES]>, Error> {
// Matching source
let justified_checkpoint = if data.target.epoch == state.current_epoch() {
state.current_justified_checkpoint()
} else {
state.previous_justified_checkpoint()
};

// Matching roots.
let is_matching_source = data.source == justified_checkpoint;
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)?;

// Matching target
let target_root = *state.get_block_root_at_epoch(data.target.epoch)?;
let target_root_matches = data.target.root == target_root;
let is_matching_target = is_matching_source && target_root_matches;

// Matching head
let head_root = *state.get_block_root(data.slot)?;
let head_root_matches = data.beacon_block_root == head_root;

let is_matching_head = if state.fork_name_unchecked().gloas_enabled() {
let payload_matches = 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 } as u64)
.map_err(|_| Error::InvalidExecutionPayloadAvailabilityIndex(slot_index))?;
data.index == payload_index
};
is_matching_target && head_root_matches && payload_matches
} else {
is_matching_target && head_root_matches
};

if !is_matching_source {
return Err(Error::IncorrectAttestationSource);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use crate::per_block_processing::errors::{
BlockOperationError, PayloadAttestationInvalid as Invalid,
};
use ssz_types::VariableList;
use typenum::Unsigned;
use types::*;

pub fn get_indexed_payload_attestation<E: EthSpec>(
state: &BeaconState<E>,
slot: Slot,
payload_attestation: &PayloadAttestation<E>,
spec: &ChainSpec,
) -> Result<IndexedPayloadAttestation<E>, BlockOperationError<Invalid>> {
let attesting_indices = get_payload_attesting_indices(state, slot, payload_attestation, spec)?;

Ok(IndexedPayloadAttestation {
attesting_indices: VariableList::new(attesting_indices)?,
data: payload_attestation.data.clone(),
signature: payload_attestation.signature.clone(),
})
}

pub fn get_payload_attesting_indices<E: EthSpec>(
state: &BeaconState<E>,
slot: Slot,
payload_attestation: &PayloadAttestation<E>,
spec: &ChainSpec,
) -> Result<Vec<u64>, BeaconStateError> {
let ptc = state.get_ptc(slot, spec)?;

let bitlist = &payload_attestation.aggregation_bits;
if bitlist.len() != E::PTCSize::to_usize() {
return Err(BeaconStateError::InvalidBitfield);
}

let mut attesting_indices = Vec::<u64>::new();
for (i, index) in ptc.into_iter().enumerate() {
if let Ok(true) = bitlist.get(i) {
attesting_indices.push(index as u64);
}
}
attesting_indices.sort_unstable();

Ok(attesting_indices)
}
4 changes: 4 additions & 0 deletions consensus/state_processing/src/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod deposit_data_tree;
mod get_attestation_participation;
mod get_attesting_indices;
mod get_payload_attesting_indices;
mod initiate_validator_exit;
mod slash_validator;

Expand All @@ -13,6 +14,9 @@ pub use get_attestation_participation::get_attestation_participation_flag_indice
pub use get_attesting_indices::{
attesting_indices_base, attesting_indices_electra, get_attesting_indices_from_state,
};
pub use get_payload_attesting_indices::{
get_indexed_payload_attestation, get_payload_attesting_indices,
};
pub use initiate_validator_exit::initiate_validator_exit;
pub use slash_validator::slash_validator;

Expand Down
Loading
Loading