Skip to content

Commit 434d74f

Browse files
ajsuttonRjected
authored andcommitted
op-challenger: Interop vm runner (ethereum-optimism#14669)
* op-challenger: Add config option to set dependency set config * Fix expected error message * op-challenger: Update op-program executor to handle interop inputs. * op-challenger: Support super-cannon in run trace. * Fixes. * Remove refactoring to introduce ToSuper()
1 parent 806ba1b commit 434d74f

File tree

5 files changed

+232
-103
lines changed

5 files changed

+232
-103
lines changed

op-challenger/runner/factory.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func createTraceProvider(
2929
dir string,
3030
) (types.TraceProvider, error) {
3131
switch traceType {
32-
case types.TraceTypeCannon:
32+
case types.TraceTypeCannon, types.TraceTypeSuperCannon:
3333
serverExecutor := vm.NewOpProgramServerExecutor(logger)
3434
stateConverter := cannon.NewStateConverter(cfg.Cannon)
3535
prestate, err := prestateSource.getPrestate(ctx, logger, cfg.CannonAbsolutePreStateBaseURL, cfg.CannonAbsolutePreState, dir, stateConverter)
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package runner
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"math/big"
8+
"math/rand"
9+
10+
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/super"
11+
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils"
12+
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
13+
"github.com/ethereum-optimism/optimism/op-service/sources"
14+
"github.com/ethereum/go-ethereum/common"
15+
"github.com/ethereum/go-ethereum/crypto"
16+
"github.com/ethereum/go-ethereum/log"
17+
)
18+
19+
func createGameInputs(ctx context.Context, log log.Logger, rollupClient *sources.RollupClient, supervisorClient *sources.SupervisorClient, typeName string, traceType types.TraceType) (utils.LocalGameInputs, error) {
20+
switch traceType {
21+
case types.TraceTypeSuperCannon, types.TraceTypeSuperPermissioned:
22+
if supervisorClient == nil {
23+
return utils.LocalGameInputs{}, fmt.Errorf("trace type %s requires supervisor rpc to be set", traceType)
24+
}
25+
return createGameInputsInterop(ctx, log, supervisorClient, typeName)
26+
default:
27+
if rollupClient == nil {
28+
return utils.LocalGameInputs{}, fmt.Errorf("trace type %s requires rollup rpc to be set", traceType)
29+
}
30+
return createGameInputsSingle(ctx, log, rollupClient, typeName)
31+
}
32+
}
33+
34+
func createGameInputsSingle(ctx context.Context, log log.Logger, client *sources.RollupClient, typeName string) (utils.LocalGameInputs, error) {
35+
status, err := client.SyncStatus(ctx)
36+
if err != nil {
37+
return utils.LocalGameInputs{}, fmt.Errorf("failed to get rollup sync status: %w", err)
38+
}
39+
log.Info("Got sync status", "status", status, "type", typeName)
40+
41+
if status.FinalizedL2.Number == 0 {
42+
return utils.LocalGameInputs{}, errors.New("safe head is 0")
43+
}
44+
l1Head := status.FinalizedL1
45+
if status.FinalizedL1.Number > status.CurrentL1.Number {
46+
// Restrict the L1 head to a block that has actually been processed by op-node.
47+
// This only matters if op-node is behind and hasn't processed all finalized L1 blocks yet.
48+
l1Head = status.CurrentL1
49+
log.Info("Node has not completed syncing finalized L1 block, using CurrentL1 instead", "type", typeName)
50+
} else if status.FinalizedL1.Number == 0 {
51+
// The node is resetting its pipeline and has set FinalizedL1 to 0, use the current L1 instead as it is the best
52+
// hope of getting a non-zero L1 block
53+
l1Head = status.CurrentL1
54+
log.Warn("Node has zero finalized L1 block, using CurrentL1 instead", "type", typeName)
55+
}
56+
log.Info("Using L1 head", "head", l1Head, "type", typeName)
57+
if l1Head.Number == 0 {
58+
return utils.LocalGameInputs{}, errors.New("l1 head is 0")
59+
}
60+
blockNumber, err := findL2BlockNumberToDispute(ctx, log, client, l1Head.Number, status.FinalizedL2.Number)
61+
if err != nil {
62+
return utils.LocalGameInputs{}, fmt.Errorf("failed to find l2 block number to dispute: %w", err)
63+
}
64+
claimOutput, err := client.OutputAtBlock(ctx, blockNumber)
65+
if err != nil {
66+
return utils.LocalGameInputs{}, fmt.Errorf("failed to get claim output: %w", err)
67+
}
68+
parentOutput, err := client.OutputAtBlock(ctx, blockNumber-1)
69+
if err != nil {
70+
return utils.LocalGameInputs{}, fmt.Errorf("failed to get claim output: %w", err)
71+
}
72+
localInputs := utils.LocalGameInputs{
73+
L1Head: l1Head.Hash,
74+
L2Head: parentOutput.BlockRef.Hash,
75+
L2OutputRoot: common.Hash(parentOutput.OutputRoot),
76+
L2Claim: common.Hash(claimOutput.OutputRoot),
77+
L2BlockNumber: new(big.Int).SetUint64(blockNumber),
78+
}
79+
return localInputs, nil
80+
}
81+
82+
func createGameInputsInterop(ctx context.Context, log log.Logger, client *sources.SupervisorClient, typeName string) (utils.LocalGameInputs, error) {
83+
status, err := client.SyncStatus(ctx)
84+
if err != nil {
85+
return utils.LocalGameInputs{}, fmt.Errorf("failed to get rollup sync status: %w", err)
86+
}
87+
log.Info("Got sync status", "status", status, "type", typeName)
88+
89+
claimTimestamp := status.FinalizedTimestamp
90+
agreedTimestamp := claimTimestamp - 1
91+
if claimTimestamp == 0 {
92+
return utils.LocalGameInputs{}, errors.New("finalized timestamp is 0")
93+
}
94+
l1Head := status.MinSyncedL1
95+
log.Info("Using L1 head", "head", l1Head, "type", typeName)
96+
if l1Head.Number == 0 {
97+
return utils.LocalGameInputs{}, errors.New("l1 head is 0")
98+
}
99+
100+
prestateProvider := super.NewSuperRootPrestateProvider(client, agreedTimestamp)
101+
gameDepth := types.Depth(30)
102+
provider := super.NewSuperTraceProvider(log, nil, prestateProvider, client, l1Head.ID(), gameDepth, agreedTimestamp, claimTimestamp+10)
103+
var agreedPrestate []byte
104+
var claim common.Hash
105+
switch 2 { //rand.Intn(3) {
106+
case 0: // Derive block on first chain
107+
log.Info("Running first chain")
108+
prestate, err := prestateProvider.AbsolutePreState(ctx)
109+
if err != nil {
110+
return utils.LocalGameInputs{}, fmt.Errorf("failed to get pre-state commitment: %w", err)
111+
}
112+
agreedPrestate = prestate.Marshal()
113+
claim, err = provider.Get(ctx, types.NewPosition(gameDepth, big.NewInt(0)))
114+
if err != nil {
115+
return utils.LocalGameInputs{}, fmt.Errorf("failed to get claim: %w", err)
116+
}
117+
case 1: // Derive block on second chain
118+
log.Info("Deriving second chain")
119+
agreedPrestate, err = provider.GetPreimageBytes(ctx, types.NewPosition(gameDepth, big.NewInt(0)))
120+
if err != nil {
121+
return utils.LocalGameInputs{}, fmt.Errorf("failed to get agreed prestate at position 0: %w", err)
122+
}
123+
claim, err = provider.Get(ctx, types.NewPosition(gameDepth, big.NewInt(1)))
124+
if err != nil {
125+
return utils.LocalGameInputs{}, fmt.Errorf("failed to get claim: %w", err)
126+
}
127+
case 2: // Consolidate
128+
log.Info("Running consolidate step")
129+
step := int64(super.StepsPerTimestamp - 1)
130+
agreedPrestate, err = provider.GetPreimageBytes(ctx, types.NewPosition(gameDepth, big.NewInt(step-1)))
131+
if err != nil {
132+
return utils.LocalGameInputs{}, fmt.Errorf("failed to get agreed prestate at position 0: %w", err)
133+
}
134+
claim, err = provider.Get(ctx, types.NewPosition(gameDepth, big.NewInt(step)))
135+
if err != nil {
136+
return utils.LocalGameInputs{}, fmt.Errorf("failed to get claim: %w", err)
137+
}
138+
}
139+
localInputs := utils.LocalGameInputs{
140+
L1Head: l1Head.Hash,
141+
AgreedPreState: agreedPrestate,
142+
L2OutputRoot: crypto.Keccak256Hash(agreedPrestate),
143+
L2Claim: claim,
144+
L2BlockNumber: new(big.Int).SetUint64(claimTimestamp + 10), // Anything beyond the claim
145+
}
146+
return localInputs, nil
147+
}
148+
149+
func findL2BlockNumberToDispute(ctx context.Context, log log.Logger, client *sources.RollupClient, l1HeadNum uint64, l2BlockNum uint64) (uint64, error) {
150+
// Try to find a L1 block prior to the batch that make l2BlockNum safe
151+
// Limits how far back we search to 10 * 32 blocks
152+
const skipSize = uint64(32)
153+
for i := 0; i < 10; i++ {
154+
if l1HeadNum < skipSize {
155+
// Too close to genesis, give up and just use the original block
156+
log.Info("Failed to find prior batch.")
157+
return l2BlockNum, nil
158+
}
159+
l1HeadNum -= skipSize
160+
prevSafeHead, err := client.SafeHeadAtL1Block(ctx, l1HeadNum)
161+
if err != nil {
162+
return 0, fmt.Errorf("failed to get prior safe head at L1 block %v: %w", l1HeadNum, err)
163+
}
164+
if prevSafeHead.SafeHead.Number < l2BlockNum {
165+
switch rand.Intn(3) {
166+
case 0: // First block of span batch
167+
return prevSafeHead.SafeHead.Number + 1, nil
168+
case 1: // Last block of span batch
169+
return prevSafeHead.SafeHead.Number, nil
170+
case 2: // Random block, probably but not guaranteed to be in the middle of a span batch
171+
firstBlockInSpanBatch := prevSafeHead.SafeHead.Number + 1
172+
if l2BlockNum <= firstBlockInSpanBatch {
173+
// There is only one block in the next batch so we just have to use it
174+
return l2BlockNum, nil
175+
}
176+
offset := rand.Intn(int(l2BlockNum - firstBlockInSpanBatch))
177+
return firstBlockInSpanBatch + uint64(offset), nil
178+
}
179+
180+
}
181+
if prevSafeHead.SafeHead.Number < l2BlockNum {
182+
// We walked back far enough to be before the batch that included l2BlockNum
183+
// So use the first block after the prior safe head as the disputed block.
184+
// It must be the first block in a batch.
185+
return prevSafeHead.SafeHead.Number + 1, nil
186+
}
187+
}
188+
log.Warn("Failed to find prior batch", "l2BlockNum", l2BlockNum, "earliestCheckL1Block", l1HeadNum)
189+
return l2BlockNum, nil
190+
}

op-challenger/runner/prestates.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ func (f *HashPrestateFetcher) getPrestate(ctx context.Context, _ log.Logger, pre
6767
return prestate, nil
6868
}
6969

70+
type LocalPrestateFetcher struct {
71+
path string
72+
}
73+
74+
func (f *LocalPrestateFetcher) getPrestate(ctx context.Context, logger log.Logger, prestateBaseUrl *url.URL, _ string, dataDir string, stateConverter vm.StateConverter) (string, error) {
75+
return f.path, nil
76+
}
77+
7078
// NamedPrestateFetcher downloads a file with a specified name from the prestate base URL and uses it as the prestate.
7179
// The file is re-downloaded on each run rather than being cached. This makes it possible to run the latest builds
7280
// from develop.

0 commit comments

Comments
 (0)