Commit 36ee1ec6 authored by Adrian Sutton's avatar Adrian Sutton

op-challenger: Load games by block hash instead of block number.

parent 4a712b28
...@@ -32,16 +32,16 @@ func NewDisputeGameFactoryContract(addr common.Address, caller *batching.MultiCa ...@@ -32,16 +32,16 @@ func NewDisputeGameFactoryContract(addr common.Address, caller *batching.MultiCa
}, nil }, nil
} }
func (f *DisputeGameFactoryContract) GetGameCount(ctx context.Context, blockNum uint64) (uint64, error) { func (f *DisputeGameFactoryContract) GetGameCount(ctx context.Context, blockHash common.Hash) (uint64, error) {
result, err := f.multiCaller.SingleCall(ctx, batching.BlockByNumber(blockNum), f.contract.Call(methodGameCount)) result, err := f.multiCaller.SingleCall(ctx, batching.BlockByHash(blockHash), f.contract.Call(methodGameCount))
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to load game count: %w", err) return 0, fmt.Errorf("failed to load game count: %w", err)
} }
return result.GetBigInt(0).Uint64(), nil return result.GetBigInt(0).Uint64(), nil
} }
func (f *DisputeGameFactoryContract) GetGame(ctx context.Context, idx uint64, blockNum uint64) (types.GameMetadata, error) { func (f *DisputeGameFactoryContract) GetGame(ctx context.Context, idx uint64, blockHash common.Hash) (types.GameMetadata, error) {
result, err := f.multiCaller.SingleCall(ctx, batching.BlockByNumber(blockNum), f.contract.Call(methodGameAtIndex, new(big.Int).SetUint64(idx))) result, err := f.multiCaller.SingleCall(ctx, batching.BlockByHash(blockHash), f.contract.Call(methodGameAtIndex, new(big.Int).SetUint64(idx)))
if err != nil { if err != nil {
return types.GameMetadata{}, fmt.Errorf("failed to load game %v: %w", idx, err) return types.GameMetadata{}, fmt.Errorf("failed to load game %v: %w", idx, err)
} }
......
...@@ -18,7 +18,7 @@ var ( ...@@ -18,7 +18,7 @@ var (
) )
func TestDisputeGameFactorySimpleGetters(t *testing.T) { func TestDisputeGameFactorySimpleGetters(t *testing.T) {
blockNum := uint64(23) blockHash := common.Hash{0xbb, 0xcd}
tests := []struct { tests := []struct {
method string method string
args []interface{} args []interface{}
...@@ -31,7 +31,7 @@ func TestDisputeGameFactorySimpleGetters(t *testing.T) { ...@@ -31,7 +31,7 @@ func TestDisputeGameFactorySimpleGetters(t *testing.T) {
result: big.NewInt(9876), result: big.NewInt(9876),
expected: uint64(9876), expected: uint64(9876),
call: func(game *DisputeGameFactoryContract) (any, error) { call: func(game *DisputeGameFactoryContract) (any, error) {
return game.GetGameCount(context.Background(), blockNum) return game.GetGameCount(context.Background(), blockHash)
}, },
}, },
} }
...@@ -39,7 +39,7 @@ func TestDisputeGameFactorySimpleGetters(t *testing.T) { ...@@ -39,7 +39,7 @@ func TestDisputeGameFactorySimpleGetters(t *testing.T) {
test := test test := test
t.Run(test.method, func(t *testing.T) { t.Run(test.method, func(t *testing.T) {
stubRpc, factory := setupDisputeGameFactoryTest(t) stubRpc, factory := setupDisputeGameFactoryTest(t)
stubRpc.SetResponse(factoryAddr, test.method, batching.BlockByNumber(blockNum), nil, []interface{}{test.result}) stubRpc.SetResponse(factoryAddr, test.method, batching.BlockByHash(blockHash), nil, []interface{}{test.result})
status, err := test.call(factory) status, err := test.call(factory)
require.NoError(t, err) require.NoError(t, err)
expected := test.expected expected := test.expected
...@@ -52,7 +52,7 @@ func TestDisputeGameFactorySimpleGetters(t *testing.T) { ...@@ -52,7 +52,7 @@ func TestDisputeGameFactorySimpleGetters(t *testing.T) {
} }
func TestLoadGame(t *testing.T) { func TestLoadGame(t *testing.T) {
blockNum := uint64(23) blockHash := common.Hash{0xbb, 0xce}
stubRpc, factory := setupDisputeGameFactoryTest(t) stubRpc, factory := setupDisputeGameFactoryTest(t)
game0 := types.GameMetadata{ game0 := types.GameMetadata{
GameType: 0, GameType: 0,
...@@ -71,18 +71,18 @@ func TestLoadGame(t *testing.T) { ...@@ -71,18 +71,18 @@ func TestLoadGame(t *testing.T) {
} }
expectedGames := []types.GameMetadata{game0, game1, game2} expectedGames := []types.GameMetadata{game0, game1, game2}
for idx, expected := range expectedGames { for idx, expected := range expectedGames {
expectGetGame(stubRpc, idx, blockNum, expected) expectGetGame(stubRpc, idx, blockHash, expected)
actual, err := factory.GetGame(context.Background(), uint64(idx), blockNum) actual, err := factory.GetGame(context.Background(), uint64(idx), blockHash)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, expected, actual) require.Equal(t, expected, actual)
} }
} }
func expectGetGame(stubRpc *batchingTest.AbiBasedRpc, idx int, blockNum uint64, game types.GameMetadata) { func expectGetGame(stubRpc *batchingTest.AbiBasedRpc, idx int, blockHash common.Hash, game types.GameMetadata) {
stubRpc.SetResponse( stubRpc.SetResponse(
factoryAddr, factoryAddr,
methodGameAtIndex, methodGameAtIndex,
batching.BlockByNumber(blockNum), batching.BlockByHash(blockHash),
[]interface{}{big.NewInt(int64(idx))}, []interface{}{big.NewInt(int64(idx))},
[]interface{}{ []interface{}{
game.GameType, game.GameType,
......
...@@ -2,21 +2,17 @@ package loader ...@@ -2,21 +2,17 @@ package loader
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-challenger/game/types"
) "github.com/ethereum/go-ethereum/common"
var (
ErrMissingBlockNumber = errors.New("game loader missing block number")
) )
// MinimalDisputeGameFactoryCaller is a minimal interface around [bindings.DisputeGameFactoryCaller]. // MinimalDisputeGameFactoryCaller is a minimal interface around [bindings.DisputeGameFactoryCaller].
// This needs to be updated if the [bindings.DisputeGameFactoryCaller] interface changes. // This needs to be updated if the [bindings.DisputeGameFactoryCaller] interface changes.
type MinimalDisputeGameFactoryCaller interface { type MinimalDisputeGameFactoryCaller interface {
GetGameCount(ctx context.Context, blockNum uint64) (uint64, error) GetGameCount(ctx context.Context, blockHash common.Hash) (uint64, error)
GetGame(ctx context.Context, idx uint64, blockNum uint64) (types.GameMetadata, error) GetGame(ctx context.Context, idx uint64, blockHash common.Hash) (types.GameMetadata, error)
} }
type GameLoader struct { type GameLoader struct {
...@@ -31,15 +27,15 @@ func NewGameLoader(caller MinimalDisputeGameFactoryCaller) *GameLoader { ...@@ -31,15 +27,15 @@ func NewGameLoader(caller MinimalDisputeGameFactoryCaller) *GameLoader {
} }
// FetchAllGamesAtBlock fetches all dispute games from the factory at a given block number. // FetchAllGamesAtBlock fetches all dispute games from the factory at a given block number.
func (l *GameLoader) FetchAllGamesAtBlock(ctx context.Context, earliestTimestamp uint64, blockNumber uint64) ([]types.GameMetadata, error) { func (l *GameLoader) FetchAllGamesAtBlock(ctx context.Context, earliestTimestamp uint64, blockHash common.Hash) ([]types.GameMetadata, error) {
gameCount, err := l.caller.GetGameCount(ctx, blockNumber) gameCount, err := l.caller.GetGameCount(ctx, blockHash)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to fetch game count: %w", err) return nil, fmt.Errorf("failed to fetch game count: %w", err)
} }
games := make([]types.GameMetadata, 0, gameCount) games := make([]types.GameMetadata, 0, gameCount)
for i := gameCount; i > 0; i-- { for i := gameCount; i > 0; i-- {
game, err := l.caller.GetGame(ctx, i-1, blockNumber) game, err := l.caller.GetGame(ctx, i-1, blockHash)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to fetch game at index %d: %w", i-1, err) return nil, fmt.Errorf("failed to fetch game at index %d: %w", i-1, err)
} }
......
...@@ -24,39 +24,39 @@ func TestGameLoader_FetchAllGames(t *testing.T) { ...@@ -24,39 +24,39 @@ func TestGameLoader_FetchAllGames(t *testing.T) {
name string name string
caller *mockMinimalDisputeGameFactoryCaller caller *mockMinimalDisputeGameFactoryCaller
earliest uint64 earliest uint64
blockNumber uint64 blockHash common.Hash
expectedErr error expectedErr error
expectedLen int expectedLen int
}{ }{
{ {
name: "success", name: "success",
caller: newMockMinimalDisputeGameFactoryCaller(10, false, false), caller: newMockMinimalDisputeGameFactoryCaller(10, false, false),
blockNumber: 1, blockHash: common.Hash{0x01},
expectedLen: 10, expectedLen: 10,
}, },
{ {
name: "expired game ignored", name: "expired game ignored",
caller: newMockMinimalDisputeGameFactoryCaller(10, false, false), caller: newMockMinimalDisputeGameFactoryCaller(10, false, false),
earliest: 500, earliest: 500,
blockNumber: 1, blockHash: common.Hash{0x01},
expectedLen: 5, expectedLen: 5,
}, },
{ {
name: "game count error", name: "game count error",
caller: newMockMinimalDisputeGameFactoryCaller(10, true, false), caller: newMockMinimalDisputeGameFactoryCaller(10, true, false),
blockNumber: 1, blockHash: common.Hash{0x01},
expectedErr: gameCountErr, expectedErr: gameCountErr,
}, },
{ {
name: "game index error", name: "game index error",
caller: newMockMinimalDisputeGameFactoryCaller(10, false, true), caller: newMockMinimalDisputeGameFactoryCaller(10, false, true),
blockNumber: 1, blockHash: common.Hash{0x01},
expectedErr: gameIndexErr, expectedErr: gameIndexErr,
}, },
{ {
name: "no games", name: "no games",
caller: newMockMinimalDisputeGameFactoryCaller(0, false, false), caller: newMockMinimalDisputeGameFactoryCaller(0, false, false),
blockNumber: 1, blockHash: common.Hash{0x01},
}, },
} }
...@@ -67,7 +67,7 @@ func TestGameLoader_FetchAllGames(t *testing.T) { ...@@ -67,7 +67,7 @@ func TestGameLoader_FetchAllGames(t *testing.T) {
t.Parallel() t.Parallel()
loader := NewGameLoader(test.caller) loader := NewGameLoader(test.caller)
games, err := loader.FetchAllGamesAtBlock(context.Background(), test.earliest, test.blockNumber) games, err := loader.FetchAllGamesAtBlock(context.Background(), test.earliest, test.blockHash)
require.ErrorIs(t, err, test.expectedErr) require.ErrorIs(t, err, test.expectedErr)
require.Len(t, games, test.expectedLen) require.Len(t, games, test.expectedLen)
expectedGames := test.caller.games expectedGames := test.caller.games
...@@ -138,7 +138,7 @@ func newMockMinimalDisputeGameFactoryCaller(count uint64, gameCountErr bool, ind ...@@ -138,7 +138,7 @@ func newMockMinimalDisputeGameFactoryCaller(count uint64, gameCountErr bool, ind
} }
} }
func (m *mockMinimalDisputeGameFactoryCaller) GetGameCount(_ context.Context, blockNum uint64) (uint64, error) { func (m *mockMinimalDisputeGameFactoryCaller) GetGameCount(_ context.Context, _ common.Hash) (uint64, error) {
if m.gameCountErr { if m.gameCountErr {
return 0, gameCountErr return 0, gameCountErr
} }
...@@ -146,7 +146,7 @@ func (m *mockMinimalDisputeGameFactoryCaller) GetGameCount(_ context.Context, bl ...@@ -146,7 +146,7 @@ func (m *mockMinimalDisputeGameFactoryCaller) GetGameCount(_ context.Context, bl
return m.gameCount, nil return m.gameCount, nil
} }
func (m *mockMinimalDisputeGameFactoryCaller) GetGame(_ context.Context, index uint64, blockNum uint64) (types.GameMetadata, error) { func (m *mockMinimalDisputeGameFactoryCaller) GetGame(_ context.Context, index uint64, _ common.Hash) (types.GameMetadata, error) {
if m.indexErrors[index] { if m.indexErrors[index] {
return struct { return struct {
GameType uint8 GameType uint8
......
...@@ -23,7 +23,7 @@ type blockNumberFetcher func(ctx context.Context) (uint64, error) ...@@ -23,7 +23,7 @@ type blockNumberFetcher func(ctx context.Context) (uint64, error)
// gameSource loads information about the games available to play // gameSource loads information about the games available to play
type gameSource interface { type gameSource interface {
FetchAllGamesAtBlock(ctx context.Context, earliest uint64, blockNumber uint64) ([]types.GameMetadata, error) FetchAllGamesAtBlock(ctx context.Context, earliest uint64, blockHash common.Hash) ([]types.GameMetadata, error)
} }
type gameScheduler interface { type gameScheduler interface {
...@@ -101,8 +101,8 @@ func (m *gameMonitor) minGameTimestamp() uint64 { ...@@ -101,8 +101,8 @@ func (m *gameMonitor) minGameTimestamp() uint64 {
return 0 return 0
} }
func (m *gameMonitor) progressGames(ctx context.Context, blockNum uint64) error { func (m *gameMonitor) progressGames(ctx context.Context, blockHash common.Hash) error {
games, err := m.source.FetchAllGamesAtBlock(ctx, m.minGameTimestamp(), blockNum) games, err := m.source.FetchAllGamesAtBlock(ctx, m.minGameTimestamp(), blockHash)
if err != nil { if err != nil {
return fmt.Errorf("failed to load games: %w", err) return fmt.Errorf("failed to load games: %w", err)
} }
...@@ -123,7 +123,7 @@ func (m *gameMonitor) progressGames(ctx context.Context, blockNum uint64) error ...@@ -123,7 +123,7 @@ func (m *gameMonitor) progressGames(ctx context.Context, blockNum uint64) error
} }
func (m *gameMonitor) onNewL1Head(ctx context.Context, sig eth.L1BlockRef) { func (m *gameMonitor) onNewL1Head(ctx context.Context, sig eth.L1BlockRef) {
if err := m.progressGames(ctx, sig.Number); err != nil { if err := m.progressGames(ctx, sig.Hash); err != nil {
m.logger.Error("Failed to progress games", "err", err) m.logger.Error("Failed to progress games", "err", err)
} }
} }
......
...@@ -145,7 +145,7 @@ func TestMonitorCreateAndProgressGameAgents(t *testing.T) { ...@@ -145,7 +145,7 @@ func TestMonitorCreateAndProgressGameAgents(t *testing.T) {
addr2 := common.Address{0xbb} addr2 := common.Address{0xbb}
source.games = []types.GameMetadata{newFDG(addr1, 9999), newFDG(addr2, 9999)} source.games = []types.GameMetadata{newFDG(addr1, 9999), newFDG(addr2, 9999)}
require.NoError(t, monitor.progressGames(context.Background(), uint64(1))) require.NoError(t, monitor.progressGames(context.Background(), common.Hash{0x01}))
require.Len(t, sched.scheduled, 1) require.Len(t, sched.scheduled, 1)
require.Equal(t, []common.Address{addr1, addr2}, sched.scheduled[0]) require.Equal(t, []common.Address{addr1, addr2}, sched.scheduled[0])
...@@ -157,7 +157,7 @@ func TestMonitorOnlyScheduleSpecifiedGame(t *testing.T) { ...@@ -157,7 +157,7 @@ func TestMonitorOnlyScheduleSpecifiedGame(t *testing.T) {
monitor, source, sched, _ := setupMonitorTest(t, []common.Address{addr2}) monitor, source, sched, _ := setupMonitorTest(t, []common.Address{addr2})
source.games = []types.GameMetadata{newFDG(addr1, 9999), newFDG(addr2, 9999)} source.games = []types.GameMetadata{newFDG(addr1, 9999), newFDG(addr2, 9999)}
require.NoError(t, monitor.progressGames(context.Background(), uint64(1))) require.NoError(t, monitor.progressGames(context.Background(), common.Hash{0x01}))
require.Len(t, sched.scheduled, 1) require.Len(t, sched.scheduled, 1)
require.Equal(t, []common.Address{addr2}, sched.scheduled[0]) require.Equal(t, []common.Address{addr2}, sched.scheduled[0])
...@@ -232,7 +232,7 @@ type stubGameSource struct { ...@@ -232,7 +232,7 @@ type stubGameSource struct {
func (s *stubGameSource) FetchAllGamesAtBlock( func (s *stubGameSource) FetchAllGamesAtBlock(
ctx context.Context, ctx context.Context,
earliest uint64, earliest uint64,
blockNumber uint64, blockHash common.Hash,
) ([]types.GameMetadata, error) { ) ([]types.GameMetadata, error) {
return s.games, nil return s.games, nil
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment