diff --git a/beacon_node/beacon_chain/tests/store_tests.rs b/beacon_node/beacon_chain/tests/store_tests.rs index ba0621ae720..69e1346cfd6 100644 --- a/beacon_node/beacon_chain/tests/store_tests.rs +++ b/beacon_node/beacon_chain/tests/store_tests.rs @@ -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; diff --git a/common/eth2_network_config/built_in_network_configs/mainnet/config.yaml b/common/eth2_network_config/built_in_network_configs/mainnet/config.yaml index d4df1582f32..5df6370abe2 100644 --- a/common/eth2_network_config/built_in_network_configs/mainnet/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/mainnet/config.yaml @@ -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 @@ -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 # --------------------------------------------------------------- @@ -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) @@ -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 @@ -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 @@ -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 # --------------------------------------------------------------- @@ -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 diff --git a/consensus/state_processing/src/upgrade/gloas.rs b/consensus/state_processing/src/upgrade/gloas.rs index 81c0fcfe63e..0e0f39fa023 100644 --- a/consensus/state_processing/src/upgrade/gloas.rs +++ b/consensus/state_processing/src/upgrade/gloas.rs @@ -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( 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( 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) } diff --git a/consensus/types/presets/mainnet/gloas.yaml b/consensus/types/presets/mainnet/gloas.yaml index 45b7b8ae968..2da198dcae6 100644 --- a/consensus/types/presets/mainnet/gloas.yaml +++ b/consensus/types/presets/mainnet/gloas.yaml @@ -1 +1,23 @@ # Mainnet preset - Gloas + +# Misc +# --------------------------------------------------------------- +# 2**9 (= 512) validators +PTC_SIZE: 512 + +# Max operations per block +# --------------------------------------------------------------- +# 2**2 (= 4) attestations +MAX_PAYLOAD_ATTESTATIONS: 4 + +# State list lengths +# --------------------------------------------------------------- +# 2**40 (= 1,099,511,627,776) builder spots +BUILDER_REGISTRY_LIMIT: 1099511627776 +# 2**20 (= 1,048,576) builder pending withdrawals +BUILDER_PENDING_WITHDRAWALS_LIMIT: 1048576 + +# Withdrawals processing +# --------------------------------------------------------------- +# 2**14 (= 16,384) builders +MAX_BUILDERS_PER_WITHDRAWALS_SWEEP: 16384 \ No newline at end of file diff --git a/consensus/types/presets/minimal/gloas.yaml b/consensus/types/presets/minimal/gloas.yaml index 51b3f048571..7ae61ddf97f 100644 --- a/consensus/types/presets/minimal/gloas.yaml +++ b/consensus/types/presets/minimal/gloas.yaml @@ -1 +1,23 @@ # Minimal preset - Gloas + +# Misc +# --------------------------------------------------------------- +# [customized] 2**1 (= 2) validators +PTC_SIZE: 2 + +# Max operations per block +# --------------------------------------------------------------- +# 2**2 (= 4) attestations +MAX_PAYLOAD_ATTESTATIONS: 4 + +# State list lengths +# --------------------------------------------------------------- +# 2**40 (= 1,099,511,627,776) builder spots +BUILDER_REGISTRY_LIMIT: 1099511627776 +# 2**20 (= 1,048,576) builder pending withdrawals +BUILDER_PENDING_WITHDRAWALS_LIMIT: 1048576 + +# Withdrawals processing +# --------------------------------------------------------------- +# [customized] 2**4 (= 16) builders +MAX_BUILDERS_PER_WITHDRAWALS_SWEEP: 16 diff --git a/consensus/types/src/attestation/mod.rs b/consensus/types/src/attestation/mod.rs index 586d99bd900..5b59b83e726 100644 --- a/consensus/types/src/attestation/mod.rs +++ b/consensus/types/src/attestation/mod.rs @@ -11,6 +11,7 @@ mod payload_attestation; mod payload_attestation_data; mod payload_attestation_message; mod pending_attestation; +mod ptc; mod selection_proof; mod shuffling_id; mod signed_aggregate_and_proof; @@ -36,6 +37,7 @@ pub use payload_attestation::PayloadAttestation; pub use payload_attestation_data::PayloadAttestationData; pub use payload_attestation_message::PayloadAttestationMessage; pub use pending_attestation::PendingAttestation; +pub use ptc::PTC; pub use selection_proof::SelectionProof; pub use shuffling_id::AttestationShufflingId; pub use signed_aggregate_and_proof::{ diff --git a/consensus/types/src/attestation/payload_attestation.rs b/consensus/types/src/attestation/payload_attestation.rs index 192a4a8fea5..115a5ec4d62 100644 --- a/consensus/types/src/attestation/payload_attestation.rs +++ b/consensus/types/src/attestation/payload_attestation.rs @@ -5,7 +5,7 @@ use bls::AggregateSignature; use context_deserialize::context_deserialize; use educe::Educe; use serde::{Deserialize, Serialize}; -use ssz::BitList; +use ssz::BitVector; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; @@ -17,7 +17,7 @@ use tree_hash_derive::TreeHash; #[educe(PartialEq, Hash)] #[context_deserialize(ForkName)] pub struct PayloadAttestation { - pub aggregation_bits: BitList, + pub aggregation_bits: BitVector, pub data: PayloadAttestationData, pub signature: AggregateSignature, } diff --git a/consensus/types/src/attestation/ptc.rs b/consensus/types/src/attestation/ptc.rs new file mode 100644 index 00000000000..1eef2f7d683 --- /dev/null +++ b/consensus/types/src/attestation/ptc.rs @@ -0,0 +1,23 @@ +use crate::EthSpec; +use ssz_types::FixedVector; + +#[derive(Clone, Debug, PartialEq)] +pub struct PTC(pub FixedVector); + +impl<'a, E: EthSpec> IntoIterator for &'a PTC { + type Item = &'a usize; + type IntoIter = std::slice::Iter<'a, usize>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl IntoIterator for PTC { + type Item = usize; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} diff --git a/consensus/types/src/builder/builder.rs b/consensus/types/src/builder/builder.rs new file mode 100644 index 00000000000..81ca45046c7 --- /dev/null +++ b/consensus/types/src/builder/builder.rs @@ -0,0 +1,24 @@ +use crate::test_utils::TestRandom; +use crate::{Address, Epoch}; +use bls::PublicKeyBytes; +use serde::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +pub type BuilderIndex = u64; + +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash, +)] +pub struct Builder { + pub pubkey: PublicKeyBytes, + #[serde(with = "serde_utils::quoted_u8")] + pub version: u8, + pub execution_address: Address, + #[serde(with = "serde_utils::quoted_u64")] + pub balance: u64, + pub deposit_epoch: Epoch, + pub withdrawable_epoch: Epoch, +} diff --git a/consensus/types/src/builder/builder_pending_withdrawal.rs b/consensus/types/src/builder/builder_pending_withdrawal.rs index 436d331c003..dbbb029a5d8 100644 --- a/consensus/types/src/builder/builder_pending_withdrawal.rs +++ b/consensus/types/src/builder/builder_pending_withdrawal.rs @@ -1,5 +1,5 @@ use crate::test_utils::TestRandom; -use crate::{Address, Epoch, ForkName}; +use crate::{Address, ForkName}; use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; @@ -29,7 +29,6 @@ pub struct BuilderPendingWithdrawal { pub amount: u64, #[serde(with = "serde_utils::quoted_u64")] pub builder_index: u64, - pub withdrawable_epoch: Epoch, } #[cfg(test)] diff --git a/consensus/types/src/builder/mod.rs b/consensus/types/src/builder/mod.rs index 54d0ae4eb73..f4e0e346f24 100644 --- a/consensus/types/src/builder/mod.rs +++ b/consensus/types/src/builder/mod.rs @@ -1,7 +1,9 @@ +mod builder; mod builder_bid; mod builder_pending_payment; mod builder_pending_withdrawal; +pub use builder::{Builder, BuilderIndex}; pub use builder_bid::{ BuilderBid, BuilderBidBellatrix, BuilderBidCapella, BuilderBidDeneb, BuilderBidElectra, BuilderBidFulu, SignedBuilderBid, diff --git a/consensus/types/src/core/chain_spec.rs b/consensus/types/src/core/chain_spec.rs index 1bdf6c2cb86..c02456540c1 100644 --- a/consensus/types/src/core/chain_spec.rs +++ b/consensus/types/src/core/chain_spec.rs @@ -36,6 +36,7 @@ pub enum Domain { SyncCommitteeSelectionProof, BeaconBuilder, PTCAttester, + ProposerPreferences, ApplicationMask(ApplicationDomain), } @@ -130,6 +131,7 @@ pub struct ChainSpec { pub(crate) domain_aggregate_and_proof: u32, pub(crate) domain_beacon_builder: u32, pub(crate) domain_ptc_attester: u32, + pub(crate) domain_proposer_preferences: u32, /* * Fork choice @@ -234,6 +236,7 @@ pub struct ChainSpec { pub gloas_fork_epoch: Option, pub builder_payment_threshold_numerator: u64, pub builder_payment_threshold_denominator: u64, + pub min_builder_withdrawability_delay: Epoch, /* * Networking @@ -500,6 +503,7 @@ impl ChainSpec { Domain::AggregateAndProof => self.domain_aggregate_and_proof, Domain::BeaconBuilder => self.domain_beacon_builder, Domain::PTCAttester => self.domain_ptc_attester, + Domain::ProposerPreferences => self.domain_proposer_preferences, Domain::SyncCommittee => self.domain_sync_committee, Domain::ContributionAndProof => self.domain_contribution_and_proof, Domain::SyncCommitteeSelectionProof => self.domain_sync_committee_selection_proof, @@ -977,8 +981,9 @@ impl ChainSpec { domain_voluntary_exit: 4, domain_selection_proof: 5, domain_aggregate_and_proof: 6, - domain_beacon_builder: 0x1B, + domain_beacon_builder: 0x0B, domain_ptc_attester: 0x0C, + domain_proposer_preferences: 0x0D, /* * Fork choice @@ -1102,6 +1107,7 @@ impl ChainSpec { gloas_fork_epoch: None, builder_payment_threshold_numerator: 6, builder_payment_threshold_denominator: 10, + min_builder_withdrawability_delay: Epoch::new(4096), /* * Network specific @@ -1242,7 +1248,7 @@ impl ChainSpec { fulu_fork_version: [0x06, 0x00, 0x00, 0x01], fulu_fork_epoch: None, // Gloas - gloas_fork_version: [0x07, 0x00, 0x00, 0x00], + gloas_fork_version: [0x07, 0x00, 0x00, 0x01], gloas_fork_epoch: None, // Other network_id: 2, // lighthouse testnet network id @@ -1350,8 +1356,9 @@ impl ChainSpec { domain_voluntary_exit: 4, domain_selection_proof: 5, domain_aggregate_and_proof: 6, - domain_beacon_builder: 0x1B, + domain_beacon_builder: 0x0B, domain_ptc_attester: 0x0C, + domain_proposer_preferences: 0x0D, /* * Fork choice @@ -1474,6 +1481,7 @@ impl ChainSpec { gloas_fork_epoch: None, builder_payment_threshold_numerator: 6, builder_payment_threshold_denominator: 10, + min_builder_withdrawability_delay: Epoch::new(4096), /* * Network specific diff --git a/consensus/types/src/core/consts.rs b/consensus/types/src/core/consts.rs index b6d63c47a88..2c67657ee6d 100644 --- a/consensus/types/src/core/consts.rs +++ b/consensus/types/src/core/consts.rs @@ -25,3 +25,17 @@ pub mod bellatrix { pub mod deneb { pub use kzg::VERSIONED_HASH_VERSION_KZG; } +pub mod gloas { + pub const BUILDER_INDEX_SELF_BUILD: u64 = u64::MAX; + pub const BUILDER_INDEX_FLAG: u64 = 1 << 40; + + // Fork choice constants + pub type PayloadStatus = u8; + pub const PAYLOAD_STATUS_PENDING: PayloadStatus = 0; + pub const PAYLOAD_STATUS_EMPTY: PayloadStatus = 1; + pub const PAYLOAD_STATUS_FULL: PayloadStatus = 2; + + pub const ATTESTATION_TIMELINESS_INDEX: usize = 0; + pub const PTC_TIMELINESS_INDEX: usize = 1; + pub const NUM_BLOCK_TIMELINESS_DEADLINES: usize = 2; +} diff --git a/consensus/types/src/core/eth_spec.rs b/consensus/types/src/core/eth_spec.rs index 2983fc2c629..a4b22da3f88 100644 --- a/consensus/types/src/core/eth_spec.rs +++ b/consensus/types/src/core/eth_spec.rs @@ -7,7 +7,7 @@ use safe_arith::{ArithError, SafeArith}; use serde::{Deserialize, Serialize}; use typenum::{ U0, U1, U2, U4, U8, U16, U17, U32, U64, U128, U256, U512, U625, U1024, U2048, U4096, U8192, - U65536, U131072, U262144, U1048576, U16777216, U33554432, U134217728, U1073741824, + U16384, U65536, U131072, U262144, U1048576, U16777216, U33554432, U134217728, U1073741824, U1099511627776, UInt, Unsigned, bit::B0, }; @@ -122,6 +122,10 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + type CellsPerExtBlob: Unsigned + Clone + Sync + Send + Debug + PartialEq; type NumberOfColumns: Unsigned + Clone + Sync + Send + Debug + PartialEq; type ProposerLookaheadSlots: Unsigned + Clone + Sync + Send + Debug + PartialEq; + /* + * New in Gloas + */ + type BuilderRegistryLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq; /* * Derived values (set these CAREFULLY) */ @@ -175,6 +179,7 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + type MaxPayloadAttestations: Unsigned + Clone + Sync + Send + Debug + PartialEq; type BuilderPendingPaymentsLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq; type BuilderPendingWithdrawalsLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq; + type MaxBuildersPerWithdrawalsSweep: Unsigned + Clone + Sync + Send + Debug + PartialEq; fn default_spec() -> ChainSpec; @@ -427,6 +432,16 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + fn max_payload_attestations() -> usize { Self::MaxPayloadAttestations::to_usize() } + + /// Returns the `MaxBuildersPerWithdrawalsSweep` constant for this specification. + fn max_builders_per_withdrawals_sweep() -> usize { + Self::MaxBuildersPerWithdrawalsSweep::to_usize() + } + + /// Returns the `PAYLOAD_TIMELY_THRESHOLD` constant (PTC_SIZE / 2). + fn payload_timely_threshold() -> usize { + Self::PTCSize::to_usize() / 2 + } } /// Macro to inherit some type values from another EthSpec. @@ -484,6 +499,7 @@ impl EthSpec for MainnetEthSpec { type CellsPerExtBlob = U128; type NumberOfColumns = U128; type ProposerLookaheadSlots = U64; // Derived from (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH + type BuilderRegistryLimit = U1099511627776; type SyncSubcommitteeSize = U128; // 512 committee size / 4 sync committee subnet count type MaxPendingAttestations = U4096; // 128 max attestations * 32 slots per epoch type SlotsPerEth1VotingPeriod = U2048; // 64 epochs * 32 slots per epoch @@ -500,6 +516,7 @@ impl EthSpec for MainnetEthSpec { type MaxPendingDepositsPerEpoch = U16; type PTCSize = U512; type MaxPayloadAttestations = U4; + type MaxBuildersPerWithdrawalsSweep = U16384; fn default_spec() -> ChainSpec { ChainSpec::mainnet() @@ -543,6 +560,8 @@ impl EthSpec for MinimalEthSpec { type NumberOfColumns = U128; type ProposerLookaheadSlots = U16; // Derived from (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH type BuilderPendingPaymentsLimit = U16; // 2 * SLOTS_PER_EPOCH = 2 * 8 = 16 + type PTCSize = U2; + type MaxBuildersPerWithdrawalsSweep = U16; params_from_eth_spec!(MainnetEthSpec { JustificationBitsLength, @@ -573,8 +592,8 @@ impl EthSpec for MinimalEthSpec { MaxAttestationsElectra, MaxDepositRequestsPerPayload, MaxWithdrawalRequestsPerPayload, - PTCSize, - MaxPayloadAttestations + MaxPayloadAttestations, + BuilderRegistryLimit }); fn default_spec() -> ChainSpec { @@ -647,8 +666,10 @@ impl EthSpec for GnosisEthSpec { type CellsPerExtBlob = U128; type NumberOfColumns = U128; type ProposerLookaheadSlots = U32; // Derived from (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH + type BuilderRegistryLimit = U1099511627776; type PTCSize = U512; type MaxPayloadAttestations = U2; + type MaxBuildersPerWithdrawalsSweep = U16384; fn default_spec() -> ChainSpec { ChainSpec::gnosis() diff --git a/consensus/types/src/execution/signed_execution_payload_envelope.rs b/consensus/types/src/execution/signed_execution_payload_envelope.rs index 16410416157..cdcebc7b31e 100644 --- a/consensus/types/src/execution/signed_execution_payload_envelope.rs +++ b/consensus/types/src/execution/signed_execution_payload_envelope.rs @@ -1,6 +1,9 @@ use crate::test_utils::TestRandom; -use crate::{EthSpec, ExecutionPayloadEnvelope}; -use bls::Signature; +use crate::{ + ChainSpec, Domain, Epoch, EthSpec, ExecutionBlockHash, ExecutionPayloadEnvelope, Fork, Hash256, + SignedRoot, Slot, +}; +use bls::{PublicKey, Signature}; use educe::Educe; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; @@ -15,6 +18,46 @@ pub struct SignedExecutionPayloadEnvelope { pub signature: Signature, } +impl SignedExecutionPayloadEnvelope { + pub fn slot(&self) -> Slot { + self.message.slot + } + + pub fn epoch(&self) -> Epoch { + self.slot().epoch(E::slots_per_epoch()) + } + + pub fn beacon_block_root(&self) -> Hash256 { + self.message.beacon_block_root + } + + pub fn block_hash(&self) -> ExecutionBlockHash { + self.message.payload.block_hash + } + + /// Verify `self.signature`. + pub fn verify_signature( + &self, + pubkey: &PublicKey, + fork: &Fork, + genesis_validators_root: Hash256, + spec: &ChainSpec, + ) -> bool { + // Signed envelopes using the new BeaconBuilder domain per the spec: + // https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.1/specs/gloas/beacon-chain.md#new-verify_execution_payload_envelope_signature + let domain = spec.get_domain( + self.epoch(), + Domain::BeaconBuilder, + fork, + genesis_validators_root, + ); + + let message = self.message.signing_root(domain); + + self.signature.verify(pubkey, message) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/consensus/types/src/state/beacon_state.rs b/consensus/types/src/state/beacon_state.rs index f3e5ae411b8..3f8fa4cfff7 100644 --- a/consensus/types/src/state/beacon_state.rs +++ b/consensus/types/src/state/beacon_state.rs @@ -23,10 +23,11 @@ use tree_hash_derive::TreeHash; use typenum::Unsigned; use crate::{ - BuilderPendingPayment, BuilderPendingWithdrawal, ExecutionBlockHash, ExecutionPayloadBid, + Builder, BuilderIndex, BuilderPendingPayment, BuilderPendingWithdrawal, ExecutionBlockHash, + ExecutionPayloadBid, Withdrawal, attestation::{ - AttestationDuty, BeaconCommittee, Checkpoint, CommitteeIndex, ParticipationFlags, - PendingAttestation, + AttestationData, AttestationDuty, BeaconCommittee, Checkpoint, CommitteeIndex, PTC, + ParticipationFlags, PendingAttestation, }, block::{BeaconBlock, BeaconBlockHeader, SignedBeaconBlockHash}, consolidation::PendingConsolidation, @@ -195,6 +196,7 @@ pub enum BeaconStateError { ProposerLookaheadOutOfBounds { i: usize, }, + InvalidIndicesCount, } /// Control whether an epoch-indexed field can be indexed at the next epoch or not. @@ -602,8 +604,17 @@ where #[superstruct(only(Fulu, Gloas))] #[serde(with = "ssz_types::serde_utils::quoted_u64_fixed_vec")] pub proposer_lookahead: Vector, - // Gloas + #[compare_fields(as_iter)] + #[test_random(default)] + #[superstruct(only(Gloas))] + pub builders: List, + + #[metastruct(exclude_from(tree_lists))] + #[serde(with = "serde_utils::quoted_u64")] + #[superstruct(only(Gloas), partial_getter(copy))] + pub next_withdrawal_builder_index: BuilderIndex, + #[test_random(default)] #[superstruct(only(Gloas))] #[metastruct(exclude_from(tree_lists))] @@ -625,10 +636,10 @@ where #[metastruct(exclude_from(tree_lists))] pub latest_block_hash: ExecutionBlockHash, + #[compare_fields(as_iter)] #[test_random(default)] #[superstruct(only(Gloas))] - #[metastruct(exclude_from(tree_lists))] - pub latest_withdrawals_root: Hash256, + pub payload_expected_withdrawals: List, // Caching (not in the spec) #[serde(skip_serializing, skip_deserializing)] @@ -1115,13 +1126,22 @@ impl BeaconState { } } + let gloas_enabled = self.fork_name_unchecked().gloas_enabled(); epoch .slot_iter(E::slots_per_epoch()) .map(|slot| { let mut preimage = seed.to_vec(); preimage.append(&mut int_to_bytes8(slot.as_u64())); let seed = hash(&preimage); - self.compute_proposer_index(indices, &seed, spec) + + if gloas_enabled { + self.compute_balance_weighted_selection(indices, &seed, 1, true, spec)? + .first() + .copied() + .ok_or(BeaconStateError::InsufficientValidators) + } else { + self.compute_proposer_index(indices, &seed, spec) + } }) .collect() } @@ -1378,39 +1398,50 @@ impl BeaconState { let epoch = self.current_epoch().safe_add(1)?; let active_validator_indices = self.get_active_validator_indices(epoch, spec)?; - let active_validator_count = active_validator_indices.len(); - let seed = self.get_seed(epoch, Domain::SyncCommittee, spec)?; - let max_effective_balance = spec.max_effective_balance_for_fork(self.fork_name_unchecked()); - let max_random_value = if self.fork_name_unchecked().electra_enabled() { - MAX_RANDOM_VALUE - } else { - MAX_RANDOM_BYTE - }; - let mut i = 0; - let mut sync_committee_indices = Vec::with_capacity(E::SyncCommitteeSize::to_usize()); - while sync_committee_indices.len() < E::SyncCommitteeSize::to_usize() { - let shuffled_index = compute_shuffled_index( - i.safe_rem(active_validator_count)?, - active_validator_count, + if self.fork_name_unchecked().gloas_enabled() { + self.compute_balance_weighted_selection( + &active_validator_indices, seed.as_slice(), - spec.shuffle_round_count, + E::SyncCommitteeSize::to_usize(), + true, + spec, ) - .ok_or(BeaconStateError::UnableToShuffle)?; - let candidate_index = *active_validator_indices - .get(shuffled_index) - .ok_or(BeaconStateError::ShuffleIndexOutOfBounds(shuffled_index))?; - let random_value = self.shuffling_random_value(i, seed.as_slice())?; - let effective_balance = self.get_validator(candidate_index)?.effective_balance; - if effective_balance.safe_mul(max_random_value)? - >= max_effective_balance.safe_mul(random_value)? - { - sync_committee_indices.push(candidate_index); + } else { + let active_validator_count = active_validator_indices.len(); + let max_effective_balance = + spec.max_effective_balance_for_fork(self.fork_name_unchecked()); + let max_random_value = if self.fork_name_unchecked().electra_enabled() { + MAX_RANDOM_VALUE + } else { + MAX_RANDOM_BYTE + }; + + let mut i = 0; + let mut sync_committee_indices = Vec::with_capacity(E::SyncCommitteeSize::to_usize()); + while sync_committee_indices.len() < E::SyncCommitteeSize::to_usize() { + let shuffled_index = compute_shuffled_index( + i.safe_rem(active_validator_count)?, + active_validator_count, + seed.as_slice(), + spec.shuffle_round_count, + ) + .ok_or(BeaconStateError::UnableToShuffle)?; + let candidate_index = *active_validator_indices + .get(shuffled_index) + .ok_or(BeaconStateError::ShuffleIndexOutOfBounds(shuffled_index))?; + let random_value = self.shuffling_random_value(i, seed.as_slice())?; + let effective_balance = self.get_validator(candidate_index)?.effective_balance; + if effective_balance.safe_mul(max_random_value)? + >= max_effective_balance.safe_mul(random_value)? + { + sync_committee_indices.push(candidate_index); + } + i.safe_add_assign(1)?; } - i.safe_add_assign(1)?; + Ok(sync_committee_indices) } - Ok(sync_committee_indices) } /// Compute the next sync committee. @@ -2032,6 +2063,25 @@ impl BeaconState { Ok(cache.get_attestation_duties(validator_index)) } + /// Check if the attestation is for the block proposed at the attestation slot. + /// + /// Returns `true` if the attestation's block root matches the block root at the + /// attestation's slot, and the block root differs from the previous slot's root. + pub fn is_attestation_same_slot( + &self, + data: &AttestationData, + ) -> Result { + if data.slot == 0 { + return Ok(true); + } + + let block_root = data.beacon_block_root; + let slot_block_root = *self.get_block_root(data.slot)?; + let prev_block_root = *self.get_block_root(data.slot.safe_sub(1)?)?; + + Ok(block_root == slot_block_root && block_root != prev_block_root) + } + /// Compute the total active balance cache from scratch. /// /// This method should rarely be invoked because single-pass epoch processing keeps the total @@ -2292,6 +2342,7 @@ impl BeaconState { } } + /// Return true if the parent block was full (both beacon block and execution payload were present). pub fn is_parent_block_full(&self) -> bool { match self { BeaconState::Base(_) | BeaconState::Altair(_) => false, @@ -2878,6 +2929,111 @@ impl BeaconState { Ok(()) } + + /// Get the payload timeliness committee for the given `slot`. + /// + /// Requires the committee cache to be initialized. + /// TODO(EIP-7732): definitely gonna have to cache this.. + pub fn get_ptc(&self, slot: Slot, spec: &ChainSpec) -> Result, BeaconStateError> { + let committee_cache = self.committee_cache_at_slot(slot)?; + let committees = committee_cache.get_beacon_committees_at_slot(slot)?; + + let seed = self.get_ptc_attester_seed(slot, spec)?; + + let committee_indices: Vec = committees + .iter() + .flat_map(|committee| committee.committee.iter().copied()) + .collect(); + let selected_indices = self.compute_balance_weighted_selection( + &committee_indices, + &seed, + E::ptc_size(), + false, + spec, + )?; + + Ok(PTC(FixedVector::new(selected_indices)?)) + } + + /// Compute the seed to use for the ptc attester selection at the given `slot`. + pub fn get_ptc_attester_seed( + &self, + slot: Slot, + spec: &ChainSpec, + ) -> Result, BeaconStateError> { + let epoch = slot.epoch(E::slots_per_epoch()); + let mut preimage = self + .get_seed(epoch, Domain::PTCAttester, spec)? + .as_slice() + .to_vec(); + preimage.append(&mut int_to_bytes8(slot.as_u64())); + Ok(hash(&preimage)) + } + + /// Return size indices sampled by effective balance, using indices as candidates. + /// + /// If shuffle_indices is True, candidate indices are themselves sampled from indices + /// by shuffling it, otherwise indices is traversed in order. + fn compute_balance_weighted_selection( + &self, + indices: &[usize], + seed: &[u8], + size: usize, + shuffle_indices: bool, + spec: &ChainSpec, + ) -> Result, BeaconStateError> { + let total = indices.len(); + if total == 0 { + return Err(BeaconStateError::InvalidIndicesCount); + } + + let mut selected = Vec::with_capacity(size); + let mut i = 0usize; + + while selected.len() < size { + let mut next_index = i.safe_rem(total)?; + + if shuffle_indices { + next_index = + compute_shuffled_index(next_index, total, seed, spec.shuffle_round_count) + .ok_or(BeaconStateError::UnableToShuffle)?; + } + + let candidate_index = indices + .get(next_index) + .ok_or(BeaconStateError::InvalidIndicesCount)?; + + if self.compute_balance_weighted_acceptance(*candidate_index, seed, i, spec)? { + selected.push(*candidate_index); + } + + i.safe_add_assign(1)?; + } + + Ok(selected) + } + + /// Return whether to accept the selection of the validator `index`, with probability + /// proportional to its `effective_balance`, and randomness given by `seed` and `iteration`. + fn compute_balance_weighted_acceptance( + &self, + index: usize, + seed: &[u8], + iteration: usize, + spec: &ChainSpec, + ) -> Result { + // TODO(EIP-7732): Consider grabbing effective balances from the epoch cache here. + // Note that this function will be used in a loop, so using cached values could be nice for performance. + // However, post-gloas, this function will be used in `compute_proposer_indices`, `get_next_sync_committee_indices`, and `get_ptc`, which has ~15 call sites in total + // so we will need to check each one to ensure epoch cache is initialized first, if we deem a good idea. + // Currently, we can't test if making the change would work since the test suite is not ready for gloas. + let effective_balance = self.get_effective_balance(index)?; + let max_effective_balance = spec.max_effective_balance_for_fork(self.fork_name_unchecked()); + let random_value = self.shuffling_random_value(iteration, seed)?; + + Ok(effective_balance.safe_mul(MAX_RANDOM_VALUE)? + >= max_effective_balance.safe_mul(random_value)?) + } } impl ForkVersionDecode for BeaconState { diff --git a/testing/ef_tests/Makefile b/testing/ef_tests/Makefile index 0ead9d00472..0c6371f8253 100644 --- a/testing/ef_tests/Makefile +++ b/testing/ef_tests/Makefile @@ -1,6 +1,6 @@ # To download/extract nightly tests, run: # CONSENSUS_SPECS_TEST_VERSION=nightly make -CONSENSUS_SPECS_TEST_VERSION ?= v1.6.0-beta.1 +CONSENSUS_SPECS_TEST_VERSION ?= v1.7.0-alpha.1 REPO_NAME := consensus-spec-tests OUTPUT_DIR := ./$(REPO_NAME) diff --git a/testing/ef_tests/check_all_files_accessed.py b/testing/ef_tests/check_all_files_accessed.py index 1f70881a887..7787af64f08 100755 --- a/testing/ef_tests/check_all_files_accessed.py +++ b/testing/ef_tests/check_all_files_accessed.py @@ -64,6 +64,8 @@ # Ignore KZG tests that target internal kzg library functions "tests/.*/compute_verify_cell_kzg_proof_batch_challenge/.*", "tests/.*/compute_challenge/.*", + # We don't need these manifest files at the moment. + "tests/.*/manifest.yaml" ] diff --git a/testing/ef_tests/src/cases/fork_choice.rs b/testing/ef_tests/src/cases/fork_choice.rs index 8e9d438a243..93a8805240a 100644 --- a/testing/ef_tests/src/cases/fork_choice.rs +++ b/testing/ef_tests/src/cases/fork_choice.rs @@ -297,6 +297,15 @@ impl Case for ForkChoiceTest { } fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> { + // TODO(gloas): We have not implemented this change to fork choice/proposer boost yet. + // https://github.com/sigp/lighthouse/issues/8689 + if self.description == "voting_source_beyond_two_epoch" + || self.description == "justified_update_not_realized_finality" + || self.description == "justified_update_always_if_better" + { + return Err(Error::SkippedKnownFailure); + } + let tester = Tester::new(self, testing_spec::(fork_name))?; for step in &self.steps { diff --git a/testing/ef_tests/src/handler.rs b/testing/ef_tests/src/handler.rs index a5b2ffada37..b974a17a804 100644 --- a/testing/ef_tests/src/handler.rs +++ b/testing/ef_tests/src/handler.rs @@ -387,8 +387,16 @@ where T::name().into() } + fn disabled_forks(&self) -> Vec { + // TODO(gloas): Can be removed once we enable Gloas on all tests + vec![] + } + fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool { + // TODO(gloas): DataColumnSidecar tests are disabled until we update the DataColumnSidecar + // type. self.supported_forks.contains(&fork_name) + && !(fork_name == ForkName::Gloas && T::name() == "DataColumnSidecar") } }