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 (
cannonL2 = "http://example.com:9545"
alphabetTrace = "abcdefghijz"
agreeWithProposedOutput = "true"
gameDepth = "4"
)
func TestLogLevel(t *testing.T) {
......@@ -45,14 +44,14 @@ func TestLogLevel(t *testing.T) {
func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) {
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
defaultCfg.AlphabetTrace = alphabetTrace
require.Equal(t, defaultCfg, cfg)
}
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
// To avoid needing to specify unused options, these aren't included in the params for NewConfig
cfg.AlphabetTrace = alphabetTrace
......@@ -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) {
t.Run("NotRequiredForAlphabetTrace", func(t *testing.T) {
configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--cannon-bin"))
......@@ -327,7 +314,6 @@ func addRequiredArgsExcept(traceType config.TraceType, name string, optionalArgs
func requiredArgs(traceType config.TraceType) map[string]string {
args := map[string]string{
"--game-depth": gameDepth,
"--agree-with-proposed-output": agreeWithProposedOutput,
"--l1-eth-rpc": l1EthRpc,
"--game-address": gameAddressValue,
......
......@@ -67,7 +67,6 @@ type Config struct {
L1EthRpc string // L1 RPC Url
GameAddress common.Address // Address of the fault game
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
......@@ -93,14 +92,12 @@ func NewConfig(
gameAddress common.Address,
traceType TraceType,
agreeWithProposedOutput bool,
gameDepth int,
) Config {
return Config{
L1EthRpc: l1EthRpc,
GameAddress: gameAddress,
AgreeWithProposedOutput: agreeWithProposedOutput,
GameDepth: gameDepth,
TraceType: traceType,
......
......@@ -19,11 +19,10 @@ var (
validCannonDatadir = "/tmp/cannon"
validCannonL2 = "http://localhost:9545"
agreeWithProposedOutput = true
gameDepth = 4
)
func validConfig(traceType TraceType) Config {
cfg := NewConfig(validL1EthRpc, validGameAddress, traceType, agreeWithProposedOutput, gameDepth)
cfg := NewConfig(validL1EthRpc, validGameAddress, traceType, agreeWithProposedOutput)
switch traceType {
case TraceTypeAlphabet:
cfg.AlphabetTrace = validAlphabetTrace
......
......@@ -21,7 +21,7 @@ const execTestCannonPrestate = "/foo/pre.json"
func TestGenerateProof(t *testing.T) {
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.CannonAbsolutePreState = "pre.json"
cfg.CannonBin = "./bin/cannon"
......
......@@ -19,11 +19,13 @@ type ClaimFetcher interface {
Clock *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.
type Loader interface {
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.
......@@ -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.
func (l *loader) fetchClaim(ctx context.Context, arrIndex uint64) (types.Claim, error) {
callOpts := bind.CallOpts{
......
......@@ -12,15 +12,18 @@ import (
)
var (
mockClaimDataError = fmt.Errorf("claim data errored")
mockClaimLenError = fmt.Errorf("claim len errored")
mockClaimDataError = fmt.Errorf("claim data errored")
mockClaimLenError = fmt.Errorf("claim len errored")
mockMaxGameDepthError = fmt.Errorf("max game depth errored")
)
type mockClaimFetcher struct {
claimDataError bool
claimLenError bool
currentIndex uint64
returnClaims []struct {
claimDataError bool
claimLenError bool
maxGameDepthError bool
maxGameDepth uint64
currentIndex uint64
returnClaims []struct {
ParentIndex uint32
Countered bool
Claim [32]byte
......@@ -88,6 +91,34 @@ func (m *mockClaimFetcher) ClaimDataLen(opts *bind.CallOpts) (*big.Int, error) {
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].
func TestLoader_FetchClaims_Succeeds(t *testing.T) {
mockClaimFetcher := newMockClaimFetcher()
......
......@@ -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)
}
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 updater types.OracleUpdater
switch cfg.TraceType {
......@@ -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)
}
case config.TraceTypeAlphabet:
trace = alphabet.NewTraceProvider(cfg.AlphabetTrace, uint64(cfg.GameDepth))
trace = alphabet.NewTraceProvider(cfg.AlphabetTrace, gameDepth)
updater = alphabet.NewOracleUpdater(logger)
default:
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.
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) {
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)
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) {
gameLogger := logger.New("game", cfg.GameAddress)
responder, err := NewFaultResponder(gameLogger, txMgr, cfg.GameAddress)
if err != nil {
......@@ -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)
}
agent := NewAgent(loader, cfg.GameDepth, provider, responder, updater, cfg.AgreeWithProposedOutput, gameLogger)
agent := NewAgent(loader, int(gameDepth), provider, responder, updater, cfg.AgreeWithProposedOutput, gameLogger)
return &service{
agent: agent,
......
......@@ -48,11 +48,6 @@ var (
Usage: "Temporary hardcoded flag if we agree or disagree with the 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
AlphabetFlag = &cli.StringFlag{
Name: "alphabet",
......@@ -113,7 +108,6 @@ var requiredFlags = []cli.Flag{
DGFAddressFlag,
TraceTypeFlag,
AgreeWithProposedOutputFlag,
GameDepthFlag,
}
// optionalFlags is a list of unchecked cli flags
......@@ -212,7 +206,6 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) {
CannonL2: ctx.String(CannonL2Flag.Name),
CannonSnapshotFreq: ctx.Uint(CannonSnapshotFreqFlag.Name),
AgreeWithProposedOutput: ctx.Bool(AgreeWithProposedOutputFlag.Name),
GameDepth: ctx.Int(GameDepthFlag.Name),
TxMgrConfig: txMgrConfig,
}, nil
}
......@@ -24,5 +24,4 @@ $CHALLENGER_DIR/bin/op-challenger \
--game-address $FAULT_GAME_ADDRESS \
--private-key $CHARLIE_KEY \
--num-confirmations 1 \
--game-depth 4 \
--agree-with-proposed-output=true
......@@ -24,5 +24,4 @@ $CHALLENGER_DIR/bin/op-challenger \
--game-address $FAULT_GAME_ADDRESS \
--private-key $MALLORY_KEY \
--num-confirmations 1 \
--game-depth 4 \
--agree-with-proposed-output=false
......@@ -16,7 +16,6 @@ func (g *AlphabetGameHelper) StartChallenger(ctx context.Context, l1Endpoint str
opts := []challenger.Option{
func(c *config.Config) {
c.GameAddress = g.addr
c.GameDepth = alphabetGameDepth
c.TraceType = config.TraceTypeAlphabet
// By default the challenger agrees with the root claim (thus disagrees with the proposed output)
// This can be overridden by passing in options
......
......@@ -20,7 +20,6 @@ func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollu
opts := []challenger.Option{
func(c *config.Config) {
c.GameAddress = g.addr
c.GameDepth = cannonGameDepth
c.TraceType = config.TraceTypeCannon
c.AgreeWithProposedOutput = false
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