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
1 change: 1 addition & 0 deletions op-challenger/game/fault/contracts/disputegame.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type GenericGameMetadata struct {
}

type DisputeGameContract interface {
Addr() common.Address
GetL1Head(ctx context.Context) (common.Hash, error)
GetStatus(ctx context.Context) (gameTypes.GameStatus, error)
GetGameRange(ctx context.Context) (prestateBlock uint64, poststateBlock uint64, retErr error)
Expand Down
4 changes: 4 additions & 0 deletions op-challenger/game/fault/contracts/faultdisputegame.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ func mustParseAbi(json []byte) *abi.ABI {
return &loaded
}

func (f *FaultDisputeGameContractLatest) Addr() common.Address {
return f.contract.Addr()
}

// GetBalanceAndDelay returns the total amount of ETH controlled by this contract.
// Note that the ETH is actually held by the DelayedWETH contract which may be shared by multiple games.
// Returns the balance and the address of the contract that actually holds the balance.
Expand Down
18 changes: 16 additions & 2 deletions op-challenger/game/fault/contracts/gamefactory.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,29 @@ func (f *DisputeGameFactoryContract) GetGameCount(ctx context.Context, blockHash
return result.GetBigInt(0).Uint64(), nil
}

func (f *DisputeGameFactoryContract) GetGame(ctx context.Context, idx uint64, blockHash common.Hash) (gameTypes.GameMetadata, error) {
func (f *DisputeGameFactoryContract) GetGame(ctx context.Context, idx uint64, block rpcblock.Block) (gameTypes.GameMetadata, error) {
defer f.metrics.StartContractRequest("GetGame")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.ByHash(blockHash), f.contract.Call(methodGameAtIndex, new(big.Int).SetUint64(idx)))
result, err := f.multiCaller.SingleCall(ctx, block, f.contract.Call(methodGameAtIndex, new(big.Int).SetUint64(idx)))
if err != nil {
return gameTypes.GameMetadata{}, fmt.Errorf("failed to load game %v: %w", idx, err)
}
return f.decodeGame(idx, result), nil
}

func (f *DisputeGameFactoryContract) GetGameStatus(ctx context.Context, idx uint64) (gameTypes.GameStatus, error) {
defer f.metrics.StartContractRequest("GetGameStatus")()
game, err := f.GetGame(ctx, idx, rpcblock.Latest)
if err != nil {
return 0, fmt.Errorf("failed to load game status: %w", err)
}

gameContract, err := NewDisputeGameContract(ctx, f.metrics, f.multiCaller, gameTypes.GameType(game.GameType), game.Proxy)
if err != nil {
return 0, fmt.Errorf("failed to create contract bindings for game %s: %w", game.Proxy, err)
}
return gameContract.GetStatus(ctx)
}

func (f *DisputeGameFactoryContract) getGameImpl(ctx context.Context, gameType gameTypes.GameType) (common.Address, error) {
defer f.metrics.StartContractRequest("GetGameImpl")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodGameImpls, gameType))
Expand Down
34 changes: 28 additions & 6 deletions op-challenger/game/fault/contracts/gamefactory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,37 @@ func TestLoadGame(t *testing.T) {
}
expectedGames := []gameTypes.GameMetadata{game0, game1, game2}
for idx, expected := range expectedGames {
expectGetGame(stubRpc, idx, blockHash, expected)
actual, err := factory.GetGame(context.Background(), uint64(idx), blockHash)
expectGetGame(stubRpc, idx, rpcblock.ByHash(blockHash), expected)
actual, err := factory.GetGame(context.Background(), uint64(idx), rpcblock.ByHash(blockHash))
require.NoError(t, err)
require.Equal(t, expected, actual)
}
})
}
}

func TestGetGameStatus(t *testing.T) {
for _, version := range factoryVersions {
t.Run(version.String(), func(t *testing.T) {
stubRpc, factory := setupDisputeGameFactoryTest(t, version)
game0 := gameTypes.GameMetadata{
Index: 0,
GameType: 0,
Timestamp: 1234,
Proxy: common.Address{0xaa},
}
expectGetGame(stubRpc, 0, rpcblock.Latest, game0)
stubRpc.AddContract(game0.Proxy, snapshots.LoadFaultDisputeGameABI())
expectedStatus := gameTypes.GameStatusChallengerWon
stubRpc.SetResponse(game0.Proxy, methodVersion, rpcblock.Latest, nil, []interface{}{versLatest})
stubRpc.SetResponse(game0.Proxy, methodStatus, rpcblock.Latest, nil, []interface{}{expectedStatus})
actual, err := factory.GetGameStatus(context.Background(), 0)
require.NoError(t, err)
require.Equal(t, expectedStatus, actual)
})
}
}

func TestGetAllGames(t *testing.T) {
for _, version := range factoryVersions {
t.Run(version.String(), func(t *testing.T) {
Expand Down Expand Up @@ -159,7 +181,7 @@ func TestGetAllGames(t *testing.T) {
expectedGames := []gameTypes.GameMetadata{game0, game1, game2}
stubRpc.SetResponse(factoryAddr, methodGameCount, rpcblock.ByHash(blockHash), nil, []interface{}{big.NewInt(int64(len(expectedGames)))})
for idx, expected := range expectedGames {
expectGetGame(stubRpc, idx, blockHash, expected)
expectGetGame(stubRpc, idx, rpcblock.ByHash(blockHash), expected)
}
actualGames, err := factory.GetAllGames(context.Background(), blockHash)
require.NoError(t, err)
Expand Down Expand Up @@ -201,7 +223,7 @@ func TestGetAllGamesAtOrAfter(t *testing.T) {

stubRpc.SetResponse(factoryAddr, methodGameCount, rpcblock.ByHash(blockHash), nil, []interface{}{big.NewInt(int64(len(allGames)))})
for idx, expected := range allGames {
expectGetGame(stubRpc, idx, blockHash, expected)
expectGetGame(stubRpc, idx, rpcblock.ByHash(blockHash), expected)
}
// Set an earliest timestamp that's in the middle of a batch
earliestTimestamp := uint64(test.earliestGameIdx)
Expand Down Expand Up @@ -427,11 +449,11 @@ func TestDecodeDisputeGameCreatedLog(t *testing.T) {
}
}

func expectGetGame(stubRpc *batchingTest.AbiBasedRpc, idx int, blockHash common.Hash, game gameTypes.GameMetadata) {
func expectGetGame(stubRpc *batchingTest.AbiBasedRpc, idx int, block rpcblock.Block, game gameTypes.GameMetadata) {
stubRpc.SetResponse(
factoryAddr,
methodGameAtIndex,
rpcblock.ByHash(blockHash),
block,
[]interface{}{big.NewInt(int64(idx))},
[]interface{}{
game.GameType,
Expand Down
41 changes: 29 additions & 12 deletions op-challenger/game/fault/contracts/optimisticzkdisputegame.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ type claimData struct {

type OptimisticZKDisputeGameContract interface {
DisputeGameContract
CanChallenge(ctx context.Context) (bool, error)
ChallengeTx(ctx context.Context) (txmgr.TxCandidate, error)
GetProposal(ctx context.Context) (common.Hash, uint64, error)
GetChallengerMetadata(ctx context.Context, block rpcblock.Block) (ChallengerMetadata, error)
}

type OptimisticZKDisputeGameContractLatest struct {
Expand All @@ -65,6 +65,10 @@ func NewOptimisticZKDisputeGameContract(
}, nil
}

func (g *OptimisticZKDisputeGameContractLatest) Addr() common.Address {
return g.contract.Addr()
}

// GetMetadata returns the basic game metadata
func (g *OptimisticZKDisputeGameContractLatest) GetMetadata(ctx context.Context, block rpcblock.Block) (GenericGameMetadata, error) {
defer g.metrics.StartContractRequest("GetMetadata")()
Expand Down Expand Up @@ -131,21 +135,34 @@ func (g *OptimisticZKDisputeGameContractLatest) GetGameRange(ctx context.Context
return
}

func (g *OptimisticZKDisputeGameContractLatest) CanChallenge(ctx context.Context) (bool, error) {
data, err := g.claimData(ctx)
if err != nil {
return false, err
}
return data.Status == ProposalStatusUnchallenged, nil
type ChallengerMetadata struct {
ParentIndex uint32
ProposalStatus ProposalStatus
ProposedRoot common.Hash
L2SequenceNumber uint64
Deadline time.Time
}

func (g *OptimisticZKDisputeGameContractLatest) claimData(ctx context.Context) (claimData, error) {
defer g.metrics.StartContractRequest("ClaimData")()
result, err := g.multiCaller.SingleCall(ctx, rpcblock.Latest, g.contract.Call(methodClaimData))
func (g *OptimisticZKDisputeGameContractLatest) GetChallengerMetadata(ctx context.Context, block rpcblock.Block) (ChallengerMetadata, error) {
defer g.metrics.StartContractRequest("GetChallengerMetadata")()
results, err := g.multiCaller.Call(ctx, block,
g.contract.Call(methodClaimData),
g.contract.Call(methodL2SequenceNumber))
if err != nil {
return claimData{}, fmt.Errorf("failed to retrieve claim data: %w", err)
return ChallengerMetadata{}, fmt.Errorf("failed to retrieve challenger metadata: %w", err)
}
return g.decodeClaimData(result), nil
if len(results) != 2 {
return ChallengerMetadata{}, fmt.Errorf("expected 2 results but got %v", len(results))
}
data := g.decodeClaimData(results[0])
l2SeqNum := results[1].GetBigInt(0).Uint64()
return ChallengerMetadata{
ParentIndex: data.ParentIndex,
ProposalStatus: data.Status,
ProposedRoot: data.Claim,
L2SequenceNumber: l2SeqNum,
Deadline: time.Unix(int64(data.Deadline), 0),
}, nil
}

func (g *OptimisticZKDisputeGameContractLatest) ChallengeTx(ctx context.Context) (txmgr.TxCandidate, error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,63 +157,33 @@ func TestZKResolveTx(t *testing.T) {
}
}

func TestZKCanChallenge(t *testing.T) {
func TestZKGetChallengerMetadata(t *testing.T) {
for _, version := range zkVersions {
version := version
t.Run(version.String(), func(t *testing.T) {
parentIndex := uint32(525)
claim := common.Hash{0xbb}
deadline := uint64(42824240)

tests := []struct {
name string
counteredBy common.Address
prover common.Address
status ProposalStatus
expectedResult bool
}{
{
name: "Unchallenged",
status: ProposalStatusUnchallenged,
expectedResult: true,
},
{
name: "Challenged",
counteredBy: common.Address{0xaa},
status: ProposalStatusChallenged,
expectedResult: false,
},
{
name: "UnchallengedAndProven",
prover: common.Address{0xaa},
status: ProposalStatusUnchallengedAndValidProofProvided,
expectedResult: false,
},
{
name: "ChallengedAndProven",
counteredBy: common.Address{0xaa},
prover: common.Address{0xbb},
status: ProposalStatusChallengedAndValidProofProvided,
expectedResult: false,
},
{
name: "Resolved",
status: ProposalStatusResolved,
expectedResult: false,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
stubRpc, game := setupZKDisputeGameTest(t, version)
stubRpc.SetResponse(zkGameAddr, methodClaimData, rpcblock.Latest, nil, []interface{}{
parentIndex, test.counteredBy, test.prover, claim, test.status, deadline,
})
result, err := game.CanChallenge(context.Background())
require.NoError(t, err)
require.Equal(t, test.expectedResult, result)
})
stubRpc, contract := setupZKDisputeGameTest(t, version)
expectedParentIndex := uint32(525)
expectedProposalStatus := ProposalStatusChallengedAndValidProofProvided
counteredBy := common.Address{0xad}
prover := common.Address{0xac}
expectedL2BlockNumber := uint64(123)
expectedRootClaim := common.Hash{0x01, 0x02}
expectedDeadline := time.Unix(84928429020, 0)
block := rpcblock.ByNumber(889)
stubRpc.SetResponse(zkGameAddr, methodClaimData, block, nil, []interface{}{
expectedParentIndex, counteredBy, prover, expectedRootClaim, expectedProposalStatus, uint64(expectedDeadline.Unix()),
})
stubRpc.SetResponse(zkGameAddr, methodL2SequenceNumber, block, nil, []interface{}{new(big.Int).SetUint64(expectedL2BlockNumber)})
actual, err := contract.GetChallengerMetadata(context.Background(), block)
expected := ChallengerMetadata{
ParentIndex: expectedParentIndex,
ProposalStatus: expectedProposalStatus,
ProposedRoot: expectedRootClaim,
L2SequenceNumber: expectedL2BlockNumber,
Deadline: expectedDeadline,
}
require.NoError(t, err)
require.Equal(t, expected, actual)
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion op-challenger/game/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ func (s *Service) registerGameTypes(ctx context.Context, cfg *config.Config) err
if err != nil {
return err
}
err = zk.RegisterGameTypes(ctx, s.systemClock, s.l1Clock, s.logger, s.metrics, cfg, gameTypeRegistry, s.txSender, s.clientProvider)
err = zk.RegisterGameTypes(ctx, s.l1Clock, s.logger, s.metrics, cfg, gameTypeRegistry, s.txSender, s.clientProvider, s.factoryContract)
if err != nil {
return err
}
Expand Down
Loading