Commit 374f9aa6 authored by OptimismBot's avatar OptimismBot Committed by GitHub

Merge pull request #6541 from ethereum-optimism/aj/challenger-fetch-inputs

op-challenger: Load local inputs from the game contract.
parents 235cab90 84c78cc5
......@@ -29,6 +29,7 @@ type Executor struct {
logger log.Logger
l1 string
l2 string
inputs localGameInputs
cannon string
server string
absolutePreState string
......@@ -38,11 +39,12 @@ type Executor struct {
cmdExecutor cmdExecutor
}
func NewExecutor(logger log.Logger, cfg *config.Config) *Executor {
func NewExecutor(logger log.Logger, cfg *config.Config, inputs localGameInputs) *Executor {
return &Executor{
logger: logger,
l1: cfg.L1EthRpc,
l2: cfg.CannonL2,
inputs: inputs,
cannon: cfg.CannonBin,
server: cfg.CannonServer,
absolutePreState: cfg.CannonAbsolutePreState,
......@@ -71,7 +73,12 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro
"--l1", e.l1,
"--l2", e.l2,
"--datadir", filepath.Join(e.dataDir, preimagesDir),
// TODO(CLI-4240): Pass local game inputs (l1.head, l2.head, l2.claim etc)
"--l1.head", e.inputs.l1Head.Hex(),
"--l2.head", e.inputs.l2Head.Hex(),
"--l2.outputroot", e.inputs.l2OutputRoot.Hex(),
"--l2.claim", e.inputs.l2Claim.Hex(),
"--l2.blocknumber", e.inputs.l2BlockNumber.Text(10),
"--l2.chainid", e.inputs.l2ChainId.Text(10),
}
e.logger.Info("Generating trace", "proof", i, "cmd", e.cannon, "args", args)
......
......@@ -3,6 +3,7 @@ package cannon
import (
"context"
"fmt"
"math/big"
"os"
"path/filepath"
"testing"
......@@ -27,7 +28,16 @@ func TestGenerateProof(t *testing.T) {
cfg.CannonL2 = "http://localhost:9999"
cfg.CannonSnapshotFreq = 500
executor := NewExecutor(testlog.Logger(t, log.LvlInfo), &cfg)
inputs := localGameInputs{
l1Head: common.Hash{0x11},
l2ChainId: big.NewInt(2342),
l2Head: common.Hash{0x22},
l2OutputRoot: common.Hash{0x33},
l2Claim: common.Hash{0x44},
l2BlockNumber: big.NewInt(3333),
}
executor := NewExecutor(testlog.Logger(t, log.LvlInfo), &cfg, inputs)
executor.selectSnapshot = func(logger log.Logger, dir string, absolutePreState string, i uint64) (string, error) {
return input, nil
}
......@@ -56,6 +66,14 @@ func TestGenerateProof(t *testing.T) {
require.Equal(t, filepath.Join(cfg.CannonDatadir, preimagesDir), args["--datadir"])
require.Equal(t, filepath.Join(cfg.CannonDatadir, proofsDir, "%d.json"), args["--proof-fmt"])
require.Equal(t, filepath.Join(cfg.CannonDatadir, snapsDir, "%d.json"), args["--snapshot-fmt"])
// Local game inputs
require.Equal(t, inputs.l1Head.Hex(), args["--l1.head"])
require.Equal(t, inputs.l2Head.Hex(), args["--l2.head"])
require.Equal(t, inputs.l2OutputRoot.Hex(), args["--l2.outputroot"])
require.Equal(t, inputs.l2Claim.Hex(), args["--l2.claim"])
require.Equal(t, "3333", args["--l2.blocknumber"])
require.Equal(t, "2342", args["--l2.chainid"])
}
func TestRunCmdLogsOutput(t *testing.T) {
......
package cannon
import (
"context"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
)
type localGameInputs struct {
l1Head common.Hash
l2ChainId *big.Int
l2Head common.Hash
l2OutputRoot common.Hash
l2Claim common.Hash
l2BlockNumber *big.Int
}
type L2DataSource interface {
ChainID(context.Context) (*big.Int, error)
HeaderByNumber(context.Context, *big.Int) (*ethtypes.Header, error)
}
type GameInputsSource interface {
L1Head(opts *bind.CallOpts) ([32]byte, error)
Proposals(opts *bind.CallOpts) (struct {
Starting bindings.IFaultDisputeGameOutputProposal
Disputed bindings.IFaultDisputeGameOutputProposal
}, error)
}
func fetchLocalInputs(ctx context.Context, gameAddr common.Address, caller GameInputsSource, l2Client L2DataSource) (localGameInputs, error) {
opts := &bind.CallOpts{Context: ctx}
l1Head, err := caller.L1Head(opts)
if err != nil {
return localGameInputs{}, fmt.Errorf("fetch L1 head for game %v: %w", gameAddr, err)
}
l2ChainId, err := l2Client.ChainID(ctx)
if err != nil {
return localGameInputs{}, fmt.Errorf("fetch L2 chain ID: %w", err)
}
proposals, err := caller.Proposals(opts)
if err != nil {
return localGameInputs{}, fmt.Errorf("fetch proposals: %w", err)
}
claimedOutput := proposals.Disputed
agreedOutput := proposals.Starting
agreedHeader, err := l2Client.HeaderByNumber(ctx, agreedOutput.L2BlockNumber)
if err != nil {
return localGameInputs{}, fmt.Errorf("fetch L2 block header %v: %w", agreedOutput.L2BlockNumber, err)
}
l2Head := agreedHeader.Hash()
return localGameInputs{
l1Head: l1Head,
l2ChainId: l2ChainId,
l2Head: l2Head,
l2OutputRoot: agreedOutput.OutputRoot,
l2Claim: claimedOutput.OutputRoot,
l2BlockNumber: claimedOutput.L2BlockNumber,
}, nil
}
package cannon
import (
"context"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/require"
)
func TestFetchLocalInputs(t *testing.T) {
ctx := context.Background()
gameAddr := common.Address{0xab}
l1Client := &mockGameInputsSource{
l1Head: common.Hash{0xcc},
starting: bindings.IFaultDisputeGameOutputProposal{
Index: big.NewInt(6),
L2BlockNumber: big.NewInt(2222),
OutputRoot: common.Hash{0xdd},
},
disputed: bindings.IFaultDisputeGameOutputProposal{
Index: big.NewInt(7),
L2BlockNumber: big.NewInt(3333),
OutputRoot: common.Hash{0xee},
},
}
l2Client := &mockL2DataSource{
chainID: big.NewInt(88422),
header: ethtypes.Header{
Number: l1Client.starting.L2BlockNumber,
},
}
inputs, err := fetchLocalInputs(ctx, gameAddr, l1Client, l2Client)
require.NoError(t, err)
require.Equal(t, l1Client.l1Head, inputs.l1Head)
require.Equal(t, l2Client.chainID, inputs.l2ChainId)
require.Equal(t, l2Client.header.Hash(), inputs.l2Head)
require.EqualValues(t, l1Client.starting.OutputRoot, inputs.l2OutputRoot)
require.EqualValues(t, l1Client.disputed.OutputRoot, inputs.l2Claim)
require.Equal(t, l1Client.disputed.L2BlockNumber, inputs.l2BlockNumber)
}
type mockGameInputsSource struct {
l1Head common.Hash
starting bindings.IFaultDisputeGameOutputProposal
disputed bindings.IFaultDisputeGameOutputProposal
}
func (s *mockGameInputsSource) L1Head(opts *bind.CallOpts) ([32]byte, error) {
return s.l1Head, nil
}
func (s *mockGameInputsSource) Proposals(opts *bind.CallOpts) (struct {
Starting bindings.IFaultDisputeGameOutputProposal
Disputed bindings.IFaultDisputeGameOutputProposal
}, error) {
return struct {
Starting bindings.IFaultDisputeGameOutputProposal
Disputed bindings.IFaultDisputeGameOutputProposal
}{
Starting: s.starting,
Disputed: s.disputed,
}, nil
}
type mockL2DataSource struct {
chainID *big.Int
header ethtypes.Header
}
func (s *mockL2DataSource) ChainID(ctx context.Context) (*big.Int, error) {
return s.chainID, nil
}
func (s *mockL2DataSource) HeaderByNumber(ctx context.Context, num *big.Int) (*ethtypes.Header, error) {
if s.header.Number.Cmp(num) == 0 {
return &s.header, nil
}
return nil, ethereum.NotFound
}
......@@ -9,10 +9,13 @@ import (
"path/filepath"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
)
......@@ -40,12 +43,25 @@ type CannonTraceProvider struct {
generator ProofGenerator
}
func NewTraceProvider(logger log.Logger, cfg *config.Config) *CannonTraceProvider {
func NewTraceProvider(ctx context.Context, logger log.Logger, cfg *config.Config, l1Client bind.ContractCaller) (*CannonTraceProvider, error) {
l2Client, err := ethclient.DialContext(ctx, cfg.CannonL2)
if err != nil {
return nil, fmt.Errorf("dial l2 cleint %v: %w", cfg.CannonL2, err)
}
defer l2Client.Close() // Not needed after fetching the inputs
gameCaller, err := bindings.NewFaultDisputeGameCaller(cfg.GameAddress, l1Client)
if err != nil {
return nil, fmt.Errorf("create caller for game %v: %w", cfg.GameAddress, err)
}
l1Head, err := fetchLocalInputs(ctx, cfg.GameAddress, gameCaller, l2Client)
if err != nil {
return nil, fmt.Errorf("fetch local game inputs: %w", err)
}
return &CannonTraceProvider{
dir: cfg.CannonDatadir,
prestate: cfg.CannonAbsolutePreState,
generator: NewExecutor(logger, cfg),
}
generator: NewExecutor(logger, cfg, l1Head),
}, nil
}
func (p *CannonTraceProvider) GetOracleData(ctx context.Context, i uint64) (*types.PreimageOracleData, error) {
......
......@@ -36,11 +36,19 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se
return nil, fmt.Errorf("failed to create the transaction manager: %w", err)
}
client, err := ethclient.Dial(cfg.L1EthRpc)
if err != nil {
return nil, fmt.Errorf("failed to dial L1: %w", err)
}
var trace types.TraceProvider
var updater types.OracleUpdater
switch cfg.TraceType {
case config.TraceTypeCannon:
trace = cannon.NewTraceProvider(logger, cfg)
trace, err = cannon.NewTraceProvider(ctx, logger, cfg, client)
if err != nil {
return nil, fmt.Errorf("create cannon trace provider: %w", err)
}
updater, err = cannon.NewOracleUpdater(logger, txMgr, cfg.GameAddress, cfg.PreimageOracleAddress)
if err != nil {
return nil, fmt.Errorf("failed to create the cannon updater: %w", err)
......@@ -52,15 +60,11 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se
return nil, fmt.Errorf("unsupported trace type: %v", cfg.TraceType)
}
return newTypedService(ctx, logger, cfg, trace, updater, txMgr)
return newTypedService(ctx, logger, cfg, 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, provider types.TraceProvider, uploader types.OracleUpdater, txMgr txmgr.TxManager) (*service, error) {
client, err := ethclient.Dial(cfg.L1EthRpc)
if err != nil {
return nil, fmt.Errorf("failed to dial L1: %w", err)
}
func newTypedService(ctx context.Context, logger log.Logger, cfg *config.Config, client *ethclient.Client, provider types.TraceProvider, uploader types.OracleUpdater, txMgr txmgr.TxManager) (*service, error) {
contract, err := bindings.NewFaultDisputeGameCaller(cfg.GameAddress, client)
if err != nil {
......
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