@@ -147,6 +147,8 @@ type Manager struct {
147147 metrics * Metrics
148148
149149 exec coreexecutor.Executor
150+ // executionMode caches the execution mode from the executor
151+ executionMode coreexecutor.ExecutionMode
150152
151153 // daIncludedHeight is rollkit height at which all blocks have been included
152154 // in the DA
@@ -351,6 +353,8 @@ func NewManager(
351353 daH := atomic.Uint64 {}
352354 daH .Store (s .DAHeight )
353355
356+ executionMode := exec .GetExecutionMode ()
357+
354358 m := & Manager {
355359 signer : signer ,
356360 config : config ,
@@ -380,6 +384,7 @@ func NewManager(
380384 metrics : seqMetrics ,
381385 sequencer : sequencer ,
382386 exec : exec ,
387+ executionMode : executionMode ,
383388 da : da ,
384389 gasPrice : gasPrice ,
385390 gasMultiplier : gasMultiplier ,
@@ -800,22 +805,26 @@ func (m *Manager) execValidate(lastState types.State, header *types.SignedHeader
800805 }
801806
802807 // // Verify that the header's timestamp is strictly greater than the last block's time
803- // headerTime := header.Time()
804- // if header.Height() > 1 && lastState.LastBlockTime.After(headerTime) {
805- // return fmt.Errorf("block time must be strictly increasing: got %v, last block time was %v",
806- // headerTime.UnixNano(), lastState.LastBlockTime)
807- // }
808-
809- // // Validate that the header's AppHash matches the lastState's AppHash
810- // // Note: Assumes deferred execution
811- // if !bytes.Equal(header.AppHash, lastState.AppHash) {
812- // return fmt.Errorf("app hash mismatch: expected %x, got %x", lastState.AppHash, header.AppHash)
813- // }
808+ headerTime := header .Time ()
809+ if header .Height () > 1 && lastState .LastBlockTime .After (headerTime ) {
810+ return fmt .Errorf ("block time must be strictly increasing: got %v, last block time was %v" ,
811+ headerTime .UnixNano (), lastState .LastBlockTime )
812+ }
813+
814+ // Validate AppHash based on execution mode
815+ if m .executionMode == coreexecutor .ExecutionModeDelayed {
816+ // In delayed mode, AppHash should match the last state's AppHash
817+ if ! bytes .Equal (header .AppHash , lastState .AppHash ) {
818+ return fmt .Errorf ("appHash mismatch in delayed mode: expected %x, got %x" , lastState .AppHash , header .AppHash )
819+ }
820+ }
821+ // Note: For immediate mode, we can't validate AppHash against lastState because
822+ // it represents the state after executing the current block's transactions
814823
815824 return nil
816825}
817826
818- func (m * Manager ) execCreateBlock (_ context.Context , height uint64 , lastSignature * types.Signature , lastHeaderHash types.Hash , _ types.State , batchData * BatchData ) (* types.SignedHeader , * types.Data , error ) {
827+ func (m * Manager ) execCreateBlock (ctx context.Context , height uint64 , lastSignature * types.Signature , lastHeaderHash types.Hash , lastState types.State , batchData * BatchData ) (* types.SignedHeader , * types.Data , error ) {
819828 // Use when batchData is set to data IDs from the DA layer
820829 // batchDataIDs := convertBatchDataToBytes(batchData.Data)
821830
@@ -840,21 +849,54 @@ func (m *Manager) execCreateBlock(_ context.Context, height uint64, lastSignatur
840849 // Determine if this is an empty block
841850 isEmpty := batchData .Batch == nil || len (batchData .Transactions ) == 0
842851
852+ // Create block data with appropriate transactions
853+ blockData := & types.Data {
854+ Txs : make (types.Txs , 0 ), // Start with empty transaction list
855+ }
856+
857+ // Only add transactions if this is not an empty block
858+ if ! isEmpty {
859+ blockData .Txs = make (types.Txs , len (batchData .Transactions ))
860+ for i := range batchData .Transactions {
861+ blockData .Txs [i ] = types .Tx (batchData .Transactions [i ])
862+ }
863+ }
864+
865+ // Determine AppHash based on execution mode
866+ var appHash []byte
867+ if m .executionMode == coreexecutor .ExecutionModeImmediate && ! isEmpty {
868+ // For immediate execution, execute transactions now to get the new state root
869+ rawTxs := make ([][]byte , len (blockData .Txs ))
870+ for i := range blockData .Txs {
871+ rawTxs [i ] = blockData .Txs [i ]
872+ }
873+
874+ // Execute transactions
875+ newStateRoot , _ , err := m .exec .ExecuteTxs (ctx , rawTxs , height , batchData .Time , lastState .AppHash )
876+ if err != nil {
877+ return nil , nil , fmt .Errorf ("failed to execute transactions for immediate mode: %w" , err )
878+ }
879+ appHash = newStateRoot
880+ } else {
881+ // For delayed execution or empty blocks, use the app hash from last state
882+ appHash = lastState .AppHash
883+ }
884+
843885 header := & types.SignedHeader {
844886 Header : types.Header {
845887 Version : types.Version {
846- Block : m . lastState .Version .Block ,
847- App : m . lastState .Version .App ,
888+ Block : lastState .Version .Block ,
889+ App : lastState .Version .App ,
848890 },
849891 BaseHeader : types.BaseHeader {
850- ChainID : m . lastState .ChainID ,
892+ ChainID : lastState .ChainID ,
851893 Height : height ,
852894 Time : uint64 (batchData .UnixNano ()), //nolint:gosec // why is time unix? (tac0turtle)
853895 },
854896 LastHeaderHash : lastHeaderHash ,
855- // DataHash is set at the end of the function
897+ // DataHash is set below
856898 ConsensusHash : make (types.Hash , 32 ),
857- AppHash : m . lastState . AppHash ,
899+ AppHash : appHash ,
858900 ProposerAddress : m .genesis .ProposerAddress ,
859901 },
860902 Signature : * lastSignature ,
@@ -864,17 +906,8 @@ func (m *Manager) execCreateBlock(_ context.Context, height uint64, lastSignatur
864906 },
865907 }
866908
867- // Create block data with appropriate transactions
868- blockData := & types.Data {
869- Txs : make (types.Txs , 0 ), // Start with empty transaction list
870- }
871-
872- // Only add transactions if this is not an empty block
909+ // Set DataHash
873910 if ! isEmpty {
874- blockData .Txs = make (types.Txs , len (batchData .Transactions ))
875- for i := range batchData .Transactions {
876- blockData .Txs [i ] = types .Tx (batchData .Transactions [i ])
877- }
878911 header .DataHash = blockData .DACommitment ()
879912 } else {
880913 header .DataHash = dataHashForEmptyTxs
@@ -884,15 +917,61 @@ func (m *Manager) execCreateBlock(_ context.Context, height uint64, lastSignatur
884917}
885918
886919func (m * Manager ) execApplyBlock (ctx context.Context , lastState types.State , header * types.SignedHeader , data * types.Data ) (types.State , error ) {
887- rawTxs := make ([][]byte , len (data .Txs ))
888- for i := range data .Txs {
889- rawTxs [i ] = data .Txs [i ]
890- }
920+ var newStateRoot []byte
921+
922+ // Check if we need to execute transactions
923+ if m .executionMode == coreexecutor .ExecutionModeImmediate {
924+ // For immediate mode, the aggregator already executed during block creation
925+ // But syncing nodes need to execute to verify the AppHash
926+ // Check if we're the block creator by verifying we signed this block
927+ isBlockCreator := false
928+ if m .signer != nil {
929+ myAddress , err := m .signer .GetAddress ()
930+ if err == nil && bytes .Equal (header .ProposerAddress , myAddress ) {
931+ // Also verify this is the current height we're producing
932+ currentHeight , err := m .store .Height (ctx )
933+ if err == nil && header .Height () == currentHeight + 1 {
934+ isBlockCreator = true
935+ }
936+ }
937+ }
891938
892- ctx = context .WithValue (ctx , types .SignedHeaderContextKey , header )
893- newStateRoot , _ , err := m .exec .ExecuteTxs (ctx , rawTxs , header .Height (), header .Time (), lastState .AppHash )
894- if err != nil {
895- return types.State {}, fmt .Errorf ("failed to execute transactions: %w" , err )
939+ if isBlockCreator {
940+ // We created this block, so we already executed transactions in execCreateBlock
941+ // Just use the AppHash from the header
942+ newStateRoot = header .AppHash
943+ } else {
944+ // We're syncing this block, need to execute to verify AppHash
945+ rawTxs := make ([][]byte , len (data .Txs ))
946+ for i := range data .Txs {
947+ rawTxs [i ] = data .Txs [i ]
948+ }
949+
950+ ctx = context .WithValue (ctx , types .SignedHeaderContextKey , header )
951+ computedStateRoot , _ , err := m .exec .ExecuteTxs (ctx , rawTxs , header .Height (), header .Time (), lastState .AppHash )
952+ if err != nil {
953+ return types.State {}, fmt .Errorf ("failed to execute transactions: %w" , err )
954+ }
955+
956+ // Verify the AppHash matches
957+ if ! bytes .Equal (header .AppHash , computedStateRoot ) {
958+ return types.State {}, fmt .Errorf ("AppHash mismatch in immediate mode: expected %x, got %x" , computedStateRoot , header .AppHash )
959+ }
960+ newStateRoot = computedStateRoot
961+ }
962+ } else {
963+ // For delayed mode, always execute transactions
964+ rawTxs := make ([][]byte , len (data .Txs ))
965+ for i := range data .Txs {
966+ rawTxs [i ] = data .Txs [i ]
967+ }
968+
969+ ctx = context .WithValue (ctx , types .SignedHeaderContextKey , header )
970+ var err error
971+ newStateRoot , _ , err = m .exec .ExecuteTxs (ctx , rawTxs , header .Height (), header .Time (), lastState .AppHash )
972+ if err != nil {
973+ return types.State {}, fmt .Errorf ("failed to execute transactions: %w" , err )
974+ }
896975 }
897976
898977 s , err := lastState .NextState (header , newStateRoot )
0 commit comments