diff --git a/op-challenger/game/fault/agent.go b/op-challenger/game/fault/agent.go index c76331ce667..72249268bed 100644 --- a/op-challenger/game/fault/agent.go +++ b/op-challenger/game/fault/agent.go @@ -9,17 +9,41 @@ import ( "sync/atomic" "time" + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/claims" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/preimages" + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/responder" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/solver" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" + "github.com/ethereum-optimism/optimism/op-challenger/game/generic" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" "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/batching/rpcblock" + "github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" ) +type TxSender interface { + From() common.Address + SendAndWaitSimple(txPurpose string, txs ...txmgr.TxCandidate) error +} + +type GameContract interface { + generic.GenericGameLoader + preimages.PreimageGameContract + responder.GameContract + claims.BondContract + ClaimLoader + GetStatus(ctx context.Context) (gameTypes.GameStatus, error) + GetMaxGameDepth(ctx context.Context) (types.Depth, error) + GetMaxClockDuration(ctx context.Context) (time.Duration, error) + GetOracle(ctx context.Context) (contracts.PreimageOracleContract, error) + GetL1Head(ctx context.Context) (common.Hash, error) +} + // Responder takes a response action & executes. // For full op-challenger this means executing the transaction on chain. type Responder interface { @@ -31,6 +55,7 @@ type Responder interface { } type ClaimLoader interface { + GetClaimCount(ctx context.Context) (uint64, error) GetAllClaims(ctx context.Context, block rpcblock.Block) ([]types.Claim, error) IsL2BlockNumberChallenged(ctx context.Context, block rpcblock.Block) (bool, error) GetClockExtension(ctx context.Context) (time.Duration, error) @@ -39,6 +64,8 @@ type ClaimLoader interface { GetOracle(ctx context.Context) (contracts.PreimageOracleContract, error) } +type resourceCreator func(ctx context.Context, logger log.Logger, gameDepth types.Depth, l1Head eth.BlockID, dir string) (types.TraceAccessor, error) + type Agent struct { metrics metrics.Metricer systemClock clock.Clock @@ -56,6 +83,71 @@ type Agent struct { responseCount atomic.Uint64 // Number of responses made in this game } +func AgentCreator( + systemClock clock.Clock, + l1Clock types.ClockReader, + m metrics.Metricer, + dir string, + txSender TxSender, + loader GameContract, + creator resourceCreator, + selective bool, + claimants []common.Address, + responseDelay time.Duration, + responseDelayAfter uint64, +) generic.ActorCreator { + return func(ctx context.Context, logger log.Logger, l1Head eth.BlockID) (generic.Actor, error) { + maxClockDuration, err := loader.GetMaxClockDuration(ctx) + if err != nil { + return nil, fmt.Errorf("failed to fetch the game duration: %w", err) + } + + gameDepth, err := loader.GetMaxGameDepth(ctx) + if err != nil { + return nil, fmt.Errorf("failed to fetch the game depth: %w", err) + } + + accessor, err := creator(ctx, logger, gameDepth, l1Head, dir) + if err != nil { + return nil, fmt.Errorf("failed to create trace accessor: %w", err) + } + + oracle, err := loader.GetOracle(ctx) + if err != nil { + return nil, fmt.Errorf("failed to load oracle: %w", err) + } + + minLargePreimageSize, err := oracle.MinLargePreimageSize(ctx) + if err != nil { + return nil, fmt.Errorf("failed to load min large preimage size: %w", err) + } + direct := preimages.NewDirectPreimageUploader(logger, txSender, loader) + large := preimages.NewLargePreimageUploader(logger, l1Clock, txSender, oracle) + uploader := preimages.NewSplitPreimageUploader(direct, large, minLargePreimageSize) + responder, err := responder.NewFaultResponder(logger, txSender, loader, uploader, oracle) + if err != nil { + return nil, fmt.Errorf("failed to create the responder: %w", err) + } + + agent := NewAgent( + m, + systemClock, + l1Clock, + loader, + gameDepth, + maxClockDuration, + accessor, + responder, + logger, + selective, + claimants, + responseDelay, + responseDelayAfter, + ) + return agent, nil + } +} + func NewAgent( m metrics.Metricer, systemClock clock.Clock, @@ -126,6 +218,14 @@ func (a *Agent) Act(ctx context.Context) error { return nil } +func (a *Agent) AdditionalStatus(ctx context.Context) ([]any, error) { + claimCount, err := a.loader.GetClaimCount(ctx) + if err != nil { + return nil, err + } + return []any{"claims", claimCount}, nil +} + func (a *Agent) performAction(ctx context.Context, wg *sync.WaitGroup, game types.Game, action types.Action) { defer wg.Done() actionLog := a.log.New("action", action.Type) diff --git a/op-challenger/game/fault/agent_test.go b/op-challenger/game/fault/agent_test.go index 8f274dbe106..669c5a23df0 100644 --- a/op-challenger/game/fault/agent_test.go +++ b/op-challenger/game/fault/agent_test.go @@ -236,6 +236,10 @@ func (s *stubClaimLoader) IsL2BlockNumberChallenged(_ context.Context, _ rpcbloc return s.blockNumChallenged, nil } +func (s *stubClaimLoader) GetClaimCount(_ context.Context) (uint64, error) { + return uint64(len(s.claims)), nil +} + func (s *stubClaimLoader) GetAllClaims(_ context.Context, _ rpcblock.Block) ([]types.Claim, error) { s.callCount++ if s.callCount > s.maxLoads && s.maxLoads != 0 { diff --git a/op-challenger/game/fault/register_task.go b/op-challenger/game/fault/register_task.go index f87aee3b27b..dbd10bd2fa4 100644 --- a/op-challenger/game/fault/register_task.go +++ b/op-challenger/game/fault/register_task.go @@ -20,6 +20,7 @@ import ( "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" + "github.com/ethereum-optimism/optimism/op-challenger/game/generic" "github.com/ethereum-optimism/optimism/op-challenger/game/scheduler" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-challenger/metrics" @@ -35,7 +36,7 @@ type RegisterTask struct { gameType gameTypes.GameType skipPrestateValidation bool - syncValidator SyncValidator + syncValidator generic.SyncValidator getTopPrestateProvider func(ctx context.Context, prestateBlock uint64) (faultTypes.PrestateProvider, error) getBottomPrestateProvider func(ctx context.Context, prestateHash common.Hash) (faultTypes.PrestateProvider, error) @@ -51,11 +52,11 @@ 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 SyncValidator) *RegisterTask { +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 NewSuperCannonKonaRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider super.RootProvider, syncValidator SyncValidator) *RegisterTask { +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) } @@ -65,7 +66,7 @@ func newSuperCannonVMRegisterTaskWithConfig( m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider super.RootProvider, - syncValidator SyncValidator, + syncValidator generic.SyncValidator, vmCfg vm.Config, preStateBaseURL *url.URL, preState string, @@ -105,11 +106,11 @@ func newSuperCannonVMRegisterTaskWithConfig( } } -func NewCannonRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator SyncValidator) *RegisterTask { +func NewCannonRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator generic.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 SyncValidator) *RegisterTask { +func NewCannonKonaRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator generic.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 SyncValidator, + syncValidator generic.SyncValidator, vmCfg vm.Config, preStateBaseURL *url.URL, preState string, @@ -162,7 +163,7 @@ func newCannonVMRegisterTaskWithConfig( } } -func NewAsteriscRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator SyncValidator) *RegisterTask { +func NewAsteriscRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator generic.SyncValidator) *RegisterTask { stateConverter := asterisc.NewStateConverter(cfg.Asterisc) return &RegisterTask{ gameType: gameType, @@ -196,7 +197,7 @@ func NewAsteriscRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m } } -func NewAsteriscKonaRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator SyncValidator) *RegisterTask { +func NewAsteriscKonaRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator generic.SyncValidator) *RegisterTask { stateConverter := asterisc.NewStateConverter(cfg.Asterisc) return &RegisterTask{ gameType: gameType, @@ -230,7 +231,7 @@ func NewAsteriscKonaRegisterTask(gameType gameTypes.GameType, cfg *config.Config } } -func NewSuperAsteriscKonaRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider super.RootProvider, syncValidator SyncValidator) *RegisterTask { +func NewSuperAsteriscKonaRegisterTask(gameType gameTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider super.RootProvider, syncValidator generic.SyncValidator) *RegisterTask { stateConverter := asterisc.NewStateConverter(cfg.AsteriscKona) return &RegisterTask{ gameType: gameType, @@ -266,7 +267,7 @@ func NewSuperAsteriscKonaRegisterTask(gameType gameTypes.GameType, cfg *config.C } } -func NewAlphabetRegisterTask(gameType gameTypes.GameType, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator SyncValidator) *RegisterTask { +func NewAlphabetRegisterTask(gameType gameTypes.GameType, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator generic.SyncValidator) *RegisterTask { return &RegisterTask{ gameType: gameType, syncValidator: syncValidator, @@ -323,7 +324,7 @@ func (e *RegisterTask) Register( txSender TxSender, gameFactory *contracts.DisputeGameFactoryContract, caller *batching.MultiCaller, - l1HeaderSource L1HeaderSource, + l1HeaderSource generic.L1HeaderSource, selective bool, claimants []common.Address, responseDelay time.Duration, @@ -357,27 +358,32 @@ func (e *RegisterTask) Register( if err != nil { return nil, fmt.Errorf("failed to load split depth: %w", err) } - l1HeadID, err := loadL1Head(contract, ctx, l1HeaderSource) - if err != nil { - return nil, err - } prestateProvider, err := e.getTopPrestateProvider(ctx, prestateBlock) if err != nil { return nil, fmt.Errorf("failed to create top prestate provider: %w", err) } - creator := func(ctx context.Context, logger log.Logger, gameDepth faultTypes.Depth, dir string) (faultTypes.TraceAccessor, error) { + creator := func(ctx context.Context, logger log.Logger, gameDepth faultTypes.Depth, l1HeadID eth.BlockID, dir string) (faultTypes.TraceAccessor, error) { accessor, err := e.newTraceAccessor(logger, m, prestateProvider, vmPrestateProvider, dir, l1HeadID, splitDepth, prestateBlock, poststateBlock) if err != nil { return nil, err } return accessor, nil } - var validators []Validator + var validators []generic.PrestateValidator if !e.skipPrestateValidation { validators = append(validators, NewPrestateValidator(e.gameType.String(), contract.GetAbsolutePrestateHash, vmPrestateProvider)) validators = append(validators, NewPrestateValidator("output root", contract.GetStartingRootHash, prestateProvider)) } - return NewGamePlayer(ctx, systemClock, l1Clock, logger, m, dir, game.Proxy, txSender, contract, e.syncValidator, validators, creator, l1HeaderSource, selective, claimants, responseDelay, responseDelayAfter) + return generic.NewGenericGamePlayer( + ctx, + logger, + game.Proxy, + contract, + e.syncValidator, + validators, + l1HeaderSource, + AgentCreator(systemClock, l1Clock, m, dir, txSender, contract, creator, selective, claimants, responseDelay, responseDelayAfter), + ) } err := registerOracle(ctx, logger, oracles, gameFactory, e.gameType) if err != nil { @@ -413,15 +419,3 @@ func registerOracle(ctx context.Context, logger log.Logger, oracles OracleRegist oracles.RegisterOracle(oracle) return nil } - -func loadL1Head(contract contracts.FaultDisputeGameContract, ctx context.Context, l1HeaderSource L1HeaderSource) (eth.BlockID, error) { - l1Head, err := contract.GetL1Head(ctx) - if err != nil { - return eth.BlockID{}, fmt.Errorf("failed to load L1 head: %w", err) - } - l1Header, err := l1HeaderSource.HeaderByHash(ctx, l1Head) - if err != nil { - return eth.BlockID{}, fmt.Errorf("failed to load L1 header: %w", err) - } - return eth.HeaderBlockID(l1Header), nil -} diff --git a/op-challenger/game/fault/player.go b/op-challenger/game/generic/player.go similarity index 52% rename from op-challenger/game/fault/player.go rename to op-challenger/game/generic/player.go index b1b83738b0f..5545a51b709 100644 --- a/op-challenger/game/fault/player.go +++ b/op-challenger/game/generic/player.go @@ -1,37 +1,35 @@ -package fault +package generic import ( "context" "errors" "fmt" - "time" "github.com/ethereum-optimism/optimism/op-challenger/game/client" - "github.com/ethereum-optimism/optimism/op-challenger/game/fault/claims" - "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" - "github.com/ethereum-optimism/optimism/op-challenger/game/fault/preimages" - "github.com/ethereum-optimism/optimism/op-challenger/game/fault/responder" - "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" - "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/txmgr" "github.com/ethereum/go-ethereum/common" gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" ) -type actor func(ctx context.Context) error +type PrestateValidator interface { + Validate(ctx context.Context) error +} + +type Actor interface { + Act(ctx context.Context) error + AdditionalStatus(ctx context.Context) ([]any, error) +} -type GameInfo interface { +type GenericGameLoader interface { + GetL1Head(context.Context) (common.Hash, error) GetStatus(context.Context) (gameTypes.GameStatus, error) - GetClaimCount(context.Context) (uint64, error) } type SyncValidator interface { // ValidateNodeSynced checks that the local node is sufficiently up to date to play the game. - // It returns types.ErrNotInSync if the node is too far behind. + // It returns client.ErrNotInSync if the node is too far behind. ValidateNodeSynced(ctx context.Context, gameL1Head eth.BlockID) error } @@ -39,58 +37,32 @@ type L1HeaderSource interface { HeaderByHash(context.Context, common.Hash) (*gethTypes.Header, error) } -type TxSender interface { - From() common.Address - SendAndWaitSimple(txPurpose string, txs ...txmgr.TxCandidate) error -} +type ActorCreator func(ctx context.Context, logger log.Logger, l1Head eth.BlockID) (Actor, error) type GamePlayer struct { - act actor - loader GameInfo + actor Actor + loader GenericGameLoader logger log.Logger syncValidator SyncValidator - prestateValidators []Validator + prestateValidators []PrestateValidator status gameTypes.GameStatus gameL1Head eth.BlockID } -type GameContract interface { - preimages.PreimageGameContract - responder.GameContract - claims.BondContract - GameInfo - ClaimLoader - GetStatus(ctx context.Context) (gameTypes.GameStatus, error) - GetMaxGameDepth(ctx context.Context) (types.Depth, error) - GetMaxClockDuration(ctx context.Context) (time.Duration, error) - GetOracle(ctx context.Context) (contracts.PreimageOracleContract, error) - GetL1Head(ctx context.Context) (common.Hash, error) -} - -var actNoop = func(ctx context.Context) error { - return nil -} +type actNoop struct{} -type resourceCreator func(ctx context.Context, logger log.Logger, gameDepth types.Depth, dir string) (types.TraceAccessor, error) +func (a *actNoop) Act(_ context.Context) error { return nil } +func (a *actNoop) AdditionalStatus(_ context.Context) ([]any, error) { return nil, nil } -func NewGamePlayer( +func NewGenericGamePlayer( ctx context.Context, - systemClock clock.Clock, - l1Clock types.ClockReader, logger log.Logger, - m metrics.Metricer, - dir string, addr common.Address, - txSender TxSender, - loader GameContract, + loader GenericGameLoader, syncValidator SyncValidator, - validators []Validator, - creator resourceCreator, + validators []PrestateValidator, l1HeaderSource L1HeaderSource, - selective bool, - claimants []common.Address, - responseDelay time.Duration, - responseDelayAfter uint64, + createActor ActorCreator, ) (*GamePlayer, error) { logger = logger.New("game", addr) @@ -107,30 +79,9 @@ func NewGamePlayer( prestateValidators: validators, status: status, // Act function does nothing because the game is already complete - act: actNoop, + actor: &actNoop{}, }, nil } - - maxClockDuration, err := loader.GetMaxClockDuration(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch the game duration: %w", err) - } - - gameDepth, err := loader.GetMaxGameDepth(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch the game depth: %w", err) - } - - accessor, err := creator(ctx, logger, gameDepth, dir) - if err != nil { - return nil, fmt.Errorf("failed to create trace accessor: %w", err) - } - - oracle, err := loader.GetOracle(ctx) - if err != nil { - return nil, fmt.Errorf("failed to load oracle: %w", err) - } - l1HeadHash, err := loader.GetL1Head(ctx) if err != nil { return nil, fmt.Errorf("failed to load game L1 head: %w", err) @@ -141,21 +92,13 @@ func NewGamePlayer( } l1Head := eth.HeaderBlockID(l1Header) - minLargePreimageSize, err := oracle.MinLargePreimageSize(ctx) - if err != nil { - return nil, fmt.Errorf("failed to load min large preimage size: %w", err) - } - direct := preimages.NewDirectPreimageUploader(logger, txSender, loader) - large := preimages.NewLargePreimageUploader(logger, l1Clock, txSender, oracle) - uploader := preimages.NewSplitPreimageUploader(direct, large, minLargePreimageSize) - responder, err := responder.NewFaultResponder(logger, txSender, loader, uploader, oracle) + actor, err := createActor(ctx, logger, l1Head) if err != nil { - return nil, fmt.Errorf("failed to create the responder: %w", err) + return nil, fmt.Errorf("failed to create actor: %w", err) } - agent := NewAgent(m, systemClock, l1Clock, loader, gameDepth, maxClockDuration, accessor, responder, logger, selective, claimants, responseDelay, responseDelayAfter) return &GamePlayer{ - act: agent.Act, + actor: actor, loader: loader, logger: logger, status: status, @@ -192,7 +135,7 @@ func (g *GamePlayer) ProgressGame(ctx context.Context) gameTypes.GameStatus { return g.status } g.logger.Trace("Checking if actions are required") - if err := g.act(ctx); err != nil { + if err := g.actor.Act(ctx); err != nil { g.logger.Error("Error when acting on game", "err", err) } status, err := g.loader.GetStatus(ctx) @@ -204,19 +147,20 @@ func (g *GamePlayer) ProgressGame(ctx context.Context) gameTypes.GameStatus { g.status = status if status != gameTypes.GameStatusInProgress { // Release the agent as we will no longer need to act on this game. - g.act = actNoop + g.actor = &actNoop{} } return status } func (g *GamePlayer) logGameStatus(ctx context.Context, status gameTypes.GameStatus) { if status == gameTypes.GameStatusInProgress { - claimCount, err := g.loader.GetClaimCount(ctx) + additionalStatus, err := g.actor.AdditionalStatus(ctx) if err != nil { - g.logger.Error("Failed to get claim count for in progress game", "err", err) + g.logger.Error("Failed to get additional status info for in progress game", "err", err) return } - g.logger.Info("Game info", "claims", claimCount, "status", status) + additionalStatus = append(additionalStatus, "status", g.status) + g.logger.Info("Game info", additionalStatus...) return } g.logger.Info("Game resolved", "status", status) diff --git a/op-challenger/game/fault/player_test.go b/op-challenger/game/generic/player_test.go similarity index 83% rename from op-challenger/game/fault/player_test.go rename to op-challenger/game/generic/player_test.go index 7c05525a882..ba3348e2c25 100644 --- a/op-challenger/game/fault/player_test.go +++ b/op-challenger/game/generic/player_test.go @@ -1,4 +1,4 @@ -package fault +package generic import ( "context" @@ -33,7 +33,7 @@ func TestProgressGame_LogErrorFromAct(t *testing.T) { msgFilter = testlog.NewMessageFilter("Game info") msg := handler.FindLog(levelFilter, msgFilter) require.NotNil(t, msg) - require.Equal(t, uint64(1), msg.AttrValue("claims")) + require.Equal(t, "statusValue", msg.AttrValue("extra")) } func TestProgressGame_LogGameStatus(t *testing.T) { @@ -93,7 +93,7 @@ func TestDoNotActOnCompleteGame(t *testing.T) { // Should have replaced the act function with a noop so callCount doesn't update even when called directly // This allows the agent resources to be GC'd - require.NoError(t, game.act(context.Background())) + require.NoError(t, game.actor.Act(context.Background())) require.Equal(t, 1, gameState.callCount) }) } @@ -113,27 +113,27 @@ func TestValidateLocalNodeSync(t *testing.T) { func TestValidatePrestate(t *testing.T) { tests := []struct { name string - validators []Validator + validators []PrestateValidator errors bool }{ { name: "SingleValidator", - validators: []Validator{&mockValidator{}}, + validators: []PrestateValidator{&mockValidator{}}, errors: false, }, { name: "MultipleValidators", - validators: []Validator{&mockValidator{}, &mockValidator{}}, + validators: []PrestateValidator{&mockValidator{}, &mockValidator{}}, errors: false, }, { name: "SingleValidator_Errors", - validators: []Validator{&mockValidator{true}}, + validators: []PrestateValidator{&mockValidator{true}}, errors: true, }, { name: "MultipleValidators_Errors", - validators: []Validator{&mockValidator{}, &mockValidator{true}}, + validators: []PrestateValidator{&mockValidator{}, &mockValidator{true}}, errors: true, }, } @@ -153,7 +153,7 @@ func TestValidatePrestate(t *testing.T) { } } -var _ Validator = (*mockValidator)(nil) +var _ PrestateValidator = (*mockValidator)(nil) type mockValidator struct { err bool @@ -171,7 +171,7 @@ func setupProgressGameTest(t *testing.T) (*testlog.CapturingHandler, *GamePlayer gameState := &stubGameState{claimCount: 1} syncValidator := &stubSyncValidator{} game := &GamePlayer{ - act: gameState.Act, + actor: gameState, loader: gameState, logger: logger, syncValidator: syncValidator, @@ -199,19 +199,27 @@ type stubGameState struct { Err error } -func (s *stubGameState) Act(ctx context.Context) error { +func (s *stubGameState) AdditionalStatus(_ context.Context) ([]any, error) { + return []any{"extra", "statusValue"}, nil +} + +func (s *stubGameState) Act(_ context.Context) error { s.callCount++ return s.actErr } -func (s *stubGameState) GetStatus(ctx context.Context) (types.GameStatus, error) { +func (s *stubGameState) GetL1Head(_ context.Context) (common.Hash, error) { + return common.Hash{0x1a}, nil +} + +func (s *stubGameState) GetStatus(_ context.Context) (types.GameStatus, error) { return s.status, nil } -func (s *stubGameState) GetClaimCount(ctx context.Context) (uint64, error) { +func (s *stubGameState) GetClaimCount(_ context.Context) (uint64, error) { return s.claimCount, nil } -func (s *stubGameState) GetAbsolutePrestateHash(ctx context.Context) (common.Hash, error) { +func (s *stubGameState) GetAbsolutePrestateHash(_ context.Context) (common.Hash, error) { return common.Hash{}, s.Err }