This repository was archived by the owner on Jan 16, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 212
test(supervisor): preinterop acceptance test #2463
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,72 +1,216 @@ | ||
| package preinterop | ||
|
|
||
| import ( | ||
| "math/rand" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/ethereum-optimism/optimism/op-acceptance-tests/tests/interop" | ||
| "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" | ||
| "github.com/ethereum-optimism/optimism/op-devstack/devtest" | ||
| "github.com/ethereum-optimism/optimism/op-devstack/dsl" | ||
| "github.com/ethereum-optimism/optimism/op-devstack/presets" | ||
| "github.com/ethereum-optimism/optimism/op-devstack/stack/match" | ||
| "github.com/ethereum-optimism/optimism/op-node/rollup" | ||
| "github.com/ethereum-optimism/optimism/op-service/eth" | ||
| "github.com/ethereum-optimism/optimism/op-service/predeploys" | ||
| stypes "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" | ||
| "github.com/ethereum/go-ethereum/common" | ||
| "github.com/ethereum/go-ethereum/core/types" | ||
| ) | ||
|
|
||
| func TestPostInteropStatusSync(gt *testing.T) { | ||
| t := devtest.ParallelT(gt) | ||
| sys := presets.NewSimpleInterop(t) | ||
| func testSupervisorActivationBlock(t devtest.T, sys *presets.SimpleInterop, net *dsl.L2Network, activationBlock eth.BlockID) { | ||
| require := t.Require() | ||
|
|
||
| devtest.RunParallel(t, sys.L2Networks(), func(t devtest.T, net *dsl.L2Network) { | ||
| t.Logger().Info("Awaiting activation block for chain", "chainID", net.ChainID()) | ||
| activationBlock := net.AwaitActivation(t, rollup.Interop) | ||
| require.NotNil(activationBlock, "ActivationBlock should return a valid block number") | ||
| // wait for some time to ensure the interop activation block is become cross-safe | ||
| t.Logger().Info("Waiting for interop activation block to be cross-safe") | ||
| sys.Supervisor.WaitForL2HeadToAdvanceTo(net.ChainID(), stypes.CrossSafe, activationBlock) | ||
|
|
||
| t.Logger().Info("Activation block found", "blockNumber", activationBlock.Number) | ||
| time.Sleep(10 * time.Second) | ||
| interopTime := net.Escape().ChainConfig().InteropTime | ||
| pre := net.LatestBlockBeforeTimestamp(t, *interopTime) | ||
| require.NotNil(pre, "Pre-interop block should be found before interop time") | ||
|
|
||
| // wait for some time to ensure the interop activation block is become cross-safe | ||
| t.Logger().Info("Waiting for interop activation block to be cross-safe") | ||
| sys.Supervisor.WaitForL2HeadToAdvanceTo(net.ChainID(), stypes.CrossSafe, activationBlock) | ||
| // make sure pre-interop block is parent of activation block | ||
| require.Equal(pre.Number, activationBlock.Number-1, "Activation block should be one after pre-interop block") | ||
|
|
||
| // fetching the source for the pre-interop block should return the error | ||
| // this is to make sure that we only store the blocks after interop | ||
| _, err := sys.Supervisor.Escape().QueryAPI().CrossDerivedToSource(t.Ctx(), net.ChainID(), pre.ID()) | ||
| require.Error(err, "CrossDerivedToSource should error before interop") | ||
|
|
||
| // fetch the source for the activation block | ||
| derivedFrom, err := sys.Supervisor.Escape().QueryAPI().CrossDerivedToSource(t.Ctx(), net.ChainID(), activationBlock) | ||
| require.NoError(err, "CrossDerivedToSource should not error after interop") | ||
| require.NotNil(derivedFrom, "CrossDerivedToSource should return a valid source block") | ||
| } | ||
|
|
||
| // Acceptance Test: https://github.com/ethereum-optimism/optimism/blob/develop/op-acceptance-tests/tests/interop/upgrade/post_test.go | ||
| // test case modified to check the correctness of the supervisor activation block as well | ||
| func TestPostInbox(gt *testing.T) { | ||
| t := devtest.ParallelT(gt) | ||
| sys := presets.NewSimpleInterop(t) | ||
| devtest.RunParallel(t, sys.L2Networks(), func(t devtest.T, net *dsl.L2Network) { | ||
| require := t.Require() | ||
| activationBlock := net.AwaitActivation(t, rollup.Interop) | ||
|
|
||
| status, err := sys.Supervisor.Escape().QueryAPI().SyncStatus(t.Ctx()) | ||
| require.NoError(err, "SyncStatus should not error after interop") | ||
| require.NotNil(status, "SyncStatus should return a valid status") | ||
| el := net.Escape().L2ELNode(match.FirstL2EL) | ||
| implAddrBytes, err := el.EthClient().GetStorageAt(t.Ctx(), predeploys.CrossL2InboxAddr, | ||
| genesis.ImplementationSlot, activationBlock.Hash.String()) | ||
| require.NoError(err) | ||
| implAddr := common.BytesToAddress(implAddrBytes[:]) | ||
| require.NotEqual(common.Address{}, implAddr) | ||
| code, err := el.EthClient().CodeAtHash(t.Ctx(), implAddr, activationBlock.Hash) | ||
| require.NoError(err) | ||
| require.NotEmpty(code) | ||
|
|
||
| testSupervisorActivationBlock(t, sys, net, activationBlock) | ||
| }) | ||
| } | ||
|
|
||
| func TestSupervisorActivationBlock(gt *testing.T) { | ||
| // Acceptance Test: https://github.com/ethereum-optimism/optimism/blob/develop/op-acceptance-tests/tests/interop/upgrade/post_test.go | ||
| func TestPostInteropUpgradeComprehensive(gt *testing.T) { | ||
| t := devtest.ParallelT(gt) | ||
| sys := presets.NewSimpleInterop(t) | ||
| require := t.Require() | ||
| logger := t.Logger() | ||
|
|
||
| // Wait for networks to be online by waiting for blocks | ||
| sys.L1Network.WaitForBlock() | ||
| sys.L2ChainA.WaitForBlock() | ||
| sys.L2ChainB.WaitForBlock() | ||
|
|
||
| // Get interop activation time | ||
| interopTime := sys.L2ChainA.Escape().ChainConfig().InteropTime | ||
| require.NotNil(interopTime, "InteropTime must be set") | ||
|
|
||
| logger.Info("Starting comprehensive post-interop upgrade tests", "interopTime", *interopTime) | ||
|
|
||
| // 1. Check that anchor block of supervisor matches the activation block | ||
| logger.Info("Checking supervisor anchor block matches activation block") | ||
| testSupervisorAnchorBlock(t, sys) | ||
|
|
||
| // 2. Check that the supervisor has safety progression for each level | ||
| logger.Info("Checking supervisor safety progression") | ||
| testSupervisorSafetyProgression(t, sys) | ||
|
|
||
| // 3. Confirms that interop message can be included | ||
| logger.Info("Testing interop message inclusion") | ||
| testInteropMessageInclusion(t, sys) | ||
|
|
||
| logger.Info("All comprehensive post-interop upgrade tests completed successfully") | ||
| } | ||
|
|
||
| // testSupervisorAnchorBlock checks that the supervisor's anchor block has been set and matches the upgrade timestamp | ||
| func testSupervisorAnchorBlock(t devtest.T, sys *presets.SimpleInterop) { | ||
| logger := t.Logger() | ||
|
|
||
| // Use the DSL helper for anchor block validation | ||
| logger.Info("Testing supervisor anchor block functionality") | ||
|
|
||
| // Phase 1: Wait for L2 chains to reach interop activation time | ||
| logger.Info("Phase 1: Waiting for L2 chains to reach interop activation time") | ||
|
|
||
| devtest.RunParallel(t, sys.L2Networks(), func(t devtest.T, net *dsl.L2Network) { | ||
| t.Logger().Info("Awaiting activation block for chain", "chainID", net.ChainID()) | ||
|
|
||
| // Gate test to not time out before upgrade happens | ||
| forkTimestamp := net.Escape().ChainConfig().InteropTime | ||
| t.Gate().NotNil(forkTimestamp, "Must have fork configured") | ||
| t.Gate().Greater(*forkTimestamp, uint64(0), "Must not start fork at genesis") | ||
| upgradeTime := time.Unix(int64(*forkTimestamp), 0) | ||
| if deadline, hasDeadline := t.Deadline(); hasDeadline { | ||
| t.Gate().True(upgradeTime.Before(deadline), "test must not time out before upgrade happens") | ||
| } | ||
|
|
||
| activationBlock := net.AwaitActivation(t, rollup.Interop) | ||
| require.NotNil(activationBlock, "ActivationBlock should return a valid block number") | ||
| sys.Supervisor.WaitForL2HeadToAdvanceTo(net.ChainID(), stypes.CrossSafe, activationBlock) | ||
|
|
||
| t.Logger().Info("Activation block found", "blockNumber", activationBlock.Number) | ||
| time.Sleep(10 * time.Second) | ||
| logger.Info("Validating anchor block timing", | ||
| "chainID", net.ChainID(), | ||
| "derivedBlockNumber", activationBlock.Number, | ||
| "interopTime", *forkTimestamp) | ||
| }) | ||
|
|
||
| // wait for some time to ensure the interop activation block is become cross-safe | ||
| t.Logger().Info("Waiting for interop activation block to be cross-safe") | ||
| sys.Supervisor.WaitForL2HeadToAdvanceTo(net.ChainID(), stypes.CrossSafe, activationBlock) | ||
| logger.Info("Supervisor anchor block validation completed successfully") | ||
| } | ||
|
|
||
| interopTime := net.Escape().ChainConfig().InteropTime | ||
| pre := net.LatestBlockBeforeTimestamp(t, *interopTime) | ||
| require.NotNil(pre, "Pre-interop block should be found before interop time") | ||
| // testSupervisorSafetyProgression checks that supervisor has safety progression for each level | ||
| func testSupervisorSafetyProgression(t devtest.T, sys *presets.SimpleInterop) { | ||
| logger := t.Logger() | ||
| logger.Info("Testing supervisor safety progression") | ||
|
|
||
| // make sure pre-interop block is parent of activation block | ||
| require.Equal(pre.Number, activationBlock.Number-1, "Activation block should be one after pre-interop block") | ||
| delta := uint64(3) // Minimum blocks of progression expected | ||
| dsl.CheckAll(t, | ||
| sys.L2CLA.AdvancedFn(stypes.LocalUnsafe, delta, 30), | ||
| sys.L2CLB.AdvancedFn(stypes.LocalUnsafe, delta, 30), | ||
|
|
||
| // fetching the source for the pre-interop block should return the error | ||
| // this is to make sure that we only store the blocks after interop | ||
| _, err := sys.Supervisor.Escape().QueryAPI().CrossDerivedToSource(t.Ctx(), net.ChainID(), pre.ID()) | ||
| require.Error(err, "CrossDerivedToSource should error before interop") | ||
| sys.L2CLA.AdvancedFn(stypes.LocalSafe, delta, 30), | ||
| sys.L2CLB.AdvancedFn(stypes.LocalSafe, delta, 30), | ||
|
|
||
| // fetch the source for the activation block | ||
| derivedFrom, err := sys.Supervisor.Escape().QueryAPI().CrossDerivedToSource(t.Ctx(), net.ChainID(), activationBlock) | ||
| require.NoError(err, "CrossDerivedToSource should not error after interop") | ||
| require.NotNil(derivedFrom, "CrossDerivedToSource should return a valid source block") | ||
| }) | ||
| sys.L2CLA.AdvancedFn(stypes.CrossUnsafe, delta, 30), | ||
| sys.L2CLB.AdvancedFn(stypes.CrossUnsafe, delta, 30), | ||
|
|
||
| sys.L2CLA.AdvancedFn(stypes.CrossSafe, delta, 60), | ||
| sys.L2CLB.AdvancedFn(stypes.CrossSafe, delta, 60), | ||
| ) | ||
|
|
||
| logger.Info("Supervisor safety progression validation completed successfully") | ||
| } | ||
|
|
||
| // testInteropMessageInclusion confirms that interop messages can be included post-upgrade | ||
| func testInteropMessageInclusion(t devtest.T, sys *presets.SimpleInterop) { | ||
| logger := t.Logger() | ||
| logger.Info("Starting interop message inclusion test") | ||
|
|
||
| // Phase 1: Setup test accounts and contracts | ||
| alice, bob, eventLoggerAddress := setupInteropTestEnvironment(sys) | ||
|
|
||
| // Phase 2: Send init message on chain A | ||
| rng := rand.New(rand.NewSource(1234)) | ||
| initIntent, initReceipt := alice.SendInitMessage(interop.RandomInitTrigger(rng, eventLoggerAddress, rng.Intn(5), rng.Intn(30))) | ||
|
|
||
| // Make sure supervisor indexes block which includes init message | ||
| sys.Supervisor.WaitForUnsafeHeadToAdvance(alice.ChainID(), 2) | ||
|
|
||
| // Single event in tx so index is 0 | ||
| _, execReceipt := bob.SendExecMessage(initIntent, 0) | ||
|
|
||
| // Phase 5: Verify cross-safe progression | ||
| verifyInteropMessagesProgression(t, sys, initReceipt, execReceipt) | ||
|
|
||
| logger.Info("Interop message inclusion test completed successfully") | ||
| } | ||
|
|
||
| // setupInteropTestEnvironment creates test accounts and deploys necessary contracts | ||
| func setupInteropTestEnvironment(sys *presets.SimpleInterop) (alice, bob *dsl.EOA, eventLoggerAddress common.Address) { | ||
|
|
||
| // Create EOAs for interop messaging | ||
| alice = sys.FunderA.NewFundedEOA(eth.OneHundredthEther) | ||
| bob = sys.FunderB.NewFundedEOA(eth.OneHundredthEther) | ||
|
|
||
| // Deploy event logger contract on chain A | ||
| eventLoggerAddress = alice.DeployEventLogger() | ||
|
|
||
| // Wait for chains to catch up | ||
| sys.L2ChainB.CatchUpTo(sys.L2ChainA) | ||
|
|
||
| return alice, bob, eventLoggerAddress | ||
| } | ||
|
|
||
| // verifyInteropMessagesProgression verifies cross-safe progression for both init and exec messages | ||
| func verifyInteropMessagesProgression(t devtest.T, sys *presets.SimpleInterop, initReceipt, execReceipt *types.Receipt) { | ||
| logger := t.Logger() | ||
|
|
||
| // Verify cross-safe progression for both messages | ||
| dsl.CheckAll(t, | ||
| sys.L2CLA.ReachedRefFn(stypes.CrossSafe, eth.BlockID{ | ||
| Number: initReceipt.BlockNumber.Uint64(), | ||
| Hash: initReceipt.BlockHash, | ||
| }, 60), | ||
| sys.L2CLB.ReachedRefFn(stypes.CrossSafe, eth.BlockID{ | ||
| Number: execReceipt.BlockNumber.Uint64(), | ||
| Hash: execReceipt.BlockHash, | ||
| }, 60), | ||
| ) | ||
|
|
||
| logger.Info("Cross-safe progression verified for both init and exec messages") | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.