Commit d4706dad authored by Park Changwan's avatar Park Changwan Committed by GitHub

op-e2e: Refactor for asterisc e2e (#10214)

* op-e2e: Expose fields for asterisc e2e

* op-e2e: Add Helper Initializer methods

* op-e2e: Apply initializer methods and exposed fields

* op-e2e: Expose methods for asterisc e2e
parent 1667124c
...@@ -40,6 +40,16 @@ type Helper struct { ...@@ -40,6 +40,16 @@ type Helper struct {
chl cliapp.Lifecycle chl cliapp.Lifecycle
} }
func NewHelper(log log.Logger, t *testing.T, require *require.Assertions, dir string, chl cliapp.Lifecycle) *Helper {
return &Helper{
log: log,
t: t,
require: require,
dir: dir,
chl: chl,
}
}
type Option func(config2 *config.Config) type Option func(config2 *config.Config)
func WithFactoryAddress(addr common.Address) Option { func WithFactoryAddress(addr common.Address) Option {
...@@ -66,9 +76,9 @@ func WithPollInterval(pollInterval time.Duration) Option { ...@@ -66,9 +76,9 @@ func WithPollInterval(pollInterval time.Duration) Option {
} }
} }
// findMonorepoRoot finds the relative path to the monorepo root // FindMonorepoRoot finds the relative path to the monorepo root
// Different tests might be nested in subdirectories of the op-e2e dir. // Different tests might be nested in subdirectories of the op-e2e dir.
func findMonorepoRoot(t *testing.T) string { func FindMonorepoRoot(t *testing.T) string {
path := "./" path := "./"
// Only search up 5 directories // Only search up 5 directories
// Avoids infinite recursion if the root isn't found for some reason // Avoids infinite recursion if the root isn't found for some reason
...@@ -94,7 +104,7 @@ func applyCannonConfig( ...@@ -94,7 +104,7 @@ func applyCannonConfig(
) { ) {
require := require.New(t) require := require.New(t)
c.L2Rpc = l2Endpoint c.L2Rpc = l2Endpoint
root := findMonorepoRoot(t) root := FindMonorepoRoot(t)
c.CannonBin = root + "cannon/bin/cannon" c.CannonBin = root + "cannon/bin/cannon"
c.CannonServer = root + "op-program/bin/op-program" c.CannonServer = root + "op-program/bin/op-program"
c.CannonAbsolutePreState = root + "op-program/bin/prestate.json" c.CannonAbsolutePreState = root + "op-program/bin/prestate.json"
......
...@@ -15,53 +15,53 @@ import ( ...@@ -15,53 +15,53 @@ import (
type ClaimHelper struct { type ClaimHelper struct {
require *require.Assertions require *require.Assertions
game *OutputGameHelper game *OutputGameHelper
index int64 Index int64
parentIndex uint32 ParentIndex uint32
position types.Position Position types.Position
claim common.Hash claim common.Hash
} }
func newClaimHelper(game *OutputGameHelper, idx int64, claim ContractClaim) *ClaimHelper { func newClaimHelper(game *OutputGameHelper, idx int64, claim ContractClaim) *ClaimHelper {
return &ClaimHelper{ return &ClaimHelper{
require: game.require, require: game.Require,
game: game, game: game,
index: idx, Index: idx,
parentIndex: claim.ParentIndex, ParentIndex: claim.ParentIndex,
position: types.NewPositionFromGIndex(claim.Position), Position: types.NewPositionFromGIndex(claim.Position),
claim: claim.Claim, claim: claim.Claim,
} }
} }
func (c *ClaimHelper) AgreesWithOutputRoot() bool { func (c *ClaimHelper) AgreesWithOutputRoot() bool {
return c.position.Depth()%2 == 0 return c.Position.Depth()%2 == 0
} }
func (c *ClaimHelper) IsRootClaim() bool { func (c *ClaimHelper) IsRootClaim() bool {
return c.position.IsRootPosition() return c.Position.IsRootPosition()
} }
func (c *ClaimHelper) IsOutputRoot(ctx context.Context) bool { func (c *ClaimHelper) IsOutputRoot(ctx context.Context) bool {
splitDepth := c.game.SplitDepth(ctx) splitDepth := c.game.SplitDepth(ctx)
return c.position.Depth() <= splitDepth return c.Position.Depth() <= splitDepth
} }
func (c *ClaimHelper) IsOutputRootLeaf(ctx context.Context) bool { func (c *ClaimHelper) IsOutputRootLeaf(ctx context.Context) bool {
splitDepth := c.game.SplitDepth(ctx) splitDepth := c.game.SplitDepth(ctx)
return c.position.Depth() == splitDepth return c.Position.Depth() == splitDepth
} }
func (c *ClaimHelper) IsBottomGameRoot(ctx context.Context) bool { func (c *ClaimHelper) IsBottomGameRoot(ctx context.Context) bool {
splitDepth := c.game.SplitDepth(ctx) splitDepth := c.game.SplitDepth(ctx)
return c.position.Depth() == splitDepth+1 return c.Position.Depth() == splitDepth+1
} }
func (c *ClaimHelper) IsMaxDepth(ctx context.Context) bool { func (c *ClaimHelper) IsMaxDepth(ctx context.Context) bool {
maxDepth := c.game.MaxDepth(ctx) maxDepth := c.game.MaxDepth(ctx)
return c.position.Depth() == maxDepth return c.Position.Depth() == maxDepth
} }
func (c *ClaimHelper) Depth() types.Depth { func (c *ClaimHelper) Depth() types.Depth {
return c.position.Depth() return c.Position.Depth()
} }
// WaitForCounterClaim waits for the claim to be countered by another claim being posted. // WaitForCounterClaim waits for the claim to be countered by another claim being posted.
...@@ -72,8 +72,8 @@ func (c *ClaimHelper) WaitForCounterClaim(ctx context.Context, ignoreClaims ...* ...@@ -72,8 +72,8 @@ func (c *ClaimHelper) WaitForCounterClaim(ctx context.Context, ignoreClaims ...*
// This is the first claim we need to run cannon on, so give it more time // This is the first claim we need to run cannon on, so give it more time
timeout = timeout * 2 timeout = timeout * 2
} }
counterIdx, counterClaim := c.game.waitForClaim(ctx, timeout, fmt.Sprintf("failed to find claim with parent idx %v", c.index), func(claimIdx int64, claim ContractClaim) bool { counterIdx, counterClaim := c.game.waitForClaim(ctx, timeout, fmt.Sprintf("failed to find claim with parent idx %v", c.Index), func(claimIdx int64, claim ContractClaim) bool {
return int64(claim.ParentIndex) == c.index && !containsClaim(claimIdx, ignoreClaims) return int64(claim.ParentIndex) == c.Index && !containsClaim(claimIdx, ignoreClaims)
}) })
return newClaimHelper(c.game, counterIdx, counterClaim) return newClaimHelper(c.game, counterIdx, counterClaim)
} }
...@@ -83,28 +83,28 @@ func (c *ClaimHelper) WaitForCountered(ctx context.Context) { ...@@ -83,28 +83,28 @@ func (c *ClaimHelper) WaitForCountered(ctx context.Context) {
timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout) timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel() defer cancel()
err := wait.For(timedCtx, time.Second, func() (bool, error) { err := wait.For(timedCtx, time.Second, func() (bool, error) {
latestData := c.game.getClaim(ctx, c.index) latestData := c.game.getClaim(ctx, c.Index)
return latestData.CounteredBy != common.Address{}, nil return latestData.CounteredBy != common.Address{}, nil
}) })
if err != nil { // Avoid waiting time capturing game data when there's no error if err != nil { // Avoid waiting time capturing game data when there's no error
c.require.NoErrorf(err, "Claim %v was not countered\n%v", c.index, c.game.gameData(ctx)) c.require.NoErrorf(err, "Claim %v was not countered\n%v", c.Index, c.game.GameData(ctx))
} }
} }
func (c *ClaimHelper) RequireCorrectOutputRoot(ctx context.Context) { func (c *ClaimHelper) RequireCorrectOutputRoot(ctx context.Context) {
c.require.True(c.IsOutputRoot(ctx), "Should not expect a valid output root in the bottom game") c.require.True(c.IsOutputRoot(ctx), "Should not expect a valid output root in the bottom game")
expected, err := c.game.correctOutputProvider.Get(ctx, c.position) expected, err := c.game.CorrectOutputProvider.Get(ctx, c.Position)
c.require.NoError(err, "Failed to get correct output root") c.require.NoError(err, "Failed to get correct output root")
c.require.Equalf(expected, c.claim, "Should have correct output root in claim %v and position %v", c.index, c.position) c.require.Equalf(expected, c.claim, "Should have correct output root in claim %v and position %v", c.Index, c.Position)
} }
func (c *ClaimHelper) Attack(ctx context.Context, value common.Hash, opts ...MoveOpt) *ClaimHelper { func (c *ClaimHelper) Attack(ctx context.Context, value common.Hash, opts ...MoveOpt) *ClaimHelper {
c.game.Attack(ctx, c.index, value, opts...) c.game.Attack(ctx, c.Index, value, opts...)
return c.WaitForCounterClaim(ctx) return c.WaitForCounterClaim(ctx)
} }
func (c *ClaimHelper) Defend(ctx context.Context, value common.Hash, opts ...MoveOpt) *ClaimHelper { func (c *ClaimHelper) Defend(ctx context.Context, value common.Hash, opts ...MoveOpt) *ClaimHelper {
c.game.Defend(ctx, c.index, value, opts...) c.game.Defend(ctx, c.Index, value, opts...)
return c.WaitForCounterClaim(ctx) return c.WaitForCounterClaim(ctx)
} }
...@@ -115,19 +115,19 @@ func (c *ClaimHelper) RequireDifferentClaimValue(other *ClaimHelper) { ...@@ -115,19 +115,19 @@ func (c *ClaimHelper) RequireDifferentClaimValue(other *ClaimHelper) {
func (c *ClaimHelper) RequireOnlyCounteredBy(ctx context.Context, expected ...*ClaimHelper) { func (c *ClaimHelper) RequireOnlyCounteredBy(ctx context.Context, expected ...*ClaimHelper) {
claims := c.game.getAllClaims(ctx) claims := c.game.getAllClaims(ctx)
for idx, claim := range claims { for idx, claim := range claims {
if int64(claim.ParentIndex) != c.index { if int64(claim.ParentIndex) != c.Index {
// Doesn't counter this claim, so ignore // Doesn't counter this claim, so ignore
continue continue
} }
if !containsClaim(int64(idx), expected) { if !containsClaim(int64(idx), expected) {
// Found a countering claim not in the expected list. Fail. // Found a countering claim not in the expected list. Fail.
c.require.FailNowf("Found unexpected countering claim", "Parent claim index: %v Game state:\n%v", c.index, c.game.gameData(ctx)) c.require.FailNowf("Found unexpected countering claim", "Parent claim index: %v Game state:\n%v", c.Index, c.game.GameData(ctx))
} }
} }
} }
func containsClaim(claimIdx int64, haystack []*ClaimHelper) bool { func containsClaim(claimIdx int64, haystack []*ClaimHelper) bool {
return slices.ContainsFunc(haystack, func(candidate *ClaimHelper) bool { return slices.ContainsFunc(haystack, func(candidate *ClaimHelper) bool {
return candidate.index == claimIdx return candidate.Index == claimIdx
}) })
} }
...@@ -39,7 +39,7 @@ func (d *DishonestHelper) ExhaustDishonestClaims(ctx context.Context, rootClaim ...@@ -39,7 +39,7 @@ func (d *DishonestHelper) ExhaustDishonestClaims(ctx context.Context, rootClaim
} }
d.LogGameData(ctx) d.LogGameData(ctx)
d.OutputGameHelper.t.Logf("Dishonest moves against claimIndex %d", claimIndex) d.OutputGameHelper.T.Logf("Dishonest moves against claimIndex %d", claimIndex)
agreeWithLevel := d.defender == (pos.Depth()%2 == 0) agreeWithLevel := d.defender == (pos.Depth()%2 == 0)
if !agreeWithLevel { if !agreeWithLevel {
d.OutputHonestHelper.Attack(ctx, claimIndex, WithIgnoreDuplicates()) d.OutputHonestHelper.Attack(ctx, claimIndex, WithIgnoreDuplicates())
...@@ -53,7 +53,7 @@ func (d *DishonestHelper) ExhaustDishonestClaims(ctx context.Context, rootClaim ...@@ -53,7 +53,7 @@ func (d *DishonestHelper) ExhaustDishonestClaims(ctx context.Context, rootClaim
} }
} }
numClaimsSeen := rootClaim.index numClaimsSeen := rootClaim.Index
for { for {
// Use a short timeout since we don't know the challenger will respond, // Use a short timeout since we don't know the challenger will respond,
// and this is only designed for the alphabet game where the response should be fast. // and this is only designed for the alphabet game where the response should be fast.
...@@ -63,7 +63,7 @@ func (d *DishonestHelper) ExhaustDishonestClaims(ctx context.Context, rootClaim ...@@ -63,7 +63,7 @@ func (d *DishonestHelper) ExhaustDishonestClaims(ctx context.Context, rootClaim
// There's nothing to respond to. // There's nothing to respond to.
break break
} }
d.OutputGameHelper.require.NoError(err) d.OutputGameHelper.Require.NoError(err)
for ; numClaimsSeen < newCount; numClaimsSeen++ { for ; numClaimsSeen < newCount; numClaimsSeen++ {
claimData := d.getClaim(ctx, numClaimsSeen) claimData := d.getClaim(ctx, numClaimsSeen)
......
...@@ -64,27 +64,27 @@ func (s Status) String() string { ...@@ -64,27 +64,27 @@ func (s Status) String() string {
} }
} }
type gameCfg struct { type GameCfg struct {
allowFuture bool allowFuture bool
allowUnsafe bool allowUnsafe bool
} }
type GameOpt interface { type GameOpt interface {
Apply(cfg *gameCfg) Apply(cfg *GameCfg)
} }
type gameOptFn func(c *gameCfg) type gameOptFn func(c *GameCfg)
func (g gameOptFn) Apply(cfg *gameCfg) { func (g gameOptFn) Apply(cfg *GameCfg) {
g(cfg) g(cfg)
} }
func WithUnsafeProposal() GameOpt { func WithUnsafeProposal() GameOpt {
return gameOptFn(func(c *gameCfg) { return gameOptFn(func(c *GameCfg) {
c.allowUnsafe = true c.allowUnsafe = true
}) })
} }
func WithFutureProposal() GameOpt { func WithFutureProposal() GameOpt {
return gameOptFn(func(c *gameCfg) { return gameOptFn(func(c *GameCfg) {
c.allowFuture = true c.allowFuture = true
}) })
} }
...@@ -104,13 +104,13 @@ type DisputeSystem interface { ...@@ -104,13 +104,13 @@ type DisputeSystem interface {
} }
type FactoryHelper struct { type FactoryHelper struct {
t *testing.T T *testing.T
require *require.Assertions Require *require.Assertions
system DisputeSystem System DisputeSystem
client *ethclient.Client Client *ethclient.Client
opts *bind.TransactOpts Opts *bind.TransactOpts
factoryAddr common.Address FactoryAddr common.Address
factory *bindings.DisputeGameFactory Factory *bindings.DisputeGameFactory
} }
func NewFactoryHelper(t *testing.T, ctx context.Context, system DisputeSystem) *FactoryHelper { func NewFactoryHelper(t *testing.T, ctx context.Context, system DisputeSystem) *FactoryHelper {
...@@ -127,33 +127,33 @@ func NewFactoryHelper(t *testing.T, ctx context.Context, system DisputeSystem) * ...@@ -127,33 +127,33 @@ func NewFactoryHelper(t *testing.T, ctx context.Context, system DisputeSystem) *
require.NoError(err) require.NoError(err)
return &FactoryHelper{ return &FactoryHelper{
t: t, T: t,
require: require, Require: require,
system: system, System: system,
client: client, Client: client,
opts: opts, Opts: opts,
factory: factory, Factory: factory,
factoryAddr: factoryAddr, FactoryAddr: factoryAddr,
} }
} }
func (h *FactoryHelper) PreimageHelper(ctx context.Context) *preimage.Helper { func (h *FactoryHelper) PreimageHelper(ctx context.Context) *preimage.Helper {
opts := &bind.CallOpts{Context: ctx} opts := &bind.CallOpts{Context: ctx}
gameAddr, err := h.factory.GameImpls(opts, cannonGameType) gameAddr, err := h.Factory.GameImpls(opts, cannonGameType)
h.require.NoError(err) h.Require.NoError(err)
game, err := bindings.NewFaultDisputeGameCaller(gameAddr, h.client) game, err := bindings.NewFaultDisputeGameCaller(gameAddr, h.Client)
h.require.NoError(err) h.Require.NoError(err)
vmAddr, err := game.Vm(opts) vmAddr, err := game.Vm(opts)
h.require.NoError(err) h.Require.NoError(err)
vm, err := bindings.NewMIPSCaller(vmAddr, h.client) vm, err := bindings.NewMIPSCaller(vmAddr, h.Client)
h.require.NoError(err) h.Require.NoError(err)
oracleAddr, err := vm.Oracle(opts) oracleAddr, err := vm.Oracle(opts)
h.require.NoError(err) h.Require.NoError(err)
return preimage.NewHelper(h.t, h.opts, h.client, oracleAddr) return preimage.NewHelper(h.T, h.Opts, h.Client, oracleAddr)
} }
func newGameCfg(opts ...GameOpt) *gameCfg { func NewGameCfg(opts ...GameOpt) *GameCfg {
cfg := &gameCfg{} cfg := &GameCfg{}
for _, opt := range opts { for _, opt := range opts {
opt.Apply(cfg) opt.Apply(cfg)
} }
...@@ -161,159 +161,139 @@ func newGameCfg(opts ...GameOpt) *gameCfg { ...@@ -161,159 +161,139 @@ func newGameCfg(opts ...GameOpt) *gameCfg {
} }
func (h *FactoryHelper) StartOutputCannonGameWithCorrectRoot(ctx context.Context, l2Node string, l2BlockNumber uint64, opts ...GameOpt) *OutputCannonGameHelper { func (h *FactoryHelper) StartOutputCannonGameWithCorrectRoot(ctx context.Context, l2Node string, l2BlockNumber uint64, opts ...GameOpt) *OutputCannonGameHelper {
cfg := newGameCfg(opts...) cfg := NewGameCfg(opts...)
h.waitForBlock(l2Node, l2BlockNumber, cfg) h.WaitForBlock(l2Node, l2BlockNumber, cfg)
output, err := h.system.RollupClient(l2Node).OutputAtBlock(ctx, l2BlockNumber) output, err := h.System.RollupClient(l2Node).OutputAtBlock(ctx, l2BlockNumber)
h.require.NoErrorf(err, "Failed to get output at block %v", l2BlockNumber) h.Require.NoErrorf(err, "Failed to get output at block %v", l2BlockNumber)
return h.StartOutputCannonGame(ctx, l2Node, l2BlockNumber, common.Hash(output.OutputRoot), opts...) return h.StartOutputCannonGame(ctx, l2Node, l2BlockNumber, common.Hash(output.OutputRoot), opts...)
} }
func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, l2Node string, l2BlockNumber uint64, rootClaim common.Hash, opts ...GameOpt) *OutputCannonGameHelper { func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, l2Node string, l2BlockNumber uint64, rootClaim common.Hash, opts ...GameOpt) *OutputCannonGameHelper {
cfg := newGameCfg(opts...) cfg := NewGameCfg(opts...)
logger := testlog.Logger(h.t, log.LevelInfo).New("role", "OutputCannonGameHelper") logger := testlog.Logger(h.T, log.LevelInfo).New("role", "OutputCannonGameHelper")
rollupClient := h.system.RollupClient(l2Node) rollupClient := h.System.RollupClient(l2Node)
extraData := h.createBisectionGameExtraData(l2Node, l2BlockNumber, cfg) extraData := h.CreateBisectionGameExtraData(l2Node, l2BlockNumber, cfg)
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute) ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
defer cancel() defer cancel()
tx, err := transactions.PadGasEstimate(h.opts, 2, func(opts *bind.TransactOpts) (*types.Transaction, error) { tx, err := transactions.PadGasEstimate(h.Opts, 2, func(opts *bind.TransactOpts) (*types.Transaction, error) {
return h.factory.Create(opts, cannonGameType, rootClaim, extraData) return h.Factory.Create(opts, cannonGameType, rootClaim, extraData)
}) })
h.require.NoError(err, "create fault dispute game") h.Require.NoError(err, "create fault dispute game")
rcpt, err := wait.ForReceiptOK(ctx, h.client, tx.Hash()) rcpt, err := wait.ForReceiptOK(ctx, h.Client, tx.Hash())
h.require.NoError(err, "wait for create fault dispute game receipt to be OK") h.Require.NoError(err, "wait for create fault dispute game receipt to be OK")
h.require.Len(rcpt.Logs, 2, "should have emitted a single DisputeGameCreated event") h.Require.Len(rcpt.Logs, 2, "should have emitted a single DisputeGameCreated event")
createdEvent, err := h.factory.ParseDisputeGameCreated(*rcpt.Logs[1]) createdEvent, err := h.Factory.ParseDisputeGameCreated(*rcpt.Logs[1])
h.require.NoError(err) h.Require.NoError(err)
game, err := bindings.NewFaultDisputeGame(createdEvent.DisputeProxy, h.client) game, err := bindings.NewFaultDisputeGame(createdEvent.DisputeProxy, h.Client)
h.require.NoError(err) h.Require.NoError(err)
callOpts := &bind.CallOpts{Context: ctx} callOpts := &bind.CallOpts{Context: ctx}
prestateBlock, err := game.StartingBlockNumber(callOpts) prestateBlock, err := game.StartingBlockNumber(callOpts)
h.require.NoError(err, "Failed to load starting block number") h.Require.NoError(err, "Failed to load starting block number")
poststateBlock, err := game.L2BlockNumber(callOpts) poststateBlock, err := game.L2BlockNumber(callOpts)
h.require.NoError(err, "Failed to load l2 block number") h.Require.NoError(err, "Failed to load l2 block number")
splitDepth, err := game.SplitDepth(callOpts) splitDepth, err := game.SplitDepth(callOpts)
h.require.NoError(err, "Failed to load split depth") h.Require.NoError(err, "Failed to load split depth")
l1Head := h.getL1Head(ctx, game) l1Head := h.GetL1Head(ctx, game)
prestateProvider := outputs.NewPrestateProvider(rollupClient, prestateBlock.Uint64()) prestateProvider := outputs.NewPrestateProvider(rollupClient, prestateBlock.Uint64())
provider := outputs.NewTraceProvider(logger, prestateProvider, rollupClient, l1Head, faultTypes.Depth(splitDepth.Uint64()), prestateBlock.Uint64(), poststateBlock.Uint64()) provider := outputs.NewTraceProvider(logger, prestateProvider, rollupClient, l1Head, faultTypes.Depth(splitDepth.Uint64()), prestateBlock.Uint64(), poststateBlock.Uint64())
return &OutputCannonGameHelper{ return &OutputCannonGameHelper{
OutputGameHelper: OutputGameHelper{ OutputGameHelper: *NewOutputGameHelper(h.T, h.Require, h.Client, h.Opts, game, h.FactoryAddr, createdEvent.DisputeProxy, provider, h.System),
t: h.t,
require: h.require,
client: h.client,
opts: h.opts,
game: game,
factoryAddr: h.factoryAddr,
addr: createdEvent.DisputeProxy,
correctOutputProvider: provider,
system: h.system,
},
} }
} }
func (h *FactoryHelper) getL1Head(ctx context.Context, game *bindings.FaultDisputeGame) eth.BlockID { func (h *FactoryHelper) GetL1Head(ctx context.Context, game *bindings.FaultDisputeGame) eth.BlockID {
l1HeadHash, err := game.L1Head(&bind.CallOpts{Context: ctx}) l1HeadHash, err := game.L1Head(&bind.CallOpts{Context: ctx})
h.require.NoError(err, "Failed to load L1 head") h.Require.NoError(err, "Failed to load L1 head")
l1Header, err := h.client.HeaderByHash(ctx, l1HeadHash) l1Header, err := h.Client.HeaderByHash(ctx, l1HeadHash)
h.require.NoError(err, "Failed to load L1 header") h.Require.NoError(err, "Failed to load L1 header")
l1Head := eth.HeaderBlockID(l1Header) l1Head := eth.HeaderBlockID(l1Header)
return l1Head return l1Head
} }
func (h *FactoryHelper) StartOutputAlphabetGameWithCorrectRoot(ctx context.Context, l2Node string, l2BlockNumber uint64, opts ...GameOpt) *OutputAlphabetGameHelper { func (h *FactoryHelper) StartOutputAlphabetGameWithCorrectRoot(ctx context.Context, l2Node string, l2BlockNumber uint64, opts ...GameOpt) *OutputAlphabetGameHelper {
cfg := newGameCfg(opts...) cfg := NewGameCfg(opts...)
h.waitForBlock(l2Node, l2BlockNumber, cfg) h.WaitForBlock(l2Node, l2BlockNumber, cfg)
output, err := h.system.RollupClient(l2Node).OutputAtBlock(ctx, l2BlockNumber) output, err := h.System.RollupClient(l2Node).OutputAtBlock(ctx, l2BlockNumber)
h.require.NoErrorf(err, "Failed to get output at block %v", l2BlockNumber) h.Require.NoErrorf(err, "Failed to get output at block %v", l2BlockNumber)
return h.StartOutputAlphabetGame(ctx, l2Node, l2BlockNumber, common.Hash(output.OutputRoot)) return h.StartOutputAlphabetGame(ctx, l2Node, l2BlockNumber, common.Hash(output.OutputRoot))
} }
func (h *FactoryHelper) StartOutputAlphabetGame(ctx context.Context, l2Node string, l2BlockNumber uint64, rootClaim common.Hash, opts ...GameOpt) *OutputAlphabetGameHelper { func (h *FactoryHelper) StartOutputAlphabetGame(ctx context.Context, l2Node string, l2BlockNumber uint64, rootClaim common.Hash, opts ...GameOpt) *OutputAlphabetGameHelper {
cfg := newGameCfg(opts...) cfg := NewGameCfg(opts...)
logger := testlog.Logger(h.t, log.LevelInfo).New("role", "OutputAlphabetGameHelper") logger := testlog.Logger(h.T, log.LevelInfo).New("role", "OutputAlphabetGameHelper")
rollupClient := h.system.RollupClient(l2Node) rollupClient := h.System.RollupClient(l2Node)
extraData := h.createBisectionGameExtraData(l2Node, l2BlockNumber, cfg) extraData := h.CreateBisectionGameExtraData(l2Node, l2BlockNumber, cfg)
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute) ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
defer cancel() defer cancel()
tx, err := transactions.PadGasEstimate(h.opts, 2, func(opts *bind.TransactOpts) (*types.Transaction, error) { tx, err := transactions.PadGasEstimate(h.Opts, 2, func(opts *bind.TransactOpts) (*types.Transaction, error) {
return h.factory.Create(opts, alphabetGameType, rootClaim, extraData) return h.Factory.Create(opts, alphabetGameType, rootClaim, extraData)
}) })
h.require.NoError(err, "create output bisection game") h.Require.NoError(err, "create output bisection game")
rcpt, err := wait.ForReceiptOK(ctx, h.client, tx.Hash()) rcpt, err := wait.ForReceiptOK(ctx, h.Client, tx.Hash())
h.require.NoError(err, "wait for create output bisection game receipt to be OK") h.Require.NoError(err, "wait for create output bisection game receipt to be OK")
h.require.Len(rcpt.Logs, 2, "should have emitted a single DisputeGameCreated event") h.Require.Len(rcpt.Logs, 2, "should have emitted a single DisputeGameCreated event")
createdEvent, err := h.factory.ParseDisputeGameCreated(*rcpt.Logs[1]) createdEvent, err := h.Factory.ParseDisputeGameCreated(*rcpt.Logs[1])
h.require.NoError(err) h.Require.NoError(err)
game, err := bindings.NewFaultDisputeGame(createdEvent.DisputeProxy, h.client) game, err := bindings.NewFaultDisputeGame(createdEvent.DisputeProxy, h.Client)
h.require.NoError(err) h.Require.NoError(err)
callOpts := &bind.CallOpts{Context: ctx} callOpts := &bind.CallOpts{Context: ctx}
prestateBlock, err := game.StartingBlockNumber(callOpts) prestateBlock, err := game.StartingBlockNumber(callOpts)
h.require.NoError(err, "Failed to load starting block number") h.Require.NoError(err, "Failed to load starting block number")
poststateBlock, err := game.L2BlockNumber(callOpts) poststateBlock, err := game.L2BlockNumber(callOpts)
h.require.NoError(err, "Failed to load l2 block number") h.Require.NoError(err, "Failed to load l2 block number")
splitDepth, err := game.SplitDepth(callOpts) splitDepth, err := game.SplitDepth(callOpts)
h.require.NoError(err, "Failed to load split depth") h.Require.NoError(err, "Failed to load split depth")
l1Head := h.getL1Head(ctx, game) l1Head := h.GetL1Head(ctx, game)
prestateProvider := outputs.NewPrestateProvider(rollupClient, prestateBlock.Uint64()) prestateProvider := outputs.NewPrestateProvider(rollupClient, prestateBlock.Uint64())
provider := outputs.NewTraceProvider(logger, prestateProvider, rollupClient, l1Head, faultTypes.Depth(splitDepth.Uint64()), prestateBlock.Uint64(), poststateBlock.Uint64()) provider := outputs.NewTraceProvider(logger, prestateProvider, rollupClient, l1Head, faultTypes.Depth(splitDepth.Uint64()), prestateBlock.Uint64(), poststateBlock.Uint64())
return &OutputAlphabetGameHelper{ return &OutputAlphabetGameHelper{
OutputGameHelper: OutputGameHelper{ OutputGameHelper: *NewOutputGameHelper(h.T, h.Require, h.Client, h.Opts, game, h.FactoryAddr, createdEvent.DisputeProxy, provider, h.System),
t: h.t,
require: h.require,
client: h.client,
opts: h.opts,
game: game,
factoryAddr: h.factoryAddr,
addr: createdEvent.DisputeProxy,
correctOutputProvider: provider,
system: h.system,
},
} }
} }
func (h *FactoryHelper) createBisectionGameExtraData(l2Node string, l2BlockNumber uint64, cfg *gameCfg) []byte { func (h *FactoryHelper) CreateBisectionGameExtraData(l2Node string, l2BlockNumber uint64, cfg *GameCfg) []byte {
h.waitForBlock(l2Node, l2BlockNumber, cfg) h.WaitForBlock(l2Node, l2BlockNumber, cfg)
h.t.Logf("Creating game with l2 block number: %v", l2BlockNumber) h.T.Logf("Creating game with l2 block number: %v", l2BlockNumber)
extraData := make([]byte, 32) extraData := make([]byte, 32)
binary.BigEndian.PutUint64(extraData[24:], l2BlockNumber) binary.BigEndian.PutUint64(extraData[24:], l2BlockNumber)
return extraData return extraData
} }
func (h *FactoryHelper) waitForBlock(l2Node string, l2BlockNumber uint64, cfg *gameCfg) { func (h *FactoryHelper) WaitForBlock(l2Node string, l2BlockNumber uint64, cfg *GameCfg) {
if cfg.allowFuture { if cfg.allowFuture {
// Proposing a block that doesn't exist yet, so don't perform any checks // Proposing a block that doesn't exist yet, so don't perform any checks
return return
} }
l2Client := h.system.NodeClient(l2Node) l2Client := h.System.NodeClient(l2Node)
if cfg.allowUnsafe { if cfg.allowUnsafe {
_, err := geth.WaitForBlock(new(big.Int).SetUint64(l2BlockNumber), l2Client, 1*time.Minute) _, err := geth.WaitForBlock(new(big.Int).SetUint64(l2BlockNumber), l2Client, 1*time.Minute)
h.require.NoErrorf(err, "Block number %v did not become unsafe", l2BlockNumber) h.Require.NoErrorf(err, "Block number %v did not become unsafe", l2BlockNumber)
} else { } else {
_, err := geth.WaitForBlockToBeSafe(new(big.Int).SetUint64(l2BlockNumber), l2Client, 1*time.Minute) _, err := geth.WaitForBlockToBeSafe(new(big.Int).SetUint64(l2BlockNumber), l2Client, 1*time.Minute)
h.require.NoErrorf(err, "Block number %v did not become safe", l2BlockNumber) h.Require.NoErrorf(err, "Block number %v did not become safe", l2BlockNumber)
} }
} }
func (h *FactoryHelper) StartChallenger(ctx context.Context, name string, options ...challenger.Option) *challenger.Helper { func (h *FactoryHelper) StartChallenger(ctx context.Context, name string, options ...challenger.Option) *challenger.Helper {
opts := []challenger.Option{ opts := []challenger.Option{
challenger.WithFactoryAddress(h.factoryAddr), challenger.WithFactoryAddress(h.FactoryAddr),
} }
opts = append(opts, options...) opts = append(opts, options...)
c := challenger.NewChallenger(h.t, ctx, h.system, name, opts...) c := challenger.NewChallenger(h.T, ctx, h.System, name, opts...)
h.t.Cleanup(func() { h.T.Cleanup(func() {
_ = c.Close() _ = c.Close()
}) })
return c return c
......
...@@ -24,37 +24,31 @@ func (g *OutputAlphabetGameHelper) StartChallenger( ...@@ -24,37 +24,31 @@ func (g *OutputAlphabetGameHelper) StartChallenger(
options ...challenger.Option, options ...challenger.Option,
) *challenger.Helper { ) *challenger.Helper {
opts := []challenger.Option{ opts := []challenger.Option{
challenger.WithAlphabet(g.system.RollupEndpoint(l2Node)), challenger.WithAlphabet(g.System.RollupEndpoint(l2Node)),
challenger.WithFactoryAddress(g.factoryAddr), challenger.WithFactoryAddress(g.FactoryAddr),
challenger.WithGameAddress(g.addr), challenger.WithGameAddress(g.Addr),
} }
opts = append(opts, options...) opts = append(opts, options...)
c := challenger.NewChallenger(g.t, ctx, g.system, name, opts...) c := challenger.NewChallenger(g.T, ctx, g.System, name, opts...)
g.t.Cleanup(func() { g.T.Cleanup(func() {
_ = c.Close() _ = c.Close()
}) })
return c return c
} }
func (g *OutputAlphabetGameHelper) CreateHonestActor(ctx context.Context, l2Node string) *OutputHonestHelper { func (g *OutputAlphabetGameHelper) CreateHonestActor(ctx context.Context, l2Node string) *OutputHonestHelper {
logger := testlog.Logger(g.t, log.LevelInfo).New("role", "HonestHelper", "game", g.addr) logger := testlog.Logger(g.T, log.LevelInfo).New("role", "HonestHelper", "game", g.Addr)
caller := batching.NewMultiCaller(g.system.NodeClient("l1").Client(), batching.DefaultBatchSize) caller := batching.NewMultiCaller(g.System.NodeClient("l1").Client(), batching.DefaultBatchSize)
contract := contracts.NewFaultDisputeGameContract(contractMetrics.NoopContractMetrics, g.addr, caller) contract := contracts.NewFaultDisputeGameContract(contractMetrics.NoopContractMetrics, g.Addr, caller)
prestateBlock, poststateBlock, err := contract.GetBlockRange(ctx) prestateBlock, poststateBlock, err := contract.GetBlockRange(ctx)
g.require.NoError(err, "Get block range") g.Require.NoError(err, "Get block range")
splitDepth := g.SplitDepth(ctx) splitDepth := g.SplitDepth(ctx)
l1Head := g.getL1Head(ctx) l1Head := g.GetL1Head(ctx)
rollupClient := g.system.RollupClient(l2Node) rollupClient := g.System.RollupClient(l2Node)
prestateProvider := outputs.NewPrestateProvider(rollupClient, prestateBlock) prestateProvider := outputs.NewPrestateProvider(rollupClient, prestateBlock)
correctTrace, err := outputs.NewOutputAlphabetTraceAccessor(logger, metrics.NoopMetrics, prestateProvider, rollupClient, l1Head, splitDepth, prestateBlock, poststateBlock) correctTrace, err := outputs.NewOutputAlphabetTraceAccessor(logger, metrics.NoopMetrics, prestateProvider, rollupClient, l1Head, splitDepth, prestateBlock, poststateBlock)
g.require.NoError(err, "Create trace accessor") g.Require.NoError(err, "Create trace accessor")
return &OutputHonestHelper{ return NewOutputHonestHelper(g.T, g.Require, &g.OutputGameHelper, contract, correctTrace)
t: g.t,
require: g.require,
game: &g.OutputGameHelper,
contract: contract,
correctTrace: correctTrace,
}
} }
func (g *OutputAlphabetGameHelper) CreateDishonestHelper(ctx context.Context, l2Node string, defender bool) *DishonestHelper { func (g *OutputAlphabetGameHelper) CreateDishonestHelper(ctx context.Context, l2Node string, defender bool) *DishonestHelper {
......
...@@ -40,16 +40,16 @@ func (g *OutputCannonGameHelper) StartChallenger( ...@@ -40,16 +40,16 @@ func (g *OutputCannonGameHelper) StartChallenger(
name string, name string,
options ...challenger.Option, options ...challenger.Option,
) *challenger.Helper { ) *challenger.Helper {
rollupEndpoint := g.system.RollupEndpoint(l2Node) rollupEndpoint := g.System.RollupEndpoint(l2Node)
l2Endpoint := g.system.NodeEndpoint(l2Node) l2Endpoint := g.System.NodeEndpoint(l2Node)
opts := []challenger.Option{ opts := []challenger.Option{
challenger.WithCannon(g.t, g.system.RollupCfg(), g.system.L2Genesis(), rollupEndpoint, l2Endpoint), challenger.WithCannon(g.T, g.System.RollupCfg(), g.System.L2Genesis(), rollupEndpoint, l2Endpoint),
challenger.WithFactoryAddress(g.factoryAddr), challenger.WithFactoryAddress(g.FactoryAddr),
challenger.WithGameAddress(g.addr), challenger.WithGameAddress(g.Addr),
} }
opts = append(opts, options...) opts = append(opts, options...)
c := challenger.NewChallenger(g.t, ctx, g.system, name, opts...) c := challenger.NewChallenger(g.T, ctx, g.System, name, opts...)
g.t.Cleanup(func() { g.T.Cleanup(func() {
_ = c.Close() _ = c.Close()
}) })
return c return c
...@@ -58,30 +58,24 @@ func (g *OutputCannonGameHelper) StartChallenger( ...@@ -58,30 +58,24 @@ func (g *OutputCannonGameHelper) StartChallenger(
func (g *OutputCannonGameHelper) CreateHonestActor(ctx context.Context, l2Node string, options ...challenger.Option) *OutputHonestHelper { func (g *OutputCannonGameHelper) CreateHonestActor(ctx context.Context, l2Node string, options ...challenger.Option) *OutputHonestHelper {
opts := g.defaultChallengerOptions(l2Node) opts := g.defaultChallengerOptions(l2Node)
opts = append(opts, options...) opts = append(opts, options...)
cfg := challenger.NewChallengerConfig(g.t, g.system, opts...) cfg := challenger.NewChallengerConfig(g.T, g.System, opts...)
logger := testlog.Logger(g.t, log.LevelInfo).New("role", "HonestHelper", "game", g.addr) logger := testlog.Logger(g.T, log.LevelInfo).New("role", "HonestHelper", "game", g.Addr)
l2Client := g.system.NodeClient(l2Node) l2Client := g.System.NodeClient(l2Node)
caller := batching.NewMultiCaller(g.system.NodeClient("l1").Client(), batching.DefaultBatchSize) caller := batching.NewMultiCaller(g.System.NodeClient("l1").Client(), batching.DefaultBatchSize)
contract := contracts.NewFaultDisputeGameContract(contractMetrics.NoopContractMetrics, g.addr, caller) contract := contracts.NewFaultDisputeGameContract(contractMetrics.NoopContractMetrics, g.Addr, caller)
prestateBlock, poststateBlock, err := contract.GetBlockRange(ctx) prestateBlock, poststateBlock, err := contract.GetBlockRange(ctx)
g.require.NoError(err, "Failed to load block range") g.Require.NoError(err, "Failed to load block range")
dir := filepath.Join(cfg.Datadir, "honest") dir := filepath.Join(cfg.Datadir, "honest")
splitDepth := g.SplitDepth(ctx) splitDepth := g.SplitDepth(ctx)
rollupClient := g.system.RollupClient(l2Node) rollupClient := g.System.RollupClient(l2Node)
prestateProvider := outputs.NewPrestateProvider(rollupClient, prestateBlock) prestateProvider := outputs.NewPrestateProvider(rollupClient, prestateBlock)
l1Head := g.getL1Head(ctx) l1Head := g.GetL1Head(ctx)
accessor, err := outputs.NewOutputCannonTraceAccessor( accessor, err := outputs.NewOutputCannonTraceAccessor(
logger, metrics.NoopMetrics, cfg, l2Client, prestateProvider, rollupClient, dir, l1Head, splitDepth, prestateBlock, poststateBlock) logger, metrics.NoopMetrics, cfg, l2Client, prestateProvider, rollupClient, dir, l1Head, splitDepth, prestateBlock, poststateBlock)
g.require.NoError(err, "Failed to create output cannon trace accessor") g.Require.NoError(err, "Failed to create output cannon trace accessor")
return &OutputHonestHelper{ return NewOutputHonestHelper(g.T, g.Require, &g.OutputGameHelper, contract, accessor)
t: g.t,
require: g.require,
game: &g.OutputGameHelper,
contract: contract,
correctTrace: accessor,
}
} }
type PreimageLoadCheck func(types.TraceProvider, uint64) error type PreimageLoadCheck func(types.TraceProvider, uint64) error
...@@ -94,7 +88,7 @@ func (g *OutputCannonGameHelper) CreateStepLargePreimageLoadCheck(ctx context.Co ...@@ -94,7 +88,7 @@ func (g *OutputCannonGameHelper) CreateStepLargePreimageLoadCheck(ctx context.Co
// Get the preimage data // Get the preimage data
execDepth := g.ExecDepth(ctx) execDepth := g.ExecDepth(ctx)
_, _, preimageData, err := provider.GetStepData(ctx, types.NewPosition(execDepth, big.NewInt(int64(targetTraceIndex)))) _, _, preimageData, err := provider.GetStepData(ctx, types.NewPosition(execDepth, big.NewInt(int64(targetTraceIndex))))
g.require.NoError(err) g.Require.NoError(err)
// Wait until the challenge period has started by checking until the challenge // Wait until the challenge period has started by checking until the challenge
// period start time is not zero by calling the ChallengePeriodStartTime method // period start time is not zero by calling the ChallengePeriodStartTime method
...@@ -104,11 +98,11 @@ func (g *OutputCannonGameHelper) CreateStepLargePreimageLoadCheck(ctx context.Co ...@@ -104,11 +98,11 @@ func (g *OutputCannonGameHelper) CreateStepLargePreimageLoadCheck(ctx context.Co
challengePeriodEnd := challengePeriodStart + challengePeriod challengePeriodEnd := challengePeriodStart + challengePeriod
// Time travel past the challenge period. // Time travel past the challenge period.
g.system.AdvanceTime(time.Duration(challengePeriod) * time.Second) g.System.AdvanceTime(time.Duration(challengePeriod) * time.Second)
g.require.NoError(wait.ForBlockWithTimestamp(ctx, g.system.NodeClient("l1"), challengePeriodEnd)) g.Require.NoError(wait.ForBlockWithTimestamp(ctx, g.System.NodeClient("l1"), challengePeriodEnd))
// Assert that the preimage was indeed loaded by an honest challenger // Assert that the preimage was indeed loaded by an honest challenger
g.waitForPreimageInOracle(ctx, preimageData) g.WaitForPreimageInOracle(ctx, preimageData)
return nil return nil
} }
} }
...@@ -117,8 +111,8 @@ func (g *OutputCannonGameHelper) CreateStepPreimageLoadCheck(ctx context.Context ...@@ -117,8 +111,8 @@ func (g *OutputCannonGameHelper) CreateStepPreimageLoadCheck(ctx context.Context
return func(provider types.TraceProvider, targetTraceIndex uint64) error { return func(provider types.TraceProvider, targetTraceIndex uint64) error {
execDepth := g.ExecDepth(ctx) execDepth := g.ExecDepth(ctx)
_, _, preimageData, err := provider.GetStepData(ctx, types.NewPosition(execDepth, big.NewInt(int64(targetTraceIndex)))) _, _, preimageData, err := provider.GetStepData(ctx, types.NewPosition(execDepth, big.NewInt(int64(targetTraceIndex))))
g.require.NoError(err) g.Require.NoError(err)
g.waitForPreimageInOracle(ctx, preimageData) g.WaitForPreimageInOracle(ctx, preimageData)
return nil return nil
} }
} }
...@@ -133,49 +127,49 @@ func (g *OutputCannonGameHelper) ChallengeToPreimageLoad(ctx context.Context, ou ...@@ -133,49 +127,49 @@ func (g *OutputCannonGameHelper) ChallengeToPreimageLoad(ctx context.Context, ou
// Identifying the first state transition that loads a global preimage // Identifying the first state transition that loads a global preimage
provider, _ := g.createCannonTraceProvider(ctx, "sequencer", outputRootClaim, challenger.WithPrivKey(challengerKey)) provider, _ := g.createCannonTraceProvider(ctx, "sequencer", outputRootClaim, challenger.WithPrivKey(challengerKey))
targetTraceIndex, err := provider.FindStep(ctx, 0, preimage) targetTraceIndex, err := provider.FindStep(ctx, 0, preimage)
g.require.NoError(err) g.Require.NoError(err)
splitDepth := g.SplitDepth(ctx) splitDepth := g.SplitDepth(ctx)
execDepth := g.ExecDepth(ctx) execDepth := g.ExecDepth(ctx)
g.require.NotEqual(outputRootClaim.position.TraceIndex(execDepth).Uint64(), targetTraceIndex, "cannot move to defend a terminal trace index") g.Require.NotEqual(outputRootClaim.Position.TraceIndex(execDepth).Uint64(), targetTraceIndex, "cannot move to defend a terminal trace index")
g.require.EqualValues(splitDepth+1, outputRootClaim.Depth(), "supplied claim must be the root of an execution game") g.Require.EqualValues(splitDepth+1, outputRootClaim.Depth(), "supplied claim must be the root of an execution game")
g.require.EqualValues(execDepth%2, 1, "execution game depth must be odd") // since we're challenging the execution root claim g.Require.EqualValues(execDepth%2, 1, "execution game depth must be odd") // since we're challenging the execution root claim
if preloadPreimage { if preloadPreimage {
_, _, preimageData, err := provider.GetStepData(ctx, types.NewPosition(execDepth, big.NewInt(int64(targetTraceIndex)))) _, _, preimageData, err := provider.GetStepData(ctx, types.NewPosition(execDepth, big.NewInt(int64(targetTraceIndex))))
g.require.NoError(err) g.Require.NoError(err)
g.uploadPreimage(ctx, preimageData, challengerKey) g.UploadPreimage(ctx, preimageData, challengerKey)
g.waitForPreimageInOracle(ctx, preimageData) g.WaitForPreimageInOracle(ctx, preimageData)
} }
// Descending the execution game tree to reach the step that loads the preimage // Descending the execution game tree to reach the step that loads the preimage
bisectTraceIndex := func(claim *ClaimHelper) *ClaimHelper { bisectTraceIndex := func(claim *ClaimHelper) *ClaimHelper {
execClaimPosition, err := claim.position.RelativeToAncestorAtDepth(splitDepth + 1) execClaimPosition, err := claim.Position.RelativeToAncestorAtDepth(splitDepth + 1)
g.require.NoError(err) g.Require.NoError(err)
claimTraceIndex := execClaimPosition.TraceIndex(execDepth).Uint64() claimTraceIndex := execClaimPosition.TraceIndex(execDepth).Uint64()
g.t.Logf("Bisecting: Into targetTraceIndex %v: claimIndex=%v at depth=%v. claimPosition=%v execClaimPosition=%v claimTraceIndex=%v", g.T.Logf("Bisecting: Into targetTraceIndex %v: claimIndex=%v at depth=%v. claimPosition=%v execClaimPosition=%v claimTraceIndex=%v",
targetTraceIndex, claim.index, claim.Depth(), claim.position, execClaimPosition, claimTraceIndex) targetTraceIndex, claim.Index, claim.Depth(), claim.Position, execClaimPosition, claimTraceIndex)
// We always want to position ourselves such that the challenger generates proofs for the targetTraceIndex as prestate // We always want to position ourselves such that the challenger generates proofs for the targetTraceIndex as prestate
if execClaimPosition.Depth() == execDepth-1 { if execClaimPosition.Depth() == execDepth-1 {
if execClaimPosition.TraceIndex(execDepth).Uint64() == targetTraceIndex { if execClaimPosition.TraceIndex(execDepth).Uint64() == targetTraceIndex {
newPosition := execClaimPosition.Attack() newPosition := execClaimPosition.Attack()
correct, err := provider.Get(ctx, newPosition) correct, err := provider.Get(ctx, newPosition)
g.require.NoError(err) g.Require.NoError(err)
g.t.Logf("Bisecting: Attack correctly for step at newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth)) g.T.Logf("Bisecting: Attack correctly for step at newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
return claim.Attack(ctx, correct) return claim.Attack(ctx, correct)
} else if execClaimPosition.TraceIndex(execDepth).Uint64() > targetTraceIndex { } else if execClaimPosition.TraceIndex(execDepth).Uint64() > targetTraceIndex {
g.t.Logf("Bisecting: Attack incorrectly for step") g.T.Logf("Bisecting: Attack incorrectly for step")
return claim.Attack(ctx, common.Hash{0xdd}) return claim.Attack(ctx, common.Hash{0xdd})
} else if execClaimPosition.TraceIndex(execDepth).Uint64()+1 == targetTraceIndex { } else if execClaimPosition.TraceIndex(execDepth).Uint64()+1 == targetTraceIndex {
g.t.Logf("Bisecting: Defend incorrectly for step") g.T.Logf("Bisecting: Defend incorrectly for step")
return claim.Defend(ctx, common.Hash{0xcc}) return claim.Defend(ctx, common.Hash{0xcc})
} else { } else {
newPosition := execClaimPosition.Defend() newPosition := execClaimPosition.Defend()
correct, err := provider.Get(ctx, newPosition) correct, err := provider.Get(ctx, newPosition)
g.require.NoError(err) g.Require.NoError(err)
g.t.Logf("Bisecting: Defend correctly for step at newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth)) g.T.Logf("Bisecting: Defend correctly for step at newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
return claim.Defend(ctx, correct) return claim.Defend(ctx, correct)
} }
} }
...@@ -185,23 +179,23 @@ func (g *OutputCannonGameHelper) ChallengeToPreimageLoad(ctx context.Context, ou ...@@ -185,23 +179,23 @@ func (g *OutputCannonGameHelper) ChallengeToPreimageLoad(ctx context.Context, ou
if execClaimPosition.TraceIndex(execDepth).Uint64() < targetTraceIndex && claim.Depth() != splitDepth+1 { if execClaimPosition.TraceIndex(execDepth).Uint64() < targetTraceIndex && claim.Depth() != splitDepth+1 {
newPosition := execClaimPosition.Defend() newPosition := execClaimPosition.Defend()
if newPosition.TraceIndex(execDepth).Uint64() < targetTraceIndex { if newPosition.TraceIndex(execDepth).Uint64() < targetTraceIndex {
g.t.Logf("Bisecting: Defend correct. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth)) g.T.Logf("Bisecting: Defend correct. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
correct, err := provider.Get(ctx, newPosition) correct, err := provider.Get(ctx, newPosition)
g.require.NoError(err) g.Require.NoError(err)
return claim.Defend(ctx, correct) return claim.Defend(ctx, correct)
} else { } else {
g.t.Logf("Bisecting: Defend incorrect. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth)) g.T.Logf("Bisecting: Defend incorrect. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
return claim.Defend(ctx, common.Hash{0xaa}) return claim.Defend(ctx, common.Hash{0xaa})
} }
} else { } else {
newPosition := execClaimPosition.Attack() newPosition := execClaimPosition.Attack()
if newPosition.TraceIndex(execDepth).Uint64() < targetTraceIndex { if newPosition.TraceIndex(execDepth).Uint64() < targetTraceIndex {
g.t.Logf("Bisecting: Attack correct. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth)) g.T.Logf("Bisecting: Attack correct. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
correct, err := provider.Get(ctx, newPosition) correct, err := provider.Get(ctx, newPosition)
g.require.NoError(err) g.Require.NoError(err)
return claim.Attack(ctx, correct) return claim.Attack(ctx, correct)
} else { } else {
g.t.Logf("Bisecting: Attack incorrect. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth)) g.T.Logf("Bisecting: Attack incorrect. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
return claim.Attack(ctx, common.Hash{0xbb}) return claim.Attack(ctx, common.Hash{0xbb})
} }
} }
...@@ -213,7 +207,7 @@ func (g *OutputCannonGameHelper) ChallengeToPreimageLoad(ctx context.Context, ou ...@@ -213,7 +207,7 @@ func (g *OutputCannonGameHelper) ChallengeToPreimageLoad(ctx context.Context, ou
leafClaim := g.DefendClaim(ctx, mover, bisectTraceIndex, WithoutWaitingForStep()) leafClaim := g.DefendClaim(ctx, mover, bisectTraceIndex, WithoutWaitingForStep())
// Validate that the preimage was loaded correctly // Validate that the preimage was loaded correctly
g.require.NoError(preimageCheck(provider, targetTraceIndex)) g.Require.NoError(preimageCheck(provider, targetTraceIndex))
// Now the preimage is available wait for the step call to succeed. // Now the preimage is available wait for the step call to succeed.
leafClaim.WaitForCountered(ctx) leafClaim.WaitForCountered(ctx)
...@@ -229,45 +223,45 @@ func (g *OutputCannonGameHelper) VerifyPreimage(ctx context.Context, outputRootC ...@@ -229,45 +223,45 @@ func (g *OutputCannonGameHelper) VerifyPreimage(ctx context.Context, outputRootC
found := false found := false
for offset := uint32(0); ; offset += 4 { for offset := uint32(0); ; offset += 4 {
preimageOpt := utils.PreimageLoad(preimageKey, offset) preimageOpt := utils.PreimageLoad(preimageKey, offset)
g.t.Logf("Searching for step with key %x and offset %v", preimageKey.PreimageKey(), offset) g.T.Logf("Searching for step with key %x and offset %v", preimageKey.PreimageKey(), offset)
targetTraceIndex, err := provider.FindStep(ctx, start, preimageOpt) targetTraceIndex, err := provider.FindStep(ctx, start, preimageOpt)
if errors.Is(err, io.EOF) { if errors.Is(err, io.EOF) {
// Did not find any more reads // Did not find any more reads
g.require.True(found, "Should have found at least one preimage read") g.Require.True(found, "Should have found at least one preimage read")
g.t.Logf("Searching for step with key %x and offset %v did not find another read", preimageKey.PreimageKey(), offset) g.T.Logf("Searching for step with key %x and offset %v did not find another read", preimageKey.PreimageKey(), offset)
return return
} }
g.require.NoError(err, "Failed to find step that loads requested preimage") g.Require.NoError(err, "Failed to find step that loads requested preimage")
start = targetTraceIndex start = targetTraceIndex
found = true found = true
g.t.Logf("Target trace index: %v", targetTraceIndex) g.T.Logf("Target trace index: %v", targetTraceIndex)
pos := types.NewPosition(execDepth, new(big.Int).SetUint64(targetTraceIndex)) pos := types.NewPosition(execDepth, new(big.Int).SetUint64(targetTraceIndex))
g.require.Equal(targetTraceIndex, pos.TraceIndex(execDepth).Uint64()) g.Require.Equal(targetTraceIndex, pos.TraceIndex(execDepth).Uint64())
prestate, proof, oracleData, err := provider.GetStepData(ctx, pos) prestate, proof, oracleData, err := provider.GetStepData(ctx, pos)
g.require.NoError(err, "Failed to get step data") g.Require.NoError(err, "Failed to get step data")
g.require.NotNil(oracleData, "Should have had required preimage oracle data") g.Require.NotNil(oracleData, "Should have had required preimage oracle data")
g.require.Equal(common.Hash(preimageKey.PreimageKey()).Bytes(), oracleData.OracleKey, "Must have correct preimage key") g.Require.Equal(common.Hash(preimageKey.PreimageKey()).Bytes(), oracleData.OracleKey, "Must have correct preimage key")
tx, err := g.game.AddLocalData(g.opts, tx, err := g.Game.AddLocalData(g.Opts,
oracleData.GetIdent(), oracleData.GetIdent(),
big.NewInt(outputRootClaim.index), big.NewInt(outputRootClaim.Index),
new(big.Int).SetUint64(uint64(oracleData.OracleOffset))) new(big.Int).SetUint64(uint64(oracleData.OracleOffset)))
g.require.NoError(err) g.Require.NoError(err)
_, err = wait.ForReceiptOK(ctx, g.client, tx.Hash()) _, err = wait.ForReceiptOK(ctx, g.Client, tx.Hash())
g.require.NoError(err) g.Require.NoError(err)
expectedPostState, err := provider.Get(ctx, pos) expectedPostState, err := provider.Get(ctx, pos)
g.require.NoError(err, "Failed to get expected post state") g.Require.NoError(err, "Failed to get expected post state")
callOpts := &bind.CallOpts{Context: ctx} callOpts := &bind.CallOpts{Context: ctx}
vmAddr, err := g.game.Vm(callOpts) vmAddr, err := g.Game.Vm(callOpts)
g.require.NoError(err, "Failed to get VM address") g.Require.NoError(err, "Failed to get VM address")
abi, err := bindings.MIPSMetaData.GetAbi() abi, err := bindings.MIPSMetaData.GetAbi()
g.require.NoError(err, "Failed to load MIPS ABI") g.Require.NoError(err, "Failed to load MIPS ABI")
caller := batching.NewMultiCaller(g.client.Client(), batching.DefaultBatchSize) caller := batching.NewMultiCaller(g.Client.Client(), batching.DefaultBatchSize)
result, err := caller.SingleCall(ctx, rpcblock.Latest, &batching.ContractCall{ result, err := caller.SingleCall(ctx, rpcblock.Latest, &batching.ContractCall{
Abi: abi, Abi: abi,
Addr: vmAddr, Addr: vmAddr,
...@@ -275,41 +269,41 @@ func (g *OutputCannonGameHelper) VerifyPreimage(ctx context.Context, outputRootC ...@@ -275,41 +269,41 @@ func (g *OutputCannonGameHelper) VerifyPreimage(ctx context.Context, outputRootC
Args: []interface{}{ Args: []interface{}{
prestate, proof, localContext, prestate, proof, localContext,
}, },
From: g.addr, From: g.Addr,
}) })
g.require.NoError(err, "Failed to call step") g.Require.NoError(err, "Failed to call step")
actualPostState := result.GetBytes32(0) actualPostState := result.GetBytes32(0)
g.require.Equal(expectedPostState, common.Hash(actualPostState)) g.Require.Equal(expectedPostState, common.Hash(actualPostState))
} }
} }
func (g *OutputCannonGameHelper) createCannonTraceProvider(ctx context.Context, l2Node string, outputRootClaim *ClaimHelper, options ...challenger.Option) (*cannon.CannonTraceProviderForTest, common.Hash) { func (g *OutputCannonGameHelper) createCannonTraceProvider(ctx context.Context, l2Node string, outputRootClaim *ClaimHelper, options ...challenger.Option) (*cannon.CannonTraceProviderForTest, common.Hash) {
splitDepth := g.SplitDepth(ctx) splitDepth := g.SplitDepth(ctx)
g.require.EqualValues(outputRootClaim.Depth(), splitDepth+1, "outputRootClaim must be the root of an execution game") g.Require.EqualValues(outputRootClaim.Depth(), splitDepth+1, "outputRootClaim must be the root of an execution game")
logger := testlog.Logger(g.t, log.LevelInfo).New("role", "CannonTraceProvider", "game", g.addr) logger := testlog.Logger(g.T, log.LevelInfo).New("role", "CannonTraceProvider", "game", g.Addr)
opt := g.defaultChallengerOptions(l2Node) opt := g.defaultChallengerOptions(l2Node)
opt = append(opt, options...) opt = append(opt, options...)
cfg := challenger.NewChallengerConfig(g.t, g.system, opt...) cfg := challenger.NewChallengerConfig(g.T, g.System, opt...)
caller := batching.NewMultiCaller(g.system.NodeClient("l1").Client(), batching.DefaultBatchSize) caller := batching.NewMultiCaller(g.System.NodeClient("l1").Client(), batching.DefaultBatchSize)
l2Client := g.system.NodeClient(l2Node) l2Client := g.System.NodeClient(l2Node)
contract := contracts.NewFaultDisputeGameContract(contractMetrics.NoopContractMetrics, g.addr, caller) contract := contracts.NewFaultDisputeGameContract(contractMetrics.NoopContractMetrics, g.Addr, caller)
prestateBlock, poststateBlock, err := contract.GetBlockRange(ctx) prestateBlock, poststateBlock, err := contract.GetBlockRange(ctx)
g.require.NoError(err, "Failed to load block range") g.Require.NoError(err, "Failed to load block range")
rollupClient := g.system.RollupClient(l2Node) rollupClient := g.System.RollupClient(l2Node)
prestateProvider := outputs.NewPrestateProvider(rollupClient, prestateBlock) prestateProvider := outputs.NewPrestateProvider(rollupClient, prestateBlock)
l1Head := g.getL1Head(ctx) l1Head := g.GetL1Head(ctx)
outputProvider := outputs.NewTraceProvider(logger, prestateProvider, rollupClient, l1Head, splitDepth, prestateBlock, poststateBlock) outputProvider := outputs.NewTraceProvider(logger, prestateProvider, rollupClient, l1Head, splitDepth, prestateBlock, poststateBlock)
var localContext common.Hash var localContext common.Hash
selector := split.NewSplitProviderSelector(outputProvider, splitDepth, func(ctx context.Context, depth types.Depth, pre types.Claim, post types.Claim) (types.TraceProvider, error) { selector := split.NewSplitProviderSelector(outputProvider, splitDepth, func(ctx context.Context, depth types.Depth, pre types.Claim, post types.Claim) (types.TraceProvider, error) {
agreed, disputed, err := outputs.FetchProposals(ctx, outputProvider, pre, post) agreed, disputed, err := outputs.FetchProposals(ctx, outputProvider, pre, post)
g.require.NoError(err) g.Require.NoError(err)
g.t.Logf("Using trace between blocks %v and %v\n", agreed.L2BlockNumber, disputed.L2BlockNumber) g.T.Logf("Using trace between blocks %v and %v\n", agreed.L2BlockNumber, disputed.L2BlockNumber)
localInputs, err := utils.FetchLocalInputsFromProposals(ctx, l1Head.Hash, l2Client, agreed, disputed) localInputs, err := utils.FetchLocalInputsFromProposals(ctx, l1Head.Hash, l2Client, agreed, disputed)
g.require.NoError(err, "Failed to fetch local inputs") g.Require.NoError(err, "Failed to fetch local inputs")
localContext = outputs.CreateLocalContext(pre, post) localContext = outputs.CreateLocalContext(pre, post)
dir := filepath.Join(cfg.Datadir, "cannon-trace") dir := filepath.Join(cfg.Datadir, "cannon-trace")
subdir := filepath.Join(dir, localContext.Hex()) subdir := filepath.Join(dir, localContext.Hex())
...@@ -317,19 +311,19 @@ func (g *OutputCannonGameHelper) createCannonTraceProvider(ctx context.Context, ...@@ -317,19 +311,19 @@ func (g *OutputCannonGameHelper) createCannonTraceProvider(ctx context.Context,
}) })
claims, err := contract.GetAllClaims(ctx, rpcblock.Latest) claims, err := contract.GetAllClaims(ctx, rpcblock.Latest)
g.require.NoError(err) g.Require.NoError(err)
game := types.NewGameState(claims, g.MaxDepth(ctx)) game := types.NewGameState(claims, g.MaxDepth(ctx))
provider, err := selector(ctx, game, game.Claims()[outputRootClaim.parentIndex], outputRootClaim.position) provider, err := selector(ctx, game, game.Claims()[outputRootClaim.ParentIndex], outputRootClaim.Position)
g.require.NoError(err) g.Require.NoError(err)
translatingProvider := provider.(*trace.TranslatingProvider) translatingProvider := provider.(*trace.TranslatingProvider)
return translatingProvider.Original().(*cannon.CannonTraceProviderForTest), localContext return translatingProvider.Original().(*cannon.CannonTraceProviderForTest), localContext
} }
func (g *OutputCannonGameHelper) defaultChallengerOptions(l2Node string) []challenger.Option { func (g *OutputCannonGameHelper) defaultChallengerOptions(l2Node string) []challenger.Option {
return []challenger.Option{ return []challenger.Option{
challenger.WithCannon(g.t, g.system.RollupCfg(), g.system.L2Genesis(), g.system.RollupEndpoint(l2Node), g.system.NodeEndpoint(l2Node)), challenger.WithCannon(g.T, g.System.RollupCfg(), g.System.L2Genesis(), g.System.RollupEndpoint(l2Node), g.System.NodeEndpoint(l2Node)),
challenger.WithFactoryAddress(g.factoryAddr), challenger.WithFactoryAddress(g.FactoryAddr),
challenger.WithGameAddress(g.addr), challenger.WithGameAddress(g.Addr),
} }
} }
...@@ -30,19 +30,34 @@ import ( ...@@ -30,19 +30,34 @@ import (
const defaultTimeout = 5 * time.Minute const defaultTimeout = 5 * time.Minute
type OutputGameHelper struct { type OutputGameHelper struct {
t *testing.T T *testing.T
require *require.Assertions Require *require.Assertions
client *ethclient.Client Client *ethclient.Client
opts *bind.TransactOpts Opts *bind.TransactOpts
game *bindings.FaultDisputeGame Game *bindings.FaultDisputeGame
factoryAddr common.Address FactoryAddr common.Address
addr common.Address Addr common.Address
correctOutputProvider *outputs.OutputTraceProvider CorrectOutputProvider *outputs.OutputTraceProvider
system DisputeSystem System DisputeSystem
}
func NewOutputGameHelper(t *testing.T, require *require.Assertions, client *ethclient.Client, opts *bind.TransactOpts,
game *bindings.FaultDisputeGame, factoryAddr common.Address, addr common.Address, correctOutputProvider *outputs.OutputTraceProvider, system DisputeSystem) *OutputGameHelper {
return &OutputGameHelper{
T: t,
Require: require,
Client: client,
Opts: opts,
Game: game,
FactoryAddr: factoryAddr,
Addr: addr,
CorrectOutputProvider: correctOutputProvider,
System: system,
}
} }
type moveCfg struct { type moveCfg struct {
opts *bind.TransactOpts Opts *bind.TransactOpts
ignoreDupes bool ignoreDupes bool
} }
...@@ -56,9 +71,9 @@ func (f moveOptFn) Apply(c *moveCfg) { ...@@ -56,9 +71,9 @@ func (f moveOptFn) Apply(c *moveCfg) {
f(c) f(c)
} }
func WithTransactOpts(opts *bind.TransactOpts) MoveOpt { func WithTransactOpts(Opts *bind.TransactOpts) MoveOpt {
return moveOptFn(func(c *moveCfg) { return moveOptFn(func(c *moveCfg) {
c.opts = opts c.Opts = Opts
}) })
} }
...@@ -68,13 +83,9 @@ func WithIgnoreDuplicates() MoveOpt { ...@@ -68,13 +83,9 @@ func WithIgnoreDuplicates() MoveOpt {
}) })
} }
func (g *OutputGameHelper) Addr() common.Address {
return g.addr
}
func (g *OutputGameHelper) SplitDepth(ctx context.Context) types.Depth { func (g *OutputGameHelper) SplitDepth(ctx context.Context) types.Depth {
splitDepth, err := g.game.SplitDepth(&bind.CallOpts{Context: ctx}) splitDepth, err := g.Game.SplitDepth(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "failed to load split depth") g.Require.NoError(err, "failed to load split depth")
return types.Depth(splitDepth.Uint64()) return types.Depth(splitDepth.Uint64())
} }
...@@ -83,14 +94,14 @@ func (g *OutputGameHelper) ExecDepth(ctx context.Context) types.Depth { ...@@ -83,14 +94,14 @@ func (g *OutputGameHelper) ExecDepth(ctx context.Context) types.Depth {
} }
func (g *OutputGameHelper) L2BlockNum(ctx context.Context) uint64 { func (g *OutputGameHelper) L2BlockNum(ctx context.Context) uint64 {
blockNum, err := g.game.L2BlockNumber(&bind.CallOpts{Context: ctx}) blockNum, err := g.Game.L2BlockNumber(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "failed to load l2 block number") g.Require.NoError(err, "failed to load l2 block number")
return blockNum.Uint64() return blockNum.Uint64()
} }
func (g *OutputGameHelper) StartingBlockNum(ctx context.Context) uint64 { func (g *OutputGameHelper) StartingBlockNum(ctx context.Context) uint64 {
blockNum, err := g.game.StartingBlockNumber(&bind.CallOpts{Context: ctx}) blockNum, err := g.Game.StartingBlockNumber(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "failed to load starting block number") g.Require.NoError(err, "failed to load starting block number")
return blockNum.Uint64() return blockNum.Uint64()
} }
...@@ -112,8 +123,8 @@ func (g *OutputGameHelper) DisputeBlock(ctx context.Context, disputeBlockNum uin ...@@ -112,8 +123,8 @@ func (g *OutputGameHelper) DisputeBlock(ctx context.Context, disputeBlockNum uin
} }
pos := types.NewPositionFromGIndex(big.NewInt(1)) pos := types.NewPositionFromGIndex(big.NewInt(1))
getClaimValue := func(parentClaim *ClaimHelper, claimPos types.Position) common.Hash { getClaimValue := func(parentClaim *ClaimHelper, claimPos types.Position) common.Hash {
claimBlockNum, err := g.correctOutputProvider.ClaimedBlockNumber(claimPos) claimBlockNum, err := g.CorrectOutputProvider.ClaimedBlockNumber(claimPos)
g.require.NoError(err, "failed to calculate claim block number") g.Require.NoError(err, "failed to calculate claim block number")
if claimBlockNum < disputeBlockNum { if claimBlockNum < disputeBlockNum {
// Use the correct output root for all claims prior to the dispute block number // Use the correct output root for all claims prior to the dispute block number
// This pushes the game to dispute the last block in the range // This pushes the game to dispute the last block in the range
...@@ -130,8 +141,8 @@ func (g *OutputGameHelper) DisputeBlock(ctx context.Context, disputeBlockNum uin ...@@ -130,8 +141,8 @@ func (g *OutputGameHelper) DisputeBlock(ctx context.Context, disputeBlockNum uin
claim := g.RootClaim(ctx) claim := g.RootClaim(ctx)
for !claim.IsOutputRootLeaf(ctx) { for !claim.IsOutputRootLeaf(ctx) {
parentClaimBlockNum, err := g.correctOutputProvider.ClaimedBlockNumber(pos) parentClaimBlockNum, err := g.CorrectOutputProvider.ClaimedBlockNumber(pos)
g.require.NoError(err, "failed to calculate parent claim block number") g.Require.NoError(err, "failed to calculate parent claim block number")
if parentClaimBlockNum >= disputeBlockNum { if parentClaimBlockNum >= disputeBlockNum {
pos = pos.Attack() pos = pos.Attack()
claim = claim.Attack(ctx, getClaimValue(claim, pos)) claim = claim.Attack(ctx, getClaimValue(claim, pos))
...@@ -152,18 +163,18 @@ func (g *OutputGameHelper) WaitForCorrectOutputRoot(ctx context.Context, claimId ...@@ -152,18 +163,18 @@ func (g *OutputGameHelper) WaitForCorrectOutputRoot(ctx context.Context, claimId
g.WaitForClaimCount(ctx, claimIdx+1) g.WaitForClaimCount(ctx, claimIdx+1)
claim := g.getClaim(ctx, claimIdx) claim := g.getClaim(ctx, claimIdx)
output := g.correctOutputRoot(ctx, types.NewPositionFromGIndex(claim.Position)) output := g.correctOutputRoot(ctx, types.NewPositionFromGIndex(claim.Position))
g.require.EqualValuesf(output, claim.Claim, "Incorrect output root at claim %v at position %v", claimIdx, claim.Position.Uint64()) g.Require.EqualValuesf(output, claim.Claim, "Incorrect output root at claim %v at position %v", claimIdx, claim.Position.Uint64())
} }
func (g *OutputGameHelper) correctOutputRoot(ctx context.Context, pos types.Position) common.Hash { func (g *OutputGameHelper) correctOutputRoot(ctx context.Context, pos types.Position) common.Hash {
outputRoot, err := g.correctOutputProvider.Get(ctx, pos) outputRoot, err := g.CorrectOutputProvider.Get(ctx, pos)
g.require.NoErrorf(err, "Failed to get correct output for position %v", pos) g.Require.NoErrorf(err, "Failed to get correct output for position %v", pos)
return outputRoot return outputRoot
} }
func (g *OutputGameHelper) MaxClockDuration(ctx context.Context) time.Duration { func (g *OutputGameHelper) MaxClockDuration(ctx context.Context) time.Duration {
duration, err := g.game.MaxClockDuration(&bind.CallOpts{Context: ctx}) duration, err := g.Game.MaxClockDuration(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "failed to get max clock duration") g.Require.NoError(err, "failed to get max clock duration")
return time.Duration(duration) * time.Second return time.Duration(duration) * time.Second
} }
...@@ -171,43 +182,43 @@ func (g *OutputGameHelper) WaitForNoAvailableCredit(ctx context.Context, addr co ...@@ -171,43 +182,43 @@ func (g *OutputGameHelper) WaitForNoAvailableCredit(ctx context.Context, addr co
timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout) timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel() defer cancel()
err := wait.For(timedCtx, time.Second, func() (bool, error) { err := wait.For(timedCtx, time.Second, func() (bool, error) {
bal, err := g.game.Credit(&bind.CallOpts{Context: timedCtx}, addr) bal, err := g.Game.Credit(&bind.CallOpts{Context: timedCtx}, addr)
if err != nil { if err != nil {
return false, err return false, err
} }
g.t.Log("Waiting for zero available credit", "current", bal, "addr", addr) g.T.Log("Waiting for zero available credit", "current", bal, "addr", addr)
return bal.Cmp(big.NewInt(0)) == 0, nil return bal.Cmp(big.NewInt(0)) == 0, nil
}) })
if err != nil { if err != nil {
g.LogGameData(ctx) g.LogGameData(ctx)
g.require.NoError(err, "Failed to wait for zero available credit") g.Require.NoError(err, "Failed to wait for zero available credit")
} }
} }
func (g *OutputGameHelper) AvailableCredit(ctx context.Context, addr common.Address) *big.Int { func (g *OutputGameHelper) AvailableCredit(ctx context.Context, addr common.Address) *big.Int {
credit, err := g.game.Credit(&bind.CallOpts{Context: ctx}, addr) credit, err := g.Game.Credit(&bind.CallOpts{Context: ctx}, addr)
g.require.NoErrorf(err, "Failed to fetch available credit for %v", addr) g.Require.NoErrorf(err, "Failed to fetch available credit for %v", addr)
return credit return credit
} }
func (g *OutputGameHelper) CreditUnlockDuration(ctx context.Context) time.Duration { func (g *OutputGameHelper) CreditUnlockDuration(ctx context.Context) time.Duration {
weth, err := g.game.Weth(&bind.CallOpts{Context: ctx}) weth, err := g.Game.Weth(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "Failed to get WETH contract") g.Require.NoError(err, "Failed to get WETH contract")
contract, err := bindings.NewDelayedWETH(weth, g.client) contract, err := bindings.NewDelayedWETH(weth, g.Client)
g.require.NoError(err) g.Require.NoError(err)
period, err := contract.Delay(&bind.CallOpts{Context: ctx}) period, err := contract.Delay(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "Failed to get WETH unlock period") g.Require.NoError(err, "Failed to get WETH unlock period")
float, _ := period.Float64() float, _ := period.Float64()
return time.Duration(float) * time.Second return time.Duration(float) * time.Second
} }
func (g *OutputGameHelper) WethBalance(ctx context.Context, addr common.Address) *big.Int { func (g *OutputGameHelper) WethBalance(ctx context.Context, addr common.Address) *big.Int {
weth, err := g.game.Weth(&bind.CallOpts{Context: ctx}) weth, err := g.Game.Weth(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "Failed to get WETH contract") g.Require.NoError(err, "Failed to get WETH contract")
contract, err := bindings.NewDelayedWETH(weth, g.client) contract, err := bindings.NewDelayedWETH(weth, g.Client)
g.require.NoError(err) g.Require.NoError(err)
balance, err := contract.BalanceOf(&bind.CallOpts{Context: ctx}, addr) balance, err := contract.BalanceOf(&bind.CallOpts{Context: ctx}, addr)
g.require.NoError(err, "Failed to get WETH balance") g.Require.NoError(err, "Failed to get WETH balance")
return balance return balance
} }
...@@ -218,16 +229,16 @@ func (g *OutputGameHelper) WaitForClaimCount(ctx context.Context, count int64) { ...@@ -218,16 +229,16 @@ func (g *OutputGameHelper) WaitForClaimCount(ctx context.Context, count int64) {
timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout) timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel() defer cancel()
err := wait.For(timedCtx, time.Second, func() (bool, error) { err := wait.For(timedCtx, time.Second, func() (bool, error) {
actual, err := g.game.ClaimDataLen(&bind.CallOpts{Context: timedCtx}) actual, err := g.Game.ClaimDataLen(&bind.CallOpts{Context: timedCtx})
if err != nil { if err != nil {
return false, err return false, err
} }
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
}) })
if err != nil { if err != nil {
g.LogGameData(ctx) g.LogGameData(ctx)
g.require.NoErrorf(err, "Did not find expected claim count %v", count) g.Require.NoErrorf(err, "Did not find expected claim count %v", count)
} }
} }
...@@ -242,8 +253,8 @@ type ContractClaim struct { ...@@ -242,8 +253,8 @@ type ContractClaim struct {
} }
func (g *OutputGameHelper) MaxDepth(ctx context.Context) types.Depth { func (g *OutputGameHelper) MaxDepth(ctx context.Context) types.Depth {
depth, err := g.game.MaxGameDepth(&bind.CallOpts{Context: ctx}) depth, err := g.Game.MaxGameDepth(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "Failed to load game depth") g.Require.NoError(err, "Failed to load game depth")
return types.Depth(depth.Uint64()) return types.Depth(depth.Uint64())
} }
...@@ -253,13 +264,13 @@ func (g *OutputGameHelper) waitForClaim(ctx context.Context, timeout time.Durati ...@@ -253,13 +264,13 @@ func (g *OutputGameHelper) waitForClaim(ctx context.Context, timeout time.Durati
var matchedClaim ContractClaim var matchedClaim ContractClaim
var matchClaimIdx int64 var matchClaimIdx int64
err := wait.For(timedCtx, time.Second, func() (bool, error) { err := wait.For(timedCtx, time.Second, func() (bool, error) {
count, err := g.game.ClaimDataLen(&bind.CallOpts{Context: timedCtx}) count, err := g.Game.ClaimDataLen(&bind.CallOpts{Context: timedCtx})
if err != nil { if err != nil {
return false, fmt.Errorf("retrieve number of claims: %w", err) return false, fmt.Errorf("retrieve number of claims: %w", err)
} }
// Search backwards because the new claims are at the end and more likely the ones we want. // Search backwards because the new claims are at the end and more likely the ones we want.
for i := count.Int64() - 1; i >= 0; i-- { for i := count.Int64() - 1; i >= 0; i-- {
claimData, err := g.game.ClaimData(&bind.CallOpts{Context: timedCtx}, big.NewInt(i)) claimData, err := g.Game.ClaimData(&bind.CallOpts{Context: timedCtx}, big.NewInt(i))
if err != nil { if err != nil {
return false, fmt.Errorf("retrieve claim %v: %w", i, err) return false, fmt.Errorf("retrieve claim %v: %w", i, err)
} }
...@@ -272,7 +283,7 @@ func (g *OutputGameHelper) waitForClaim(ctx context.Context, timeout time.Durati ...@@ -272,7 +283,7 @@ func (g *OutputGameHelper) waitForClaim(ctx context.Context, timeout time.Durati
return false, nil return false, nil
}) })
if err != nil { // Avoid waiting time capturing game data when there's no error if err != nil { // Avoid waiting time capturing game data when there's no error
g.require.NoErrorf(err, "%v\n%v", errorMsg, g.gameData(ctx)) g.Require.NoErrorf(err, "%v\n%v", errorMsg, g.GameData(ctx))
} }
return matchClaimIdx, matchedClaim return matchClaimIdx, matchedClaim
} }
...@@ -281,13 +292,13 @@ func (g *OutputGameHelper) waitForNoClaim(ctx context.Context, errorMsg string, ...@@ -281,13 +292,13 @@ func (g *OutputGameHelper) waitForNoClaim(ctx context.Context, errorMsg string,
timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout) timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel() defer cancel()
err := wait.For(timedCtx, time.Second, func() (bool, error) { err := wait.For(timedCtx, time.Second, func() (bool, error) {
count, err := g.game.ClaimDataLen(&bind.CallOpts{Context: timedCtx}) count, err := g.Game.ClaimDataLen(&bind.CallOpts{Context: timedCtx})
if err != nil { if err != nil {
return false, fmt.Errorf("retrieve number of claims: %w", err) return false, fmt.Errorf("retrieve number of claims: %w", err)
} }
// Search backwards because the new claims are at the end and more likely the ones we will fail on. // Search backwards because the new claims are at the end and more likely the ones we will fail on.
for i := count.Int64() - 1; i >= 0; i-- { for i := count.Int64() - 1; i >= 0; i-- {
claimData, err := g.game.ClaimData(&bind.CallOpts{Context: timedCtx}, big.NewInt(i)) claimData, err := g.Game.ClaimData(&bind.CallOpts{Context: timedCtx}, big.NewInt(i))
if err != nil { if err != nil {
return false, fmt.Errorf("retrieve claim %v: %w", i, err) return false, fmt.Errorf("retrieve claim %v: %w", i, err)
} }
...@@ -298,7 +309,7 @@ func (g *OutputGameHelper) waitForNoClaim(ctx context.Context, errorMsg string, ...@@ -298,7 +309,7 @@ func (g *OutputGameHelper) waitForNoClaim(ctx context.Context, errorMsg string,
return true, nil return true, nil
}) })
if err != nil { // Avoid waiting time capturing game data when there's no error if err != nil { // Avoid waiting time capturing game data when there's no error
g.require.NoErrorf(err, "%v\n%v", errorMsg, g.gameData(ctx)) g.Require.NoErrorf(err, "%v\n%v", errorMsg, g.GameData(ctx))
} }
} }
...@@ -320,9 +331,9 @@ func (g *OutputGameHelper) getAllClaims(ctx context.Context) []ContractClaim { ...@@ -320,9 +331,9 @@ func (g *OutputGameHelper) getAllClaims(ctx context.Context) []ContractClaim {
// getClaim retrieves the claim data for a specific index. // getClaim retrieves the claim data for a specific index.
// Note that it is deliberately not exported as tests should use WaitForClaim to avoid race conditions. // Note that it is deliberately not exported as tests should use WaitForClaim to avoid race conditions.
func (g *OutputGameHelper) getClaim(ctx context.Context, claimIdx int64) ContractClaim { func (g *OutputGameHelper) getClaim(ctx context.Context, claimIdx int64) ContractClaim {
claimData, err := g.game.ClaimData(&bind.CallOpts{Context: ctx}, big.NewInt(claimIdx)) claimData, err := g.Game.ClaimData(&bind.CallOpts{Context: ctx}, big.NewInt(claimIdx))
if err != nil { if err != nil {
g.require.NoErrorf(err, "retrieve claim %v", claimIdx) g.Require.NoErrorf(err, "retrieve claim %v", claimIdx)
} }
return claimData return claimData
} }
...@@ -362,40 +373,40 @@ func (g *OutputGameHelper) WaitForAllClaimsCountered(ctx context.Context) { ...@@ -362,40 +373,40 @@ func (g *OutputGameHelper) WaitForAllClaimsCountered(ctx context.Context) {
func (g *OutputGameHelper) Resolve(ctx context.Context) { func (g *OutputGameHelper) Resolve(ctx context.Context) {
ctx, cancel := context.WithTimeout(ctx, time.Minute) ctx, cancel := context.WithTimeout(ctx, time.Minute)
defer cancel() defer cancel()
tx, err := g.game.Resolve(g.opts) tx, err := g.Game.Resolve(g.Opts)
g.require.NoError(err) g.Require.NoError(err)
_, err = wait.ForReceiptOK(ctx, g.client, tx.Hash()) _, err = wait.ForReceiptOK(ctx, g.Client, tx.Hash())
g.require.NoError(err) g.Require.NoError(err)
} }
func (g *OutputGameHelper) Status(ctx context.Context) Status { func (g *OutputGameHelper) Status(ctx context.Context) Status {
status, err := g.game.Status(&bind.CallOpts{Context: ctx}) status, err := g.Game.Status(&bind.CallOpts{Context: ctx})
g.require.NoError(err) g.Require.NoError(err)
return Status(status) return Status(status)
} }
func (g *OutputGameHelper) WaitForGameStatus(ctx context.Context, expected Status) { func (g *OutputGameHelper) WaitForGameStatus(ctx context.Context, expected Status) {
g.t.Logf("Waiting for game %v to have status %v", g.addr, expected) g.T.Logf("Waiting for game %v to have status %v", g.Addr, expected)
timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout) timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel() defer cancel()
err := wait.For(timedCtx, time.Second, func() (bool, error) { err := wait.For(timedCtx, time.Second, func() (bool, error) {
ctx, cancel := context.WithTimeout(timedCtx, 30*time.Second) ctx, cancel := context.WithTimeout(timedCtx, 30*time.Second)
defer cancel() defer cancel()
status, err := g.game.Status(&bind.CallOpts{Context: ctx}) status, err := g.Game.Status(&bind.CallOpts{Context: ctx})
if err != nil { if err != nil {
return false, fmt.Errorf("game status unavailable: %w", err) return false, fmt.Errorf("game status unavailable: %w", err)
} }
g.t.Logf("Game %v has state %v, waiting for state %v", g.addr, Status(status), expected) g.T.Logf("Game %v has state %v, waiting for state %v", g.Addr, Status(status), expected)
return expected == Status(status), nil return expected == Status(status), nil
}) })
g.require.NoErrorf(err, "wait for game status. Game state: \n%v", g.gameData(ctx)) g.Require.NoErrorf(err, "wait for Game status. Game state: \n%v", g.GameData(ctx))
} }
func (g *OutputGameHelper) WaitForInactivity(ctx context.Context, numInactiveBlocks int, untilGameEnds bool) { func (g *OutputGameHelper) WaitForInactivity(ctx context.Context, numInactiveBlocks int, untilGameEnds bool) {
g.t.Logf("Waiting for game %v to have no activity for %v blocks", g.addr, numInactiveBlocks) g.T.Logf("Waiting for game %v to have no activity for %v blocks", g.Addr, numInactiveBlocks)
headCh := make(chan *gethtypes.Header, 100) headCh := make(chan *gethtypes.Header, 100)
headSub, err := g.client.SubscribeNewHead(ctx, headCh) headSub, err := g.Client.SubscribeNewHead(ctx, headCh)
g.require.NoError(err) g.Require.NoError(err)
defer headSub.Unsubscribe() defer headSub.Unsubscribe()
var lastActiveBlock uint64 var lastActiveBlock uint64
...@@ -411,22 +422,22 @@ func (g *OutputGameHelper) WaitForInactivity(ctx context.Context, numInactiveBlo ...@@ -411,22 +422,22 @@ func (g *OutputGameHelper) WaitForInactivity(ctx context.Context, numInactiveBlo
} else if lastActiveBlock+uint64(numInactiveBlocks) < head.Number.Uint64() { } else if lastActiveBlock+uint64(numInactiveBlocks) < head.Number.Uint64() {
return return
} }
block, err := g.client.BlockByNumber(ctx, head.Number) block, err := g.Client.BlockByNumber(ctx, head.Number)
g.require.NoError(err) g.Require.NoError(err)
numActions := 0 numActions := 0
for _, tx := range block.Transactions() { for _, tx := range block.Transactions() {
if tx.To().Hex() == g.addr.Hex() { if tx.To().Hex() == g.Addr.Hex() {
numActions++ numActions++
} }
} }
if numActions != 0 { if numActions != 0 {
g.t.Logf("Game %v has %v actions in block %d. Resetting inactivity timeout", g.addr, numActions, block.NumberU64()) g.T.Logf("Game %v has %v actions in block %d. Resetting inactivity timeout", g.Addr, numActions, block.NumberU64())
lastActiveBlock = head.Number.Uint64() lastActiveBlock = head.Number.Uint64()
} }
case err := <-headSub.Err(): case err := <-headSub.Err():
g.require.NoError(err) g.Require.NoError(err)
case <-ctx.Done(): case <-ctx.Done():
g.require.Fail("Context canceled", ctx.Err()) g.Require.Fail("Context canceled", ctx.Err())
} }
} }
} }
...@@ -453,10 +464,10 @@ func WithoutWaitingForStep() DefendClaimOpt { ...@@ -453,10 +464,10 @@ func WithoutWaitingForStep() DefendClaimOpt {
// It is assumed that the specified claim is invalid and that an honest op-challenger is already running. // It is assumed that the specified claim is invalid and that an honest op-challenger is already running.
// When the game has reached the maximum depth it waits for the honest challenger to counter the leaf claim with step. // When the game has reached the maximum depth it waits for the honest challenger to counter the leaf claim with step.
// Returns the final leaf claim // Returns the final leaf claim
func (g *OutputGameHelper) DefendClaim(ctx context.Context, claim *ClaimHelper, performMove Mover, opts ...DefendClaimOpt) *ClaimHelper { func (g *OutputGameHelper) DefendClaim(ctx context.Context, claim *ClaimHelper, performMove Mover, Opts ...DefendClaimOpt) *ClaimHelper {
g.t.Logf("Defending claim %v at depth %v", claim.index, claim.Depth()) g.T.Logf("Defending claim %v at depth %v", claim.Index, claim.Depth())
cfg := &defendClaimCfg{} cfg := &defendClaimCfg{}
for _, opt := range opts { for _, opt := range Opts {
opt(cfg) opt(cfg)
} }
for !claim.IsMaxDepth(ctx) { for !claim.IsMaxDepth(ctx) {
...@@ -495,12 +506,12 @@ func (g *OutputGameHelper) ChallengeClaim(ctx context.Context, claim *ClaimHelpe ...@@ -495,12 +506,12 @@ func (g *OutputGameHelper) ChallengeClaim(ctx context.Context, claim *ClaimHelpe
g.LogGameData(ctx) g.LogGameData(ctx)
// It's on us to call step if we want to win but shouldn't be possible // It's on us to call step if we want to win but shouldn't be possible
attemptStep(claim.index) attemptStep(claim.Index)
} }
func (g *OutputGameHelper) getClaimCount(ctx context.Context) int64 { func (g *OutputGameHelper) getClaimCount(ctx context.Context) int64 {
claimCount, err := g.game.ClaimDataLen(&bind.CallOpts{Context: ctx}) claimCount, err := g.Game.ClaimDataLen(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "Failed to get current claim count") g.Require.NoError(err, "Failed to get current claim count")
return claimCount.Int64() return claimCount.Int64()
} }
...@@ -513,7 +524,7 @@ func (g *OutputGameHelper) waitForNewClaim(ctx context.Context, checkPoint int64 ...@@ -513,7 +524,7 @@ func (g *OutputGameHelper) waitForNewClaim(ctx context.Context, checkPoint int64
defer cancel() defer cancel()
var newClaimLen int64 var newClaimLen int64
err := wait.For(timedCtx, time.Second, func() (bool, error) { err := wait.For(timedCtx, time.Second, func() (bool, error) {
actual, err := g.game.ClaimDataLen(&bind.CallOpts{Context: ctx}) actual, err := g.Game.ClaimDataLen(&bind.CallOpts{Context: ctx})
if err != nil { if err != nil {
return false, err return false, err
} }
...@@ -523,55 +534,55 @@ func (g *OutputGameHelper) waitForNewClaim(ctx context.Context, checkPoint int64 ...@@ -523,55 +534,55 @@ func (g *OutputGameHelper) waitForNewClaim(ctx context.Context, checkPoint int64
return newClaimLen, err return newClaimLen, err
} }
func (g *OutputGameHelper) moveCfg(opts ...MoveOpt) *moveCfg { func (g *OutputGameHelper) moveCfg(Opts ...MoveOpt) *moveCfg {
cfg := &moveCfg{ cfg := &moveCfg{
opts: g.opts, Opts: g.Opts,
} }
for _, opt := range opts { for _, opt := range Opts {
opt.Apply(cfg) opt.Apply(cfg)
} }
return cfg return cfg
} }
func (g *OutputGameHelper) Attack(ctx context.Context, claimIdx int64, claim common.Hash, opts ...MoveOpt) { func (g *OutputGameHelper) Attack(ctx context.Context, claimIdx int64, claim common.Hash, Opts ...MoveOpt) {
g.t.Logf("Attacking claim %v with value %v", claimIdx, claim) g.T.Logf("Attacking claim %v with value %v", claimIdx, claim)
cfg := g.moveCfg(opts...) cfg := g.moveCfg(Opts...)
claimData, err := g.game.ClaimData(&bind.CallOpts{Context: ctx}, big.NewInt(claimIdx)) claimData, err := g.Game.ClaimData(&bind.CallOpts{Context: ctx}, big.NewInt(claimIdx))
g.require.NoError(err, "Failed to get claim data") g.Require.NoError(err, "Failed to get claim data")
pos := types.NewPositionFromGIndex(claimData.Position) pos := types.NewPositionFromGIndex(claimData.Position)
attackPos := pos.Attack() attackPos := pos.Attack()
transactOpts := g.makeBondedTransactOpts(ctx, pos.Attack().ToGIndex(), cfg.opts) transactOpts := g.makeBondedTransactOpts(ctx, pos.Attack().ToGIndex(), cfg.Opts)
err = g.sendMove(ctx, func() (*gethtypes.Transaction, error) { err = g.sendMove(ctx, func() (*gethtypes.Transaction, error) {
return g.game.Attack(transactOpts, big.NewInt(claimIdx), claim) return g.Game.Attack(transactOpts, big.NewInt(claimIdx), claim)
}) })
if err != nil { if err != nil {
if cfg.ignoreDupes && g.hasClaim(ctx, claimIdx, attackPos, claim) { if cfg.ignoreDupes && g.hasClaim(ctx, claimIdx, attackPos, claim) {
return return
} }
g.require.NoErrorf(err, "Defend transaction failed. Game state: \n%v", g.gameData(ctx)) g.Require.NoErrorf(err, "Defend transaction failed. Game state: \n%v", g.GameData(ctx))
} }
} }
func (g *OutputGameHelper) Defend(ctx context.Context, claimIdx int64, claim common.Hash, opts ...MoveOpt) { func (g *OutputGameHelper) Defend(ctx context.Context, claimIdx int64, claim common.Hash, Opts ...MoveOpt) {
g.t.Logf("Defending claim %v with value %v", claimIdx, claim) g.T.Logf("Defending claim %v with value %v", claimIdx, claim)
cfg := g.moveCfg(opts...) cfg := g.moveCfg(Opts...)
claimData, err := g.game.ClaimData(&bind.CallOpts{Context: ctx}, big.NewInt(claimIdx)) claimData, err := g.Game.ClaimData(&bind.CallOpts{Context: ctx}, big.NewInt(claimIdx))
g.require.NoError(err, "Failed to get claim data") g.Require.NoError(err, "Failed to get claim data")
pos := types.NewPositionFromGIndex(claimData.Position) pos := types.NewPositionFromGIndex(claimData.Position)
defendPos := pos.Defend() defendPos := pos.Defend()
transactOpts := g.makeBondedTransactOpts(ctx, defendPos.ToGIndex(), cfg.opts) transactOpts := g.makeBondedTransactOpts(ctx, defendPos.ToGIndex(), cfg.Opts)
err = g.sendMove(ctx, func() (*gethtypes.Transaction, error) { err = g.sendMove(ctx, func() (*gethtypes.Transaction, error) {
return g.game.Defend(transactOpts, big.NewInt(claimIdx), claim) return g.Game.Defend(transactOpts, big.NewInt(claimIdx), claim)
}) })
if err != nil { if err != nil {
if cfg.ignoreDupes && g.hasClaim(ctx, claimIdx, defendPos, claim) { if cfg.ignoreDupes && g.hasClaim(ctx, claimIdx, defendPos, claim) {
return return
} }
g.require.NoErrorf(err, "Defend transaction failed. Game state: \n%v", g.gameData(ctx)) g.Require.NoErrorf(err, "Defend transaction failed. Game state: \n%v", g.GameData(ctx))
} }
} }
...@@ -590,19 +601,19 @@ func (g *OutputGameHelper) sendMove(ctx context.Context, send func() (*gethtypes ...@@ -590,19 +601,19 @@ func (g *OutputGameHelper) sendMove(ctx context.Context, send func() (*gethtypes
if err != nil { if err != nil {
return fmt.Errorf("transaction did not send: %w", err) return fmt.Errorf("transaction did not send: %w", err)
} }
_, err = wait.ForReceiptOK(ctx, g.client, tx.Hash()) _, err = wait.ForReceiptOK(ctx, g.Client, tx.Hash())
if err != nil { if err != nil {
return fmt.Errorf("transaction was not ok: %w", err) return fmt.Errorf("transaction was not ok: %w", err)
} }
return nil return nil
} }
func (g *OutputGameHelper) makeBondedTransactOpts(ctx context.Context, pos *big.Int, opts *bind.TransactOpts) *bind.TransactOpts { func (g *OutputGameHelper) makeBondedTransactOpts(ctx context.Context, pos *big.Int, Opts *bind.TransactOpts) *bind.TransactOpts {
bopts := *opts bOpts := *Opts
bond, err := g.game.GetRequiredBond(&bind.CallOpts{Context: ctx}, pos) bond, err := g.Game.GetRequiredBond(&bind.CallOpts{Context: ctx}, pos)
g.require.NoError(err, "Failed to get required bond") g.Require.NoError(err, "Failed to get required bond")
bopts.Value = bond bOpts.Value = bond
return &bopts return &bOpts
} }
type ErrWithData interface { type ErrWithData interface {
...@@ -611,19 +622,19 @@ type ErrWithData interface { ...@@ -611,19 +622,19 @@ type ErrWithData interface {
// StepFails attempts to call step and verifies that it fails with ValidStep() // StepFails attempts to call step and verifies that it fails with ValidStep()
func (g *OutputGameHelper) StepFails(claimIdx int64, isAttack bool, stateData []byte, proof []byte) { func (g *OutputGameHelper) StepFails(claimIdx int64, isAttack bool, stateData []byte, proof []byte) {
g.t.Logf("Attempting step against claim %v isAttack: %v", claimIdx, isAttack) g.T.Logf("Attempting step against claim %v isAttack: %v", claimIdx, isAttack)
_, err := g.game.Step(g.opts, big.NewInt(claimIdx), isAttack, stateData, proof) _, err := g.Game.Step(g.Opts, big.NewInt(claimIdx), isAttack, stateData, proof)
errData, ok := err.(ErrWithData) errData, ok := err.(ErrWithData)
g.require.Truef(ok, "Error should provide ErrorData method: %v", err) g.Require.Truef(ok, "Error should provide ErrorData method: %v", err)
g.require.Equal("0xfb4e40dd", errData.ErrorData(), "Revert reason should be abi encoded ValidStep()") g.Require.Equal("0xfb4e40dd", errData.ErrorData(), "Revert reason should be abi encoded ValidStep()")
} }
// ResolveClaim resolves a single subgame // ResolveClaim resolves a single subgame
func (g *OutputGameHelper) ResolveClaim(ctx context.Context, claimIdx int64) { func (g *OutputGameHelper) ResolveClaim(ctx context.Context, claimIdx int64) {
tx, err := g.game.ResolveClaim(g.opts, big.NewInt(claimIdx), common.Big0) tx, err := g.Game.ResolveClaim(g.Opts, big.NewInt(claimIdx), common.Big0)
g.require.NoError(err, "ResolveClaim transaction did not send") g.Require.NoError(err, "ResolveClaim transaction did not send")
_, err = wait.ForReceiptOK(ctx, g.client, tx.Hash()) _, err = wait.ForReceiptOK(ctx, g.Client, tx.Hash())
g.require.NoError(err, "ResolveClaim transaction was not OK") g.Require.NoError(err, "ResolveClaim transaction was not OK")
} }
// ChallengePeriod returns the challenge period fetched from the PreimageOracle contract. // ChallengePeriod returns the challenge period fetched from the PreimageOracle contract.
...@@ -631,7 +642,7 @@ func (g *OutputGameHelper) ResolveClaim(ctx context.Context, claimIdx int64) { ...@@ -631,7 +642,7 @@ func (g *OutputGameHelper) ResolveClaim(ctx context.Context, claimIdx int64) {
func (g *OutputGameHelper) ChallengePeriod(ctx context.Context) uint64 { func (g *OutputGameHelper) ChallengePeriod(ctx context.Context) uint64 {
oracle := g.oracle(ctx) oracle := g.oracle(ctx)
period, err := oracle.ChallengePeriod(ctx) period, err := oracle.ChallengePeriod(ctx)
g.require.NoError(err, "Failed to get challenge period") g.Require.NoError(err, "Failed to get challenge period")
return period return period
} }
...@@ -643,12 +654,12 @@ func (g *OutputGameHelper) WaitForChallengePeriodStart(ctx context.Context, send ...@@ -643,12 +654,12 @@ func (g *OutputGameHelper) WaitForChallengePeriodStart(ctx context.Context, send
ctx, cancel := context.WithTimeout(timedCtx, 30*time.Second) ctx, cancel := context.WithTimeout(timedCtx, 30*time.Second)
defer cancel() defer cancel()
timestamp := g.ChallengePeriodStartTime(ctx, sender, data) timestamp := g.ChallengePeriodStartTime(ctx, sender, data)
g.t.Log("Waiting for challenge period start", "timestamp", timestamp, "key", data.OracleKey, "game", g.addr) g.T.Log("Waiting for challenge period start", "timestamp", timestamp, "key", data.OracleKey, "game", g.Addr)
return timestamp > 0, nil return timestamp > 0, nil
}) })
if err != nil { if err != nil {
g.LogGameData(ctx) g.LogGameData(ctx)
g.require.NoErrorf(err, "Failed to get challenge start period for preimage data %v", data) g.Require.NoErrorf(err, "Failed to get challenge start period for preimage data %v", data)
} }
} }
...@@ -661,68 +672,68 @@ func (g *OutputGameHelper) ChallengePeriodStartTime(ctx context.Context, sender ...@@ -661,68 +672,68 @@ func (g *OutputGameHelper) ChallengePeriodStartTime(ctx context.Context, sender
Claimant: sender, Claimant: sender,
UUID: uuid, UUID: uuid,
}) })
g.require.NoError(err, "Failed to get proposal metadata") g.Require.NoError(err, "Failed to get proposal metadata")
if len(metadata) == 0 { if len(metadata) == 0 {
return 0 return 0
} }
return metadata[0].Timestamp return metadata[0].Timestamp
} }
func (g *OutputGameHelper) waitForPreimageInOracle(ctx context.Context, data *types.PreimageOracleData) { func (g *OutputGameHelper) WaitForPreimageInOracle(ctx context.Context, data *types.PreimageOracleData) {
timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout) timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel() defer cancel()
oracle := g.oracle(ctx) oracle := g.oracle(ctx)
err := wait.For(timedCtx, time.Second, func() (bool, error) { err := wait.For(timedCtx, time.Second, func() (bool, error) {
g.t.Logf("Waiting for preimage (%v) to be present in oracle", common.Bytes2Hex(data.OracleKey)) g.T.Logf("Waiting for preimage (%v) to be present in oracle", common.Bytes2Hex(data.OracleKey))
return oracle.GlobalDataExists(ctx, data) return oracle.GlobalDataExists(ctx, data)
}) })
g.require.NoErrorf(err, "Did not find preimage (%v) in oracle", common.Bytes2Hex(data.OracleKey)) g.Require.NoErrorf(err, "Did not find preimage (%v) in oracle", common.Bytes2Hex(data.OracleKey))
} }
func (g *OutputGameHelper) uploadPreimage(ctx context.Context, data *types.PreimageOracleData, privateKey *ecdsa.PrivateKey) { func (g *OutputGameHelper) UploadPreimage(ctx context.Context, data *types.PreimageOracleData, privateKey *ecdsa.PrivateKey) {
oracle := g.oracle(ctx) oracle := g.oracle(ctx)
boundOracle, err := bindings.NewPreimageOracle(oracle.Addr(), g.client) boundOracle, err := bindings.NewPreimageOracle(oracle.Addr(), g.Client)
g.require.NoError(err) g.Require.NoError(err)
var tx *gethtypes.Transaction var tx *gethtypes.Transaction
switch data.OracleKey[0] { switch data.OracleKey[0] {
case byte(preimage.PrecompileKeyType): case byte(preimage.PrecompileKeyType):
tx, err = boundOracle.LoadPrecompilePreimagePart( tx, err = boundOracle.LoadPrecompilePreimagePart(
g.opts, g.Opts,
new(big.Int).SetUint64(uint64(data.OracleOffset)), new(big.Int).SetUint64(uint64(data.OracleOffset)),
data.GetPrecompileAddress(), data.GetPrecompileAddress(),
data.GetPrecompileInput(), data.GetPrecompileInput(),
) )
default: default:
tx, err = boundOracle.LoadKeccak256PreimagePart(g.opts, new(big.Int).SetUint64(uint64(data.OracleOffset)), data.GetPreimageWithoutSize()) tx, err = boundOracle.LoadKeccak256PreimagePart(g.Opts, new(big.Int).SetUint64(uint64(data.OracleOffset)), data.GetPreimageWithoutSize())
} }
g.require.NoError(err, "Failed to load preimage part") g.Require.NoError(err, "Failed to load preimage part")
_, err = wait.ForReceiptOK(ctx, g.client, tx.Hash()) _, err = wait.ForReceiptOK(ctx, g.Client, tx.Hash())
g.require.NoError(err) g.Require.NoError(err)
} }
func (g *OutputGameHelper) oracle(ctx context.Context) *contracts.PreimageOracleContract { func (g *OutputGameHelper) oracle(ctx context.Context) *contracts.PreimageOracleContract {
caller := batching.NewMultiCaller(g.system.NodeClient("l1").Client(), batching.DefaultBatchSize) caller := batching.NewMultiCaller(g.System.NodeClient("l1").Client(), batching.DefaultBatchSize)
contract := contracts.NewFaultDisputeGameContract(contractMetrics.NoopContractMetrics, g.addr, caller) contract := contracts.NewFaultDisputeGameContract(contractMetrics.NoopContractMetrics, g.Addr, caller)
oracle, err := contract.GetOracle(ctx) oracle, err := contract.GetOracle(ctx)
g.require.NoError(err, "Failed to create oracle contract") g.Require.NoError(err, "Failed to create oracle contract")
return oracle return oracle
} }
func (g *OutputGameHelper) gameData(ctx context.Context) string { func (g *OutputGameHelper) GameData(ctx context.Context) string {
opts := &bind.CallOpts{Context: ctx} Opts := &bind.CallOpts{Context: ctx}
maxDepth := g.MaxDepth(ctx) maxDepth := g.MaxDepth(ctx)
splitDepth := g.SplitDepth(ctx) splitDepth := g.SplitDepth(ctx)
claimCount, err := g.game.ClaimDataLen(opts) claimCount, err := g.Game.ClaimDataLen(Opts)
info := fmt.Sprintf("Claim count: %v\n", claimCount) info := fmt.Sprintf("Claim count: %v\n", claimCount)
g.require.NoError(err, "Fetching claim count") g.Require.NoError(err, "Fetching claim count")
for i := int64(0); i < claimCount.Int64(); i++ { for i := int64(0); i < claimCount.Int64(); i++ {
claim, err := g.game.ClaimData(opts, big.NewInt(i)) claim, err := g.Game.ClaimData(Opts, big.NewInt(i))
g.require.NoErrorf(err, "Fetch claim %v", i) g.Require.NoErrorf(err, "Fetch claim %v", i)
pos := types.NewPositionFromGIndex(claim.Position) pos := types.NewPositionFromGIndex(claim.Position)
extra := "" extra := ""
if pos.Depth() <= splitDepth { if pos.Depth() <= splitDepth {
blockNum, err := g.correctOutputProvider.ClaimedBlockNumber(pos) blockNum, err := g.CorrectOutputProvider.ClaimedBlockNumber(pos)
if err != nil { if err != nil {
} else { } else {
extra = fmt.Sprintf("Block num: %v", blockNum) extra = fmt.Sprintf("Block num: %v", blockNum)
...@@ -732,28 +743,28 @@ func (g *OutputGameHelper) gameData(ctx context.Context) string { ...@@ -732,28 +743,28 @@ func (g *OutputGameHelper) gameData(ctx context.Context) string {
i, claim.Position.Int64(), pos.Depth(), pos.IndexAtDepth(), pos.TraceIndex(maxDepth), common.Hash(claim.Claim).Hex(), claim.CounteredBy, claim.ParentIndex, claim.Claimant, claim.Bond, extra) i, claim.Position.Int64(), pos.Depth(), pos.IndexAtDepth(), pos.TraceIndex(maxDepth), common.Hash(claim.Claim).Hex(), claim.CounteredBy, claim.ParentIndex, claim.Claimant, claim.Bond, extra)
} }
l2BlockNum := g.L2BlockNum(ctx) l2BlockNum := g.L2BlockNum(ctx)
status, err := g.game.Status(opts) status, err := g.Game.Status(Opts)
g.require.NoError(err, "Load game status") g.Require.NoError(err, "Load game status")
return fmt.Sprintf("Game %v - %v - L2 Block: %v - Split Depth: %v - Max Depth: %v:\n%v\n", return fmt.Sprintf("Game %v - %v - L2 Block: %v - Split Depth: %v - Max Depth: %v:\n%v\n",
g.addr, Status(status), l2BlockNum, splitDepth, maxDepth, info) g.Addr, Status(status), l2BlockNum, splitDepth, maxDepth, info)
} }
func (g *OutputGameHelper) LogGameData(ctx context.Context) { func (g *OutputGameHelper) LogGameData(ctx context.Context) {
g.t.Log(g.gameData(ctx)) g.T.Log(g.GameData(ctx))
} }
func (g *OutputGameHelper) Credit(ctx context.Context, addr common.Address) *big.Int { func (g *OutputGameHelper) Credit(ctx context.Context, addr common.Address) *big.Int {
opts := &bind.CallOpts{Context: ctx} Opts := &bind.CallOpts{Context: ctx}
amt, err := g.game.Credit(opts, addr) amt, err := g.Game.Credit(Opts, addr)
g.require.NoError(err) g.Require.NoError(err)
return amt return amt
} }
func (g *OutputGameHelper) getL1Head(ctx context.Context) eth.BlockID { func (g *OutputGameHelper) GetL1Head(ctx context.Context) eth.BlockID {
l1HeadHash, err := g.game.L1Head(&bind.CallOpts{Context: ctx}) l1HeadHash, err := g.Game.L1Head(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "Failed to load L1 head") g.Require.NoError(err, "Failed to load L1 head")
l1Header, err := g.client.HeaderByHash(ctx, l1HeadHash) l1Header, err := g.Client.HeaderByHash(ctx, l1HeadHash)
g.require.NoError(err, "Failed to load L1 header") g.Require.NoError(err, "Failed to load L1 header")
l1Head := eth.HeaderBlockID(l1Header) l1Head := eth.HeaderBlockID(l1Header)
return l1Head return l1Head
} }
...@@ -21,8 +21,18 @@ type OutputHonestHelper struct { ...@@ -21,8 +21,18 @@ type OutputHonestHelper struct {
correctTrace types.TraceAccessor correctTrace types.TraceAccessor
} }
func NewOutputHonestHelper(t *testing.T, require *require.Assertions, game *OutputGameHelper, contract *contracts.FaultDisputeGameContract, correctTrace types.TraceAccessor) *OutputHonestHelper {
return &OutputHonestHelper{
t: t,
require: require,
game: game,
contract: contract,
correctTrace: correctTrace,
}
}
func (h *OutputHonestHelper) CounterClaim(ctx context.Context, claim *ClaimHelper, opts ...MoveOpt) *ClaimHelper { func (h *OutputHonestHelper) CounterClaim(ctx context.Context, claim *ClaimHelper, opts ...MoveOpt) *ClaimHelper {
game, target := h.loadState(ctx, claim.index) game, target := h.loadState(ctx, claim.Index)
value, err := h.correctTrace.Get(ctx, game, target, target.Position) value, err := h.correctTrace.Get(ctx, game, target, target.Position)
h.require.NoErrorf(err, "Failed to determine correct claim at position %v with g index %v", target.Position, target.Position.ToGIndex()) h.require.NoErrorf(err, "Failed to determine correct claim at position %v with g index %v", target.Position, target.Position.ToGIndex())
if value == claim.claim { if value == claim.claim {
...@@ -33,12 +43,12 @@ func (h *OutputHonestHelper) CounterClaim(ctx context.Context, claim *ClaimHelpe ...@@ -33,12 +43,12 @@ func (h *OutputHonestHelper) CounterClaim(ctx context.Context, claim *ClaimHelpe
} }
func (h *OutputHonestHelper) AttackClaim(ctx context.Context, claim *ClaimHelper, opts ...MoveOpt) *ClaimHelper { func (h *OutputHonestHelper) AttackClaim(ctx context.Context, claim *ClaimHelper, opts ...MoveOpt) *ClaimHelper {
h.Attack(ctx, claim.index, opts...) h.Attack(ctx, claim.Index, opts...)
return claim.WaitForCounterClaim(ctx) return claim.WaitForCounterClaim(ctx)
} }
func (h *OutputHonestHelper) DefendClaim(ctx context.Context, claim *ClaimHelper, opts ...MoveOpt) *ClaimHelper { func (h *OutputHonestHelper) DefendClaim(ctx context.Context, claim *ClaimHelper, opts ...MoveOpt) *ClaimHelper {
h.Defend(ctx, claim.index, opts...) h.Defend(ctx, claim.Index, opts...)
return claim.WaitForCounterClaim(ctx) return claim.WaitForCounterClaim(ctx)
} }
...@@ -68,12 +78,12 @@ func (h *OutputHonestHelper) Defend(ctx context.Context, claimIdx int64, opts .. ...@@ -68,12 +78,12 @@ func (h *OutputHonestHelper) Defend(ctx context.Context, claimIdx int64, opts ..
game, claim := h.loadState(ctx, claimIdx) game, claim := h.loadState(ctx, claimIdx)
defendPos := claim.Position.Defend() defendPos := claim.Position.Defend()
value, err := h.correctTrace.Get(ctx, game, claim, defendPos) value, err := h.correctTrace.Get(ctx, game, claim, defendPos)
h.game.require.NoErrorf(err, "Get correct claim at position %v with g index %v", defendPos, defendPos.ToGIndex()) h.game.Require.NoErrorf(err, "Get correct claim at position %v with g index %v", defendPos, defendPos.ToGIndex())
h.game.Defend(ctx, claimIdx, value, opts...) h.game.Defend(ctx, claimIdx, value, opts...)
} }
func (h *OutputHonestHelper) StepClaimFails(ctx context.Context, claim *ClaimHelper, isAttack bool) { func (h *OutputHonestHelper) StepClaimFails(ctx context.Context, claim *ClaimHelper, isAttack bool) {
h.StepFails(ctx, claim.index, isAttack) h.StepFails(ctx, claim.Index, isAttack)
} }
func (h *OutputHonestHelper) StepFails(ctx context.Context, claimIdx int64, isAttack bool) { func (h *OutputHonestHelper) StepFails(ctx context.Context, claimIdx int64, isAttack bool) {
......
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
func TestChallengeLargePreimages_ChallengeFirst(t *testing.T) { func TestChallengeLargePreimages_ChallengeFirst(t *testing.T) {
op_e2e.InitParallel(t) op_e2e.InitParallel(t)
ctx := context.Background() ctx := context.Background()
sys, _ := startFaultDisputeSystem(t) sys, _ := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -34,7 +34,7 @@ func TestChallengeLargePreimages_ChallengeFirst(t *testing.T) { ...@@ -34,7 +34,7 @@ func TestChallengeLargePreimages_ChallengeFirst(t *testing.T) {
func TestChallengeLargePreimages_ChallengeMiddle(t *testing.T) { func TestChallengeLargePreimages_ChallengeMiddle(t *testing.T) {
op_e2e.InitParallel(t) op_e2e.InitParallel(t)
ctx := context.Background() ctx := context.Background()
sys, _ := startFaultDisputeSystem(t) sys, _ := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
disputeGameFactory.StartChallenger(ctx, "Challenger", disputeGameFactory.StartChallenger(ctx, "Challenger",
...@@ -52,7 +52,7 @@ func TestChallengeLargePreimages_ChallengeMiddle(t *testing.T) { ...@@ -52,7 +52,7 @@ func TestChallengeLargePreimages_ChallengeMiddle(t *testing.T) {
func TestChallengeLargePreimages_ChallengeLast(t *testing.T) { func TestChallengeLargePreimages_ChallengeLast(t *testing.T) {
op_e2e.InitParallel(t) op_e2e.InitParallel(t)
ctx := context.Background() ctx := context.Background()
sys, _ := startFaultDisputeSystem(t) sys, _ := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
disputeGameFactory.StartChallenger(ctx, "Challenger", disputeGameFactory.StartChallenger(ctx, "Challenger",
......
...@@ -14,7 +14,7 @@ func TestMultipleGameTypes(t *testing.T) { ...@@ -14,7 +14,7 @@ func TestMultipleGameTypes(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, _ := startFaultDisputeSystem(t) sys, _ := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
gameFactory := disputegame.NewFactoryHelper(t, ctx, sys) gameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
......
...@@ -18,7 +18,7 @@ import ( ...@@ -18,7 +18,7 @@ import (
func TestOutputAlphabetGame_ChallengerWins(t *testing.T) { func TestOutputAlphabetGame_ChallengerWins(t *testing.T) {
op_e2e.InitParallel(t) op_e2e.InitParallel(t)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -75,7 +75,7 @@ func TestOutputAlphabetGame_ChallengerWins(t *testing.T) { ...@@ -75,7 +75,7 @@ func TestOutputAlphabetGame_ChallengerWins(t *testing.T) {
func TestOutputAlphabetGame_ReclaimBond(t *testing.T) { func TestOutputAlphabetGame_ReclaimBond(t *testing.T) {
op_e2e.InitParallel(t) op_e2e.InitParallel(t)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -83,7 +83,7 @@ func TestOutputAlphabetGame_ReclaimBond(t *testing.T) { ...@@ -83,7 +83,7 @@ func TestOutputAlphabetGame_ReclaimBond(t *testing.T) {
game.LogGameData(ctx) game.LogGameData(ctx)
// The dispute game should have a zero balance // The dispute game should have a zero balance
balance := game.WethBalance(ctx, game.Addr()) balance := game.WethBalance(ctx, game.Addr)
require.Zero(t, balance.Uint64()) require.Zero(t, balance.Uint64())
alice := sys.Cfg.Secrets.Addresses().Alice alice := sys.Cfg.Secrets.Addresses().Alice
...@@ -105,7 +105,7 @@ func TestOutputAlphabetGame_ReclaimBond(t *testing.T) { ...@@ -105,7 +105,7 @@ func TestOutputAlphabetGame_ReclaimBond(t *testing.T) {
_ = claim.WaitForCounterClaim(ctx) _ = claim.WaitForCounterClaim(ctx)
// Expect posted claims so the game balance is non-zero // Expect posted claims so the game balance is non-zero
balance = game.WethBalance(ctx, game.Addr()) balance = game.WethBalance(ctx, game.Addr)
require.Truef(t, balance.Cmp(big.NewInt(0)) > 0, "Expected game balance to be above zero") require.Truef(t, balance.Cmp(big.NewInt(0)) > 0, "Expected game balance to be above zero")
sys.TimeTravelClock.AdvanceTime(game.MaxClockDuration(ctx)) sys.TimeTravelClock.AdvanceTime(game.MaxClockDuration(ctx))
...@@ -130,13 +130,13 @@ func TestOutputAlphabetGame_ReclaimBond(t *testing.T) { ...@@ -130,13 +130,13 @@ func TestOutputAlphabetGame_ReclaimBond(t *testing.T) {
game.WaitForNoAvailableCredit(ctx, alice) game.WaitForNoAvailableCredit(ctx, alice)
// The dispute game delayed weth balance should be zero since it's all claimed // The dispute game delayed weth balance should be zero since it's all claimed
require.True(t, game.WethBalance(ctx, game.Addr()).Cmp(big.NewInt(0)) == 0) require.True(t, game.WethBalance(ctx, game.Addr).Cmp(big.NewInt(0)) == 0)
} }
func TestOutputAlphabetGame_ValidOutputRoot(t *testing.T) { func TestOutputAlphabetGame_ValidOutputRoot(t *testing.T) {
op_e2e.InitParallel(t) op_e2e.InitParallel(t)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -169,7 +169,7 @@ func TestChallengerCompleteExhaustiveDisputeGame(t *testing.T) { ...@@ -169,7 +169,7 @@ func TestChallengerCompleteExhaustiveDisputeGame(t *testing.T) {
testCase := func(t *testing.T, isRootCorrect bool) { testCase := func(t *testing.T, isRootCorrect bool) {
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -234,7 +234,7 @@ func TestChallengerCompleteExhaustiveDisputeGame(t *testing.T) { ...@@ -234,7 +234,7 @@ func TestChallengerCompleteExhaustiveDisputeGame(t *testing.T) {
func TestOutputAlphabetGame_FreeloaderEarnsNothing(t *testing.T) { func TestOutputAlphabetGame_FreeloaderEarnsNothing(t *testing.T) {
op_e2e.InitParallel(t) op_e2e.InitParallel(t)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
freeloaderOpts, err := bind.NewKeyedTransactorWithChainID(sys.Cfg.Secrets.Mallory, sys.Cfg.L1ChainIDBig()) freeloaderOpts, err := bind.NewKeyedTransactorWithChainID(sys.Cfg.Secrets.Mallory, sys.Cfg.L1ChainIDBig())
......
...@@ -21,7 +21,7 @@ import ( ...@@ -21,7 +21,7 @@ import (
func TestOutputCannonGame(t *testing.T) { func TestOutputCannonGame(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -77,7 +77,7 @@ func TestOutputCannon_ChallengeAllZeroClaim(t *testing.T) { ...@@ -77,7 +77,7 @@ func TestOutputCannon_ChallengeAllZeroClaim(t *testing.T) {
// The dishonest actor always posts claims with all zeros. // The dishonest actor always posts claims with all zeros.
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -116,7 +116,7 @@ func TestOutputCannon_PublishCannonRootClaim(t *testing.T) { ...@@ -116,7 +116,7 @@ func TestOutputCannon_PublishCannonRootClaim(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, _ := startFaultDisputeSystem(t) sys, _ := StartFaultDisputeSystem(t)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", test.disputeL2BlockNumber, common.Hash{0x01}) game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", test.disputeL2BlockNumber, common.Hash{0x01})
...@@ -147,7 +147,7 @@ func TestOutputCannonDisputeGame(t *testing.T) { ...@@ -147,7 +147,7 @@ func TestOutputCannonDisputeGame(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -184,7 +184,7 @@ func TestOutputCannonDefendStep(t *testing.T) { ...@@ -184,7 +184,7 @@ func TestOutputCannonDefendStep(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -220,7 +220,7 @@ func TestOutputCannonStepWithLargePreimage(t *testing.T) { ...@@ -220,7 +220,7 @@ func TestOutputCannonStepWithLargePreimage(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, _ := startFaultDisputeSystem(t, withBatcherStopped()) sys, _ := StartFaultDisputeSystem(t, WithBatcherStopped())
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
// Manually send a tx from the correct batcher key to the batcher input with very large (invalid) data // Manually send a tx from the correct batcher key to the batcher input with very large (invalid) data
...@@ -263,7 +263,7 @@ func TestOutputCannonStepWithPreimage(t *testing.T) { ...@@ -263,7 +263,7 @@ func TestOutputCannonStepWithPreimage(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, _ := startFaultDisputeSystem(t, withBlobBatches()) sys, _ := StartFaultDisputeSystem(t, WithBlobBatches())
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -306,7 +306,7 @@ func TestOutputCannonStepWithKZGPointEvaluation(t *testing.T) { ...@@ -306,7 +306,7 @@ func TestOutputCannonStepWithKZGPointEvaluation(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, _ := startFaultDisputeSystem(t, withEcotone()) sys, _ := StartFaultDisputeSystem(t, WithEcotone())
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
// NOTE: Flake prevention // NOTE: Flake prevention
...@@ -315,7 +315,7 @@ func TestOutputCannonStepWithKZGPointEvaluation(t *testing.T) { ...@@ -315,7 +315,7 @@ func TestOutputCannonStepWithKZGPointEvaluation(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, wait.ForSafeBlock(ctx, sys.RollupClient("sequencer"), safeBlock.NumberU64()+3)) require.NoError(t, wait.ForSafeBlock(ctx, sys.RollupClient("sequencer"), safeBlock.NumberU64()+3))
receipt := sendKZGPointEvaluationTx(t, sys, "sequencer", sys.Cfg.Secrets.Alice) receipt := SendKZGPointEvaluationTx(t, sys, "sequencer", sys.Cfg.Secrets.Alice)
precompileBlock := receipt.BlockNumber precompileBlock := receipt.BlockNumber
t.Logf("KZG Point Evaluation block number: %d", precompileBlock) t.Logf("KZG Point Evaluation block number: %d", precompileBlock)
...@@ -406,7 +406,7 @@ func TestOutputCannonProposedOutputRootValid(t *testing.T) { ...@@ -406,7 +406,7 @@ func TestOutputCannonProposedOutputRootValid(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -440,7 +440,7 @@ func TestOutputCannonPoisonedPostState(t *testing.T) { ...@@ -440,7 +440,7 @@ func TestOutputCannonPoisonedPostState(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -504,7 +504,7 @@ func TestDisputeOutputRootBeyondProposedBlock_ValidOutputRoot(t *testing.T) { ...@@ -504,7 +504,7 @@ func TestDisputeOutputRootBeyondProposedBlock_ValidOutputRoot(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -554,7 +554,7 @@ func TestDisputeOutputRootBeyondProposedBlock_InvalidOutputRoot(t *testing.T) { ...@@ -554,7 +554,7 @@ func TestDisputeOutputRootBeyondProposedBlock_InvalidOutputRoot(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -605,7 +605,7 @@ func TestDisputeOutputRoot_ChangeClaimedOutputRoot(t *testing.T) { ...@@ -605,7 +605,7 @@ func TestDisputeOutputRoot_ChangeClaimedOutputRoot(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t) sys, l1Client := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
...@@ -693,7 +693,7 @@ func TestInvalidateUnsafeProposal(t *testing.T) { ...@@ -693,7 +693,7 @@ func TestInvalidateUnsafeProposal(t *testing.T) {
test := test test := test
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
sys, l1Client := startFaultDisputeSystem(t, withSequencerWindowSize(100000), withBatcherStopped()) sys, l1Client := StartFaultDisputeSystem(t, WithSequencerWindowSize(100000), WithBatcherStopped())
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
blockNum := uint64(1) blockNum := uint64(1)
...@@ -755,7 +755,7 @@ func TestInvalidateProposalForFutureBlock(t *testing.T) { ...@@ -755,7 +755,7 @@ func TestInvalidateProposalForFutureBlock(t *testing.T) {
test := test test := test
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
sys, l1Client := startFaultDisputeSystem(t, withSequencerWindowSize(100000)) sys, l1Client := StartFaultDisputeSystem(t, WithSequencerWindowSize(100000))
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
farFutureBlockNum := uint64(10_000_000) farFutureBlockNum := uint64(10_000_000)
......
...@@ -32,7 +32,7 @@ func TestLocalPreimages(t *testing.T) { ...@@ -32,7 +32,7 @@ func TestLocalPreimages(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, _ := startFaultDisputeSystem(t) sys, _ := StartFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
......
...@@ -15,13 +15,13 @@ import ( ...@@ -15,13 +15,13 @@ import (
type faultDisputeConfigOpts func(cfg *op_e2e.SystemConfig) type faultDisputeConfigOpts func(cfg *op_e2e.SystemConfig)
func withBatcherStopped() faultDisputeConfigOpts { func WithBatcherStopped() faultDisputeConfigOpts {
return func(cfg *op_e2e.SystemConfig) { return func(cfg *op_e2e.SystemConfig) {
cfg.DisableBatcher = true cfg.DisableBatcher = true
} }
} }
func withBlobBatches() faultDisputeConfigOpts { func WithBlobBatches() faultDisputeConfigOpts {
return func(cfg *op_e2e.SystemConfig) { return func(cfg *op_e2e.SystemConfig) {
cfg.DataAvailabilityType = batcherFlags.BlobsType cfg.DataAvailabilityType = batcherFlags.BlobsType
...@@ -32,7 +32,7 @@ func withBlobBatches() faultDisputeConfigOpts { ...@@ -32,7 +32,7 @@ func withBlobBatches() faultDisputeConfigOpts {
} }
} }
func withEcotone() faultDisputeConfigOpts { func WithEcotone() faultDisputeConfigOpts {
return func(cfg *op_e2e.SystemConfig) { return func(cfg *op_e2e.SystemConfig) {
genesisActivation := hexutil.Uint64(0) genesisActivation := hexutil.Uint64(0)
cfg.DeployConfig.L1CancunTimeOffset = &genesisActivation cfg.DeployConfig.L1CancunTimeOffset = &genesisActivation
...@@ -41,13 +41,13 @@ func withEcotone() faultDisputeConfigOpts { ...@@ -41,13 +41,13 @@ func withEcotone() faultDisputeConfigOpts {
} }
} }
func withSequencerWindowSize(size uint64) faultDisputeConfigOpts { func WithSequencerWindowSize(size uint64) faultDisputeConfigOpts {
return func(cfg *op_e2e.SystemConfig) { return func(cfg *op_e2e.SystemConfig) {
cfg.DeployConfig.SequencerWindowSize = size cfg.DeployConfig.SequencerWindowSize = size
} }
} }
func startFaultDisputeSystem(t *testing.T, opts ...faultDisputeConfigOpts) (*op_e2e.System, *ethclient.Client) { func StartFaultDisputeSystem(t *testing.T, opts ...faultDisputeConfigOpts) (*op_e2e.System, *ethclient.Client) {
cfg := op_e2e.DefaultSystemConfig(t) cfg := op_e2e.DefaultSystemConfig(t)
delete(cfg.Nodes, "verifier") delete(cfg.Nodes, "verifier")
for _, opt := range opts { for _, opt := range opts {
...@@ -64,7 +64,7 @@ func startFaultDisputeSystem(t *testing.T, opts ...faultDisputeConfigOpts) (*op_ ...@@ -64,7 +64,7 @@ func startFaultDisputeSystem(t *testing.T, opts ...faultDisputeConfigOpts) (*op_
return sys, sys.Clients["l1"] return sys, sys.Clients["l1"]
} }
func sendKZGPointEvaluationTx(t *testing.T, sys *op_e2e.System, l2Node string, privateKey *ecdsa.PrivateKey) *types.Receipt { func SendKZGPointEvaluationTx(t *testing.T, sys *op_e2e.System, l2Node string, privateKey *ecdsa.PrivateKey) *types.Receipt {
return op_e2e.SendL2Tx(t, sys.Cfg, sys.Clients[l2Node], privateKey, func(opts *op_e2e.TxOpts) { return op_e2e.SendL2Tx(t, sys.Cfg, sys.Clients[l2Node], privateKey, func(opts *op_e2e.TxOpts) {
precompile := common.BytesToAddress([]byte{0x0a}) precompile := common.BytesToAddress([]byte{0x0a})
opts.Gas = 100_000 opts.Gas = 100_000
......
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