From 6b7e1d456c56726b62884efb127988850fed7c20 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Mon, 22 Dec 2025 10:23:40 +1000 Subject: [PATCH 01/11] op-service: Implement supernode client. --- op-service/sources/supernode_client.go | 28 ++++ op-service/sources/supernode_client_test.go | 148 ++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 op-service/sources/supernode_client.go create mode 100644 op-service/sources/supernode_client_test.go diff --git a/op-service/sources/supernode_client.go b/op-service/sources/supernode_client.go new file mode 100644 index 0000000000000..c201145c42fba --- /dev/null +++ b/op-service/sources/supernode_client.go @@ -0,0 +1,28 @@ +package sources + +import ( + "context" + + "github.com/ethereum-optimism/optimism/op-service/client" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +type SuperNodeClient struct { + rpc client.RPC +} + +func NewSuperNodeClient(rpc client.RPC) *SuperNodeClient { + return &SuperNodeClient{ + rpc: rpc, + } +} + +func (c *SuperNodeClient) SuperRootAtTimestamp(ctx context.Context, timestamp uint64) (result eth.SuperRootAtTimestampResponse, err error) { + err = c.rpc.CallContext(ctx, &result, "superroot_atTimestamp", hexutil.Uint64(timestamp)) + return +} + +func (cl *SuperNodeClient) Close() { + cl.rpc.Close() +} diff --git a/op-service/sources/supernode_client_test.go b/op-service/sources/supernode_client_test.go new file mode 100644 index 0000000000000..951268cebd13f --- /dev/null +++ b/op-service/sources/supernode_client_test.go @@ -0,0 +1,148 @@ +package sources + +import ( + "context" + "errors" + "testing" + + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestSuperNodeClient_SuperRootAtTimestamp(t *testing.T) { + t.Run("Success", func(t *testing.T) { + ctx := context.Background() + rpc := new(mockRPC) + defer rpc.AssertExpectations(t) + client := NewSuperNodeClient(rpc) + + timestamp := uint64(245) + + chainA := eth.ChainIDFromUInt64(1) + chainB := eth.ChainIDFromUInt64(4) + expected := eth.SuperRootAtTimestampResponse{ + CurrentL1: eth.BlockID{ + Number: 305, + Hash: common.Hash{0xdd, 0xee, 0xff}, + }, + ChainIDs: []eth.ChainID{chainA, chainB}, + OptimisticAtTimestamp: map[eth.ChainID]eth.OutputWithRequiredL1{ + chainA: { + Output: ð.OutputResponse{ + Version: eth.Bytes32{0x01}, + OutputRoot: eth.Bytes32{0x11, 0x12}, + BlockRef: eth.L2BlockRef{ + Hash: common.Hash{0x22}, + Number: 472, + ParentHash: common.Hash{0xdd}, + Time: 9895839, + L1Origin: eth.BlockID{ + Hash: common.Hash{0xee}, + Number: 9802, + }, + SequenceNumber: 4982, + }, + WithdrawalStorageRoot: common.Hash{0xff}, + StateRoot: common.Hash{0xaa}, + }, + RequiredL1: eth.BlockID{ + Hash: common.Hash{0xbb}, + Number: 7842, + }, + }, + }, + Data: ð.SuperRootResponseData{ + VerifiedRequiredL1: eth.BlockID{ + Hash: common.Hash{0xcc}, + Number: 7411111, + }, + Super: eth.NewSuperV1(timestamp, eth.ChainIDAndOutput{ + ChainID: chainA, + Output: eth.Bytes32{0xa1}, + }, eth.ChainIDAndOutput{ + ChainID: chainB, + Output: eth.Bytes32{0xa2}, + }), + SuperRoot: eth.Bytes32{0xdd}, + }, + } + rpc.On("CallContext", ctx, new(eth.SuperRootAtTimestampResponse), + "superroot_atTimestamp", []any{hexutil.Uint64(timestamp)}).Run(func(args mock.Arguments) { + *args[1].(*eth.SuperRootAtTimestampResponse) = expected + }).Return([]error{nil}) + result, err := client.SuperRootAtTimestamp(ctx, timestamp) + require.NoError(t, err) + require.Equal(t, expected, result) + }) + + t.Run("NotFound", func(t *testing.T) { + ctx := context.Background() + rpc := new(mockRPC) + defer rpc.AssertExpectations(t) + client := NewSuperNodeClient(rpc) + + timestamp := uint64(245) + + chainA := eth.ChainIDFromUInt64(1) + chainB := eth.ChainIDFromUInt64(4) + expected := eth.SuperRootAtTimestampResponse{ + CurrentL1: eth.BlockID{ + Number: 305, + Hash: common.Hash{0xdd, 0xee, 0xff}, + }, + ChainIDs: []eth.ChainID{chainA, chainB}, + OptimisticAtTimestamp: map[eth.ChainID]eth.OutputWithRequiredL1{ + chainA: { + Output: ð.OutputResponse{ + Version: eth.Bytes32{0x01}, + OutputRoot: eth.Bytes32{0x11, 0x12}, + BlockRef: eth.L2BlockRef{ + Hash: common.Hash{0x22}, + Number: 472, + ParentHash: common.Hash{0xdd}, + Time: 9895839, + L1Origin: eth.BlockID{ + Hash: common.Hash{0xee}, + Number: 9802, + }, + SequenceNumber: 4982, + }, + WithdrawalStorageRoot: common.Hash{0xff}, + StateRoot: common.Hash{0xaa}, + }, + RequiredL1: eth.BlockID{ + Hash: common.Hash{0xbb}, + Number: 7842, + }, + }, + }, + Data: nil, // No super root found, so data is nil. + } + rpc.On("CallContext", ctx, new(eth.SuperRootAtTimestampResponse), + "superroot_atTimestamp", []any{hexutil.Uint64(timestamp)}).Run(func(args mock.Arguments) { + *args[1].(*eth.SuperRootAtTimestampResponse) = expected + }).Return([]error{nil}) + result, err := client.SuperRootAtTimestamp(ctx, timestamp) + require.NoError(t, err) + require.Equal(t, expected, result) + }) + + t.Run("Error", func(t *testing.T) { + ctx := context.Background() + rpc := new(mockRPC) + defer rpc.AssertExpectations(t) + client := NewSuperNodeClient(rpc) + + timestamp := uint64(245) + + rpc.On("CallContext", ctx, new(eth.SuperRootAtTimestampResponse), + "superroot_atTimestamp", []any{hexutil.Uint64(timestamp)}).Return([]error{errors.New("blah blah blah: not found")}) + _, err := client.SuperRootAtTimestamp(ctx, timestamp) + require.NotErrorIs(t, err, ethereum.NotFound) // should not convert to not found even though it contains not found + require.NotNil(t, err) + }) +} From 99250f995ee7c7bf550db5e991ab7f0cacc8e46b Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Mon, 22 Dec 2025 10:24:17 +1000 Subject: [PATCH 02/11] op-challenger: Integrate super node trace provider (toggled off) --- op-challenger/game/client/noop_sync.go | 16 +++++++ op-challenger/game/client/provider.go | 44 ++++++++++++------- op-challenger/game/client/rollup_sync.go | 3 +- op-challenger/game/client/rollup_sync_test.go | 5 ++- .../{supervisor.go => supervisor_sync.go} | 3 +- ...rvisor_test.go => supervisor_sync_test.go} | 5 ++- op-challenger/game/fault/register.go | 12 ++--- op-challenger/game/fault/register_task.go | 21 ++++----- .../game/fault/trace/super/provider.go | 34 ++++++++++---- .../fault/trace/super/provider_supernode.go | 8 ++-- .../trace/super/provider_supernode_test.go | 8 ++-- .../game/fault/trace/super/provider_test.go | 8 ++-- .../fault/trace/super/split_adapter_test.go | 2 +- .../game/fault/trace/super/super_cannon.go | 3 +- op-challenger/game/generic/player.go | 13 ++---- op-challenger/game/types/types.go | 13 +++++- op-challenger/runner/game_inputs.go | 2 +- op-e2e/actions/interop/proofs_test.go | 2 +- op-e2e/e2eutils/disputegame/helper.go | 2 +- .../disputegame/super_cannon_helper.go | 7 +-- .../e2eutils/disputegame/super_game_helper.go | 2 +- op-service/dial/dial.go | 9 ++++ 22 files changed, 144 insertions(+), 78 deletions(-) create mode 100644 op-challenger/game/client/noop_sync.go rename op-challenger/game/client/{supervisor.go => supervisor_sync.go} (80%) rename op-challenger/game/client/{supervisor_test.go => supervisor_sync_test.go} (92%) diff --git a/op-challenger/game/client/noop_sync.go b/op-challenger/game/client/noop_sync.go new file mode 100644 index 0000000000000..f6e5ffbc0cc61 --- /dev/null +++ b/op-challenger/game/client/noop_sync.go @@ -0,0 +1,16 @@ +package client + +import ( + "context" + + "github.com/ethereum-optimism/optimism/op-challenger/game/types" + "github.com/ethereum-optimism/optimism/op-service/eth" +) + +type NoopSyncStatusValidator struct{} + +func (n *NoopSyncStatusValidator) ValidateNodeSynced(_ context.Context, _ eth.BlockID) error { + return nil +} + +var _ types.SyncValidator = (*NoopSyncStatusValidator)(nil) diff --git a/op-challenger/game/client/provider.go b/op-challenger/game/client/provider.go index d827cae977f4e..cbc98a26f190f 100644 --- a/op-challenger/game/client/provider.go +++ b/op-challenger/game/client/provider.go @@ -2,10 +2,10 @@ package client import ( "context" - "errors" "fmt" "github.com/ethereum-optimism/optimism/op-challenger/config" + "github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-service/dial" "github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-service/sources/batching" @@ -13,8 +13,6 @@ import ( "github.com/ethereum/go-ethereum/log" ) -var ErrNotInSync = errors.New("local node too far behind") - type Provider struct { ctx context.Context logger log.Logger @@ -22,11 +20,13 @@ type Provider struct { l1Client *ethclient.Client caller *batching.MultiCaller - l2EL *ethclient.Client - rollupClient *sources.RollupClient - syncValidator *RollupSyncStatusValidator - supervisorClient *sources.SupervisorClient - toClose []func() + l2EL *ethclient.Client + rollupClient *sources.RollupClient + syncValidator *RollupSyncStatusValidator + supervisorClient *sources.SupervisorClient + superSyncValidator types.SyncValidator + superNodeClient *sources.SuperNodeClient + toClose []func() } func NewProvider(ctx context.Context, logger log.Logger, cfg *config.Config, l1Client *ethclient.Client) *Provider { @@ -96,12 +96,26 @@ func (c *Provider) RollupClients() (*sources.RollupClient, *RollupSyncStatusVali return rollupClient, c.syncValidator, nil } -func (c *Provider) SuperchainClients() (*sources.SupervisorClient, *SupervisorSyncValidator, error) { - supervisorClient, err := dial.DialSupervisorClientWithTimeout(c.ctx, c.logger, c.cfg.SupervisorRPC) - if err != nil { - return nil, nil, fmt.Errorf("failed to dial supervisor: %w", err) +func (c *Provider) SuperchainClients() (*sources.SupervisorClient, *sources.SuperNodeClient, types.SyncValidator, error) { + if c.supervisorClient != nil || c.superNodeClient != nil { + return c.supervisorClient, c.superNodeClient, c.superSyncValidator, nil + } + if false { + superNodeClient, err := dial.DialSuperNodeClientWithTimeout(c.ctx, c.logger, c.cfg.SupervisorRPC) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to dial supernode: %w", err) + } + c.superNodeClient = superNodeClient + c.superSyncValidator = &NoopSyncStatusValidator{} + c.toClose = append(c.toClose, superNodeClient.Close) + } else { + supervisorClient, err := dial.DialSupervisorClientWithTimeout(c.ctx, c.logger, c.cfg.SupervisorRPC) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to dial supervisor: %w", err) + } + c.supervisorClient = supervisorClient + c.superSyncValidator = NewSupervisorSyncValidator(supervisorClient) + c.toClose = append(c.toClose, supervisorClient.Close) } - c.supervisorClient = supervisorClient - c.toClose = append(c.toClose, supervisorClient.Close) - return supervisorClient, NewSupervisorSyncValidator(supervisorClient), nil + return c.supervisorClient, nil, c.superSyncValidator, nil } diff --git a/op-challenger/game/client/rollup_sync.go b/op-challenger/game/client/rollup_sync.go index 2b0d47c221483..495814339753f 100644 --- a/op-challenger/game/client/rollup_sync.go +++ b/op-challenger/game/client/rollup_sync.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-service/eth" ) @@ -27,7 +28,7 @@ func (s *RollupSyncStatusValidator) ValidateNodeSynced(ctx context.Context, game return fmt.Errorf("failed to retrieve local node sync status: %w", err) } if syncStatus.CurrentL1.Number <= gameL1Head.Number { - return fmt.Errorf("%w require L1 block above %v but at %v", ErrNotInSync, gameL1Head.Number, syncStatus.CurrentL1.Number) + return fmt.Errorf("%w require L1 block above %v but at %v", types.ErrNotInSync, gameL1Head.Number, syncStatus.CurrentL1.Number) } return nil } diff --git a/op-challenger/game/client/rollup_sync_test.go b/op-challenger/game/client/rollup_sync_test.go index 8914a1956f465..138d718b04843 100644 --- a/op-challenger/game/client/rollup_sync_test.go +++ b/op-challenger/game/client/rollup_sync_test.go @@ -5,6 +5,7 @@ import ( "errors" "testing" + "github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/stretchr/testify/require" ) @@ -34,7 +35,7 @@ func TestSyncStatusProvider(t *testing.T) { }, }, statusReqErr: nil, - expected: ErrNotInSync, + expected: types.ErrNotInSync, }, { name: "CurrentL1EqualToGameL1Head", @@ -45,7 +46,7 @@ func TestSyncStatusProvider(t *testing.T) { }, }, statusReqErr: nil, - expected: ErrNotInSync, + expected: types.ErrNotInSync, }, { name: "CurrentL1AboveGameL1Head", diff --git a/op-challenger/game/client/supervisor.go b/op-challenger/game/client/supervisor_sync.go similarity index 80% rename from op-challenger/game/client/supervisor.go rename to op-challenger/game/client/supervisor_sync.go index f4a57ed251918..b38147e73db4b 100644 --- a/op-challenger/game/client/supervisor.go +++ b/op-challenger/game/client/supervisor_sync.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-service/eth" ) @@ -27,7 +28,7 @@ func (s SupervisorSyncValidator) ValidateNodeSynced(ctx context.Context, gameL1H return fmt.Errorf("failed to retrieve sync status: %w", err) } if syncStatus.MinSyncedL1.Number <= gameL1Head.Number { - return fmt.Errorf("%w require L1 block above %v but at %v", ErrNotInSync, gameL1Head.Number, syncStatus.MinSyncedL1.Number) + return fmt.Errorf("%w require L1 block above %v but at %v", types.ErrNotInSync, gameL1Head.Number, syncStatus.MinSyncedL1.Number) } return nil } diff --git a/op-challenger/game/client/supervisor_test.go b/op-challenger/game/client/supervisor_sync_test.go similarity index 92% rename from op-challenger/game/client/supervisor_test.go rename to op-challenger/game/client/supervisor_sync_test.go index 44bf124af2766..64d6f2bcc831b 100644 --- a/op-challenger/game/client/supervisor_test.go +++ b/op-challenger/game/client/supervisor_sync_test.go @@ -5,6 +5,7 @@ import ( "errors" "testing" + "github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/stretchr/testify/require" ) @@ -30,7 +31,7 @@ func TestSupervisorSyncStatusProvider(t *testing.T) { syncStatus: eth.SupervisorSyncStatus{ MinSyncedL1: eth.L1BlockRef{Number: 99}, }, - expectedError: ErrNotInSync, + expectedError: types.ErrNotInSync, }, { name: "MinSyncedL1EqualToGameHead", @@ -38,7 +39,7 @@ func TestSupervisorSyncStatusProvider(t *testing.T) { syncStatus: eth.SupervisorSyncStatus{ MinSyncedL1: eth.L1BlockRef{Number: 100}, }, - expectedError: ErrNotInSync, + expectedError: types.ErrNotInSync, }, { name: "InSync", diff --git a/op-challenger/game/fault/register.go b/op-challenger/game/fault/register.go index afa4fb9fe2bcf..89c13b8e672ae 100644 --- a/op-challenger/game/fault/register.go +++ b/op-challenger/game/fault/register.go @@ -68,18 +68,18 @@ func RegisterGameTypes( registerTasks = append(registerTasks, NewCannonKonaRegisterTask(gameTypes.CannonKonaGameType, cfg, m, vm.NewKonaExecutor(), l2HeaderSource, rollupClient, syncValidator)) } if cfg.GameTypeEnabled(gameTypes.SuperCannonGameType) { - rootProvider, syncValidator, err := clients.SuperchainClients() + rootProvider, superNodeProvider, syncValidator, err := clients.SuperchainClients() if err != nil { return err } - registerTasks = append(registerTasks, NewSuperCannonRegisterTask(gameTypes.SuperCannonGameType, cfg, m, vm.NewOpProgramServerExecutor(logger), rootProvider, syncValidator)) + registerTasks = append(registerTasks, NewSuperCannonRegisterTask(gameTypes.SuperCannonGameType, cfg, m, vm.NewOpProgramServerExecutor(logger), rootProvider, superNodeProvider, syncValidator)) } if cfg.GameTypeEnabled(gameTypes.SuperCannonKonaGameType) { - rootProvider, syncValidator, err := clients.SuperchainClients() + rootProvider, superNodeProvider, syncValidator, err := clients.SuperchainClients() if err != nil { return err } - registerTasks = append(registerTasks, NewSuperCannonKonaRegisterTask(gameTypes.SuperCannonKonaGameType, cfg, m, vm.NewKonaSuperExecutor(), rootProvider, syncValidator)) + registerTasks = append(registerTasks, NewSuperCannonKonaRegisterTask(gameTypes.SuperCannonKonaGameType, cfg, m, vm.NewKonaSuperExecutor(), rootProvider, superNodeProvider, syncValidator)) } if cfg.GameTypeEnabled(gameTypes.PermissionedGameType) { l2HeaderSource, rollupClient, syncValidator, err := clients.SingleChainClients() @@ -89,11 +89,11 @@ func RegisterGameTypes( registerTasks = append(registerTasks, NewCannonRegisterTask(gameTypes.PermissionedGameType, cfg, m, vm.NewOpProgramServerExecutor(logger), l2HeaderSource, rollupClient, syncValidator)) } if cfg.GameTypeEnabled(gameTypes.SuperPermissionedGameType) { - rootProvider, syncValidator, err := clients.SuperchainClients() + rootProvider, superNodeProvider, syncValidator, err := clients.SuperchainClients() if err != nil { return err } - registerTasks = append(registerTasks, NewSuperCannonRegisterTask(gameTypes.SuperPermissionedGameType, cfg, m, vm.NewOpProgramServerExecutor(logger), rootProvider, syncValidator)) + registerTasks = append(registerTasks, NewSuperCannonRegisterTask(gameTypes.SuperPermissionedGameType, cfg, m, vm.NewOpProgramServerExecutor(logger), rootProvider, superNodeProvider, syncValidator)) } if cfg.GameTypeEnabled(gameTypes.FastGameType) { l2HeaderSource, rollupClient, syncValidator, err := clients.SingleChainClients() diff --git a/op-challenger/game/fault/register_task.go b/op-challenger/game/fault/register_task.go index be1a8b1c55453..0670bb987054f 100644 --- a/op-challenger/game/fault/register_task.go +++ b/op-challenger/game/fault/register_task.go @@ -35,7 +35,7 @@ type RegisterTask struct { gameType gameTypes.GameType skipPrestateValidation bool - syncValidator generic.SyncValidator + syncValidator gameTypes.SyncValidator getTopPrestateProvider func(ctx context.Context, prestateBlock uint64) (faultTypes.PrestateProvider, error) getBottomPrestateProvider func(ctx context.Context, prestateHash common.Hash) (faultTypes.PrestateProvider, error) @@ -51,12 +51,12 @@ type RegisterTask struct { poststateBlock uint64) (*trace.Accessor, error) } -func NewSuperCannonRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider super.RootProvider, syncValidator generic.SyncValidator) *RegisterTask { - return newSuperCannonVMRegisterTaskWithConfig(gameType, cfg, m, serverExecutor, rootProvider, syncValidator, cfg.Cannon, cfg.CannonAbsolutePreStateBaseURL, cfg.CannonAbsolutePreState) +func NewSuperCannonRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider super.RootProvider, superNodeProvider super.SuperNodeRootProvider, syncValidator gameTypes.SyncValidator) *RegisterTask { + return newSuperCannonVMRegisterTaskWithConfig(gameType, cfg, m, serverExecutor, rootProvider, superNodeProvider, syncValidator, cfg.Cannon, cfg.CannonAbsolutePreStateBaseURL, cfg.CannonAbsolutePreState) } -func NewSuperCannonKonaRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider super.RootProvider, syncValidator generic.SyncValidator) *RegisterTask { - return newSuperCannonVMRegisterTaskWithConfig(gameType, cfg, m, serverExecutor, rootProvider, syncValidator, cfg.CannonKona, cfg.CannonKonaAbsolutePreStateBaseURL, cfg.CannonKonaAbsolutePreState) +func NewSuperCannonKonaRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider super.RootProvider, superNodeProvider super.SuperNodeRootProvider, syncValidator gameTypes.SyncValidator) *RegisterTask { + return newSuperCannonVMRegisterTaskWithConfig(gameType, cfg, m, serverExecutor, rootProvider, superNodeProvider, syncValidator, cfg.CannonKona, cfg.CannonKonaAbsolutePreStateBaseURL, cfg.CannonKonaAbsolutePreState) } func newSuperCannonVMRegisterTaskWithConfig( @@ -65,7 +65,8 @@ func newSuperCannonVMRegisterTaskWithConfig( m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider super.RootProvider, - syncValidator generic.SyncValidator, + superNodeProvider super.SuperNodeRootProvider, + syncValidator gameTypes.SyncValidator, vmCfg vm.Config, preStateBaseURL *url.URL, preState string, @@ -100,16 +101,16 @@ func newSuperCannonVMRegisterTaskWithConfig( poststateBlock uint64) (*trace.Accessor, error) { provider := vmPrestateProvider.(*vm.PrestateProvider) preimagePrestateProvider := prestateProvider.(super.PreimagePrestateProvider) - return super.NewSuperCannonTraceAccessor(logger, m, vmCfg, serverExecutor, preimagePrestateProvider, rootProvider, provider.PrestatePath(), dir, l1Head, splitDepth, prestateBlock, poststateBlock) + return super.NewSuperCannonTraceAccessor(logger, m, vmCfg, serverExecutor, preimagePrestateProvider, rootProvider, superNodeProvider, provider.PrestatePath(), dir, l1Head, splitDepth, prestateBlock, poststateBlock) }, } } -func NewCannonRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator generic.SyncValidator) *RegisterTask { +func NewCannonRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator gameTypes.SyncValidator) *RegisterTask { return newCannonVMRegisterTaskWithConfig(gameType, cfg, m, serverExecutor, l2Client, rollupClient, syncValidator, cfg.Cannon, cfg.CannonAbsolutePreStateBaseURL, cfg.CannonAbsolutePreState) } -func NewCannonKonaRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator generic.SyncValidator) *RegisterTask { +func NewCannonKonaRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator gameTypes.SyncValidator) *RegisterTask { return newCannonVMRegisterTaskWithConfig(gameType, cfg, m, serverExecutor, l2Client, rollupClient, syncValidator, cfg.CannonKona, cfg.CannonKonaAbsolutePreStateBaseURL, cfg.CannonKonaAbsolutePreState) } @@ -120,7 +121,7 @@ func newCannonVMRegisterTaskWithConfig( serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, - syncValidator generic.SyncValidator, + syncValidator gameTypes.SyncValidator, vmCfg vm.Config, preStateBaseURL *url.URL, preState string, diff --git a/op-challenger/game/fault/trace/super/provider.go b/op-challenger/game/fault/trace/super/provider.go index a3587ed1f5571..456c26d52f436 100644 --- a/op-challenger/game/fault/trace/super/provider.go +++ b/op-challenger/game/fault/trace/super/provider.go @@ -36,7 +36,12 @@ type RootProvider interface { AllSafeDerivedAt(ctx context.Context, derivedFrom eth.BlockID) (map[eth.ChainID]eth.BlockID, error) } -type SuperTraceProvider struct { +type SuperTraceProvider interface { + types.TraceProvider + PreimageTraceProvider +} + +type SupervisorSuperTraceProvider struct { PreimagePrestateProvider logger log.Logger rollupCfgs *RollupConfigs @@ -47,8 +52,18 @@ type SuperTraceProvider struct { gameDepth types.Depth } -func NewSuperTraceProvider(logger log.Logger, rollupCfgs *RollupConfigs, prestateProvider PreimagePrestateProvider, rootProvider RootProvider, l1Head eth.BlockID, gameDepth types.Depth, prestateTimestamp, poststateTimestamp uint64) *SuperTraceProvider { - return &SuperTraceProvider{ +func NewSuperTraceProvider(logger log.Logger, rollupCfgs *RollupConfigs, prestateProvider PreimagePrestateProvider, rootProvider RootProvider, superNodeProvider SuperNodeRootProvider, l1Head eth.BlockID, gameDepth types.Depth, prestateTimestamp, poststateTimestamp uint64) SuperTraceProvider { + if rootProvider == nil && superNodeProvider != nil { + return NewSuperNodeTraceProvider(logger, prestateProvider, superNodeProvider, l1Head, gameDepth, prestateTimestamp, poststateTimestamp) + } else if rootProvider != nil && superNodeProvider == nil { + return NewSupervisorSuperTraceProvider(logger, rollupCfgs, prestateProvider, rootProvider, l1Head, gameDepth, prestateTimestamp, poststateTimestamp) + } else { + panic("Invalid configuration: must provide either a super node provider or a root provider, but not both.") + } +} + +func NewSupervisorSuperTraceProvider(logger log.Logger, rollupCfgs *RollupConfigs, prestateProvider PreimagePrestateProvider, rootProvider RootProvider, l1Head eth.BlockID, gameDepth types.Depth, prestateTimestamp, poststateTimestamp uint64) *SupervisorSuperTraceProvider { + return &SupervisorSuperTraceProvider{ logger: logger, rollupCfgs: rollupCfgs, PreimagePrestateProvider: prestateProvider, @@ -60,7 +75,7 @@ func NewSuperTraceProvider(logger log.Logger, rollupCfgs *RollupConfigs, prestat } } -func (s *SuperTraceProvider) Get(ctx context.Context, pos types.Position) (common.Hash, error) { +func (s *SupervisorSuperTraceProvider) Get(ctx context.Context, pos types.Position) (common.Hash, error) { preimage, err := s.GetPreimageBytes(ctx, pos) if err != nil { return common.Hash{}, err @@ -68,7 +83,7 @@ func (s *SuperTraceProvider) Get(ctx context.Context, pos types.Position) (commo return crypto.Keccak256Hash(preimage), nil } -func (s *SuperTraceProvider) GetPreimageBytes(ctx context.Context, pos types.Position) ([]byte, error) { +func (s *SupervisorSuperTraceProvider) GetPreimageBytes(ctx context.Context, pos types.Position) ([]byte, error) { // Find the timestamp and step at position timestamp, step, err := s.ComputeStep(pos) if err != nil { @@ -165,7 +180,7 @@ func (s *SuperTraceProvider) GetPreimageBytes(ctx context.Context, pos types.Pos return expectedState.Marshal(), nil } -func (s *SuperTraceProvider) ComputeStep(pos types.Position) (timestamp uint64, step uint64, err error) { +func (s *SupervisorSuperTraceProvider) ComputeStep(pos types.Position) (timestamp uint64, step uint64, err error) { bigIdx := pos.TraceIndex(s.gameDepth) if !bigIdx.IsUint64() { err = fmt.Errorf("%w: %v", ErrIndexTooBig, bigIdx) @@ -184,13 +199,14 @@ func (s *SuperTraceProvider) ComputeStep(pos types.Position) (timestamp uint64, return } -func (s *SuperTraceProvider) GetStepData(_ context.Context, _ types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) { +func (s *SupervisorSuperTraceProvider) GetStepData(_ context.Context, _ types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) { return nil, nil, nil, ErrGetStepData } -func (s *SuperTraceProvider) GetL2BlockNumberChallenge(_ context.Context) (*types.InvalidL2BlockNumberChallenge, error) { +func (s *SupervisorSuperTraceProvider) GetL2BlockNumberChallenge(_ context.Context) (*types.InvalidL2BlockNumberChallenge, error) { // Never need to challenge L2 block number for super root games. return nil, types.ErrL2BlockNumberValid } -var _ types.TraceProvider = (*SuperTraceProvider)(nil) +var _ types.TraceProvider = (*SupervisorSuperTraceProvider)(nil) +var _ SuperTraceProvider = (*SupervisorSuperTraceProvider)(nil) diff --git a/op-challenger/game/fault/trace/super/provider_supernode.go b/op-challenger/game/fault/trace/super/provider_supernode.go index 91ba757c32b79..60315cefb0647 100644 --- a/op-challenger/game/fault/trace/super/provider_supernode.go +++ b/op-challenger/game/fault/trace/super/provider_supernode.go @@ -5,8 +5,8 @@ import ( "fmt" "slices" - "github.com/ethereum-optimism/optimism/op-challenger/game/client" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" + types2 "github.com/ethereum-optimism/optimism/op-challenger/game/types" interopTypes "github.com/ethereum-optimism/optimism/op-program/client/interop/types" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/common" @@ -55,7 +55,7 @@ func (s *SuperNodeTraceProvider) getPreimageBytesAtTimestampBoundary(ctx context } if root.CurrentL1.Number < s.l1Head.Number { // Node has not processed the game's L1 head so it is not safe to play until it syncs further. - return nil, client.ErrNotInSync + return nil, types2.ErrNotInSync } if root.Data == nil { // No block at this timestamp so it must be invalid @@ -83,7 +83,7 @@ func (s *SuperNodeTraceProvider) GetPreimageBytes(ctx context.Context, pos types return nil, fmt.Errorf("failed to retrieve previous super root at timestamp %v: %w", timestamp, err) } if prevRoot.CurrentL1.Number < s.l1Head.Number { - return nil, client.ErrNotInSync + return nil, types2.ErrNotInSync } if prevRoot.Data == nil { // No block at this timestamp so it must be invalid @@ -100,7 +100,7 @@ func (s *SuperNodeTraceProvider) GetPreimageBytes(ctx context.Context, pos types return nil, fmt.Errorf("failed to retrieve next super root at timestamp %v: %w", nextTimestamp, err) } if nextRoot.CurrentL1.Number < s.l1Head.Number { - return nil, client.ErrNotInSync + return nil, types2.ErrNotInSync } prevSuper := prevRoot.Data.Super diff --git a/op-challenger/game/fault/trace/super/provider_supernode_test.go b/op-challenger/game/fault/trace/super/provider_supernode_test.go index f1ee1ed29ec8f..d25856d6265bb 100644 --- a/op-challenger/game/fault/trace/super/provider_supernode_test.go +++ b/op-challenger/game/fault/trace/super/provider_supernode_test.go @@ -7,8 +7,8 @@ import ( "math/rand" "testing" - "github.com/ethereum-optimism/optimism/op-challenger/game/client" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" + types2 "github.com/ethereum-optimism/optimism/op-challenger/game/types" interopTypes "github.com/ethereum-optimism/optimism/op-program/client/interop/types" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/testlog" @@ -311,7 +311,7 @@ func TestSuperNodeProvider_Get(t *testing.T) { } stubSupervisor.Add(response) _, err := provider.Get(context.Background(), types.RootPosition) - require.ErrorIs(t, err, client.ErrNotInSync) + require.ErrorIs(t, err, types2.ErrNotInSync) }) t.Run("PreviousSuperRootNotInSync", func(t *testing.T) { @@ -321,7 +321,7 @@ func TestSuperNodeProvider_Get(t *testing.T) { ChainIDs: []eth.ChainID{eth.ChainIDFromUInt64(1), eth.ChainIDFromUInt64(2)}, }) _, err := provider.Get(context.Background(), types.NewPosition(gameDepth, big.NewInt(1))) - require.ErrorIs(t, err, client.ErrNotInSync) + require.ErrorIs(t, err, types2.ErrNotInSync) }) t.Run("NextSuperRootNotInSync", func(t *testing.T) { @@ -335,7 +335,7 @@ func TestSuperNodeProvider_Get(t *testing.T) { ChainIDs: []eth.ChainID{eth.ChainIDFromUInt64(1), eth.ChainIDFromUInt64(2)}, }) _, err := provider.Get(context.Background(), types.NewPosition(gameDepth, big.NewInt(1))) - require.ErrorIs(t, err, client.ErrNotInSync) + require.ErrorIs(t, err, types2.ErrNotInSync) }) } diff --git a/op-challenger/game/fault/trace/super/provider_test.go b/op-challenger/game/fault/trace/super/provider_test.go index 4fd59c9716abb..0da4a56af12f9 100644 --- a/op-challenger/game/fault/trace/super/provider_test.go +++ b/op-challenger/game/fault/trace/super/provider_test.go @@ -268,7 +268,7 @@ func TestComputeStep(t *testing.T) { rollupCfgs, err := NewRollupConfigs(vm.Config{}) require.NoError(t, err) // Uses a big game depth so the trace index doesn't fit in uint64 - provider := NewSuperTraceProvider(testlog.Logger(t, log.LvlInfo), rollupCfgs, nil, &stubRootProvider{}, eth.BlockID{}, 65, prestateTimestamp, poststateTimestamp) + provider := NewSupervisorSuperTraceProvider(testlog.Logger(t, log.LvlInfo), rollupCfgs, nil, &stubRootProvider{}, eth.BlockID{}, 65, prestateTimestamp, poststateTimestamp) // Left-most position in top game _, _, err = provider.ComputeStep(types.RootPosition) require.ErrorIs(t, err, ErrIndexTooBig) @@ -326,7 +326,7 @@ func TestComputeStep(t *testing.T) { }) } -func createProvider(t *testing.T) (*SuperTraceProvider, *stubRootProvider, eth.BlockID, *RollupConfigs) { +func createProvider(t *testing.T) (*SupervisorSuperTraceProvider, *stubRootProvider, eth.BlockID, *RollupConfigs) { logger := testlog.Logger(t, log.LvlInfo) l1Head := eth.BlockID{Number: 23542, Hash: common.Hash{0xab, 0xcd}} stubSupervisor := &stubRootProvider{ @@ -348,7 +348,7 @@ func createProvider(t *testing.T) (*SuperTraceProvider, *stubRootProvider, eth.B } rollupCfgs, err := NewRollupConfigsFromParsed(chain1Cfg, chain2Cfg) require.NoError(t, err) - provider := NewSuperTraceProvider(logger, rollupCfgs, nil, stubSupervisor, l1Head, gameDepth, prestateTimestamp, poststateTimestamp) + provider := NewSupervisorSuperTraceProvider(logger, rollupCfgs, nil, stubSupervisor, l1Head, gameDepth, prestateTimestamp, poststateTimestamp) return provider, stubSupervisor, l1Head, rollupCfgs } @@ -423,7 +423,7 @@ func createValidSuperRoots(l1Head eth.BlockID) (superRootData, superRootData) { return prev, next } -func expectValidTransition(t *testing.T, provider *SuperTraceProvider, prev superRootData, next superRootData) { +func expectValidTransition(t *testing.T, provider *SupervisorSuperTraceProvider, prev superRootData, next superRootData) { expectedFirstStep := &interopTypes.TransitionState{ SuperRoot: prev.super.Marshal(), PendingProgress: []interopTypes.OptimisticBlock{ diff --git a/op-challenger/game/fault/trace/super/split_adapter_test.go b/op-challenger/game/fault/trace/super/split_adapter_test.go index 7d1f4ffa35fb3..1808dddd657f5 100644 --- a/op-challenger/game/fault/trace/super/split_adapter_test.go +++ b/op-challenger/game/fault/trace/super/split_adapter_test.go @@ -123,7 +123,7 @@ func setupSplitAdapterTest(t *testing.T, depth types.Depth, prestateTimestamp ui creator := &capturingCreator{} rootProvider := &stubRootProvider{} prestateProvider := NewSuperRootPrestateProvider(rootProvider, prestateTimestamp) - traceProvider := NewSuperTraceProvider(testlog.Logger(t, log.LvlInfo), nil, prestateProvider, rootProvider, eth.BlockID{}, depth, prestateTimestamp, poststateTimestamp) + traceProvider := NewSupervisorSuperTraceProvider(testlog.Logger(t, log.LvlInfo), nil, prestateProvider, rootProvider, eth.BlockID{}, depth, prestateTimestamp, poststateTimestamp) adapter := SuperRootSplitAdapter(traceProvider, creator.Create) return creator, rootProvider, adapter } diff --git a/op-challenger/game/fault/trace/super/super_cannon.go b/op-challenger/game/fault/trace/super/super_cannon.go index 7c32773cb26a0..e57620e971dbf 100644 --- a/op-challenger/game/fault/trace/super/super_cannon.go +++ b/op-challenger/game/fault/trace/super/super_cannon.go @@ -26,6 +26,7 @@ func NewSuperCannonTraceAccessor( serverExecutor vm.OracleServerExecutor, prestateProvider PreimagePrestateProvider, rootProvider RootProvider, + superNodeProvider SuperNodeRootProvider, cannonPrestate string, dir string, l1Head eth.BlockID, @@ -37,7 +38,7 @@ func NewSuperCannonTraceAccessor( if err != nil { return nil, fmt.Errorf("failed to load rollup configs: %w", err) } - outputProvider := NewSuperTraceProvider(logger, rollupCfgs, prestateProvider, rootProvider, l1Head, splitDepth, prestateTimestamp, poststateTimestamp) + outputProvider := NewSuperTraceProvider(logger, rollupCfgs, prestateProvider, rootProvider, superNodeProvider, l1Head, splitDepth, prestateTimestamp, poststateTimestamp) cannonCreator := func(ctx context.Context, localContext common.Hash, depth types.Depth, claimInfo ClaimInfo) (types.TraceProvider, error) { logger := logger.New("agreedPrestate", hexutil.Bytes(claimInfo.AgreedPrestate), "claim", claimInfo.Claim, "localContext", localContext) subdir := filepath.Join(dir, localContext.Hex()) diff --git a/op-challenger/game/generic/player.go b/op-challenger/game/generic/player.go index 5545a51b7095f..55850dfd0ebae 100644 --- a/op-challenger/game/generic/player.go +++ b/op-challenger/game/generic/player.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" - "github.com/ethereum-optimism/optimism/op-challenger/game/client" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/common" @@ -27,12 +26,6 @@ type GenericGameLoader interface { GetStatus(context.Context) (gameTypes.GameStatus, error) } -type SyncValidator interface { - // ValidateNodeSynced checks that the local node is sufficiently up to date to play the game. - // It returns client.ErrNotInSync if the node is too far behind. - ValidateNodeSynced(ctx context.Context, gameL1Head eth.BlockID) error -} - type L1HeaderSource interface { HeaderByHash(context.Context, common.Hash) (*gethTypes.Header, error) } @@ -43,7 +36,7 @@ type GamePlayer struct { actor Actor loader GenericGameLoader logger log.Logger - syncValidator SyncValidator + syncValidator gameTypes.SyncValidator prestateValidators []PrestateValidator status gameTypes.GameStatus gameL1Head eth.BlockID @@ -59,7 +52,7 @@ func NewGenericGamePlayer( logger log.Logger, addr common.Address, loader GenericGameLoader, - syncValidator SyncValidator, + syncValidator gameTypes.SyncValidator, validators []PrestateValidator, l1HeaderSource L1HeaderSource, createActor ActorCreator, @@ -127,7 +120,7 @@ func (g *GamePlayer) ProgressGame(ctx context.Context) gameTypes.GameStatus { g.logger.Trace("Skipping completed game") return g.status } - if err := g.syncValidator.ValidateNodeSynced(ctx, g.gameL1Head); errors.Is(err, client.ErrNotInSync) { + if err := g.syncValidator.ValidateNodeSynced(ctx, g.gameL1Head); errors.Is(err, gameTypes.ErrNotInSync) { g.logger.Warn("Local node not sufficiently up to date", "err", err) return g.status } else if err != nil { diff --git a/op-challenger/game/types/types.go b/op-challenger/game/types/types.go index 4579e5476c1e9..1df72da5dde43 100644 --- a/op-challenger/game/types/types.go +++ b/op-challenger/game/types/types.go @@ -1,13 +1,18 @@ package types import ( + "context" "errors" "fmt" + "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/common" ) -var ErrInvalidPrestate = errors.New("absolute prestate does not match") +var ( + ErrNotInSync = errors.New("local node too far behind") + ErrInvalidPrestate = errors.New("absolute prestate does not match") +) type GameStatus uint8 @@ -45,3 +50,9 @@ type GameMetadata struct { Timestamp uint64 Proxy common.Address } + +type SyncValidator interface { + // ValidateNodeSynced checks that the local node is sufficiently up to date to play the game. + // It returns client.ErrNotInSync if the node is too far behind. + ValidateNodeSynced(ctx context.Context, gameL1Head eth.BlockID) error +} diff --git a/op-challenger/runner/game_inputs.go b/op-challenger/runner/game_inputs.go index bebc9098a728c..bbd0c7039279d 100644 --- a/op-challenger/runner/game_inputs.go +++ b/op-challenger/runner/game_inputs.go @@ -96,7 +96,7 @@ func createGameInputsInterop(ctx context.Context, log log.Logger, client *source prestateProvider := super.NewSuperRootPrestateProvider(client, agreedTimestamp) gameDepth := types.Depth(30) - provider := super.NewSuperTraceProvider(log, nil, prestateProvider, client, l1Head.ID(), gameDepth, agreedTimestamp, claimTimestamp+10) + provider := super.NewSupervisorSuperTraceProvider(log, nil, prestateProvider, client, l1Head.ID(), gameDepth, agreedTimestamp, claimTimestamp+10) var agreedPrestate []byte var claim common.Hash switch rand.IntN(3) { diff --git a/op-e2e/actions/interop/proofs_test.go b/op-e2e/actions/interop/proofs_test.go index a680f0dd8f398..3913903a9657f 100644 --- a/op-e2e/actions/interop/proofs_test.go +++ b/op-e2e/actions/interop/proofs_test.go @@ -1468,7 +1468,7 @@ func runChallengerTest(gt *testing.T, test *transitionTest, actors *dsl.InteropA gameDepth := challengerTypes.Depth(30) rollupCfgs, err := super.NewRollupConfigsFromParsed(actors.ChainA.RollupCfg, actors.ChainB.RollupCfg) require.NoError(t, err) - provider := super.NewSuperTraceProvider(logger, rollupCfgs, prestateProvider, actors.Supervisor, l1Head, gameDepth, startTimestamp, endTimestamp) + provider := super.NewSupervisorSuperTraceProvider(logger, rollupCfgs, prestateProvider, actors.Supervisor, l1Head, gameDepth, startTimestamp, endTimestamp) var agreedPrestate []byte if test.disputedTraceIndex > 0 { agreedPrestate, err = provider.GetPreimageBytes(t.Ctx(), challengerTypes.NewPosition(gameDepth, big.NewInt(test.disputedTraceIndex-1))) diff --git a/op-e2e/e2eutils/disputegame/helper.go b/op-e2e/e2eutils/disputegame/helper.go index bca17232095e0..d5021d7314de6 100644 --- a/op-e2e/e2eutils/disputegame/helper.go +++ b/op-e2e/e2eutils/disputegame/helper.go @@ -293,7 +293,7 @@ func (h *FactoryHelper) startSuperCannonGameOfType(ctx context.Context, timestam prestateProvider := super.NewSuperRootPrestateProvider(rootProvider, prestateTimestamp) rollupCfgs, err := super.NewRollupConfigsFromParsed(h.System.RollupCfgs()...) require.NoError(h.T, err, "failed to create rollup configs") - provider := super.NewSuperTraceProvider(logger, rollupCfgs, prestateProvider, rootProvider, l1Head, splitDepth, prestateTimestamp, poststateTimestamp) + provider := super.NewSupervisorSuperTraceProvider(logger, rollupCfgs, prestateProvider, rootProvider, l1Head, splitDepth, prestateTimestamp, poststateTimestamp) return NewSuperCannonGameHelper(h.T, h.Client, h.Opts, h.PrivKey, game, h.FactoryAddr, createdEvent.DisputeProxy, provider, h.System) } diff --git a/op-e2e/e2eutils/disputegame/super_cannon_helper.go b/op-e2e/e2eutils/disputegame/super_cannon_helper.go index e441a938a1852..52ca34ecd7553 100644 --- a/op-e2e/e2eutils/disputegame/super_cannon_helper.go +++ b/op-e2e/e2eutils/disputegame/super_cannon_helper.go @@ -32,7 +32,7 @@ type SuperCannonGameHelper struct { CannonHelper } -func NewSuperCannonGameHelper(t *testing.T, client *ethclient.Client, opts *bind.TransactOpts, key *ecdsa.PrivateKey, game contracts.FaultDisputeGameContract, factoryAddr common.Address, gameAddr common.Address, provider *super.SuperTraceProvider, system DisputeSystem) *SuperCannonGameHelper { +func NewSuperCannonGameHelper(t *testing.T, client *ethclient.Client, opts *bind.TransactOpts, key *ecdsa.PrivateKey, game contracts.FaultDisputeGameContract, factoryAddr common.Address, gameAddr common.Address, provider super.SuperTraceProvider, system DisputeSystem) *SuperCannonGameHelper { superGameHelper := NewSuperGameHelper(t, require.New(t), client, opts, key, game, factoryAddr, gameAddr, provider, system) defaultChallengerOptions := func() []challenger.Option { return []challenger.Option{ @@ -75,6 +75,7 @@ func (g *SuperCannonGameHelper) CreateHonestActor(ctx context.Context, options . vm.NewOpProgramServerExecutor(logger), prestateProvider, supervisorClient, + nil, cfg.CannonAbsolutePreState, dir, l1Head, @@ -183,7 +184,7 @@ func (g *SuperCannonGameHelper) createSuperCannonTraceProvider(ctx context.Conte return translatingProvider.Original().(*cannon.CannonTraceProviderForTest) } -func (g *SuperCannonGameHelper) createSuperTraceProvider(ctx context.Context) *super.SuperTraceProvider { +func (g *SuperCannonGameHelper) createSuperTraceProvider(ctx context.Context) super.SuperTraceProvider { logger := testlog.Logger(g.t, log.LevelInfo).New("role", "superTraceProvider", "game", g.splitGame.Addr) rootProvider := g.System.SupervisorClient() splitDepth := g.splitGame.SplitDepth(ctx) @@ -193,7 +194,7 @@ func (g *SuperCannonGameHelper) createSuperTraceProvider(ctx context.Context) *s prestateProvider := super.NewSuperRootPrestateProvider(rootProvider, prestateTimestamp) rollupCfgs, err := super.NewRollupConfigsFromParsed(g.System.RollupCfgs()...) require.NoError(g.T, err, "failed to create rollup configs") - return super.NewSuperTraceProvider(logger, rollupCfgs, prestateProvider, rootProvider, l1Head, splitDepth, prestateTimestamp, poststateTimestamp) + return super.NewSupervisorSuperTraceProvider(logger, rollupCfgs, prestateProvider, rootProvider, l1Head, splitDepth, prestateTimestamp, poststateTimestamp) } // InitFirstDerivationGame builds a top-level game whose deepest node (at splitDepth) asserts the first diff --git a/op-e2e/e2eutils/disputegame/super_game_helper.go b/op-e2e/e2eutils/disputegame/super_game_helper.go index 09ad68f20898b..bce9559af1111 100644 --- a/op-e2e/e2eutils/disputegame/super_game_helper.go +++ b/op-e2e/e2eutils/disputegame/super_game_helper.go @@ -19,7 +19,7 @@ type SuperGameHelper struct { } func NewSuperGameHelper(t *testing.T, require *require.Assertions, client *ethclient.Client, opts *bind.TransactOpts, privKey *ecdsa.PrivateKey, - game contracts.FaultDisputeGameContract, factoryAddr common.Address, addr common.Address, correctOutputProvider *super.SuperTraceProvider, system DisputeSystem) *SuperGameHelper { + game contracts.FaultDisputeGameContract, factoryAddr common.Address, addr common.Address, correctOutputProvider super.SuperTraceProvider, system DisputeSystem) *SuperGameHelper { return &SuperGameHelper{ SplitGameHelper: SplitGameHelper{ T: t, diff --git a/op-service/dial/dial.go b/op-service/dial/dial.go index e6ce72628ed2c..fa0d36f885841 100644 --- a/op-service/dial/dial.go +++ b/op-service/dial/dial.go @@ -65,6 +65,15 @@ func DialSupervisorClientWithTimeout(ctx context.Context, log log.Logger, url st return sources.NewSupervisorClient(rpcCl), nil } +func DialSuperNodeClientWithTimeout(ctx context.Context, log log.Logger, url string, callerOpts ...client.RPCOption) (*sources.SuperNodeClient, error) { + rpcCl, err := dialClientWithTimeout(ctx, log, url, callerOpts...) + if err != nil { + return nil, err + } + + return sources.NewSuperNodeClient(rpcCl), nil +} + // DialRPCClientWithTimeout attempts to dial the RPC provider using the provided URL. // The timeout and retry logic is handled internally by the client. func DialRPCClientWithTimeout(ctx context.Context, log log.Logger, url string, opts ...rpc.ClientOption) (*rpc.Client, error) { From 50901dd807bab7ad3a15f83ce9fb47322b672f1e Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Mon, 22 Dec 2025 10:40:45 +1000 Subject: [PATCH 03/11] Make super rpc CLI generic. Add a config option to switch to super node. --- op-challenger/cmd/main_test.go | 34 +++---- op-challenger/config/config.go | 19 ++-- op-challenger/config/config_test.go | 14 +-- op-challenger/flags/flags.go | 20 ++-- op-challenger/game/client/provider.go | 6 +- .../trace/super/provider_supernode_test.go | 94 +++++++++---------- .../{provider.go => provider_supervisor.go} | 0 ...er_test.go => provider_supervisor_test.go} | 0 op-challenger/runner/runner.go | 6 +- 9 files changed, 97 insertions(+), 96 deletions(-) rename op-challenger/game/fault/trace/super/{provider.go => provider_supervisor.go} (100%) rename op-challenger/game/fault/trace/super/{provider_test.go => provider_supervisor_test.go} (100%) diff --git a/op-challenger/cmd/main_test.go b/op-challenger/cmd/main_test.go index 27752f545bff2..aa5fdc21a4a97 100644 --- a/op-challenger/cmd/main_test.go +++ b/op-challenger/cmd/main_test.go @@ -26,7 +26,7 @@ var ( network = "op-mainnet" testNetwork = "op-sepolia" l2EthRpc = "http://example.com:9545" - supervisorRpc = "http://example.com/supervisor" + superRpc = "http://example.com/super" cannonBin = "./bin/cannon" cannonServer = "./bin/op-program" cannonPreState = "./pre.json" @@ -95,15 +95,15 @@ func TestL1Beacon(t *testing.T) { }) } -func TestOpSupervisor(t *testing.T) { +func TestSuperRpc(t *testing.T) { t.Run("RequiredForSuperCannon", func(t *testing.T) { - verifyArgsInvalid(t, "flag supervisor-rpc is required", addRequiredArgsExcept(gameTypes.SuperCannonGameType, "--supervisor-rpc")) + verifyArgsInvalid(t, "flag super-rpc is required", addRequiredArgsExcept(gameTypes.SuperCannonGameType, "--super-rpc")) }) t.Run("RequiredForSuperPermissioned", func(t *testing.T) { - verifyArgsInvalid(t, "flag supervisor-rpc is required", addRequiredArgsExcept(gameTypes.SuperPermissionedGameType, "--supervisor-rpc")) + verifyArgsInvalid(t, "flag super-rpc is required", addRequiredArgsExcept(gameTypes.SuperPermissionedGameType, "--super-rpc")) }) t.Run("RequiredForSuperCannonKona", func(t *testing.T) { - verifyArgsInvalid(t, "flag supervisor-rpc is required", addRequiredArgsExcept(gameTypes.SuperCannonKonaGameType, "--supervisor-rpc")) + verifyArgsInvalid(t, "flag super-rpc is required", addRequiredArgsExcept(gameTypes.SuperCannonKonaGameType, "--super-rpc")) }) for _, gameType := range gameTypes.SupportedGameTypes { @@ -113,26 +113,26 @@ func TestOpSupervisor(t *testing.T) { } t.Run("NotRequiredForGameType-"+gameType.String(), func(t *testing.T) { - configForArgs(t, addRequiredArgsExcept(gameType, "--supervisor-rpc")) + configForArgs(t, addRequiredArgsExcept(gameType, "--super-rpc")) }) } t.Run("Valid-SuperCannon", func(t *testing.T) { - url := "http://localhost/supervisor" - cfg := configForArgs(t, addRequiredArgsExcept(gameTypes.SuperCannonGameType, "--supervisor-rpc", "--supervisor-rpc", url)) - require.Equal(t, url, cfg.SupervisorRPC) + url := "http://localhost/super" + cfg := configForArgs(t, addRequiredArgsExcept(gameTypes.SuperCannonGameType, "--super-rpc", "--super-rpc", url)) + require.Equal(t, url, cfg.SuperRPC) }) t.Run("Valid-SuperPermissioned", func(t *testing.T) { - url := "http://localhost/supervisor" - cfg := configForArgs(t, addRequiredArgsExcept(gameTypes.SuperPermissionedGameType, "--supervisor-rpc", "--supervisor-rpc", url)) - require.Equal(t, url, cfg.SupervisorRPC) + url := "http://localhost/super" + cfg := configForArgs(t, addRequiredArgsExcept(gameTypes.SuperPermissionedGameType, "--super-rpc", "--super-rpc", url)) + require.Equal(t, url, cfg.SuperRPC) }) t.Run("Valid-SuperCannonKona", func(t *testing.T) { - url := "http://localhost/supervisor" - cfg := configForArgs(t, addRequiredArgsExcept(gameTypes.SuperCannonKonaGameType, "--supervisor-rpc", "--supervisor-rpc", url)) - require.Equal(t, url, cfg.SupervisorRPC) + url := "http://localhost/super" + cfg := configForArgs(t, addRequiredArgsExcept(gameTypes.SuperCannonKonaGameType, "--super-rpc", "--super-rpc", url)) + require.Equal(t, url, cfg.SuperRPC) }) } @@ -1023,7 +1023,7 @@ func requiredArgs(gameType gameTypes.GameType) map[string]string { func addRequiredSuperCannonArgs(args map[string]string) { addRequiredCannonBaseArgs(args) - args["--supervisor-rpc"] = supervisorRpc + args["--super-rpc"] = superRpc } func addRequiredCannonArgs(args map[string]string) { @@ -1056,7 +1056,7 @@ func addRequiredCannonKonaBaseArgs(args map[string]string) { func addRequiredSuperCannonKonaArgs(args map[string]string) { addRequiredCannonKonaBaseArgs(args) - args["--supervisor-rpc"] = supervisorRpc + args["--super-rpc"] = superRpc } func toArgList(req map[string]string) []string { diff --git a/op-challenger/config/config.go b/op-challenger/config/config.go index 98875012e58b8..e073d506912ac 100644 --- a/op-challenger/config/config.go +++ b/op-challenger/config/config.go @@ -32,8 +32,8 @@ var ( ErrMissingCannonKonaInfoFreq = errors.New("missing cannon kona info freq") ErrMissingDepsetConfig = errors.New("missing network or depset config path") - ErrMissingRollupRpc = errors.New("missing rollup rpc url") - ErrMissingSupervisorRpc = errors.New("missing supervisor rpc url") + ErrMissingRollupRpc = errors.New("missing rollup rpc url") + ErrMissingSuperRpc = errors.New("missing super rpc url") ) const ( @@ -73,7 +73,8 @@ type Config struct { GameTypes []gameTypes.GameType // Type of games supported RollupRpc string // L2 Rollup RPC Url - SupervisorRPC string // L2 supervisor RPC URL + SuperRPC string // L2 RPC URL for super roots + UserSuperNode bool // Temporary: True to use op-supernode APIs, false for op-supervisor APIs L2Rpcs []string // L2 RPC Url // Specific to the cannon trace provider @@ -108,7 +109,7 @@ func NewInteropConfig( gameFactoryAddress common.Address, l1EthRpc string, l1BeaconApi string, - supervisorRpc string, + superRpc string, l2Rpcs []string, datadir string, supportedGameTypes ...gameTypes.GameType, @@ -116,7 +117,7 @@ func NewInteropConfig( return Config{ L1EthRpc: l1EthRpc, L1Beacon: l1BeaconApi, - SupervisorRPC: supervisorRpc, + SuperRPC: superRpc, L2Rpcs: l2Rpcs, GameFactoryAddress: gameFactoryAddress, MaxConcurrency: uint(runtime.NumCPU()), @@ -235,8 +236,8 @@ func (c Config) Check() error { return ErrMaxConcurrencyZero } if c.GameTypeEnabled(gameTypes.SuperCannonGameType) || c.GameTypeEnabled(gameTypes.SuperPermissionedGameType) { - if c.SupervisorRPC == "" { - return ErrMissingSupervisorRpc + if c.SuperRPC == "" { + return ErrMissingSuperRpc } if len(c.Cannon.Networks) == 0 && c.Cannon.DepsetConfigPath == "" { @@ -255,8 +256,8 @@ func (c Config) Check() error { } } if c.GameTypeEnabled(gameTypes.SuperCannonKonaGameType) { - if c.SupervisorRPC == "" { - return ErrMissingSupervisorRpc + if c.SuperRPC == "" { + return ErrMissingSuperRpc } if len(c.CannonKona.Networks) == 0 && c.CannonKona.DepsetConfigPath == "" { diff --git a/op-challenger/config/config_test.go b/op-challenger/config/config_test.go index 0791dbf8d5f9f..25d8b9c7b45a8 100644 --- a/op-challenger/config/config_test.go +++ b/op-challenger/config/config_test.go @@ -29,7 +29,7 @@ var ( validDatadir = "/tmp/data" validL2Rpc = "http://localhost:9545" validRollupRpc = "http://localhost:8555" - validSupervisorRpc = "http://localhost/supervisor" + validSuperRpc = "http://localhost/super" nonExistingFile = "path/to/nonexistent/file" @@ -66,7 +66,7 @@ func ensureExists(path string) error { } func applyValidConfigForSuperCannon(t *testing.T, cfg *Config) { - cfg.SupervisorRPC = validSupervisorRpc + cfg.SuperRPC = validSuperRpc applyValidConfigForCannon(t, cfg) } @@ -99,7 +99,7 @@ func applyValidConfigForCannonKona(t *testing.T, cfg *Config) { } func applyValidConfigForSuperCannonKona(t *testing.T, cfg *Config) { - cfg.SupervisorRPC = validSupervisorRpc + cfg.SuperRPC = validSuperRpc applyValidConfigForCannonKona(t, cfg) } @@ -530,19 +530,19 @@ func TestRollupRpcNotRequiredForInterop(t *testing.T) { }) } -func TestSupervisorRpc(t *testing.T) { +func TestSuperRpc(t *testing.T) { for _, gameType := range gameTypes.SupportedGameTypes { gameType := gameType if gameType == gameTypes.SuperCannonGameType || gameType == gameTypes.SuperPermissionedGameType || gameType == gameTypes.SuperCannonKonaGameType { t.Run("RequiredFor"+gameType.String(), func(t *testing.T) { config := validConfig(t, gameType) - config.SupervisorRPC = "" - require.ErrorIs(t, config.Check(), ErrMissingSupervisorRpc) + config.SuperRPC = "" + require.ErrorIs(t, config.Check(), ErrMissingSuperRpc) }) } else { t.Run("NotRequiredFor"+gameType.String(), func(t *testing.T) { config := validConfig(t, gameType) - config.SupervisorRPC = "" + config.SuperRPC = "" require.NoError(t, config.Check()) }) } diff --git a/op-challenger/flags/flags.go b/op-challenger/flags/flags.go index 0c83b2c84a4a6..60462df79529b 100644 --- a/op-challenger/flags/flags.go +++ b/op-challenger/flags/flags.go @@ -54,10 +54,10 @@ var ( Usage: "Address of L1 Beacon API endpoint to use", EnvVars: prefixEnvVars("L1_BEACON"), } - SupervisorRpcFlag = &cli.StringFlag{ - Name: "supervisor-rpc", - Usage: "Provider URL for supervisor RPC", - EnvVars: prefixEnvVars("SUPERVISOR_RPC"), + SuperRpcFlag = &cli.StringFlag{ + Name: "super-rpc", + Usage: "Provider URL for super roots", + EnvVars: prefixEnvVars("SUPER_RPC"), } RollupRpcFlag = &cli.StringFlag{ Name: "rollup-rpc", @@ -267,7 +267,7 @@ var optionalFlags = []cli.Flag{ FactoryAddressFlag, GameTypesFlag, MaxConcurrencyFlag, - SupervisorRpcFlag, + SuperRpcFlag, L2EthRpcFlag, L2ExperimentalEthRpcFlag, MaxPendingTransactionsFlag, @@ -338,8 +338,8 @@ func CheckCannonBaseFlags(ctx *cli.Context) error { } func CheckSuperCannonFlags(ctx *cli.Context) error { - if !ctx.IsSet(SupervisorRpcFlag.Name) { - return fmt.Errorf("flag %v is required", SupervisorRpcFlag.Name) + if !ctx.IsSet(SuperRpcFlag.Name) { + return fmt.Errorf("flag %v is required", SuperRpcFlag.Name) } if !ctx.IsSet(flags.NetworkFlagName) && !(RollupConfigFlag.IsSet(ctx, gameTypes.CannonGameType) && L2GenesisFlag.IsSet(ctx, gameTypes.CannonGameType) && DepsetConfigFlag.IsSet(ctx, gameTypes.CannonGameType)) { @@ -356,8 +356,8 @@ func CheckSuperCannonFlags(ctx *cli.Context) error { } func CheckSuperCannonKonaFlags(ctx *cli.Context) error { - if !ctx.IsSet(SupervisorRpcFlag.Name) { - return fmt.Errorf("flag %v is required", SupervisorRpcFlag.Name) + if !ctx.IsSet(SuperRpcFlag.Name) { + return fmt.Errorf("flag %v is required", SuperRpcFlag.Name) } if !ctx.IsSet(flags.NetworkFlagName) && !(RollupConfigFlag.IsSet(ctx, gameTypes.CannonKonaGameType) && L2GenesisFlag.IsSet(ctx, gameTypes.CannonKonaGameType) && DepsetConfigFlag.IsSet(ctx, gameTypes.CannonKonaGameType)) { @@ -601,7 +601,7 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro MinUpdateInterval: ctx.Duration(MinUpdateInterval.Name), AdditionalBondClaimants: claimants, RollupRpc: ctx.String(RollupRpcFlag.Name), - SupervisorRPC: ctx.String(SupervisorRpcFlag.Name), + SuperRPC: ctx.String(SuperRpcFlag.Name), Cannon: vm.Config{ VmType: gameTypes.CannonGameType, L1: l1EthRpc, diff --git a/op-challenger/game/client/provider.go b/op-challenger/game/client/provider.go index cbc98a26f190f..1c510b6965b5b 100644 --- a/op-challenger/game/client/provider.go +++ b/op-challenger/game/client/provider.go @@ -100,8 +100,8 @@ func (c *Provider) SuperchainClients() (*sources.SupervisorClient, *sources.Supe if c.supervisorClient != nil || c.superNodeClient != nil { return c.supervisorClient, c.superNodeClient, c.superSyncValidator, nil } - if false { - superNodeClient, err := dial.DialSuperNodeClientWithTimeout(c.ctx, c.logger, c.cfg.SupervisorRPC) + if c.cfg.UserSuperNode { + superNodeClient, err := dial.DialSuperNodeClientWithTimeout(c.ctx, c.logger, c.cfg.SuperRPC) if err != nil { return nil, nil, nil, fmt.Errorf("failed to dial supernode: %w", err) } @@ -109,7 +109,7 @@ func (c *Provider) SuperchainClients() (*sources.SupervisorClient, *sources.Supe c.superSyncValidator = &NoopSyncStatusValidator{} c.toClose = append(c.toClose, superNodeClient.Close) } else { - supervisorClient, err := dial.DialSupervisorClientWithTimeout(c.ctx, c.logger, c.cfg.SupervisorRPC) + supervisorClient, err := dial.DialSupervisorClientWithTimeout(c.ctx, c.logger, c.cfg.SuperRPC) if err != nil { return nil, nil, nil, fmt.Errorf("failed to dial supervisor: %w", err) } diff --git a/op-challenger/game/fault/trace/super/provider_supernode_test.go b/op-challenger/game/fault/trace/super/provider_supernode_test.go index d25856d6265bb..3c526b528190f 100644 --- a/op-challenger/game/fault/trace/super/provider_supernode_test.go +++ b/op-challenger/game/fault/trace/super/provider_supernode_test.go @@ -20,7 +20,7 @@ import ( func TestSuperNodeProvider_Get(t *testing.T) { t.Run("AtPostState", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) expectedSuper := eth.NewSuperV1(poststateTimestamp, eth.ChainIDAndOutput{ ChainID: eth.ChainIDFromUInt64(1), Output: eth.Bytes32{0xbb}, @@ -34,14 +34,14 @@ func TestSuperNodeProvider_Get(t *testing.T) { SuperRoot: eth.SuperRoot(expectedSuper), }, } - stubSupervisor.Add(response) + stubSuperNode.Add(response) claim, err := provider.Get(context.Background(), types.RootPosition) require.NoError(t, err) require.Equal(t, common.Hash(eth.SuperRoot(expectedSuper)), claim) }) t.Run("AtNewTimestamp", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) expectedSuper := eth.NewSuperV1(prestateTimestamp+1, eth.ChainIDAndOutput{ ChainID: eth.ChainIDFromUInt64(1), Output: eth.Bytes32{0xbb}, @@ -55,23 +55,23 @@ func TestSuperNodeProvider_Get(t *testing.T) { SuperRoot: eth.SuperRoot(expectedSuper), }, } - stubSupervisor.Add(response) + stubSuperNode.Add(response) claim, err := provider.Get(context.Background(), types.NewPosition(gameDepth, big.NewInt(StepsPerTimestamp-1))) require.NoError(t, err) require.Equal(t, common.Hash(eth.SuperRoot(expectedSuper)), claim) }) t.Run("ValidTransitionBetweenFirstTwoSuperRoots", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) prev, next := createValidSuperNodeSuperRoots(l1Head) - stubSupervisor.Add(prev) - stubSupervisor.Add(next) + stubSuperNode.Add(prev) + stubSuperNode.Add(next) expectSuperNodeValidTransition(t, provider, prev, next) }) t.Run("Step0SuperRootIsSafeBeforeGameL1Head", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) expectedSuper := eth.NewSuperV1(poststateTimestamp, eth.ChainIDAndOutput{ ChainID: eth.ChainIDFromUInt64(1), Output: eth.Bytes32{0xbb}, @@ -85,14 +85,14 @@ func TestSuperNodeProvider_Get(t *testing.T) { SuperRoot: eth.SuperRoot(expectedSuper), }, } - stubSupervisor.Add(response) + stubSuperNode.Add(response) claim, err := provider.Get(context.Background(), types.RootPosition) require.NoError(t, err) require.Equal(t, common.Hash(eth.SuperRoot(expectedSuper)), claim) }) t.Run("Step0SuperRootNotSafeAtGameL1Head", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) expectedSuper := eth.NewSuperV1(poststateTimestamp, eth.ChainIDAndOutput{ ChainID: eth.ChainIDFromUInt64(1), Output: eth.Bytes32{0xbb}, @@ -106,31 +106,31 @@ func TestSuperNodeProvider_Get(t *testing.T) { SuperRoot: eth.SuperRoot(expectedSuper), }, } - stubSupervisor.Add(response) + stubSuperNode.Add(response) claim, err := provider.Get(context.Background(), types.RootPosition) require.NoError(t, err) require.Equal(t, InvalidTransitionHash, claim) }) t.Run("NextSuperRootSafeBeforeGameL1Head", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) prev, next := createValidSuperNodeSuperRoots(l1Head) // Make super roots be safe earlier prev.Data.VerifiedRequiredL1 = eth.BlockID{Number: l1Head.Number - 10, Hash: common.Hash{0xaa}} next.Data.VerifiedRequiredL1 = eth.BlockID{Number: l1Head.Number - 5, Hash: common.Hash{0xbb}} - stubSupervisor.Add(prev) - stubSupervisor.Add(next) + stubSuperNode.Add(prev) + stubSuperNode.Add(next) expectSuperNodeValidTransition(t, provider, prev, next) }) t.Run("PreviousSuperRootNotSafeAtGameL1Head", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) prev, next := createValidSuperNodeSuperRoots(l1Head) // Make super roots be safe only after L1 head prev.Data.VerifiedRequiredL1 = eth.BlockID{Number: l1Head.Number + 1, Hash: common.Hash{0xaa}} next.Data.VerifiedRequiredL1 = eth.BlockID{Number: l1Head.Number + 2, Hash: common.Hash{0xbb}} - stubSupervisor.Add(prev) - stubSupervisor.Add(next) + stubSuperNode.Add(prev) + stubSuperNode.Add(next) // All steps should be the invalid transition hash. for i := int64(0); i < StepsPerTimestamp+1; i++ { @@ -141,7 +141,7 @@ func TestSuperNodeProvider_Get(t *testing.T) { }) t.Run("FirstChainUnsafe", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) prev, next := createValidSuperNodeSuperRoots(l1Head) // Make super roots be safe only after L1 head prev.Data.VerifiedRequiredL1 = eth.BlockID{Number: l1Head.Number, Hash: common.Hash{0xaa}} @@ -155,8 +155,8 @@ func TestSuperNodeProvider_Get(t *testing.T) { }, RequiredL1: eth.BlockID{Number: l1Head.Number + 1, Hash: common.Hash{0xbb}}, } - stubSupervisor.Add(prev) - stubSupervisor.Add(next) + stubSuperNode.Add(prev) + stubSuperNode.Add(next) // All steps should be the invalid transition hash. for i := int64(0); i < StepsPerTimestamp+1; i++ { @@ -167,7 +167,7 @@ func TestSuperNodeProvider_Get(t *testing.T) { }) t.Run("SecondChainUnsafe", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) prev, next := createValidSuperNodeSuperRoots(l1Head) // Make super roots be safe only after L1 head prev.Data.VerifiedRequiredL1 = eth.BlockID{Number: l1Head.Number, Hash: common.Hash{0xaa}} @@ -181,8 +181,8 @@ func TestSuperNodeProvider_Get(t *testing.T) { }, RequiredL1: eth.BlockID{Number: l1Head.Number + 1, Hash: common.Hash{0xbb}}, } - stubSupervisor.Add(prev) - stubSupervisor.Add(next) + stubSuperNode.Add(prev) + stubSuperNode.Add(next) // First step should be valid because we can reach the required block on chain 1 claim, err := provider.Get(context.Background(), types.NewPosition(gameDepth, big.NewInt(0))) @@ -198,8 +198,8 @@ func TestSuperNodeProvider_Get(t *testing.T) { }) t.Run("Step0ForTimestampBeyondChainHead", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) - stubSupervisor.AddAtTimestamp(poststateTimestamp, eth.SuperRootAtTimestampResponse{ + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) + stubSuperNode.AddAtTimestamp(poststateTimestamp, eth.SuperRootAtTimestampResponse{ CurrentL1: l1Head, ChainIDs: []eth.ChainID{eth.ChainIDFromUInt64(1), eth.ChainIDFromUInt64(2)}, Data: nil, @@ -211,10 +211,10 @@ func TestSuperNodeProvider_Get(t *testing.T) { }) t.Run("NextSuperRootTimestampBeyondAllChainHeads", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) prev, _ := createValidSuperNodeSuperRoots(l1Head) - stubSupervisor.Add(prev) - stubSupervisor.AddAtTimestamp(prestateTimestamp+1, eth.SuperRootAtTimestampResponse{ + stubSuperNode.Add(prev) + stubSuperNode.AddAtTimestamp(prestateTimestamp+1, eth.SuperRootAtTimestampResponse{ CurrentL1: l1Head, ChainIDs: prev.ChainIDs, Data: nil, @@ -229,10 +229,10 @@ func TestSuperNodeProvider_Get(t *testing.T) { }) t.Run("NextSuperRootTimestampBeyondFirstChainHead", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) prev, next := createValidSuperNodeSuperRoots(l1Head) - stubSupervisor.Add(prev) - stubSupervisor.AddAtTimestamp(prestateTimestamp+1, eth.SuperRootAtTimestampResponse{ + stubSuperNode.Add(prev) + stubSuperNode.AddAtTimestamp(prestateTimestamp+1, eth.SuperRootAtTimestampResponse{ CurrentL1: l1Head, ChainIDs: prev.ChainIDs, OptimisticAtTimestamp: map[eth.ChainID]eth.OutputWithRequiredL1{ @@ -249,10 +249,10 @@ func TestSuperNodeProvider_Get(t *testing.T) { }) t.Run("NextSuperRootTimestampBeyondSecondChainHead", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) prev, next := createValidSuperNodeSuperRoots(l1Head) - stubSupervisor.Add(prev) - stubSupervisor.AddAtTimestamp(prestateTimestamp+1, eth.SuperRootAtTimestampResponse{ + stubSuperNode.Add(prev) + stubSuperNode.AddAtTimestamp(prestateTimestamp+1, eth.SuperRootAtTimestampResponse{ CurrentL1: l1Head, ChainIDs: next.ChainIDs, OptimisticAtTimestamp: map[eth.ChainID]eth.OutputWithRequiredL1{ @@ -274,13 +274,13 @@ func TestSuperNodeProvider_Get(t *testing.T) { }) t.Run("PreviousSuperRootTimestampBeyondChainHead", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) - stubSupervisor.AddAtTimestamp(prestateTimestamp, eth.SuperRootAtTimestampResponse{ + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) + stubSuperNode.AddAtTimestamp(prestateTimestamp, eth.SuperRootAtTimestampResponse{ CurrentL1: l1Head, ChainIDs: []eth.ChainID{eth.ChainIDFromUInt64(1), eth.ChainIDFromUInt64(2)}, Data: nil, }) - stubSupervisor.AddAtTimestamp(prestateTimestamp+1, eth.SuperRootAtTimestampResponse{ + stubSuperNode.AddAtTimestamp(prestateTimestamp+1, eth.SuperRootAtTimestampResponse{ CurrentL1: l1Head, ChainIDs: []eth.ChainID{eth.ChainIDFromUInt64(1), eth.ChainIDFromUInt64(2)}, Data: nil, @@ -295,7 +295,7 @@ func TestSuperNodeProvider_Get(t *testing.T) { }) t.Run("Step0NotInSync", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) expectedSuper := eth.NewSuperV1(poststateTimestamp, eth.ChainIDAndOutput{ ChainID: eth.ChainIDFromUInt64(1), Output: eth.Bytes32{0xbb}, @@ -309,14 +309,14 @@ func TestSuperNodeProvider_Get(t *testing.T) { SuperRoot: eth.SuperRoot(expectedSuper), }, } - stubSupervisor.Add(response) + stubSuperNode.Add(response) _, err := provider.Get(context.Background(), types.RootPosition) require.ErrorIs(t, err, types2.ErrNotInSync) }) t.Run("PreviousSuperRootNotInSync", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) - stubSupervisor.AddAtTimestamp(prestateTimestamp, eth.SuperRootAtTimestampResponse{ + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) + stubSuperNode.AddAtTimestamp(prestateTimestamp, eth.SuperRootAtTimestampResponse{ CurrentL1: eth.BlockID{Number: l1Head.Number - 1, Hash: common.Hash{0xaa}}, ChainIDs: []eth.ChainID{eth.ChainIDFromUInt64(1), eth.ChainIDFromUInt64(2)}, }) @@ -325,12 +325,12 @@ func TestSuperNodeProvider_Get(t *testing.T) { }) t.Run("NextSuperRootNotInSync", func(t *testing.T) { - provider, stubSupervisor, l1Head := createSuperNodeProvider(t) + provider, stubSuperNode, l1Head := createSuperNodeProvider(t) prev, _ := createValidSuperNodeSuperRoots(l1Head) // Previous gives an in sync response - stubSupervisor.Add(prev) + stubSuperNode.Add(prev) // But next gives an out of sync response - stubSupervisor.AddAtTimestamp(prestateTimestamp+1, eth.SuperRootAtTimestampResponse{ + stubSuperNode.AddAtTimestamp(prestateTimestamp+1, eth.SuperRootAtTimestampResponse{ CurrentL1: eth.BlockID{Number: l1Head.Number - 1, Hash: common.Hash{0xaa}}, ChainIDs: []eth.ChainID{eth.ChainIDFromUInt64(1), eth.ChainIDFromUInt64(2)}, }) @@ -415,11 +415,11 @@ func TestSuperNodeProvider_GetL2BlockNumberChallengeReturnsError(t *testing.T) { func createSuperNodeProvider(t *testing.T) (*SuperNodeTraceProvider, *stubSuperNodeRootProvider, eth.BlockID) { logger := testlog.Logger(t, log.LvlInfo) l1Head := eth.BlockID{Number: 23542, Hash: common.Hash{0xab, 0xcd}} - stubSupervisor := &stubSuperNodeRootProvider{ + stubSuperNode := &stubSuperNodeRootProvider{ rootsByTimestamp: make(map[uint64]eth.SuperRootAtTimestampResponse), } - provider := NewSuperNodeTraceProvider(logger, nil, stubSupervisor, l1Head, gameDepth, prestateTimestamp, poststateTimestamp) - return provider, stubSupervisor, l1Head + provider := NewSuperNodeTraceProvider(logger, nil, stubSuperNode, l1Head, gameDepth, prestateTimestamp, poststateTimestamp) + return provider, stubSuperNode, l1Head } func toOutputResponse(output *eth.OutputV0) *eth.OutputResponse { diff --git a/op-challenger/game/fault/trace/super/provider.go b/op-challenger/game/fault/trace/super/provider_supervisor.go similarity index 100% rename from op-challenger/game/fault/trace/super/provider.go rename to op-challenger/game/fault/trace/super/provider_supervisor.go diff --git a/op-challenger/game/fault/trace/super/provider_test.go b/op-challenger/game/fault/trace/super/provider_supervisor_test.go similarity index 100% rename from op-challenger/game/fault/trace/super/provider_test.go rename to op-challenger/game/fault/trace/super/provider_supervisor_test.go diff --git a/op-challenger/runner/runner.go b/op-challenger/runner/runner.go index 796a9da1897a3..74e7044257440 100644 --- a/op-challenger/runner/runner.go +++ b/op-challenger/runner/runner.go @@ -99,9 +99,9 @@ func (r *Runner) Start(ctx context.Context) error { rollupClient = cl } var supervisorClient *sources.SupervisorClient - if r.cfg.SupervisorRPC != "" { - r.log.Info("Dialling supervisor client", "url", r.cfg.SupervisorRPC) - cl, err := dial.DialSupervisorClientWithTimeout(ctx, r.log, r.cfg.SupervisorRPC) + if r.cfg.SuperRPC != "" { + r.log.Info("Dialling supervisor client", "url", r.cfg.SuperRPC) + cl, err := dial.DialSupervisorClientWithTimeout(ctx, r.log, r.cfg.SuperRPC) if err != nil { return fmt.Errorf("failed to dial supervisor: %w", err) } From c689bf00527ea628e1ca74d978167f30817c2606 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Mon, 22 Dec 2025 10:52:44 +1000 Subject: [PATCH 04/11] op-challenger: Actually return super node client --- op-challenger/game/client/provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op-challenger/game/client/provider.go b/op-challenger/game/client/provider.go index 1c510b6965b5b..abc9011fd9d10 100644 --- a/op-challenger/game/client/provider.go +++ b/op-challenger/game/client/provider.go @@ -117,5 +117,5 @@ func (c *Provider) SuperchainClients() (*sources.SupervisorClient, *sources.Supe c.superSyncValidator = NewSupervisorSyncValidator(supervisorClient) c.toClose = append(c.toClose, supervisorClient.Close) } - return c.supervisorClient, nil, c.superSyncValidator, nil + return c.supervisorClient, c.superNodeClient, c.superSyncValidator, nil } From 6ecdf8345d0df81f8dab892d1a00d54c28369328 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Mon, 22 Dec 2025 11:45:40 +1000 Subject: [PATCH 05/11] Fix spelling. --- op-challenger/config/config.go | 8 ++++---- op-challenger/game/client/provider.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/op-challenger/config/config.go b/op-challenger/config/config.go index e073d506912ac..d1fc28a78cb8e 100644 --- a/op-challenger/config/config.go +++ b/op-challenger/config/config.go @@ -72,10 +72,10 @@ type Config struct { GameTypes []gameTypes.GameType // Type of games supported - RollupRpc string // L2 Rollup RPC Url - SuperRPC string // L2 RPC URL for super roots - UserSuperNode bool // Temporary: True to use op-supernode APIs, false for op-supervisor APIs - L2Rpcs []string // L2 RPC Url + RollupRpc string // L2 Rollup RPC Url + SuperRPC string // L2 RPC URL for super roots + UseSuperNode bool // Temporary: True to use op-supernode APIs, false for op-supervisor APIs + L2Rpcs []string // L2 RPC Url // Specific to the cannon trace provider Cannon vm.Config diff --git a/op-challenger/game/client/provider.go b/op-challenger/game/client/provider.go index abc9011fd9d10..bc08626592e45 100644 --- a/op-challenger/game/client/provider.go +++ b/op-challenger/game/client/provider.go @@ -100,7 +100,7 @@ func (c *Provider) SuperchainClients() (*sources.SupervisorClient, *sources.Supe if c.supervisorClient != nil || c.superNodeClient != nil { return c.supervisorClient, c.superNodeClient, c.superSyncValidator, nil } - if c.cfg.UserSuperNode { + if c.cfg.UseSuperNode { superNodeClient, err := dial.DialSuperNodeClientWithTimeout(c.ctx, c.logger, c.cfg.SuperRPC) if err != nil { return nil, nil, nil, fmt.Errorf("failed to dial supernode: %w", err) From 29515724c64f8c999e4ce4265ea7d9646884c834 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Mon, 22 Dec 2025 13:43:37 +1000 Subject: [PATCH 06/11] op-challenger: Avoid interfaces when nil checks are required. Interface values are only nil if the type and value are nil - in our case the type is always non-nil so superNodeProvider == nil is always false causing panics. --- op-challenger/game/fault/register.go | 3 +++ op-challenger/game/fault/register_task.go | 15 +++++++++++---- .../game/fault/trace/super/provider_supervisor.go | 5 +++-- .../game/fault/trace/super/super_cannon.go | 5 +++-- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/op-challenger/game/fault/register.go b/op-challenger/game/fault/register.go index 89c13b8e672ae..c8c885d8ca1f7 100644 --- a/op-challenger/game/fault/register.go +++ b/op-challenger/game/fault/register.go @@ -72,6 +72,9 @@ func RegisterGameTypes( if err != nil { return err } + if superNodeProvider != nil { + panic("Should be nil 1") + } registerTasks = append(registerTasks, NewSuperCannonRegisterTask(gameTypes.SuperCannonGameType, cfg, m, vm.NewOpProgramServerExecutor(logger), rootProvider, superNodeProvider, syncValidator)) } if cfg.GameTypeEnabled(gameTypes.SuperCannonKonaGameType) { diff --git a/op-challenger/game/fault/register_task.go b/op-challenger/game/fault/register_task.go index 0670bb987054f..9d3788af6389f 100644 --- a/op-challenger/game/fault/register_task.go +++ b/op-challenger/game/fault/register_task.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-service/clock" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-service/sources/batching" "github.com/ethereum-optimism/optimism/op-service/sources/caching" "github.com/ethereum/go-ethereum/common" @@ -51,11 +52,14 @@ type RegisterTask struct { poststateBlock uint64) (*trace.Accessor, error) } -func NewSuperCannonRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider super.RootProvider, superNodeProvider super.SuperNodeRootProvider, syncValidator gameTypes.SyncValidator) *RegisterTask { +func NewSuperCannonRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider *sources.SupervisorClient, superNodeProvider *sources.SuperNodeClient, syncValidator gameTypes.SyncValidator) *RegisterTask { + if superNodeProvider != nil { + panic("Should be nil 2") + } return newSuperCannonVMRegisterTaskWithConfig(gameType, cfg, m, serverExecutor, rootProvider, superNodeProvider, syncValidator, cfg.Cannon, cfg.CannonAbsolutePreStateBaseURL, cfg.CannonAbsolutePreState) } -func NewSuperCannonKonaRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider super.RootProvider, superNodeProvider super.SuperNodeRootProvider, syncValidator gameTypes.SyncValidator) *RegisterTask { +func NewSuperCannonKonaRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider *sources.SupervisorClient, superNodeProvider *sources.SuperNodeClient, syncValidator gameTypes.SyncValidator) *RegisterTask { return newSuperCannonVMRegisterTaskWithConfig(gameType, cfg, m, serverExecutor, rootProvider, superNodeProvider, syncValidator, cfg.CannonKona, cfg.CannonKonaAbsolutePreStateBaseURL, cfg.CannonKonaAbsolutePreState) } @@ -64,13 +68,16 @@ func newSuperCannonVMRegisterTaskWithConfig( cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, - rootProvider super.RootProvider, - superNodeProvider super.SuperNodeRootProvider, + rootProvider *sources.SupervisorClient, + superNodeProvider *sources.SuperNodeClient, syncValidator gameTypes.SyncValidator, vmCfg vm.Config, preStateBaseURL *url.URL, preState string, ) *RegisterTask { + if superNodeProvider != nil { + panic("Should be nil 3") + } stateConverter := cannon.NewStateConverter(vmCfg) return &RegisterTask{ gameType: gameType, diff --git a/op-challenger/game/fault/trace/super/provider_supervisor.go b/op-challenger/game/fault/trace/super/provider_supervisor.go index 456c26d52f436..3d75b3f7ae53c 100644 --- a/op-challenger/game/fault/trace/super/provider_supervisor.go +++ b/op-challenger/game/fault/trace/super/provider_supervisor.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" interopTypes "github.com/ethereum-optimism/optimism/op-program/client/interop/types" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -52,13 +53,13 @@ type SupervisorSuperTraceProvider struct { gameDepth types.Depth } -func NewSuperTraceProvider(logger log.Logger, rollupCfgs *RollupConfigs, prestateProvider PreimagePrestateProvider, rootProvider RootProvider, superNodeProvider SuperNodeRootProvider, l1Head eth.BlockID, gameDepth types.Depth, prestateTimestamp, poststateTimestamp uint64) SuperTraceProvider { +func NewSuperTraceProvider(logger log.Logger, rollupCfgs *RollupConfigs, prestateProvider PreimagePrestateProvider, rootProvider *sources.SupervisorClient, superNodeProvider *sources.SuperNodeClient, l1Head eth.BlockID, gameDepth types.Depth, prestateTimestamp, poststateTimestamp uint64) SuperTraceProvider { if rootProvider == nil && superNodeProvider != nil { return NewSuperNodeTraceProvider(logger, prestateProvider, superNodeProvider, l1Head, gameDepth, prestateTimestamp, poststateTimestamp) } else if rootProvider != nil && superNodeProvider == nil { return NewSupervisorSuperTraceProvider(logger, rollupCfgs, prestateProvider, rootProvider, l1Head, gameDepth, prestateTimestamp, poststateTimestamp) } else { - panic("Invalid configuration: must provide either a super node provider or a root provider, but not both.") + panic(fmt.Sprintf("Invalid configuration: must provide either a super node provider or a root provider, but not both. Root provider: %v, SuperNodeProvider: %v", rootProvider, superNodeProvider)) } } diff --git a/op-challenger/game/fault/trace/super/super_cannon.go b/op-challenger/game/fault/trace/super/super_cannon.go index e57620e971dbf..9fcc9d634cc90 100644 --- a/op-challenger/game/fault/trace/super/super_cannon.go +++ b/op-challenger/game/fault/trace/super/super_cannon.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/log" @@ -25,8 +26,8 @@ func NewSuperCannonTraceAccessor( cfg vm.Config, serverExecutor vm.OracleServerExecutor, prestateProvider PreimagePrestateProvider, - rootProvider RootProvider, - superNodeProvider SuperNodeRootProvider, + rootProvider *sources.SupervisorClient, + superNodeProvider *sources.SuperNodeClient, cannonPrestate string, dir string, l1Head eth.BlockID, From b7a860587bcb546c921a4a4a7706ab3028e06b14 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Mon, 22 Dec 2025 14:16:09 +1000 Subject: [PATCH 07/11] Remove test panic --- op-challenger/game/fault/register.go | 3 --- op-challenger/game/fault/register_task.go | 6 ------ 2 files changed, 9 deletions(-) diff --git a/op-challenger/game/fault/register.go b/op-challenger/game/fault/register.go index c8c885d8ca1f7..89c13b8e672ae 100644 --- a/op-challenger/game/fault/register.go +++ b/op-challenger/game/fault/register.go @@ -72,9 +72,6 @@ func RegisterGameTypes( if err != nil { return err } - if superNodeProvider != nil { - panic("Should be nil 1") - } registerTasks = append(registerTasks, NewSuperCannonRegisterTask(gameTypes.SuperCannonGameType, cfg, m, vm.NewOpProgramServerExecutor(logger), rootProvider, superNodeProvider, syncValidator)) } if cfg.GameTypeEnabled(gameTypes.SuperCannonKonaGameType) { diff --git a/op-challenger/game/fault/register_task.go b/op-challenger/game/fault/register_task.go index 9d3788af6389f..271c852caabf8 100644 --- a/op-challenger/game/fault/register_task.go +++ b/op-challenger/game/fault/register_task.go @@ -53,9 +53,6 @@ type RegisterTask struct { } func NewSuperCannonRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider *sources.SupervisorClient, superNodeProvider *sources.SuperNodeClient, syncValidator gameTypes.SyncValidator) *RegisterTask { - if superNodeProvider != nil { - panic("Should be nil 2") - } return newSuperCannonVMRegisterTaskWithConfig(gameType, cfg, m, serverExecutor, rootProvider, superNodeProvider, syncValidator, cfg.Cannon, cfg.CannonAbsolutePreStateBaseURL, cfg.CannonAbsolutePreState) } @@ -75,9 +72,6 @@ func newSuperCannonVMRegisterTaskWithConfig( preStateBaseURL *url.URL, preState string, ) *RegisterTask { - if superNodeProvider != nil { - panic("Should be nil 3") - } stateConverter := cannon.NewStateConverter(vmCfg) return &RegisterTask{ gameType: gameType, From 0e013215d8e14e5f63853f948d471c3353837d20 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Mon, 5 Jan 2026 10:09:13 +1000 Subject: [PATCH 08/11] op-challenger: Fix package --- op-challenger/game/fault/register_task.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op-challenger/game/fault/register_task.go b/op-challenger/game/fault/register_task.go index 271c852caabf8..b1921d5a46be4 100644 --- a/op-challenger/game/fault/register_task.go +++ b/op-challenger/game/fault/register_task.go @@ -164,7 +164,7 @@ func newCannonVMRegisterTaskWithConfig( } } -func NewAlphabetRegisterTask(gameType gameTypes.GameType, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator generic.SyncValidator) *RegisterTask { +func NewAlphabetRegisterTask(gameType gameTypes.GameType, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator gameTypes.SyncValidator) *RegisterTask { return &RegisterTask{ gameType: gameType, syncValidator: syncValidator, From 7dd613dba8f01fe792139811fd9441c2df8c0a96 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Wed, 7 Jan 2026 13:23:25 +1000 Subject: [PATCH 09/11] op-challenger: Rename --super-rpc to --supernode-rpc --- op-challenger/cmd/main_test.go | 20 ++++++++++---------- op-challenger/flags/flags.go | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/op-challenger/cmd/main_test.go b/op-challenger/cmd/main_test.go index aa5fdc21a4a97..012074846e729 100644 --- a/op-challenger/cmd/main_test.go +++ b/op-challenger/cmd/main_test.go @@ -95,15 +95,15 @@ func TestL1Beacon(t *testing.T) { }) } -func TestSuperRpc(t *testing.T) { +func TestSuperNodeRpc(t *testing.T) { t.Run("RequiredForSuperCannon", func(t *testing.T) { - verifyArgsInvalid(t, "flag super-rpc is required", addRequiredArgsExcept(gameTypes.SuperCannonGameType, "--super-rpc")) + verifyArgsInvalid(t, "flag supernode-rpc is required", addRequiredArgsExcept(gameTypes.SuperCannonGameType, "--supernode-rpc")) }) t.Run("RequiredForSuperPermissioned", func(t *testing.T) { - verifyArgsInvalid(t, "flag super-rpc is required", addRequiredArgsExcept(gameTypes.SuperPermissionedGameType, "--super-rpc")) + verifyArgsInvalid(t, "flag supernode-rpc is required", addRequiredArgsExcept(gameTypes.SuperPermissionedGameType, "--supernode-rpc")) }) t.Run("RequiredForSuperCannonKona", func(t *testing.T) { - verifyArgsInvalid(t, "flag super-rpc is required", addRequiredArgsExcept(gameTypes.SuperCannonKonaGameType, "--super-rpc")) + verifyArgsInvalid(t, "flag supernode-rpc is required", addRequiredArgsExcept(gameTypes.SuperCannonKonaGameType, "--supernode-rpc")) }) for _, gameType := range gameTypes.SupportedGameTypes { @@ -113,25 +113,25 @@ func TestSuperRpc(t *testing.T) { } t.Run("NotRequiredForGameType-"+gameType.String(), func(t *testing.T) { - configForArgs(t, addRequiredArgsExcept(gameType, "--super-rpc")) + configForArgs(t, addRequiredArgsExcept(gameType, "--supernode-rpc")) }) } t.Run("Valid-SuperCannon", func(t *testing.T) { url := "http://localhost/super" - cfg := configForArgs(t, addRequiredArgsExcept(gameTypes.SuperCannonGameType, "--super-rpc", "--super-rpc", url)) + cfg := configForArgs(t, addRequiredArgsExcept(gameTypes.SuperCannonGameType, "--supernode-rpc", "--supernode-rpc", url)) require.Equal(t, url, cfg.SuperRPC) }) t.Run("Valid-SuperPermissioned", func(t *testing.T) { url := "http://localhost/super" - cfg := configForArgs(t, addRequiredArgsExcept(gameTypes.SuperPermissionedGameType, "--super-rpc", "--super-rpc", url)) + cfg := configForArgs(t, addRequiredArgsExcept(gameTypes.SuperPermissionedGameType, "--supernode-rpc", "--supernode-rpc", url)) require.Equal(t, url, cfg.SuperRPC) }) t.Run("Valid-SuperCannonKona", func(t *testing.T) { url := "http://localhost/super" - cfg := configForArgs(t, addRequiredArgsExcept(gameTypes.SuperCannonKonaGameType, "--super-rpc", "--super-rpc", url)) + cfg := configForArgs(t, addRequiredArgsExcept(gameTypes.SuperCannonKonaGameType, "--supernode-rpc", "--supernode-rpc", url)) require.Equal(t, url, cfg.SuperRPC) }) } @@ -1023,7 +1023,7 @@ func requiredArgs(gameType gameTypes.GameType) map[string]string { func addRequiredSuperCannonArgs(args map[string]string) { addRequiredCannonBaseArgs(args) - args["--super-rpc"] = superRpc + args["--supernode-rpc"] = superRpc } func addRequiredCannonArgs(args map[string]string) { @@ -1056,7 +1056,7 @@ func addRequiredCannonKonaBaseArgs(args map[string]string) { func addRequiredSuperCannonKonaArgs(args map[string]string) { addRequiredCannonKonaBaseArgs(args) - args["--super-rpc"] = superRpc + args["--supernode-rpc"] = superRpc } func toArgList(req map[string]string) []string { diff --git a/op-challenger/flags/flags.go b/op-challenger/flags/flags.go index 60462df79529b..31136e1338ea5 100644 --- a/op-challenger/flags/flags.go +++ b/op-challenger/flags/flags.go @@ -54,10 +54,10 @@ var ( Usage: "Address of L1 Beacon API endpoint to use", EnvVars: prefixEnvVars("L1_BEACON"), } - SuperRpcFlag = &cli.StringFlag{ - Name: "super-rpc", - Usage: "Provider URL for super roots", - EnvVars: prefixEnvVars("SUPER_RPC"), + SuperNodeRpcFlag = &cli.StringFlag{ + Name: "supernode-rpc", + Usage: "Provider URL for supernode roots", + EnvVars: prefixEnvVars("SUPERNODE_RPC"), } RollupRpcFlag = &cli.StringFlag{ Name: "rollup-rpc", @@ -267,7 +267,7 @@ var optionalFlags = []cli.Flag{ FactoryAddressFlag, GameTypesFlag, MaxConcurrencyFlag, - SuperRpcFlag, + SuperNodeRpcFlag, L2EthRpcFlag, L2ExperimentalEthRpcFlag, MaxPendingTransactionsFlag, @@ -338,8 +338,8 @@ func CheckCannonBaseFlags(ctx *cli.Context) error { } func CheckSuperCannonFlags(ctx *cli.Context) error { - if !ctx.IsSet(SuperRpcFlag.Name) { - return fmt.Errorf("flag %v is required", SuperRpcFlag.Name) + if !ctx.IsSet(SuperNodeRpcFlag.Name) { + return fmt.Errorf("flag %v is required", SuperNodeRpcFlag.Name) } if !ctx.IsSet(flags.NetworkFlagName) && !(RollupConfigFlag.IsSet(ctx, gameTypes.CannonGameType) && L2GenesisFlag.IsSet(ctx, gameTypes.CannonGameType) && DepsetConfigFlag.IsSet(ctx, gameTypes.CannonGameType)) { @@ -356,8 +356,8 @@ func CheckSuperCannonFlags(ctx *cli.Context) error { } func CheckSuperCannonKonaFlags(ctx *cli.Context) error { - if !ctx.IsSet(SuperRpcFlag.Name) { - return fmt.Errorf("flag %v is required", SuperRpcFlag.Name) + if !ctx.IsSet(SuperNodeRpcFlag.Name) { + return fmt.Errorf("flag %v is required", SuperNodeRpcFlag.Name) } if !ctx.IsSet(flags.NetworkFlagName) && !(RollupConfigFlag.IsSet(ctx, gameTypes.CannonKonaGameType) && L2GenesisFlag.IsSet(ctx, gameTypes.CannonKonaGameType) && DepsetConfigFlag.IsSet(ctx, gameTypes.CannonKonaGameType)) { @@ -601,7 +601,7 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro MinUpdateInterval: ctx.Duration(MinUpdateInterval.Name), AdditionalBondClaimants: claimants, RollupRpc: ctx.String(RollupRpcFlag.Name), - SuperRPC: ctx.String(SuperRpcFlag.Name), + SuperRPC: ctx.String(SuperNodeRpcFlag.Name), Cannon: vm.Config{ VmType: gameTypes.CannonGameType, L1: l1EthRpc, From f5b08342e4404f143d903229d9faae7fa9128d32 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Mon, 5 Jan 2026 09:56:11 +1000 Subject: [PATCH 10/11] op-dispute-mon: Use supernode instead of supervisor. --- op-dispute-mon/README.md | 4 +- op-dispute-mon/cmd/main.go | 2 +- op-dispute-mon/cmd/main_test.go | 20 ++--- op-dispute-mon/config/config.go | 22 ++--- op-dispute-mon/config/config_test.go | 32 +++---- op-dispute-mon/flags/flags.go | 16 ++-- .../mon/extract/super_agreement_enricher.go | 40 ++++----- .../extract/super_agreement_enricher_test.go | 90 ++++++++++--------- op-dispute-mon/mon/service.go | 26 +++--- 9 files changed, 125 insertions(+), 127 deletions(-) diff --git a/op-dispute-mon/README.md b/op-dispute-mon/README.md index d80b1988e5cc3..a7a5f29b6d3e1 100644 --- a/op-dispute-mon/README.md +++ b/op-dispute-mon/README.md @@ -26,10 +26,10 @@ shows the available config options and can be accessed by running `./bin/op-disp --l1-eth-rpc \ --rollup-rpc ,, -# For networks using op-supervisor: +# For networks using op-supernode: ./bin/op-dispute-mon \ --network \ --l1-eth-rpc \ - --supervisor-rpc ,, + --supernode-rpc ,, ``` diff --git a/op-dispute-mon/cmd/main.go b/op-dispute-mon/cmd/main.go index 2fec22e2c1a54..5c0aa9843b278 100644 --- a/op-dispute-mon/cmd/main.go +++ b/op-dispute-mon/cmd/main.go @@ -59,7 +59,7 @@ func run(ctx context.Context, args []string, action ConfiguredLifecycle) error { logger.Info("RPC endpoints", "l1", cfg.L1EthRpc, "rollup", cfg.RollupRpcs, - "supervisor", cfg.SupervisorRpcs, + "supernode", cfg.SupernodeRpcs, ) return action(ctx.Context, logger, cfg) }) diff --git a/op-dispute-mon/cmd/main_test.go b/op-dispute-mon/cmd/main_test.go index 76247b9937085..fb3d2f3b288e1 100644 --- a/op-dispute-mon/cmd/main_test.go +++ b/op-dispute-mon/cmd/main_test.go @@ -60,13 +60,13 @@ func TestL1EthRpc(t *testing.T) { }) } -func TestMustSpecifyEitherRollupRpcOrSupervisorRpc(t *testing.T) { - verifyArgsInvalid(t, "flag rollup-rpc or supervisor-rpc is required", addRequiredArgsExcept("--rollup-rpc")) +func TestMustSpecifyEitherRollupRpcOrSupernodeRpc(t *testing.T) { + verifyArgsInvalid(t, "flag rollup-rpc or supernode-rpc is required", addRequiredArgsExcept("--rollup-rpc")) } func TestRollupRpc(t *testing.T) { - t.Run("NotRequiredIfSupervisorRpcSupplied", func(t *testing.T) { - configForArgs(t, addRequiredArgsExcept("--rollup-rpc", "--supervisor-rpc", "http://localhost/supervisor")) + t.Run("NotRequiredIfSupernodeRpcSupplied", func(t *testing.T) { + configForArgs(t, addRequiredArgsExcept("--rollup-rpc", "--supernode-rpc", "http://localhost/supernode")) }) t.Run("Valid", func(t *testing.T) { @@ -83,23 +83,23 @@ func TestRollupRpc(t *testing.T) { }) } -func TestSupervisorRpc(t *testing.T) { +func TestSupernodeRpc(t *testing.T) { t.Run("NotRequiredIfRollupRpcSupplied", func(t *testing.T) { // rollup-rpc is in the default args. - configForArgs(t, addRequiredArgsExcept("--supervisor-rpc")) + configForArgs(t, addRequiredArgsExcept("--supernode-rpc")) }) t.Run("Valid", func(t *testing.T) { url := "http://example.com:9999" - cfg := configForArgs(t, addRequiredArgsExcept("--rollup-rpc", "--supervisor-rpc", url)) - require.Equal(t, []string{url}, cfg.SupervisorRpcs) + cfg := configForArgs(t, addRequiredArgsExcept("--rollup-rpc", "--supernode-rpc", url)) + require.Equal(t, []string{url}, cfg.SupernodeRpcs) }) t.Run("MultipleValues", func(t *testing.T) { url1 := "http://example1.com:9999" url2 := "http://example2.com:8888" - cfg := configForArgs(t, addRequiredArgsExcept("--rollup-rpc", "--supervisor-rpc", url1, "--supervisor-rpc", url2)) - require.Equal(t, []string{url1, url2}, cfg.SupervisorRpcs) + cfg := configForArgs(t, addRequiredArgsExcept("--rollup-rpc", "--supernode-rpc", url1, "--supernode-rpc", url2)) + require.Equal(t, []string{url1, url2}, cfg.SupernodeRpcs) }) } diff --git a/op-dispute-mon/config/config.go b/op-dispute-mon/config/config.go index d39ad64a04f88..f0fbe74afec89 100644 --- a/op-dispute-mon/config/config.go +++ b/op-dispute-mon/config/config.go @@ -12,10 +12,10 @@ import ( ) var ( - ErrMissingL1EthRPC = errors.New("missing l1 eth rpc url") - ErrMissingGameFactoryAddress = errors.New("missing game factory address") - ErrMissingRollupAndSupervisorRpc = errors.New("must specify rollup rpc or supervisor rpc") - ErrMissingMaxConcurrency = errors.New("missing max concurrency") + ErrMissingL1EthRPC = errors.New("missing l1 eth rpc url") + ErrMissingGameFactoryAddress = errors.New("missing game factory address") + ErrMissingRollupAndSupernodeRpc = errors.New("must specify rollup rpc or supernode rpc") + ErrMissingMaxConcurrency = errors.New("missing max concurrency") ) const ( @@ -40,7 +40,7 @@ type Config struct { HonestActors []common.Address // List of honest actors to monitor claims for. RollupRpcs []string // The rollup node RPC URLs. - SupervisorRpcs []string // The supervisor RPC URLs. + SupernodeRpcs []string // The supernode RPC URLs. MonitorInterval time.Duration // Frequency to check for new games to monitor. GameWindow time.Duration // Maximum window to look for games to monitor. IgnoredGames []common.Address // Games to exclude from monitoring @@ -50,19 +50,19 @@ type Config struct { PprofConfig oppprof.CLIConfig } -func NewInteropConfig(gameFactoryAddress common.Address, l1EthRpc string, supervisorRpcs []string) Config { - return NewCombinedConfig(gameFactoryAddress, l1EthRpc, nil, supervisorRpcs) +func NewInteropConfig(gameFactoryAddress common.Address, l1EthRpc string, supernodeRpcs []string) Config { + return NewCombinedConfig(gameFactoryAddress, l1EthRpc, nil, supernodeRpcs) } func NewConfig(gameFactoryAddress common.Address, l1EthRpc string, rollupRpcs []string) Config { return NewCombinedConfig(gameFactoryAddress, l1EthRpc, rollupRpcs, nil) } -func NewCombinedConfig(gameFactoryAddress common.Address, l1EthRpc string, rollupRpcs []string, supervisorRpcs []string) Config { +func NewCombinedConfig(gameFactoryAddress common.Address, l1EthRpc string, rollupRpcs []string, supernodeRpcs []string) Config { return Config{ L1EthRpc: l1EthRpc, RollupRpcs: rollupRpcs, - SupervisorRpcs: supervisorRpcs, + SupernodeRpcs: supernodeRpcs, GameFactoryAddress: gameFactoryAddress, MonitorInterval: DefaultMonitorInterval, @@ -78,8 +78,8 @@ func (c Config) Check() error { if c.L1EthRpc == "" { return ErrMissingL1EthRPC } - if len(c.RollupRpcs) == 0 && len(c.SupervisorRpcs) == 0 { - return ErrMissingRollupAndSupervisorRpc + if len(c.RollupRpcs) == 0 && len(c.SupernodeRpcs) == 0 { + return ErrMissingRollupAndSupernodeRpc } if c.GameFactoryAddress == (common.Address{}) { return ErrMissingGameFactoryAddress diff --git a/op-dispute-mon/config/config_test.go b/op-dispute-mon/config/config_test.go index 2ffd21511738c..0dff73cd38d2c 100644 --- a/op-dispute-mon/config/config_test.go +++ b/op-dispute-mon/config/config_test.go @@ -12,7 +12,7 @@ var ( validL1EthRpc = "http://localhost:8545" validGameFactoryAddress = common.Address{0x23} validRollupRpcs = []string{"http://localhost:8555"} - validSupervisorRpcs = []string{"http://localhost:8999"} + validSupernodeRpcs = []string{"http://localhost:8999"} ) func validConfig() Config { @@ -35,24 +35,24 @@ func TestGameFactoryAddressRequired(t *testing.T) { require.ErrorIs(t, config.Check(), ErrMissingGameFactoryAddress) } -func TestRollupRpcOrSupervisorRpcRequired(t *testing.T) { +func TestRollupRpcOrSupernodeRpcRequired(t *testing.T) { config := validConfig() config.RollupRpcs = nil - config.SupervisorRpcs = nil - require.ErrorIs(t, config.Check(), ErrMissingRollupAndSupervisorRpc) + config.SupernodeRpcs = nil + require.ErrorIs(t, config.Check(), ErrMissingRollupAndSupernodeRpc) } -func TestRollupRpcNotRequiredWhenSupervisorRpcSet(t *testing.T) { +func TestRollupRpcNotRequiredWhenSupernodeRpcSet(t *testing.T) { config := validConfig() config.RollupRpcs = nil - config.SupervisorRpcs = validSupervisorRpcs + config.SupernodeRpcs = validSupernodeRpcs require.NoError(t, config.Check()) } -func TestSupervisorRpcNotRequiredWhenRollupRpcSet(t *testing.T) { +func TestSupernodeRpcNotRequiredWhenRollupRpcSet(t *testing.T) { config := validConfig() config.RollupRpcs = validRollupRpcs - config.SupervisorRpcs = nil + config.SupernodeRpcs = nil require.NoError(t, config.Check()) } @@ -62,22 +62,22 @@ func TestMaxConcurrencyRequired(t *testing.T) { require.ErrorIs(t, config.Check(), ErrMissingMaxConcurrency) } -func TestMultipleSupervisorRpcs(t *testing.T) { +func TestMultipleSupernodeRpcs(t *testing.T) { config := validConfig() config.RollupRpcs = nil - config.SupervisorRpcs = []string{"http://localhost:8999", "http://localhost:9000", "http://localhost:9001"} + config.SupernodeRpcs = []string{"http://localhost:8999", "http://localhost:9000", "http://localhost:9001"} require.NoError(t, config.Check()) } func TestInteropConfig(t *testing.T) { gameFactoryAddr := common.Address{0x42} l1RPC := "http://localhost:8545" - supervisorRpcs := []string{"http://localhost:8999", "http://localhost:9000"} + supernodeRpcs := []string{"http://localhost:8999", "http://localhost:9000"} - config := NewInteropConfig(gameFactoryAddr, l1RPC, supervisorRpcs) + config := NewInteropConfig(gameFactoryAddr, l1RPC, supernodeRpcs) require.Equal(t, gameFactoryAddr, config.GameFactoryAddress) require.Equal(t, l1RPC, config.L1EthRpc) - require.Equal(t, supervisorRpcs, config.SupervisorRpcs) + require.Equal(t, supernodeRpcs, config.SupernodeRpcs) require.Nil(t, config.RollupRpcs) require.NoError(t, config.Check()) } @@ -86,12 +86,12 @@ func TestCombinedConfig(t *testing.T) { gameFactoryAddr := common.Address{0x42} l1RPC := "http://localhost:8545" rollupRpcs := []string{"http://localhost:8555"} - supervisorRpcs := []string{"http://localhost:8999"} + supernodeRpcs := []string{"http://localhost:8999"} - config := NewCombinedConfig(gameFactoryAddr, l1RPC, rollupRpcs, supervisorRpcs) + config := NewCombinedConfig(gameFactoryAddr, l1RPC, rollupRpcs, supernodeRpcs) require.Equal(t, gameFactoryAddr, config.GameFactoryAddress) require.Equal(t, l1RPC, config.L1EthRpc) require.Equal(t, rollupRpcs, config.RollupRpcs) - require.Equal(t, supervisorRpcs, config.SupervisorRpcs) + require.Equal(t, supernodeRpcs, config.SupernodeRpcs) require.NoError(t, config.Check()) } diff --git a/op-dispute-mon/flags/flags.go b/op-dispute-mon/flags/flags.go index 56198fc93830f..ada15a323c762 100644 --- a/op-dispute-mon/flags/flags.go +++ b/op-dispute-mon/flags/flags.go @@ -38,10 +38,10 @@ var ( Usage: "HTTP provider URL for the rollup node. Multiple URLs can be specified for redundancy.", EnvVars: prefixEnvVars("ROLLUP_RPC"), } - SupervisorRpcFlag = &cli.StringSliceFlag{ - Name: "supervisor-rpc", - Usage: "HTTP provider URL for supervisor nodes. Multiple URLs can be specified for redundancy.", - EnvVars: prefixEnvVars("SUPERVISOR_RPC"), + SupernodeRpcFlag = &cli.StringSliceFlag{ + Name: "supernode-rpc", + Usage: "HTTP provider URL for supernodes. Multiple URLs can be specified for redundancy.", + EnvVars: prefixEnvVars("SUPERNODE_RPC"), } GameFactoryAddressFlag = &cli.StringFlag{ Name: "game-factory-address", @@ -92,7 +92,7 @@ var requiredFlags = []cli.Flag{ // optionalFlags is a list of unchecked cli flags var optionalFlags = []cli.Flag{ RollupRpcFlag, - SupervisorRpcFlag, + SupernodeRpcFlag, GameFactoryAddressFlag, NetworkFlag, HonestActorsFlag, @@ -119,8 +119,8 @@ func CheckRequired(ctx *cli.Context) error { return fmt.Errorf("flag %s is required", f.Names()[0]) } } - if len(ctx.StringSlice(RollupRpcFlag.Name)) == 0 && len(ctx.StringSlice(SupervisorRpcFlag.Name)) == 0 { - return fmt.Errorf("flag %s or %s is required", RollupRpcFlag.Name, SupervisorRpcFlag.Name) + if len(ctx.StringSlice(RollupRpcFlag.Name)) == 0 && len(ctx.StringSlice(SupernodeRpcFlag.Name)) == 0 { + return fmt.Errorf("flag %s or %s is required", RollupRpcFlag.Name, SupernodeRpcFlag.Name) } return nil } @@ -169,7 +169,7 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) { L1EthRpc: ctx.String(L1EthRpcFlag.Name), GameFactoryAddress: gameFactoryAddress, RollupRpcs: ctx.StringSlice(RollupRpcFlag.Name), - SupervisorRpcs: ctx.StringSlice(SupervisorRpcFlag.Name), + SupernodeRpcs: ctx.StringSlice(SupernodeRpcFlag.Name), HonestActors: actors, MonitorInterval: ctx.Duration(MonitorIntervalFlag.Name), diff --git a/op-dispute-mon/mon/extract/super_agreement_enricher.go b/op-dispute-mon/mon/extract/super_agreement_enricher.go index b4fbf4d1b2d34..e1be248e5e5db 100644 --- a/op-dispute-mon/mon/extract/super_agreement_enricher.go +++ b/op-dispute-mon/mon/extract/super_agreement_enricher.go @@ -10,19 +10,17 @@ import ( "github.com/ethereum-optimism/optimism/op-service/clock" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/log" ) var ( - ErrSupervisorRpcRequired = errors.New("supervisor rpc required") - ErrAllSupervisorNodesUnavailable = errors.New("all supervisor nodes returned errors") + ErrSupernodeRpcRequired = errors.New("supernode rpc required") + ErrAllSuperNodesUnavailable = errors.New("all supernodes returned errors") ) type SuperRootProvider interface { - SuperRootAtTimestamp(ctx context.Context, timestamp hexutil.Uint64) (eth.SuperRootResponse, error) + SuperRootAtTimestamp(ctx context.Context, timestamp uint64) (eth.SuperRootAtTimestampResponse, error) } type SuperAgreementEnricher struct { @@ -42,11 +40,10 @@ func NewSuperAgreementEnricher(logger log.Logger, metrics OutputMetrics, clients } type superRootResult struct { - superRoot common.Hash - isSafe bool - notFound bool - err error - crossSafeDerivedFrom uint64 + superRoot common.Hash + isSafe bool + notFound bool + err error } func (e *SuperAgreementEnricher) Enrich(ctx context.Context, block rpcblock.Block, caller GameCaller, game *monTypes.EnrichedGameData) error { @@ -54,7 +51,7 @@ func (e *SuperAgreementEnricher) Enrich(ctx context.Context, block rpcblock.Bloc return nil } if len(e.clients) == 0 { - return fmt.Errorf("%w but required for game type %v", ErrSupervisorRpcRequired, game.GameType) + return fmt.Errorf("%w but required for game type %v", ErrSupernodeRpcRequired, game.GameType) } results := make([]superRootResult, len(e.clients)) @@ -63,21 +60,20 @@ func (e *SuperAgreementEnricher) Enrich(ctx context.Context, block rpcblock.Bloc wg.Add(1) go func(i int, client SuperRootProvider) { defer wg.Done() - response, err := client.SuperRootAtTimestamp(ctx, hexutil.Uint64(game.L2SequenceNumber)) - if errors.Is(err, ethereum.NotFound) { - results[i] = superRootResult{notFound: true} - return - } + response, err := client.SuperRootAtTimestamp(ctx, game.L2SequenceNumber) if err != nil { results[i] = superRootResult{err: err} return } + if response.Data == nil { + results[i] = superRootResult{notFound: true} + return + } - superRoot := common.Hash(response.SuperRoot) + superRoot := common.Hash(response.Data.SuperRoot) results[i] = superRootResult{ - superRoot: superRoot, - crossSafeDerivedFrom: response.CrossSafeDerivedFrom.Number, - isSafe: response.CrossSafeDerivedFrom.Number <= game.L1HeadNum, + superRoot: superRoot, + isSafe: response.Data.VerifiedRequiredL1.Number <= game.L1HeadNum, } }(i, client) } @@ -100,7 +96,7 @@ func (e *SuperAgreementEnricher) Enrich(ctx context.Context, block rpcblock.Bloc // If all results were errors, return an error if len(validResults) == 0 { - return fmt.Errorf("failed to get super root at timestamp: %w", ErrAllSupervisorNodesUnavailable) + return fmt.Errorf("failed to get super root at timestamp: %w", ErrAllSuperNodesUnavailable) } // If all remaining nodes returned "not found", we disagree with any claim. @@ -129,7 +125,7 @@ func (e *SuperAgreementEnricher) Enrich(ctx context.Context, block rpcblock.Bloc } if diverged { - e.log.Warn("Supervisor nodes disagree on super root", + e.log.Warn("Super nodes disagree on super root", "l2SequenceNumber", game.L2SequenceNumber, "firstSuperRoot", firstResult.superRoot, "found", len(foundResults), diff --git a/op-dispute-mon/mon/extract/super_agreement_enricher_test.go b/op-dispute-mon/mon/extract/super_agreement_enricher_test.go index 7fa0602786214..b943da4bbfaa5 100644 --- a/op-dispute-mon/mon/extract/super_agreement_enricher_test.go +++ b/op-dispute-mon/mon/extract/super_agreement_enricher_test.go @@ -13,9 +13,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum-optimism/optimism/op-service/testlog" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/log" "github.com/stretchr/testify/require" ) @@ -23,7 +21,7 @@ import ( func TestDetector_CheckSuperRootAgreement(t *testing.T) { t.Parallel() - t.Run("ErrorWhenNoSupervisorClient", func(t *testing.T) { + t.Run("ErrorWhenNoSupernodeClient", func(t *testing.T) { validator, _, _ := setupSuperValidatorTest(t) validator.clients = nil // Set to nil to test the error case game := &types.EnrichedGameData{ @@ -35,7 +33,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { RootClaim: mockRootClaim, } err := validator.Enrich(context.Background(), rpcblock.Latest, nil, game) - require.ErrorIs(t, err, ErrSupervisorRpcRequired) + require.ErrorIs(t, err, ErrSupernodeRpcRequired) }) t.Run("SkipOutputRootGameTypes", func(t *testing.T) { @@ -44,7 +42,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { gameType := gameType t.Run(fmt.Sprintf("GameType_%d", gameType), func(t *testing.T) { validator, _, metrics := setupSuperValidatorTest(t) - validator.clients = nil // Should not error even though there's no supervisor client + validator.clients = nil // Should not error even though there's no supernode client game := &types.EnrichedGameData{ GameMetadata: challengerTypes.GameMetadata{ GameType: gameType, @@ -93,7 +91,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { RootClaim: mockRootClaim, } err := validator.Enrich(context.Background(), rpcblock.Latest, nil, game) - require.ErrorIs(t, err, ErrAllSupervisorNodesUnavailable) + require.ErrorIs(t, err, ErrAllSuperNodesUnavailable) require.Equal(t, common.Hash{}, game.ExpectedRootClaim) require.False(t, game.AgreeWithClaim) require.Zero(t, metrics.fetchTime) @@ -190,8 +188,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { t.Run("OutputNotFound", func(t *testing.T) { validator, client, metrics := setupSuperValidatorTest(t) - // The supervisor client automatically translates RPC errors back to ethereum.NotFound for us - client.outputErr = ethereum.NotFound + client.notFound = true game := &types.EnrichedGameData{ GameMetadata: challengerTypes.GameMetadata{ GameType: 999, @@ -207,8 +204,8 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { require.Zero(t, metrics.fetchTime) }) - t.Run("AllSupervisorNodesReturnError", func(t *testing.T) { - validator, clients, metrics := setupMultiSupervisorTest(t, 3) + t.Run("AllSuperNodesReturnError", func(t *testing.T) { + validator, clients, metrics := setupMultiSupernodeTest(t, 3) for _, client := range clients { client.outputErr = errors.New("boom") } @@ -222,16 +219,16 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { } err := validator.Enrich(context.Background(), rpcblock.Latest, nil, game) require.Error(t, err) - require.ErrorIs(t, err, ErrAllSupervisorNodesUnavailable) + require.ErrorIs(t, err, ErrAllSuperNodesUnavailable) require.Equal(t, common.Hash{}, game.ExpectedRootClaim) require.False(t, game.AgreeWithClaim) require.Zero(t, metrics.fetchTime) }) - t.Run("AllSupervisorNodesReturnNotFound", func(t *testing.T) { - validator, clients, metrics := setupMultiSupervisorTest(t, 3) + t.Run("AllSuperNodesReturnNotFound", func(t *testing.T) { + validator, clients, metrics := setupMultiSupernodeTest(t, 3) for _, client := range clients { - client.outputErr = ethereum.NotFound + client.notFound = true } game := &types.EnrichedGameData{ GameMetadata: challengerTypes.GameMetadata{ @@ -248,9 +245,9 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { require.Zero(t, metrics.fetchTime) }) - t.Run("SomeSupervisorNodesOutOfSync", func(t *testing.T) { - validator, clients, metrics := setupMultiSupervisorTest(t, 3) - clients[0].outputErr = ethereum.NotFound + t.Run("SomeSuperNodesOutOfSync", func(t *testing.T) { + validator, clients, metrics := setupMultiSupernodeTest(t, 3) + clients[0].notFound = true clients[1].outputErr = nil clients[2].outputErr = nil game := &types.EnrichedGameData{ @@ -268,8 +265,8 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { require.NotZero(t, metrics.fetchTime) }) - t.Run("SupervisorNodesDiverged", func(t *testing.T) { - validator, clients, metrics := setupMultiSupervisorTest(t, 3) + t.Run("SuperNodesDiverged", func(t *testing.T) { + validator, clients, metrics := setupMultiSupernodeTest(t, 3) divergedRoot := common.HexToHash("0x5678") clients[0].superRoot = mockRootClaim clients[1].superRoot = divergedRoot @@ -289,8 +286,8 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { require.NotZero(t, metrics.fetchTime) }) - t.Run("AllSupervisorNodesAgree", func(t *testing.T) { - validator, clients, metrics := setupMultiSupervisorTest(t, 3) + t.Run("AllSuperNodesAgree", func(t *testing.T) { + validator, clients, metrics := setupMultiSupernodeTest(t, 3) clients[0].derivedFromL1BlockNum = 200 clients[1].derivedFromL1BlockNum = 199 clients[2].derivedFromL1BlockNum = 201 @@ -310,9 +307,9 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) t.Run("MixedResponses_FoundNodesMatchClaimAndSafe", func(t *testing.T) { - validator, clients, metrics := setupMultiSupervisorTest(t, 4) - clients[0].outputErr = ethereum.NotFound - clients[1].outputErr = ethereum.NotFound + validator, clients, metrics := setupMultiSupernodeTest(t, 4) + clients[0].notFound = true + clients[1].notFound = true clients[2].superRoot = mockRootClaim clients[2].derivedFromL1BlockNum = 100 // Safe because L1HeadNum is 200 clients[3].superRoot = mockRootClaim @@ -333,9 +330,9 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) t.Run("MixedResponses_FoundNodesDontMatchClaim", func(t *testing.T) { - validator, clients, metrics := setupMultiSupervisorTest(t, 3) + validator, clients, metrics := setupMultiSupernodeTest(t, 3) differentRoot := common.HexToHash("0x9999") - clients[0].outputErr = ethereum.NotFound + clients[0].notFound = true clients[1].superRoot = differentRoot clients[1].derivedFromL1BlockNum = 100 clients[2].superRoot = differentRoot @@ -356,7 +353,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) t.Run("AllNodesAgree_SuperRootMatchesClaim_NoneReportSafe", func(t *testing.T) { - validator, clients, metrics := setupMultiSupervisorTest(t, 3) + validator, clients, metrics := setupMultiSupernodeTest(t, 3) for _, client := range clients { client.superRoot = mockRootClaim @@ -379,7 +376,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) t.Run("AllNodesAgree_SuperRootDifferentFromClaim", func(t *testing.T) { - validator, clients, metrics := setupMultiSupervisorTest(t, 3) + validator, clients, metrics := setupMultiSupernodeTest(t, 3) differentRoot := common.HexToHash("0xdifferent") for _, client := range clients { @@ -403,46 +400,51 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) } -func setupSuperValidatorTest(t *testing.T) (*SuperAgreementEnricher, *stubSupervisorClient, *stubOutputMetrics) { +func setupSuperValidatorTest(t *testing.T) (*SuperAgreementEnricher, *stubSuperNodeClient, *stubOutputMetrics) { logger := testlog.Logger(t, log.LvlInfo) - client := &stubSupervisorClient{derivedFromL1BlockNum: 0, superRoot: mockRootClaim} + client := &stubSuperNodeClient{derivedFromL1BlockNum: 0, superRoot: mockRootClaim} metrics := &stubOutputMetrics{} validator := NewSuperAgreementEnricher(logger, metrics, []SuperRootProvider{client}, clock.NewDeterministicClock(time.Unix(9824924, 499))) return validator, client, metrics } -func setupMultiSupervisorTest(t *testing.T, numNodes int) (*SuperAgreementEnricher, []*stubSupervisorClient, *stubOutputMetrics) { +func setupMultiSupernodeTest(t *testing.T, numNodes int) (*SuperAgreementEnricher, []*stubSuperNodeClient, *stubOutputMetrics) { logger := testlog.Logger(t, log.LvlInfo) - clients := make([]*stubSupervisorClient, numNodes) - supervisorClients := make([]SuperRootProvider, numNodes) + clients := make([]*stubSuperNodeClient, numNodes) + supernodeClients := make([]SuperRootProvider, numNodes) for i := range clients { - clients[i] = &stubSupervisorClient{ + clients[i] = &stubSuperNodeClient{ derivedFromL1BlockNum: 0, superRoot: mockRootClaim, } - supervisorClients[i] = clients[i] + supernodeClients[i] = clients[i] } metrics := &stubOutputMetrics{} - validator := NewSuperAgreementEnricher(logger, metrics, supervisorClients, clock.NewDeterministicClock(time.Unix(9824924, 499))) + validator := NewSuperAgreementEnricher(logger, metrics, supernodeClients, clock.NewDeterministicClock(time.Unix(9824924, 499))) return validator, clients, metrics } -type stubSupervisorClient struct { +type stubSuperNodeClient struct { requestedTimestamp uint64 outputErr error + notFound bool derivedFromL1BlockNum uint64 superRoot common.Hash } -func (s *stubSupervisorClient) SuperRootAtTimestamp(_ context.Context, timestamp hexutil.Uint64) (eth.SuperRootResponse, error) { +func (s *stubSuperNodeClient) SuperRootAtTimestamp(_ context.Context, timestamp uint64) (eth.SuperRootAtTimestampResponse, error) { s.requestedTimestamp = uint64(timestamp) if s.outputErr != nil { - return eth.SuperRootResponse{}, s.outputErr + return eth.SuperRootAtTimestampResponse{}, s.outputErr } - return eth.SuperRootResponse{ - CrossSafeDerivedFrom: eth.BlockID{Number: s.derivedFromL1BlockNum}, - Timestamp: uint64(timestamp), - SuperRoot: eth.Bytes32(s.superRoot), - Version: eth.SuperRootVersionV1, + if s.notFound { + return eth.SuperRootAtTimestampResponse{}, nil + } + return eth.SuperRootAtTimestampResponse{ + Data: ð.SuperRootResponseData{ + VerifiedRequiredL1: eth.BlockID{Number: s.derivedFromL1BlockNum}, + Super: eth.NewSuperV1(timestamp), + SuperRoot: eth.Bytes32(s.superRoot), + }, }, nil } diff --git a/op-dispute-mon/mon/service.go b/op-dispute-mon/mon/service.go index 73c5ab2275b99..5edd1235aa034 100644 --- a/op-dispute-mon/mon/service.go +++ b/op-dispute-mon/mon/service.go @@ -38,9 +38,9 @@ type Service struct { cl clock.Clock - game *extract.GameCallerCreator - rollupClients []*sources.RollupClient - supervisorClients []*sources.SupervisorClient + game *extract.GameCallerCreator + rollupClients []*sources.RollupClient + supernodeClients []*sources.SuperNodeClient l1RPC rpcclient.RPC l1Client *sources.L1Client @@ -84,8 +84,8 @@ func (s *Service) initFromConfig(ctx context.Context, cfg *config.Config) error if err := s.initOutputRollupClient(ctx, cfg); err != nil { return fmt.Errorf("failed to init rollup client: %w", err) } - if err := s.initSupervisorClients(ctx, cfg); err != nil { - return fmt.Errorf("failed to init supervisor clients: %w", err) + if err := s.initSupernodeClients(ctx, cfg); err != nil { + return fmt.Errorf("failed to init supernode clients: %w", err) } s.initGameCallerCreator() // Must be called before initForecast @@ -111,8 +111,8 @@ func (s *Service) outputRollupClients() []extract.OutputRollupClient { } func (s *Service) asSuperRootProviders() []extract.SuperRootProvider { - clients := make([]extract.SuperRootProvider, len(s.supervisorClients)) - for i, client := range s.supervisorClients { + clients := make([]extract.SuperRootProvider, len(s.supernodeClients)) + for i, client := range s.supernodeClients { clients[i] = client } return clients @@ -132,16 +132,16 @@ func (s *Service) initOutputRollupClient(ctx context.Context, cfg *config.Config return nil } -func (s *Service) initSupervisorClients(ctx context.Context, cfg *config.Config) error { - if len(cfg.SupervisorRpcs) == 0 { +func (s *Service) initSupernodeClients(ctx context.Context, cfg *config.Config) error { + if len(cfg.SupernodeRpcs) == 0 { return nil } - for _, rpc := range cfg.SupervisorRpcs { - client, err := dial.DialSupervisorClientWithTimeout(ctx, s.logger, rpc, rpcclient.WithLazyDial()) + for _, rpc := range cfg.SupernodeRpcs { + client, err := dial.DialSuperNodeClientWithTimeout(ctx, s.logger, rpc, rpcclient.WithLazyDial()) if err != nil { - return fmt.Errorf("failed to dial supervisor client %s: %w", rpc, err) + return fmt.Errorf("failed to dial supernode client %s: %w", rpc, err) } - s.supervisorClients = append(s.supervisorClients, client) + s.supernodeClients = append(s.supernodeClients, client) } return nil } From 108493813fa5ea4d2b4221e329e3400c4665a938 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Tue, 6 Jan 2026 09:30:21 +1000 Subject: [PATCH 11/11] op-dispute-mon: Consistent capitalisation of SuperNode. It's two words. --- op-dispute-mon/README.md | 2 +- op-dispute-mon/cmd/main.go | 2 +- op-dispute-mon/cmd/main_test.go | 10 +++--- op-dispute-mon/config/config.go | 16 +++++----- op-dispute-mon/config/config_test.go | 32 +++++++++---------- op-dispute-mon/flags/flags.go | 12 +++---- .../mon/extract/super_agreement_enricher.go | 6 ++-- .../extract/super_agreement_enricher_test.go | 32 +++++++++---------- op-dispute-mon/mon/service.go | 20 ++++++------ 9 files changed, 66 insertions(+), 66 deletions(-) diff --git a/op-dispute-mon/README.md b/op-dispute-mon/README.md index a7a5f29b6d3e1..37aa5ebb145df 100644 --- a/op-dispute-mon/README.md +++ b/op-dispute-mon/README.md @@ -30,6 +30,6 @@ shows the available config options and can be accessed by running `./bin/op-disp ./bin/op-dispute-mon \ --network \ --l1-eth-rpc \ - --supernode-rpc ,, + --supernode-rpc ,, ``` diff --git a/op-dispute-mon/cmd/main.go b/op-dispute-mon/cmd/main.go index 5c0aa9843b278..971657f5e31ed 100644 --- a/op-dispute-mon/cmd/main.go +++ b/op-dispute-mon/cmd/main.go @@ -59,7 +59,7 @@ func run(ctx context.Context, args []string, action ConfiguredLifecycle) error { logger.Info("RPC endpoints", "l1", cfg.L1EthRpc, "rollup", cfg.RollupRpcs, - "supernode", cfg.SupernodeRpcs, + "superNode", cfg.SuperNodeRpcs, ) return action(ctx.Context, logger, cfg) }) diff --git a/op-dispute-mon/cmd/main_test.go b/op-dispute-mon/cmd/main_test.go index fb3d2f3b288e1..a2431e89f582a 100644 --- a/op-dispute-mon/cmd/main_test.go +++ b/op-dispute-mon/cmd/main_test.go @@ -60,12 +60,12 @@ func TestL1EthRpc(t *testing.T) { }) } -func TestMustSpecifyEitherRollupRpcOrSupernodeRpc(t *testing.T) { +func TestMustSpecifyEitherRollupRpcOrSuperNodeRpc(t *testing.T) { verifyArgsInvalid(t, "flag rollup-rpc or supernode-rpc is required", addRequiredArgsExcept("--rollup-rpc")) } func TestRollupRpc(t *testing.T) { - t.Run("NotRequiredIfSupernodeRpcSupplied", func(t *testing.T) { + t.Run("NotRequiredIfSuperNodeRpcSupplied", func(t *testing.T) { configForArgs(t, addRequiredArgsExcept("--rollup-rpc", "--supernode-rpc", "http://localhost/supernode")) }) @@ -83,7 +83,7 @@ func TestRollupRpc(t *testing.T) { }) } -func TestSupernodeRpc(t *testing.T) { +func TestSuperNodeRpc(t *testing.T) { t.Run("NotRequiredIfRollupRpcSupplied", func(t *testing.T) { // rollup-rpc is in the default args. configForArgs(t, addRequiredArgsExcept("--supernode-rpc")) @@ -92,14 +92,14 @@ func TestSupernodeRpc(t *testing.T) { t.Run("Valid", func(t *testing.T) { url := "http://example.com:9999" cfg := configForArgs(t, addRequiredArgsExcept("--rollup-rpc", "--supernode-rpc", url)) - require.Equal(t, []string{url}, cfg.SupernodeRpcs) + require.Equal(t, []string{url}, cfg.SuperNodeRpcs) }) t.Run("MultipleValues", func(t *testing.T) { url1 := "http://example1.com:9999" url2 := "http://example2.com:8888" cfg := configForArgs(t, addRequiredArgsExcept("--rollup-rpc", "--supernode-rpc", url1, "--supernode-rpc", url2)) - require.Equal(t, []string{url1, url2}, cfg.SupernodeRpcs) + require.Equal(t, []string{url1, url2}, cfg.SuperNodeRpcs) }) } diff --git a/op-dispute-mon/config/config.go b/op-dispute-mon/config/config.go index f0fbe74afec89..724d770957fab 100644 --- a/op-dispute-mon/config/config.go +++ b/op-dispute-mon/config/config.go @@ -14,7 +14,7 @@ import ( var ( ErrMissingL1EthRPC = errors.New("missing l1 eth rpc url") ErrMissingGameFactoryAddress = errors.New("missing game factory address") - ErrMissingRollupAndSupernodeRpc = errors.New("must specify rollup rpc or supernode rpc") + ErrMissingRollupAndSuperNodeRpc = errors.New("must specify rollup rpc or super node rpc") ErrMissingMaxConcurrency = errors.New("missing max concurrency") ) @@ -40,7 +40,7 @@ type Config struct { HonestActors []common.Address // List of honest actors to monitor claims for. RollupRpcs []string // The rollup node RPC URLs. - SupernodeRpcs []string // The supernode RPC URLs. + SuperNodeRpcs []string // The super node RPC URLs. MonitorInterval time.Duration // Frequency to check for new games to monitor. GameWindow time.Duration // Maximum window to look for games to monitor. IgnoredGames []common.Address // Games to exclude from monitoring @@ -50,19 +50,19 @@ type Config struct { PprofConfig oppprof.CLIConfig } -func NewInteropConfig(gameFactoryAddress common.Address, l1EthRpc string, supernodeRpcs []string) Config { - return NewCombinedConfig(gameFactoryAddress, l1EthRpc, nil, supernodeRpcs) +func NewInteropConfig(gameFactoryAddress common.Address, l1EthRpc string, superNodeRpcs []string) Config { + return NewCombinedConfig(gameFactoryAddress, l1EthRpc, nil, superNodeRpcs) } func NewConfig(gameFactoryAddress common.Address, l1EthRpc string, rollupRpcs []string) Config { return NewCombinedConfig(gameFactoryAddress, l1EthRpc, rollupRpcs, nil) } -func NewCombinedConfig(gameFactoryAddress common.Address, l1EthRpc string, rollupRpcs []string, supernodeRpcs []string) Config { +func NewCombinedConfig(gameFactoryAddress common.Address, l1EthRpc string, rollupRpcs []string, superNodeRpcs []string) Config { return Config{ L1EthRpc: l1EthRpc, RollupRpcs: rollupRpcs, - SupernodeRpcs: supernodeRpcs, + SuperNodeRpcs: superNodeRpcs, GameFactoryAddress: gameFactoryAddress, MonitorInterval: DefaultMonitorInterval, @@ -78,8 +78,8 @@ func (c Config) Check() error { if c.L1EthRpc == "" { return ErrMissingL1EthRPC } - if len(c.RollupRpcs) == 0 && len(c.SupernodeRpcs) == 0 { - return ErrMissingRollupAndSupernodeRpc + if len(c.RollupRpcs) == 0 && len(c.SuperNodeRpcs) == 0 { + return ErrMissingRollupAndSuperNodeRpc } if c.GameFactoryAddress == (common.Address{}) { return ErrMissingGameFactoryAddress diff --git a/op-dispute-mon/config/config_test.go b/op-dispute-mon/config/config_test.go index 0dff73cd38d2c..1955d5c65ab76 100644 --- a/op-dispute-mon/config/config_test.go +++ b/op-dispute-mon/config/config_test.go @@ -12,7 +12,7 @@ var ( validL1EthRpc = "http://localhost:8545" validGameFactoryAddress = common.Address{0x23} validRollupRpcs = []string{"http://localhost:8555"} - validSupernodeRpcs = []string{"http://localhost:8999"} + validSuperNodeRpcs = []string{"http://localhost:8999"} ) func validConfig() Config { @@ -35,24 +35,24 @@ func TestGameFactoryAddressRequired(t *testing.T) { require.ErrorIs(t, config.Check(), ErrMissingGameFactoryAddress) } -func TestRollupRpcOrSupernodeRpcRequired(t *testing.T) { +func TestRollupRpcOrSuperNodeRpcRequired(t *testing.T) { config := validConfig() config.RollupRpcs = nil - config.SupernodeRpcs = nil - require.ErrorIs(t, config.Check(), ErrMissingRollupAndSupernodeRpc) + config.SuperNodeRpcs = nil + require.ErrorIs(t, config.Check(), ErrMissingRollupAndSuperNodeRpc) } -func TestRollupRpcNotRequiredWhenSupernodeRpcSet(t *testing.T) { +func TestRollupRpcNotRequiredWhenSuperNodeRpcSet(t *testing.T) { config := validConfig() config.RollupRpcs = nil - config.SupernodeRpcs = validSupernodeRpcs + config.SuperNodeRpcs = validSuperNodeRpcs require.NoError(t, config.Check()) } -func TestSupernodeRpcNotRequiredWhenRollupRpcSet(t *testing.T) { +func TestSuperNodeRpcNotRequiredWhenRollupRpcSet(t *testing.T) { config := validConfig() config.RollupRpcs = validRollupRpcs - config.SupernodeRpcs = nil + config.SuperNodeRpcs = nil require.NoError(t, config.Check()) } @@ -62,22 +62,22 @@ func TestMaxConcurrencyRequired(t *testing.T) { require.ErrorIs(t, config.Check(), ErrMissingMaxConcurrency) } -func TestMultipleSupernodeRpcs(t *testing.T) { +func TestMultipleSuperNodeRpcs(t *testing.T) { config := validConfig() config.RollupRpcs = nil - config.SupernodeRpcs = []string{"http://localhost:8999", "http://localhost:9000", "http://localhost:9001"} + config.SuperNodeRpcs = []string{"http://localhost:8999", "http://localhost:9000", "http://localhost:9001"} require.NoError(t, config.Check()) } func TestInteropConfig(t *testing.T) { gameFactoryAddr := common.Address{0x42} l1RPC := "http://localhost:8545" - supernodeRpcs := []string{"http://localhost:8999", "http://localhost:9000"} + superNodeRpcs := []string{"http://localhost:8999", "http://localhost:9000"} - config := NewInteropConfig(gameFactoryAddr, l1RPC, supernodeRpcs) + config := NewInteropConfig(gameFactoryAddr, l1RPC, superNodeRpcs) require.Equal(t, gameFactoryAddr, config.GameFactoryAddress) require.Equal(t, l1RPC, config.L1EthRpc) - require.Equal(t, supernodeRpcs, config.SupernodeRpcs) + require.Equal(t, superNodeRpcs, config.SuperNodeRpcs) require.Nil(t, config.RollupRpcs) require.NoError(t, config.Check()) } @@ -86,12 +86,12 @@ func TestCombinedConfig(t *testing.T) { gameFactoryAddr := common.Address{0x42} l1RPC := "http://localhost:8545" rollupRpcs := []string{"http://localhost:8555"} - supernodeRpcs := []string{"http://localhost:8999"} + superNodeRpcs := []string{"http://localhost:8999"} - config := NewCombinedConfig(gameFactoryAddr, l1RPC, rollupRpcs, supernodeRpcs) + config := NewCombinedConfig(gameFactoryAddr, l1RPC, rollupRpcs, superNodeRpcs) require.Equal(t, gameFactoryAddr, config.GameFactoryAddress) require.Equal(t, l1RPC, config.L1EthRpc) require.Equal(t, rollupRpcs, config.RollupRpcs) - require.Equal(t, supernodeRpcs, config.SupernodeRpcs) + require.Equal(t, superNodeRpcs, config.SuperNodeRpcs) require.NoError(t, config.Check()) } diff --git a/op-dispute-mon/flags/flags.go b/op-dispute-mon/flags/flags.go index ada15a323c762..e27267a4989a0 100644 --- a/op-dispute-mon/flags/flags.go +++ b/op-dispute-mon/flags/flags.go @@ -38,9 +38,9 @@ var ( Usage: "HTTP provider URL for the rollup node. Multiple URLs can be specified for redundancy.", EnvVars: prefixEnvVars("ROLLUP_RPC"), } - SupernodeRpcFlag = &cli.StringSliceFlag{ + SuperNodeRpcFlag = &cli.StringSliceFlag{ Name: "supernode-rpc", - Usage: "HTTP provider URL for supernodes. Multiple URLs can be specified for redundancy.", + Usage: "HTTP provider URL for super nodes. Multiple URLs can be specified for redundancy.", EnvVars: prefixEnvVars("SUPERNODE_RPC"), } GameFactoryAddressFlag = &cli.StringFlag{ @@ -92,7 +92,7 @@ var requiredFlags = []cli.Flag{ // optionalFlags is a list of unchecked cli flags var optionalFlags = []cli.Flag{ RollupRpcFlag, - SupernodeRpcFlag, + SuperNodeRpcFlag, GameFactoryAddressFlag, NetworkFlag, HonestActorsFlag, @@ -119,8 +119,8 @@ func CheckRequired(ctx *cli.Context) error { return fmt.Errorf("flag %s is required", f.Names()[0]) } } - if len(ctx.StringSlice(RollupRpcFlag.Name)) == 0 && len(ctx.StringSlice(SupernodeRpcFlag.Name)) == 0 { - return fmt.Errorf("flag %s or %s is required", RollupRpcFlag.Name, SupernodeRpcFlag.Name) + if len(ctx.StringSlice(RollupRpcFlag.Name)) == 0 && len(ctx.StringSlice(SuperNodeRpcFlag.Name)) == 0 { + return fmt.Errorf("flag %s or %s is required", RollupRpcFlag.Name, SuperNodeRpcFlag.Name) } return nil } @@ -169,7 +169,7 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) { L1EthRpc: ctx.String(L1EthRpcFlag.Name), GameFactoryAddress: gameFactoryAddress, RollupRpcs: ctx.StringSlice(RollupRpcFlag.Name), - SupernodeRpcs: ctx.StringSlice(SupernodeRpcFlag.Name), + SuperNodeRpcs: ctx.StringSlice(SuperNodeRpcFlag.Name), HonestActors: actors, MonitorInterval: ctx.Duration(MonitorIntervalFlag.Name), diff --git a/op-dispute-mon/mon/extract/super_agreement_enricher.go b/op-dispute-mon/mon/extract/super_agreement_enricher.go index e1be248e5e5db..3d50c7fb0650e 100644 --- a/op-dispute-mon/mon/extract/super_agreement_enricher.go +++ b/op-dispute-mon/mon/extract/super_agreement_enricher.go @@ -15,8 +15,8 @@ import ( ) var ( - ErrSupernodeRpcRequired = errors.New("supernode rpc required") - ErrAllSuperNodesUnavailable = errors.New("all supernodes returned errors") + ErrSuperNodeRpcRequired = errors.New("super node rpc required") + ErrAllSuperNodesUnavailable = errors.New("all super nodes returned errors") ) type SuperRootProvider interface { @@ -51,7 +51,7 @@ func (e *SuperAgreementEnricher) Enrich(ctx context.Context, block rpcblock.Bloc return nil } if len(e.clients) == 0 { - return fmt.Errorf("%w but required for game type %v", ErrSupernodeRpcRequired, game.GameType) + return fmt.Errorf("%w but required for game type %v", ErrSuperNodeRpcRequired, game.GameType) } results := make([]superRootResult, len(e.clients)) diff --git a/op-dispute-mon/mon/extract/super_agreement_enricher_test.go b/op-dispute-mon/mon/extract/super_agreement_enricher_test.go index b943da4bbfaa5..4c1ccf9f0f432 100644 --- a/op-dispute-mon/mon/extract/super_agreement_enricher_test.go +++ b/op-dispute-mon/mon/extract/super_agreement_enricher_test.go @@ -21,7 +21,7 @@ import ( func TestDetector_CheckSuperRootAgreement(t *testing.T) { t.Parallel() - t.Run("ErrorWhenNoSupernodeClient", func(t *testing.T) { + t.Run("ErrorWhenNoSuperNodeClient", func(t *testing.T) { validator, _, _ := setupSuperValidatorTest(t) validator.clients = nil // Set to nil to test the error case game := &types.EnrichedGameData{ @@ -33,7 +33,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { RootClaim: mockRootClaim, } err := validator.Enrich(context.Background(), rpcblock.Latest, nil, game) - require.ErrorIs(t, err, ErrSupernodeRpcRequired) + require.ErrorIs(t, err, ErrSuperNodeRpcRequired) }) t.Run("SkipOutputRootGameTypes", func(t *testing.T) { @@ -42,7 +42,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { gameType := gameType t.Run(fmt.Sprintf("GameType_%d", gameType), func(t *testing.T) { validator, _, metrics := setupSuperValidatorTest(t) - validator.clients = nil // Should not error even though there's no supernode client + validator.clients = nil // Should not error even though there's no super node client game := &types.EnrichedGameData{ GameMetadata: challengerTypes.GameMetadata{ GameType: gameType, @@ -205,7 +205,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) t.Run("AllSuperNodesReturnError", func(t *testing.T) { - validator, clients, metrics := setupMultiSupernodeTest(t, 3) + validator, clients, metrics := setupMultiSuperNodeTest(t, 3) for _, client := range clients { client.outputErr = errors.New("boom") } @@ -226,7 +226,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) t.Run("AllSuperNodesReturnNotFound", func(t *testing.T) { - validator, clients, metrics := setupMultiSupernodeTest(t, 3) + validator, clients, metrics := setupMultiSuperNodeTest(t, 3) for _, client := range clients { client.notFound = true } @@ -246,7 +246,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) t.Run("SomeSuperNodesOutOfSync", func(t *testing.T) { - validator, clients, metrics := setupMultiSupernodeTest(t, 3) + validator, clients, metrics := setupMultiSuperNodeTest(t, 3) clients[0].notFound = true clients[1].outputErr = nil clients[2].outputErr = nil @@ -266,7 +266,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) t.Run("SuperNodesDiverged", func(t *testing.T) { - validator, clients, metrics := setupMultiSupernodeTest(t, 3) + validator, clients, metrics := setupMultiSuperNodeTest(t, 3) divergedRoot := common.HexToHash("0x5678") clients[0].superRoot = mockRootClaim clients[1].superRoot = divergedRoot @@ -287,7 +287,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) t.Run("AllSuperNodesAgree", func(t *testing.T) { - validator, clients, metrics := setupMultiSupernodeTest(t, 3) + validator, clients, metrics := setupMultiSuperNodeTest(t, 3) clients[0].derivedFromL1BlockNum = 200 clients[1].derivedFromL1BlockNum = 199 clients[2].derivedFromL1BlockNum = 201 @@ -307,7 +307,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) t.Run("MixedResponses_FoundNodesMatchClaimAndSafe", func(t *testing.T) { - validator, clients, metrics := setupMultiSupernodeTest(t, 4) + validator, clients, metrics := setupMultiSuperNodeTest(t, 4) clients[0].notFound = true clients[1].notFound = true clients[2].superRoot = mockRootClaim @@ -330,7 +330,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) t.Run("MixedResponses_FoundNodesDontMatchClaim", func(t *testing.T) { - validator, clients, metrics := setupMultiSupernodeTest(t, 3) + validator, clients, metrics := setupMultiSuperNodeTest(t, 3) differentRoot := common.HexToHash("0x9999") clients[0].notFound = true clients[1].superRoot = differentRoot @@ -353,7 +353,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) t.Run("AllNodesAgree_SuperRootMatchesClaim_NoneReportSafe", func(t *testing.T) { - validator, clients, metrics := setupMultiSupernodeTest(t, 3) + validator, clients, metrics := setupMultiSuperNodeTest(t, 3) for _, client := range clients { client.superRoot = mockRootClaim @@ -376,7 +376,7 @@ func TestDetector_CheckSuperRootAgreement(t *testing.T) { }) t.Run("AllNodesAgree_SuperRootDifferentFromClaim", func(t *testing.T) { - validator, clients, metrics := setupMultiSupernodeTest(t, 3) + validator, clients, metrics := setupMultiSuperNodeTest(t, 3) differentRoot := common.HexToHash("0xdifferent") for _, client := range clients { @@ -408,19 +408,19 @@ func setupSuperValidatorTest(t *testing.T) (*SuperAgreementEnricher, *stubSuperN return validator, client, metrics } -func setupMultiSupernodeTest(t *testing.T, numNodes int) (*SuperAgreementEnricher, []*stubSuperNodeClient, *stubOutputMetrics) { +func setupMultiSuperNodeTest(t *testing.T, numNodes int) (*SuperAgreementEnricher, []*stubSuperNodeClient, *stubOutputMetrics) { logger := testlog.Logger(t, log.LvlInfo) clients := make([]*stubSuperNodeClient, numNodes) - supernodeClients := make([]SuperRootProvider, numNodes) + superNodeClients := make([]SuperRootProvider, numNodes) for i := range clients { clients[i] = &stubSuperNodeClient{ derivedFromL1BlockNum: 0, superRoot: mockRootClaim, } - supernodeClients[i] = clients[i] + superNodeClients[i] = clients[i] } metrics := &stubOutputMetrics{} - validator := NewSuperAgreementEnricher(logger, metrics, supernodeClients, clock.NewDeterministicClock(time.Unix(9824924, 499))) + validator := NewSuperAgreementEnricher(logger, metrics, superNodeClients, clock.NewDeterministicClock(time.Unix(9824924, 499))) return validator, clients, metrics } diff --git a/op-dispute-mon/mon/service.go b/op-dispute-mon/mon/service.go index 5edd1235aa034..43899427978e7 100644 --- a/op-dispute-mon/mon/service.go +++ b/op-dispute-mon/mon/service.go @@ -40,7 +40,7 @@ type Service struct { game *extract.GameCallerCreator rollupClients []*sources.RollupClient - supernodeClients []*sources.SuperNodeClient + superNodeClients []*sources.SuperNodeClient l1RPC rpcclient.RPC l1Client *sources.L1Client @@ -84,8 +84,8 @@ func (s *Service) initFromConfig(ctx context.Context, cfg *config.Config) error if err := s.initOutputRollupClient(ctx, cfg); err != nil { return fmt.Errorf("failed to init rollup client: %w", err) } - if err := s.initSupernodeClients(ctx, cfg); err != nil { - return fmt.Errorf("failed to init supernode clients: %w", err) + if err := s.initSuperNodeClients(ctx, cfg); err != nil { + return fmt.Errorf("failed to init super node clients: %w", err) } s.initGameCallerCreator() // Must be called before initForecast @@ -111,8 +111,8 @@ func (s *Service) outputRollupClients() []extract.OutputRollupClient { } func (s *Service) asSuperRootProviders() []extract.SuperRootProvider { - clients := make([]extract.SuperRootProvider, len(s.supernodeClients)) - for i, client := range s.supernodeClients { + clients := make([]extract.SuperRootProvider, len(s.superNodeClients)) + for i, client := range s.superNodeClients { clients[i] = client } return clients @@ -132,16 +132,16 @@ func (s *Service) initOutputRollupClient(ctx context.Context, cfg *config.Config return nil } -func (s *Service) initSupernodeClients(ctx context.Context, cfg *config.Config) error { - if len(cfg.SupernodeRpcs) == 0 { +func (s *Service) initSuperNodeClients(ctx context.Context, cfg *config.Config) error { + if len(cfg.SuperNodeRpcs) == 0 { return nil } - for _, rpc := range cfg.SupernodeRpcs { + for _, rpc := range cfg.SuperNodeRpcs { client, err := dial.DialSuperNodeClientWithTimeout(ctx, s.logger, rpc, rpcclient.WithLazyDial()) if err != nil { - return fmt.Errorf("failed to dial supernode client %s: %w", rpc, err) + return fmt.Errorf("failed to dial super node client %s: %w", rpc, err) } - s.supernodeClients = append(s.supernodeClients, client) + s.superNodeClients = append(s.superNodeClients, client) } return nil }