@@ -536,87 +536,32 @@ where
536536 // null}` if the expected and the actual arrays don't match.
537537 //
538538 // This validation **MUST** be instantly run in all cases even during active sync process.
539- let parent_hash = payload. parent_hash ( ) ;
540539
541540 let num_hash = payload. num_hash ( ) ;
542541 let engine_event = ConsensusEngineEvent :: BlockReceived ( num_hash) ;
543542 self . emit_event ( EngineApiEvent :: BeaconConsensus ( engine_event) ) ;
544543
545544 let block_hash = num_hash. hash ;
546- let mut lowest_buffered_ancestor = self . lowest_buffered_ancestor_or ( block_hash) ;
547- if lowest_buffered_ancestor == block_hash {
548- lowest_buffered_ancestor = parent_hash;
549- }
550545
551- // now check if the block has an invalid ancestor
552- if let Some ( invalid) = self . state . invalid_headers . get ( & lowest_buffered_ancestor) {
553- // Here we might have 2 cases
554- // 1. the block is well formed and indeed links to an invalid header, meaning we should
555- // remember it as invalid
556- // 2. the block is not well formed (i.e block hash is incorrect), and we should just
557- // return an error and forget it
558- let block = match self . payload_validator . ensure_well_formed_payload ( payload) {
559- Ok ( block) => block,
560- Err ( error) => {
561- let status = self . on_new_payload_error ( error, parent_hash) ?;
562- return Ok ( TreeOutcome :: new ( status) )
563- }
564- } ;
565-
566- let status = self . on_invalid_new_payload ( block. into_sealed_block ( ) , invalid) ?;
567- return Ok ( TreeOutcome :: new ( status) )
546+ // Check for invalid ancestors
547+ if let Some ( invalid) = self . find_invalid_ancestor ( & payload) {
548+ let status = self . handle_invalid_ancestor_payload ( payload, invalid) ?;
549+ return Ok ( TreeOutcome :: new ( status) ) ;
568550 }
551+
569552 // record pre-execution phase duration
570553 self . metrics . block_validation . record_payload_validation ( start. elapsed ( ) . as_secs_f64 ( ) ) ;
571554
572555 let status = if self . backfill_sync_state . is_idle ( ) {
573- let mut latest_valid_hash = None ;
574- match self . insert_payload ( payload) {
575- Ok ( status) => {
576- let status = match status {
577- InsertPayloadOk :: Inserted ( BlockStatus :: Valid ) => {
578- latest_valid_hash = Some ( block_hash) ;
579- self . try_connect_buffered_blocks ( num_hash) ?;
580- PayloadStatusEnum :: Valid
581- }
582- InsertPayloadOk :: AlreadySeen ( BlockStatus :: Valid ) => {
583- latest_valid_hash = Some ( block_hash) ;
584- PayloadStatusEnum :: Valid
585- }
586- InsertPayloadOk :: Inserted ( BlockStatus :: Disconnected { .. } ) |
587- InsertPayloadOk :: AlreadySeen ( BlockStatus :: Disconnected { .. } ) => {
588- // not known to be invalid, but we don't know anything else
589- PayloadStatusEnum :: Syncing
590- }
591- } ;
592-
593- PayloadStatus :: new ( status, latest_valid_hash)
594- }
595- Err ( error) => match error {
596- InsertPayloadError :: Block ( error) => self . on_insert_block_error ( error) ?,
597- InsertPayloadError :: Payload ( error) => {
598- self . on_new_payload_error ( error, parent_hash) ?
599- }
600- } ,
601- }
556+ self . try_insert_payload ( payload) ?
602557 } else {
603- match self . payload_validator . ensure_well_formed_payload ( payload) {
604- // if the block is well-formed, buffer it for later
605- Ok ( block) => {
606- if let Err ( error) = self . buffer_block ( block) {
607- self . on_insert_block_error ( error) ?
608- } else {
609- PayloadStatus :: from_status ( PayloadStatusEnum :: Syncing )
610- }
611- }
612- Err ( error) => self . on_new_payload_error ( error, parent_hash) ?,
613- }
558+ self . try_buffer_payload ( payload) ?
614559 } ;
615560
616561 let mut outcome = TreeOutcome :: new ( status) ;
617562 // if the block is valid and it is the current sync target head, make it canonical
618563 if outcome. outcome . is_valid ( ) && self . is_sync_target_head ( block_hash) {
619- // but only if it isn't already the canonical head
564+ // Only create the canonical event if this block isn't already the canonical head
620565 if self . state . tree_state . canonical_block_hash ( ) != block_hash {
621566 outcome = outcome. with_event ( TreeEvent :: TreeAction ( TreeAction :: MakeCanonical {
622567 sync_target_head : block_hash,
@@ -630,6 +575,78 @@ where
630575 Ok ( outcome)
631576 }
632577
578+ /// Processes a payload during normal sync operation.
579+ ///
580+ /// Returns:
581+ /// - `Valid`: Payload successfully validated and inserted
582+ /// - `Syncing`: Parent missing, payload buffered for later
583+ /// - Error status: Payload is invalid
584+ fn try_insert_payload (
585+ & mut self ,
586+ payload : T :: ExecutionData ,
587+ ) -> Result < PayloadStatus , InsertBlockFatalError > {
588+ let block_hash = payload. block_hash ( ) ;
589+ let num_hash = payload. num_hash ( ) ;
590+ let parent_hash = payload. parent_hash ( ) ;
591+ let mut latest_valid_hash = None ;
592+
593+ match self . insert_payload ( payload) {
594+ Ok ( status) => {
595+ let status = match status {
596+ InsertPayloadOk :: Inserted ( BlockStatus :: Valid ) => {
597+ latest_valid_hash = Some ( block_hash) ;
598+ self . try_connect_buffered_blocks ( num_hash) ?;
599+ PayloadStatusEnum :: Valid
600+ }
601+ InsertPayloadOk :: AlreadySeen ( BlockStatus :: Valid ) => {
602+ latest_valid_hash = Some ( block_hash) ;
603+ PayloadStatusEnum :: Valid
604+ }
605+ InsertPayloadOk :: Inserted ( BlockStatus :: Disconnected { .. } ) |
606+ InsertPayloadOk :: AlreadySeen ( BlockStatus :: Disconnected { .. } ) => {
607+ // not known to be invalid, but we don't know anything else
608+ PayloadStatusEnum :: Syncing
609+ }
610+ } ;
611+
612+ Ok ( PayloadStatus :: new ( status, latest_valid_hash) )
613+ }
614+ Err ( error) => match error {
615+ InsertPayloadError :: Block ( error) => Ok ( self . on_insert_block_error ( error) ?) ,
616+ InsertPayloadError :: Payload ( error) => {
617+ Ok ( self . on_new_payload_error ( error, parent_hash) ?)
618+ }
619+ } ,
620+ }
621+ }
622+
623+ /// Stores a payload for later processing during backfill sync.
624+ ///
625+ /// During backfill, the node lacks the state needed to validate payloads,
626+ /// so they are buffered (stored in memory) until their parent blocks are synced.
627+ ///
628+ /// Returns:
629+ /// - `Syncing`: Payload successfully buffered
630+ /// - Error status: Payload is malformed or invalid
631+ fn try_buffer_payload (
632+ & mut self ,
633+ payload : T :: ExecutionData ,
634+ ) -> Result < PayloadStatus , InsertBlockFatalError > {
635+ let parent_hash = payload. parent_hash ( ) ;
636+
637+ match self . payload_validator . ensure_well_formed_payload ( payload) {
638+ // if the block is well-formed, buffer it for later
639+ Ok ( block) => {
640+ if let Err ( error) = self . buffer_block ( block) {
641+ Ok ( self . on_insert_block_error ( error) ?)
642+ } else {
643+ Ok ( PayloadStatus :: from_status ( PayloadStatusEnum :: Syncing ) )
644+ }
645+ }
646+ Err ( error) => Ok ( self . on_new_payload_error ( error, parent_hash) ?) ,
647+ }
648+ }
649+
633650 /// Returns the new chain for the given head.
634651 ///
635652 /// This also handles reorgs.
@@ -1856,6 +1873,57 @@ where
18561873 Ok ( status)
18571874 }
18581875
1876+ /// Finds any invalid ancestor for the given payload.
1877+ ///
1878+ /// This function walks up the chain of buffered ancestors from the payload's block
1879+ /// hash and checks if any ancestor is marked as invalid in the tree state.
1880+ ///
1881+ /// The check works by:
1882+ /// 1. Finding the lowest buffered ancestor for the given block hash
1883+ /// 2. If the ancestor is the same as the block hash itself, using the parent hash instead
1884+ /// 3. Checking if this ancestor is in the `invalid_headers` map
1885+ ///
1886+ /// Returns the invalid ancestor block info if found, or None if no invalid ancestor exists.
1887+ fn find_invalid_ancestor ( & mut self , payload : & T :: ExecutionData ) -> Option < BlockWithParent > {
1888+ let parent_hash = payload. parent_hash ( ) ;
1889+ let block_hash = payload. block_hash ( ) ;
1890+ let mut lowest_buffered_ancestor = self . lowest_buffered_ancestor_or ( block_hash) ;
1891+ if lowest_buffered_ancestor == block_hash {
1892+ lowest_buffered_ancestor = parent_hash;
1893+ }
1894+
1895+ // Check if the block has an invalid ancestor
1896+ self . state . invalid_headers . get ( & lowest_buffered_ancestor)
1897+ }
1898+
1899+ /// Handles a payload that has an invalid ancestor.
1900+ ///
1901+ /// This function validates the payload and processes it according to whether it's
1902+ /// well-formed or malformed:
1903+ /// 1. **Well-formed payload**: The payload is marked as invalid since it descends from a
1904+ /// known-bad block, which violates consensus rules
1905+ /// 2. **Malformed payload**: Returns an appropriate error status since the payload cannot be
1906+ /// validated due to its own structural issues
1907+ fn handle_invalid_ancestor_payload (
1908+ & mut self ,
1909+ payload : T :: ExecutionData ,
1910+ invalid : BlockWithParent ,
1911+ ) -> Result < PayloadStatus , InsertBlockFatalError > {
1912+ let parent_hash = payload. parent_hash ( ) ;
1913+
1914+ // Here we might have 2 cases
1915+ // 1. the block is well formed and indeed links to an invalid header, meaning we should
1916+ // remember it as invalid
1917+ // 2. the block is not well formed (i.e block hash is incorrect), and we should just return
1918+ // an error and forget it
1919+ let block = match self . payload_validator . ensure_well_formed_payload ( payload) {
1920+ Ok ( block) => block,
1921+ Err ( error) => return Ok ( self . on_new_payload_error ( error, parent_hash) ?) ,
1922+ } ;
1923+
1924+ Ok ( self . on_invalid_new_payload ( block. into_sealed_block ( ) , invalid) ?)
1925+ }
1926+
18591927 /// Checks if the given `head` points to an invalid header, which requires a specific response
18601928 /// to a forkchoice update.
18611929 fn check_invalid_ancestor ( & mut self , head : B256 ) -> ProviderResult < Option < PayloadStatus > > {
@@ -2264,6 +2332,14 @@ where
22642332 Ok ( None )
22652333 }
22662334
2335+ /// Inserts a payload into the tree and executes it.
2336+ ///
2337+ /// This function validates the payload's basic structure, then executes it using the
2338+ /// payload validator. The execution includes running all transactions in the payload
2339+ /// and validating the resulting state transitions.
2340+ ///
2341+ /// Returns `InsertPayloadOk` if the payload was successfully inserted and executed,
2342+ /// or `InsertPayloadError` if validation or execution failed.
22672343 fn insert_payload (
22682344 & mut self ,
22692345 payload : T :: ExecutionData ,
@@ -2288,6 +2364,22 @@ where
22882364 )
22892365 }
22902366
2367+ /// Inserts a block or payload into the blockchain tree with full execution.
2368+ ///
2369+ /// This is a generic function that handles both blocks and payloads by accepting
2370+ /// a block identifier, input data, and execution/validation functions. It performs
2371+ /// comprehensive checks and execution:
2372+ ///
2373+ /// - Validates that the block doesn't already exist in the tree
2374+ /// - Ensures parent state is available, buffering if necessary
2375+ /// - Executes the block/payload using the provided execute function
2376+ /// - Handles both canonical and fork chain insertions
2377+ /// - Updates pending block state when appropriate
2378+ /// - Emits consensus engine events and records metrics
2379+ ///
2380+ /// Returns `InsertPayloadOk::Inserted(BlockStatus::Valid)` on successful execution,
2381+ /// `InsertPayloadOk::AlreadySeen` if the block already exists, or
2382+ /// `InsertPayloadOk::Inserted(BlockStatus::Disconnected)` if parent state is missing.
22912383 fn insert_block_or_payload < Input , Err > (
22922384 & mut self ,
22932385 block_id : BlockWithParent ,
0 commit comments