@@ -34,7 +34,7 @@ use crate::envelope_times_cache::EnvelopeTimesCache;
3434use crate :: errors:: { BeaconChainError as Error , BlockProductionError } ;
3535use crate :: events:: ServerSentEventHandler ;
3636use crate :: execution_payload:: { NotifyExecutionLayer , PreparePayloadHandle , get_execution_payload} ;
37- use crate :: execution_proof_verification:: ObservedExecutionProofs ;
37+ use crate :: execution_proof_verification:: { GossipVerifiedExecutionProof , ObservedExecutionProofs } ;
3838use crate :: fork_choice_signal:: { ForkChoiceSignalRx , ForkChoiceSignalTx } ;
3939use crate :: graffiti_calculator:: { GraffitiCalculator , GraffitiSettings } ;
4040use crate :: light_client_finality_update_verification:: {
@@ -58,6 +58,7 @@ use crate::observed_attesters::{
5858} ;
5959use crate :: observed_block_producers:: ObservedBlockProducers ;
6060use crate :: observed_data_sidecars:: ObservedDataSidecars ;
61+ use crate :: observed_execution_payloads:: ObservedExecutionPayloads ;
6162use crate :: observed_operations:: { ObservationOutcome , ObservedOperations } ;
6263use crate :: observed_slashable:: ObservedSlashable ;
6364use crate :: partial_data_column_assembler:: PartialMergeResult ;
@@ -123,7 +124,9 @@ use slasher::Slasher;
123124use slot_clock:: SlotClock ;
124125use ssz:: Encode ;
125126use state_processing:: {
126- BlockSignatureStrategy , ConsensusContext , SigVerifiedOp , VerifyBlockRoot , VerifyOperation ,
127+ BlockSignatureStrategy , ConsensusContext , GloasVerificationContext , SigVerifiedOp ,
128+ VerifyBlockRoot , VerifyOperation ,
129+ builder_deposits_cache:: OnboardBuildersCache ,
127130 common:: get_attesting_indices_from_state,
128131 epoch_cache:: initialize_epoch_cache,
129132 per_block_processing,
@@ -441,6 +444,8 @@ pub struct BeaconChain<T: BeaconChainTypes> {
441444 pub observed_slashable : RwLock < ObservedSlashable < T :: EthSpec > > ,
442445 /// Maintains a record of execution proofs seen over the gossip network.
443446 pub observed_execution_proofs : RwLock < ObservedExecutionProofs > ,
447+ /// Maintains the gas limit of execution payloads seen through gossip or trusted imports.
448+ pub observed_execution_payloads : ObservedExecutionPayloads ,
444449 /// Cache of pending execution payload envelopes for local block building.
445450 /// Envelopes are stored here during block production and eventually published.
446451 pub pending_payload_envelopes : RwLock < PendingPayloadEnvelopes < T :: EthSpec > > ,
@@ -522,6 +527,10 @@ pub struct BeaconChain<T: BeaconChainTypes> {
522527 pub pending_payload_cache : Arc < PendingPayloadCache < T > > ,
523528 /// The KZG trusted setup used by this chain.
524529 pub kzg : Arc < Kzg > ,
530+ /// Pre-verifies pending deposit signatures ahead of the Gloas fork transition.
531+ /// Only present when gloas is scheduled and the chain had not yet transitioned to gloas
532+ /// at startup, so nodes started post-fork skip the cache's allocation entirely.
533+ pub builder_onboarding_cache : Option < Arc < OnboardBuildersCache > > ,
525534 /// RNG instance used by the chain. Currently used for shuffling column sidecars in block publishing.
526535 pub rng : Arc < Mutex < Box < dyn RngCore + Send > > > ,
527536}
@@ -1527,7 +1536,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
15271536 while state. slot ( ) < slot {
15281537 // Note: supplying some `state_root` when it is known would be a cheap and easy
15291538 // optimization.
1530- match per_slot_processing ( & mut state, skip_state_root, & self . spec ) {
1539+ match per_slot_processing (
1540+ & mut state,
1541+ skip_state_root,
1542+ GloasVerificationContext :: from_cache (
1543+ self . builder_onboarding_cache . as_deref ( ) ,
1544+ ) ,
1545+ & self . spec ,
1546+ ) {
15311547 Ok ( _) => ( ) ,
15321548 Err ( e) => {
15331549 warn ! (
@@ -2133,6 +2149,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
21332149 & mut state,
21342150 Some ( advanced_state_root) ,
21352151 request_epoch. start_slot ( T :: EthSpec :: slots_per_epoch ( ) ) ,
2152+ self . builder_onboarding_cache . as_deref ( ) ,
21362153 & self . spec ,
21372154 )
21382155 . map_err ( Error :: StateAdvanceError ) ?;
@@ -4203,6 +4220,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
42034220 }
42044221 }
42054222
4223+ /// Caches an execution proof, importing the payload envelope if that was the last piece.
4224+ pub async fn check_execution_proof_availability_and_import (
4225+ self : & Arc < Self > ,
4226+ verified_proof : GossipVerifiedExecutionProof ,
4227+ ) -> Result < AvailabilityProcessingStatus , BlockError > {
4228+ let GossipVerifiedExecutionProof { proof, block_slot } = verified_proof;
4229+ let availability = self
4230+ . pending_payload_cache
4231+ . put_execution_proof ( proof)
4232+ . map_err ( BlockError :: from) ?;
4233+ self . process_payload_envelope_availability ( block_slot, availability, || Ok ( ( ) ) )
4234+ . await
4235+ }
4236+
42064237 fn check_data_column_sidecar_header_signature_and_slashability < ' a > (
42074238 self : & Arc < Self > ,
42084239 block_root : Hash256 ,
@@ -4626,14 +4657,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
46264657
46274658 if let Err ( e) = self . store . do_atomically_with_block_and_blobs_cache ( ops) {
46284659 error ! (
4629- msg = "Restoring fork choice from disk" ,
46304660 error = ?e,
46314661 "Database write failed!"
46324662 ) ;
4633- return Err ( self
4634- . handle_import_block_db_write_error ( fork_choice)
4635- . err ( )
4636- . unwrap_or ( e. into ( ) ) ) ;
4663+ self . handle_import_block_db_write_error ( fork_choice, block_root) ;
4664+ return Err ( e. into ( ) ) ;
46374665 }
46384666
46394667 drop ( db_span) ;
@@ -4642,6 +4670,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
46424670 // This prevents inconsistency between the two at the expense of concurrency.
46434671 drop ( fork_choice) ;
46444672
4673+ // Keep pre-Gloas payloads available across a live transition to Gloas.
4674+ if self . spec . is_gloas_scheduled ( )
4675+ && !block. fork_name_unchecked ( ) . gloas_enabled ( )
4676+ && let Ok ( payload) = block. body ( ) . execution_payload ( )
4677+ && payload. block_hash ( ) != ExecutionBlockHash :: zero ( )
4678+ {
4679+ self . observed_execution_payloads
4680+ . insert ( payload. block_hash ( ) , payload. gas_limit ( ) ) ;
4681+ }
4682+
46454683 // We're declaring the block "imported" at this point, since fork choice and the DB know
46464684 // about it.
46474685 let block_time_imported = self . slot_clock . now_duration ( ) . unwrap_or ( Duration :: MAX ) ;
@@ -4676,39 +4714,57 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
46764714 current_slot,
46774715 ) ;
46784716
4717+ // Pre-verify the signatures of any deposits this block added to the `pending_deposits`
4718+ // queue, so that builder onboarding at the gloas fork transition is a cache lookup.
4719+ // Post-gloas the fork transition has already happened and the cache is no longer needed.
4720+ if !state. fork_name_unchecked ( ) . gloas_enabled ( )
4721+ && let Some ( builder_onboarding_cache) = & self . builder_onboarding_cache
4722+ {
4723+ let cache = builder_onboarding_cache. clone ( ) ;
4724+ let spec = self . spec . clone ( ) ;
4725+ // Using the rayon pool here since `add_new_pending_deposits` uses rayon threads to
4726+ // perform the signature verification in batches. We have until the fork transition
4727+ // for the cache to be populated, so use the low priority pool.
4728+ self . task_executor . clone ( ) . spawn_blocking_with_rayon (
4729+ move || cache. add_new_pending_deposits :: < T :: EthSpec > ( & state, & spec) ,
4730+ RayonPoolType :: LowPriority ,
4731+ "pre_verify_pending_deposits" ,
4732+ ) ;
4733+ }
4734+
46794735 Ok ( block_root)
46804736 }
46814737
4738+ /// Handle a database write failure during block import, which causes fork choice
4739+ /// to contain a block that the store does not.
4740+ ///
4741+ /// Poison fork choice so the diverged version is never persisted, and shut down the
4742+ /// node. On restart, the normal startup procedure loads the last consistent fork
4743+ /// choice from disk.
46824744 fn handle_import_block_db_write_error (
46834745 & self ,
46844746 // We don't actually need this value, however it's always present when we call this function
46854747 // and it needs to be dropped to prevent a dead-lock. Requiring it to be passed here is
46864748 // defensive programming.
46874749 fork_choice_write_lock : ForkChoiceWriteGuard < T > ,
4688- ) -> Result < ( ) , BlockError > {
4750+ block_root : Hash256 ,
4751+ ) {
4752+ drop ( fork_choice_write_lock) ;
4753+
46894754 // Clear the early attester cache to prevent attestations which we would later be unable
46904755 // to verify due to the failure.
46914756 self . early_attester_cache . clear ( ) ;
46924757
4693- // Since the write failed, try to revert the canonical head back to what was stored
4694- // in the database. This attempts to prevent inconsistency between the database and
4695- // fork choice.
4696- if let Err ( e) = self . canonical_head . restore_from_store (
4697- fork_choice_write_lock,
4698- ResetPayloadStatuses :: always_reset_conditionally (
4699- self . config . always_reset_payload_statuses ,
4700- ) ,
4701- & self . store ,
4702- & self . spec ,
4703- ) {
4704- crit ! (
4705- error = ?e,
4706- warning = "The database is likely corrupt now, consider --purge-db" ,
4707- "No stored fork choice found to restore from"
4708- ) ;
4709- Err ( BlockError :: BeaconChainError ( Box :: new ( e) ) )
4710- } else {
4711- Ok ( ( ) )
4758+ self . canonical_head . poison_fork_choice ( ) ;
4759+ crit ! (
4760+ ?block_root,
4761+ advice = "restart the node to recover the last consistent fork choice from disk" ,
4762+ "Shutting down due to database write failure"
4763+ ) ;
4764+ if let Err ( e) = self . shutdown_sender ( ) . try_send ( ShutdownReason :: Failure (
4765+ "Database write failure during block import" ,
4766+ ) ) {
4767+ crit ! ( error = ?e, "Failed to send shutdown signal" ) ;
47124768 }
47134769 }
47144770
@@ -5286,6 +5342,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
52865342 & mut advanced_state,
52875343 Some ( unadvanced_state_root) ,
52885344 proposal_slot,
5345+ self . builder_onboarding_cache . as_deref ( ) ,
52895346 & self . spec ,
52905347 ) ?;
52915348
@@ -5703,7 +5760,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
57035760 let slot_timer = metrics:: start_timer ( & metrics:: BLOCK_PRODUCTION_SLOT_PROCESS_TIMES ) ;
57045761
57055762 // Ensure the state has performed a complete transition into the required slot.
5706- complete_state_advance ( & mut state, state_root_opt, produce_at_slot, & self . spec ) ?;
5763+ complete_state_advance (
5764+ & mut state,
5765+ state_root_opt,
5766+ produce_at_slot,
5767+ self . builder_onboarding_cache . as_deref ( ) ,
5768+ & self . spec ,
5769+ ) ?;
57075770
57085771 drop ( slot_timer) ;
57095772
@@ -7195,6 +7258,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
71957258 proposal_epoch,
71967259 accessor,
71977260 state_provider,
7261+ self . builder_onboarding_cache . as_deref ( ) ,
71987262 & self . spec ,
71997263 )
72007264 }
@@ -7240,6 +7304,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
72407304 & self . canonical_head ,
72417305 & self . shuffling_cache ,
72427306 & self . store ,
7307+ self . builder_onboarding_cache . as_deref ( ) ,
72437308 & self . spec ,
72447309 head_block_root,
72457310 shuffling_epoch,
@@ -7780,10 +7845,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
77807845
77817846impl < T : BeaconChainTypes > Drop for BeaconChain < T > {
77827847 fn drop ( & mut self ) {
7848+ if self . canonical_head . fork_choice_poisoned ( ) {
7849+ warn ! ( "Skipping persistence on drop: fork choice is poisoned" ) ;
7850+ return ;
7851+ }
7852+
77837853 let drop = || -> Result < ( ) , Error > {
7784- self . persist_fork_choice ( ) ?;
77857854 self . persist_op_pool ( ) ?;
7786- self . persist_custody_context ( )
7855+ self . persist_custody_context ( ) ?;
7856+ self . persist_fork_choice ( )
77877857 } ;
77887858
77897859 if let Err ( e) = drop ( ) {
0 commit comments