Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 65 additions & 24 deletions op-e2e/e2eutils/disputegame/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,17 @@ var (
)

const (
cannonGameType uint32 = 0
permissionedGameType uint32 = 1
superCannonGameType uint32 = 4
superPermissionedGameType uint32 = 5
alphabetGameType uint32 = 255
cannonGameType uint32 = 0
permissionedGameType uint32 = 1
superCannonGameType uint32 = 4
alphabetGameType uint32 = 255
)

type GameCfg struct {
allowFuture bool
allowUnsafe bool
allowFuture bool
allowUnsafe bool
superOutputRoots []eth.Bytes32
super eth.Super
}
type GameOpt interface {
Apply(cfg *GameCfg)
Expand All @@ -77,6 +78,20 @@ func WithFutureProposal() GameOpt {
})
}

// WithInvalidSuperRoot configures the game to use invalid super output roots.
func WithInvalidSuperRoot() GameOpt {
return gameOptFn(func(c *GameCfg) {
c.superOutputRoots = []eth.Bytes32{{0x01}, {0x02}}
})
}

// WithSuper allows specifying a custom super structure.
func WithSuper(super eth.Super) GameOpt {
return gameOptFn(func(c *GameCfg) {
c.super = super
})
}

type DisputeSystem interface {
L1BeaconEndpoint() endpoint.RestHTTP
SupervisorClient() *sources.SupervisorClient
Expand Down Expand Up @@ -225,37 +240,34 @@ func (h *FactoryHelper) StartSuperCannonGameWithCorrectRoot(ctx context.Context,
require.NoError(h.T, err)
l2Timestamp := b.Time()
h.WaitForSuperTimestamp(l2Timestamp, cfg)
output, err := h.System.SupervisorClient().SuperRootAtTimestamp(ctx, hexutil.Uint64(l2Timestamp))
h.Require.NoErrorf(err, "Failed to get output at timestamp %v", l2Timestamp)
return h.startSuperCannonGameOfType(ctx, l2Timestamp, common.Hash(output.SuperRoot), superCannonGameType, opts...)
return h.startSuperCannonGameOfType(ctx, l2Timestamp, superCannonGameType, opts...)
}

func (h *FactoryHelper) StartSuperCannonGameWithCorrectRootAtTimestamp(ctx context.Context, l2Timestamp uint64, opts ...GameOpt) *SuperCannonGameHelper {
cfg := NewGameCfg(opts...)
h.WaitForSuperTimestamp(l2Timestamp, cfg)
output, err := h.System.SupervisorClient().SuperRootAtTimestamp(ctx, hexutil.Uint64(l2Timestamp))
h.Require.NoErrorf(err, "Failed to get output at timestamp %v", l2Timestamp)
return h.startSuperCannonGameOfType(ctx, l2Timestamp, common.Hash(output.SuperRoot), superCannonGameType, opts...)
return h.startSuperCannonGameOfType(ctx, l2Timestamp, superCannonGameType, opts...)
}

func (h *FactoryHelper) StartSuperCannonGame(ctx context.Context, rootClaim common.Hash, opts ...GameOpt) *SuperCannonGameHelper {
func (h *FactoryHelper) StartSuperCannonGame(ctx context.Context, opts ...GameOpt) *SuperCannonGameHelper {
// Can't create a game at L1 genesis!
require.NoError(h.T, wait.ForBlock(ctx, h.Client, 1))
b, err := h.Client.BlockByNumber(ctx, nil)
require.NoError(h.T, err)
return h.startSuperCannonGameOfType(ctx, b.Time(), rootClaim, superCannonGameType, opts...)
return h.startSuperCannonGameOfType(ctx, b.Time(), superCannonGameType, opts...)
}

func (h *FactoryHelper) StartSuperCannonGameAtTimestamp(ctx context.Context, timestamp uint64, rootClaim common.Hash, opts ...GameOpt) *SuperCannonGameHelper {
return h.startSuperCannonGameOfType(ctx, timestamp, rootClaim, superCannonGameType, opts...)
func (h *FactoryHelper) StartSuperCannonGameAtTimestamp(ctx context.Context, timestamp uint64, opts ...GameOpt) *SuperCannonGameHelper {
return h.startSuperCannonGameOfType(ctx, timestamp, superCannonGameType, opts...)
}

func (h *FactoryHelper) startSuperCannonGameOfType(ctx context.Context, timestamp uint64, rootClaim common.Hash, gameType uint32, opts ...GameOpt) *SuperCannonGameHelper {
func (h *FactoryHelper) startSuperCannonGameOfType(ctx context.Context, timestamp uint64, gameType uint32, opts ...GameOpt) *SuperCannonGameHelper {
cfg := NewGameCfg(opts...)
logger := testlog.Logger(h.T, log.LevelInfo).New("role", "CannonGameHelper")
rootProvider := h.System.SupervisorClient()

extraData := h.CreateSuperGameExtraData(ctx, rootProvider, timestamp, cfg)
extraData := h.createSuperGameExtraData(ctx, rootProvider, timestamp, cfg)
rootClaim := crypto.Keccak256Hash(extraData)

ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
defer cancel()
Expand Down Expand Up @@ -348,7 +360,7 @@ func (h *FactoryHelper) CreateBisectionGameExtraData(l2Node string, l2BlockNumbe
return extraData
}

func (h *FactoryHelper) CreateSuperGameExtraData(ctx context.Context, supervisor *sources.SupervisorClient, timestamp uint64, cfg *GameCfg) []byte {
func (h *FactoryHelper) createSuperGameExtraData(ctx context.Context, supervisor *sources.SupervisorClient, timestamp uint64, cfg *GameCfg) []byte {
if !cfg.allowFuture {
timedCtx, cancel := context.WithTimeout(ctx, time.Minute)
defer cancel()
Expand All @@ -361,10 +373,26 @@ func (h *FactoryHelper) CreateSuperGameExtraData(ctx context.Context, supervisor
})
require.NoError(h.T, err, "Safe head did not reach proposal timestamp")
}
h.T.Logf("Creating game with l2 timestamp: %v", timestamp)
extraData := make([]byte, 32)
binary.BigEndian.PutUint64(extraData[24:], timestamp)
return extraData

super := cfg.super
if super == nil {
h.T.Logf("Creating game with l2 timestamp: %v", timestamp)
superResponse, err := h.System.SupervisorClient().SuperRootAtTimestamp(ctx, hexutil.Uint64(timestamp))
h.Require.NoErrorf(err, "Failed to get super root at timestamp %v", timestamp)
super, err = superResponse.ToSuper()
h.Require.NoErrorf(err, "Failed to parse super at timestamp %v", timestamp)
}

superV1, ok := super.(*eth.SuperV1)
h.Require.Truef(ok, "Unsupported super type %T", super)
superV1.Timestamp = timestamp // override in case it's different from the game timestamp
if len(cfg.superOutputRoots) != 0 {
h.Require.Len(cfg.superOutputRoots, len(superV1.Chains), "Super output roots length mismatch")
for i := range superV1.Chains {
superV1.Chains[i].Output = cfg.superOutputRoots[i]
}
}
return superV1.Marshal()
}

func (h *FactoryHelper) WaitForBlock(l2Node string, l2BlockNumber uint64, cfg *GameCfg) {
Expand Down Expand Up @@ -442,3 +470,16 @@ func (h *FactoryHelper) StartChallenger(ctx context.Context, name string, option
})
return c
}

// CreateInvalidSuper creates a SuperV1 with invalid outputs
func CreateInvalidSuper(timestamp uint64) *eth.SuperV1 {
return &eth.SuperV1{
Timestamp: timestamp,
Chains: []eth.ChainIDAndOutput{
{
ChainID: eth.ChainIDFromUInt64(1),
Output: eth.Bytes32{0x01},
},
},
}
}
36 changes: 19 additions & 17 deletions op-e2e/faultproofs/super_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func TestCreateSuperCannonGame(t *testing.T) {
ctx := context.Background()
sys, disputeGameFactory, _ := StartInteropFaultDisputeSystem(t, WithAllocType(allocType))
sys.L2IDs()
game := disputeGameFactory.StartSuperCannonGame(ctx, common.Hash{0x01})
game := disputeGameFactory.StartSuperCannonGame(ctx, disputegame.WithInvalidSuperRoot())
game.LogGameData(ctx)
})
}
Expand All @@ -40,7 +40,7 @@ func TestSuperCannonGame(t *testing.T) {
RunTestAcrossVmTypes(t, func(t *testing.T, allocType config.AllocType) {
ctx := context.Background()
sys, disputeGameFactory, _ := StartInteropFaultDisputeSystem(t, WithAllocType(allocType))
game := disputeGameFactory.StartSuperCannonGame(ctx, common.Hash{0x01})
game := disputeGameFactory.StartSuperCannonGame(ctx, disputegame.WithInvalidSuperRoot())
testCannonGame(t, ctx, createSuperGameArena(t, sys, game), &game.SplitGameHelper)
})
}
Expand All @@ -49,7 +49,7 @@ func TestSuperCannonGame_WithBlobs(t *testing.T) {
RunTestAcrossVmTypes(t, func(t *testing.T, allocType config.AllocType) {
ctx := context.Background()
sys, disputeGameFactory, _ := StartInteropFaultDisputeSystem(t, WithAllocType(allocType), WithBlobBatches())
game := disputeGameFactory.StartSuperCannonGame(ctx, common.Hash{0x01})
game := disputeGameFactory.StartSuperCannonGame(ctx, disputegame.WithInvalidSuperRoot())
testCannonGame(t, ctx, createSuperGameArena(t, sys, game), &game.SplitGameHelper)
})
}
Expand All @@ -58,7 +58,7 @@ func TestSuperCannonGame_ChallengeAllZeroClaim(t *testing.T) {
RunTestAcrossVmTypes(t, func(t *testing.T, allocType config.AllocType) {
ctx := context.Background()
sys, disputeGameFactory, _ := StartInteropFaultDisputeSystem(t, WithAllocType(allocType))
game := disputeGameFactory.StartSuperCannonGame(ctx, common.Hash{0x01})
game := disputeGameFactory.StartSuperCannonGame(ctx, disputegame.WithInvalidSuperRoot())
testCannonChallengeAllZeroClaim(t, ctx, createSuperGameArena(t, sys, game), &game.SplitGameHelper)
}, WithNextVMOnly[any]())
}
Expand Down Expand Up @@ -88,7 +88,7 @@ func TestSuperCannonPublishCannonRootClaim(t *testing.T) {
require.NoError(t, err)
disputeL2SequenceNumber := b.Time() + test.disputeL2SequenceNumberOffset

game := disputeGameFactory.StartSuperCannonGameAtTimestamp(ctx, disputeL2SequenceNumber, common.Hash{0x01})
game := disputeGameFactory.StartSuperCannonGameAtTimestamp(ctx, disputeL2SequenceNumber, disputegame.WithInvalidSuperRoot())
game.DisputeLastBlock(ctx)
game.LogGameData(ctx)
game.StartChallenger(ctx, "Challenger", challenger.WithPrivKey(aliceKey(t)), challenger.WithDepset(t, sys.DependencySet()))
Expand Down Expand Up @@ -140,7 +140,7 @@ func TestSuperCannonDisputeGame(t *testing.T) {
RunTestsAcrossVmTypes(t, tests, func(t *testing.T, allocType config.AllocType, test TestCase) {
ctx := context.Background()
sys, disputeGameFactory, _ := StartInteropFaultDisputeSystem(t, WithAllocType(allocType))
game := disputeGameFactory.StartSuperCannonGame(ctx, common.Hash{0x01, 0xaa})
game := disputeGameFactory.StartSuperCannonGame(ctx, disputegame.WithInvalidSuperRoot())
game.LogGameData(ctx)

disputeClaim := game.DisputeLastBlock(ctx)
Expand Down Expand Up @@ -180,7 +180,7 @@ func TestSuperCannonDefendStep(t *testing.T) {
RunTestAcrossVmTypes(t, func(t *testing.T, allocType config.AllocType) {
ctx := context.Background()
sys, disputeGameFactory, _ := StartInteropFaultDisputeSystem(t, WithAllocType(allocType))
game := disputeGameFactory.StartSuperCannonGame(ctx, common.Hash{0x01})
game := disputeGameFactory.StartSuperCannonGame(ctx, disputegame.WithInvalidSuperRoot())
testCannonDefendStep(t, ctx, createSuperGameArena(t, sys, game), &game.SplitGameHelper)
}, WithNextVMOnly[any]())
}
Expand Down Expand Up @@ -300,7 +300,7 @@ func TestSuperCannonPoisonedPostState(t *testing.T) {
RunTestAcrossVmTypes(t, func(t *testing.T, allocType config.AllocType) {
ctx := context.Background()
sys, disputeGameFactory, _ := StartInteropFaultDisputeSystem(t, WithAllocType(allocType))
game := disputeGameFactory.StartSuperCannonGame(ctx, common.Hash{0x01})
game := disputeGameFactory.StartSuperCannonGame(ctx, disputegame.WithInvalidSuperRoot())
testCannonPoisonedPostState(t, ctx, createSuperGameArena(t, sys, game), &game.SplitGameHelper)
}, WithNextVMOnly[any]())
}
Expand All @@ -318,7 +318,7 @@ func TestSuperCannonRootBeyondProposedBlock_InvalidRoot(t *testing.T) {
RunTestAcrossVmTypes(t, func(t *testing.T, allocType config.AllocType) {
ctx := context.Background()
sys, disputeGameFactory, _ := StartInteropFaultDisputeSystem(t, WithAllocType(allocType))
game := disputeGameFactory.StartSuperCannonGame(ctx, common.Hash{0x01})
game := disputeGameFactory.StartSuperCannonGame(ctx, disputegame.WithInvalidSuperRoot())
testDisputeRootBeyondProposedBlockInvalidOutputRoot(t, ctx, createSuperGameArena(t, sys, game), &game.SplitGameHelper)
}, WithNextVMOnly[any]())
}
Expand All @@ -327,7 +327,7 @@ func TestSuperCannonRootChangeClaimedRoot(t *testing.T) {
RunTestAcrossVmTypes(t, func(t *testing.T, allocType config.AllocType) {
ctx := context.Background()
sys, disputeGameFactory, _ := StartInteropFaultDisputeSystem(t, WithAllocType(allocType))
game := disputeGameFactory.StartSuperCannonGame(ctx, common.Hash{0x01})
game := disputeGameFactory.StartSuperCannonGame(ctx, disputegame.WithInvalidSuperRoot())
testDisputeRootChangeClaimedRoot(t, ctx, createSuperGameArena(t, sys, game), &game.SplitGameHelper)
}, WithNextVMOnly[any]())
}
Expand Down Expand Up @@ -387,8 +387,7 @@ func TestSuperInvalidateUnsafeProposal(t *testing.T) {

// Root claim is _dishonest_ because the required data is not available on L1
unsafeSuper := createSuperRoot(t, ctx, sys, unsafeTimestamp)
unsafeRoot := eth.SuperRoot(unsafeSuper)
game := disputeGameFactory.StartSuperCannonGameAtTimestamp(ctx, unsafeTimestamp, common.Hash(unsafeRoot), disputegame.WithFutureProposal())
game := disputeGameFactory.StartSuperCannonGameAtTimestamp(ctx, unsafeTimestamp, disputegame.WithSuper(unsafeSuper), disputegame.WithFutureProposal())

correctTrace := game.CreateHonestActor(ctx, disputegame.WithPrivKey(malloryKey(t)), func(c *disputegame.HonestActorConfig) {
c.ChallengerOpts = append(c.ChallengerOpts, challenger.WithDepset(t, sys.DependencySet()))
Expand Down Expand Up @@ -446,8 +445,7 @@ func TestSuperInalidateUnsafeProposal_SecondChainIsUnsafe(t *testing.T) {

// Root claim is _dishonest_ because the required data to construct the chain B output root is not available on L1
unsafeSuper := createSuperRoot(t, ctx, sys, gameTimestamp)
unsafeRoot := eth.SuperRoot(unsafeSuper)
game := disputeGameFactory.StartSuperCannonGameAtTimestamp(ctx, gameTimestamp, common.Hash(unsafeRoot), disputegame.WithFutureProposal())
game := disputeGameFactory.StartSuperCannonGameAtTimestamp(ctx, gameTimestamp, disputegame.WithSuper(unsafeSuper), disputegame.WithFutureProposal())

prestateTimestamp, _, err := game.Game.GetGameRange(ctx)
require.NoError(t, err, "Failed to get game range")
Expand Down Expand Up @@ -501,7 +499,9 @@ func TestSuperInvalidateProposalForFutureBlock(t *testing.T) {
sys, disputeGameFactory, _ := StartInteropFaultDisputeSystem(t, WithAllocType(allocType))
// Root claim is _dishonest_ because the required data is not available on L1
farFutureTimestamp := time.Now().Add(time.Second * 10_000_000).Unix()
game := disputeGameFactory.StartSuperCannonGameAtTimestamp(ctx, uint64(farFutureTimestamp), common.Hash{0x01}, disputegame.WithFutureProposal())
invalidSuper := disputegame.CreateInvalidSuper(uint64(farFutureTimestamp))
// manually create an invalid super since we can't rely on the super RPC to fetch one at the far timestamp
game := disputeGameFactory.StartSuperCannonGameAtTimestamp(ctx, uint64(farFutureTimestamp), disputegame.WithSuper(invalidSuper), disputegame.WithFutureProposal())
correctTrace := game.CreateHonestActor(ctx, disputegame.WithPrivKey(malloryKey(t)), func(c *disputegame.HonestActorConfig) {
c.ChallengerOpts = append(c.ChallengerOpts, challenger.WithDepset(t, sys.DependencySet()))
})
Expand Down Expand Up @@ -567,6 +567,8 @@ func TestSuperInvalidateCorrectProposalFutureBlock(t *testing.T) {
require.NoError(t, err, "Failed to get sync status")
superRoot, err := client.SuperRootAtTimestamp(ctx, hexutil.Uint64(status.SafeTimestamp))
require.NoError(t, err, "Failed to get super root at safe timestamp")
super, err := superRoot.ToSuper()
require.NoError(t, err, "Failed to parse super root")

// Stop the batcher so the safe head doesn't advance
for _, id := range sys.L2IDs() {
Expand All @@ -575,7 +577,7 @@ func TestSuperInvalidateCorrectProposalFutureBlock(t *testing.T) {

// Create a dispute game with a proposal that is valid at `superRoot.Timestamp`, but that claims to correspond to timestamp
// `superRoot.Timestamp + 100000`. This is dishonest, because the superchain hasn't reached this timestamp yet.
game := disputeGameFactory.StartSuperCannonGameAtTimestamp(ctx, superRoot.Timestamp+100_000, common.Hash(superRoot.SuperRoot), disputegame.WithFutureProposal())
game := disputeGameFactory.StartSuperCannonGameAtTimestamp(ctx, superRoot.Timestamp+100_000, disputegame.WithSuper(super), disputegame.WithFutureProposal())

game.StartChallenger(ctx, "Challenger", challenger.WithPrivKey(aliceKey(t)), challenger.WithDepset(t, sys.DependencySet()))

Expand Down Expand Up @@ -673,7 +675,7 @@ func TestSuperCannonHonestSafeTraceExtensionInvalidRoot(t *testing.T) {

disputeGameFactory.WaitForSuperTimestamp(safeTimestamp, new(disputegame.GameCfg))

game := disputeGameFactory.StartSuperCannonGameAtTimestamp(ctx, safeTimestamp-1, common.Hash{0xCA, 0xFE})
game := disputeGameFactory.StartSuperCannonGameAtTimestamp(ctx, safeTimestamp-1, disputegame.WithInvalidSuperRoot())
require.NotNil(t, game)

// Create a correct trace actor with an honest trace extending to safeTimestamp
Expand Down