Skip to content
Open
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
26 changes: 22 additions & 4 deletions op-proposer/contracts/disputegamefactory.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,23 @@ func (f *DisputeGameFactory) HasProposedSince(ctx context.Context, proposer comm
return false, time.Time{}, common.Hash{}, nil
}
for idx := gameCount - 1; ; idx-- {
game, err := f.gameAtIndex(ctx, idx)
gt, ts, err := f.gameBasicInfoAtIndex(ctx, idx)
if err != nil {
return false, time.Time{}, common.Hash{}, fmt.Errorf("failed to get dispute game %d: %w", idx, err)
}
if game.Timestamp.Before(cutoff) {
if ts.Before(cutoff) {
// Reached a game that is before the expected cutoff, so we haven't found a suitable proposal
return false, time.Time{}, common.Hash{}, nil
}
if game.GameType == gameType && game.Proposer == proposer {
if gt == gameType {
game, err := f.gameAtIndex(ctx, idx)
if err != nil {
return false, time.Time{}, common.Hash{}, fmt.Errorf("failed to get dispute game %d: %w", idx, err)
}
// Found a matching proposal
return true, game.Timestamp, game.Claim, nil
if game.Proposer == proposer {
return true, game.Timestamp, game.Claim, nil
}
}
if idx == 0 { // Need to check here rather than in the for condition to avoid underflow
// Checked every game and didn't find a match
Expand Down Expand Up @@ -151,3 +157,15 @@ func (f *DisputeGameFactory) gameAtIndex(ctx context.Context, idx uint64) (gameM
Claim: claim,
}, nil
}

// gameBasicInfoAtIndex returns basic game info without loading claimData
// This avoids reverts when iterating over games with incompatible ABIs
func (f *DisputeGameFactory) gameBasicInfoAtIndex(ctx context.Context, idx uint64) (gameType uint32, timestamp time.Time, err error) {
cCtx, cancel := context.WithTimeout(ctx, f.networkTimeout)
defer cancel()
result, err := f.caller.SingleCall(cCtx, rpcblock.Latest, f.contract.Call(methodGameAtIndex, new(big.Int).SetUint64(idx)))
if err != nil {
return 0, time.Time{}, fmt.Errorf("failed to load game %v: %w", idx, err)
}
return result.GetUint32(0), time.Unix(int64(result.GetUint64(1)), 0), nil
}
19 changes: 19 additions & 0 deletions op-proposer/contracts/disputegamefactory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,25 @@ func TestHasProposedSince(t *testing.T) {
require.Equal(t, expectedProposalTime, proposalTime)
require.Equal(t, common.Hash{0xdd}, claim)
})

t.Run("SkipsClaimForDifferentType-"+contractType.name, func(t *testing.T) {
stubRpc, factory := setupDisputeGameFactoryTest(t)
// idx 0: matching game type with claim data
withClaims(stubRpc, contractType.abi,
gameMetadata{GameType: 0, Timestamp: time.Unix(1400, 0), Address: common.Address{0x66}, Proposer: proposerAddr},
)
// Append idx 1: different game type, no claim data (would revert if called)
stubRpc.SetResponse(factoryAddr, methodGameCount, rpcblock.Latest, nil, []interface{}{big.NewInt(2)})
stubRpc.SetResponse(factoryAddr, methodGameAtIndex, rpcblock.Latest, []interface{}{big.NewInt(1)}, []interface{}{
uint32(1), uint64(1500), common.Address{0x55},
})

proposed, proposalTime, claim, err := factory.HasProposedSince(context.Background(), proposerAddr, cutOffTime, 0)
require.NoError(t, err)
require.True(t, proposed)
require.Equal(t, time.Unix(1400, 0), proposalTime)
require.Equal(t, common.Hash{0xdd}, claim)
})
}
}

Expand Down