Skip to content

Commit 4b215b9

Browse files
committed
op-e2e: Migrate the remaining Super DG tests
1 parent f083d84 commit 4b215b9

File tree

7 files changed

+835
-143
lines changed

7 files changed

+835
-143
lines changed

op-e2e/e2eutils/disputegame/cannon_helper.go

+69-58
Original file line numberDiff line numberDiff line change
@@ -326,68 +326,12 @@ func (g *CannonHelper) ChallengeToPreimageLoadAtTarget(ctx context.Context, cann
326326
g.WaitForPreimageInOracle(ctx, preimageData)
327327
}
328328

329-
// Descending the execution game tree to reach the step that loads the preimage
330329
bisectTraceIndex := func(claim *ClaimHelper) *ClaimHelper {
331-
execClaimPosition, err := claim.Position.RelativeToAncestorAtDepth(splitDepth + 1)
332-
g.require.NoError(err)
333-
334-
claimTraceIndex := execClaimPosition.TraceIndex(execDepth).Uint64()
335-
g.t.Logf("Bisecting: Into targetTraceIndex %v: claimIndex=%v at depth=%v. claimPosition=%v execClaimPosition=%v claimTraceIndex=%v",
336-
targetTraceIndex, claim.Index, claim.Depth(), claim.Position, execClaimPosition, claimTraceIndex)
337-
338-
// We always want to position ourselves such that the challenger generates proofs for the targetTraceIndex as prestate
339-
if execClaimPosition.Depth() == execDepth-1 {
340-
if execClaimPosition.TraceIndex(execDepth).Uint64() == targetTraceIndex {
341-
newPosition := execClaimPosition.Attack()
342-
correct, err := provider.Get(ctx, newPosition)
343-
g.require.NoError(err)
344-
g.t.Logf("Bisecting: Attack correctly for step at newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
345-
return claim.Attack(ctx, correct)
346-
} else if execClaimPosition.TraceIndex(execDepth).Uint64() > targetTraceIndex {
347-
g.t.Logf("Bisecting: Attack incorrectly for step")
348-
return claim.Attack(ctx, common.Hash{0xdd})
349-
} else if execClaimPosition.TraceIndex(execDepth).Uint64()+1 == targetTraceIndex {
350-
g.t.Logf("Bisecting: Defend incorrectly for step")
351-
return claim.Defend(ctx, common.Hash{0xcc})
352-
} else {
353-
newPosition := execClaimPosition.Defend()
354-
correct, err := provider.Get(ctx, newPosition)
355-
g.require.NoError(err)
356-
g.t.Logf("Bisecting: Defend correctly for step at newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
357-
return claim.Defend(ctx, correct)
358-
}
359-
}
360-
361-
// Attack or Defend depending on whether the claim we're responding to is to the left or right of the trace index
362-
// Induce the honest challenger to attack or defend depending on whether our new position will be to the left or right of the trace index
363-
if execClaimPosition.TraceIndex(execDepth).Uint64() < targetTraceIndex && claim.Depth() != splitDepth+1 {
364-
newPosition := execClaimPosition.Defend()
365-
if newPosition.TraceIndex(execDepth).Uint64() < targetTraceIndex {
366-
g.t.Logf("Bisecting: Defend correct. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
367-
correct, err := provider.Get(ctx, newPosition)
368-
g.require.NoError(err)
369-
return claim.Defend(ctx, correct)
370-
} else {
371-
g.t.Logf("Bisecting: Defend incorrect. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
372-
return claim.Defend(ctx, common.Hash{0xaa})
373-
}
374-
} else {
375-
newPosition := execClaimPosition.Attack()
376-
if newPosition.TraceIndex(execDepth).Uint64() < targetTraceIndex {
377-
g.t.Logf("Bisecting: Attack correct. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
378-
correct, err := provider.Get(ctx, newPosition)
379-
g.require.NoError(err)
380-
return claim.Attack(ctx, correct)
381-
} else {
382-
g.t.Logf("Bisecting: Attack incorrect. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
383-
return claim.Attack(ctx, common.Hash{0xbb})
384-
}
385-
}
330+
return traceBisection(g.t, ctx, claim, splitDepth, execDepth, targetTraceIndex, provider)
386331
}
387-
388-
g.splitGame.LogGameData(ctx)
389332
// Initial bisect to put us on defense
390333
mover := bisectTraceIndex(outputRootClaim)
334+
// Descending the execution game tree to reach the step that loads the preimage
391335
leafClaim := g.splitGame.DefendClaim(ctx, mover, bisectTraceIndex, WithoutWaitingForStep())
392336

393337
// Validate that the preimage was loaded correctly
@@ -533,3 +477,70 @@ func (g *CannonHelper) createCannonTraceProvider(ctx context.Context, l2Node str
533477
translatingProvider := provider.(*trace.TranslatingProvider)
534478
return translatingProvider.Original().(*cannon.CannonTraceProviderForTest), localContext
535479
}
480+
481+
// traceBisection performs a bisection of the trace to the desired targetTraceIndex
482+
func traceBisection(
483+
t *testing.T,
484+
ctx context.Context,
485+
claim *ClaimHelper,
486+
splitDepth types.Depth,
487+
execDepth types.Depth,
488+
targetTraceIndex uint64,
489+
provider *cannon.CannonTraceProviderForTest,
490+
) *ClaimHelper {
491+
execClaimPosition, err := claim.Position.RelativeToAncestorAtDepth(splitDepth + 1)
492+
require.NoError(t, err)
493+
494+
claimTraceIndex := execClaimPosition.TraceIndex(execDepth).Uint64()
495+
t.Logf("Bisecting: Into targetTraceIndex %v: claimIndex=%v at depth=%v. claimPosition=%v execClaimPosition=%v claimTraceIndex=%v",
496+
targetTraceIndex, claim.Index, claim.Depth(), claim.Position, execClaimPosition, claimTraceIndex)
497+
498+
// We always want to position ourselves such that the challenger generates proofs for the targetTraceIndex as prestate
499+
if execClaimPosition.Depth() == execDepth-1 {
500+
if execClaimPosition.TraceIndex(execDepth).Uint64() == targetTraceIndex {
501+
newPosition := execClaimPosition.Attack()
502+
correct, err := provider.Get(ctx, newPosition)
503+
require.NoError(t, err)
504+
t.Logf("Bisecting: Attack correctly for step at newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
505+
return claim.Attack(ctx, correct)
506+
} else if execClaimPosition.TraceIndex(execDepth).Uint64() > targetTraceIndex {
507+
t.Logf("Bisecting: Attack incorrectly for step")
508+
return claim.Attack(ctx, common.Hash{0xdd})
509+
} else if execClaimPosition.TraceIndex(execDepth).Uint64()+1 == targetTraceIndex {
510+
t.Logf("Bisecting: Defend incorrectly for step")
511+
return claim.Defend(ctx, common.Hash{0xcc})
512+
} else {
513+
newPosition := execClaimPosition.Defend()
514+
correct, err := provider.Get(ctx, newPosition)
515+
require.NoError(t, err)
516+
t.Logf("Bisecting: Defend correctly for step at newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
517+
return claim.Defend(ctx, correct)
518+
}
519+
}
520+
521+
// Attack or Defend depending on whether the claim we're responding to is to the left or right of the trace index
522+
// Induce the honest challenger to attack or defend depending on whether our new position will be to the left or right of the trace index
523+
if execClaimPosition.TraceIndex(execDepth).Uint64() < targetTraceIndex && claim.Depth() != splitDepth+1 {
524+
newPosition := execClaimPosition.Defend()
525+
if newPosition.TraceIndex(execDepth).Uint64() < targetTraceIndex {
526+
t.Logf("Bisecting: Defend correct. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
527+
correct, err := provider.Get(ctx, newPosition)
528+
require.NoError(t, err)
529+
return claim.Defend(ctx, correct)
530+
} else {
531+
t.Logf("Bisecting: Defend incorrect. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
532+
return claim.Defend(ctx, common.Hash{0xaa})
533+
}
534+
} else {
535+
newPosition := execClaimPosition.Attack()
536+
if newPosition.TraceIndex(execDepth).Uint64() < targetTraceIndex {
537+
t.Logf("Bisecting: Attack correct. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
538+
correct, err := provider.Get(ctx, newPosition)
539+
require.NoError(t, err)
540+
return claim.Attack(ctx, correct)
541+
} else {
542+
t.Logf("Bisecting: Attack incorrect. newPosition=%v execIndexAtDepth=%v", newPosition, newPosition.TraceIndex(execDepth))
543+
return claim.Attack(ctx, common.Hash{0xbb})
544+
}
545+
}
546+
}

op-e2e/e2eutils/disputegame/helper.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,14 @@ func (h *FactoryHelper) StartSuperCannonGameWithCorrectRoot(ctx context.Context,
230230
return h.startSuperCannonGameOfType(ctx, l2Timestamp, common.Hash(output.SuperRoot), superCannonGameType, opts...)
231231
}
232232

233+
func (h *FactoryHelper) StartSuperCannonGameWithCorrectRootAtTimestamp(ctx context.Context, l2Timestamp uint64, opts ...GameOpt) *SuperCannonGameHelper {
234+
cfg := NewGameCfg(opts...)
235+
h.WaitForSuperTimestamp(l2Timestamp, cfg)
236+
output, err := h.System.SupervisorClient().SuperRootAtTimestamp(ctx, hexutil.Uint64(l2Timestamp))
237+
h.Require.NoErrorf(err, "Failed to get output at timestamp %v", l2Timestamp)
238+
return h.startSuperCannonGameOfType(ctx, l2Timestamp, common.Hash(output.SuperRoot), superCannonGameType, opts...)
239+
}
240+
233241
func (h *FactoryHelper) StartSuperCannonGame(ctx context.Context, rootClaim common.Hash, opts ...GameOpt) *SuperCannonGameHelper {
234242
// Can't create a game at L1 genesis!
235243
require.NoError(h.T, wait.ForBlock(ctx, h.Client, 1))
@@ -238,6 +246,10 @@ func (h *FactoryHelper) StartSuperCannonGame(ctx context.Context, rootClaim comm
238246
return h.startSuperCannonGameOfType(ctx, b.Time(), rootClaim, superCannonGameType, opts...)
239247
}
240248

249+
func (h *FactoryHelper) StartSuperCannonGameAtTimestamp(ctx context.Context, timestamp uint64, rootClaim common.Hash, opts ...GameOpt) *SuperCannonGameHelper {
250+
return h.startSuperCannonGameOfType(ctx, timestamp, rootClaim, superCannonGameType, opts...)
251+
}
252+
241253
func (h *FactoryHelper) startSuperCannonGameOfType(ctx context.Context, timestamp uint64, rootClaim common.Hash, gameType uint32, opts ...GameOpt) *SuperCannonGameHelper {
242254
cfg := NewGameCfg(opts...)
243255
logger := testlog.Logger(h.T, log.LevelInfo).New("role", "CannonGameHelper")
@@ -251,7 +263,7 @@ func (h *FactoryHelper) startSuperCannonGameOfType(ctx context.Context, timestam
251263
tx, err := transactions.PadGasEstimate(h.Opts, 2, func(opts *bind.TransactOpts) (*types.Transaction, error) {
252264
return h.Factory.Create(opts, gameType, rootClaim, extraData)
253265
})
254-
h.Require.NoError(err, "create fault dispute game")
266+
h.Require.NoErrorf(err, "create fault dispute game at timestamp %v. extraData: %x", timestamp, extraData)
255267
rcpt, err := wait.ForReceiptOK(ctx, h.Client, tx.Hash())
256268
h.Require.NoError(err, "wait for create fault dispute game receipt to be OK")
257269
h.Require.Len(rcpt.Logs, 2, "should have emitted a single DisputeGameCreated event")
@@ -378,7 +390,7 @@ func (h *FactoryHelper) WaitForSuperTimestamp(l2Timestamp uint64, cfg *GameCfg)
378390
}
379391

380392
client := h.System.SupervisorClient()
381-
absoluteTimeout := 3 * time.Minute
393+
absoluteTimeout := 5 * time.Minute
382394
ctx, cancel := context.WithTimeout(context.Background(), absoluteTimeout)
383395
defer cancel()
384396

@@ -409,7 +421,7 @@ func (h *FactoryHelper) WaitForSuperTimestamp(l2Timestamp uint64, cfg *GameCfg)
409421
}
410422
// log every 30 seconds
411423
if time.Since(lastLog) > 30*time.Second {
412-
h.T.Logf("Waiting for super timestamp %v", l2Timestamp)
424+
h.T.Logf("Waiting for super timestamp %v. Latest safe timestamp: %v", l2Timestamp, status.SafeTimestamp)
413425
lastLog = time.Now()
414426
}
415427
case <-ctx.Done():

op-e2e/e2eutils/disputegame/split_game_helper.go

+3
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,9 @@ func WithoutWaitingForStep() DefendClaimOpt {
431431
}
432432
}
433433

434+
// SupportClaim uses the supplied Mover to perform moves in an attempt to support the supplied claim.
435+
// It is assumed that the specified claim is valid and that an honest op-challenger is already running.
436+
// When the game has reached the maximum depth it uses the specified stepper to counter the leaf claim.
434437
func (g *SplitGameHelper) SupportClaim(ctx context.Context, claim *ClaimHelper, performMove Mover, attemptStep Stepper) {
435438
g.T.Logf("Supporting claim %v at depth %v", claim.Index, claim.Depth())
436439
for !claim.IsMaxDepth(ctx) {

op-e2e/e2eutils/disputegame/super_cannon_helper.go

+93
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,21 @@ package disputegame
33
import (
44
"context"
55
"crypto/ecdsa"
6+
"math/big"
67
"path/filepath"
78
"testing"
89

910
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
11+
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
12+
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon"
13+
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/split"
1014
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/super"
15+
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils"
1116
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm"
17+
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
1218
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
1319
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
20+
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
1421
"github.com/ethereum-optimism/optimism/op-service/testlog"
1522
"github.com/ethereum/go-ethereum/accounts/abi/bind"
1623
"github.com/ethereum/go-ethereum/common"
@@ -31,6 +38,7 @@ func NewSuperCannonGameHelper(t *testing.T, client *ethclient.Client, opts *bind
3138
challenger.WithSuperCannon(t, system),
3239
challenger.WithFactoryAddress(factoryAddr),
3340
challenger.WithGameAddress(gameAddr),
41+
challenger.WithDepset(t, system.DependencySet()),
3442
}
3543
}
3644
return &SuperCannonGameHelper{
@@ -75,5 +83,90 @@ func (g *SuperCannonGameHelper) CreateHonestActor(ctx context.Context, options .
7583
)
7684
g.Require.NoError(err, "Failed to create output cannon trace accessor")
7785
return NewOutputHonestHelper(g.T, g.Require, &g.SuperGameHelper.SplitGameHelper, g.Game, accessor)
86+
}
87+
88+
// ChallengeToPreimageLoad challenges the supplied execution root claim by inducing a step that requires a preimage to be loaded
89+
// It does this by:
90+
// 1. Identifying the first state transition that loads a global preimage
91+
// 2. Descending the execution game tree to reach the step that loads the preimage
92+
// 3. Asserting that the preimage was indeed loaded by an honest challenger (assuming the preimage is not preloaded)
93+
// This expects an even execution game depth in order for the honest challenger to step on our leaf claim
94+
// PRECOND:
95+
// - The topGameLeaf must be incorrect
96+
// - The execution game depth must be even
97+
func (g *SuperCannonGameHelper) ChallengeToPreimageLoad(ctx context.Context, topGameLeaf *ClaimHelper, challengerKey *ecdsa.PrivateKey, preimage utils.PreimageOpt, preimageCheck PreimageLoadCheck, preloadPreimage bool) {
98+
provider := g.createSuperCannonTraceProvider(ctx, topGameLeaf, challenger.WithPrivKey(challengerKey))
99+
100+
targetTraceIndex, err := provider.FindStep(ctx, 0, preimage)
101+
g.require.NoError(err)
102+
103+
splitDepth := g.splitGame.SplitDepth(ctx)
104+
execDepth := g.splitGame.ExecDepth(ctx)
105+
g.require.NotEqual(topGameLeaf.Position.TraceIndex(execDepth).Uint64(), targetTraceIndex, "cannot move to defend a terminal trace index")
106+
g.require.EqualValues(splitDepth+1, topGameLeaf.Depth(), "supplied claim must be the root of an execution game")
107+
g.require.EqualValues(execDepth%2, 0, "execution game depth must be even") // since we're supporting the execution root claim
108+
109+
if preloadPreimage {
110+
_, _, preimageData, err := provider.GetStepData(ctx, types.NewPosition(execDepth, big.NewInt(int64(targetTraceIndex))))
111+
g.require.NoError(err)
112+
g.UploadPreimage(ctx, preimageData)
113+
g.WaitForPreimageInOracle(ctx, preimageData)
114+
}
115+
116+
bisectTraceIndex := func(claim *ClaimHelper) *ClaimHelper {
117+
return traceBisection(g.t, ctx, claim, splitDepth, execDepth, targetTraceIndex, provider)
118+
}
119+
leafClaim := g.splitGame.DefendClaim(ctx, topGameLeaf, bisectTraceIndex, WithoutWaitingForStep())
120+
121+
// Validate that the preimage was loaded correctly
122+
g.require.NoError(preimageCheck(provider, targetTraceIndex))
123+
124+
// Now the preimage is available wait for the step call to succeed.
125+
leafClaim.WaitForCountered(ctx)
126+
g.splitGame.LogGameData(ctx)
127+
}
128+
129+
func (g *SuperCannonGameHelper) createSuperCannonTraceProvider(ctx context.Context, proposal *ClaimHelper, options ...challenger.Option) *cannon.CannonTraceProviderForTest {
130+
splitDepth := g.splitGame.SplitDepth(ctx)
131+
g.require.EqualValues(proposal.Depth(), splitDepth+1, "outputRootClaim must be the root of an execution game")
132+
133+
logger := testlog.Logger(g.t, log.LevelInfo).New("role", "CannonTraceProvider", "game", g.splitGame.Addr)
134+
opt := g.defaultChallengerOptions()
135+
opt = append(opt, options...)
136+
cfg := challenger.NewChallengerConfig(g.t, g.system, "", opt...)
137+
138+
rootProvider := g.System.SupervisorClient()
139+
140+
l1Head := g.GetL1Head(ctx)
141+
prestateTimestamp, poststateTimestamp, err := g.Game.GetGameRange(ctx)
142+
g.require.NoError(err, "Failed to load block range")
143+
prestateProvider := super.NewSuperRootPrestateProvider(rootProvider, prestateTimestamp)
144+
rollupCfgs, err := super.NewRollupConfigsFromParsed(g.System.RollupCfgs()...)
145+
require.NoError(g.T, err, "failed to create rollup configs")
146+
superProvider := super.NewSuperTraceProvider(logger, rollupCfgs, prestateProvider, rootProvider, l1Head, splitDepth, prestateTimestamp, poststateTimestamp)
147+
148+
var localContext common.Hash
149+
selector := split.NewSplitProviderSelector(superProvider, splitDepth, func(ctx context.Context, depth types.Depth, pre types.Claim, post types.Claim) (types.TraceProvider, error) {
150+
claimInfo, err := super.FetchClaimInfo(ctx, superProvider, pre, post)
151+
g.require.NoError(err, "failed to fetch claim info")
152+
localInputs := utils.LocalGameInputs{
153+
L1Head: l1Head.Hash,
154+
AgreedPreState: claimInfo.AgreedPrestate,
155+
L2Claim: claimInfo.Claim,
156+
L2SequenceNumber: new(big.Int).SetUint64(poststateTimestamp),
157+
}
158+
localContext = split.CreateLocalContext(pre, post)
159+
dir := filepath.Join(cfg.Datadir, "super-cannon-trace")
160+
subdir := filepath.Join(dir, localContext.Hex())
161+
return cannon.NewTraceProviderForTest(logger, metrics.NoopMetrics.ToTypedVmMetrics(types.TraceTypeCannon.String()), cfg, localInputs, subdir, g.splitGame.MaxDepth(ctx)-splitDepth-1), nil
162+
})
163+
164+
claims, err := g.splitGame.Game.GetAllClaims(ctx, rpcblock.Latest)
165+
g.require.NoError(err)
166+
game := types.NewGameState(claims, g.splitGame.MaxDepth(ctx))
78167

168+
provider, err := selector(ctx, game, game.Claims()[proposal.ParentIndex], proposal.Position)
169+
g.require.NoError(err)
170+
translatingProvider := provider.(*trace.TranslatingProvider)
171+
return translatingProvider.Original().(*cannon.CannonTraceProviderForTest)
79172
}

0 commit comments

Comments
 (0)