diff --git a/op-acceptance-tests/tests/interop/reorgs/init_exec_msg_test.go b/op-acceptance-tests/tests/interop/reorgs/init_exec_msg_test.go index 559caf9e4c087..f71207091b4f1 100644 --- a/op-acceptance-tests/tests/interop/reorgs/init_exec_msg_test.go +++ b/op-acceptance-tests/tests/interop/reorgs/init_exec_msg_test.go @@ -185,12 +185,12 @@ func TestReorgInitExecMsg(gt *testing.T) { // wait and confirm reorgs on chain A and B dsl.CheckAll(t, - sys.L2ELA.ReorgTriggeredFn(eth.L2BlockRef{ + sys.L2ELA.ReorgExactFn(eth.L2BlockRef{ Number: divergenceBlockNumber_A, Hash: originalRef_A.Hash, ParentHash: originalRef_A.ParentID().Hash, }, 30), - sys.L2ELB.ReorgTriggeredFn(eth.L2BlockRef{ + sys.L2ELB.ReorgExactFn(eth.L2BlockRef{ Number: divergenceBlockNumber_B, Hash: originalRef_B.Hash, ParentHash: originalRef_B.ParentID().Hash, diff --git a/op-acceptance-tests/tests/interop/reorgs/invalid_exec_msgs_test.go b/op-acceptance-tests/tests/interop/reorgs/invalid_exec_msgs_test.go index 0d6444e6fbf7f..33255b840566b 100644 --- a/op-acceptance-tests/tests/interop/reorgs/invalid_exec_msgs_test.go +++ b/op-acceptance-tests/tests/interop/reorgs/invalid_exec_msgs_test.go @@ -214,7 +214,7 @@ func testReorgInvalidExecMsg(gt *testing.T, txModifierFn func(msg *suptypes.Mess sys.L2BatcherA.Start() // wait for reorg on chain A - sys.L2ELA.ReorgTriggered(eth.L2BlockRef{ + sys.L2ELA.ReorgExact(eth.L2BlockRef{ Number: divergenceBlockNumber_A, Hash: originalHash_A, ParentHash: originalParentHash_A, diff --git a/op-acceptance-tests/tests/interop/seqwindow/expiry_test.go b/op-acceptance-tests/tests/interop/seqwindow/expiry_test.go index 261bcbf12d8f9..6fdd977e4ee27 100644 --- a/op-acceptance-tests/tests/interop/seqwindow/expiry_test.go +++ b/op-acceptance-tests/tests/interop/seqwindow/expiry_test.go @@ -102,7 +102,7 @@ func TestSequencingWindowExpiry(gt *testing.T) { t.Logger().Info("Waiting for sequencing window expiry induced reorg now", "windowDuration", windowDuration) // Monitor that the old unsafe chain is reorged out as expected - sys.L2ELA.ReorgTriggered(old, 50) + sys.L2ELA.ReorgExact(old, 50) // Wait for the tx to no longer be included. // The tx-indexer may still return the old block or be stale. diff --git a/op-devstack/dsl/l2_el.go b/op-devstack/dsl/l2_el.go index a2d2b5f975159..dedbfc9a105ec 100644 --- a/op-devstack/dsl/l2_el.go +++ b/op-devstack/dsl/l2_el.go @@ -125,39 +125,64 @@ func (el *L2ELNode) BlockRefByNumber(num uint64) eth.L2BlockRef { return block } -// ReorgTriggeredFn returns a lambda that checks that a L2 reorg occurred on the expected block +// ReorgTriggeredFn returns a lambda that checks that a L2 reorg occurred on or before the expected block // Composable with other lambdas to wait in parallel func (el *L2ELNode) ReorgTriggeredFn(target eth.L2BlockRef, attempts int) CheckFunc { return func() error { el.log.Info("expecting chain to reorg on block ref", "name", el.inner.Name(), "chain", el.inner.ChainID(), "target", target) return retry.Do0(el.ctx, attempts, &retry.FixedStrategy{Dur: 2 * time.Second}, func() error { - reorged, err := el.inner.EthClient().BlockRefByNumber(el.ctx, target.Number) - if err != nil { - if strings.Contains(err.Error(), "not found") { // reorg is happening wait a bit longer - el.log.Info("chain still hasn't been reorged", "chain", el.inner.ChainID(), "error", err) - return err - } - return err + reorged, err := el.reorgTriggered(target) + if err == nil { + el.log.Info("reorg on divergence block", "chain", el.inner.ChainID(), "pre_blockref", target, "post_blockref", reorged) } + return err + }) + } +} - if target.Hash == reorged.Hash { // want not equal - el.log.Info("chain still hasn't been reorged", "chain", el.inner.ChainID(), "ref", reorged) - return fmt.Errorf("expected head to reorg %s, but got %s", target, reorged) +// ReorgExactFn returns a lambda that checks that a L2 reorg occurred on the exact target L2 block. +// If an L2 block prior to target was reorged, this function will block forever. +// Composable with other lambdas to wait in parallel. +func (el *L2ELNode) ReorgExactFn(target eth.L2BlockRef, attempts int) CheckFunc { + return func() error { + el.log.Info("expecting chain to reorg on block ref", "name", el.inner.Name(), "chain", el.inner.ChainID(), "target", target) + return retry.Do0(el.ctx, attempts, &retry.FixedStrategy{Dur: 2 * time.Second}, + func() error { + reorged, err := el.reorgTriggered(target) + if err != nil { + return err } if target.ParentHash != reorged.ParentHash && target.ParentHash != emptyHash { return fmt.Errorf("expected parent of target to be the same as the parent of the reorged head, but they are different") } - el.log.Info("reorg on divergence block", "chain", el.inner.ChainID(), "pre_blockref", target) - el.log.Info("reorg on divergence block", "chain", el.inner.ChainID(), "post_blockref", reorged) + el.log.Info("reorg on divergence block", "chain", el.inner.ChainID(), "pre_blockref", target, "post_blockref", reorged) return nil }) } } +func (el *L2ELNode) reorgTriggered(target eth.L2BlockRef) (eth.BlockRef, error) { + reorged, err := el.inner.EthClient().BlockRefByNumber(el.ctx, target.Number) + if err != nil { + if strings.Contains(err.Error(), "not found") { // reorg is happening wait a bit longer + el.log.Info("chain still hasn't been reorged", "chain", el.inner.ChainID(), "error", err) + return eth.BlockRef{}, err + } + return eth.BlockRef{}, err + } + + if target.Hash == reorged.Hash { // want not equal + el.log.Info("chain still hasn't been reorged", "chain", el.inner.ChainID(), "ref", reorged) + return eth.BlockRef{}, fmt.Errorf("expected head to reorg %s, but got %s", target, reorged) + } + + return reorged, nil +} + func (el *L2ELNode) Advanced(label eth.BlockLabel, block uint64) { el.require.NoError(el.AdvancedFn(label, block)()) } @@ -178,6 +203,10 @@ func (el *L2ELNode) ReorgTriggered(target eth.L2BlockRef, attempts int) { el.require.NoError(el.ReorgTriggeredFn(target, attempts)()) } +func (el *L2ELNode) ReorgExact(target eth.L2BlockRef, attempts int) { + el.require.NoError(el.ReorgExactFn(target, attempts)()) +} + func (el *L2ELNode) TransactionTimeout() time.Duration { return el.inner.TransactionTimeout() } diff --git a/rust/kona/tests/node/reorgs/l2_reorg_test.go b/rust/kona/tests/node/reorgs/l2_reorg_test.go index 1b489dc67446e..457c78a628791 100644 --- a/rust/kona/tests/node/reorgs/l2_reorg_test.go +++ b/rust/kona/tests/node/reorgs/l2_reorg_test.go @@ -51,7 +51,7 @@ func TestL2Reorg(gt *testing.T) { for _, node := range out.L2ELSequencerNodes() { reorgedHead := node.BlockRefByLabel(eth.Unsafe) require.Greater(t, reorgedHead.Number, unsafeHead.Number) - checksPostReorg = append(checksPostReorg, node.ReorgTriggeredFn(unsafeHead, 40)) + checksPostReorg = append(checksPostReorg, node.ReorgExactFn(unsafeHead, 40)) } // Ensure that all the nodes still advance even after the reorg diff --git a/rust/kona/tests/supervisor/l2reorg/init_exec_msg_test.go b/rust/kona/tests/supervisor/l2reorg/init_exec_msg_test.go index 61ccf76cc4249..235f306023544 100644 --- a/rust/kona/tests/supervisor/l2reorg/init_exec_msg_test.go +++ b/rust/kona/tests/supervisor/l2reorg/init_exec_msg_test.go @@ -185,12 +185,12 @@ func TestReorgInitExecMsg(gt *testing.T) { // wait and confirm reorgs on chain A and B dsl.CheckAll(t, - sys.L2ELA.ReorgTriggeredFn(eth.L2BlockRef{ + sys.L2ELA.ReorgExactFn(eth.L2BlockRef{ Number: divergenceBlockNumber_A, Hash: originalRef_A.Hash, ParentHash: originalRef_A.ParentID().Hash, }, 30), - sys.L2ELB.ReorgTriggeredFn(eth.L2BlockRef{ + sys.L2ELB.ReorgExactFn(eth.L2BlockRef{ Number: divergenceBlockNumber_B, Hash: originalRef_B.Hash, ParentHash: originalRef_B.ParentID().Hash,