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
12 changes: 12 additions & 0 deletions op-challenger/cmd/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum/go-ethereum/superchain"
"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -83,6 +84,17 @@ func TestL1ETHRPCAddress(t *testing.T) {
})
}

func TestL1ETHRPCKind(t *testing.T) {
t.Run("Valid", func(t *testing.T) {
const kind = sources.RPCKindAlchemy
cfg := configForArgs(t, addRequiredArgs(gameTypes.AlphabetGameType, "--l1-rpc-kind", kind.String()))
require.Equal(t, kind, cfg.L1RPCKind)
})
t.Run("Invalid", func(t *testing.T) {
verifyArgsInvalid(t, "invalid value \"bob\" for flag -l1-rpc-kind", addRequiredArgs(gameTypes.AlphabetGameType, "--l1-rpc-kind", "bob"))
})
}

func TestL1Beacon(t *testing.T) {
t.Run("Required", func(t *testing.T) {
verifyArgsInvalid(t, "flag l1-beacon is required", addRequiredArgsExcept(gameTypes.AlphabetGameType, "--l1-beacon"))
Expand Down
28 changes: 18 additions & 10 deletions op-challenger/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
)
Expand All @@ -23,6 +24,7 @@ var (
ErrMissingL2Rpc = errors.New("missing L2 rpc url")
ErrMissingCannonAbsolutePreState = errors.New("missing cannon absolute pre-state")
ErrMissingL1EthRPC = errors.New("missing l1 eth rpc url")
ErrMissingL1RPCKind = errors.New("missing l1 eth rpc kind")
ErrMissingL1Beacon = errors.New("missing l1 beacon url")
ErrMissingGameFactoryAddress = errors.New("missing game factory address")
ErrMissingCannonSnapshotFreq = errors.New("missing cannon snapshot freq")
Expand Down Expand Up @@ -55,16 +57,17 @@ const (
// This also contains config options for auxiliary services.
// It is used to initialize the challenger.
type Config struct {
L1EthRpc string // L1 RPC Url
L1Beacon string // L1 Beacon API Url
GameFactoryAddress common.Address // Address of the dispute game factory
GameAllowlist []common.Address // Allowlist of fault game addresses
GameWindow time.Duration // Maximum time duration to look for games to progress
Datadir string // Data Directory
MaxConcurrency uint // Maximum number of threads to use when progressing games
PollInterval time.Duration // Polling interval for latest-block subscription when using an HTTP RPC provider
AllowInvalidPrestate bool // Whether to allow responding to games where the prestate does not match
MinUpdateInterval time.Duration // Minimum duration the L1 head block time must advance before scheduling a new update cycle
L1EthRpc string // L1 RPC Url
L1RPCKind sources.RPCProviderKind // L1 RPC kind
L1Beacon string // L1 Beacon API Url
GameFactoryAddress common.Address // Address of the dispute game factory
GameAllowlist []common.Address // Allowlist of fault game addresses
GameWindow time.Duration // Maximum time duration to look for games to progress
Datadir string // Data Directory
MaxConcurrency uint // Maximum number of threads to use when progressing games
PollInterval time.Duration // Polling interval for latest-block subscription when using an HTTP RPC provider
AllowInvalidPrestate bool // Whether to allow responding to games where the prestate does not match
MinUpdateInterval time.Duration // Minimum duration the L1 head block time must advance before scheduling a new update cycle

AdditionalBondClaimants []common.Address // List of addresses to claim bonds for in addition to the tx manager sender

Expand Down Expand Up @@ -116,6 +119,7 @@ func NewInteropConfig(
) Config {
return Config{
L1EthRpc: l1EthRpc,
L1RPCKind: sources.RPCKindStandard,
L1Beacon: l1BeaconApi,
SuperRPC: superRpc,
L2Rpcs: l2Rpcs,
Expand Down Expand Up @@ -168,6 +172,7 @@ func NewConfig(
) Config {
return Config{
L1EthRpc: l1EthRpc,
L1RPCKind: sources.RPCKindStandard,
L1Beacon: l1BeaconApi,
RollupRpc: l2RollupRpc,
L2Rpcs: []string{l2EthRpc},
Expand Down Expand Up @@ -217,6 +222,9 @@ func (c Config) Check() error {
if c.L1EthRpc == "" {
return ErrMissingL1EthRPC
}
if c.L1RPCKind == "" {
return ErrMissingL1RPCKind
}
if c.L1Beacon == "" {
return ErrMissingL1Beacon
}
Expand Down
6 changes: 6 additions & 0 deletions op-challenger/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,12 @@ func TestL1EthRpcRequired(t *testing.T) {
require.ErrorIs(t, config.Check(), ErrMissingL1EthRPC)
}

func TestL1EthRpcKindRequired(t *testing.T) {
config := validConfig(t, gameTypes.CannonGameType)
config.L1RPCKind = ""
require.ErrorIs(t, config.Check(), ErrMissingL1RPCKind)
}

func TestL1BeaconRequired(t *testing.T) {
config := validConfig(t, gameTypes.CannonGameType)
config.L1Beacon = ""
Expand Down
13 changes: 13 additions & 0 deletions op-challenger/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/flags"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/superchain"
Expand Down Expand Up @@ -49,6 +50,16 @@ var (
Usage: "HTTP provider URL for L1.",
EnvVars: prefixEnvVars("L1_ETH_RPC"),
}
L1RPCProviderKind = &cli.GenericFlag{
Name: "l1-rpc-kind",
Usage: "The kind of RPC provider, used to inform optimal transactions receipts fetching, and thus reduce costs. Valid options: " +
openum.EnumString(sources.RPCProviderKinds),
EnvVars: prefixEnvVars("L1_RPC_KIND"),
Value: func() *sources.RPCProviderKind {
out := sources.RPCKindStandard
return &out
}(),
}
L1BeaconFlag = &cli.StringFlag{
Name: "l1-beacon",
Usage: "Address of L1 Beacon API endpoint to use",
Expand Down Expand Up @@ -262,6 +273,7 @@ var requiredFlags = []cli.Flag{

// optionalFlags is a list of unchecked cli flags
var optionalFlags = []cli.Flag{
L1RPCProviderKind,
RollupRpcFlag,
NetworkFlag,
FactoryAddressFlag,
Expand Down Expand Up @@ -589,6 +601,7 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro
return &config.Config{
// Required Flags
L1EthRpc: l1EthRpc,
L1RPCKind: sources.RPCProviderKind(strings.ToLower(ctx.String(L1RPCProviderKind.Name))),
L1Beacon: l1Beacon,
GameTypes: enabledGameTypes,
GameFactoryAddress: gameFactoryAddress,
Expand Down
9 changes: 5 additions & 4 deletions op-challenger/game/client/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/dial"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
Expand All @@ -17,7 +18,7 @@ type Provider struct {
ctx context.Context
logger log.Logger
cfg *config.Config
l1Client *ethclient.Client
l1Client *sources.L1Client
caller *batching.MultiCaller

l2EL *ethclient.Client
Expand All @@ -29,13 +30,13 @@ type Provider struct {
toClose []func()
}

func NewProvider(ctx context.Context, logger log.Logger, cfg *config.Config, l1Client *ethclient.Client) *Provider {
func NewProvider(ctx context.Context, logger log.Logger, cfg *config.Config, l1Client *sources.L1Client, rpcClient client.RPC) *Provider {
return &Provider{
ctx: ctx,
logger: logger,
cfg: cfg,
l1Client: l1Client,
caller: batching.NewMultiCaller(l1Client.Client(), batching.DefaultBatchSize),
caller: batching.NewMultiCaller(rpcClient, batching.DefaultBatchSize),
}
}

Expand All @@ -45,7 +46,7 @@ func (c *Provider) Close() {
}
}

func (c *Provider) L1Client() *ethclient.Client {
func (c *Provider) L1Client() *sources.L1Client {
return c.l1Client
}

Expand Down
5 changes: 4 additions & 1 deletion op-challenger/game/fault/trace/outputs/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"math/big"
"time"

"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
Expand Down Expand Up @@ -114,7 +115,9 @@ func (o *OutputTraceProvider) GetL2BlockNumberChallenge(ctx context.Context) (*t
if err != nil {
return nil, err
}
header, err := o.l2Client.HeaderByNumber(ctx, new(big.Int).SetUint64(outputBlock))
tCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
header, err := o.l2Client.HeaderByNumber(tCtx, new(big.Int).SetUint64(outputBlock))
if err != nil {
return nil, fmt.Errorf("failed to retrieve L2 block header %v: %w", outputBlock, err)
}
Expand Down
5 changes: 4 additions & 1 deletion op-challenger/game/fault/trace/utils/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"math/big"
"time"

"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
Expand Down Expand Up @@ -50,7 +51,9 @@ func FetchLocalInputs(ctx context.Context, caller GameInputsSource, l2Client L2H
}

func FetchLocalInputsFromProposals(ctx context.Context, l1Head common.Hash, l2Client L2HeaderSource, agreedOutput Proposal, claimedOutput Proposal) (LocalGameInputs, error) {
agreedHeader, err := l2Client.HeaderByNumber(ctx, agreedOutput.L2BlockNumber)
tCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
agreedHeader, err := l2Client.HeaderByNumber(tCtx, agreedOutput.L2BlockNumber)
if err != nil {
return LocalGameInputs{}, fmt.Errorf("fetch L2 block header %v: %w", agreedOutput.L2BlockNumber, err)
}
Expand Down
7 changes: 3 additions & 4 deletions op-challenger/game/generic/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)

Expand All @@ -27,7 +26,7 @@ type GenericGameLoader interface {
}

type L1HeaderSource interface {
HeaderByHash(context.Context, common.Hash) (*gethTypes.Header, error)
BlockRefByHash(ctx context.Context, hash common.Hash) (eth.BlockRef, error)
}

type ActorCreator func(ctx context.Context, logger log.Logger, l1Head eth.BlockID) (Actor, error)
Expand Down Expand Up @@ -79,11 +78,11 @@ func NewGenericGamePlayer(
if err != nil {
return nil, fmt.Errorf("failed to load game L1 head: %w", err)
}
l1Header, err := l1HeaderSource.HeaderByHash(ctx, l1HeadHash)
l1Header, err := l1HeaderSource.BlockRefByHash(ctx, l1HeadHash)
if err != nil {
return nil, fmt.Errorf("failed to load L1 header %v: %w", l1HeadHash, err)
}
l1Head := eth.HeaderBlockID(l1Header)
l1Head := l1Header.ID()

actor, err := createActor(ctx, logger, l1Head)
if err != nil {
Expand Down
37 changes: 19 additions & 18 deletions op-challenger/game/keccak/fetcher/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (

"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum-optimism/optimism/op-service/apis"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
Expand All @@ -19,9 +21,8 @@ var (
)

type L1Source interface {
BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error)
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
ChainID(ctx context.Context) (*big.Int, error)
BlockRefByNumber(ctx context.Context, num uint64) (eth.BlockRef, error)
apis.ReceiptsFetcher
}

type Oracle interface {
Expand All @@ -43,12 +44,16 @@ func (f *InputFetcher) FetchInputs(ctx context.Context, blockHash common.Hash, o
var inputs []keccakTypes.InputData
for _, blockNum := range blockNums {
foundRelevantTx := false
block, err := f.source.BlockByNumber(ctx, new(big.Int).SetUint64(blockNum))
blockRef, err := f.source.BlockRefByNumber(ctx, blockNum)
if err != nil {
return nil, fmt.Errorf("failed getting tx for block %v: %w", blockNum, err)
return nil, fmt.Errorf("failed getting info for block %v: %w", blockNum, err)
}
for _, tx := range block.Transactions() {
inputData, err := f.extractRelevantLeavesFromTx(ctx, oracle, tx, ident)
_, receipts, err := f.source.FetchReceipts(ctx, blockRef.Hash)
if err != nil {
return nil, fmt.Errorf("failed to retrieve receipts for block %v: %w", blockNum, err)
}
for _, rcpt := range receipts {
inputData, err := f.extractRelevantLeavesFromReceipt(rcpt, oracle, ident)
if err != nil {
return nil, err
}
Expand All @@ -67,43 +72,39 @@ func (f *InputFetcher) FetchInputs(ctx context.Context, blockHash common.Hash, o
return inputs, nil
}

func (f *InputFetcher) extractRelevantLeavesFromTx(ctx context.Context, oracle Oracle, tx *types.Transaction, ident keccakTypes.LargePreimageIdent) ([]keccakTypes.InputData, error) {
rcpt, err := f.source.TransactionReceipt(ctx, tx.Hash())
if err != nil {
return nil, fmt.Errorf("failed to retrieve receipt for tx %v: %w", tx.Hash(), err)
}
func (f *InputFetcher) extractRelevantLeavesFromReceipt(rcpt *types.Receipt, oracle Oracle, ident keccakTypes.LargePreimageIdent) ([]keccakTypes.InputData, error) {
if rcpt.Status != types.ReceiptStatusSuccessful {
f.log.Trace("Skipping transaction with failed receipt status", "tx", tx.Hash(), "status", rcpt.Status)
f.log.Trace("Skipping transaction with failed receipt status", "tx", rcpt.TxHash, "status", rcpt.Status)
return nil, nil
}

// Iterate over the logs from in this receipt, looking for relevant logs emitted from the oracle contract
var inputs []keccakTypes.InputData
for i, txLog := range rcpt.Logs {
if txLog.Address != oracle.Addr() {
f.log.Trace("Skip tx log not emitted by the oracle contract", "tx", tx.Hash(), "logIndex", i, "targetContract", oracle.Addr(), "actualContract", txLog.Address)
f.log.Trace("Skip tx log not emitted by the oracle contract", "tx", rcpt.TxHash, "logIndex", i, "targetContract", oracle.Addr(), "actualContract", txLog.Address)
continue
}
if len(txLog.Data) < 20 {
f.log.Trace("Skip tx log with insufficient data (less than 20 bytes)", "tx", tx.Hash(), "logIndex", i, "dataLength", len(txLog.Data))
f.log.Trace("Skip tx log with insufficient data (less than 20 bytes)", "tx", rcpt.TxHash, "logIndex", i, "dataLength", len(txLog.Data))
continue
}
caller := common.Address(txLog.Data[0:20])
callData := txLog.Data[20:]

if caller != ident.Claimant {
f.log.Trace("Skip tx log from irrelevant claimant", "tx", tx.Hash(), "logIndex", i, "targetClaimant", ident.Claimant, "actualClaimant", caller)
f.log.Trace("Skip tx log from irrelevant claimant", "tx", rcpt.TxHash, "logIndex", i, "targetClaimant", ident.Claimant, "actualClaimant", caller)
continue
}
uuid, inputData, err := oracle.DecodeInputData(callData)
if errors.Is(err, contracts.ErrInvalidAddLeavesCall) {
f.log.Trace("Skip tx log with call data not targeting expected method", "tx", tx.Hash(), "logIndex", i, "err", err)
f.log.Trace("Skip tx log with call data not targeting expected method", "tx", rcpt.TxHash, "logIndex", i, "err", err)
continue
} else if err != nil {
return nil, err
}
if uuid.Cmp(ident.UUID) != 0 {
f.log.Trace("Skip tx log with irrelevant UUID", "tx", tx.Hash(), "logIndex", i, "targetUUID", ident.UUID, "actualUUID", uuid)
f.log.Trace("Skip tx log with irrelevant UUID", "tx", rcpt.TxHash, "logIndex", i, "targetUUID", ident.UUID, "actualUUID", uuid)
continue
}
inputs = append(inputs, inputData)
Expand Down
Loading