Skip to content
This repository was archived by the owner on Jan 16, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 181 additions & 37 deletions tests/supervisor/pre_interop/post_test.go
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))
Comment thread
dhyaniarun1993 marked this conversation as resolved.
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")
}
Loading
Loading