11use super :: * ;
22use borsh:: { BorshDeserialize , BorshSerialize } ;
33use std:: io:: { Error , Write } ;
4- use tree_hash:: MerkleHasher ;
4+ use tree_hash:: { MerkleHasher , TreeHash } ;
55
66#[ cfg( not( target_arch = "wasm32" ) ) ]
77use {
@@ -29,6 +29,74 @@ arr_wrapper_impl_tree_hash_and_borsh!(PublicKeyBytes, PUBLIC_KEY_BYTES_LEN);
2929arr_wrapper_impl_tree_hash_and_borsh ! ( SignatureBytes , SIGNATURE_BYTES_LEN ) ;
3030arr_wrapper_impl_tree_hash_and_borsh ! ( SyncCommitteeBits , SYNC_COMMITTEE_BITS_SIZE_IN_BYTES ) ;
3131
32+ #[ derive( Debug , Clone , BorshSchema ) ]
33+ pub struct ExtraData ( pub Vec < u8 > ) ;
34+
35+ impl tree_hash:: TreeHash for ExtraData {
36+ fn tree_hash_type ( ) -> tree_hash:: TreeHashType {
37+ tree_hash:: TreeHashType :: List
38+ }
39+
40+ fn tree_hash_packed_encoding ( & self ) -> tree_hash:: PackedEncoding {
41+ unreachable ! ( "List should never be packed." )
42+ }
43+
44+ fn tree_hash_packing_factor ( ) -> usize {
45+ unreachable ! ( "List should never be packed." )
46+ }
47+
48+ fn tree_hash_root ( & self ) -> tree_hash:: Hash256 {
49+ let mut hasher =
50+ tree_hash:: MerkleHasher :: with_leaves ( self . 0 . len ( ) . div_ceil ( tree_hash:: BYTES_PER_CHUNK ) ) ;
51+
52+ for item in & self . 0 {
53+ hasher. write ( & item. tree_hash_packed_encoding ( ) ) . unwrap ( ) ;
54+ }
55+
56+ let root = hasher. finish ( ) . unwrap ( ) ;
57+ tree_hash:: mix_in_length ( & root, self . 0 . len ( ) )
58+ }
59+ }
60+
61+ // Add Borsh implementations
62+ impl borsh:: BorshSerialize for ExtraData {
63+ fn serialize < W : std:: io:: Write > ( & self , writer : & mut W ) -> std:: io:: Result < ( ) > {
64+ BorshSerialize :: serialize ( & self . 0 , writer)
65+ }
66+ }
67+
68+ impl borsh:: BorshDeserialize for ExtraData {
69+ fn deserialize_reader < R : std:: io:: Read > ( reader : & mut R ) -> std:: io:: Result < Self > {
70+ Ok ( ExtraData ( Vec :: < u8 > :: deserialize_reader ( reader) ?) )
71+ }
72+ }
73+
74+ #[ cfg( not( target_arch = "wasm32" ) ) ]
75+ impl serde:: Serialize for ExtraData {
76+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
77+ where
78+ S : serde:: Serializer ,
79+ {
80+ // Always serialize as hex string
81+ let hex_string = format ! ( "0x{}" , hex:: encode( & self . 0 ) ) ;
82+ serializer. serialize_str ( & hex_string)
83+ }
84+ }
85+
86+ #[ cfg( not( target_arch = "wasm32" ) ) ]
87+ impl < ' de > serde:: Deserialize < ' de > for ExtraData {
88+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
89+ where
90+ D : serde:: Deserializer < ' de > ,
91+ {
92+ let hex_string = <std:: string:: String as Deserialize >:: deserialize ( deserializer) ?;
93+ let hex_string = hex_string. strip_prefix ( "0x" ) . unwrap_or ( & hex_string) ;
94+ let bytes = hex:: decode ( hex_string)
95+ . map_err ( |e| serde:: de:: Error :: custom ( format ! ( "Invalid hex: {}" , e) ) ) ?;
96+ Ok ( ExtraData ( bytes) )
97+ }
98+ }
99+
32100#[ derive(
33101 Debug , Clone , BorshDeserialize , BorshSchema , BorshSerialize , tree_hash_derive:: TreeHash ,
34102) ]
@@ -43,18 +111,62 @@ pub struct BeaconBlockHeader {
43111 pub body_root : H256 ,
44112}
45113
114+ // New execution header structure for Electra
115+ #[ derive(
116+ Debug , Clone , BorshDeserialize , BorshSchema , BorshSerialize , tree_hash_derive:: TreeHash ,
117+ ) ]
118+ #[ cfg_attr( not( target_arch = "wasm32" ) , derive( Serialize , Deserialize ) ) ]
119+ pub struct ExecutionHeader {
120+ pub parent_hash : H256 ,
121+ pub fee_recipient : H160 ,
122+ pub state_root : H256 ,
123+ pub receipts_root : H256 ,
124+ pub logs_bloom : Bloom ,
125+ pub prev_randao : H256 ,
126+ #[ cfg_attr( not( target_arch = "wasm32" ) , serde( with = "serde_utils::quoted_u64" ) ) ]
127+ pub block_number : u64 ,
128+ #[ cfg_attr( not( target_arch = "wasm32" ) , serde( with = "serde_utils::quoted_u64" ) ) ]
129+ pub gas_limit : u64 ,
130+ #[ cfg_attr( not( target_arch = "wasm32" ) , serde( with = "serde_utils::quoted_u64" ) ) ]
131+ pub gas_used : u64 ,
132+ #[ cfg_attr( not( target_arch = "wasm32" ) , serde( with = "serde_utils::quoted_u64" ) ) ]
133+ pub timestamp : u64 ,
134+ pub extra_data : ExtraData ,
135+ #[ cfg_attr( not( target_arch = "wasm32" ) , serde( with = "serde_utils::quoted_u64" ) ) ]
136+ pub base_fee_per_gas : u64 ,
137+ pub block_hash : H256 ,
138+ pub transactions_root : H256 ,
139+ pub withdrawals_root : H256 ,
140+ #[ cfg_attr( not( target_arch = "wasm32" ) , serde( with = "serde_utils::quoted_u64" ) ) ]
141+ pub blob_gas_used : u64 ,
142+ #[ cfg_attr( not( target_arch = "wasm32" ) , serde( with = "serde_utils::quoted_u64" ) ) ]
143+ pub excess_blob_gas : u64 ,
144+ }
145+
146+ // New combined header structure
147+ #[ derive( Debug , Clone , BorshDeserialize , BorshSchema , BorshSerialize ) ]
148+ #[ cfg_attr( not( target_arch = "wasm32" ) , derive( Serialize , Deserialize ) ) ]
149+ pub struct AttestedHeader {
150+ pub beacon : BeaconBlockHeader ,
151+ pub execution : ExecutionHeader ,
152+ pub execution_branch : Vec < H256 > ,
153+ }
154+
155+ #[ derive( Debug , Clone , BorshDeserialize , BorshSchema , BorshSerialize ) ]
156+ #[ cfg_attr( not( target_arch = "wasm32" ) , derive( Serialize , Deserialize ) ) ]
157+ pub struct FinalizedHeader {
158+ pub beacon : BeaconBlockHeader ,
159+ pub execution : ExecutionHeader ,
160+ pub execution_branch : Vec < H256 > ,
161+ }
162+
46163#[ derive( Debug , Clone , PartialEq , tree_hash_derive:: TreeHash ) ]
47164pub struct ForkData {
48165 pub current_version : ForkVersion ,
49166 pub genesis_validators_root : H256 ,
50167}
51168
52- #[ derive( Debug , PartialEq , Clone , tree_hash_derive:: TreeHash ) ]
53- pub struct SigningData {
54- pub object_root : H256 ,
55- pub domain : H256 ,
56- }
57-
169+ /// This is used specifically for backwards-compatibility, storing state in the contract
58170#[ derive( Debug , Clone , BorshDeserialize , BorshSchema , BorshSerialize ) ]
59171#[ cfg_attr( not( target_arch = "wasm32" ) , derive( Serialize , Deserialize ) ) ]
60172pub struct ExtendedBeaconBlockHeader {
@@ -63,17 +175,23 @@ pub struct ExtendedBeaconBlockHeader {
63175 pub execution_block_hash : H256 ,
64176}
65177
66- impl From < HeaderUpdate > for ExtendedBeaconBlockHeader {
67- fn from ( item : HeaderUpdate ) -> Self {
68- let root = item . beacon_header . tree_hash_root ( ) ;
69- ExtendedBeaconBlockHeader {
70- header : item . beacon_header ,
71- beacon_block_root : H256 ( root . 0 . into ( ) ) ,
72- execution_block_hash : item . execution_block_hash ,
178+ impl From < FinalizedHeader > for ExtendedBeaconBlockHeader {
179+ fn from ( finalized_header : FinalizedHeader ) -> Self {
180+ let beacon = finalized_header . beacon ;
181+ Self {
182+ header : beacon . clone ( ) ,
183+ beacon_block_root : beacon . tree_hash_root ( ) . 0 . into ( ) ,
184+ execution_block_hash : finalized_header . execution . block_hash ,
73185 }
74186 }
75187}
76188
189+ #[ derive( Debug , PartialEq , Clone , tree_hash_derive:: TreeHash ) ]
190+ pub struct SigningData {
191+ pub object_root : H256 ,
192+ pub domain : H256 ,
193+ }
194+
77195#[ derive( Debug , Clone , BorshDeserialize , BorshSchema , BorshSerialize ) ]
78196#[ cfg_attr( not( target_arch = "wasm32" ) , derive( Serialize , Deserialize ) ) ]
79197pub struct SyncCommitteePublicKeys ( pub Vec < PublicKeyBytes > ) ;
@@ -95,40 +213,73 @@ pub struct SyncAggregate {
95213 pub sync_committee_signature : SignatureBytes ,
96214}
97215
216+ // Updated light client update structure for Electra
98217#[ derive( Debug , Clone , BorshDeserialize , BorshSchema , BorshSerialize ) ]
99218#[ cfg_attr( not( target_arch = "wasm32" ) , derive( Serialize , Deserialize ) ) ]
100- pub struct SyncCommitteeUpdate {
101- pub next_sync_committee : SyncCommittee ,
102- pub next_sync_committee_branch : Vec < H256 > ,
219+ pub struct LightClientUpdate {
220+ pub attested_header : AttestedHeader ,
221+ pub next_sync_committee : Option < SyncCommittee > ,
222+ pub next_sync_committee_branch : Option < Vec < H256 > > ,
223+ pub finalized_header : FinalizedHeader ,
224+ pub finality_branch : Vec < H256 > ,
225+ pub sync_aggregate : SyncAggregate ,
226+ #[ cfg_attr( not( target_arch = "wasm32" ) , serde( with = "serde_utils::quoted_u64" ) ) ]
227+ pub signature_slot : Slot ,
103228}
104229
105- #[ derive( Debug , Clone , BorshDeserialize , BorshSchema , BorshSerialize ) ]
230+ // Version enum for different Ethereum fork versions
231+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , BorshDeserialize , BorshSchema , BorshSerialize ) ]
106232#[ cfg_attr( not( target_arch = "wasm32" ) , derive( Serialize , Deserialize ) ) ]
107- pub struct HeaderUpdate {
108- pub beacon_header : BeaconBlockHeader ,
109- pub execution_block_hash : H256 ,
110- pub execution_hash_branch : Vec < H256 > ,
233+ #[ cfg_attr( not( target_arch = "wasm32" ) , serde( rename_all = "lowercase" ) ) ]
234+ pub enum LightClientVersion {
235+ Altair ,
236+ Bellatrix ,
237+ Capella ,
238+ Deneb ,
239+ Electra ,
111240}
112241
242+ impl LightClientVersion {
243+ pub fn as_str ( & self ) -> & ' static str {
244+ match self {
245+ LightClientVersion :: Altair => "altair" ,
246+ LightClientVersion :: Bellatrix => "bellatrix" ,
247+ LightClientVersion :: Capella => "capella" ,
248+ LightClientVersion :: Deneb => "deneb" ,
249+ LightClientVersion :: Electra => "electra" ,
250+ }
251+ }
252+ }
253+
254+ impl std:: fmt:: Display for LightClientVersion {
255+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
256+ write ! ( f, "{}" , self . as_str( ) )
257+ }
258+ }
259+
260+ // Top-level wrapper with version (optional, for when you need versioning)
113261#[ derive( Debug , Clone , BorshDeserialize , BorshSchema , BorshSerialize ) ]
114262#[ cfg_attr( not( target_arch = "wasm32" ) , derive( Serialize , Deserialize ) ) ]
115- pub struct FinalizedHeaderUpdate {
116- pub header_update : HeaderUpdate ,
117- pub finality_branch : Vec < H256 > ,
263+ pub struct VersionedLightClientUpdate {
264+ pub version : LightClientVersion ,
265+ pub data : LightClientUpdate ,
118266}
119267
268+ // For arrays of light client updates
120269#[ derive( Debug , Clone , BorshDeserialize , BorshSchema , BorshSerialize ) ]
121270#[ cfg_attr( not( target_arch = "wasm32" ) , derive( Serialize , Deserialize ) ) ]
122- pub struct LightClientUpdate {
123- pub attested_beacon_header : BeaconBlockHeader ,
124- pub sync_aggregate : SyncAggregate ,
125- #[ cfg_attr( not( target_arch = "wasm32" ) , serde( with = "serde_utils::quoted_u64" ) ) ]
126- pub signature_slot : Slot ,
127- pub finality_update : FinalizedHeaderUpdate ,
128- pub sync_committee_update : Option < SyncCommitteeUpdate > ,
271+ pub struct LightClientUpdates ( pub Vec < LightClientUpdate > ) ;
272+
273+ // Alternative: Version-specific data if structures differ significantly between versions
274+ #[ derive( Debug , Clone , BorshDeserialize , BorshSchema , BorshSerialize ) ]
275+ #[ cfg_attr( not( target_arch = "wasm32" ) , derive( Serialize , Deserialize ) ) ]
276+ pub enum LightClientUpdateVariant {
277+ Electra ( LightClientUpdate ) ,
278+ // Future versions can be added here with different data structures
279+ // Deneb(DenebLightClientUpdate),
129280}
130281
131- #[ derive( Clone , BorshDeserialize , BorshSchema , BorshSerialize ) ]
282+ #[ derive( Clone , BorshDeserialize , BorshSchema , BorshSerialize , Debug ) ]
132283pub struct LightClientState {
133284 pub finalized_beacon_header : ExtendedBeaconBlockHeader ,
134285 pub current_sync_committee : SyncCommittee ,
0 commit comments