Commit 4c16e214 authored by Adrian Sutton's avatar Adrian Sutton

challenger: Move to using an interface for creating resources instead of individual functions.

Allows the specific game types to retain the contract wrapper with full type information instead of being limited to the generic interface.
parent 388a4174
...@@ -9,10 +9,8 @@ import ( ...@@ -9,10 +9,8 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/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-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -23,10 +21,6 @@ type GameInfo interface { ...@@ -23,10 +21,6 @@ type GameInfo interface {
GetClaimCount(context.Context) (uint64, error) GetClaimCount(context.Context) (uint64, error)
} }
// gameValidator checks that the specific game instance is compatible with the configuration.
// Typically, this is done by verifying the absolute prestate of the game matches the local absolute prestate.
type gameValidator func(ctx context.Context, gameContract GameContract) error
type GamePlayer struct { type GamePlayer struct {
act actor act actor
loader GameInfo loader GameInfo
...@@ -42,9 +36,12 @@ type GameContract interface { ...@@ -42,9 +36,12 @@ type GameContract interface {
GetMaxGameDepth(ctx context.Context) (uint64, error) GetMaxGameDepth(ctx context.Context) (uint64, error)
} }
type contractCreator func(addr common.Address, caller *batching.MultiCaller) (GameContract, error) type gameTypeResources interface {
Contract() GameContract
CreateAccessor(ctx context.Context, logger log.Logger, gameDepth uint64, dir string) (types.TraceAccessor, error)
}
type resourceCreator func(addr common.Address, contract GameContract, gameDepth uint64, dir string) (types.TraceAccessor, gameValidator, error) type resourceCreator func(addr common.Address) (gameTypeResources, error)
func NewGamePlayer( func NewGamePlayer(
ctx context.Context, ctx context.Context,
...@@ -53,17 +50,17 @@ func NewGamePlayer( ...@@ -53,17 +50,17 @@ func NewGamePlayer(
dir string, dir string,
addr common.Address, addr common.Address,
txMgr txmgr.TxManager, txMgr txmgr.TxManager,
client *ethclient.Client,
contractCreator contractCreator,
creator resourceCreator, creator resourceCreator,
) (*GamePlayer, error) { ) (*GamePlayer, error) {
logger = logger.New("game", addr) logger = logger.New("game", addr)
loader, err := contractCreator(addr, batching.NewMultiCaller(client.Client(), batching.DefaultBatchSize)) resources, err := creator(addr)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create fault dispute game contract wrapper: %w", err) return nil, fmt.Errorf("failed to create game resources: %w", err)
} }
loader := resources.Contract()
status, err := loader.GetStatus(ctx) status, err := loader.GetStatus(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to fetch game status: %w", err) return nil, fmt.Errorf("failed to fetch game status: %w", err)
...@@ -87,15 +84,11 @@ func NewGamePlayer( ...@@ -87,15 +84,11 @@ func NewGamePlayer(
return nil, fmt.Errorf("failed to fetch the game depth: %w", err) return nil, fmt.Errorf("failed to fetch the game depth: %w", err)
} }
accessor, validator, err := creator(addr, loader, gameDepth, dir) accessor, err := resources.CreateAccessor(ctx, logger, gameDepth, dir)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create trace accessor: %w", err) return nil, fmt.Errorf("failed to create trace accessor: %w", err)
} }
if err := validator(ctx, loader); err != nil {
return nil, fmt.Errorf("failed to validate absolute prestate: %w", err)
}
responder, err := responder.NewFaultResponder(logger, txMgr, loader) responder, err := responder.NewFaultResponder(logger, txMgr, loader)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create the responder: %w", err) return nil, fmt.Errorf("failed to create the responder: %w", err)
......
...@@ -40,7 +40,7 @@ func RegisterGameTypes( ...@@ -40,7 +40,7 @@ func RegisterGameTypes(
m metrics.Metricer, m metrics.Metricer,
cfg *config.Config, cfg *config.Config,
txMgr txmgr.TxManager, txMgr txmgr.TxManager,
client *ethclient.Client, caller *batching.MultiCaller,
) (CloseFunc, error) { ) (CloseFunc, error) {
var closer CloseFunc var closer CloseFunc
var l2Client *ethclient.Client var l2Client *ethclient.Client
...@@ -53,13 +53,13 @@ func RegisterGameTypes( ...@@ -53,13 +53,13 @@ func RegisterGameTypes(
closer = l2Client.Close closer = l2Client.Close
} }
if cfg.TraceTypeEnabled(config.TraceTypeOutputCannon) { if cfg.TraceTypeEnabled(config.TraceTypeOutputCannon) {
registerOutputCannon(registry, ctx, logger, m, cfg, txMgr, client, l2Client) registerOutputCannon(registry, ctx, logger, m, cfg, txMgr, caller, l2Client)
} }
if cfg.TraceTypeEnabled(config.TraceTypeCannon) { if cfg.TraceTypeEnabled(config.TraceTypeCannon) {
registerCannon(registry, ctx, logger, m, cfg, txMgr, client, l2Client) registerCannon(registry, ctx, logger, m, cfg, txMgr, caller, l2Client)
} }
if cfg.TraceTypeEnabled(config.TraceTypeAlphabet) { if cfg.TraceTypeEnabled(config.TraceTypeAlphabet) {
registerAlphabet(registry, ctx, logger, m, cfg, txMgr, client) registerAlphabet(registry, ctx, logger, m, cfg, txMgr, caller)
} }
return closer, nil return closer, nil
} }
...@@ -71,36 +71,53 @@ func registerOutputCannon( ...@@ -71,36 +71,53 @@ func registerOutputCannon(
m metrics.Metricer, m metrics.Metricer,
cfg *config.Config, cfg *config.Config,
txMgr txmgr.TxManager, txMgr txmgr.TxManager,
client *ethclient.Client, caller *batching.MultiCaller,
l2Client cannon.L2HeaderSource) { l2Client cannon.L2HeaderSource) {
// Currently still using the old fault dispute game contracts for output_cannon resourceCreator := func(addr common.Address) (gameTypeResources, error) {
contractCreator := func(addr common.Address, caller *batching.MultiCaller) (GameContract, error) { // Currently still using the old fault dispute game contracts for output_cannon
return contracts.NewFaultDisputeGameContract(addr, caller) // as the output bisection+cannon contract isn't being deployed.
} contract, err := contracts.NewFaultDisputeGameContract(addr, caller)
resourceCreator := func(addr common.Address, genericContract GameContract, gameDepth uint64, dir string) (faultTypes.TraceAccessor, gameValidator, error) {
logger := logger.New("game", addr)
contract := genericContract.(*contracts.FaultDisputeGameContract)
// TODO(client-pod#43): Updated contracts should expose this as the pre and post state blocks
agreed, disputed, err := contract.GetProposals(ctx)
if err != nil {
return nil, nil, err
}
accessor, err := outputs.NewOutputCannonTraceAccessor(ctx, logger, m, cfg, l2Client, contract, dir, gameDepth, agreed.L2BlockNumber.Uint64(), disputed.L2BlockNumber.Uint64())
if err != nil { if err != nil {
return nil, nil, err return nil, err
}
// TODO(client-pod#44): Validate absolute pre-state for split games
noopValidator := func(ctx context.Context, gameContract GameContract) error {
return nil
} }
return accessor, noopValidator, nil return &outputCannonResources{
m: m,
cfg: cfg,
l2Client: l2Client,
contract: contract,
}, nil
} }
playerCreator := func(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) { playerCreator := func(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) {
return NewGamePlayer(ctx, logger, m, dir, game.Proxy, txMgr, client, contractCreator, resourceCreator) return NewGamePlayer(ctx, logger, m, dir, game.Proxy, txMgr, resourceCreator)
} }
registry.RegisterGameType(outputCannonGameType, playerCreator) registry.RegisterGameType(outputCannonGameType, playerCreator)
} }
type outputCannonResources struct {
m metrics.Metricer
cfg *config.Config
l2Client cannon.L2HeaderSource
contract *contracts.FaultDisputeGameContract
}
func (r *outputCannonResources) Contract() GameContract {
return r.contract
}
func (r *outputCannonResources) CreateAccessor(ctx context.Context, logger log.Logger, gameDepth uint64, dir string) (faultTypes.TraceAccessor, error) {
// TODO(client-pod#44): Validate absolute pre-state for split games
// TODO(client-pod#43): Updated contracts should expose this as the pre and post state blocks
agreed, disputed, err := r.contract.GetProposals(ctx)
if err != nil {
return nil, err
}
accessor, err := outputs.NewOutputCannonTraceAccessor(ctx, logger, r.m, r.cfg, r.l2Client, r.contract, dir, gameDepth, agreed.L2BlockNumber.Uint64(), disputed.L2BlockNumber.Uint64())
if err != nil {
return nil, err
}
return accessor, nil
}
func registerCannon( func registerCannon(
registry Registry, registry Registry,
ctx context.Context, ctx context.Context,
...@@ -108,30 +125,49 @@ func registerCannon( ...@@ -108,30 +125,49 @@ func registerCannon(
m metrics.Metricer, m metrics.Metricer,
cfg *config.Config, cfg *config.Config,
txMgr txmgr.TxManager, txMgr txmgr.TxManager,
client *ethclient.Client, caller *batching.MultiCaller,
l2Client cannon.L2HeaderSource) { l2Client cannon.L2HeaderSource) {
contractCreator := func(addr common.Address, caller *batching.MultiCaller) (GameContract, error) { resourceCreator := func(addr common.Address) (gameTypeResources, error) {
return contracts.NewFaultDisputeGameContract(addr, caller) contract, err := contracts.NewFaultDisputeGameContract(addr, caller)
}
resourceCreator := func(addr common.Address, genericContract GameContract, gameDepth uint64, dir string) (faultTypes.TraceAccessor, gameValidator, error) {
logger := logger.New("game", addr)
contract := genericContract.(*contracts.FaultDisputeGameContract)
localInputs, err := cannon.FetchLocalInputs(ctx, contract, l2Client)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to fetch cannon local inputs: %w", err) return nil, err
}
provider := cannon.NewTraceProvider(logger, m, cfg, faultTypes.NoLocalContext, localInputs, dir, gameDepth)
validator := func(ctx context.Context, contract GameContract) error {
return ValidateAbsolutePrestate(ctx, provider, contract.(*contracts.FaultDisputeGameContract))
} }
return trace.NewSimpleTraceAccessor(provider), validator, nil return &cannonResources{
m: m,
cfg: cfg,
l2Client: l2Client,
contract: contract,
}, nil
} }
playerCreator := func(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) { playerCreator := func(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) {
return NewGamePlayer(ctx, logger, m, dir, game.Proxy, txMgr, client, contractCreator, resourceCreator) return NewGamePlayer(ctx, logger, m, dir, game.Proxy, txMgr, resourceCreator)
} }
registry.RegisterGameType(cannonGameType, playerCreator) registry.RegisterGameType(cannonGameType, playerCreator)
} }
type cannonResources struct {
m metrics.Metricer
cfg *config.Config
l2Client cannon.L2HeaderSource
contract *contracts.FaultDisputeGameContract
}
func (r *cannonResources) Contract() GameContract {
return r.contract
}
func (r *cannonResources) CreateAccessor(ctx context.Context, logger log.Logger, gameDepth uint64, dir string) (faultTypes.TraceAccessor, error) {
localInputs, err := cannon.FetchLocalInputs(ctx, r.contract, r.l2Client)
if err != nil {
return nil, fmt.Errorf("failed to fetch cannon local inputs: %w", err)
}
provider := cannon.NewTraceProvider(logger, r.m, r.cfg, faultTypes.NoLocalContext, localInputs, dir, gameDepth)
if err := ValidateAbsolutePrestate(ctx, provider, r.contract); err != nil {
return nil, err
}
return trace.NewSimpleTraceAccessor(provider), nil
}
func registerAlphabet( func registerAlphabet(
registry Registry, registry Registry,
ctx context.Context, ctx context.Context,
...@@ -139,19 +175,36 @@ func registerAlphabet( ...@@ -139,19 +175,36 @@ func registerAlphabet(
m metrics.Metricer, m metrics.Metricer,
cfg *config.Config, cfg *config.Config,
txMgr txmgr.TxManager, txMgr txmgr.TxManager,
client *ethclient.Client) { caller *batching.MultiCaller) {
contractCreator := func(addr common.Address, caller *batching.MultiCaller) (GameContract, error) { resourceCreator := func(addr common.Address) (gameTypeResources, error) {
return contracts.NewFaultDisputeGameContract(addr, caller) contract, err := contracts.NewFaultDisputeGameContract(addr, caller)
} if err != nil {
resourceCreator := func(addr common.Address, contract GameContract, gameDepth uint64, dir string) (faultTypes.TraceAccessor, gameValidator, error) { return nil, err
provider := alphabet.NewTraceProvider(cfg.AlphabetTrace, gameDepth)
validator := func(ctx context.Context, contract GameContract) error {
return ValidateAbsolutePrestate(ctx, provider, contract.(*contracts.FaultDisputeGameContract))
} }
return trace.NewSimpleTraceAccessor(provider), validator, nil return &alphabetResources{
cfg: cfg,
contract: contract,
}, nil
} }
playerCreator := func(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) { playerCreator := func(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) {
return NewGamePlayer(ctx, logger, m, dir, game.Proxy, txMgr, client, contractCreator, resourceCreator) return NewGamePlayer(ctx, logger, m, dir, game.Proxy, txMgr, resourceCreator)
} }
registry.RegisterGameType(alphabetGameType, playerCreator) registry.RegisterGameType(alphabetGameType, playerCreator)
} }
type alphabetResources struct {
cfg *config.Config
contract *contracts.FaultDisputeGameContract
}
func (r *alphabetResources) Contract() GameContract {
return r.contract
}
func (r *alphabetResources) CreateAccessor(ctx context.Context, _ log.Logger, gameDepth uint64, _ string) (faultTypes.TraceAccessor, error) {
provider := alphabet.NewTraceProvider(r.cfg.AlphabetTrace, gameDepth)
if err := ValidateAbsolutePrestate(ctx, provider, r.contract); err != nil {
return nil, err
}
return trace.NewSimpleTraceAccessor(provider), nil
}
...@@ -168,7 +168,8 @@ func (s *Service) initGameLoader(cfg *config.Config) error { ...@@ -168,7 +168,8 @@ func (s *Service) initGameLoader(cfg *config.Config) error {
func (s *Service) initScheduler(ctx context.Context, cfg *config.Config) error { func (s *Service) initScheduler(ctx context.Context, cfg *config.Config) error {
gameTypeRegistry := registry.NewGameTypeRegistry() gameTypeRegistry := registry.NewGameTypeRegistry()
closer, err := fault.RegisterGameTypes(gameTypeRegistry, ctx, s.logger, s.metrics, cfg, s.txMgr, s.l1Client) caller := batching.NewMultiCaller(s.l1Client.Client(), batching.DefaultBatchSize)
closer, err := fault.RegisterGameTypes(gameTypeRegistry, ctx, s.logger, s.metrics, cfg, s.txMgr, caller)
if err != nil { if err != nil {
return err return err
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment