@@ -3,6 +3,7 @@ package executing
33import (
44 "bytes"
55 "context"
6+ "encoding/hex"
67 "errors"
78 "fmt"
89 "sync"
@@ -220,6 +221,14 @@ func (e *Executor) initializeState() error {
220221 e .logger .Info ().Uint64 ("height" , state .LastBlockHeight ).
221222 Str ("chain_id" , state .ChainID ).Msg ("initialized state" )
222223
224+ // Synchronization check: verify execution layer is at the correct height
225+ // This is critical - if we cannot sync the execution layer, we must fail startup
226+ // to prevent running with an inconsistent state
227+ if err := e .syncExecutionLayer (e .ctx , state ); err != nil {
228+ e .sendCriticalError (fmt .Errorf ("failed to sync execution layer: %w" , err ))
229+ return fmt .Errorf ("failed to sync execution layer: %w" , err )
230+ }
231+
223232 return nil
224233}
225234
@@ -575,6 +584,142 @@ func (e *Executor) applyBlock(ctx context.Context, header types.Header, data *ty
575584 return newState , nil
576585}
577586
587+ // syncExecutionLayer checks if the execution layer is behind ev-node and syncs it if needed.
588+ // This is called during initialization to handle crash recovery scenarios where ev-node
589+ // is ahead of the execution layer.
590+ func (e * Executor ) syncExecutionLayer (ctx context.Context , state types.State ) error {
591+ // Check if the executor supports height checking
592+ heightChecker , ok := e .exec .(interface {
593+ GetLatestHeight (ctx context.Context ) (uint64 , error )
594+ })
595+ if ! ok {
596+ e .logger .Debug ().Msg ("executor does not support height checking, skipping sync" )
597+ return nil
598+ }
599+
600+ evNodeHeight := state .LastBlockHeight
601+
602+ // Skip sync check if we're at genesis
603+ if evNodeHeight < e .genesis .InitialHeight {
604+ e .logger .Debug ().Msg ("at genesis height, skipping execution layer sync check" )
605+ return nil
606+ }
607+
608+ // Get execution layer height
609+ execHeight , err := heightChecker .GetLatestHeight (ctx )
610+ if err != nil {
611+ return fmt .Errorf ("failed to get execution layer height: %w" , err )
612+ }
613+
614+ e .logger .Info ().
615+ Uint64 ("ev_node_height" , evNodeHeight ).
616+ Uint64 ("exec_layer_height" , execHeight ).
617+ Msg ("execution layer height check" )
618+
619+ // If execution layer is ahead, this is unexpected and dangerous
620+ if execHeight > evNodeHeight {
621+ e .logger .Error ().
622+ Uint64 ("ev_node_height" , evNodeHeight ).
623+ Uint64 ("exec_layer_height" , execHeight ).
624+ Msg ("execution layer is ahead of ev-node - this should not happen" )
625+ return fmt .Errorf ("execution layer height (%d) is ahead of ev-node (%d)" , execHeight , evNodeHeight )
626+ }
627+
628+ // If execution layer is behind, sync the missing blocks
629+ if execHeight < evNodeHeight {
630+ e .logger .Info ().
631+ Uint64 ("ev_node_height" , evNodeHeight ).
632+ Uint64 ("exec_layer_height" , execHeight ).
633+ Uint64 ("blocks_to_sync" , evNodeHeight - execHeight ).
634+ Msg ("execution layer is behind, syncing blocks" )
635+
636+ // Sync blocks from execHeight+1 to evNodeHeight
637+ for height := execHeight + 1 ; height <= evNodeHeight ; height ++ {
638+ if err := e .syncBlockToExecutionLayer (ctx , height ); err != nil {
639+ return fmt .Errorf ("failed to sync block %d to execution layer: %w" , height , err )
640+ }
641+ }
642+
643+ e .logger .Info ().
644+ Uint64 ("synced_blocks" , evNodeHeight - execHeight ).
645+ Msg ("successfully synced execution layer" )
646+ } else {
647+ e .logger .Info ().Msg ("execution layer is in sync" )
648+ }
649+
650+ return nil
651+ }
652+
653+ // syncBlockToExecutionLayer syncs a specific block from ev-node to the execution layer.
654+ func (e * Executor ) syncBlockToExecutionLayer (ctx context.Context , height uint64 ) error {
655+ e .logger .Info ().Uint64 ("height" , height ).Msg ("syncing block to execution layer" )
656+
657+ // Get the block from store
658+ header , data , err := e .store .GetBlockData (ctx , height )
659+ if err != nil {
660+ return fmt .Errorf ("failed to get block data from store: %w" , err )
661+ }
662+
663+ // Get the previous state
664+ var prevState types.State
665+ if height == e .genesis .InitialHeight {
666+ // For the first block, use genesis state
667+ prevState = types.State {
668+ ChainID : e .genesis .ChainID ,
669+ InitialHeight : e .genesis .InitialHeight ,
670+ LastBlockHeight : e .genesis .InitialHeight - 1 ,
671+ LastBlockTime : e .genesis .StartTime ,
672+ AppHash : header .AppHash , // This will be updated by InitChain
673+ }
674+ } else {
675+ // Get previous state from store
676+ prevState , err = e .store .GetState (ctx )
677+ if err != nil {
678+ return fmt .Errorf ("failed to get previous state: %w" , err )
679+ }
680+ // We need the state at height-1, so load that block's app hash
681+ prevHeader , _ , err := e .store .GetBlockData (ctx , height - 1 )
682+ if err != nil {
683+ return fmt .Errorf ("failed to get previous block header: %w" , err )
684+ }
685+ prevState .AppHash = prevHeader .AppHash
686+ prevState .LastBlockHeight = height - 1
687+ }
688+
689+ // Prepare transactions
690+ rawTxs := make ([][]byte , len (data .Txs ))
691+ for i , tx := range data .Txs {
692+ rawTxs [i ] = []byte (tx )
693+ }
694+
695+ // Execute transactions on the execution layer
696+ e .logger .Debug ().
697+ Uint64 ("height" , height ).
698+ Int ("tx_count" , len (rawTxs )).
699+ Msg ("executing transactions on execution layer" )
700+
701+ newAppHash , _ , err := e .exec .ExecuteTxs (ctx , rawTxs , height , header .Time (), prevState .AppHash )
702+ if err != nil {
703+ return fmt .Errorf ("failed to execute transactions: %w" , err )
704+ }
705+
706+ // Verify the app hash matches
707+ if ! bytes .Equal (newAppHash , header .AppHash ) {
708+ e .logger .Warn ().
709+ Str ("expected" , hex .EncodeToString (header .AppHash )).
710+ Str ("got" , hex .EncodeToString (newAppHash )).
711+ Msg ("app hash mismatch during sync" )
712+ // Don't fail here - the execution layer may compute slightly different
713+ // state roots in some cases, but the state should still be valid
714+ }
715+
716+ e .logger .Info ().
717+ Uint64 ("height" , height ).
718+ Msg ("successfully synced block to execution layer" )
719+
720+ return nil
721+ }
722+
578723// signHeader signs the block header
579724func (e * Executor ) signHeader (header types.Header ) (types.Signature , error ) {
580725 bz , err := e .options .AggregatorNodeSignatureBytesProvider (& header )
0 commit comments