github.com/ethereum-optimism/optimism@v1.7.2/op-node/rollup/derive/batches_test.go (about) 1 package derive 2 3 import ( 4 "context" 5 "errors" 6 "math/big" 7 "math/rand" 8 "testing" 9 10 "github.com/stretchr/testify/require" 11 12 "github.com/ethereum-optimism/optimism/op-node/rollup" 13 "github.com/ethereum-optimism/optimism/op-service/eth" 14 "github.com/ethereum-optimism/optimism/op-service/testlog" 15 "github.com/ethereum-optimism/optimism/op-service/testutils" 16 "github.com/ethereum/go-ethereum/common/hexutil" 17 "github.com/ethereum/go-ethereum/core/types" 18 "github.com/ethereum/go-ethereum/log" 19 ) 20 21 type ValidBatchTestCase struct { 22 Name string 23 L1Blocks []eth.L1BlockRef 24 L2SafeHead eth.L2BlockRef 25 Batch BatchWithL1InclusionBlock 26 Expected BatchValidity 27 ExpectedLog string // log message that must be included 28 NotExpectedLog string // log message that must not be included 29 DeltaTime *uint64 30 } 31 32 func TestValidBatch(t *testing.T) { 33 defaultConf := rollup.Config{ 34 Genesis: rollup.Genesis{ 35 L2Time: 31, // a genesis time that itself does not align to make it more interesting 36 }, 37 BlockTime: 2, 38 SeqWindowSize: 4, 39 MaxSequencerDrift: 6, 40 // other config fields are ignored and can be left empty. 41 DeltaTime: nil, 42 } 43 44 rng := rand.New(rand.NewSource(1234)) 45 46 minTs := uint64(0) 47 chainId := new(big.Int).SetUint64(rng.Uint64()) 48 signer := types.NewLondonSigner(chainId) 49 randTx := testutils.RandomTx(rng, new(big.Int).SetUint64(rng.Uint64()), signer) 50 randTxData, _ := randTx.MarshalBinary() 51 52 l1A := testutils.RandomBlockRef(rng) 53 l1B := eth.L1BlockRef{ 54 Hash: testutils.RandomHash(rng), 55 Number: l1A.Number + 1, 56 ParentHash: l1A.Hash, 57 Time: l1A.Time + 7, 58 } 59 l1C := eth.L1BlockRef{ 60 Hash: testutils.RandomHash(rng), 61 Number: l1B.Number + 1, 62 ParentHash: l1B.Hash, 63 Time: l1B.Time + 7, 64 } 65 l1D := eth.L1BlockRef{ 66 Hash: testutils.RandomHash(rng), 67 Number: l1C.Number + 1, 68 ParentHash: l1C.Hash, 69 Time: l1C.Time + 7, 70 } 71 l1E := eth.L1BlockRef{ 72 Hash: testutils.RandomHash(rng), 73 Number: l1D.Number + 1, 74 ParentHash: l1D.Hash, 75 Time: l1D.Time + 7, 76 } 77 l1F := eth.L1BlockRef{ 78 Hash: testutils.RandomHash(rng), 79 Number: l1E.Number + 1, 80 ParentHash: l1E.Hash, 81 Time: l1E.Time + 7, 82 } 83 84 l2A0 := eth.L2BlockRef{ 85 Hash: testutils.RandomHash(rng), 86 Number: 100, 87 ParentHash: testutils.RandomHash(rng), 88 Time: l1A.Time, 89 L1Origin: l1A.ID(), 90 SequenceNumber: 0, 91 } 92 93 l2A1 := eth.L2BlockRef{ 94 Hash: testutils.RandomHash(rng), 95 Number: l2A0.Number + 1, 96 ParentHash: l2A0.Hash, 97 Time: l2A0.Time + defaultConf.BlockTime, 98 L1Origin: l1A.ID(), 99 SequenceNumber: 1, 100 } 101 102 l2A2 := eth.L2BlockRef{ 103 Hash: testutils.RandomHash(rng), 104 Number: l2A1.Number + 1, 105 ParentHash: l2A1.Hash, 106 Time: l2A1.Time + defaultConf.BlockTime, 107 L1Origin: l1A.ID(), 108 SequenceNumber: 2, 109 } 110 111 l2A3 := eth.L2BlockRef{ 112 Hash: testutils.RandomHash(rng), 113 Number: l2A2.Number + 1, 114 ParentHash: l2A2.Hash, 115 Time: l2A2.Time + defaultConf.BlockTime, 116 L1Origin: l1A.ID(), 117 SequenceNumber: 3, 118 } 119 120 l2B0 := eth.L2BlockRef{ 121 Hash: testutils.RandomHash(rng), 122 Number: l2A3.Number + 1, 123 ParentHash: l2A3.Hash, 124 Time: l2A3.Time + defaultConf.BlockTime, // 8 seconds larger than l1A0, 1 larger than origin 125 L1Origin: l1B.ID(), 126 SequenceNumber: 0, 127 } 128 129 l2B1 := eth.L2BlockRef{ 130 Hash: testutils.RandomHash(rng), 131 Number: l2B0.Number + 1, 132 ParentHash: l2B0.Hash, 133 Time: l2B0.Time + defaultConf.BlockTime, 134 L1Origin: l1B.ID(), 135 SequenceNumber: 1, 136 } 137 138 l2B2 := eth.L2BlockRef{ 139 Hash: testutils.RandomHash(rng), 140 Number: l2B1.Number + 1, 141 ParentHash: l2B1.Hash, 142 Time: l2B1.Time + defaultConf.BlockTime, 143 L1Origin: l1B.ID(), 144 SequenceNumber: 2, 145 } 146 147 l1X := eth.L1BlockRef{ 148 Hash: testutils.RandomHash(rng), 149 Number: 42, 150 ParentHash: testutils.RandomHash(rng), 151 Time: 10_000, 152 } 153 l1Y := eth.L1BlockRef{ 154 Hash: testutils.RandomHash(rng), 155 Number: l1X.Number + 1, 156 ParentHash: l1X.Hash, 157 Time: l1X.Time + 12, 158 } 159 l1Z := eth.L1BlockRef{ 160 Hash: testutils.RandomHash(rng), 161 Number: l1Y.Number + 1, 162 ParentHash: l1Y.Hash, 163 Time: l1Y.Time + 12, 164 } 165 l2X0 := eth.L2BlockRef{ 166 Hash: testutils.RandomHash(rng), 167 Number: 1000, 168 ParentHash: testutils.RandomHash(rng), 169 Time: 10_000 + 24 + 6 - 1, // add one block, and you get ahead of next l1 block by more than the drift 170 L1Origin: l1X.ID(), 171 SequenceNumber: 0, 172 } 173 l2Y0 := eth.L2BlockRef{ 174 Hash: testutils.RandomHash(rng), 175 Number: l2X0.Number + 1, 176 ParentHash: l2X0.Hash, 177 Time: l2X0.Time + defaultConf.BlockTime, // exceeds sequencer time drift, forced to be empty block 178 L1Origin: l1Y.ID(), 179 SequenceNumber: 0, 180 } 181 l2Z0 := eth.L2BlockRef{ 182 Hash: testutils.RandomHash(rng), 183 Number: l2Y0.Number + 1, 184 ParentHash: l2Y0.Hash, 185 Time: l2Y0.Time + defaultConf.BlockTime, // exceeds sequencer time drift, forced to be empty block 186 L1Origin: l1Z.ID(), 187 SequenceNumber: 0, 188 } 189 190 l2A4 := eth.L2BlockRef{ 191 Hash: testutils.RandomHash(rng), 192 Number: l2A3.Number + 1, 193 ParentHash: l2A3.Hash, 194 Time: l2A3.Time + defaultConf.BlockTime, // 4*2 = 8, higher than seq time drift 195 L1Origin: l1A.ID(), 196 SequenceNumber: 4, 197 } 198 199 l1BLate := eth.L1BlockRef{ 200 Hash: testutils.RandomHash(rng), 201 Number: l1A.Number + 1, 202 ParentHash: l1A.Hash, 203 Time: l2A4.Time + 1, // too late for l2A4 to adopt yet 204 } 205 206 singularBatchTestCases := []ValidBatchTestCase{ 207 { 208 Name: "missing L1 info", 209 L1Blocks: []eth.L1BlockRef{}, 210 L2SafeHead: l2A0, 211 Batch: BatchWithL1InclusionBlock{ 212 L1InclusionBlock: l1B, 213 Batch: &SingularBatch{ 214 ParentHash: l2A1.ParentHash, 215 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 216 EpochHash: l2A1.L1Origin.Hash, 217 Timestamp: l2A1.Time, 218 Transactions: nil, 219 }, 220 }, 221 Expected: BatchUndecided, 222 }, 223 { 224 Name: "future timestamp", 225 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 226 L2SafeHead: l2A0, 227 Batch: BatchWithL1InclusionBlock{ 228 L1InclusionBlock: l1B, 229 Batch: &SingularBatch{ 230 ParentHash: l2A1.ParentHash, 231 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 232 EpochHash: l2A1.L1Origin.Hash, 233 Timestamp: l2A1.Time + 1, // 1 too high 234 Transactions: nil, 235 }, 236 }, 237 Expected: BatchFuture, 238 }, 239 { 240 Name: "old timestamp", 241 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 242 L2SafeHead: l2A0, 243 Batch: BatchWithL1InclusionBlock{ 244 L1InclusionBlock: l1B, 245 Batch: &SingularBatch{ 246 ParentHash: l2A1.ParentHash, 247 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 248 EpochHash: l2A1.L1Origin.Hash, 249 Timestamp: l2A0.Time, // repeating the same time 250 Transactions: nil, 251 }, 252 }, 253 Expected: BatchDrop, 254 }, 255 { 256 Name: "misaligned timestamp", 257 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 258 L2SafeHead: l2A0, 259 Batch: BatchWithL1InclusionBlock{ 260 L1InclusionBlock: l1B, 261 Batch: &SingularBatch{ 262 ParentHash: l2A1.ParentHash, 263 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 264 EpochHash: l2A1.L1Origin.Hash, 265 Timestamp: l2A1.Time - 1, // block time is 2, so this is 1 too low 266 Transactions: nil, 267 }, 268 }, 269 Expected: BatchDrop, 270 }, 271 { 272 Name: "invalid parent block hash", 273 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 274 L2SafeHead: l2A0, 275 Batch: BatchWithL1InclusionBlock{ 276 L1InclusionBlock: l1B, 277 Batch: &SingularBatch{ 278 ParentHash: testutils.RandomHash(rng), 279 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 280 EpochHash: l2A1.L1Origin.Hash, 281 Timestamp: l2A1.Time, 282 Transactions: nil, 283 }, 284 }, 285 Expected: BatchDrop, 286 }, 287 { 288 Name: "sequence window expired", 289 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C, l1D, l1E, l1F}, 290 L2SafeHead: l2A0, 291 Batch: BatchWithL1InclusionBlock{ 292 L1InclusionBlock: l1F, // included in 5th block after epoch of batch, while seq window is 4 293 Batch: &SingularBatch{ 294 ParentHash: l2A1.ParentHash, 295 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 296 EpochHash: l2A1.L1Origin.Hash, 297 Timestamp: l2A1.Time, 298 Transactions: nil, 299 }, 300 }, 301 Expected: BatchDrop, 302 }, 303 { 304 Name: "epoch too old, but good parent hash and timestamp", // repeat of now outdated l2A3 data 305 L1Blocks: []eth.L1BlockRef{l1B, l1C, l1D}, 306 L2SafeHead: l2B0, // we already moved on to B 307 Batch: BatchWithL1InclusionBlock{ 308 L1InclusionBlock: l1C, 309 Batch: &SingularBatch{ 310 ParentHash: l2B0.Hash, // build on top of safe head to continue 311 EpochNum: rollup.Epoch(l2A3.L1Origin.Number), // epoch A is no longer valid 312 EpochHash: l2A3.L1Origin.Hash, 313 Timestamp: l2B0.Time + defaultConf.BlockTime, // pass the timestamp check to get too epoch check 314 Transactions: nil, 315 }, 316 }, 317 Expected: BatchDrop, 318 }, 319 { 320 Name: "insufficient L1 info for eager derivation", 321 L1Blocks: []eth.L1BlockRef{l1A}, // don't know about l1B yet 322 L2SafeHead: l2A3, 323 Batch: BatchWithL1InclusionBlock{ 324 L1InclusionBlock: l1C, 325 Batch: &SingularBatch{ 326 ParentHash: l2B0.ParentHash, 327 EpochNum: rollup.Epoch(l2B0.L1Origin.Number), 328 EpochHash: l2B0.L1Origin.Hash, 329 Timestamp: l2B0.Time, 330 Transactions: nil, 331 }, 332 }, 333 Expected: BatchUndecided, 334 }, 335 { 336 Name: "epoch too new", 337 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C, l1D}, 338 L2SafeHead: l2A3, 339 Batch: BatchWithL1InclusionBlock{ 340 L1InclusionBlock: l1D, 341 Batch: &SingularBatch{ 342 ParentHash: l2B0.ParentHash, 343 EpochNum: rollup.Epoch(l1C.Number), // invalid, we need to adopt epoch B before C 344 EpochHash: l1C.Hash, 345 Timestamp: l2B0.Time, 346 Transactions: nil, 347 }, 348 }, 349 Expected: BatchDrop, 350 }, 351 { 352 Name: "epoch hash wrong", 353 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 354 L2SafeHead: l2A3, 355 Batch: BatchWithL1InclusionBlock{ 356 L1InclusionBlock: l1C, 357 Batch: &SingularBatch{ 358 ParentHash: l2B0.ParentHash, 359 EpochNum: rollup.Epoch(l2B0.L1Origin.Number), 360 EpochHash: l1A.Hash, // invalid, epoch hash should be l1B 361 Timestamp: l2B0.Time, 362 Transactions: nil, 363 }, 364 }, 365 Expected: BatchDrop, 366 }, 367 { 368 Name: "sequencer time drift on same epoch with non-empty txs", 369 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 370 L2SafeHead: l2A3, 371 Batch: BatchWithL1InclusionBlock{ 372 L1InclusionBlock: l1B, 373 Batch: &SingularBatch{ // we build l2A4, which has a timestamp of 2*4 = 8 higher than l2A0 374 ParentHash: l2A4.ParentHash, 375 EpochNum: rollup.Epoch(l2A4.L1Origin.Number), 376 EpochHash: l2A4.L1Origin.Hash, 377 Timestamp: l2A4.Time, 378 Transactions: []hexutil.Bytes{[]byte("sequencer should not include this tx")}, 379 }, 380 }, 381 Expected: BatchDrop, 382 }, 383 { 384 Name: "sequencer time drift on changing epoch with non-empty txs", 385 L1Blocks: []eth.L1BlockRef{l1X, l1Y, l1Z}, 386 L2SafeHead: l2X0, 387 Batch: BatchWithL1InclusionBlock{ 388 L1InclusionBlock: l1Z, 389 Batch: &SingularBatch{ 390 ParentHash: l2Y0.ParentHash, 391 EpochNum: rollup.Epoch(l2Y0.L1Origin.Number), 392 EpochHash: l2Y0.L1Origin.Hash, 393 Timestamp: l2Y0.Time, // valid, but more than 6 ahead of l1Y.Time 394 Transactions: []hexutil.Bytes{[]byte("sequencer should not include this tx")}, 395 }, 396 }, 397 Expected: BatchDrop, 398 }, 399 { 400 Name: "sequencer time drift on same epoch with empty txs and late next epoch", 401 L1Blocks: []eth.L1BlockRef{l1A, l1BLate}, 402 L2SafeHead: l2A3, 403 Batch: BatchWithL1InclusionBlock{ 404 L1InclusionBlock: l1BLate, 405 Batch: &SingularBatch{ // l2A4 time < l1BLate time, so we cannot adopt origin B yet 406 ParentHash: l2A4.ParentHash, 407 EpochNum: rollup.Epoch(l2A4.L1Origin.Number), 408 EpochHash: l2A4.L1Origin.Hash, 409 Timestamp: l2A4.Time, 410 Transactions: nil, 411 }, 412 }, 413 Expected: BatchAccept, // accepted because empty & preserving L2 time invariant 414 }, 415 { 416 Name: "sequencer time drift on changing epoch with empty txs", 417 L1Blocks: []eth.L1BlockRef{l1X, l1Y, l1Z}, 418 L2SafeHead: l2X0, 419 Batch: BatchWithL1InclusionBlock{ 420 L1InclusionBlock: l1Z, 421 Batch: &SingularBatch{ 422 ParentHash: l2Y0.ParentHash, 423 EpochNum: rollup.Epoch(l2Y0.L1Origin.Number), 424 EpochHash: l2Y0.L1Origin.Hash, 425 Timestamp: l2Y0.Time, // valid, but more than 6 ahead of l1Y.Time 426 Transactions: nil, 427 }, 428 }, 429 Expected: BatchAccept, // accepted because empty & still advancing epoch 430 }, 431 { 432 Name: "sequencer time drift on same epoch with empty txs and no next epoch in sight yet", 433 L1Blocks: []eth.L1BlockRef{l1A}, 434 L2SafeHead: l2A3, 435 Batch: BatchWithL1InclusionBlock{ 436 L1InclusionBlock: l1B, 437 Batch: &SingularBatch{ // we build l2A4, which has a timestamp of 2*4 = 8 higher than l2A0 438 ParentHash: l2A4.ParentHash, 439 EpochNum: rollup.Epoch(l2A4.L1Origin.Number), 440 EpochHash: l2A4.L1Origin.Hash, 441 Timestamp: l2A4.Time, 442 Transactions: nil, 443 }, 444 }, 445 Expected: BatchUndecided, // we have to wait till the next epoch is in sight to check the time 446 }, 447 { 448 Name: "sequencer time drift on same epoch with empty txs and but in-sight epoch that invalidates it", 449 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 450 L2SafeHead: l2A3, 451 Batch: BatchWithL1InclusionBlock{ 452 L1InclusionBlock: l1C, 453 Batch: &SingularBatch{ // we build l2A4, which has a timestamp of 2*4 = 8 higher than l2A0 454 ParentHash: l2A4.ParentHash, 455 EpochNum: rollup.Epoch(l2A4.L1Origin.Number), 456 EpochHash: l2A4.L1Origin.Hash, 457 Timestamp: l2A4.Time, 458 Transactions: nil, 459 }, 460 }, 461 Expected: BatchDrop, // dropped because it could have advanced the epoch to B 462 }, 463 { 464 Name: "empty tx included", 465 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 466 L2SafeHead: l2A0, 467 Batch: BatchWithL1InclusionBlock{ 468 L1InclusionBlock: l1B, 469 Batch: &SingularBatch{ 470 ParentHash: l2A1.ParentHash, 471 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 472 EpochHash: l2A1.L1Origin.Hash, 473 Timestamp: l2A1.Time, 474 Transactions: []hexutil.Bytes{ 475 []byte{}, // empty tx data 476 }, 477 }, 478 }, 479 Expected: BatchDrop, 480 }, 481 { 482 Name: "deposit tx included", 483 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 484 L2SafeHead: l2A0, 485 Batch: BatchWithL1InclusionBlock{ 486 L1InclusionBlock: l1B, 487 Batch: &SingularBatch{ 488 ParentHash: l2A1.ParentHash, 489 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 490 EpochHash: l2A1.L1Origin.Hash, 491 Timestamp: l2A1.Time, 492 Transactions: []hexutil.Bytes{ 493 []byte{types.DepositTxType, 0}, // piece of data alike to a deposit 494 }, 495 }, 496 }, 497 Expected: BatchDrop, 498 }, 499 { 500 Name: "valid batch same epoch", 501 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 502 L2SafeHead: l2A0, 503 Batch: BatchWithL1InclusionBlock{ 504 L1InclusionBlock: l1B, 505 Batch: &SingularBatch{ 506 ParentHash: l2A1.ParentHash, 507 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 508 EpochHash: l2A1.L1Origin.Hash, 509 Timestamp: l2A1.Time, 510 Transactions: []hexutil.Bytes{ 511 []byte{0x02, 0x42, 0x13, 0x37}, 512 []byte{0x02, 0xde, 0xad, 0xbe, 0xef}, 513 }, 514 }, 515 }, 516 Expected: BatchAccept, 517 }, 518 { 519 Name: "valid batch changing epoch", 520 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 521 L2SafeHead: l2A3, 522 Batch: BatchWithL1InclusionBlock{ 523 L1InclusionBlock: l1C, 524 Batch: &SingularBatch{ 525 ParentHash: l2B0.ParentHash, 526 EpochNum: rollup.Epoch(l2B0.L1Origin.Number), 527 EpochHash: l2B0.L1Origin.Hash, 528 Timestamp: l2B0.Time, 529 Transactions: []hexutil.Bytes{ 530 []byte{0x02, 0x42, 0x13, 0x37}, 531 []byte{0x02, 0xde, 0xad, 0xbe, 0xef}, 532 }, 533 }, 534 }, 535 Expected: BatchAccept, 536 }, 537 { 538 Name: "batch with L2 time before L1 time", 539 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 540 L2SafeHead: l2A2, 541 Batch: BatchWithL1InclusionBlock{ 542 L1InclusionBlock: l1B, 543 Batch: &SingularBatch{ // we build l2B0', which starts a new epoch too early 544 ParentHash: l2A2.Hash, 545 EpochNum: rollup.Epoch(l2B0.L1Origin.Number), 546 EpochHash: l2B0.L1Origin.Hash, 547 Timestamp: l2A2.Time + defaultConf.BlockTime, 548 Transactions: nil, 549 }, 550 }, 551 Expected: BatchDrop, 552 }, 553 } 554 spanBatchTestCases := []ValidBatchTestCase{ 555 { 556 Name: "missing L1 info", 557 L1Blocks: []eth.L1BlockRef{}, 558 L2SafeHead: l2A0, 559 Batch: BatchWithL1InclusionBlock{ 560 L1InclusionBlock: l1B, 561 Batch: NewSpanBatch([]*SingularBatch{ 562 { 563 ParentHash: l2A1.ParentHash, 564 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 565 EpochHash: l2A1.L1Origin.Hash, 566 Timestamp: l2A1.Time, 567 Transactions: nil, 568 }, 569 }), 570 }, 571 Expected: BatchUndecided, 572 ExpectedLog: "missing L1 block input, cannot proceed with batch checking", 573 DeltaTime: &minTs, 574 }, 575 { 576 Name: "future timestamp", 577 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 578 L2SafeHead: l2A0, 579 Batch: BatchWithL1InclusionBlock{ 580 L1InclusionBlock: l1B, 581 Batch: NewSpanBatch([]*SingularBatch{ 582 { 583 ParentHash: l2A1.ParentHash, 584 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 585 EpochHash: l2A1.L1Origin.Hash, 586 Timestamp: l2A1.Time + 1, // 1 too high 587 Transactions: nil, 588 }, 589 }), 590 }, 591 Expected: BatchFuture, 592 ExpectedLog: "received out-of-order batch for future processing after next batch", 593 DeltaTime: &minTs, 594 }, 595 { 596 Name: "misaligned timestamp", 597 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 598 L2SafeHead: l2A0, 599 Batch: BatchWithL1InclusionBlock{ 600 L1InclusionBlock: l1B, 601 Batch: NewSpanBatch([]*SingularBatch{ 602 { 603 ParentHash: l2A1.ParentHash, 604 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 605 EpochHash: l2A1.L1Origin.Hash, 606 Timestamp: l2A1.Time - 1, // block time is 2, so this is 1 too low 607 Transactions: nil, 608 }, 609 }), 610 }, 611 Expected: BatchDrop, 612 ExpectedLog: "span batch has no new blocks after safe head", 613 DeltaTime: &minTs, 614 }, 615 { 616 Name: "invalid parent block hash", 617 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 618 L2SafeHead: l2A0, 619 Batch: BatchWithL1InclusionBlock{ 620 L1InclusionBlock: l1B, 621 Batch: NewSpanBatch([]*SingularBatch{ 622 { 623 ParentHash: testutils.RandomHash(rng), 624 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 625 EpochHash: l2A1.L1Origin.Hash, 626 Timestamp: l2A1.Time, 627 Transactions: nil, 628 }, 629 }), 630 }, 631 Expected: BatchDrop, 632 ExpectedLog: "ignoring batch with mismatching parent hash", 633 DeltaTime: &minTs, 634 }, 635 { 636 Name: "sequence window expired", 637 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C, l1D, l1E, l1F}, 638 L2SafeHead: l2A0, 639 Batch: BatchWithL1InclusionBlock{ 640 L1InclusionBlock: l1F, 641 Batch: NewSpanBatch([]*SingularBatch{ 642 { 643 ParentHash: l2A1.ParentHash, 644 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 645 EpochHash: l2A1.L1Origin.Hash, 646 Timestamp: l2A1.Time, 647 Transactions: nil, 648 }, 649 }), 650 }, 651 Expected: BatchDrop, 652 ExpectedLog: "batch was included too late, sequence window expired", 653 DeltaTime: &minTs, 654 }, 655 { 656 Name: "epoch too old, but good parent hash and timestamp", // repeat of now outdated l2A3 data 657 L1Blocks: []eth.L1BlockRef{l1B, l1C, l1D}, 658 L2SafeHead: l2B0, // we already moved on to B 659 Batch: BatchWithL1InclusionBlock{ 660 L1InclusionBlock: l1C, 661 Batch: NewSpanBatch([]*SingularBatch{ 662 { 663 ParentHash: l2B0.Hash, // build on top of safe head to continue 664 EpochNum: rollup.Epoch(l2A3.L1Origin.Number), // epoch A is no longer valid 665 EpochHash: l2A3.L1Origin.Hash, 666 Timestamp: l2B0.Time + defaultConf.BlockTime, // pass the timestamp check to get too epoch check 667 Transactions: nil, 668 }, 669 { 670 EpochNum: rollup.Epoch(l1B.Number), 671 EpochHash: l1B.Hash, // pass the l1 origin check 672 Timestamp: l2B0.Time + defaultConf.BlockTime*2, 673 Transactions: nil, 674 }, 675 }), 676 }, 677 Expected: BatchDrop, 678 ExpectedLog: "dropped batch, epoch is too old", 679 DeltaTime: &minTs, 680 }, 681 { 682 Name: "insufficient L1 info for eager derivation", 683 L1Blocks: []eth.L1BlockRef{l1A}, // don't know about l1B yet 684 L2SafeHead: l2A3, 685 Batch: BatchWithL1InclusionBlock{ 686 L1InclusionBlock: l1C, 687 Batch: NewSpanBatch([]*SingularBatch{ 688 { 689 ParentHash: l2B0.ParentHash, 690 EpochNum: rollup.Epoch(l2B0.L1Origin.Number), 691 EpochHash: l2B0.L1Origin.Hash, 692 Timestamp: l2B0.Time, 693 Transactions: nil, 694 }, 695 }), 696 }, 697 Expected: BatchUndecided, 698 ExpectedLog: "eager batch wants to advance epoch, but could not without more L1 blocks", 699 DeltaTime: &minTs, 700 }, 701 { 702 Name: "insufficient L1 info for eager derivation - long span", 703 L1Blocks: []eth.L1BlockRef{l1A}, // don't know about l1B yet 704 L2SafeHead: l2A2, 705 Batch: BatchWithL1InclusionBlock{ 706 L1InclusionBlock: l1C, 707 Batch: NewSpanBatch([]*SingularBatch{ 708 { 709 ParentHash: l2A3.ParentHash, 710 EpochNum: rollup.Epoch(l2A3.L1Origin.Number), 711 EpochHash: l2A3.L1Origin.Hash, 712 Timestamp: l2A3.Time, 713 Transactions: nil, 714 }, 715 { 716 ParentHash: l2B0.ParentHash, 717 EpochNum: rollup.Epoch(l2B0.L1Origin.Number), 718 EpochHash: l2B0.L1Origin.Hash, 719 Timestamp: l2B0.Time, 720 Transactions: nil, 721 }, 722 }), 723 }, 724 Expected: BatchUndecided, 725 ExpectedLog: "need more l1 blocks to check entire origins of span batch", 726 DeltaTime: &minTs, 727 }, 728 { 729 Name: "epoch too new", 730 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C, l1D}, 731 L2SafeHead: l2A3, 732 Batch: BatchWithL1InclusionBlock{ 733 L1InclusionBlock: l1D, 734 Batch: NewSpanBatch([]*SingularBatch{ 735 { 736 ParentHash: l2B0.ParentHash, 737 EpochNum: rollup.Epoch(l1C.Number), // invalid, we need to adopt epoch B before C 738 EpochHash: l1C.Hash, 739 Timestamp: l2B0.Time, 740 Transactions: nil, 741 }, 742 }), 743 }, 744 Expected: BatchDrop, 745 ExpectedLog: "batch is for future epoch too far ahead, while it has the next timestamp, so it must be invalid", 746 DeltaTime: &minTs, 747 }, 748 { 749 Name: "epoch hash wrong", 750 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 751 L2SafeHead: l2A3, 752 Batch: BatchWithL1InclusionBlock{ 753 L1InclusionBlock: l1C, 754 Batch: NewSpanBatch([]*SingularBatch{ 755 { 756 ParentHash: l2B0.ParentHash, 757 EpochNum: rollup.Epoch(l2B0.L1Origin.Number), 758 EpochHash: l1A.Hash, // invalid, epoch hash should be l1B 759 Timestamp: l2B0.Time, 760 Transactions: nil, 761 }, 762 }), 763 }, 764 Expected: BatchDrop, 765 ExpectedLog: "batch is for different L1 chain, epoch hash does not match", 766 DeltaTime: &minTs, 767 }, 768 { 769 Name: "epoch hash wrong - long span", 770 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 771 L2SafeHead: l2A2, 772 Batch: BatchWithL1InclusionBlock{ 773 L1InclusionBlock: l1C, 774 Batch: NewSpanBatch([]*SingularBatch{ 775 { // valid batch 776 ParentHash: l2A3.ParentHash, 777 EpochNum: rollup.Epoch(l2A3.L1Origin.Number), 778 EpochHash: l1A.Hash, 779 Timestamp: l2A3.Time, 780 Transactions: nil, 781 }, 782 { 783 ParentHash: l2B0.ParentHash, 784 EpochNum: rollup.Epoch(l2B0.L1Origin.Number), 785 EpochHash: l1A.Hash, // invalid, epoch hash should be l1B 786 Timestamp: l2B0.Time, 787 Transactions: nil, 788 }, 789 }), 790 }, 791 Expected: BatchDrop, 792 ExpectedLog: "batch is for different L1 chain, epoch hash does not match", 793 DeltaTime: &minTs, 794 }, 795 { 796 Name: "sequencer time drift on same epoch with non-empty txs", 797 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 798 L2SafeHead: l2A3, 799 Batch: BatchWithL1InclusionBlock{ 800 L1InclusionBlock: l1B, 801 Batch: NewSpanBatch([]*SingularBatch{ 802 { // we build l2A4, which has a timestamp of 2*4 = 8 higher than l2A0 803 ParentHash: l2A4.ParentHash, 804 EpochNum: rollup.Epoch(l2A4.L1Origin.Number), 805 EpochHash: l2A4.L1Origin.Hash, 806 Timestamp: l2A4.Time, 807 Transactions: []hexutil.Bytes{randTxData}, 808 }, 809 }), 810 }, 811 Expected: BatchDrop, 812 ExpectedLog: "batch exceeded sequencer time drift, sequencer must adopt new L1 origin to include transactions again", 813 DeltaTime: &minTs, 814 }, 815 { 816 Name: "sequencer time drift on same epoch with non-empty txs - long span", 817 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 818 L2SafeHead: l2A2, 819 Batch: BatchWithL1InclusionBlock{ 820 L1InclusionBlock: l1B, 821 Batch: NewSpanBatch([]*SingularBatch{ 822 { // valid batch 823 ParentHash: l2A3.ParentHash, 824 EpochNum: rollup.Epoch(l2A3.L1Origin.Number), 825 EpochHash: l2A3.L1Origin.Hash, 826 Timestamp: l2A3.Time, 827 Transactions: []hexutil.Bytes{randTxData}, 828 }, 829 { // we build l2A4, which has a timestamp of 2*4 = 8 higher than l2A0 830 ParentHash: l2A4.ParentHash, 831 EpochNum: rollup.Epoch(l2A4.L1Origin.Number), 832 EpochHash: l2A4.L1Origin.Hash, 833 Timestamp: l2A4.Time, 834 Transactions: []hexutil.Bytes{randTxData}, 835 }, 836 }), 837 }, 838 Expected: BatchDrop, 839 ExpectedLog: "batch exceeded sequencer time drift, sequencer must adopt new L1 origin to include transactions again", 840 DeltaTime: &minTs, 841 }, 842 { 843 Name: "sequencer time drift on changing epoch with non-empty txs", 844 L1Blocks: []eth.L1BlockRef{l1X, l1Y, l1Z}, 845 L2SafeHead: l2X0, 846 Batch: BatchWithL1InclusionBlock{ 847 L1InclusionBlock: l1Z, 848 Batch: NewSpanBatch([]*SingularBatch{ 849 { 850 ParentHash: l2Y0.ParentHash, 851 EpochNum: rollup.Epoch(l2Y0.L1Origin.Number), 852 EpochHash: l2Y0.L1Origin.Hash, 853 Timestamp: l2Y0.Time, // valid, but more than 6 ahead of l1Y.Time 854 Transactions: []hexutil.Bytes{randTxData}, 855 }, 856 }), 857 }, 858 Expected: BatchDrop, 859 ExpectedLog: "batch exceeded sequencer time drift, sequencer must adopt new L1 origin to include transactions again", 860 DeltaTime: &minTs, 861 }, 862 { 863 Name: "sequencer time drift on same epoch with empty txs and late next epoch", 864 L1Blocks: []eth.L1BlockRef{l1A, l1BLate}, 865 L2SafeHead: l2A3, 866 Batch: BatchWithL1InclusionBlock{ 867 L1InclusionBlock: l1BLate, 868 Batch: NewSpanBatch([]*SingularBatch{ 869 { // l2A4 time < l1BLate time, so we cannot adopt origin B yet 870 ParentHash: l2A4.ParentHash, 871 EpochNum: rollup.Epoch(l2A4.L1Origin.Number), 872 EpochHash: l2A4.L1Origin.Hash, 873 Timestamp: l2A4.Time, 874 Transactions: nil, 875 }, 876 }), 877 }, 878 Expected: BatchAccept, // accepted because empty & preserving L2 time invariant 879 DeltaTime: &minTs, 880 }, 881 { 882 Name: "sequencer time drift on changing epoch with empty txs", 883 L1Blocks: []eth.L1BlockRef{l1X, l1Y, l1Z}, 884 L2SafeHead: l2X0, 885 Batch: BatchWithL1InclusionBlock{ 886 L1InclusionBlock: l1Z, 887 Batch: NewSpanBatch([]*SingularBatch{ 888 { 889 ParentHash: l2Y0.ParentHash, 890 EpochNum: rollup.Epoch(l2Y0.L1Origin.Number), 891 EpochHash: l2Y0.L1Origin.Hash, 892 Timestamp: l2Y0.Time, // valid, but more than 6 ahead of l1Y.Time 893 Transactions: nil, 894 }, 895 { 896 ParentHash: l2Z0.ParentHash, 897 EpochNum: rollup.Epoch(l2Z0.L1Origin.Number), 898 EpochHash: l2Z0.L1Origin.Hash, 899 Timestamp: l2Z0.Time, // valid, but more than 6 ahead of l1Y.Time 900 Transactions: nil, 901 }, 902 }), 903 }, 904 Expected: BatchAccept, // accepted because empty & still advancing epoch 905 DeltaTime: &minTs, 906 NotExpectedLog: "continuing with empty batch before late L1 block to preserve L2 time invariant", 907 }, 908 { 909 Name: "sequencer time drift on same epoch with empty txs and no next epoch in sight yet", 910 L1Blocks: []eth.L1BlockRef{l1A}, 911 L2SafeHead: l2A3, 912 Batch: BatchWithL1InclusionBlock{ 913 L1InclusionBlock: l1B, 914 Batch: NewSpanBatch([]*SingularBatch{ 915 { // we build l2A4, which has a timestamp of 2*4 = 8 higher than l2A0 916 ParentHash: l2A4.ParentHash, 917 EpochNum: rollup.Epoch(l2A4.L1Origin.Number), 918 EpochHash: l2A4.L1Origin.Hash, 919 Timestamp: l2A4.Time, 920 Transactions: nil, 921 }, 922 }), 923 }, 924 Expected: BatchUndecided, // we have to wait till the next epoch is in sight to check the time 925 ExpectedLog: "without the next L1 origin we cannot determine yet if this empty batch that exceeds the time drift is still valid", 926 DeltaTime: &minTs, 927 }, 928 { 929 Name: "sequencer time drift on same epoch with empty txs and no next epoch in sight yet - long span", 930 L1Blocks: []eth.L1BlockRef{l1A}, 931 L2SafeHead: l2A2, 932 Batch: BatchWithL1InclusionBlock{ 933 L1InclusionBlock: l1B, 934 Batch: NewSpanBatch([]*SingularBatch{ 935 { // valid batch 936 ParentHash: l2A3.ParentHash, 937 EpochNum: rollup.Epoch(l2A3.L1Origin.Number), 938 EpochHash: l2A3.L1Origin.Hash, 939 Timestamp: l2A3.Time, 940 Transactions: nil, 941 }, 942 { // we build l2A4, which has a timestamp of 2*4 = 8 higher than l2A0 943 ParentHash: l2A4.ParentHash, 944 EpochNum: rollup.Epoch(l2A4.L1Origin.Number), 945 EpochHash: l2A4.L1Origin.Hash, 946 Timestamp: l2A4.Time, 947 Transactions: nil, 948 }, 949 }), 950 }, 951 Expected: BatchUndecided, // we have to wait till the next epoch is in sight to check the time 952 ExpectedLog: "without the next L1 origin we cannot determine yet if this empty batch that exceeds the time drift is still valid", 953 DeltaTime: &minTs, 954 }, 955 { 956 Name: "sequencer time drift on same epoch with empty txs and but in-sight epoch that invalidates it", 957 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 958 L2SafeHead: l2A3, 959 Batch: BatchWithL1InclusionBlock{ 960 L1InclusionBlock: l1C, 961 Batch: NewSpanBatch([]*SingularBatch{ 962 { // we build l2A4, which has a timestamp of 2*4 = 8 higher than l2A0 963 ParentHash: l2A4.ParentHash, 964 EpochNum: rollup.Epoch(l2A4.L1Origin.Number), 965 EpochHash: l2A4.L1Origin.Hash, 966 Timestamp: l2A4.Time, 967 Transactions: nil, 968 }, 969 }), 970 }, 971 Expected: BatchDrop, // dropped because it could have advanced the epoch to B 972 ExpectedLog: "batch exceeded sequencer time drift without adopting next origin, and next L1 origin would have been valid", 973 DeltaTime: &minTs, 974 }, 975 { 976 Name: "sequencer time drift on same epoch with empty txs and but in-sight epoch that invalidates it - long span", 977 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 978 L2SafeHead: l2A2, 979 Batch: BatchWithL1InclusionBlock{ 980 L1InclusionBlock: l1C, 981 Batch: NewSpanBatch([]*SingularBatch{ 982 { // valid batch 983 ParentHash: l2A3.ParentHash, 984 EpochNum: rollup.Epoch(l2A3.L1Origin.Number), 985 EpochHash: l2A3.L1Origin.Hash, 986 Timestamp: l2A3.Time, 987 Transactions: nil, 988 }, 989 { // we build l2A4, which has a timestamp of 2*4 = 8 higher than l2A0 990 ParentHash: l2A4.ParentHash, 991 EpochNum: rollup.Epoch(l2A4.L1Origin.Number), 992 EpochHash: l2A4.L1Origin.Hash, 993 Timestamp: l2A4.Time, 994 Transactions: nil, 995 }, 996 }), 997 }, 998 Expected: BatchDrop, // dropped because it could have advanced the epoch to B 999 ExpectedLog: "batch exceeded sequencer time drift without adopting next origin, and next L1 origin would have been valid", 1000 DeltaTime: &minTs, 1001 }, 1002 { 1003 Name: "empty tx included", 1004 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1005 L2SafeHead: l2A0, 1006 Batch: BatchWithL1InclusionBlock{ 1007 L1InclusionBlock: l1B, 1008 Batch: NewSpanBatch([]*SingularBatch{ 1009 { 1010 ParentHash: l2A1.ParentHash, 1011 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 1012 EpochHash: l2A1.L1Origin.Hash, 1013 Timestamp: l2A1.Time, 1014 Transactions: []hexutil.Bytes{ 1015 []byte{}, // empty tx data 1016 }, 1017 }, 1018 }), 1019 }, 1020 Expected: BatchDrop, 1021 ExpectedLog: "transaction data must not be empty, but found empty tx", 1022 DeltaTime: &minTs, 1023 }, 1024 { 1025 Name: "deposit tx included", 1026 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1027 L2SafeHead: l2A0, 1028 Batch: BatchWithL1InclusionBlock{ 1029 L1InclusionBlock: l1B, 1030 Batch: NewSpanBatch([]*SingularBatch{ 1031 { 1032 ParentHash: l2A1.ParentHash, 1033 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 1034 EpochHash: l2A1.L1Origin.Hash, 1035 Timestamp: l2A1.Time, 1036 Transactions: []hexutil.Bytes{ 1037 []byte{types.DepositTxType, 0}, // piece of data alike to a deposit 1038 }, 1039 }, 1040 }), 1041 }, 1042 Expected: BatchDrop, 1043 ExpectedLog: "sequencers may not embed any deposits into batch data, but found tx that has one", 1044 DeltaTime: &minTs, 1045 }, 1046 { 1047 Name: "valid batch same epoch", 1048 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1049 L2SafeHead: l2A0, 1050 Batch: BatchWithL1InclusionBlock{ 1051 L1InclusionBlock: l1B, 1052 Batch: NewSpanBatch([]*SingularBatch{ 1053 { 1054 ParentHash: l2A1.ParentHash, 1055 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 1056 EpochHash: l2A1.L1Origin.Hash, 1057 Timestamp: l2A1.Time, 1058 Transactions: []hexutil.Bytes{randTxData}, 1059 }, 1060 }), 1061 }, 1062 Expected: BatchAccept, 1063 DeltaTime: &minTs, 1064 }, 1065 { 1066 Name: "valid batch changing epoch", 1067 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 1068 L2SafeHead: l2A3, 1069 Batch: BatchWithL1InclusionBlock{ 1070 L1InclusionBlock: l1C, 1071 Batch: NewSpanBatch([]*SingularBatch{ 1072 { 1073 ParentHash: l2B0.ParentHash, 1074 EpochNum: rollup.Epoch(l2B0.L1Origin.Number), 1075 EpochHash: l2B0.L1Origin.Hash, 1076 Timestamp: l2B0.Time, 1077 Transactions: []hexutil.Bytes{randTxData}, 1078 }, 1079 }), 1080 }, 1081 Expected: BatchAccept, 1082 DeltaTime: &minTs, 1083 }, 1084 { 1085 Name: "batch with L2 time before L1 time", 1086 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 1087 L2SafeHead: l2A2, 1088 Batch: BatchWithL1InclusionBlock{ 1089 L1InclusionBlock: l1B, 1090 Batch: NewSpanBatch([]*SingularBatch{ 1091 { // we build l2B0, which starts a new epoch too early 1092 ParentHash: l2A2.Hash, 1093 EpochNum: rollup.Epoch(l2B0.L1Origin.Number), 1094 EpochHash: l2B0.L1Origin.Hash, 1095 Timestamp: l2A2.Time + defaultConf.BlockTime, 1096 Transactions: nil, 1097 }, 1098 }), 1099 }, 1100 Expected: BatchDrop, 1101 ExpectedLog: "block timestamp is less than L1 origin timestamp", 1102 DeltaTime: &minTs, 1103 }, 1104 { 1105 Name: "batch with L2 time before L1 time - long span", 1106 L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, 1107 L2SafeHead: l2A1, 1108 Batch: BatchWithL1InclusionBlock{ 1109 L1InclusionBlock: l1B, 1110 Batch: NewSpanBatch([]*SingularBatch{ 1111 { // valid batch 1112 ParentHash: l2A1.Hash, 1113 EpochNum: rollup.Epoch(l2A2.L1Origin.Number), 1114 EpochHash: l2A2.L1Origin.Hash, 1115 Timestamp: l2A2.Time, 1116 Transactions: nil, 1117 }, 1118 { // we build l2B0, which starts a new epoch too early 1119 ParentHash: l2A2.Hash, 1120 EpochNum: rollup.Epoch(l2B0.L1Origin.Number), 1121 EpochHash: l2B0.L1Origin.Hash, 1122 Timestamp: l2A2.Time + defaultConf.BlockTime, 1123 Transactions: nil, 1124 }, 1125 }), 1126 }, 1127 Expected: BatchDrop, 1128 ExpectedLog: "block timestamp is less than L1 origin timestamp", 1129 DeltaTime: &minTs, 1130 }, 1131 { 1132 Name: "valid overlapping batch", 1133 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1134 L2SafeHead: l2A2, 1135 Batch: BatchWithL1InclusionBlock{ 1136 L1InclusionBlock: l1B, 1137 Batch: NewSpanBatch([]*SingularBatch{ 1138 { 1139 ParentHash: l2A1.Hash, 1140 EpochNum: rollup.Epoch(l2A2.L1Origin.Number), 1141 EpochHash: l2A2.L1Origin.Hash, 1142 Timestamp: l2A2.Time, 1143 Transactions: nil, 1144 }, 1145 { 1146 ParentHash: l2A2.Hash, 1147 EpochNum: rollup.Epoch(l2A3.L1Origin.Number), 1148 EpochHash: l2A3.L1Origin.Hash, 1149 Timestamp: l2A3.Time, 1150 Transactions: nil, 1151 }, 1152 }), 1153 }, 1154 Expected: BatchAccept, 1155 DeltaTime: &minTs, 1156 }, 1157 { 1158 Name: "longer overlapping batch", 1159 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1160 L2SafeHead: l2A2, 1161 Batch: BatchWithL1InclusionBlock{ 1162 L1InclusionBlock: l1B, 1163 Batch: NewSpanBatch([]*SingularBatch{ 1164 { 1165 ParentHash: l2A0.Hash, 1166 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 1167 EpochHash: l2A1.L1Origin.Hash, 1168 Timestamp: l2A1.Time, 1169 Transactions: nil, 1170 }, 1171 { 1172 ParentHash: l2A1.Hash, 1173 EpochNum: rollup.Epoch(l2A2.L1Origin.Number), 1174 EpochHash: l2A2.L1Origin.Hash, 1175 Timestamp: l2A2.Time, 1176 Transactions: nil, 1177 }, 1178 { 1179 ParentHash: l2A2.Hash, 1180 EpochNum: rollup.Epoch(l2A3.L1Origin.Number), 1181 EpochHash: l2A3.L1Origin.Hash, 1182 Timestamp: l2A3.Time, 1183 Transactions: nil, 1184 }, 1185 }), 1186 }, 1187 Expected: BatchAccept, 1188 DeltaTime: &minTs, 1189 }, 1190 { 1191 Name: "fully overlapping batch", 1192 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1193 L2SafeHead: l2A2, 1194 Batch: BatchWithL1InclusionBlock{ 1195 L1InclusionBlock: l1B, 1196 Batch: NewSpanBatch([]*SingularBatch{ 1197 { 1198 ParentHash: l2A0.Hash, 1199 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 1200 EpochHash: l2A1.L1Origin.Hash, 1201 Timestamp: l2A1.Time, 1202 Transactions: nil, 1203 }, 1204 { 1205 ParentHash: l2A1.Hash, 1206 EpochNum: rollup.Epoch(l2A2.L1Origin.Number), 1207 EpochHash: l2A2.L1Origin.Hash, 1208 Timestamp: l2A2.Time, 1209 Transactions: nil, 1210 }, 1211 }), 1212 }, 1213 Expected: BatchDrop, 1214 ExpectedLog: "span batch has no new blocks after safe head", 1215 DeltaTime: &minTs, 1216 }, 1217 { 1218 Name: "overlapping batch with invalid parent hash", 1219 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1220 L2SafeHead: l2A2, 1221 Batch: BatchWithL1InclusionBlock{ 1222 L1InclusionBlock: l1B, 1223 Batch: NewSpanBatch([]*SingularBatch{ 1224 { 1225 ParentHash: l2A0.Hash, 1226 EpochNum: rollup.Epoch(l2A2.L1Origin.Number), 1227 EpochHash: l2A2.L1Origin.Hash, 1228 Timestamp: l2A2.Time, 1229 Transactions: nil, 1230 }, 1231 { 1232 ParentHash: l2A2.Hash, 1233 EpochNum: rollup.Epoch(l2A3.L1Origin.Number), 1234 EpochHash: l2A3.L1Origin.Hash, 1235 Timestamp: l2A3.Time, 1236 Transactions: nil, 1237 }, 1238 }), 1239 }, 1240 Expected: BatchDrop, 1241 ExpectedLog: "ignoring batch with mismatching parent hash", 1242 DeltaTime: &minTs, 1243 }, 1244 { 1245 Name: "overlapping batch with invalid origin number", 1246 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1247 L2SafeHead: l2A2, 1248 Batch: BatchWithL1InclusionBlock{ 1249 L1InclusionBlock: l1B, 1250 Batch: NewSpanBatch([]*SingularBatch{ 1251 { 1252 ParentHash: l2A1.Hash, 1253 EpochNum: rollup.Epoch(l2A2.L1Origin.Number) + 1, 1254 EpochHash: l2A2.L1Origin.Hash, 1255 Timestamp: l2A2.Time, 1256 Transactions: nil, 1257 }, 1258 { 1259 ParentHash: l2A2.Hash, 1260 EpochNum: rollup.Epoch(l2A3.L1Origin.Number), 1261 EpochHash: l2A3.L1Origin.Hash, 1262 Timestamp: l2A3.Time, 1263 Transactions: nil, 1264 }, 1265 }), 1266 }, 1267 Expected: BatchDrop, 1268 ExpectedLog: "overlapped block's L1 origin number does not match", 1269 DeltaTime: &minTs, 1270 }, 1271 { 1272 Name: "overlapping batch with invalid tx", 1273 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1274 L2SafeHead: l2A2, 1275 Batch: BatchWithL1InclusionBlock{ 1276 L1InclusionBlock: l1B, 1277 Batch: NewSpanBatch([]*SingularBatch{ 1278 { 1279 ParentHash: l2A1.Hash, 1280 EpochNum: rollup.Epoch(l2A2.L1Origin.Number), 1281 EpochHash: l2A2.L1Origin.Hash, 1282 Timestamp: l2A2.Time, 1283 Transactions: []hexutil.Bytes{randTxData}, 1284 }, 1285 { 1286 ParentHash: l2A2.Hash, 1287 EpochNum: rollup.Epoch(l2A3.L1Origin.Number), 1288 EpochHash: l2A3.L1Origin.Hash, 1289 Timestamp: l2A3.Time, 1290 Transactions: nil, 1291 }, 1292 }), 1293 }, 1294 Expected: BatchDrop, 1295 ExpectedLog: "overlapped block's tx count does not match", 1296 DeltaTime: &minTs, 1297 }, 1298 { 1299 Name: "overlapping batch l2 fetcher error", 1300 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1301 L2SafeHead: l2A1, 1302 Batch: BatchWithL1InclusionBlock{ 1303 L1InclusionBlock: l1B, 1304 Batch: NewSpanBatch([]*SingularBatch{ 1305 { 1306 ParentHash: l2A0.ParentHash, 1307 EpochNum: rollup.Epoch(l2A0.L1Origin.Number), 1308 EpochHash: l2A0.L1Origin.Hash, 1309 Timestamp: l2A0.Time, 1310 Transactions: nil, 1311 }, 1312 { 1313 ParentHash: l2A0.Hash, 1314 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 1315 EpochHash: l2A1.L1Origin.Hash, 1316 Timestamp: l2A1.Time, 1317 Transactions: nil, 1318 }, 1319 { 1320 ParentHash: l2A1.Hash, 1321 EpochNum: rollup.Epoch(l2A2.L1Origin.Number), 1322 EpochHash: l2A2.L1Origin.Hash, 1323 Timestamp: l2A2.Time, 1324 Transactions: nil, 1325 }, 1326 }), 1327 }, 1328 Expected: BatchUndecided, 1329 ExpectedLog: "failed to fetch L2 block", 1330 DeltaTime: &minTs, 1331 }, 1332 { 1333 Name: "short block time", 1334 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1335 L2SafeHead: l2A0, 1336 Batch: BatchWithL1InclusionBlock{ 1337 L1InclusionBlock: l1B, 1338 Batch: NewSpanBatch([]*SingularBatch{ 1339 { 1340 ParentHash: l2A0.Hash, 1341 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 1342 EpochHash: l2A1.L1Origin.Hash, 1343 Timestamp: l2A0.Time + 1, 1344 Transactions: nil, 1345 }, 1346 { 1347 ParentHash: l2A1.Hash, 1348 EpochNum: rollup.Epoch(l2A2.L1Origin.Number), 1349 EpochHash: l2A2.L1Origin.Hash, 1350 Timestamp: l2A1.Time + 1, 1351 Transactions: nil, 1352 }, 1353 }), 1354 }, 1355 Expected: BatchDrop, 1356 ExpectedLog: "batch has misaligned timestamp, block time is too short", 1357 DeltaTime: &minTs, 1358 }, 1359 { 1360 Name: "misaligned batch", 1361 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1362 L2SafeHead: l2A0, 1363 Batch: BatchWithL1InclusionBlock{ 1364 L1InclusionBlock: l1B, 1365 Batch: NewSpanBatch([]*SingularBatch{ 1366 { 1367 ParentHash: l2A0.Hash, 1368 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 1369 EpochHash: l2A1.L1Origin.Hash, 1370 Timestamp: l2A0.Time - 1, 1371 Transactions: nil, 1372 }, 1373 { 1374 ParentHash: l2A1.Hash, 1375 EpochNum: rollup.Epoch(l2A2.L1Origin.Number), 1376 EpochHash: l2A2.L1Origin.Hash, 1377 Timestamp: l2A1.Time, 1378 Transactions: nil, 1379 }, 1380 }), 1381 }, 1382 Expected: BatchDrop, 1383 ExpectedLog: "batch has misaligned timestamp, not overlapped exactly", 1384 DeltaTime: &minTs, 1385 }, 1386 { 1387 Name: "failed to fetch overlapping block payload", 1388 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1389 L2SafeHead: l2A3, 1390 Batch: BatchWithL1InclusionBlock{ 1391 L1InclusionBlock: l1B, 1392 Batch: NewSpanBatch([]*SingularBatch{ 1393 { 1394 ParentHash: l2A2.Hash, 1395 EpochNum: rollup.Epoch(l2A3.L1Origin.Number), 1396 EpochHash: l2A3.L1Origin.Hash, 1397 Timestamp: l2A3.Time, 1398 Transactions: nil, 1399 }, 1400 { 1401 ParentHash: l2A3.Hash, 1402 EpochNum: rollup.Epoch(l2B0.L1Origin.Number), 1403 EpochHash: l2B0.L1Origin.Hash, 1404 Timestamp: l2B0.Time, 1405 Transactions: nil, 1406 }, 1407 }), 1408 }, 1409 Expected: BatchUndecided, 1410 ExpectedLog: "failed to fetch L2 block payload", 1411 DeltaTime: &minTs, 1412 }, 1413 { 1414 Name: "singular batch before hard fork", 1415 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1416 L2SafeHead: l2A0, 1417 Batch: BatchWithL1InclusionBlock{ 1418 L1InclusionBlock: l1B, 1419 Batch: &SingularBatch{ 1420 ParentHash: l2A1.ParentHash, 1421 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 1422 EpochHash: l2A1.L1Origin.Hash, 1423 Timestamp: l2A1.Time, 1424 Transactions: []hexutil.Bytes{randTxData}, 1425 }, 1426 }, 1427 DeltaTime: &l1B.Time, 1428 Expected: BatchAccept, 1429 }, 1430 { 1431 Name: "span batch before hard fork", 1432 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1433 L2SafeHead: l2A0, 1434 Batch: BatchWithL1InclusionBlock{ 1435 L1InclusionBlock: l1B, 1436 Batch: NewSpanBatch([]*SingularBatch{ 1437 { 1438 ParentHash: l2A1.ParentHash, 1439 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 1440 EpochHash: l2A1.L1Origin.Hash, 1441 Timestamp: l2A1.Time, 1442 Transactions: []hexutil.Bytes{randTxData}, 1443 }, 1444 }), 1445 }, 1446 DeltaTime: &l1B.Time, 1447 Expected: BatchDrop, 1448 ExpectedLog: "received SpanBatch with L1 origin before Delta hard fork", 1449 }, 1450 { 1451 Name: "singular batch after hard fork", 1452 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1453 L2SafeHead: l2A0, 1454 Batch: BatchWithL1InclusionBlock{ 1455 L1InclusionBlock: l1B, 1456 Batch: &SingularBatch{ 1457 ParentHash: l2A1.ParentHash, 1458 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 1459 EpochHash: l2A1.L1Origin.Hash, 1460 Timestamp: l2A1.Time, 1461 Transactions: []hexutil.Bytes{randTxData}, 1462 }, 1463 }, 1464 DeltaTime: &l1A.Time, 1465 Expected: BatchAccept, 1466 }, 1467 { 1468 Name: "span batch after hard fork", 1469 L1Blocks: []eth.L1BlockRef{l1A, l1B}, 1470 L2SafeHead: l2A0, 1471 Batch: BatchWithL1InclusionBlock{ 1472 L1InclusionBlock: l1B, 1473 Batch: NewSpanBatch([]*SingularBatch{ 1474 { 1475 ParentHash: l2A1.ParentHash, 1476 EpochNum: rollup.Epoch(l2A1.L1Origin.Number), 1477 EpochHash: l2A1.L1Origin.Hash, 1478 Timestamp: l2A1.Time, 1479 Transactions: []hexutil.Bytes{randTxData}, 1480 }, 1481 }), 1482 }, 1483 DeltaTime: &l1A.Time, 1484 Expected: BatchAccept, 1485 }, 1486 } 1487 1488 // Log level can be increased for debugging purposes 1489 logger, logs := testlog.CaptureLogger(t, log.LevelDebug) 1490 1491 l2Client := testutils.MockL2Client{} 1492 var nilErr error 1493 tempErr := errors.New("temp error") 1494 // will return an error for block #99 (parent of l2A0) 1495 l2Client.Mock.On("L2BlockRefByNumber", l2A0.Number-1).Return(eth.L2BlockRef{}, &tempErr) 1496 // will return an error for l2A3 1497 l2Client.Mock.On("PayloadByNumber", l2A3.Number).Return(ð.ExecutionPayloadEnvelope{}, &tempErr) 1498 1499 // make payloads for L2 blocks and set as expected return value of MockL2Client 1500 for _, l2Block := range []eth.L2BlockRef{l2A0, l2A1, l2A2, l2B0} { 1501 l2Client.ExpectL2BlockRefByNumber(l2Block.Number, l2Block, nil) 1502 txData := l1InfoDepositTx(t, l2Block.L1Origin.Number) 1503 payload := eth.ExecutionPayloadEnvelope{ 1504 ExecutionPayload: ð.ExecutionPayload{ 1505 ParentHash: l2Block.ParentHash, 1506 BlockNumber: hexutil.Uint64(l2Block.Number), 1507 Timestamp: hexutil.Uint64(l2Block.Time), 1508 BlockHash: l2Block.Hash, 1509 Transactions: []hexutil.Bytes{txData}, 1510 }, 1511 } 1512 l2Client.Mock.On("L2BlockRefByNumber", l2Block.Number).Return(l2Block, &nilErr) 1513 l2Client.Mock.On("PayloadByNumber", l2Block.Number).Return(&payload, &nilErr) 1514 } 1515 1516 runTestCase := func(t *testing.T, testCase ValidBatchTestCase) { 1517 ctx := context.Background() 1518 rcfg := defaultConf 1519 if testCase.DeltaTime != nil { 1520 rcfg.DeltaTime = testCase.DeltaTime 1521 } 1522 validity := CheckBatch(ctx, &rcfg, logger, testCase.L1Blocks, testCase.L2SafeHead, &testCase.Batch, &l2Client) 1523 require.Equal(t, testCase.Expected, validity, "batch check must return expected validity level") 1524 if expLog := testCase.ExpectedLog; expLog != "" { 1525 // Check if ExpectedLog is contained in the log buffer 1526 containsFilter := testlog.NewMessageContainsFilter(expLog) 1527 if l := logs.FindLog(containsFilter); l == nil { 1528 t.Errorf("Expected log message was not logged: %q", expLog) 1529 } 1530 } 1531 if notExpLog := testCase.NotExpectedLog; notExpLog != "" { 1532 // Check if NotExpectedLog is contained in the log buffer 1533 containsFilter := testlog.NewMessageContainsFilter(notExpLog) 1534 if l := logs.FindLog(containsFilter); l != nil { 1535 t.Errorf("Unexpected log message containing %q was logged: %q", notExpLog, l.Message) 1536 } 1537 } 1538 logs.Clear() 1539 } 1540 1541 // Run singular batch test cases 1542 for _, testCase := range singularBatchTestCases { 1543 t.Run("singular_"+testCase.Name, func(t *testing.T) { 1544 runTestCase(t, testCase) 1545 }) 1546 } 1547 1548 // Run span batch test cases 1549 for _, testCase := range spanBatchTestCases { 1550 t.Run("span_"+testCase.Name, func(t *testing.T) { 1551 runTestCase(t, testCase) 1552 }) 1553 } 1554 1555 // ====== Test different TX for overlapping batches ====== 1556 l2Client.ExpectL2BlockRefByNumber(l2B1.Number, l2B1, nil) 1557 txData := l1InfoDepositTx(t, l2B1.L1Origin.Number) 1558 randTx = testutils.RandomTx(rng, new(big.Int).SetUint64(rng.Uint64()), signer) 1559 randTxData, _ = randTx.MarshalBinary() 1560 payload := eth.ExecutionPayloadEnvelope{ 1561 ExecutionPayload: ð.ExecutionPayload{ 1562 ParentHash: l2B0.Hash, 1563 BlockNumber: hexutil.Uint64(l2B1.Number), 1564 Timestamp: hexutil.Uint64(l2B1.Time), 1565 BlockHash: l2B1.Hash, 1566 Transactions: []hexutil.Bytes{txData, randTxData}, 1567 }, 1568 } 1569 l2Client.Mock.On("PayloadByNumber", l2B1.Number).Return(&payload, &nilErr).Once() 1570 1571 randTx = testutils.RandomTx(rng, new(big.Int).SetUint64(rng.Uint64()), signer) 1572 randTxData, _ = randTx.MarshalBinary() 1573 differentTxtestCase := ValidBatchTestCase{ 1574 Name: "different_tx_overlapping_batch", 1575 L1Blocks: []eth.L1BlockRef{l1B}, 1576 L2SafeHead: l2B1, 1577 Batch: BatchWithL1InclusionBlock{ 1578 L1InclusionBlock: l1B, 1579 Batch: NewSpanBatch([]*SingularBatch{ 1580 { 1581 ParentHash: l2B0.Hash, 1582 EpochNum: rollup.Epoch(l2B1.L1Origin.Number), 1583 EpochHash: l2B1.L1Origin.Hash, 1584 Timestamp: l2B1.Time, 1585 Transactions: []hexutil.Bytes{randTxData}, // Random generated TX that does not match overlapping block 1586 }, 1587 { 1588 ParentHash: l2B1.Hash, 1589 EpochNum: rollup.Epoch(l2B2.L1Origin.Number), 1590 EpochHash: l2B2.L1Origin.Hash, 1591 Timestamp: l2B2.Time, 1592 Transactions: nil, 1593 }, 1594 }), 1595 }, 1596 Expected: BatchDrop, 1597 ExpectedLog: "overlapped block's transaction does not match", 1598 DeltaTime: &minTs, 1599 } 1600 1601 t.Run(differentTxtestCase.Name, func(t *testing.T) { 1602 runTestCase(t, differentTxtestCase) 1603 }) 1604 1605 // ====== Test invalid TX for overlapping batches ====== 1606 payload = eth.ExecutionPayloadEnvelope{ 1607 ExecutionPayload: ð.ExecutionPayload{ 1608 ParentHash: l2B0.Hash, 1609 BlockNumber: hexutil.Uint64(l2B1.Number), 1610 Timestamp: hexutil.Uint64(l2B1.Time), 1611 BlockHash: l2B1.Hash, 1612 // First TX is not a deposit TX. it will make error when extracting L2BlockRef from the payload 1613 Transactions: []hexutil.Bytes{randTxData}, 1614 }, 1615 } 1616 l2Client.Mock.On("PayloadByNumber", l2B1.Number).Return(&payload, &nilErr).Once() 1617 1618 invalidTxTestCase := ValidBatchTestCase{ 1619 Name: "invalid_tx_overlapping_batch", 1620 L1Blocks: []eth.L1BlockRef{l1B}, 1621 L2SafeHead: l2B1, 1622 Batch: BatchWithL1InclusionBlock{ 1623 L1InclusionBlock: l1B, 1624 Batch: NewSpanBatch([]*SingularBatch{ 1625 { 1626 ParentHash: l2B0.Hash, 1627 EpochNum: rollup.Epoch(l2B1.L1Origin.Number), 1628 EpochHash: l2B1.L1Origin.Hash, 1629 Timestamp: l2B1.Time, 1630 Transactions: []hexutil.Bytes{randTxData}, 1631 }, 1632 { 1633 ParentHash: l2B1.Hash, 1634 EpochNum: rollup.Epoch(l2B2.L1Origin.Number), 1635 EpochHash: l2B2.L1Origin.Hash, 1636 Timestamp: l2B2.Time, 1637 Transactions: nil, 1638 }, 1639 }), 1640 }, 1641 Expected: BatchDrop, 1642 ExpectedLog: "failed to extract L2BlockRef from execution payload", 1643 DeltaTime: &minTs, 1644 } 1645 1646 t.Run(invalidTxTestCase.Name, func(t *testing.T) { 1647 runTestCase(t, invalidTxTestCase) 1648 }) 1649 }