From d9cef1da59166f95ad060842967fc9651086dbd8 Mon Sep 17 00:00:00 2001 From: Hussein Ait Lahcen Date: Tue, 25 Feb 2025 13:13:41 +0100 Subject: [PATCH 1/3] feat(ethereum-lc): fork parameters for electra --- lib/ethereum-sync-protocol/src/utils.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/ethereum-sync-protocol/src/utils.rs b/lib/ethereum-sync-protocol/src/utils.rs index 766bb9d2a4..c1c8c93906 100644 --- a/lib/ethereum-sync-protocol/src/utils.rs +++ b/lib/ethereum-sync-protocol/src/utils.rs @@ -17,7 +17,9 @@ use crate::{ /// /// [See in consensus-spec](https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/fork.md#modified-compute_fork_version) pub fn compute_fork_version(fork_parameters: &ForkParameters, epoch: u64) -> Version { - if epoch >= fork_parameters.deneb.epoch { + if epoch >= fork_parameters.electra.epoch { + fork_parameters.electra.version + } else if epoch >= fork_parameters.deneb.epoch { fork_parameters.deneb.version } else if epoch >= fork_parameters.capella.epoch { fork_parameters.capella.version From e6096b9b5b9e6067c1176f26e1f9e0b546d7cc4c Mon Sep 17 00:00:00 2001 From: Hussein Ait Lahcen Date: Wed, 26 Feb 2025 15:43:58 +0100 Subject: [PATCH 2/3] feat(eth-lc): introduce electra constants --- lib/beacon-api-types/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/beacon-api-types/src/lib.rs b/lib/beacon-api-types/src/lib.rs index 29da2e9c6b..9021245a11 100644 --- a/lib/beacon-api-types/src/lib.rs +++ b/lib/beacon-api-types/src/lib.rs @@ -412,6 +412,11 @@ pub mod consts { epoch: default_epoch(), } } + + // https://github.com/ethereum/consensus-specs/blob/dev/specs/electra/light-client/sync-protocol.md#new-constants + pub const FINALIZED_ROOT_GINDEX_ELECTRA: u64 = 169; + pub const CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA: u64 = 86; + pub const NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA: u64 = 87; } pub mod preset { From 249756fa8f7d3a284dd668d458b0e278642c5758 Mon Sep 17 00:00:00 2001 From: Hussein Ait Lahcen Date: Wed, 26 Feb 2025 16:35:17 +0100 Subject: [PATCH 3/3] feat(eth-lc): handle electra fork --- lib/beacon-api-types/src/lib.rs | 14 ++-- .../src/light_client_bootstrap.rs | 6 +- .../src/light_client_finality_update.rs | 10 +-- .../src/light_client_header.rs | 6 +- .../src/light_client_update.rs | 8 +-- .../src/light_client_update.rs | 2 +- .../src/light_client_update_data.rs | 3 +- lib/ethereum-sync-protocol/src/lib.rs | 70 ++++++++++++++----- lib/ethereum-sync-protocol/src/utils.rs | 23 +++--- 9 files changed, 84 insertions(+), 58 deletions(-) diff --git a/lib/beacon-api-types/src/lib.rs b/lib/beacon-api-types/src/lib.rs index 9021245a11..90718d2944 100644 --- a/lib/beacon-api-types/src/lib.rs +++ b/lib/beacon-api-types/src/lib.rs @@ -63,7 +63,7 @@ use std::{ use hex_literal::hex; use typenum::{NonZero, Unsigned}; -use unionlabs::primitives::FixedBytes; +use unionlabs::primitives::{FixedBytes, H256}; pub use crate::{ attestation::Attestation, attestation_data::AttestationData, @@ -368,6 +368,10 @@ consts_traits![ mk_chain_spec!(Minimal is preset::MINIMAL); mk_chain_spec!(Mainnet is preset::MAINNET); +pub type CurrentSyncCommitteeBranch = Vec; +pub type NextSyncCommitteeBranch = Vec; +pub type FinalityBranch = Vec; + /// Values that are constant across all configurations. pub mod consts { use hex_literal::hex; @@ -394,13 +398,13 @@ pub mod consts { // https://github.com/ethereum/consensus-specs/blob/dev/ssz/merkle-proofs.md /// `get_generalized_index(BeaconState, "finalized_checkpoint", "root")` - pub const FINALIZED_ROOT_INDEX: u64 = 105; + pub const FINALIZED_ROOT_GINDEX: u64 = 105; /// `get_generalized_index(BeaconState, "current_sync_committee")` - pub const CURRENT_SYNC_COMMITTEE_INDEX: u64 = 54; + pub const CURRENT_SYNC_COMMITTEE_GINDEX: u64 = 54; /// `get_generalized_index(BeaconState, "next_sync_committee")` - pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; + pub const NEXT_SYNC_COMMITTEE_GINDEX: u64 = 55; /// `get_generalized_index(BeaconBlockBody, "execution_payload")` - pub const EXECUTION_PAYLOAD_INDEX: u64 = 25; + pub const EXECUTION_PAYLOAD_GINDEX: u64 = 25; pub const fn default_epoch() -> u64 { u64::MAX diff --git a/lib/beacon-api-types/src/light_client_bootstrap.rs b/lib/beacon-api-types/src/light_client_bootstrap.rs index 23f0f8626d..2cce94945f 100644 --- a/lib/beacon-api-types/src/light_client_bootstrap.rs +++ b/lib/beacon-api-types/src/light_client_bootstrap.rs @@ -1,9 +1,7 @@ -use unionlabs::primitives::H256; - use crate::{ - consts::{floorlog2, CURRENT_SYNC_COMMITTEE_INDEX}, light_client_header::LightClientHeader, sync_committee::SyncCommittee, + CurrentSyncCommitteeBranch, }; #[derive(Debug, Clone, PartialEq)] @@ -12,5 +10,5 @@ pub struct LightClientBootstrap { pub header: LightClientHeader, /// Current sync committee corresponding to `beacon_header.state_root` pub current_sync_committee: SyncCommittee, - pub current_sync_committee_branch: [H256; floorlog2(CURRENT_SYNC_COMMITTEE_INDEX)], + pub current_sync_committee_branch: CurrentSyncCommitteeBranch, } diff --git a/lib/beacon-api-types/src/light_client_finality_update.rs b/lib/beacon-api-types/src/light_client_finality_update.rs index 6a2a4e3bf8..8892c70685 100644 --- a/lib/beacon-api-types/src/light_client_finality_update.rs +++ b/lib/beacon-api-types/src/light_client_finality_update.rs @@ -1,10 +1,4 @@ -use unionlabs::primitives::H256; - -use crate::{ - consts::{floorlog2, FINALIZED_ROOT_INDEX}, - light_client_header::LightClientHeader, - Slot, SyncAggregate, -}; +use crate::{light_client_header::LightClientHeader, FinalityBranch, Slot, SyncAggregate}; #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -13,7 +7,7 @@ pub struct LightClientFinalityUpdate { pub attested_header: LightClientHeader, /// Finalized header corresponding to `attested_header.state_root` pub finalized_header: LightClientHeader, - pub finality_branch: [H256; floorlog2(FINALIZED_ROOT_INDEX)], + pub finality_branch: FinalityBranch, /// Sync committee aggregate signature pub sync_aggregate: SyncAggregate, /// Slot at which the aggregate signature was created (untrusted) diff --git a/lib/beacon-api-types/src/light_client_header.rs b/lib/beacon-api-types/src/light_client_header.rs index f282144863..ff22617c93 100644 --- a/lib/beacon-api-types/src/light_client_header.rs +++ b/lib/beacon-api-types/src/light_client_header.rs @@ -2,7 +2,7 @@ use unionlabs::primitives::H256; use crate::{ beacon_block_header::BeaconBlockHeader, - consts::{floorlog2, EXECUTION_PAYLOAD_INDEX}, + consts::{floorlog2, EXECUTION_PAYLOAD_GINDEX}, execution_payload_header::ExecutionPayloadHeader, }; @@ -12,7 +12,7 @@ use crate::{ pub struct LightClientHeader { pub beacon: BeaconBlockHeader, pub execution: ExecutionPayloadHeader, - pub execution_branch: [H256; floorlog2(EXECUTION_PAYLOAD_INDEX)], + pub execution_branch: [H256; floorlog2(EXECUTION_PAYLOAD_GINDEX)], } #[cfg(feature = "ssz")] @@ -25,5 +25,5 @@ pub struct LightClientHeader { pub struct LightClientHeaderSsz { pub beacon: BeaconBlockHeader, pub execution: crate::ExecutionPayloadHeaderSsz, - pub execution_branch: [H256; floorlog2(EXECUTION_PAYLOAD_INDEX)], + pub execution_branch: [H256; floorlog2(EXECUTION_PAYLOAD_GINDEX)], } diff --git a/lib/beacon-api-types/src/light_client_update.rs b/lib/beacon-api-types/src/light_client_update.rs index 4f5d596907..9718d4ff25 100644 --- a/lib/beacon-api-types/src/light_client_update.rs +++ b/lib/beacon-api-types/src/light_client_update.rs @@ -1,13 +1,7 @@ -use unionlabs::primitives::H256; - use crate::{ - consts::{floorlog2, FINALIZED_ROOT_INDEX, NEXT_SYNC_COMMITTEE_INDEX}, - LightClientHeader, Slot, SyncAggregate, SyncCommittee, + FinalityBranch, LightClientHeader, NextSyncCommitteeBranch, Slot, SyncAggregate, SyncCommittee, }; -pub type NextSyncCommitteeBranch = [H256; floorlog2(NEXT_SYNC_COMMITTEE_INDEX)]; -pub type FinalityBranch = [H256; floorlog2(FINALIZED_ROOT_INDEX)]; - #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct LightClientUpdate { diff --git a/lib/ethereum-light-client-types/src/light_client_update.rs b/lib/ethereum-light-client-types/src/light_client_update.rs index 8fcfca9ee5..bd839f222e 100644 --- a/lib/ethereum-light-client-types/src/light_client_update.rs +++ b/lib/ethereum-light-client-types/src/light_client_update.rs @@ -1,4 +1,4 @@ -use beacon_api_types::{light_client_update::NextSyncCommitteeBranch, SyncCommittee}; +use beacon_api_types::{NextSyncCommitteeBranch, SyncCommittee}; use crate::LightClientUpdateData; diff --git a/lib/ethereum-light-client-types/src/light_client_update_data.rs b/lib/ethereum-light-client-types/src/light_client_update_data.rs index 2df1842ec0..b228159da2 100644 --- a/lib/ethereum-light-client-types/src/light_client_update_data.rs +++ b/lib/ethereum-light-client-types/src/light_client_update_data.rs @@ -1,6 +1,5 @@ use beacon_api_types::{ - light_client_update::{FinalityBranch, NextSyncCommitteeBranch}, - LightClientHeader, Slot, SyncAggregate, SyncCommittee, + FinalityBranch, LightClientHeader, NextSyncCommitteeBranch, Slot, SyncAggregate, SyncCommittee }; /// Common data required for all light client updates. diff --git a/lib/ethereum-sync-protocol/src/lib.rs b/lib/ethereum-sync-protocol/src/lib.rs index 1203af5da9..8a78c0b968 100644 --- a/lib/ethereum-sync-protocol/src/lib.rs +++ b/lib/ethereum-sync-protocol/src/lib.rs @@ -7,8 +7,9 @@ pub mod utils; use beacon_api_types::{ consts::{ - floorlog2, get_subtree_index, EXECUTION_PAYLOAD_INDEX, FINALIZED_ROOT_INDEX, - NEXT_SYNC_COMMITTEE_INDEX, + CURRENT_SYNC_COMMITTEE_GINDEX, CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA, + EXECUTION_PAYLOAD_GINDEX, FINALIZED_ROOT_GINDEX, FINALIZED_ROOT_GINDEX_ELECTRA, + NEXT_SYNC_COMMITTEE_GINDEX, NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA, }, light_client_update::LightClientUpdate, ChainSpec, DomainType, ExecutionPayloadHeaderSsz, ForkParameters, LightClientHeader, Slot, @@ -42,6 +43,42 @@ pub trait BlsVerify { ) -> Result<(), Error>; } +// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#finalized_root_gindex_at_slot +pub fn finalized_root_gindex_at_slot( + fork_parameters: &ForkParameters, + slot: Slot, +) -> u64 { + let epoch = compute_epoch_at_slot::(slot); + if epoch >= fork_parameters.electra.epoch { + return FINALIZED_ROOT_GINDEX_ELECTRA; + } + FINALIZED_ROOT_GINDEX +} + +// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#current_sync_committee_gindex_at_slot +pub fn current_sync_committee_gindex_at_slot( + fork_parameters: &ForkParameters, + slot: Slot, +) -> u64 { + let epoch = compute_epoch_at_slot::(slot); + if epoch >= fork_parameters.electra.epoch { + return CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA; + } + CURRENT_SYNC_COMMITTEE_GINDEX +} + +// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#next_sync_committee_gindex_at_slot +pub fn next_sync_committee_gindex_at_slot( + fork_parameters: &ForkParameters, + slot: Slot, +) -> u64 { + let epoch = compute_epoch_at_slot::(slot); + if epoch >= fork_parameters.electra.epoch { + return NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA; + } + NEXT_SYNC_COMMITTEE_GINDEX +} + /// Verifies if the light client `update` is valid. /// /// * `update`: The light client update we want to verify. @@ -169,8 +206,7 @@ pub fn validate_light_client_update( validate_merkle_branch( &update.finalized_header.beacon.tree_hash_root(), &update.finality_branch, - floorlog2(FINALIZED_ROOT_INDEX), - get_subtree_index(FINALIZED_ROOT_INDEX), + finalized_root_gindex_at_slot::(fork_parameters, update_attested_slot), &update.attested_header.beacon.state_root, )?; @@ -188,14 +224,17 @@ pub fn validate_light_client_update( }, )?; } + // This validates the given next sync committee against the attested header's state root. validate_merkle_branch( &TryInto::>::try_into(next_sync_committee.clone()) .unwrap() .tree_hash_root(), - &update.next_sync_committee_branch.unwrap_or_default(), - floorlog2(NEXT_SYNC_COMMITTEE_INDEX), - get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX), + &update + .next_sync_committee_branch + .clone() + .unwrap_or_default()[..], + next_sync_committee_gindex_at_slot::(fork_parameters, update_attested_slot), &update.attested_header.beacon.state_root, )?; } @@ -245,19 +284,17 @@ pub fn get_lc_execution_root( header: &LightClientHeader, ) -> H256 { let epoch = compute_epoch_at_slot::(header.beacon.slot); + // Now new field in electra + if epoch >= fork_parameters.electra.epoch { + return TryInto::>::try_into(header.execution.clone()) + .unwrap() + .tree_hash_root(); + } if epoch >= fork_parameters.deneb.epoch { return TryInto::>::try_into(header.execution.clone()) .unwrap() .tree_hash_root(); } - - // TODO: Figure out what to do here - // if epoch >= fork_parameters.capella.epoch { - // return CapellaExecutionPayloadHeader::from(header.execution.clone()) - // .tree_hash_root() - // .into(); - // } - H256::default() } @@ -285,8 +322,7 @@ pub fn is_valid_light_client_header( validate_merkle_branch( &get_lc_execution_root::(fork_parameters, header), &header.execution_branch, - floorlog2(EXECUTION_PAYLOAD_INDEX), - get_subtree_index(EXECUTION_PAYLOAD_INDEX), + EXECUTION_PAYLOAD_GINDEX, &header.beacon.body_root, ) } diff --git a/lib/ethereum-sync-protocol/src/utils.rs b/lib/ethereum-sync-protocol/src/utils.rs index c1c8c93906..9ce3615b2a 100644 --- a/lib/ethereum-sync-protocol/src/utils.rs +++ b/lib/ethereum-sync-protocol/src/utils.rs @@ -1,4 +1,5 @@ use beacon_api_types::{ + consts::{floorlog2, get_subtree_index}, Domain, DomainType, ForkData, ForkParameters, SigningData, Slot, Version, EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SECONDS_PER_SLOT, SLOTS_PER_EPOCH, }; @@ -139,12 +140,12 @@ pub fn validate_signature_supermajority(sync_committee_bits: &[u8]) -> bool { pub fn validate_merkle_branch<'a>( leaf: &H256, branch: impl IntoIterator, - depth: usize, - index: u64, + gindex: u64, root: &H256, ) -> Result<(), Error> { + let depth = floorlog2(gindex); + let index = get_subtree_index(gindex); let branch = branch.into_iter().cloned().collect::>(); - 'block: { let mut value = *leaf; @@ -340,8 +341,8 @@ pub fn validate_merkle_branch<'a>( // // validate_merkle_branch( // // &valid_leaf, // // &valid_branch, -// // floorlog2(EXECUTION_PAYLOAD_INDEX), -// // EXECUTION_PAYLOAD_INDEX, +// // floorlog2(EXECUTION_PAYLOAD_GINDEX), +// // EXECUTION_PAYLOAD_GINDEX, // // &valid_root, // // ), // // Ok(()) @@ -351,8 +352,8 @@ pub fn validate_merkle_branch<'a>( // // assert!(validate_merkle_branch( // // &valid_leaf, // // &valid_branch, -// // floorlog2(EXECUTION_PAYLOAD_INDEX), -// // EXECUTION_PAYLOAD_INDEX + 1, +// // floorlog2(EXECUTION_PAYLOAD_GINDEX), +// // EXECUTION_PAYLOAD_GINDEX + 1, // // &valid_root, // // ) // // .is_err()); @@ -367,8 +368,8 @@ pub fn validate_merkle_branch<'a>( // // assert!(validate_merkle_branch( // // &invalid_leaf, // // &valid_branch, -// // floorlog2(EXECUTION_PAYLOAD_INDEX), -// // EXECUTION_PAYLOAD_INDEX, +// // floorlog2(EXECUTION_PAYLOAD_GINDEX), +// // EXECUTION_PAYLOAD_GINDEX, // // &valid_root, // // ) // // .is_err()); @@ -383,8 +384,8 @@ pub fn validate_merkle_branch<'a>( // // assert!(validate_merkle_branch( // // &valid_leaf, // // &invalid_branch, -// // floorlog2(EXECUTION_PAYLOAD_INDEX), -// // EXECUTION_PAYLOAD_INDEX, +// // floorlog2(EXECUTION_PAYLOAD_GINDEX), +// // EXECUTION_PAYLOAD_GINDEX, // // &valid_root, // // ) // // .is_err());