11use crate :: errors:: BeaconChainError ;
22use crate :: { metrics, BeaconChainTypes , BeaconStore } ;
3+ use eth2:: types:: light_client_update:: CurrentSyncCommitteeProofLen ;
34use parking_lot:: { Mutex , RwLock } ;
45use safe_arith:: SafeArith ;
56use slog:: { debug, Logger } ;
67use ssz:: Decode ;
7- use ssz:: Encode ;
88use ssz_types:: FixedVector ;
99use std:: num:: NonZeroUsize ;
1010use std:: sync:: Arc ;
1111use store:: DBColumn ;
1212use store:: KeyValueStore ;
13+ use tree_hash:: TreeHash ;
1314use types:: light_client_update:: {
14- FinalizedRootProofLen , NextSyncCommitteeProofLen , FINALIZED_ROOT_INDEX ,
15- NEXT_SYNC_COMMITTEE_INDEX ,
15+ FinalizedRootProofLen , NextSyncCommitteeProofLen , CURRENT_SYNC_COMMITTEE_INDEX ,
16+ FINALIZED_ROOT_INDEX , NEXT_SYNC_COMMITTEE_INDEX ,
1617} ;
1718use types:: non_zero_usize:: new_non_zero_usize;
1819use types:: {
19- BeaconBlockRef , BeaconState , ChainSpec , EthSpec , ForkName , Hash256 , LightClientFinalityUpdate ,
20- LightClientOptimisticUpdate , LightClientUpdate , Slot , SyncAggregate , SyncCommittee ,
20+ BeaconBlockRef , BeaconState , ChainSpec , Checkpoint , EthSpec , ForkName , Hash256 ,
21+ LightClientBootstrap , LightClientFinalityUpdate , LightClientOptimisticUpdate ,
22+ LightClientUpdate , Slot , SyncAggregate , SyncCommittee ,
2123} ;
2224
2325/// A prev block cache miss requires to re-generate the state of the post-parent block. Items in the
@@ -28,7 +30,6 @@ const PREV_BLOCK_CACHE_SIZE: NonZeroUsize = new_non_zero_usize(32);
2830/// This cache computes light client messages ahead of time, required to satisfy p2p and API
2931/// requests. These messages include proofs on historical states, so on-demand computation is
3032/// expensive.
31- ///
3233pub struct LightClientServerCache < T : BeaconChainTypes > {
3334 /// Tracks a single global latest finality update out of all imported blocks.
3435 ///
@@ -41,6 +42,8 @@ pub struct LightClientServerCache<T: BeaconChainTypes> {
4142 latest_optimistic_update : RwLock < Option < LightClientOptimisticUpdate < T :: EthSpec > > > ,
4243 /// Caches the most recent light client update
4344 latest_light_client_update : RwLock < Option < LightClientUpdate < T :: EthSpec > > > ,
45+ /// Caches the current sync committee,
46+ latest_written_current_sync_committee : RwLock < Option < Arc < SyncCommittee < T :: EthSpec > > > > ,
4447 /// Caches state proofs by block root
4548 prev_block_cache : Mutex < lru:: LruCache < Hash256 , LightClientCachedData < T :: EthSpec > > > ,
4649}
@@ -51,6 +54,7 @@ impl<T: BeaconChainTypes> LightClientServerCache<T> {
5154 latest_finality_update : None . into ( ) ,
5255 latest_optimistic_update : None . into ( ) ,
5356 latest_light_client_update : None . into ( ) ,
57+ latest_written_current_sync_committee : None . into ( ) ,
5458 prev_block_cache : lru:: LruCache :: new ( PREV_BLOCK_CACHE_SIZE ) . into ( ) ,
5559 }
5660 }
@@ -96,6 +100,10 @@ impl<T: BeaconChainTypes> LightClientServerCache<T> {
96100 let signature_slot = block_slot;
97101 let attested_block_root = block_parent_root;
98102
103+ let sync_period = block_slot
104+ . epoch ( T :: EthSpec :: slots_per_epoch ( ) )
105+ . sync_committee_period ( chain_spec) ?;
106+
99107 let attested_block = store. get_blinded_block ( attested_block_root) ?. ok_or (
100108 BeaconChainError :: DBInconsistent ( format ! (
101109 "Block not available {:?}" ,
@@ -110,6 +118,18 @@ impl<T: BeaconChainTypes> LightClientServerCache<T> {
110118 attested_block. slot ( ) ,
111119 ) ?;
112120
121+ let finalized_period = cached_parts
122+ . finalized_checkpoint
123+ . epoch
124+ . sync_committee_period ( chain_spec) ?;
125+
126+ store. store_sync_committee_branch (
127+ attested_block. message ( ) . tree_hash_root ( ) ,
128+ & cached_parts. current_sync_committee_branch ,
129+ ) ?;
130+
131+ self . store_current_sync_committee ( & store, & cached_parts, sync_period, finalized_period) ?;
132+
113133 let attested_slot = attested_block. slot ( ) ;
114134
115135 let maybe_finalized_block = store. get_blinded_block ( & cached_parts. finalized_block_root ) ?;
@@ -178,57 +198,57 @@ impl<T: BeaconChainTypes> LightClientServerCache<T> {
178198
179199 // Spec: Full nodes SHOULD provide the best derivable LightClientUpdate (according to is_better_update)
180200 // for each sync committee period
181- let prev_light_client_update = match & self . latest_light_client_update . read ( ) . clone ( ) {
182- Some ( prev_light_client_update) => Some ( prev_light_client_update. clone ( ) ) ,
183- None => self . get_light_client_update ( & store, sync_period, chain_spec) ?,
184- } ;
201+ let prev_light_client_update =
202+ self . get_light_client_update ( & store, sync_period, chain_spec) ?;
185203
186204 let should_persist_light_client_update =
187205 if let Some ( prev_light_client_update) = prev_light_client_update {
188- let prev_sync_period = prev_light_client_update
189- . signature_slot ( )
190- . epoch ( T :: EthSpec :: slots_per_epoch ( ) )
191- . sync_committee_period ( chain_spec) ?;
192-
193- if sync_period != prev_sync_period {
194- true
195- } else {
196- prev_light_client_update
197- . is_better_light_client_update ( & new_light_client_update, chain_spec) ?
198- }
206+ prev_light_client_update
207+ . is_better_light_client_update ( & new_light_client_update, chain_spec) ?
199208 } else {
200209 true
201210 } ;
202211
203212 if should_persist_light_client_update {
204- self . store_light_client_update ( & store, sync_period, & new_light_client_update) ?;
213+ store. store_light_client_update ( sync_period, & new_light_client_update) ?;
214+ * self . latest_light_client_update . write ( ) = Some ( new_light_client_update) ;
205215 }
206216
207217 Ok ( ( ) )
208218 }
209219
210- fn store_light_client_update (
220+ fn store_current_sync_committee (
211221 & self ,
212222 store : & BeaconStore < T > ,
223+ cached_parts : & LightClientCachedData < T :: EthSpec > ,
213224 sync_committee_period : u64 ,
214- light_client_update : & LightClientUpdate < T :: EthSpec > ,
225+ finalized_period : u64 ,
215226 ) -> Result < ( ) , BeaconChainError > {
216- let column = DBColumn :: LightClientUpdate ;
217-
218- store . hot_db . put_bytes (
219- column . into ( ) ,
220- & sync_committee_period . to_le_bytes ( ) ,
221- & light_client_update . as_ssz_bytes ( ) ,
222- ) ? ;
227+ if let Some ( latest_sync_committee ) =
228+ self . latest_written_current_sync_committee . read ( ) . clone ( )
229+ {
230+ if latest_sync_committee == cached_parts . current_sync_committee {
231+ return Ok ( ( ) ) ;
232+ }
233+ } ;
223234
224- * self . latest_light_client_update . write ( ) = Some ( light_client_update. clone ( ) ) ;
235+ if finalized_period + 1 >= sync_committee_period {
236+ store. store_sync_committee (
237+ sync_committee_period,
238+ & cached_parts. current_sync_committee ,
239+ ) ?;
240+ * self . latest_written_current_sync_committee . write ( ) =
241+ Some ( cached_parts. current_sync_committee . clone ( ) ) ;
242+ }
225243
226244 Ok ( ( ) )
227245 }
228246
229- // Used to fetch the most recently persisted "best" light client update.
230- // Should not be used outside the light client server, as it also caches the fetched
231- // light client update.
247+ /// Used to fetch the most recently persisted light client update for the given `sync_committee_period`.
248+ /// It first checks the `latest_light_client_update` cache before querying the db.
249+ ///
250+ /// Note: Should not be used outside the light client server, as it also caches the fetched
251+ /// light client update.
232252 fn get_light_client_update (
233253 & self ,
234254 store : & BeaconStore < T > ,
@@ -245,21 +265,7 @@ impl<T: BeaconChainTypes> LightClientServerCache<T> {
245265 }
246266 }
247267
248- let column = DBColumn :: LightClientUpdate ;
249- let res = store
250- . hot_db
251- . get_bytes ( column. into ( ) , & sync_committee_period. to_le_bytes ( ) ) ?;
252-
253- if let Some ( light_client_update_bytes) = res {
254- let epoch = sync_committee_period
255- . safe_mul ( chain_spec. epochs_per_sync_committee_period . into ( ) ) ?;
256-
257- let fork_name = chain_spec. fork_name_at_epoch ( epoch. into ( ) ) ;
258-
259- let light_client_update =
260- LightClientUpdate :: from_ssz_bytes ( & light_client_update_bytes, & fork_name)
261- . map_err ( store:: errors:: Error :: SszDecodeError ) ?;
262-
268+ if let Some ( light_client_update) = store. get_light_client_update ( sync_committee_period) ? {
263269 * self . latest_light_client_update . write ( ) = Some ( light_client_update. clone ( ) ) ;
264270 return Ok ( Some ( light_client_update) ) ;
265271 }
@@ -340,6 +346,65 @@ impl<T: BeaconChainTypes> LightClientServerCache<T> {
340346 pub fn get_latest_optimistic_update ( & self ) -> Option < LightClientOptimisticUpdate < T :: EthSpec > > {
341347 self . latest_optimistic_update . read ( ) . clone ( )
342348 }
349+
350+ /// Fetches a light client bootstrap for a given finalized checkpoint `block_root`. We eagerly persist
351+ /// `sync_committee_branch and `sync_committee` to allow for a more efficient bootstrap construction.
352+ ///
353+ /// Note: It should be the case that a `sync_committee_branch` and `sync_committee` exist in the db
354+ /// for a finalized checkpoint block root. However, we currently have no backfill mechanism for these values.
355+ /// Therefore, `sync_committee_branch` and `sync_committee` are only persisted while a node is synced.
356+ #[ allow( clippy:: type_complexity) ]
357+ pub fn get_light_client_bootstrap (
358+ & self ,
359+ store : & BeaconStore < T > ,
360+ block_root : & Hash256 ,
361+ finalized_period : u64 ,
362+ chain_spec : & ChainSpec ,
363+ ) -> Result < Option < ( LightClientBootstrap < T :: EthSpec > , ForkName ) > , BeaconChainError > {
364+ let Some ( block) = store. get_blinded_block ( block_root) ? else {
365+ return Err ( BeaconChainError :: LightClientBootstrapError ( format ! (
366+ "Block root {block_root} not found"
367+ ) ) ) ;
368+ } ;
369+
370+ let ( _, slot) = ( block. state_root ( ) , block. slot ( ) ) ;
371+
372+ let fork_name = chain_spec. fork_name_at_slot :: < T :: EthSpec > ( slot) ;
373+
374+ let sync_committee_period = block
375+ . slot ( )
376+ . epoch ( T :: EthSpec :: slots_per_epoch ( ) )
377+ . sync_committee_period ( chain_spec) ?;
378+
379+ let Some ( current_sync_committee_branch) = store. get_sync_committee_branch ( block_root) ?
380+ else {
381+ return Err ( BeaconChainError :: LightClientBootstrapError ( format ! (
382+ "Sync committee branch for block root {:?} not found" ,
383+ block_root
384+ ) ) ) ;
385+ } ;
386+
387+ if sync_committee_period > finalized_period {
388+ return Err ( BeaconChainError :: LightClientBootstrapError (
389+ format ! ( "The blocks sync committee period {sync_committee_period} is greater than the current finalized period {finalized_period}" ) ,
390+ ) ) ;
391+ }
392+
393+ let Some ( current_sync_committee) = store. get_sync_committee ( sync_committee_period) ? else {
394+ return Err ( BeaconChainError :: LightClientBootstrapError ( format ! (
395+ "Sync committee for period {sync_committee_period} not found"
396+ ) ) ) ;
397+ } ;
398+
399+ let light_client_bootstrap = LightClientBootstrap :: new (
400+ & block,
401+ Arc :: new ( current_sync_committee) ,
402+ current_sync_committee_branch,
403+ chain_spec,
404+ ) ?;
405+
406+ Ok ( Some ( ( light_client_bootstrap, fork_name) ) )
407+ }
343408}
344409
345410impl < T : BeaconChainTypes > Default for LightClientServerCache < T > {
@@ -350,23 +415,32 @@ impl<T: BeaconChainTypes> Default for LightClientServerCache<T> {
350415
351416type FinalityBranch = FixedVector < Hash256 , FinalizedRootProofLen > ;
352417type NextSyncCommitteeBranch = FixedVector < Hash256 , NextSyncCommitteeProofLen > ;
418+ type CurrentSyncCommitteeBranch = FixedVector < Hash256 , CurrentSyncCommitteeProofLen > ;
353419
354420#[ derive( Clone ) ]
355421struct LightClientCachedData < E : EthSpec > {
422+ finalized_checkpoint : Checkpoint ,
356423 finality_branch : FinalityBranch ,
357424 next_sync_committee_branch : NextSyncCommitteeBranch ,
425+ current_sync_committee_branch : CurrentSyncCommitteeBranch ,
358426 next_sync_committee : Arc < SyncCommittee < E > > ,
427+ current_sync_committee : Arc < SyncCommittee < E > > ,
359428 finalized_block_root : Hash256 ,
360429}
361430
362431impl < E : EthSpec > LightClientCachedData < E > {
363432 fn from_state ( state : & mut BeaconState < E > ) -> Result < Self , BeaconChainError > {
364433 Ok ( Self {
434+ finalized_checkpoint : state. finalized_checkpoint ( ) ,
365435 finality_branch : state. compute_merkle_proof ( FINALIZED_ROOT_INDEX ) ?. into ( ) ,
366436 next_sync_committee : state. next_sync_committee ( ) ?. clone ( ) ,
437+ current_sync_committee : state. current_sync_committee ( ) ?. clone ( ) ,
367438 next_sync_committee_branch : state
368439 . compute_merkle_proof ( NEXT_SYNC_COMMITTEE_INDEX ) ?
369440 . into ( ) ,
441+ current_sync_committee_branch : state
442+ . compute_merkle_proof ( CURRENT_SYNC_COMMITTEE_INDEX ) ?
443+ . into ( ) ,
370444 finalized_block_root : state. finalized_checkpoint ( ) . root ,
371445 } )
372446 }
0 commit comments