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

     1  package sync
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/ethereum-optimism/optimism/op-node/rollup"
    10  	"github.com/ethereum-optimism/optimism/op-service/eth"
    11  	"github.com/ethereum-optimism/optimism/op-service/testlog"
    12  	"github.com/ethereum-optimism/optimism/op-service/testutils"
    13  	"github.com/ethereum/go-ethereum/common"
    14  	"github.com/ethereum/go-ethereum/log"
    15  )
    16  
    17  var _ L1Chain = (*testutils.FakeChainSource)(nil)
    18  var _ L2Chain = (*testutils.FakeChainSource)(nil)
    19  
    20  // generateFakeL2 creates a fake L2 chain with the following conditions:
    21  // - The L2 chain is based off of the L1 chain
    22  // - The actual L1 chain is the New L1 chain
    23  // - Both heads are at the tip of their respective chains
    24  func (c *syncStartTestCase) generateFakeL2(t *testing.T) (*testutils.FakeChainSource, rollup.Genesis) {
    25  	t.Helper()
    26  	log := testlog.Logger(t, log.LevelError)
    27  	chain := testutils.NewFakeChainSource([]string{c.L1, c.NewL1}, []string{c.L2}, int(c.GenesisL1Num), log)
    28  	chain.SetL2Head(len(c.L2) - 1)
    29  	genesis := testutils.FakeGenesis(c.GenesisL1, c.GenesisL2, int(c.GenesisL1Num))
    30  	chain.ReorgL1()
    31  	for i := 0; i < len(c.NewL1)-1; i++ {
    32  		chain.AdvanceL1()
    33  	}
    34  	return chain, genesis
    35  
    36  }
    37  
    38  func runeToHash(id rune) common.Hash {
    39  	var h common.Hash
    40  	copy(h[:], string(id))
    41  	return h
    42  }
    43  
    44  type syncStartTestCase struct {
    45  	Name string
    46  
    47  	L1    string // L1 Chain prior to a re-org or other change
    48  	L2    string // L2 Chain that follows from L1Chain
    49  	NewL1 string // New L1 chain
    50  
    51  	PreFinalizedL2 rune
    52  	PreSafeL2      rune
    53  
    54  	GenesisL1    rune
    55  	GenesisL1Num uint64
    56  	GenesisL2    rune
    57  
    58  	SeqWindowSize uint64
    59  	SafeL2Head    rune
    60  	UnsafeL2Head  rune
    61  	ExpectedErr   error
    62  }
    63  
    64  func refToRune(r eth.BlockID) rune {
    65  	return rune(r.Hash.Bytes()[0])
    66  }
    67  
    68  func (c *syncStartTestCase) Run(t *testing.T) {
    69  	chain, genesis := c.generateFakeL2(t)
    70  	chain.SetL2Finalized(runeToHash(c.PreFinalizedL2))
    71  	chain.SetL2Safe(runeToHash(c.PreSafeL2))
    72  
    73  	cfg := &rollup.Config{
    74  		Genesis:       genesis,
    75  		SeqWindowSize: c.SeqWindowSize,
    76  	}
    77  	lgr := log.NewLogger(log.DiscardHandler())
    78  	result, err := FindL2Heads(context.Background(), cfg, chain, chain, lgr, &Config{})
    79  	if c.ExpectedErr != nil {
    80  		require.ErrorIs(t, err, c.ExpectedErr, "expected error")
    81  		return
    82  	} else {
    83  		require.NoError(t, err, "expected no error")
    84  	}
    85  
    86  	gotUnsafeHead := refToRune(result.Unsafe.ID())
    87  	require.Equal(t, string(c.UnsafeL2Head), string(gotUnsafeHead), "Unsafe L2 Head not equal")
    88  
    89  	gotSafeHead := refToRune(result.Safe.ID())
    90  	require.Equal(t, string(c.SafeL2Head), string(gotSafeHead), "Safe L2 Head not equal")
    91  }
    92  
    93  func TestFindSyncStart(t *testing.T) {
    94  	testCases := []syncStartTestCase{
    95  		{
    96  			Name:           "already synced",
    97  			GenesisL1Num:   0,
    98  			L1:             "ab",
    99  			L2:             "AB",
   100  			NewL1:          "ab",
   101  			PreFinalizedL2: 'A',
   102  			PreSafeL2:      'A',
   103  			GenesisL1:      'a',
   104  			GenesisL2:      'A',
   105  			UnsafeL2Head:   'B',
   106  			SeqWindowSize:  2,
   107  			SafeL2Head:     'A',
   108  			ExpectedErr:    nil,
   109  		},
   110  		{
   111  			Name:           "already synced with safe head after genesis",
   112  			GenesisL1Num:   0,
   113  			L1:             "abcdefghijkj",
   114  			L2:             "ABCDEFGHIJKJ",
   115  			NewL1:          "abcdefghijkj",
   116  			PreFinalizedL2: 'B',
   117  			PreSafeL2:      'D',
   118  			GenesisL1:      'a',
   119  			GenesisL2:      'A',
   120  			UnsafeL2Head:   'J',
   121  			SeqWindowSize:  2,
   122  			// Important this steps back at least one safe block so the safedb is sent the latest safe head
   123  			// again - we may be resetting because the safedb failed to write the previous entry
   124  			SafeL2Head:  'C',
   125  			ExpectedErr: nil,
   126  		},
   127  		{
   128  			Name:           "small reorg long chain",
   129  			GenesisL1Num:   0,
   130  			L1:             "abcdefgh",
   131  			L2:             "ABCDEFGH",
   132  			NewL1:          "abcdefgx",
   133  			PreFinalizedL2: 'B',
   134  			PreSafeL2:      'H',
   135  			GenesisL1:      'a',
   136  			GenesisL2:      'A',
   137  			UnsafeL2Head:   'G',
   138  			SeqWindowSize:  2,
   139  			SafeL2Head:     'C',
   140  			ExpectedErr:    nil,
   141  		},
   142  		{
   143  			Name:           "L1 Chain ahead",
   144  			GenesisL1Num:   0,
   145  			L1:             "abcdef",
   146  			L2:             "ABCDE",
   147  			NewL1:          "abcdef",
   148  			PreFinalizedL2: 'A',
   149  			PreSafeL2:      'D',
   150  			GenesisL1:      'a',
   151  			GenesisL2:      'A',
   152  			UnsafeL2Head:   'E',
   153  			SeqWindowSize:  2,
   154  			SafeL2Head:     'A',
   155  			ExpectedErr:    nil,
   156  		},
   157  		{
   158  			Name:           "L2 Chain ahead after reorg",
   159  			GenesisL1Num:   0,
   160  			L1:             "abcxyz",
   161  			L2:             "ABCXYZ",
   162  			NewL1:          "abcx",
   163  			PreFinalizedL2: 'B',
   164  			PreSafeL2:      'X',
   165  			GenesisL1:      'a',
   166  			GenesisL2:      'A',
   167  			UnsafeL2Head:   'Z',
   168  			SeqWindowSize:  2,
   169  			SafeL2Head:     'B',
   170  			ExpectedErr:    nil,
   171  		},
   172  		{
   173  			Name:           "genesis",
   174  			GenesisL1Num:   0,
   175  			L1:             "a",
   176  			L2:             "A",
   177  			NewL1:          "a",
   178  			PreFinalizedL2: 'A',
   179  			PreSafeL2:      'A',
   180  			GenesisL1:      'a',
   181  			GenesisL2:      'A',
   182  			UnsafeL2Head:   'A',
   183  			SeqWindowSize:  2,
   184  			SafeL2Head:     'A',
   185  			ExpectedErr:    nil,
   186  		},
   187  		{
   188  			Name:           "reorg one step back",
   189  			GenesisL1Num:   0,
   190  			L1:             "abcdefg",
   191  			L2:             "ABCDEFG",
   192  			NewL1:          "abcdefx",
   193  			PreFinalizedL2: 'A',
   194  			PreSafeL2:      'E',
   195  			GenesisL1:      'a',
   196  			GenesisL2:      'A',
   197  			UnsafeL2Head:   'F',
   198  			SeqWindowSize:  3,
   199  			SafeL2Head:     'A',
   200  			ExpectedErr:    nil,
   201  		},
   202  		{
   203  			Name:           "reorg two steps back, clip genesis and finalized",
   204  			GenesisL1Num:   0,
   205  			L1:             "abc",
   206  			L2:             "ABC",
   207  			PreFinalizedL2: 'A',
   208  			PreSafeL2:      'B',
   209  			NewL1:          "axy",
   210  			GenesisL1:      'a',
   211  			GenesisL2:      'A',
   212  			UnsafeL2Head:   'A',
   213  			SeqWindowSize:  2,
   214  			SafeL2Head:     'A',
   215  			ExpectedErr:    nil,
   216  		},
   217  		{
   218  			Name:           "reorg three steps back",
   219  			GenesisL1Num:   0,
   220  			L1:             "abcdefgh",
   221  			L2:             "ABCDEFGH",
   222  			NewL1:          "abcdexyz",
   223  			PreFinalizedL2: 'A',
   224  			PreSafeL2:      'D',
   225  			GenesisL1:      'a',
   226  			GenesisL2:      'A',
   227  			UnsafeL2Head:   'E',
   228  			SeqWindowSize:  2,
   229  			SafeL2Head:     'A',
   230  			ExpectedErr:    nil,
   231  		},
   232  		{
   233  			Name:           "unexpected L1 chain",
   234  			GenesisL1Num:   0,
   235  			L1:             "abcdef",
   236  			L2:             "ABCDEF",
   237  			NewL1:          "xyzwio",
   238  			PreFinalizedL2: 'A',
   239  			PreSafeL2:      'B',
   240  			GenesisL1:      'a',
   241  			GenesisL2:      'A',
   242  			UnsafeL2Head:   0,
   243  			SeqWindowSize:  2,
   244  			ExpectedErr:    WrongChainErr,
   245  		},
   246  		{
   247  			Name:           "unexpected L2 chain",
   248  			GenesisL1Num:   0,
   249  			L1:             "abcdef",
   250  			L2:             "ABCDEF",
   251  			NewL1:          "xyzwio",
   252  			PreFinalizedL2: 'A',
   253  			PreSafeL2:      'B',
   254  			GenesisL1:      'a',
   255  			GenesisL2:      'X',
   256  			UnsafeL2Head:   0,
   257  			SeqWindowSize:  2,
   258  			ExpectedErr:    WrongChainErr,
   259  		},
   260  		{
   261  			Name:           "offset L2 genesis",
   262  			GenesisL1Num:   3,
   263  			L1:             "abcdefghi",
   264  			L2:             "DEFGHI",
   265  			NewL1:          "abcdefghi",
   266  			PreFinalizedL2: 'E',
   267  			PreSafeL2:      'H',
   268  			GenesisL1:      'd',
   269  			GenesisL2:      'D',
   270  			UnsafeL2Head:   'I',
   271  			SeqWindowSize:  2,
   272  			SafeL2Head:     'E',
   273  			ExpectedErr:    nil,
   274  		},
   275  		{
   276  			Name:           "offset L2 genesis reorg",
   277  			GenesisL1Num:   3,
   278  			L1:             "abcdefgh",
   279  			L2:             "DEFGH",
   280  			NewL1:          "abcdxyzw",
   281  			PreFinalizedL2: 'D',
   282  			PreSafeL2:      'D',
   283  			GenesisL1:      'd',
   284  			GenesisL2:      'D',
   285  			UnsafeL2Head:   'D',
   286  			SeqWindowSize:  2,
   287  			SafeL2Head:     'D',
   288  			ExpectedErr:    nil,
   289  		},
   290  		{
   291  			Name:           "reorg past offset genesis",
   292  			GenesisL1Num:   3,
   293  			L1:             "abcdefgh",
   294  			L2:             "DEFGH",
   295  			NewL1:          "abxyzwio",
   296  			PreFinalizedL2: 'D',
   297  			PreSafeL2:      'D',
   298  			GenesisL1:      'd',
   299  			GenesisL2:      'D',
   300  			UnsafeL2Head:   0,
   301  			SeqWindowSize:  2,
   302  			SafeL2Head:     'D',
   303  			ExpectedErr:    WrongChainErr,
   304  		},
   305  		{
   306  			// FindL2Heads() keeps walking back to safe head after finding canonical unsafe head
   307  			// TooDeepReorgErr must not be raised
   308  			Name:           "long traverse to safe head",
   309  			GenesisL1Num:   0,
   310  			L1:             "abcdefgh",
   311  			L2:             "ABCDEFGH",
   312  			NewL1:          "abcdefgx",
   313  			PreFinalizedL2: 'B',
   314  			PreSafeL2:      'B',
   315  			GenesisL1:      'a',
   316  			GenesisL2:      'A',
   317  			UnsafeL2Head:   'G',
   318  			SeqWindowSize:  1,
   319  			SafeL2Head:     'B',
   320  			ExpectedErr:    nil,
   321  		},
   322  		{
   323  			// L2 reorg is too deep
   324  			Name:           "reorg too deep",
   325  			GenesisL1Num:   0,
   326  			L1:             "abcdefgh",
   327  			L2:             "ABCDEFGH",
   328  			NewL1:          "abijklmn",
   329  			PreFinalizedL2: 'B',
   330  			PreSafeL2:      'B',
   331  			GenesisL1:      'a',
   332  			GenesisL2:      'A',
   333  			SeqWindowSize:  1,
   334  			ExpectedErr:    TooDeepReorgErr,
   335  		},
   336  	}
   337  
   338  	for _, testCase := range testCases {
   339  		t.Run(testCase.Name, testCase.Run)
   340  	}
   341  }