Commit fd3a6dc1 authored by OptimismBot's avatar OptimismBot Committed by GitHub

Merge pull request #6678 from ethereum-optimism/refcell/load-game-depth

feat(op-challenger): Load Onchain Fault Dispute Game Contract Game Depth
parents 9daa786e 7c5cef8b
...@@ -25,7 +25,6 @@ var ( ...@@ -25,7 +25,6 @@ var (
cannonL2 = "http://example.com:9545" cannonL2 = "http://example.com:9545"
alphabetTrace = "abcdefghijz" alphabetTrace = "abcdefghijz"
agreeWithProposedOutput = "true" agreeWithProposedOutput = "true"
gameDepth = "4"
) )
func TestLogLevel(t *testing.T) { func TestLogLevel(t *testing.T) {
...@@ -45,14 +44,14 @@ func TestLogLevel(t *testing.T) { ...@@ -45,14 +44,14 @@ func TestLogLevel(t *testing.T) {
func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) { func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs(config.TraceTypeAlphabet)) cfg := configForArgs(t, addRequiredArgs(config.TraceTypeAlphabet))
defaultCfg := config.NewConfig(l1EthRpc, common.HexToAddress(gameAddressValue), config.TraceTypeAlphabet, true, 4) defaultCfg := config.NewConfig(l1EthRpc, common.HexToAddress(gameAddressValue), config.TraceTypeAlphabet, true)
// Add in the extra CLI options required when using alphabet trace type // Add in the extra CLI options required when using alphabet trace type
defaultCfg.AlphabetTrace = alphabetTrace defaultCfg.AlphabetTrace = alphabetTrace
require.Equal(t, defaultCfg, cfg) require.Equal(t, defaultCfg, cfg)
} }
func TestDefaultConfigIsValid(t *testing.T) { func TestDefaultConfigIsValid(t *testing.T) {
cfg := config.NewConfig(l1EthRpc, common.HexToAddress(gameAddressValue), config.TraceTypeAlphabet, true, 4) cfg := config.NewConfig(l1EthRpc, common.HexToAddress(gameAddressValue), config.TraceTypeAlphabet, true)
// Add in options that are required based on the specific trace type // Add in options that are required based on the specific trace type
// To avoid needing to specify unused options, these aren't included in the params for NewConfig // To avoid needing to specify unused options, these aren't included in the params for NewConfig
cfg.AlphabetTrace = alphabetTrace cfg.AlphabetTrace = alphabetTrace
...@@ -130,18 +129,6 @@ func TestAgreeWithProposedOutput(t *testing.T) { ...@@ -130,18 +129,6 @@ func TestAgreeWithProposedOutput(t *testing.T) {
}) })
} }
func TestGameDepth(t *testing.T) {
t.Run("Required", func(t *testing.T) {
verifyArgsInvalid(t, "flag game-depth is required", addRequiredArgsExcept(config.TraceTypeAlphabet, "--game-depth"))
})
t.Run("Valid", func(t *testing.T) {
value := "4"
cfg := configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--game-depth", "--game-depth="+value))
require.Equal(t, value, fmt.Sprint(cfg.GameDepth))
})
}
func TestCannonBin(t *testing.T) { func TestCannonBin(t *testing.T) {
t.Run("NotRequiredForAlphabetTrace", func(t *testing.T) { t.Run("NotRequiredForAlphabetTrace", func(t *testing.T) {
configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--cannon-bin")) configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--cannon-bin"))
...@@ -327,7 +314,6 @@ func addRequiredArgsExcept(traceType config.TraceType, name string, optionalArgs ...@@ -327,7 +314,6 @@ func addRequiredArgsExcept(traceType config.TraceType, name string, optionalArgs
func requiredArgs(traceType config.TraceType) map[string]string { func requiredArgs(traceType config.TraceType) map[string]string {
args := map[string]string{ args := map[string]string{
"--game-depth": gameDepth,
"--agree-with-proposed-output": agreeWithProposedOutput, "--agree-with-proposed-output": agreeWithProposedOutput,
"--l1-eth-rpc": l1EthRpc, "--l1-eth-rpc": l1EthRpc,
"--game-address": gameAddressValue, "--game-address": gameAddressValue,
......
...@@ -67,7 +67,6 @@ type Config struct { ...@@ -67,7 +67,6 @@ type Config struct {
L1EthRpc string // L1 RPC Url L1EthRpc string // L1 RPC Url
GameAddress common.Address // Address of the fault game GameAddress common.Address // Address of the fault game
AgreeWithProposedOutput bool // Temporary config if we agree or disagree with the posted output AgreeWithProposedOutput bool // Temporary config if we agree or disagree with the posted output
GameDepth int // Depth of the game tree
TraceType TraceType // Type of trace TraceType TraceType // Type of trace
...@@ -93,14 +92,12 @@ func NewConfig( ...@@ -93,14 +92,12 @@ func NewConfig(
gameAddress common.Address, gameAddress common.Address,
traceType TraceType, traceType TraceType,
agreeWithProposedOutput bool, agreeWithProposedOutput bool,
gameDepth int,
) Config { ) Config {
return Config{ return Config{
L1EthRpc: l1EthRpc, L1EthRpc: l1EthRpc,
GameAddress: gameAddress, GameAddress: gameAddress,
AgreeWithProposedOutput: agreeWithProposedOutput, AgreeWithProposedOutput: agreeWithProposedOutput,
GameDepth: gameDepth,
TraceType: traceType, TraceType: traceType,
......
...@@ -19,11 +19,10 @@ var ( ...@@ -19,11 +19,10 @@ var (
validCannonDatadir = "/tmp/cannon" validCannonDatadir = "/tmp/cannon"
validCannonL2 = "http://localhost:9545" validCannonL2 = "http://localhost:9545"
agreeWithProposedOutput = true agreeWithProposedOutput = true
gameDepth = 4
) )
func validConfig(traceType TraceType) Config { func validConfig(traceType TraceType) Config {
cfg := NewConfig(validL1EthRpc, validGameAddress, traceType, agreeWithProposedOutput, gameDepth) cfg := NewConfig(validL1EthRpc, validGameAddress, traceType, agreeWithProposedOutput)
switch traceType { switch traceType {
case TraceTypeAlphabet: case TraceTypeAlphabet:
cfg.AlphabetTrace = validAlphabetTrace cfg.AlphabetTrace = validAlphabetTrace
......
...@@ -21,7 +21,7 @@ const execTestCannonPrestate = "/foo/pre.json" ...@@ -21,7 +21,7 @@ const execTestCannonPrestate = "/foo/pre.json"
func TestGenerateProof(t *testing.T) { func TestGenerateProof(t *testing.T) {
input := "starting.json" input := "starting.json"
cfg := config.NewConfig("http://localhost:8888", common.Address{0xaa}, config.TraceTypeCannon, true, 5) cfg := config.NewConfig("http://localhost:8888", common.Address{0xaa}, config.TraceTypeCannon, true)
cfg.CannonDatadir = t.TempDir() cfg.CannonDatadir = t.TempDir()
cfg.CannonAbsolutePreState = "pre.json" cfg.CannonAbsolutePreState = "pre.json"
cfg.CannonBin = "./bin/cannon" cfg.CannonBin = "./bin/cannon"
......
...@@ -19,11 +19,13 @@ type ClaimFetcher interface { ...@@ -19,11 +19,13 @@ type ClaimFetcher interface {
Clock *big.Int Clock *big.Int
}, error) }, error)
ClaimDataLen(opts *bind.CallOpts) (*big.Int, error) ClaimDataLen(opts *bind.CallOpts) (*big.Int, error)
MAXGAMEDEPTH(opts *bind.CallOpts) (*big.Int, error)
} }
// Loader is a minimal interface for loading onchain [Claim] data. // Loader is a minimal interface for loading onchain [Claim] data.
type Loader interface { type Loader interface {
FetchClaims(ctx context.Context) ([]types.Claim, error) FetchClaims(ctx context.Context) ([]types.Claim, error)
FetchGameDepth(ctx context.Context) (uint64, error)
} }
// loader pulls in fault dispute game claim data periodically and over subscriptions. // loader pulls in fault dispute game claim data periodically and over subscriptions.
...@@ -38,6 +40,20 @@ func NewLoader(claimFetcher ClaimFetcher) *loader { ...@@ -38,6 +40,20 @@ func NewLoader(claimFetcher ClaimFetcher) *loader {
} }
} }
// FetchGameDepth fetches the game depth from the fault dispute game.
func (l *loader) FetchGameDepth(ctx context.Context) (uint64, error) {
callOpts := bind.CallOpts{
Context: ctx,
}
gameDepth, err := l.claimFetcher.MAXGAMEDEPTH(&callOpts)
if err != nil {
return 0, err
}
return gameDepth.Uint64(), nil
}
// fetchClaim fetches a single [Claim] with a hydrated parent. // fetchClaim fetches a single [Claim] with a hydrated parent.
func (l *loader) fetchClaim(ctx context.Context, arrIndex uint64) (types.Claim, error) { func (l *loader) fetchClaim(ctx context.Context, arrIndex uint64) (types.Claim, error) {
callOpts := bind.CallOpts{ callOpts := bind.CallOpts{
......
...@@ -12,15 +12,18 @@ import ( ...@@ -12,15 +12,18 @@ import (
) )
var ( var (
mockClaimDataError = fmt.Errorf("claim data errored") mockClaimDataError = fmt.Errorf("claim data errored")
mockClaimLenError = fmt.Errorf("claim len errored") mockClaimLenError = fmt.Errorf("claim len errored")
mockMaxGameDepthError = fmt.Errorf("max game depth errored")
) )
type mockClaimFetcher struct { type mockClaimFetcher struct {
claimDataError bool claimDataError bool
claimLenError bool claimLenError bool
currentIndex uint64 maxGameDepthError bool
returnClaims []struct { maxGameDepth uint64
currentIndex uint64
returnClaims []struct {
ParentIndex uint32 ParentIndex uint32
Countered bool Countered bool
Claim [32]byte Claim [32]byte
...@@ -88,6 +91,34 @@ func (m *mockClaimFetcher) ClaimDataLen(opts *bind.CallOpts) (*big.Int, error) { ...@@ -88,6 +91,34 @@ func (m *mockClaimFetcher) ClaimDataLen(opts *bind.CallOpts) (*big.Int, error) {
return big.NewInt(int64(len(m.returnClaims))), nil return big.NewInt(int64(len(m.returnClaims))), nil
} }
func (m *mockClaimFetcher) MAXGAMEDEPTH(opts *bind.CallOpts) (*big.Int, error) {
if m.maxGameDepthError {
return nil, mockMaxGameDepthError
}
return big.NewInt(int64(m.maxGameDepth)), nil
}
// TestLoader_FetchGameDepth tests [loader.FetchGameDepth].
func TestLoader_FetchGameDepth(t *testing.T) {
t.Run("Succeeds", func(t *testing.T) {
mockClaimFetcher := newMockClaimFetcher()
mockClaimFetcher.maxGameDepth = 10
loader := NewLoader(mockClaimFetcher)
depth, err := loader.FetchGameDepth(context.Background())
require.NoError(t, err)
require.Equal(t, uint64(10), depth)
})
t.Run("Errors", func(t *testing.T) {
mockClaimFetcher := newMockClaimFetcher()
mockClaimFetcher.maxGameDepthError = true
loader := NewLoader(mockClaimFetcher)
depth, err := loader.FetchGameDepth(context.Background())
require.ErrorIs(t, mockMaxGameDepthError, err)
require.Equal(t, depth, uint64(0))
})
}
// TestLoader_FetchClaims_Succeeds tests [loader.FetchClaims]. // TestLoader_FetchClaims_Succeeds tests [loader.FetchClaims].
func TestLoader_FetchClaims_Succeeds(t *testing.T) { func TestLoader_FetchClaims_Succeeds(t *testing.T) {
mockClaimFetcher := newMockClaimFetcher() mockClaimFetcher := newMockClaimFetcher()
......
...@@ -41,6 +41,19 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se ...@@ -41,6 +41,19 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se
return nil, fmt.Errorf("failed to dial L1: %w", err) return nil, fmt.Errorf("failed to dial L1: %w", err)
} }
contract, err := bindings.NewFaultDisputeGameCaller(cfg.GameAddress, client)
if err != nil {
return nil, fmt.Errorf("failed to bind the fault dispute game contract: %w", err)
}
loader := NewLoader(contract)
gameDepth, err := loader.FetchGameDepth(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch the game depth: %w", err)
}
gameDepth = uint64(gameDepth)
var trace types.TraceProvider var trace types.TraceProvider
var updater types.OracleUpdater var updater types.OracleUpdater
switch cfg.TraceType { switch cfg.TraceType {
...@@ -54,23 +67,17 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se ...@@ -54,23 +67,17 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se
return nil, fmt.Errorf("failed to create the cannon updater: %w", err) return nil, fmt.Errorf("failed to create the cannon updater: %w", err)
} }
case config.TraceTypeAlphabet: case config.TraceTypeAlphabet:
trace = alphabet.NewTraceProvider(cfg.AlphabetTrace, uint64(cfg.GameDepth)) trace = alphabet.NewTraceProvider(cfg.AlphabetTrace, gameDepth)
updater = alphabet.NewOracleUpdater(logger) updater = alphabet.NewOracleUpdater(logger)
default: default:
return nil, fmt.Errorf("unsupported trace type: %v", cfg.TraceType) return nil, fmt.Errorf("unsupported trace type: %v", cfg.TraceType)
} }
return newTypedService(ctx, logger, cfg, client, trace, updater, txMgr) return newTypedService(ctx, logger, cfg, loader, gameDepth, client, trace, updater, txMgr)
} }
// newTypedService creates a new Service from a provided trace provider. // newTypedService creates a new Service from a provided trace provider.
func newTypedService(ctx context.Context, logger log.Logger, cfg *config.Config, client *ethclient.Client, provider types.TraceProvider, updater types.OracleUpdater, txMgr txmgr.TxManager) (*service, error) { func newTypedService(ctx context.Context, logger log.Logger, cfg *config.Config, loader Loader, gameDepth uint64, client *ethclient.Client, provider types.TraceProvider, updater types.OracleUpdater, txMgr txmgr.TxManager) (*service, error) {
contract, err := bindings.NewFaultDisputeGameCaller(cfg.GameAddress, client)
if err != nil {
return nil, fmt.Errorf("failed to bind the fault dispute game contract: %w", err)
}
loader := NewLoader(contract)
gameLogger := logger.New("game", cfg.GameAddress) gameLogger := logger.New("game", cfg.GameAddress)
responder, err := NewFaultResponder(gameLogger, txMgr, cfg.GameAddress) responder, err := NewFaultResponder(gameLogger, txMgr, cfg.GameAddress)
if err != nil { if err != nil {
...@@ -82,7 +89,7 @@ func newTypedService(ctx context.Context, logger log.Logger, cfg *config.Config, ...@@ -82,7 +89,7 @@ func newTypedService(ctx context.Context, logger log.Logger, cfg *config.Config,
return nil, fmt.Errorf("failed to bind the fault contract: %w", err) return nil, fmt.Errorf("failed to bind the fault contract: %w", err)
} }
agent := NewAgent(loader, cfg.GameDepth, provider, responder, updater, cfg.AgreeWithProposedOutput, gameLogger) agent := NewAgent(loader, int(gameDepth), provider, responder, updater, cfg.AgreeWithProposedOutput, gameLogger)
return &service{ return &service{
agent: agent, agent: agent,
......
...@@ -48,11 +48,6 @@ var ( ...@@ -48,11 +48,6 @@ var (
Usage: "Temporary hardcoded flag if we agree or disagree with the proposed output.", Usage: "Temporary hardcoded flag if we agree or disagree with the proposed output.",
EnvVars: prefixEnvVars("AGREE_WITH_PROPOSED_OUTPUT"), EnvVars: prefixEnvVars("AGREE_WITH_PROPOSED_OUTPUT"),
} }
GameDepthFlag = &cli.IntFlag{
Name: "game-depth",
Usage: "Depth of the game tree.",
EnvVars: prefixEnvVars("GAME_DEPTH"),
}
// Optional Flags // Optional Flags
AlphabetFlag = &cli.StringFlag{ AlphabetFlag = &cli.StringFlag{
Name: "alphabet", Name: "alphabet",
...@@ -113,7 +108,6 @@ var requiredFlags = []cli.Flag{ ...@@ -113,7 +108,6 @@ var requiredFlags = []cli.Flag{
DGFAddressFlag, DGFAddressFlag,
TraceTypeFlag, TraceTypeFlag,
AgreeWithProposedOutputFlag, AgreeWithProposedOutputFlag,
GameDepthFlag,
} }
// optionalFlags is a list of unchecked cli flags // optionalFlags is a list of unchecked cli flags
...@@ -212,7 +206,6 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) { ...@@ -212,7 +206,6 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) {
CannonL2: ctx.String(CannonL2Flag.Name), CannonL2: ctx.String(CannonL2Flag.Name),
CannonSnapshotFreq: ctx.Uint(CannonSnapshotFreqFlag.Name), CannonSnapshotFreq: ctx.Uint(CannonSnapshotFreqFlag.Name),
AgreeWithProposedOutput: ctx.Bool(AgreeWithProposedOutputFlag.Name), AgreeWithProposedOutput: ctx.Bool(AgreeWithProposedOutputFlag.Name),
GameDepth: ctx.Int(GameDepthFlag.Name),
TxMgrConfig: txMgrConfig, TxMgrConfig: txMgrConfig,
}, nil }, nil
} }
...@@ -24,5 +24,4 @@ $CHALLENGER_DIR/bin/op-challenger \ ...@@ -24,5 +24,4 @@ $CHALLENGER_DIR/bin/op-challenger \
--game-address $FAULT_GAME_ADDRESS \ --game-address $FAULT_GAME_ADDRESS \
--private-key $CHARLIE_KEY \ --private-key $CHARLIE_KEY \
--num-confirmations 1 \ --num-confirmations 1 \
--game-depth 4 \
--agree-with-proposed-output=true --agree-with-proposed-output=true
...@@ -24,5 +24,4 @@ $CHALLENGER_DIR/bin/op-challenger \ ...@@ -24,5 +24,4 @@ $CHALLENGER_DIR/bin/op-challenger \
--game-address $FAULT_GAME_ADDRESS \ --game-address $FAULT_GAME_ADDRESS \
--private-key $MALLORY_KEY \ --private-key $MALLORY_KEY \
--num-confirmations 1 \ --num-confirmations 1 \
--game-depth 4 \
--agree-with-proposed-output=false --agree-with-proposed-output=false
...@@ -16,7 +16,6 @@ func (g *AlphabetGameHelper) StartChallenger(ctx context.Context, l1Endpoint str ...@@ -16,7 +16,6 @@ func (g *AlphabetGameHelper) StartChallenger(ctx context.Context, l1Endpoint str
opts := []challenger.Option{ opts := []challenger.Option{
func(c *config.Config) { func(c *config.Config) {
c.GameAddress = g.addr c.GameAddress = g.addr
c.GameDepth = alphabetGameDepth
c.TraceType = config.TraceTypeAlphabet c.TraceType = config.TraceTypeAlphabet
// By default the challenger agrees with the root claim (thus disagrees with the proposed output) // By default the challenger agrees with the root claim (thus disagrees with the proposed output)
// This can be overridden by passing in options // This can be overridden by passing in options
......
...@@ -20,7 +20,6 @@ func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollu ...@@ -20,7 +20,6 @@ func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollu
opts := []challenger.Option{ opts := []challenger.Option{
func(c *config.Config) { func(c *config.Config) {
c.GameAddress = g.addr c.GameAddress = g.addr
c.GameDepth = cannonGameDepth
c.TraceType = config.TraceTypeCannon c.TraceType = config.TraceTypeCannon
c.AgreeWithProposedOutput = false c.AgreeWithProposedOutput = false
c.CannonL2 = l2Endpoint c.CannonL2 = l2Endpoint
......
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