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 (
"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/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
)
......@@ -23,10 +21,6 @@ type GameInfo interface {
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 {
act actor
loader GameInfo
......@@ -42,9 +36,12 @@ type GameContract interface {
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(
ctx context.Context,
......@@ -53,17 +50,17 @@ func NewGamePlayer(
dir string,
addr common.Address,
txMgr txmgr.TxManager,
client *ethclient.Client,
contractCreator contractCreator,
creator resourceCreator,
) (*GamePlayer, error) {
logger = logger.New("game", addr)
loader, err := contractCreator(addr, batching.NewMultiCaller(client.Client(), batching.DefaultBatchSize))
resources, err := creator(addr)
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)
if err != nil {
return nil, fmt.Errorf("failed to fetch game status: %w", err)
......@@ -87,15 +84,11 @@ func NewGamePlayer(
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 {
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)
if err != nil {
return nil, fmt.Errorf("failed to create the responder: %w", err)
......
......@@ -40,7 +40,7 @@ func RegisterGameTypes(
m metrics.Metricer,
cfg *config.Config,
txMgr txmgr.TxManager,
client *ethclient.Client,
caller *batching.MultiCaller,
) (CloseFunc, error) {
var closer CloseFunc
var l2Client *ethclient.Client
......@@ -53,13 +53,13 @@ func RegisterGameTypes(
closer = l2Client.Close
}
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) {
registerCannon(registry, ctx, logger, m, cfg, txMgr, client, l2Client)
registerCannon(registry, ctx, logger, m, cfg, txMgr, caller, l2Client)
}
if cfg.TraceTypeEnabled(config.TraceTypeAlphabet) {
registerAlphabet(registry, ctx, logger, m, cfg, txMgr, client)
registerAlphabet(registry, ctx, logger, m, cfg, txMgr, caller)
}
return closer, nil
}
......@@ -71,36 +71,53 @@ func registerOutputCannon(
m metrics.Metricer,
cfg *config.Config,
txMgr txmgr.TxManager,
client *ethclient.Client,
caller *batching.MultiCaller,
l2Client cannon.L2HeaderSource) {
// Currently still using the old fault dispute game contracts for output_cannon
contractCreator := func(addr common.Address, caller *batching.MultiCaller) (GameContract, error) {
return 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())
resourceCreator := func(addr common.Address) (gameTypeResources, error) {
// Currently still using the old fault dispute game contracts for output_cannon
// as the output bisection+cannon contract isn't being deployed.
contract, err := contracts.NewFaultDisputeGameContract(addr, caller)
if err != nil {
return nil, nil, err
}
// TODO(client-pod#44): Validate absolute pre-state for split games
noopValidator := func(ctx context.Context, gameContract GameContract) error {
return nil
return nil, err
}
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) {
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)
}
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(
registry Registry,
ctx context.Context,
......@@ -108,30 +125,49 @@ func registerCannon(
m metrics.Metricer,
cfg *config.Config,
txMgr txmgr.TxManager,
client *ethclient.Client,
caller *batching.MultiCaller,
l2Client cannon.L2HeaderSource) {
contractCreator := func(addr common.Address, caller *batching.MultiCaller) (GameContract, error) {
return 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)
resourceCreator := func(addr common.Address) (gameTypeResources, error) {
contract, err := contracts.NewFaultDisputeGameContract(addr, caller)
if err != nil {
return nil, nil, fmt.Errorf("failed to fetch cannon local inputs: %w", 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 nil, err
}
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) {
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)
}
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(
registry Registry,
ctx context.Context,
......@@ -139,19 +175,36 @@ func registerAlphabet(
m metrics.Metricer,
cfg *config.Config,
txMgr txmgr.TxManager,
client *ethclient.Client) {
contractCreator := func(addr common.Address, caller *batching.MultiCaller) (GameContract, error) {
return contracts.NewFaultDisputeGameContract(addr, caller)
}
resourceCreator := func(addr common.Address, contract GameContract, gameDepth uint64, dir string) (faultTypes.TraceAccessor, gameValidator, error) {
provider := alphabet.NewTraceProvider(cfg.AlphabetTrace, gameDepth)
validator := func(ctx context.Context, contract GameContract) error {
return ValidateAbsolutePrestate(ctx, provider, contract.(*contracts.FaultDisputeGameContract))
caller *batching.MultiCaller) {
resourceCreator := func(addr common.Address) (gameTypeResources, error) {
contract, err := contracts.NewFaultDisputeGameContract(addr, caller)
if err != nil {
return nil, err
}
return trace.NewSimpleTraceAccessor(provider), validator, nil
return &alphabetResources{
cfg: cfg,
contract: contract,
}, nil
}
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)
}
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 {
func (s *Service) initScheduler(ctx context.Context, cfg *config.Config) error {
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 {
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