Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 23 additions & 33 deletions op-challenger/cmd/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,12 @@ func TestLogLevel(t *testing.T) {

func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs(config.TraceTypeAlphabet))
defaultCfg := config.NewConfig(common.HexToAddress(gameFactoryAddressValue), l1EthRpc, l1Beacon, datadir, config.TraceTypeAlphabet)
// Add in the extra CLI options required when using alphabet trace type
defaultCfg.RollupRpc = rollupRpc
defaultCfg := config.NewConfig(common.HexToAddress(gameFactoryAddressValue), l1EthRpc, l1Beacon, rollupRpc, l2EthRpc, datadir, config.TraceTypeAlphabet)
require.Equal(t, defaultCfg, cfg)
}

func TestDefaultConfigIsValid(t *testing.T) {
cfg := config.NewConfig(common.HexToAddress(gameFactoryAddressValue), l1EthRpc, l1Beacon, datadir, config.TraceTypeAlphabet)
// 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.RollupRpc = rollupRpc
cfg := config.NewConfig(common.HexToAddress(gameFactoryAddressValue), l1EthRpc, l1Beacon, rollupRpc, l2EthRpc, datadir, config.TraceTypeAlphabet)
require.NoError(t, cfg.Check())
}

Expand Down Expand Up @@ -114,7 +109,6 @@ func TestTraceType(t *testing.T) {
func TestMultipleTraceTypes(t *testing.T) {
t.Run("WithAllOptions", func(t *testing.T) {
argsMap := requiredArgs(config.TraceTypeCannon)
addRequiredOutputArgs(argsMap)
// Add Asterisc required flags
addRequiredAsteriscArgs(argsMap)
args := toArgList(argsMap)
Expand All @@ -130,7 +124,6 @@ func TestMultipleTraceTypes(t *testing.T) {
})
t.Run("WithSomeOptions", func(t *testing.T) {
argsMap := requiredArgs(config.TraceTypeCannon)
addRequiredOutputArgs(argsMap)
args := toArgList(argsMap)
// Add extra trace types (cannon is already specified)
args = append(args,
Expand Down Expand Up @@ -315,14 +308,6 @@ func TestAsteriscRequiredArgs(t *testing.T) {
})

t.Run(fmt.Sprintf("TestL2Rpc-%v", traceType), func(t *testing.T) {
t.Run("NotRequiredForAlphabetTraceLegacy", func(t *testing.T) {
configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--cannon-l2"))
})

t.Run("NotRequiredForAlphabetTrace", func(t *testing.T) {
configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--l2-eth-rpc"))
})

t.Run("RequiredForAsteriscTrace", func(t *testing.T) {
verifyArgsInvalid(t, "flag l2-eth-rpc is required", addRequiredArgsExcept(traceType, "--l2-eth-rpc"))
})
Expand Down Expand Up @@ -438,6 +423,25 @@ func TestAsteriscRequiredArgs(t *testing.T) {
})
}
}

func TestAlphabetRequiredArgs(t *testing.T) {
t.Run(fmt.Sprintf("TestL2Rpc-%v", config.TraceTypeAlphabet), func(t *testing.T) {
t.Run("RequiredForAlphabetTrace", func(t *testing.T) {
verifyArgsInvalid(t, "flag l2-eth-rpc is required", addRequiredArgsExcept(config.TraceTypeAlphabet, "--l2-eth-rpc"))
})

t.Run("ValidLegacy", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--l2-eth-rpc", fmt.Sprintf("--cannon-l2=%s", l2EthRpc)))
require.Equal(t, l2EthRpc, cfg.L2Rpc)
})

t.Run("Valid", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs(config.TraceTypeAlphabet))
require.Equal(t, l2EthRpc, cfg.L2Rpc)
})
})
}

func TestCannonRequiredArgs(t *testing.T) {
for _, traceType := range []config.TraceType{config.TraceTypeCannon, config.TraceTypePermissioned} {
traceType := traceType
Expand Down Expand Up @@ -502,14 +506,6 @@ func TestCannonRequiredArgs(t *testing.T) {
})

t.Run(fmt.Sprintf("TestL2Rpc-%v", traceType), func(t *testing.T) {
t.Run("NotRequiredForAlphabetTraceLegacy", func(t *testing.T) {
configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--cannon-l2"))
})

t.Run("NotRequiredForAlphabetTrace", func(t *testing.T) {
configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--l2-eth-rpc"))
})

t.Run("RequiredForCannonTrace", func(t *testing.T) {
verifyArgsInvalid(t, "flag l2-eth-rpc is required", addRequiredArgsExcept(traceType, "--l2-eth-rpc"))
})
Expand Down Expand Up @@ -772,6 +768,8 @@ func requiredArgs(traceType config.TraceType) map[string]string {
args := map[string]string{
"--l1-eth-rpc": l1EthRpc,
"--l1-beacon": l1Beacon,
"--rollup-rpc": rollupRpc,
"--l2-eth-rpc": l2EthRpc,
"--game-factory-address": gameFactoryAddressValue,
"--trace-type": traceType.String(),
"--datadir": datadir,
Expand All @@ -781,8 +779,6 @@ func requiredArgs(traceType config.TraceType) map[string]string {
addRequiredCannonArgs(args)
case config.TraceTypeAsterisc:
addRequiredAsteriscArgs(args)
case config.TraceTypeAlphabet:
addRequiredOutputArgs(args)
}
return args
}
Expand All @@ -793,7 +789,6 @@ func addRequiredCannonArgs(args map[string]string) {
args["--cannon-server"] = cannonServer
args["--cannon-prestate"] = cannonPreState
args["--l2-eth-rpc"] = l2EthRpc
addRequiredOutputArgs(args)
}

func addRequiredAsteriscArgs(args map[string]string) {
Expand All @@ -802,11 +797,6 @@ func addRequiredAsteriscArgs(args map[string]string) {
args["--asterisc-server"] = asteriscServer
args["--asterisc-prestate"] = asteriscPreState
args["--l2-eth-rpc"] = l2EthRpc
addRequiredOutputArgs(args)
}

func addRequiredOutputArgs(args map[string]string) {
args["--rollup-rpc"] = rollupRpc
}

func toArgList(req map[string]string) []string {
Expand Down
13 changes: 7 additions & 6 deletions op-challenger/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,16 @@ func NewConfig(
gameFactoryAddress common.Address,
l1EthRpc string,
l1BeaconApi string,
l2RollupRpc string,
l2EthRpc string,
datadir string,
supportedTraceTypes ...TraceType,
) Config {
return Config{
L1EthRpc: l1EthRpc,
L1Beacon: l1BeaconApi,
RollupRpc: l2RollupRpc,
L2Rpc: l2EthRpc,
GameFactoryAddress: gameFactoryAddress,
MaxConcurrency: uint(runtime.NumCPU()),
PollInterval: DefaultPollInterval,
Expand Down Expand Up @@ -201,6 +205,9 @@ func (c Config) Check() error {
if c.RollupRpc == "" {
return ErrMissingRollupRpc
}
if c.L2Rpc == "" {
return ErrMissingL2Rpc
}
if c.GameFactoryAddress == (common.Address{}) {
return ErrMissingGameFactoryAddress
}
Expand Down Expand Up @@ -244,9 +251,6 @@ func (c Config) Check() error {
if c.CannonAbsolutePreState != "" && c.CannonAbsolutePreStateBaseURL != nil {
return ErrCannonAbsolutePreStateAndBaseURL
}
if c.L2Rpc == "" {
return ErrMissingL2Rpc
}
if c.CannonSnapshotFreq == 0 {
return ErrMissingCannonSnapshotFreq
}
Expand Down Expand Up @@ -285,9 +289,6 @@ func (c Config) Check() error {
if c.AsteriscAbsolutePreState != "" && c.AsteriscAbsolutePreStateBaseURL != nil {
return ErrAsteriscAbsolutePreStateAndBaseURL
}
if c.L2Rpc == "" {
return ErrMissingL2Rpc
}
if c.AsteriscSnapshotFreq == 0 {
return ErrMissingAsteriscSnapshotFreq
}
Expand Down
5 changes: 1 addition & 4 deletions op-challenger/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,23 @@ func applyValidConfigForCannon(cfg *Config) {
cfg.CannonServer = validCannonOpProgramBin
cfg.CannonAbsolutePreStateBaseURL = validCannonAbsolutPreStateBaseURL
cfg.CannonNetwork = validCannonNetwork
cfg.L2Rpc = validL2Rpc
}

func applyValidConfigForAsterisc(cfg *Config) {
cfg.AsteriscBin = validAsteriscBin
cfg.AsteriscServer = validAsteriscOpProgramBin
cfg.AsteriscAbsolutePreStateBaseURL = validAsteriscAbsolutPreStateBaseURL
cfg.AsteriscNetwork = validAsteriscNetwork
cfg.L2Rpc = validL2Rpc
}

func validConfig(traceType TraceType) Config {
cfg := NewConfig(validGameFactoryAddress, validL1EthRpc, validL1BeaconUrl, validDatadir, traceType)
cfg := NewConfig(validGameFactoryAddress, validL1EthRpc, validL1BeaconUrl, validRollupRpc, validL2Rpc, validDatadir, traceType)
if traceType == TraceTypeCannon || traceType == TraceTypePermissioned {
applyValidConfigForCannon(&cfg)
}
if traceType == TraceTypeAsterisc {
applyValidConfigForAsterisc(&cfg)
}
cfg.RollupRpc = validRollupRpc
return cfg
}

Expand Down
12 changes: 4 additions & 8 deletions op-challenger/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,6 @@ func CheckCannonFlags(ctx *cli.Context) error {
if !ctx.IsSet(CannonPreStateFlag.Name) && !ctx.IsSet(CannonPreStatesURLFlag.Name) {
return fmt.Errorf("flag %s or %s is required", CannonPreStatesURLFlag.Name, CannonPreStateFlag.Name)
}
// CannonL2Flag is checked because it is an alias with L2EthRpcFlag
if !ctx.IsSet(CannonL2Flag.Name) && !ctx.IsSet(L2EthRpcFlag.Name) {
return fmt.Errorf("flag %s is required", L2EthRpcFlag.Name)
}
return nil
}

Expand All @@ -323,10 +319,6 @@ func CheckAsteriscFlags(ctx *cli.Context) error {
if !ctx.IsSet(AsteriscPreStateFlag.Name) && !ctx.IsSet(AsteriscPreStatesURLFlag.Name) {
return fmt.Errorf("flag %s or %s is required", AsteriscPreStatesURLFlag.Name, AsteriscPreStateFlag.Name)
}
// CannonL2Flag is checked because it is an alias with L2EthRpcFlag
if !ctx.IsSet(CannonL2Flag.Name) && !ctx.IsSet(L2EthRpcFlag.Name) {
return fmt.Errorf("flag %s is required", L2EthRpcFlag.Name)
}
return nil
}

Expand All @@ -336,6 +328,10 @@ func CheckRequired(ctx *cli.Context, traceTypes []config.TraceType) error {
return fmt.Errorf("flag %s is required", f.Names()[0])
}
}
// CannonL2Flag is checked because it is an alias with L2EthRpcFlag
if !ctx.IsSet(CannonL2Flag.Name) && !ctx.IsSet(L2EthRpcFlag.Name) {
return fmt.Errorf("flag %s is required", L2EthRpcFlag.Name)
}
for _, traceType := range traceTypes {
switch traceType {
case config.TraceTypeCannon, config.TraceTypePermissioned:
Expand Down
18 changes: 15 additions & 3 deletions op-challenger/game/fault/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Responder interface {

type ClaimLoader interface {
GetAllClaims(ctx context.Context, block rpcblock.Block) ([]types.Claim, error)
IsL2BlockNumberChallenged(ctx context.Context, block rpcblock.Block) (bool, error)
}

type Agent struct {
Expand Down Expand Up @@ -84,6 +85,14 @@ func (a *Agent) Act(ctx context.Context) error {
defer func() {
a.metrics.RecordGameActTime(a.systemClock.Since(start).Seconds())
}()

if challenged, err := a.loader.IsL2BlockNumberChallenged(ctx, rpcblock.Latest); err != nil {
return fmt.Errorf("failed to check if L2 block number already challenged: %w", err)
} else if challenged {
a.log.Debug("Skipping game with already challenged L2 block number")
return nil
}

game, err := a.newGameFromContracts(ctx)
if err != nil {
return fmt.Errorf("create game from contracts: %w", err)
Expand All @@ -105,11 +114,13 @@ func (a *Agent) Act(ctx context.Context) error {

func (a *Agent) performAction(ctx context.Context, wg *sync.WaitGroup, action types.Action) {
defer wg.Done()
actionLog := a.log.New("action", action.Type, "is_attack", action.IsAttack, "parent", action.ParentIdx)
actionLog := a.log.New("action", action.Type)
if action.Type == types.ActionTypeStep {
containsOracleData := action.OracleData != nil
isLocal := containsOracleData && action.OracleData.IsLocal
actionLog = actionLog.New(
"is_attack", action.IsAttack,
"parent", action.ParentIdx,
"prestate", common.Bytes2Hex(action.PreState),
"proof", common.Bytes2Hex(action.ProofData),
"containsOracleData", containsOracleData,
Expand All @@ -118,15 +129,16 @@ func (a *Agent) performAction(ctx context.Context, wg *sync.WaitGroup, action ty
if action.OracleData != nil {
actionLog = actionLog.New("oracleKey", common.Bytes2Hex(action.OracleData.OracleKey))
}
} else {
actionLog = actionLog.New("value", action.Value)
} else if action.Type == types.ActionTypeMove {
actionLog = actionLog.New("is_attack", action.IsAttack, "parent", action.ParentIdx, "value", action.Value)
}

switch action.Type {
case types.ActionTypeMove:
a.metrics.RecordGameMove()
case types.ActionTypeStep:
a.metrics.RecordGameStep()
// TODO(client-pod#852): Add metrics for L2 block number challenges
}
actionLog.Info("Performing action")
err := a.responder.PerformAction(ctx, action)
Expand Down
23 changes: 20 additions & 3 deletions op-challenger/game/fault/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ func TestDoNotMakeMovesWhenGameIsResolvable(t *testing.T) {
}
}

func TestDoNotMakeMovesWhenL2BlockNumberChallenged(t *testing.T) {
ctx := context.Background()

agent, claimLoader, responder := setupTestAgent(t)
claimLoader.blockNumChallenged = true

require.NoError(t, agent.Act(ctx))

require.Equal(t, 1, responder.callResolveCount, "should check if game is resolvable")
require.Equal(t, 1, claimLoader.callCount, "should fetch claims only once for resolveClaim")
}

func createClaimsWithClaimants(t *testing.T, d types.Depth) []types.Claim {
claimBuilder := test.NewClaimBuilder(t, d, alphabet.NewTraceProvider(big.NewInt(0), d))
rootClaim := claimBuilder.CreateRootClaim()
Expand Down Expand Up @@ -180,9 +192,14 @@ func setupTestAgent(t *testing.T) (*Agent, *stubClaimLoader, *stubResponder) {
}

type stubClaimLoader struct {
callCount int
maxLoads int
claims []types.Claim
callCount int
maxLoads int
claims []types.Claim
blockNumChallenged bool
}

func (s *stubClaimLoader) IsL2BlockNumberChallenged(_ context.Context, _ rpcblock.Block) (bool, error) {
return s.blockNumChallenged, nil
}

func (s *stubClaimLoader) GetAllClaims(_ context.Context, _ rpcblock.Block) ([]types.Claim, error) {
Expand Down
15 changes: 14 additions & 1 deletion op-challenger/game/fault/contracts/faultdisputegame.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ var (
methodWETH = "weth"
)

var ErrSimulationFailed = errors.New("tx simulation failed")
var (
ErrSimulationFailed = errors.New("tx simulation failed")
ErrChallengeL2BlockNotSupported = errors.New("contract version does not support challenging L2 block number")
)

type FaultDisputeGameContractLatest struct {
metrics metrics.ContractMetricer
Expand Down Expand Up @@ -410,6 +413,14 @@ func (f *FaultDisputeGameContractLatest) vm(ctx context.Context) (*VMContract, e
return NewVMContract(vmAddr, f.multiCaller), nil
}

func (f *FaultDisputeGameContractLatest) IsL2BlockNumberChallenged(ctx context.Context, block rpcblock.Block) (bool, error) {
return false, nil
}

func (f *FaultDisputeGameContractLatest) ChallengeL2BlockNumberTx(challenge *types.InvalidL2BlockNumberChallenge) (txmgr.TxCandidate, error) {
return txmgr.TxCandidate{}, ErrChallengeL2BlockNotSupported
}

func (f *FaultDisputeGameContractLatest) AttackTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodAttack, new(big.Int).SetUint64(parentContractIndex), pivot)
return call.ToTxCandidate()
Expand Down Expand Up @@ -523,6 +534,8 @@ type FaultDisputeGameContract interface {
GetClaim(ctx context.Context, idx uint64) (types.Claim, error)
GetAllClaims(ctx context.Context, block rpcblock.Block) ([]types.Claim, error)
IsResolved(ctx context.Context, block rpcblock.Block, claims ...types.Claim) ([]bool, error)
IsL2BlockNumberChallenged(ctx context.Context, block rpcblock.Block) (bool, error)
ChallengeL2BlockNumberTx(challenge *types.InvalidL2BlockNumberChallenge) (txmgr.TxCandidate, error)
AttackTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error)
DefendTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error)
StepTx(claimIdx uint64, isAttack bool, stateData []byte, proof []byte) (txmgr.TxCandidate, error)
Expand Down
24 changes: 24 additions & 0 deletions op-challenger/game/fault/contracts/faultdisputegame_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,30 @@ func TestFaultDisputeGame_IsResolved(t *testing.T) {
}
}

func TestFaultDisputeGameContractLatest_IsL2BlockNumberChallenged(t *testing.T) {
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
_, game := setupFaultDisputeGameTest(t, version)
challenged, err := game.IsL2BlockNumberChallenged(context.Background(), rpcblock.Latest)
require.NoError(t, err)
require.False(t, challenged)
})
}
}

func TestFaultDisputeGameContractLatest_ChallengeL2BlockNumberTx(t *testing.T) {
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
_, game := setupFaultDisputeGameTest(t, version)
tx, err := game.ChallengeL2BlockNumberTx(&faultTypes.InvalidL2BlockNumberChallenge{})
require.ErrorIs(t, err, ErrChallengeL2BlockNotSupported)
require.Equal(t, txmgr.TxCandidate{}, tx)
})
}
}

func setupFaultDisputeGameTest(t *testing.T, version contractVersion) (*batchingTest.AbiBasedRpc, FaultDisputeGameContract) {
fdgAbi := version.loadAbi()

Expand Down
Loading