diff --git a/block/internal/common/replay.go b/block/internal/common/replay.go index 9c95bc3f3..b96567ef7 100644 --- a/block/internal/common/replay.go +++ b/block/internal/common/replay.go @@ -67,15 +67,11 @@ func (s *Replayer) SyncToHeight(ctx context.Context, targetHeight uint64) error Uint64("exec_layer_height", execHeight). Msg("execution layer height check") - // If execution layer is ahead, skip syncing and continue. This can happen if execution - // progressed independently (e.g. after manual intervention). We log it for visibility but - // do not treat it as fatal. + // If execution layer is ahead, we cannot proceed safely as this indicates state divergence. + // The execution layer must be rolled back before the node can continue. if execHeight > targetHeight { - s.logger.Warn(). - Uint64("target_height", targetHeight). - Uint64("exec_layer_height", execHeight). - Msg("execution layer is ahead of target height - skipping replay") - return nil + return fmt.Errorf("execution layer height (%d) ahead of target height (%d): manually rollback execution layer to height %d", + execHeight, targetHeight, targetHeight) } // If execution layer is behind, sync the missing blocks diff --git a/block/internal/common/replay_test.go b/block/internal/common/replay_test.go index 6bc344d94..188e819d0 100644 --- a/block/internal/common/replay_test.go +++ b/block/internal/common/replay_test.go @@ -135,15 +135,16 @@ func TestReplayer_SyncToHeight_ExecutorAhead(t *testing.T) { syncer := NewReplayer(mockStore, mockExec, gen, logger) - // Setup: target height is 100, execution layer is at 101 (unexpected!) + // Setup: execution layer is ahead of target (indicates state divergence) targetHeight := uint64(100) execHeight := uint64(101) mockExec.On("GetLatestHeight", mock.Anything).Return(execHeight, nil) - // Execute sync - should just log and continue without error + // Should return error to prevent proceeding with divergent state err := syncer.SyncToHeight(ctx, targetHeight) - require.NoError(t, err) + require.Error(t, err) + require.Contains(t, err.Error(), "ahead of target height") // No replay should be attempted mockExec.AssertNotCalled(t, "ExecuteTxs", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)