Commit 273bd06f authored by OptimismBot's avatar OptimismBot Committed by GitHub

Merge pull request #6776 from ethereum-optimism/aj/cannon-dispute-by-agreeing

op-challenger: Add e2e test that disputes an output root by agreeing 
parents 0eac68d1 b6a3a7d0
...@@ -32,7 +32,7 @@ type Executor struct { ...@@ -32,7 +32,7 @@ type Executor struct {
logger log.Logger logger log.Logger
l1 string l1 string
l2 string l2 string
inputs localGameInputs inputs LocalGameInputs
cannon string cannon string
server string server string
network string network string
...@@ -45,7 +45,7 @@ type Executor struct { ...@@ -45,7 +45,7 @@ type Executor struct {
cmdExecutor cmdExecutor cmdExecutor cmdExecutor
} }
func NewExecutor(logger log.Logger, cfg *config.Config, inputs localGameInputs) *Executor { func NewExecutor(logger log.Logger, cfg *config.Config, inputs LocalGameInputs) *Executor {
return &Executor{ return &Executor{
logger: logger, logger: logger,
l1: cfg.L1EthRpc, l1: cfg.L1EthRpc,
...@@ -92,11 +92,11 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro ...@@ -92,11 +92,11 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro
"--l1", e.l1, "--l1", e.l1,
"--l2", e.l2, "--l2", e.l2,
"--datadir", dataDir, "--datadir", dataDir,
"--l1.head", e.inputs.l1Head.Hex(), "--l1.head", e.inputs.L1Head.Hex(),
"--l2.head", e.inputs.l2Head.Hex(), "--l2.head", e.inputs.L2Head.Hex(),
"--l2.outputroot", e.inputs.l2OutputRoot.Hex(), "--l2.outputroot", e.inputs.L2OutputRoot.Hex(),
"--l2.claim", e.inputs.l2Claim.Hex(), "--l2.claim", e.inputs.L2Claim.Hex(),
"--l2.blocknumber", e.inputs.l2BlockNumber.Text(10), "--l2.blocknumber", e.inputs.L2BlockNumber.Text(10),
) )
if e.network != "" { if e.network != "" {
args = append(args, "--network", e.network) args = append(args, "--network", e.network)
......
...@@ -29,12 +29,12 @@ func TestGenerateProof(t *testing.T) { ...@@ -29,12 +29,12 @@ func TestGenerateProof(t *testing.T) {
cfg.CannonL2 = "http://localhost:9999" cfg.CannonL2 = "http://localhost:9999"
cfg.CannonSnapshotFreq = 500 cfg.CannonSnapshotFreq = 500
inputs := localGameInputs{ inputs := LocalGameInputs{
l1Head: common.Hash{0x11}, L1Head: common.Hash{0x11},
l2Head: common.Hash{0x22}, L2Head: common.Hash{0x22},
l2OutputRoot: common.Hash{0x33}, L2OutputRoot: common.Hash{0x33},
l2Claim: common.Hash{0x44}, L2Claim: common.Hash{0x44},
l2BlockNumber: big.NewInt(3333), L2BlockNumber: big.NewInt(3333),
} }
captureExec := func(t *testing.T, cfg config.Config, proofAt uint64) (string, string, map[string]string) { captureExec := func(t *testing.T, cfg config.Config, proofAt uint64) (string, string, map[string]string) {
executor := NewExecutor(testlog.Logger(t, log.LvlInfo), &cfg, inputs) executor := NewExecutor(testlog.Logger(t, log.LvlInfo), &cfg, inputs)
...@@ -94,10 +94,10 @@ func TestGenerateProof(t *testing.T) { ...@@ -94,10 +94,10 @@ func TestGenerateProof(t *testing.T) {
require.NotContains(t, args, "--l2.genesis") require.NotContains(t, args, "--l2.genesis")
// Local game inputs // Local game inputs
require.Equal(t, inputs.l1Head.Hex(), args["--l1.head"]) require.Equal(t, inputs.L1Head.Hex(), args["--l1.head"])
require.Equal(t, inputs.l2Head.Hex(), args["--l2.head"]) require.Equal(t, inputs.L2Head.Hex(), args["--l2.head"])
require.Equal(t, inputs.l2OutputRoot.Hex(), args["--l2.outputroot"]) require.Equal(t, inputs.L2OutputRoot.Hex(), args["--l2.outputroot"])
require.Equal(t, inputs.l2Claim.Hex(), args["--l2.claim"]) require.Equal(t, inputs.L2Claim.Hex(), args["--l2.claim"])
require.Equal(t, "3333", args["--l2.blocknumber"]) require.Equal(t, "3333", args["--l2.blocknumber"])
}) })
......
...@@ -11,12 +11,12 @@ import ( ...@@ -11,12 +11,12 @@ import (
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
) )
type localGameInputs struct { type LocalGameInputs struct {
l1Head common.Hash L1Head common.Hash
l2Head common.Hash L2Head common.Hash
l2OutputRoot common.Hash L2OutputRoot common.Hash
l2Claim common.Hash L2Claim common.Hash
l2BlockNumber *big.Int L2BlockNumber *big.Int
} }
type L2DataSource interface { type L2DataSource interface {
...@@ -32,30 +32,30 @@ type GameInputsSource interface { ...@@ -32,30 +32,30 @@ type GameInputsSource interface {
}, error) }, error)
} }
func fetchLocalInputs(ctx context.Context, gameAddr common.Address, caller GameInputsSource, l2Client L2DataSource) (localGameInputs, error) { func fetchLocalInputs(ctx context.Context, gameAddr common.Address, caller GameInputsSource, l2Client L2DataSource) (LocalGameInputs, error) {
opts := &bind.CallOpts{Context: ctx} opts := &bind.CallOpts{Context: ctx}
l1Head, err := caller.L1Head(opts) l1Head, err := caller.L1Head(opts)
if err != nil { if err != nil {
return localGameInputs{}, fmt.Errorf("fetch L1 head for game %v: %w", gameAddr, err) return LocalGameInputs{}, fmt.Errorf("fetch L1 head for game %v: %w", gameAddr, err)
} }
proposals, err := caller.Proposals(opts) proposals, err := caller.Proposals(opts)
if err != nil { if err != nil {
return localGameInputs{}, fmt.Errorf("fetch proposals: %w", err) return LocalGameInputs{}, fmt.Errorf("fetch proposals: %w", err)
} }
claimedOutput := proposals.Disputed claimedOutput := proposals.Disputed
agreedOutput := proposals.Starting agreedOutput := proposals.Starting
agreedHeader, err := l2Client.HeaderByNumber(ctx, agreedOutput.L2BlockNumber) agreedHeader, err := l2Client.HeaderByNumber(ctx, agreedOutput.L2BlockNumber)
if err != nil { if err != nil {
return localGameInputs{}, fmt.Errorf("fetch L2 block header %v: %w", agreedOutput.L2BlockNumber, err) return LocalGameInputs{}, fmt.Errorf("fetch L2 block header %v: %w", agreedOutput.L2BlockNumber, err)
} }
l2Head := agreedHeader.Hash() l2Head := agreedHeader.Hash()
return localGameInputs{ return LocalGameInputs{
l1Head: l1Head, L1Head: l1Head,
l2Head: l2Head, L2Head: l2Head,
l2OutputRoot: agreedOutput.OutputRoot, L2OutputRoot: agreedOutput.OutputRoot,
l2Claim: claimedOutput.OutputRoot, L2Claim: claimedOutput.OutputRoot,
l2BlockNumber: claimedOutput.L2BlockNumber, L2BlockNumber: claimedOutput.L2BlockNumber,
}, nil }, nil
} }
...@@ -39,11 +39,11 @@ func TestFetchLocalInputs(t *testing.T) { ...@@ -39,11 +39,11 @@ func TestFetchLocalInputs(t *testing.T) {
inputs, err := fetchLocalInputs(ctx, gameAddr, l1Client, l2Client) inputs, err := fetchLocalInputs(ctx, gameAddr, l1Client, l2Client)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, l1Client.l1Head, inputs.l1Head) require.Equal(t, l1Client.l1Head, inputs.L1Head)
require.Equal(t, l2Client.header.Hash(), inputs.l2Head) require.Equal(t, l2Client.header.Hash(), inputs.L2Head)
require.EqualValues(t, l1Client.starting.OutputRoot, inputs.l2OutputRoot) require.EqualValues(t, l1Client.starting.OutputRoot, inputs.L2OutputRoot)
require.EqualValues(t, l1Client.disputed.OutputRoot, inputs.l2Claim) require.EqualValues(t, l1Client.disputed.OutputRoot, inputs.L2Claim)
require.Equal(t, l1Client.disputed.L2BlockNumber, inputs.l2BlockNumber) require.Equal(t, l1Client.disputed.L2BlockNumber, inputs.L2BlockNumber)
} }
type mockGameInputsSource struct { type mockGameInputsSource struct {
......
...@@ -60,16 +60,20 @@ func NewTraceProvider(ctx context.Context, logger log.Logger, cfg *config.Config ...@@ -60,16 +60,20 @@ func NewTraceProvider(ctx context.Context, logger log.Logger, cfg *config.Config
if err != nil { if err != nil {
return nil, fmt.Errorf("create caller for game %v: %w", cfg.GameAddress, err) return nil, fmt.Errorf("create caller for game %v: %w", cfg.GameAddress, err)
} }
l1Head, err := fetchLocalInputs(ctx, cfg.GameAddress, gameCaller, l2Client) localInputs, err := fetchLocalInputs(ctx, cfg.GameAddress, gameCaller, l2Client)
if err != nil { if err != nil {
return nil, fmt.Errorf("fetch local game inputs: %w", err) return nil, fmt.Errorf("fetch local game inputs: %w", err)
} }
return NewTraceProviderFromInputs(logger, cfg, localInputs), nil
}
func NewTraceProviderFromInputs(logger log.Logger, cfg *config.Config, localInputs LocalGameInputs) *CannonTraceProvider {
return &CannonTraceProvider{ return &CannonTraceProvider{
logger: logger, logger: logger,
dir: cfg.CannonDatadir, dir: cfg.CannonDatadir,
prestate: cfg.CannonAbsolutePreState, prestate: cfg.CannonAbsolutePreState,
generator: NewExecutor(logger, cfg, l1Head), generator: NewExecutor(logger, cfg, localInputs),
}, nil }
} }
func (p *CannonTraceProvider) Get(ctx context.Context, i uint64) (common.Hash, error) { func (p *CannonTraceProvider) Get(ctx context.Context, i uint64) (common.Hash, error) {
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"os" "os"
"path/filepath" "path/filepath"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault/cannon" "github.com/ethereum-optimism/optimism/op-challenger/fault/cannon"
...@@ -12,8 +13,10 @@ import ( ...@@ -12,8 +13,10 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
) )
type CannonGameHelper struct { type CannonGameHelper struct {
...@@ -21,7 +24,7 @@ type CannonGameHelper struct { ...@@ -21,7 +24,7 @@ type CannonGameHelper struct {
} }
func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Endpoint string, l2Endpoint string, name string, options ...challenger.Option) *challenger.Helper { func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Endpoint string, l2Endpoint string, name string, options ...challenger.Option) *challenger.Helper {
opts := []challenger.Option{g.createConfigOption(rollupCfg, l2Genesis, l2Endpoint)} opts := []challenger.Option{createConfigOption(g.t, rollupCfg, l2Genesis, g.addr, l2Endpoint)}
opts = append(opts, options...) opts = append(opts, options...)
c := challenger.NewChallenger(g.t, ctx, l1Endpoint, name, opts...) c := challenger.NewChallenger(g.t, ctx, l1Endpoint, name, opts...)
g.t.Cleanup(func() { g.t.Cleanup(func() {
...@@ -31,10 +34,10 @@ func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollu ...@@ -31,10 +34,10 @@ func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollu
} }
func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Client bind.ContractCaller, l1Endpoint string, l2Endpoint string, options ...challenger.Option) *HonestHelper { func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Client bind.ContractCaller, l1Endpoint string, l2Endpoint string, options ...challenger.Option) *HonestHelper {
opts := []challenger.Option{g.createConfigOption(rollupCfg, l2Genesis, l2Endpoint)} opts := []challenger.Option{createConfigOption(g.t, rollupCfg, l2Genesis, g.addr, l2Endpoint)}
opts = append(opts, options...) opts = append(opts, options...)
cfg := challenger.NewChallengerConfig(g.t, l1Endpoint, opts...) cfg := challenger.NewChallengerConfig(g.t, l1Endpoint, opts...)
provider, err := cannon.NewTraceProvider(ctx, testlog.Logger(g.t, log.LvlTrace).New("role", "CorrectTrace"), cfg, l1Client) provider, err := cannon.NewTraceProvider(ctx, testlog.Logger(g.t, log.LvlInfo).New("role", "CorrectTrace"), cfg, l1Client)
g.require.NoError(err, "create cannon trace provider") g.require.NoError(err, "create cannon trace provider")
return &HonestHelper{ return &HonestHelper{
...@@ -45,28 +48,29 @@ func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rol ...@@ -45,28 +48,29 @@ func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rol
} }
} }
func (g *CannonGameHelper) createConfigOption(rollupCfg *rollup.Config, l2Genesis *core.Genesis, l2Endpoint string) challenger.Option { func createConfigOption(t *testing.T, rollupCfg *rollup.Config, l2Genesis *core.Genesis, gameAddr common.Address, l2Endpoint string) challenger.Option {
return func(c *config.Config) { return func(c *config.Config) {
c.GameAddress = g.addr require := require.New(t)
c.GameAddress = gameAddr
c.TraceType = config.TraceTypeCannon c.TraceType = config.TraceTypeCannon
c.AgreeWithProposedOutput = false c.AgreeWithProposedOutput = false
c.CannonL2 = l2Endpoint c.CannonL2 = l2Endpoint
c.CannonBin = "../cannon/bin/cannon" c.CannonBin = "../cannon/bin/cannon"
c.CannonDatadir = g.t.TempDir() c.CannonDatadir = t.TempDir()
c.CannonServer = "../op-program/bin/op-program" c.CannonServer = "../op-program/bin/op-program"
c.CannonAbsolutePreState = "../op-program/bin/prestate.json" c.CannonAbsolutePreState = "../op-program/bin/prestate.json"
c.CannonSnapshotFreq = 10_000_000 c.CannonSnapshotFreq = 10_000_000
genesisBytes, err := json.Marshal(l2Genesis) genesisBytes, err := json.Marshal(l2Genesis)
g.require.NoError(err, "marshall l2 genesis config") require.NoError(err, "marshall l2 genesis config")
genesisFile := filepath.Join(c.CannonDatadir, "l2-genesis.json") genesisFile := filepath.Join(c.CannonDatadir, "l2-genesis.json")
g.require.NoError(os.WriteFile(genesisFile, genesisBytes, 0644)) require.NoError(os.WriteFile(genesisFile, genesisBytes, 0644))
c.CannonL2GenesisPath = genesisFile c.CannonL2GenesisPath = genesisFile
rollupBytes, err := json.Marshal(rollupCfg) rollupBytes, err := json.Marshal(rollupCfg)
g.require.NoError(err, "marshall rollup config") require.NoError(err, "marshall rollup config")
rollupFile := filepath.Join(c.CannonDatadir, "rollup.json") rollupFile := filepath.Join(c.CannonDatadir, "rollup.json")
g.require.NoError(os.WriteFile(rollupFile, rollupBytes, 0644)) require.NoError(os.WriteFile(rollupFile, rollupBytes, 0644))
c.CannonRollupConfigPath = rollupFile c.CannonRollupConfigPath = rollupFile
} }
} }
...@@ -42,7 +42,7 @@ func (g *FaultGameHelper) WaitForClaimCount(ctx context.Context, count int64) { ...@@ -42,7 +42,7 @@ func (g *FaultGameHelper) WaitForClaimCount(ctx context.Context, count int64) {
g.t.Log("Waiting for claim count", "current", actual, "expected", count, "game", g.addr) g.t.Log("Waiting for claim count", "current", actual, "expected", count, "game", g.addr)
return actual.Cmp(big.NewInt(count)) == 0, nil return actual.Cmp(big.NewInt(count)) == 0, nil
}) })
g.require.NoError(err) g.require.NoErrorf(err, "Did not find expected claim count %v", count)
} }
type ContractClaim struct { type ContractClaim struct {
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"math"
"math/big" "math/big"
"testing" "testing"
"time" "time"
...@@ -13,11 +14,16 @@ import ( ...@@ -13,11 +14,16 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault/alphabet" "github.com/ethereum-optimism/optimism/op-challenger/fault/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/fault/cannon"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -125,9 +131,57 @@ func (h *FactoryHelper) StartAlphabetGame(ctx context.Context, claimedAlphabet s ...@@ -125,9 +131,57 @@ func (h *FactoryHelper) StartAlphabetGame(ctx context.Context, claimedAlphabet s
} }
func (h *FactoryHelper) StartCannonGame(ctx context.Context, rootClaim common.Hash) *CannonGameHelper { func (h *FactoryHelper) StartCannonGame(ctx context.Context, rootClaim common.Hash) *CannonGameHelper {
l2BlockNumber := h.waitForProposals(ctx) l2BlockNumber, l1Head := h.prepareCannonGame(ctx)
l1Head := h.checkpointL1Block(ctx) return h.createCannonGame(ctx, l2BlockNumber, l1Head, rootClaim)
}
func (h *FactoryHelper) StartCannonGameWithCorrectRoot(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Endpoint string, l2Endpoint string, options ...challenger.Option) (*CannonGameHelper, *HonestHelper) {
l2BlockNumber, l1Head := h.prepareCannonGame(ctx)
challengerOpts := []challenger.Option{createConfigOption(h.t, rollupCfg, l2Genesis, common.Address{0xaa}, l2Endpoint)}
challengerOpts = append(challengerOpts, options...)
cfg := challenger.NewChallengerConfig(h.t, l1Endpoint, challengerOpts...)
opts := &bind.CallOpts{Context: ctx}
outputIdx, err := h.l2oo.GetL2OutputIndexAfter(opts, new(big.Int).SetUint64(l2BlockNumber))
h.require.NoError(err, "Fetch challenged output index")
challengedOutput, err := h.l2oo.GetL2Output(opts, outputIdx)
h.require.NoError(err, "Fetch challenged output")
agreedOutput, err := h.l2oo.GetL2Output(opts, new(big.Int).Sub(outputIdx, common.Big1))
h.require.NoError(err, "Fetch agreed output")
l1BlockInfo, err := h.blockOracle.Load(opts, l1Head)
h.require.NoError(err, "Fetch L1 block info")
l2Client, err := ethclient.DialContext(ctx, cfg.CannonL2)
if err != nil {
h.require.NoErrorf(err, "Failed to dial l2 client %v", l2Endpoint)
}
defer l2Client.Close()
agreedHeader, err := l2Client.HeaderByNumber(ctx, agreedOutput.L2BlockNumber)
if err != nil {
h.require.NoErrorf(err, "Failed to fetch L2 block header %v", agreedOutput.L2BlockNumber)
}
inputs := cannon.LocalGameInputs{
L1Head: l1BlockInfo.Hash,
L2Head: agreedHeader.Hash(),
L2OutputRoot: agreedOutput.OutputRoot,
L2Claim: challengedOutput.OutputRoot,
L2BlockNumber: challengedOutput.L2BlockNumber,
}
provider := cannon.NewTraceProviderFromInputs(testlog.Logger(h.t, log.LvlInfo).New("role", "CorrectTrace"), cfg, inputs)
rootClaim, err := provider.Get(ctx, math.MaxUint64)
h.require.NoError(err, "Compute correct root hash")
game := h.createCannonGame(ctx, l2BlockNumber, l1Head, rootClaim)
honestHelper := &HonestHelper{
t: h.t,
require: h.require,
game: &game.FaultGameHelper,
correctTrace: provider,
}
return game, honestHelper
}
func (h *FactoryHelper) createCannonGame(ctx context.Context, l2BlockNumber uint64, l1Head *big.Int, rootClaim common.Hash) *CannonGameHelper {
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute) ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
defer cancel() defer cancel()
...@@ -171,6 +225,12 @@ func (h *FactoryHelper) StartChallenger(ctx context.Context, l1Endpoint string, ...@@ -171,6 +225,12 @@ func (h *FactoryHelper) StartChallenger(ctx context.Context, l1Endpoint string,
return c return c
} }
func (h *FactoryHelper) prepareCannonGame(ctx context.Context) (uint64, *big.Int) {
l2BlockNumber := h.waitForProposals(ctx)
l1Head := h.checkpointL1Block(ctx)
return l2BlockNumber, l1Head
}
// waitForProposals waits until there are at least two proposals in the output oracle // waitForProposals waits until there are at least two proposals in the output oracle
// This is the minimum required for creating a game. // This is the minimum required for creating a game.
// Returns the l2 block number of the latest available proposal // Returns the l2 block number of the latest available proposal
......
...@@ -290,6 +290,54 @@ func TestCannonDefendStep(t *testing.T) { ...@@ -290,6 +290,54 @@ func TestCannonDefendStep(t *testing.T) {
game.LogGameData(ctx) game.LogGameData(ctx)
} }
func TestCannonChallengeWithCorrectRoot(t *testing.T) {
t.Skip("Not currently handling this case as the correct approach will change when output root bisection is added")
InitParallel(t)
ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t)
t.Cleanup(sys.Close)
l1Endpoint := sys.NodeEndpoint("l1")
l2Endpoint := sys.NodeEndpoint("sequencer")
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys.cfg.L1Deployments, l1Client)
game, correctTrace := disputeGameFactory.StartCannonGameWithCorrectRoot(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Endpoint, l2Endpoint, func(c *config.Config) {
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Mallory)
})
require.NotNil(t, game)
game.LogGameData(ctx)
game.StartChallenger(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Endpoint, l2Endpoint, "Challenger", func(c *config.Config) {
c.AgreeWithProposedOutput = true // Agree with the proposed output, so disagree with the root claim
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Alice)
})
maxDepth := game.MaxDepth(ctx)
for claimCount := int64(1); claimCount < maxDepth; {
game.LogGameData(ctx)
claimCount++
// Wait for the challenger to counter
game.WaitForClaimCount(ctx, claimCount)
// Defend everything because we have the same trace as the honest proposer
correctTrace.Defend(ctx, claimCount-1)
claimCount++
game.LogGameData(ctx)
game.WaitForClaimCount(ctx, claimCount)
}
game.LogGameData(ctx)
// Wait for the challenger to call step and counter our invalid claim
game.WaitForClaimAtMaxDepth(ctx, true)
sys.TimeTravelClock.AdvanceTime(game.GameDuration(ctx))
require.NoError(t, wait.ForNextBlock(ctx, l1Client))
game.WaitForGameStatus(ctx, disputegame.StatusChallengerWins)
game.LogGameData(ctx)
}
func startFaultDisputeSystem(t *testing.T) (*System, *ethclient.Client) { func startFaultDisputeSystem(t *testing.T) (*System, *ethclient.Client) {
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
delete(cfg.Nodes, "verifier") delete(cfg.Nodes, "verifier")
......
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