diff --git a/op-acceptance-tests/tests/base/chain/init_test.go b/op-acceptance-tests/tests/base/chain/init_test.go index c1ecc9ea1af..1240bfee4f0 100644 --- a/op-acceptance-tests/tests/base/chain/init_test.go +++ b/op-acceptance-tests/tests/base/chain/init_test.go @@ -8,6 +8,6 @@ import ( func TestMain(m *testing.M) { presets.DoMain(m, - presets.WithMinimal(), + presets.WithSingleChainMultiNode(), ) } diff --git a/op-acceptance-tests/tests/base/chain/unsafe_sync_test.go b/op-acceptance-tests/tests/base/chain/unsafe_sync_test.go new file mode 100644 index 00000000000..c1d6a675e14 --- /dev/null +++ b/op-acceptance-tests/tests/base/chain/unsafe_sync_test.go @@ -0,0 +1,86 @@ +package chain + +import ( + "strings" + "testing" + "time" + + "github.com/ethereum-optimism/optimism/op-devstack/devtest" + "github.com/ethereum-optimism/optimism/op-devstack/presets" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" + "github.com/stretchr/testify/require" +) + +// TestUnsafeSync tests that the verifier nodes are able to sync the unsafe head of the chain. +// It does this by waiting for the sequencer to produce NUM_UNSAFE_BLOCKS unsafe blocks and requiring the verifier nodes to +// be progress to the final unsafe head number. +func TestUnsafeSync(gt *testing.T) { + t := devtest.SerialT(gt) + sys := presets.NewSingleChainMultiNode(t) + + // Stop batcher so there is no sync from deriving from L1 + // This won't work on external networks. + sys.L2Batcher.Stop() + + l2chainID := sys.L2Chain.Escape().ChainID() + const NUM_UNSAFE_BLOCKS = 10 + const TIMEOUT = 5 * time.Second // time to wait for all verifier nodes to catch up to the sequencer + + // In order for this test to be valid, we need at least 2 L2 EL nodes. + t.Require().Greater(len(sys.L2Chain.L2ELNodes()), 1, "expected at least 2 L2 EL nodes") + + // Find the index of the sequencer node. + indexOfSequencer := 0 + for i, el := range sys.L2Chain.L2ELNodes() { + if strings.Contains(el.ID().String(), "sequencer") { + indexOfSequencer = i + break + } + } + + // Wait for the sequencer to produce at least NUM_UNSAFE_BLOCKS unsafe blocks. + initialUnsafeHeadNumber := sys.L2Chain.L2ELNodes()[0].ChainSyncStatus(sys.L2Chain.Escape().ChainID(), types.LocalUnsafe).Number + finalUnsafeHeadNumber := initialUnsafeHeadNumber + NUM_UNSAFE_BLOCKS + ticker := time.NewTicker(time.Duration(sys.L2Chain.Escape().RollupConfig().BlockTime) * time.Second) + for range ticker.C { + sequencerUnsafeHead := sys.L2Chain.L2ELNodes()[indexOfSequencer].ChainSyncStatus(sys.L2Chain.Escape().ChainID(), types.LocalUnsafe) + t.Logf("Sequencer unsafe head (number %d) is %d/%d blocks ahead of initial unsafe head (number %d)", + sequencerUnsafeHead.Number, sequencerUnsafeHead.Number-initialUnsafeHeadNumber, NUM_UNSAFE_BLOCKS, initialUnsafeHeadNumber) + if sequencerUnsafeHead.Number >= finalUnsafeHeadNumber { + break + } + } + + // Check verifier nodes progess to the final unsafe head number. + allVerifierNodesInSync := func() bool { + for i, el := range sys.L2Chain.L2ELNodes() { + if i == indexOfSequencer { + continue + } + verifierUnsafeHead := el.ChainSyncStatus(l2chainID, types.LocalUnsafe) + if verifierUnsafeHead.Number < initialUnsafeHeadNumber+NUM_UNSAFE_BLOCKS { + return false + } + } + return true + } + + require.Eventually(t, allVerifierNodesInSync, TIMEOUT, time.Duration(sys.L2Chain.Escape().RollupConfig().BlockTime)*time.Second, + "all verifier nodes did not progress to the final unsafe head number within %s", + TIMEOUT, + ) + t.Log("All verifier nodes progressed to the final unsafe head number", finalUnsafeHeadNumber) + + // Final sanity check on the block hashes being consistent. + finalUnsafeBlockHash := sys.L2Chain.L2ELNodes()[indexOfSequencer].BlockRefByNumber(finalUnsafeHeadNumber).Hash + for i, el := range sys.L2Chain.L2ELNodes() { + if i == indexOfSequencer { + continue + } + verifierUnsafeBlockHash := el.BlockRefByNumber(finalUnsafeHeadNumber).Hash + if verifierUnsafeBlockHash != finalUnsafeBlockHash { + t.Require().Fail("verifier (%s) unsafe block hash (%s) does not match final unsafe block hash (%s)", el.ID(), verifierUnsafeBlockHash, finalUnsafeBlockHash) + } + } + t.Log("All verifier nodes have the final unsafe block hash", finalUnsafeBlockHash) +}