github.com/ethereum-optimism/optimism@v1.7.2/op-node/rollup/driver/origin_selector_test.go (about)

     1  package driver
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/ethereum-optimism/optimism/op-node/rollup"
     8  	"github.com/ethereum-optimism/optimism/op-service/eth"
     9  	"github.com/ethereum-optimism/optimism/op-service/testlog"
    10  	"github.com/ethereum-optimism/optimism/op-service/testutils"
    11  	"github.com/ethereum/go-ethereum/common"
    12  	"github.com/ethereum/go-ethereum/log"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  // TestOriginSelectorAdvances ensures that the origin selector
    17  // advances the origin
    18  //
    19  // There are 2 L1 blocks at time 20 & 25. The L2 Head is at time 24.
    20  // The next L2 time is 26 which is after the next L1 block time. There
    21  // is no conf depth to stop the origin selection so block `b` should
    22  // be the next L1 origin
    23  func TestOriginSelectorAdvances(t *testing.T) {
    24  	log := testlog.Logger(t, log.LevelCrit)
    25  	cfg := &rollup.Config{
    26  		MaxSequencerDrift: 500,
    27  		BlockTime:         2,
    28  	}
    29  	l1 := &testutils.MockL1Source{}
    30  	defer l1.AssertExpectations(t)
    31  	a := eth.L1BlockRef{
    32  		Hash:   common.Hash{'a'},
    33  		Number: 10,
    34  		Time:   20,
    35  	}
    36  	b := eth.L1BlockRef{
    37  		Hash:       common.Hash{'b'},
    38  		Number:     11,
    39  		Time:       25,
    40  		ParentHash: a.Hash,
    41  	}
    42  	l2Head := eth.L2BlockRef{
    43  		L1Origin: a.ID(),
    44  		Time:     24,
    45  	}
    46  
    47  	l1.ExpectL1BlockRefByHash(a.Hash, a, nil)
    48  	l1.ExpectL1BlockRefByNumber(b.Number, b, nil)
    49  
    50  	s := NewL1OriginSelector(log, cfg, l1)
    51  	next, err := s.FindL1Origin(context.Background(), l2Head)
    52  	require.Nil(t, err)
    53  	require.Equal(t, b, next)
    54  }
    55  
    56  // TestOriginSelectorRespectsOriginTiming ensures that the origin selector
    57  // does not pick an origin that is ahead of the next L2 block time
    58  //
    59  // There are 2 L1 blocks at time 20 & 25. The L2 Head is at time 22.
    60  // The next L2 time is 24 which is before the next L1 block time. There
    61  // is no conf depth to stop the LOS from potentially selecting block `b`
    62  // but it should select block `a` because the L2 block time must be ahead
    63  // of the the timestamp of it's L1 origin.
    64  func TestOriginSelectorRespectsOriginTiming(t *testing.T) {
    65  	log := testlog.Logger(t, log.LevelCrit)
    66  	cfg := &rollup.Config{
    67  		MaxSequencerDrift: 500,
    68  		BlockTime:         2,
    69  	}
    70  	l1 := &testutils.MockL1Source{}
    71  	defer l1.AssertExpectations(t)
    72  	a := eth.L1BlockRef{
    73  		Hash:   common.Hash{'a'},
    74  		Number: 10,
    75  		Time:   20,
    76  	}
    77  	b := eth.L1BlockRef{
    78  		Hash:       common.Hash{'b'},
    79  		Number:     11,
    80  		Time:       25,
    81  		ParentHash: a.Hash,
    82  	}
    83  	l2Head := eth.L2BlockRef{
    84  		L1Origin: a.ID(),
    85  		Time:     22,
    86  	}
    87  
    88  	l1.ExpectL1BlockRefByHash(a.Hash, a, nil)
    89  	l1.ExpectL1BlockRefByNumber(b.Number, b, nil)
    90  
    91  	s := NewL1OriginSelector(log, cfg, l1)
    92  	next, err := s.FindL1Origin(context.Background(), l2Head)
    93  	require.Nil(t, err)
    94  	require.Equal(t, a, next)
    95  }
    96  
    97  // TestOriginSelectorRespectsConfDepth ensures that the origin selector
    98  // will respect the confirmation depth requirement
    99  //
   100  // There are 2 L1 blocks at time 20 & 25. The L2 Head is at time 27.
   101  // The next L2 time is 29 which enough to normally select block `b`
   102  // as the origin, however block `b` is the L1 Head & the sequencer
   103  // needs to wait until that block is confirmed enough before advancing.
   104  func TestOriginSelectorRespectsConfDepth(t *testing.T) {
   105  	log := testlog.Logger(t, log.LevelCrit)
   106  	cfg := &rollup.Config{
   107  		MaxSequencerDrift: 500,
   108  		BlockTime:         2,
   109  	}
   110  	l1 := &testutils.MockL1Source{}
   111  	defer l1.AssertExpectations(t)
   112  	a := eth.L1BlockRef{
   113  		Hash:   common.Hash{'a'},
   114  		Number: 10,
   115  		Time:   20,
   116  	}
   117  	b := eth.L1BlockRef{
   118  		Hash:       common.Hash{'b'},
   119  		Number:     11,
   120  		Time:       25,
   121  		ParentHash: a.Hash,
   122  	}
   123  	l2Head := eth.L2BlockRef{
   124  		L1Origin: a.ID(),
   125  		Time:     27,
   126  	}
   127  
   128  	l1.ExpectL1BlockRefByHash(a.Hash, a, nil)
   129  	confDepthL1 := NewConfDepth(10, func() eth.L1BlockRef { return b }, l1)
   130  	s := NewL1OriginSelector(log, cfg, confDepthL1)
   131  
   132  	next, err := s.FindL1Origin(context.Background(), l2Head)
   133  	require.Nil(t, err)
   134  	require.Equal(t, a, next)
   135  }
   136  
   137  // TestOriginSelectorStrictConfDepth ensures that the origin selector will maintain the sequencer conf depth,
   138  // even while the time delta between the current L1 origin and the next
   139  // L2 block is greater than the sequencer drift.
   140  // It's more important to maintain safety with an empty block than to maintain liveness with poor conf depth.
   141  //
   142  // There are 2 L1 blocks at time 20 & 25. The L2 Head is at time 27.
   143  // The next L2 time is 29. The sequencer drift is 8 so the L2 head is
   144  // valid with origin `a`, but the next L2 block is not valid with origin `b.`
   145  // This is because 29 (next L2 time) > 20 (origin) + 8 (seq drift) => invalid block.
   146  // We maintain confirmation distance, even though we would shift to the next origin if we could.
   147  func TestOriginSelectorStrictConfDepth(t *testing.T) {
   148  	log := testlog.Logger(t, log.LevelCrit)
   149  	cfg := &rollup.Config{
   150  		MaxSequencerDrift: 8,
   151  		BlockTime:         2,
   152  	}
   153  	l1 := &testutils.MockL1Source{}
   154  	defer l1.AssertExpectations(t)
   155  	a := eth.L1BlockRef{
   156  		Hash:   common.Hash{'a'},
   157  		Number: 10,
   158  		Time:   20,
   159  	}
   160  	b := eth.L1BlockRef{
   161  		Hash:       common.Hash{'b'},
   162  		Number:     11,
   163  		Time:       25,
   164  		ParentHash: a.Hash,
   165  	}
   166  	l2Head := eth.L2BlockRef{
   167  		L1Origin: a.ID(),
   168  		Time:     27,
   169  	}
   170  
   171  	l1.ExpectL1BlockRefByHash(a.Hash, a, nil)
   172  	confDepthL1 := NewConfDepth(10, func() eth.L1BlockRef { return b }, l1)
   173  	s := NewL1OriginSelector(log, cfg, confDepthL1)
   174  
   175  	_, err := s.FindL1Origin(context.Background(), l2Head)
   176  	require.ErrorContains(t, err, "sequencer time drift")
   177  }
   178  
   179  // TestOriginSelectorSeqDriftRespectsNextOriginTime
   180  //
   181  // There are 2 L1 blocks at time 20 & 100. The L2 Head is at time 27.
   182  // The next L2 time is 29. Even though the next L2 time is past the seq
   183  // drift, the origin should remain on block `a` because the next origin's
   184  // time is greater than the next L2 time.
   185  func TestOriginSelectorSeqDriftRespectsNextOriginTime(t *testing.T) {
   186  	log := testlog.Logger(t, log.LevelCrit)
   187  	cfg := &rollup.Config{
   188  		MaxSequencerDrift: 8,
   189  		BlockTime:         2,
   190  	}
   191  	l1 := &testutils.MockL1Source{}
   192  	defer l1.AssertExpectations(t)
   193  	a := eth.L1BlockRef{
   194  		Hash:   common.Hash{'a'},
   195  		Number: 10,
   196  		Time:   20,
   197  	}
   198  	b := eth.L1BlockRef{
   199  		Hash:       common.Hash{'b'},
   200  		Number:     11,
   201  		Time:       100,
   202  		ParentHash: a.Hash,
   203  	}
   204  	l2Head := eth.L2BlockRef{
   205  		L1Origin: a.ID(),
   206  		Time:     27,
   207  	}
   208  
   209  	l1.ExpectL1BlockRefByHash(a.Hash, a, nil)
   210  	l1.ExpectL1BlockRefByNumber(b.Number, b, nil)
   211  
   212  	s := NewL1OriginSelector(log, cfg, l1)
   213  	next, err := s.FindL1Origin(context.Background(), l2Head)
   214  	require.Nil(t, err)
   215  	require.Equal(t, a, next)
   216  }
   217  
   218  // TestOriginSelectorHandlesLateL1Blocks tests the forced repeat of the previous origin,
   219  // but with a conf depth that first prevents it from learning about the need to repeat.
   220  //
   221  // There are 2 L1 blocks at time 20 & 100. The L2 Head is at time 27.
   222  // The next L2 time is 29. Even though the next L2 time is past the seq
   223  // drift, the origin should remain on block `a` because the next origin's
   224  // time is greater than the next L2 time.
   225  // Due to a conf depth of 2, block `b` is not immediately visible,
   226  // and the origin selection should fail until it is visible, by waiting for block `c`.
   227  func TestOriginSelectorHandlesLateL1Blocks(t *testing.T) {
   228  	log := testlog.Logger(t, log.LevelCrit)
   229  	cfg := &rollup.Config{
   230  		MaxSequencerDrift: 8,
   231  		BlockTime:         2,
   232  	}
   233  	l1 := &testutils.MockL1Source{}
   234  	defer l1.AssertExpectations(t)
   235  	a := eth.L1BlockRef{
   236  		Hash:   common.Hash{'a'},
   237  		Number: 10,
   238  		Time:   20,
   239  	}
   240  	b := eth.L1BlockRef{
   241  		Hash:       common.Hash{'b'},
   242  		Number:     11,
   243  		Time:       100,
   244  		ParentHash: a.Hash,
   245  	}
   246  	c := eth.L1BlockRef{
   247  		Hash:       common.Hash{'c'},
   248  		Number:     12,
   249  		Time:       150,
   250  		ParentHash: b.Hash,
   251  	}
   252  	d := eth.L1BlockRef{
   253  		Hash:       common.Hash{'d'},
   254  		Number:     13,
   255  		Time:       200,
   256  		ParentHash: c.Hash,
   257  	}
   258  	l2Head := eth.L2BlockRef{
   259  		L1Origin: a.ID(),
   260  		Time:     27,
   261  	}
   262  
   263  	// l2 head does not change, so we start at the same origin again and again until we meet the conf depth
   264  	l1.ExpectL1BlockRefByHash(a.Hash, a, nil)
   265  	l1.ExpectL1BlockRefByHash(a.Hash, a, nil)
   266  	l1.ExpectL1BlockRefByHash(a.Hash, a, nil)
   267  	l1.ExpectL1BlockRefByNumber(b.Number, b, nil)
   268  
   269  	l1Head := b
   270  	confDepthL1 := NewConfDepth(2, func() eth.L1BlockRef { return l1Head }, l1)
   271  	s := NewL1OriginSelector(log, cfg, confDepthL1)
   272  
   273  	_, err := s.FindL1Origin(context.Background(), l2Head)
   274  	require.ErrorContains(t, err, "sequencer time drift")
   275  
   276  	l1Head = c
   277  	_, err = s.FindL1Origin(context.Background(), l2Head)
   278  	require.ErrorContains(t, err, "sequencer time drift")
   279  
   280  	l1Head = d
   281  	next, err := s.FindL1Origin(context.Background(), l2Head)
   282  	require.Nil(t, err)
   283  	require.Equal(t, a, next, "must stay on a because the L1 time may not be higher than the L2 time")
   284  }