diff --git a/.github/workflows/e2e_tests_supervisor.yaml b/.github/workflows/e2e_tests_supervisor.yaml index d6688c5527..f88d29a332 100644 --- a/.github/workflows/e2e_tests_supervisor.yaml +++ b/.github/workflows/e2e_tests_supervisor.yaml @@ -4,33 +4,89 @@ on: env: CARGO_TERM_COLOR: always jobs: - kurtosis-e2e-tests-supervisor: + supervisor-e2e-tests: runs-on: ubuntu-latest timeout-minutes: 40 - name: test + name: ${{ matrix.devnet-config }}-${{ matrix.test-pkg }}-tests + strategy: + fail-fast: false + matrix: + include: + - devnet-config: simple-supervisor + test-pkg: sync + - devnet-config: simple-supervisor + test-pkg: message + - devnet-config: simple-supervisor + test-pkg: rpc + - devnet-config: preinterop-supervisor + test-pkg: pre_interop steps: - name: Checkout sources uses: actions/checkout@v4 with: submodules: true + - name: Free Disk Space (Ubuntu) uses: jlumbroso/free-disk-space@main with: large-packages: false + - uses: taiki-e/install-action@just + - uses: dtolnay/rust-toolchain@stable with: toolchain: 1.86 + - name: Setup Go 1.24.3 uses: actions/setup-go@v5 with: # Semantic version range syntax or exact version of Go go-version: '1.24.3' + + - name: Download Go dependencies + run: go mod download + working-directory: tests + - uses: jdx/mise-action@v2 # installs Mise + runs `mise install` - - name: test with simple-supervisor devnet + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build kona supervisor docker image + uses: docker/bake-action@v6 env: - DISABLE_OP_E2E_LEGACY: true - DEVSTACK_ORCHESTRATOR: sysext - # todo:: once the PR is merged, switch back to the ethpandaops/optimism-package - # pr: https://github.com/ethpandaops/optimism-package/pull/343 - run: just build-devnet-and-test-e2e simple-supervisor supervisor github.com/dhyaniarun1993/optimism-package@kona-supervisor + BIN_TARGET: kona-supervisor + BUILD_PROFILE: release + REPO_LOCATION: local + PLATFORMS: linux/${{ runner.arch == 'X64' && 'amd64' || runner.arch == 'ARM64' && 'arm64' || runner.arch }} + with: + files: | + ./docker/docker-bake.hcl + load: true + targets: | + generic + set: | + *.tags=kona-supervisor:local + *.cache-from=type=local,src=${{ runner.temp }}/.buildx-cache + *.cache-to=type=local,dest=${{ runner.temp }}/.buildx-cache-new,mode=max + + - name: deploy ${{ matrix.devnet-config }} devnet + run: just devnet "${{ matrix.devnet-config }}" github.com/dhyaniarun1993/optimism-package@kona-supervisor + + - name: test ${{ matrix.test-pkg }} with ${{ matrix.devnet-config }} devnet + run: just test-e2e "${{ matrix.devnet-config }}" "/supervisor/${{ matrix.test-pkg }}" + + - # Temp fix + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + name: Move cache + run: | + rm -rf ${{ runner.temp }}/.buildx-cache + mv ${{ runner.temp }}/.buildx-cache-new ${{ runner.temp }}/.buildx-cache + + - name: Save docker cache + uses: actions/cache/save@v4 + if: github.ref == 'refs/heads/main' + with: + path: ${{ runner.temp }}/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} \ No newline at end of file diff --git a/tests/devnets/preinterop-supervisor.yaml b/tests/devnets/preinterop-supervisor.yaml new file mode 100644 index 0000000000..7a38bae72b --- /dev/null +++ b/tests/devnets/preinterop-supervisor.yaml @@ -0,0 +1,81 @@ +optimism_package: + faucet: + enabled: true + chains: + chain1: + participants: + node1: + el: + type: op-geth + image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101511.0 + cl: + type: op-node + image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:develop + log_level: debug + network_params: + network: "kurtosis" + network_id: "2151908" + interop_time_offset: 220 + holocene_time_offset: 0 + isthmus_time_offset: 0 + fjord_time_offset: 0 + granite_time_offset: 0 + fund_dev_accounts: true + batcher_params: + extra_params: [] + proposer_params: + game_type: 1 + proposal_interval: 10m + chain2: + participants: + node1: + el: + type: op-geth + image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101511.0 + cl: + type: op-node + image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:develop + log_level: debug + network_params: + network: "kurtosis" + network_id: "2151909" + interop_time_offset: 220 + holocene_time_offset: 0 + isthmus_time_offset: 0 + fjord_time_offset: 0 + granite_time_offset: 0 + fund_dev_accounts: true + batcher_params: + extra_params: [] + proposer_params: + game_type: 1 + proposal_interval: 10m + superchains: + superchain: + enabled: true + supervisors: + supervisor: # default op-supervisor + enabled: true + type: kona-supervisor + image: kona-supervisor:local + superchain: superchain + global_log_level: "info" + global_node_selectors: {} + global_tolerations: [] + persistent: false +ethereum_package: + participants: + - el_type: geth + cl_type: teku + network_params: + preset: minimal + genesis_delay: 5 + additional_preloaded_contracts: ' + { + "0x4e59b44847b379578588920cA78FbF26c0B4956C": { + "balance": "0ETH", + "code": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3", + "storage": {}, + "nonce": "1" + } + }' \ No newline at end of file diff --git a/tests/devnets/simple-supervisor.yaml b/tests/devnets/simple-supervisor.yaml index 090fe86e95..3c37e38cca 100644 --- a/tests/devnets/simple-supervisor.yaml +++ b/tests/devnets/simple-supervisor.yaml @@ -7,6 +7,7 @@ optimism_package: node1: el: type: op-geth + image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101511.0 cl: type: op-node image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:develop @@ -30,6 +31,7 @@ optimism_package: node1: el: type: op-geth + image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101511.0 cl: type: op-node image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:develop diff --git a/tests/supervisor/config_test.go b/tests/supervisor/config_test.go deleted file mode 100644 index 37bddbbebe..0000000000 --- a/tests/supervisor/config_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package supervisor - -import ( - "testing" - - "github.com/ethereum-optimism/optimism/op-devstack/devtest" - "github.com/ethereum-optimism/optimism/op-devstack/presets" -) - -const ( - L2A_CHAIN_ID = "2151908" - L2B_CHAIN_ID = "2151909" -) - -func TestNetworkConfig(gt *testing.T) { - t := devtest.SerialT(gt) - - out := presets.NewSimpleInterop(t) - t.Require().Equal(out.L2CLA.ChainID().String(), L2A_CHAIN_ID) - t.Require().Equal(out.L2CLB.ChainID().String(), L2B_CHAIN_ID) -} diff --git a/tests/supervisor/message/init_test.go b/tests/supervisor/message/init_test.go new file mode 100644 index 0000000000..d51f54da12 --- /dev/null +++ b/tests/supervisor/message/init_test.go @@ -0,0 +1,21 @@ +package message + +import ( + "testing" + "time" + + "github.com/ethereum-optimism/optimism/op-devstack/presets" +) + +const ( + // SLEEP_BACKEND_READY is the time to wait for the backend to be ready + SLEEP_BACKEND_READY = 60 * time.Second +) + +// TestMain creates the test-setups against the shared backend +func TestMain(m *testing.M) { + // sleep to ensure the backend is ready + time.Sleep(SLEEP_BACKEND_READY) + + presets.DoMain(m, presets.WithSimpleInterop()) +} diff --git a/tests/supervisor/interop_contract_test.go b/tests/supervisor/message/interop_contract_test.go similarity index 99% rename from tests/supervisor/interop_contract_test.go rename to tests/supervisor/message/interop_contract_test.go index ab964a7541..659f84ea7f 100644 --- a/tests/supervisor/interop_contract_test.go +++ b/tests/supervisor/message/interop_contract_test.go @@ -1,4 +1,4 @@ -package supervisor +package message import ( "math/rand" diff --git a/tests/supervisor/interop_happy_tx_test.go b/tests/supervisor/message/interop_happy_tx_test.go similarity index 99% rename from tests/supervisor/interop_happy_tx_test.go rename to tests/supervisor/message/interop_happy_tx_test.go index 6d826acf14..659ae37dc4 100644 --- a/tests/supervisor/interop_happy_tx_test.go +++ b/tests/supervisor/message/interop_happy_tx_test.go @@ -1,4 +1,4 @@ -package supervisor +package message import ( "math/rand" diff --git a/tests/supervisor/interop_msg_test.go b/tests/supervisor/message/interop_msg_test.go similarity index 99% rename from tests/supervisor/interop_msg_test.go rename to tests/supervisor/message/interop_msg_test.go index 114ebef63b..2ab5905b52 100644 --- a/tests/supervisor/interop_msg_test.go +++ b/tests/supervisor/message/interop_msg_test.go @@ -1,4 +1,4 @@ -package supervisor +package message import ( "context" diff --git a/tests/supervisor/pre_interop/init_test.go b/tests/supervisor/pre_interop/init_test.go new file mode 100644 index 0000000000..3ddb3a5798 --- /dev/null +++ b/tests/supervisor/pre_interop/init_test.go @@ -0,0 +1,17 @@ +package preinterop + +// todo: add tests +import ( + "testing" + + "github.com/ethereum-optimism/optimism/op-devstack/presets" +) + +// TestMain creates the test-setups against the shared backend +func TestMain(m *testing.M) { + // sleep to ensure the backend is ready + + presets.DoMain(m, + presets.WithSimpleInterop(), + presets.WithInteropNotAtGenesis()) +} diff --git a/tests/supervisor/pre_interop/post_test.go b/tests/supervisor/pre_interop/post_test.go new file mode 100644 index 0000000000..f68acc9a03 --- /dev/null +++ b/tests/supervisor/pre_interop/post_test.go @@ -0,0 +1,72 @@ +package preinterop + +import ( + "testing" + "time" + + "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-node/rollup" + stypes "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" +) + +func TestPostInteropStatusSync(gt *testing.T) { + t := devtest.ParallelT(gt) + sys := presets.NewSimpleInterop(t) + 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") + + t.Logger().Info("Activation block found", "blockNumber", activationBlock.Number) + time.Sleep(10 * time.Second) + + // 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) + + 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") + }) +} + +func TestSupervisorActivationBlock(gt *testing.T) { + t := devtest.ParallelT(gt) + sys := presets.NewSimpleInterop(t) + 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") + + t.Logger().Info("Activation block found", "blockNumber", activationBlock.Number) + time.Sleep(10 * time.Second) + + // 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) + + interopTime := net.Escape().ChainConfig().InteropTime + pre := net.LatestBlockBeforeTimestamp(t, *interopTime) + require.NotNil(pre, "Pre-interop block should be found before interop time") + + // 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") + }) +} diff --git a/tests/supervisor/pre_interop/pre_test.go b/tests/supervisor/pre_interop/pre_test.go new file mode 100644 index 0000000000..7b4146cceb --- /dev/null +++ b/tests/supervisor/pre_interop/pre_test.go @@ -0,0 +1,92 @@ +package preinterop + +import ( + "math/rand" + "testing" + "time" + + "github.com/ethereum/go-ethereum/crypto" + + "github.com/ethereum-optimism/optimism/op-acceptance-tests/tests/interop" + "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-service/eth" + stypes "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" + gethTypes "github.com/ethereum/go-ethereum/core/types" +) + +func TestPreInteropNoSyncStatus(gt *testing.T) { + t := devtest.ParallelT(gt) + sys := presets.NewSimpleInterop(t) + require := t.Require() + + t.Logger().Info("Starting") + + devtest.RunParallel(t, sys.L2Networks(), func(t devtest.T, net *dsl.L2Network) { + interopTime := net.Escape().ChainConfig().InteropTime + t.Require().NotNil(interopTime) + + require.False(net.IsActivated(*interopTime), "Test requires pre-interop state") + t.Logger().Info("Timestamps", "interopTime", *interopTime, "now", time.Now().Unix()) + + _, err := sys.Supervisor.Escape().QueryAPI().SyncStatus(t.Ctx()) + require.ErrorContains(err, "chain database is not initialized") + }) + + t.Logger().Info("Done") +} + +func TestPreInteropCheckAccessList(gt *testing.T) { + t := devtest.ParallelT(gt) + sys := presets.NewSimpleInterop(t) + require := t.Require() + + alice := sys.FunderA.NewFundedEOA(eth.OneHundredthEther) + bob := sys.FunderB.NewFundedEOA(eth.OneHundredthEther) + + eventLoggerAddress := alice.DeployEventLogger() + sys.L2ChainB.CatchUpTo(sys.L2ChainA) + + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + _, initReceipt := alice.SendInitMessage( + interop.RandomInitTrigger(rng, eventLoggerAddress, rng.Intn(3), rng.Intn(10)), + ) + + logToAccess := func(chainID eth.ChainID, log *gethTypes.Log, timestamp uint64) stypes.Access { + msgPayload := make([]byte, 0) + for _, topic := range log.Topics { + msgPayload = append(msgPayload, topic.Bytes()...) + } + msgPayload = append(msgPayload, log.Data...) + + msgHash := crypto.Keccak256Hash(msgPayload) + args := stypes.ChecksumArgs{ + BlockNumber: log.BlockNumber, + Timestamp: timestamp, + LogIndex: uint32(log.Index), + ChainID: chainID, + LogHash: stypes.PayloadHashToLogHash(msgHash, log.Address), + } + return args.Access() + } + + blockRef := sys.L2ChainA.PublicRPC().BlockRefByNumber(initReceipt.BlockNumber.Uint64()) + + var accessEntries []stypes.Access + for _, evLog := range initReceipt.Logs { + accessEntries = append(accessEntries, logToAccess(alice.ChainID(), evLog, blockRef.Time)) + } + + sys.L2ChainB.WaitForBlock() + + accessList := stypes.EncodeAccessList(accessEntries) + timestamp := uint64(time.Now().Unix()) + ed := stypes.ExecutingDescriptor{ + Timestamp: timestamp, + ChainID: bob.ChainID(), + } + + err := sys.Supervisor.Escape().QueryAPI().CheckAccessList(t.Ctx(), accessList, stypes.LocalUnsafe, ed) + require.Error(err, "CheckAccessList should fail before interop") +} diff --git a/tests/supervisor/init_test.go b/tests/supervisor/rpc/init_test.go similarity index 91% rename from tests/supervisor/init_test.go rename to tests/supervisor/rpc/init_test.go index 8c647a2d3c..b11738839e 100644 --- a/tests/supervisor/init_test.go +++ b/tests/supervisor/rpc/init_test.go @@ -1,6 +1,5 @@ -package supervisor +package rpc -// todo: add tests import ( "testing" "time" diff --git a/tests/supervisor/rpc_test.go b/tests/supervisor/rpc/rpc_test.go similarity index 99% rename from tests/supervisor/rpc_test.go rename to tests/supervisor/rpc/rpc_test.go index b53e2efffc..d6e3af1e5b 100644 --- a/tests/supervisor/rpc_test.go +++ b/tests/supervisor/rpc/rpc_test.go @@ -1,4 +1,4 @@ -package supervisor +package rpc import ( "context" diff --git a/tests/supervisor/sync/init_test.go b/tests/supervisor/sync/init_test.go new file mode 100644 index 0000000000..3562c1cbf4 --- /dev/null +++ b/tests/supervisor/sync/init_test.go @@ -0,0 +1,21 @@ +package sync + +import ( + "testing" + "time" + + "github.com/ethereum-optimism/optimism/op-devstack/presets" +) + +const ( + // SLEEP_BACKEND_READY is the time to wait for the backend to be ready + SLEEP_BACKEND_READY = 90 * time.Second +) + +// TestMain creates the test-setups against the shared backend +func TestMain(m *testing.M) { + // sleep to ensure the backend is ready + time.Sleep(SLEEP_BACKEND_READY) + + presets.DoMain(m, presets.WithSimpleInterop()) +} diff --git a/tests/supervisor/interop_sync_test.go b/tests/supervisor/sync/resync_test.go similarity index 99% rename from tests/supervisor/interop_sync_test.go rename to tests/supervisor/sync/resync_test.go index 061d2cb22a..a142d0ea26 100644 --- a/tests/supervisor/interop_sync_test.go +++ b/tests/supervisor/sync/resync_test.go @@ -1,4 +1,4 @@ -package supervisor +package sync import ( "testing" diff --git a/tests/supervisor/sync_test.go b/tests/supervisor/sync/sync_test.go similarity index 99% rename from tests/supervisor/sync_test.go rename to tests/supervisor/sync/sync_test.go index c1b3ff0f8e..dea2b9735d 100644 --- a/tests/supervisor/sync_test.go +++ b/tests/supervisor/sync/sync_test.go @@ -1,4 +1,4 @@ -package supervisor +package sync import ( "testing"