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 }