From 8715589e40d8b39433cf02b7c0c6cd2612e28749 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:36:02 +0200 Subject: [PATCH] Add electra lightclient types --- .../types/src/execution_payload_header.rs | 8 +- consensus/types/src/lib.rs | 8 +- consensus/types/src/light_client_bootstrap.rs | 36 ++++--- .../types/src/light_client_finality_update.rs | 99 ++++++++++--------- consensus/types/src/light_client_header.rs | 54 ++++++++-- .../src/light_client_optimistic_update.rs | 37 ++++--- consensus/types/src/light_client_update.rs | 31 +++++- 7 files changed, 181 insertions(+), 92 deletions(-) diff --git a/consensus/types/src/execution_payload_header.rs b/consensus/types/src/execution_payload_header.rs index a3df69652c2..b8d0c8c2b0f 100644 --- a/consensus/types/src/execution_payload_header.rs +++ b/consensus/types/src/execution_payload_header.rs @@ -118,12 +118,8 @@ impl ExecutionPayloadHeader { pub fn ssz_max_var_len_for_fork(fork_name: ForkName) -> usize { // Matching here in case variable fields are added in future forks. match fork_name { - ForkName::Base - | ForkName::Altair - | ForkName::Bellatrix - | ForkName::Capella - | ForkName::Deneb - | ForkName::Electra => { + ForkName::Base | ForkName::Altair => 0, + ForkName::Bellatrix | ForkName::Capella | ForkName::Deneb | ForkName::Electra => { // Max size of variable length `extra_data` field E::max_extra_data_bytes() * ::ssz_fixed_len() } diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 2a73ff0f37a..1683ae6c977 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -181,22 +181,24 @@ pub use crate::indexed_attestation::{ }; pub use crate::light_client_bootstrap::{ LightClientBootstrap, LightClientBootstrapAltair, LightClientBootstrapCapella, - LightClientBootstrapDeneb, + LightClientBootstrapDeneb, LightClientBootstrapElectra, }; pub use crate::light_client_finality_update::{ LightClientFinalityUpdate, LightClientFinalityUpdateAltair, LightClientFinalityUpdateCapella, - LightClientFinalityUpdateDeneb, + LightClientFinalityUpdateDeneb, LightClientFinalityUpdateElectra, }; pub use crate::light_client_header::{ LightClientHeader, LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, + LightClientHeaderElectra, }; pub use crate::light_client_optimistic_update::{ LightClientOptimisticUpdate, LightClientOptimisticUpdateAltair, LightClientOptimisticUpdateCapella, LightClientOptimisticUpdateDeneb, + LightClientOptimisticUpdateElectra, }; pub use crate::light_client_update::{ Error as LightClientError, LightClientUpdate, LightClientUpdateAltair, - LightClientUpdateCapella, LightClientUpdateDeneb, + LightClientUpdateCapella, LightClientUpdateDeneb, LightClientUpdateElectra, }; pub use crate::participation_flags::ParticipationFlags; pub use crate::participation_list::ParticipationList; diff --git a/consensus/types/src/light_client_bootstrap.rs b/consensus/types/src/light_client_bootstrap.rs index 61da0e1b117..e3a85744ded 100644 --- a/consensus/types/src/light_client_bootstrap.rs +++ b/consensus/types/src/light_client_bootstrap.rs @@ -1,7 +1,8 @@ use crate::{ light_client_update::*, test_utils::TestRandom, BeaconState, ChainSpec, EthSpec, FixedVector, ForkName, ForkVersionDeserialize, Hash256, LightClientHeader, LightClientHeaderAltair, - LightClientHeaderCapella, LightClientHeaderDeneb, SignedBeaconBlock, Slot, SyncCommittee, + LightClientHeaderCapella, LightClientHeaderDeneb, LightClientHeaderElectra, SignedBeaconBlock, + Slot, SyncCommittee, }; use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; @@ -16,7 +17,7 @@ use tree_hash_derive::TreeHash; /// A LightClientBootstrap is the initializer we send over to light_client nodes /// that are trying to generate their basic storage when booting up. #[superstruct( - variants(Altair, Capella, Deneb), + variants(Altair, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -51,6 +52,8 @@ pub struct LightClientBootstrap { pub header: LightClientHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "header_deneb"))] pub header: LightClientHeaderDeneb, + #[superstruct(only(Electra), partial_getter(rename = "header_electra"))] + pub header: LightClientHeaderElectra, /// The `SyncCommittee` used in the requested period. pub current_sync_committee: Arc>, /// Merkle proof for sync committee @@ -66,6 +69,7 @@ impl LightClientBootstrap { Self::Altair(_) => func(ForkName::Altair), Self::Capella(_) => func(ForkName::Capella), Self::Deneb(_) => func(ForkName::Deneb), + Self::Electra(_) => func(ForkName::Electra), } } @@ -82,9 +86,8 @@ impl LightClientBootstrap { Self::Altair(LightClientBootstrapAltair::from_ssz_bytes(bytes)?) } ForkName::Capella => Self::Capella(LightClientBootstrapCapella::from_ssz_bytes(bytes)?), - ForkName::Deneb | ForkName::Electra => { - Self::Deneb(LightClientBootstrapDeneb::from_ssz_bytes(bytes)?) - } + ForkName::Deneb => Self::Deneb(LightClientBootstrapDeneb::from_ssz_bytes(bytes)?), + ForkName::Electra => Self::Electra(LightClientBootstrapElectra::from_ssz_bytes(bytes)?), ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( "LightClientBootstrap decoding for {fork_name} not implemented" @@ -97,18 +100,16 @@ impl LightClientBootstrap { #[allow(clippy::arithmetic_side_effects)] pub fn ssz_max_len_for_fork(fork_name: ForkName) -> usize { - // TODO(electra): review electra changes - match fork_name { + let fixed_len = match fork_name { ForkName::Base => 0, - ForkName::Altair - | ForkName::Bellatrix - | ForkName::Capella - | ForkName::Deneb - | ForkName::Electra => { + ForkName::Altair | ForkName::Bellatrix => { as Encode>::ssz_fixed_len() - + LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } - } + ForkName::Capella => as Encode>::ssz_fixed_len(), + ForkName::Deneb => as Encode>::ssz_fixed_len(), + ForkName::Electra => as Encode>::ssz_fixed_len(), + }; + fixed_len + LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } pub fn from_beacon_state( @@ -138,11 +139,16 @@ impl LightClientBootstrap { current_sync_committee, current_sync_committee_branch, }), - ForkName::Deneb | ForkName::Electra => Self::Deneb(LightClientBootstrapDeneb { + ForkName::Deneb => Self::Deneb(LightClientBootstrapDeneb { header: LightClientHeaderDeneb::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch, }), + ForkName::Electra => Self::Electra(LightClientBootstrapElectra { + header: LightClientHeaderElectra::block_to_light_client_header(block)?, + current_sync_committee, + current_sync_committee_branch, + }), }; Ok(light_client_bootstrap) diff --git a/consensus/types/src/light_client_finality_update.rs b/consensus/types/src/light_client_finality_update.rs index 29c526e2916..a9e24e03db1 100644 --- a/consensus/types/src/light_client_finality_update.rs +++ b/consensus/types/src/light_client_finality_update.rs @@ -2,7 +2,8 @@ use super::{EthSpec, FixedVector, Hash256, LightClientHeader, Slot, SyncAggregat use crate::ChainSpec; use crate::{ light_client_update::*, test_utils::TestRandom, ForkName, ForkVersionDeserialize, - LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, SignedBeaconBlock, + LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, + LightClientHeaderElectra, SignedBeaconBlock, }; use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; @@ -15,7 +16,7 @@ use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; #[superstruct( - variants(Altair, Capella, Deneb), + variants(Altair, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -50,6 +51,8 @@ pub struct LightClientFinalityUpdate { pub attested_header: LightClientHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "attested_header_deneb"))] pub attested_header: LightClientHeaderDeneb, + #[superstruct(only(Electra), partial_getter(rename = "attested_header_electra"))] + pub attested_header: LightClientHeaderElectra, /// The last `BeaconBlockHeader` from the last attested finalized block (end of epoch). #[superstruct(only(Altair), partial_getter(rename = "finalized_header_altair"))] pub finalized_header: LightClientHeaderAltair, @@ -57,6 +60,8 @@ pub struct LightClientFinalityUpdate { pub finalized_header: LightClientHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "finalized_header_deneb"))] pub finalized_header: LightClientHeaderDeneb, + #[superstruct(only(Electra), partial_getter(rename = "finalized_header_electra"))] + pub finalized_header: LightClientHeaderElectra, /// Merkle proof attesting finalized header. #[test_random(default)] pub finality_branch: FixedVector, @@ -80,7 +85,7 @@ impl LightClientFinalityUpdate { .map_err(|_| Error::InconsistentFork)? { ForkName::Altair | ForkName::Bellatrix => { - let finality_update = LightClientFinalityUpdateAltair { + Self::Altair(LightClientFinalityUpdateAltair { attested_header: LightClientHeaderAltair::block_to_light_client_header( attested_block, )?, @@ -90,37 +95,42 @@ impl LightClientFinalityUpdate { finality_branch, sync_aggregate, signature_slot, - }; - Self::Altair(finality_update) - } - ForkName::Capella => { - let finality_update = LightClientFinalityUpdateCapella { - attested_header: LightClientHeaderCapella::block_to_light_client_header( - attested_block, - )?, - finalized_header: LightClientHeaderCapella::block_to_light_client_header( - finalized_block, - )?, - finality_branch, - sync_aggregate, - signature_slot, - }; - Self::Capella(finality_update) - } - ForkName::Deneb | ForkName::Electra => { - let finality_update = LightClientFinalityUpdateDeneb { - attested_header: LightClientHeaderDeneb::block_to_light_client_header( - attested_block, - )?, - finalized_header: LightClientHeaderDeneb::block_to_light_client_header( - finalized_block, - )?, - finality_branch, - sync_aggregate, - signature_slot, - }; - Self::Deneb(finality_update) + }) } + ForkName::Capella => Self::Capella(LightClientFinalityUpdateCapella { + attested_header: LightClientHeaderCapella::block_to_light_client_header( + attested_block, + )?, + finalized_header: LightClientHeaderCapella::block_to_light_client_header( + finalized_block, + )?, + finality_branch, + sync_aggregate, + signature_slot, + }), + ForkName::Deneb => Self::Deneb(LightClientFinalityUpdateDeneb { + attested_header: LightClientHeaderDeneb::block_to_light_client_header( + attested_block, + )?, + finalized_header: LightClientHeaderDeneb::block_to_light_client_header( + finalized_block, + )?, + finality_branch, + sync_aggregate, + signature_slot, + }), + ForkName::Electra => Self::Electra(LightClientFinalityUpdateElectra { + attested_header: LightClientHeaderElectra::block_to_light_client_header( + attested_block, + )?, + finalized_header: LightClientHeaderElectra::block_to_light_client_header( + finalized_block, + )?, + finality_branch, + sync_aggregate, + signature_slot, + }), + ForkName::Base => return Err(Error::AltairForkNotActive), }; @@ -135,6 +145,7 @@ impl LightClientFinalityUpdate { Self::Altair(_) => func(ForkName::Altair), Self::Capella(_) => func(ForkName::Capella), Self::Deneb(_) => func(ForkName::Deneb), + Self::Electra(_) => func(ForkName::Electra), } } @@ -153,8 +164,9 @@ impl LightClientFinalityUpdate { ForkName::Capella => { Self::Capella(LightClientFinalityUpdateCapella::from_ssz_bytes(bytes)?) } - ForkName::Deneb | ForkName::Electra => { - Self::Deneb(LightClientFinalityUpdateDeneb::from_ssz_bytes(bytes)?) + ForkName::Deneb => Self::Deneb(LightClientFinalityUpdateDeneb::from_ssz_bytes(bytes)?), + ForkName::Electra => { + Self::Electra(LightClientFinalityUpdateElectra::from_ssz_bytes(bytes)?) } ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( @@ -168,18 +180,17 @@ impl LightClientFinalityUpdate { #[allow(clippy::arithmetic_side_effects)] pub fn ssz_max_len_for_fork(fork_name: ForkName) -> usize { - // TODO(electra): review electra changes - match fork_name { + let fixed_size = match fork_name { ForkName::Base => 0, - ForkName::Altair - | ForkName::Bellatrix - | ForkName::Capella - | ForkName::Deneb - | ForkName::Electra => { + ForkName::Altair | ForkName::Bellatrix => { as Encode>::ssz_fixed_len() - + 2 * LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } - } + ForkName::Capella => as Encode>::ssz_fixed_len(), + ForkName::Deneb => as Encode>::ssz_fixed_len(), + ForkName::Electra => as Encode>::ssz_fixed_len(), + }; + // `2 *` because there are two headers in the update + fixed_size + 2 * LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } } diff --git a/consensus/types/src/light_client_header.rs b/consensus/types/src/light_client_header.rs index 213ec90f955..1d6432ed6f3 100644 --- a/consensus/types/src/light_client_header.rs +++ b/consensus/types/src/light_client_header.rs @@ -4,7 +4,7 @@ use crate::ForkVersionDeserialize; use crate::{light_client_update::*, BeaconBlockBody}; use crate::{ test_utils::TestRandom, EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, - FixedVector, Hash256, SignedBeaconBlock, + ExecutionPayloadHeaderElectra, FixedVector, Hash256, SignedBeaconBlock, }; use crate::{BeaconBlockHeader, ExecutionPayloadHeader}; use derivative::Derivative; @@ -17,7 +17,7 @@ use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; #[superstruct( - variants(Altair, Capella, Deneb), + variants(Altair, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -54,8 +54,13 @@ pub struct LightClientHeader { pub execution: ExecutionPayloadHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "execution_payload_header_deneb"))] pub execution: ExecutionPayloadHeaderDeneb, + #[superstruct( + only(Electra), + partial_getter(rename = "execution_payload_header_electra") + )] + pub execution: ExecutionPayloadHeaderElectra, - #[superstruct(only(Capella, Deneb))] + #[superstruct(only(Capella, Deneb, Electra))] pub execution_branch: FixedVector, #[ssz(skip_serializing, skip_deserializing)] @@ -81,9 +86,12 @@ impl LightClientHeader { ForkName::Capella => LightClientHeader::Capella( LightClientHeaderCapella::block_to_light_client_header(block)?, ), - ForkName::Deneb | ForkName::Electra => LightClientHeader::Deneb( + ForkName::Deneb => LightClientHeader::Deneb( LightClientHeaderDeneb::block_to_light_client_header(block)?, ), + ForkName::Electra => LightClientHeader::Electra( + LightClientHeaderElectra::block_to_light_client_header(block)?, + ), }; Ok(header) } @@ -96,9 +104,12 @@ impl LightClientHeader { ForkName::Capella => { LightClientHeader::Capella(LightClientHeaderCapella::from_ssz_bytes(bytes)?) } - ForkName::Deneb | ForkName::Electra => { + ForkName::Deneb => { LightClientHeader::Deneb(LightClientHeaderDeneb::from_ssz_bytes(bytes)?) } + ForkName::Electra => { + LightClientHeader::Electra(LightClientHeaderElectra::from_ssz_bytes(bytes)?) + } ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( "LightClientHeader decoding for {fork_name} not implemented" @@ -192,6 +203,34 @@ impl LightClientHeaderDeneb { } } +impl LightClientHeaderElectra { + pub fn block_to_light_client_header(block: &SignedBeaconBlock) -> Result { + let payload = block + .message() + .execution_payload()? + .execution_payload_electra()?; + + let header = ExecutionPayloadHeaderElectra::from(payload); + let beacon_block_body = BeaconBlockBody::from( + block + .message() + .body_electra() + .map_err(|_| Error::BeaconBlockBodyError)? + .to_owned(), + ); + + let execution_branch = + beacon_block_body.block_body_merkle_proof(EXECUTION_PAYLOAD_INDEX)?; + + Ok(LightClientHeaderElectra { + beacon: block.message().block_header(), + execution: header, + execution_branch: FixedVector::new(execution_branch)?, + _phantom_data: PhantomData, + }) + } +} + impl ForkVersionDeserialize for LightClientHeader { fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>( value: serde_json::value::Value, @@ -204,9 +243,12 @@ impl ForkVersionDeserialize for LightClientHeader { ForkName::Capella => serde_json::from_value(value) .map(|light_client_header| Self::Capella(light_client_header)) .map_err(serde::de::Error::custom), - ForkName::Deneb | ForkName::Electra => serde_json::from_value(value) + ForkName::Deneb => serde_json::from_value(value) .map(|light_client_header| Self::Deneb(light_client_header)) .map_err(serde::de::Error::custom), + ForkName::Electra => serde_json::from_value(value) + .map(|light_client_header| Self::Electra(light_client_header)) + .map_err(serde::de::Error::custom), ForkName::Base => Err(serde::de::Error::custom(format!( "LightClientHeader deserialization for {fork_name} not implemented" ))), diff --git a/consensus/types/src/light_client_optimistic_update.rs b/consensus/types/src/light_client_optimistic_update.rs index 4727673f6c0..708f24e7701 100644 --- a/consensus/types/src/light_client_optimistic_update.rs +++ b/consensus/types/src/light_client_optimistic_update.rs @@ -2,7 +2,7 @@ use super::{EthSpec, ForkName, ForkVersionDeserialize, LightClientHeader, Slot, use crate::test_utils::TestRandom; use crate::{ light_client_update::*, ChainSpec, LightClientHeaderAltair, LightClientHeaderCapella, - LightClientHeaderDeneb, SignedBeaconBlock, + LightClientHeaderDeneb, LightClientHeaderElectra, SignedBeaconBlock, }; use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; @@ -18,7 +18,7 @@ use tree_hash_derive::TreeHash; /// A LightClientOptimisticUpdate is the update we send on each slot, /// it is based off the current unfinalized epoch is verified only against BLS signature. #[superstruct( - variants(Altair, Capella, Deneb), + variants(Altair, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -53,6 +53,8 @@ pub struct LightClientOptimisticUpdate { pub attested_header: LightClientHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "attested_header_deneb"))] pub attested_header: LightClientHeaderDeneb, + #[superstruct(only(Electra), partial_getter(rename = "attested_header_electra"))] + pub attested_header: LightClientHeaderElectra, /// current sync aggregate pub sync_aggregate: SyncAggregate, /// Slot of the sync aggregated signature @@ -86,13 +88,20 @@ impl LightClientOptimisticUpdate { sync_aggregate, signature_slot, }), - ForkName::Deneb | ForkName::Electra => Self::Deneb(LightClientOptimisticUpdateDeneb { + ForkName::Deneb => Self::Deneb(LightClientOptimisticUpdateDeneb { attested_header: LightClientHeaderDeneb::block_to_light_client_header( attested_block, )?, sync_aggregate, signature_slot, }), + ForkName::Electra => Self::Electra(LightClientOptimisticUpdateElectra { + attested_header: LightClientHeaderElectra::block_to_light_client_header( + attested_block, + )?, + sync_aggregate, + signature_slot, + }), ForkName::Base => return Err(Error::AltairForkNotActive), }; @@ -107,6 +116,7 @@ impl LightClientOptimisticUpdate { Self::Altair(_) => func(ForkName::Altair), Self::Capella(_) => func(ForkName::Capella), Self::Deneb(_) => func(ForkName::Deneb), + Self::Electra(_) => func(ForkName::Electra), } } @@ -139,9 +149,12 @@ impl LightClientOptimisticUpdate { ForkName::Capella => { Self::Capella(LightClientOptimisticUpdateCapella::from_ssz_bytes(bytes)?) } - ForkName::Deneb | ForkName::Electra => { + ForkName::Deneb => { Self::Deneb(LightClientOptimisticUpdateDeneb::from_ssz_bytes(bytes)?) } + ForkName::Electra => { + Self::Electra(LightClientOptimisticUpdateElectra::from_ssz_bytes(bytes)?) + } ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( "LightClientOptimisticUpdate decoding for {fork_name} not implemented" @@ -154,18 +167,16 @@ impl LightClientOptimisticUpdate { #[allow(clippy::arithmetic_side_effects)] pub fn ssz_max_len_for_fork(fork_name: ForkName) -> usize { - // TODO(electra): review electra changes - match fork_name { + let fixed_len = match fork_name { ForkName::Base => 0, - ForkName::Altair - | ForkName::Bellatrix - | ForkName::Capella - | ForkName::Deneb - | ForkName::Electra => { + ForkName::Altair | ForkName::Bellatrix => { as Encode>::ssz_fixed_len() - + LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } - } + ForkName::Capella => as Encode>::ssz_fixed_len(), + ForkName::Deneb => as Encode>::ssz_fixed_len(), + ForkName::Electra => as Encode>::ssz_fixed_len(), + }; + fixed_len + LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } } diff --git a/consensus/types/src/light_client_update.rs b/consensus/types/src/light_client_update.rs index 002fbea2d37..76ad5689888 100644 --- a/consensus/types/src/light_client_update.rs +++ b/consensus/types/src/light_client_update.rs @@ -1,4 +1,5 @@ use super::{EthSpec, FixedVector, Hash256, Slot, SyncAggregate, SyncCommittee}; +use crate::light_client_header::LightClientHeaderElectra; use crate::{ beacon_state, test_utils::TestRandom, BeaconBlock, BeaconBlockHeader, BeaconState, ChainSpec, ForkName, ForkVersionDeserialize, LightClientHeaderAltair, LightClientHeaderCapella, @@ -76,7 +77,7 @@ impl From for Error { /// or to sync up to the last committee period, we need to have one ready for each ALTAIR period /// we go over, note: there is no need to keep all of the updates from [ALTAIR_PERIOD, CURRENT_PERIOD]. #[superstruct( - variants(Altair, Capella, Deneb), + variants(Altair, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -111,6 +112,8 @@ pub struct LightClientUpdate { pub attested_header: LightClientHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "attested_header_deneb"))] pub attested_header: LightClientHeaderDeneb, + #[superstruct(only(Electra), partial_getter(rename = "attested_header_electra"))] + pub attested_header: LightClientHeaderElectra, /// The `SyncCommittee` used in the next period. pub next_sync_committee: Arc>, /// Merkle proof for next sync committee @@ -122,6 +125,8 @@ pub struct LightClientUpdate { pub finalized_header: LightClientHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "finalized_header_deneb"))] pub finalized_header: LightClientHeaderDeneb, + #[superstruct(only(Electra), partial_getter(rename = "finalized_header_electra"))] + pub finalized_header: LightClientHeaderElectra, /// Merkle proof attesting finalized header. pub finality_branch: FixedVector, /// current sync aggreggate @@ -221,7 +226,7 @@ impl LightClientUpdate { signature_slot: block.slot(), }) } - ForkName::Deneb | ForkName::Electra => { + ForkName::Deneb => { let attested_header = LightClientHeaderDeneb::block_to_light_client_header(attested_block)?; let finalized_header = @@ -236,6 +241,23 @@ impl LightClientUpdate { signature_slot: block.slot(), }) } + ForkName::Electra => { + let attested_header = + LightClientHeaderElectra::block_to_light_client_header(attested_block)?; + let finalized_header = + LightClientHeaderElectra::block_to_light_client_header(finalized_block)?; + Self::Electra(LightClientUpdateElectra { + attested_header, + next_sync_committee: attested_state.next_sync_committee()?.clone(), + next_sync_committee_branch: FixedVector::new(next_sync_committee_branch)?, + finalized_header, + finality_branch: FixedVector::new(finality_branch)?, + sync_aggregate: sync_aggregate.clone(), + signature_slot: block.slot(), + }) + } // To add a new fork, just append the new fork variant on the latest fork. Forks that + // have a distinct execution header will need a new LightClientUdpate variant only + // if you need to test or support lightclient usages }; Ok(light_client_update) @@ -247,9 +269,8 @@ impl LightClientUpdate { Self::Altair(LightClientUpdateAltair::from_ssz_bytes(bytes)?) } ForkName::Capella => Self::Capella(LightClientUpdateCapella::from_ssz_bytes(bytes)?), - ForkName::Deneb | ForkName::Electra => { - Self::Deneb(LightClientUpdateDeneb::from_ssz_bytes(bytes)?) - } + ForkName::Deneb => Self::Deneb(LightClientUpdateDeneb::from_ssz_bytes(bytes)?), + ForkName::Electra => Self::Electra(LightClientUpdateElectra::from_ssz_bytes(bytes)?), ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( "LightClientUpdate decoding for {fork_name} not implemented"