Commit c5919e51 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-dispute-mon: Add option to ignore games (#10334)

When a game is ignored because of this option it logs a warning and reports a count of ignored games via a new metric.
parent 5a829ce7
......@@ -37,6 +37,7 @@ type Config struct {
RollupRpc string // The rollup node RPC URL.
MonitorInterval time.Duration // Frequency to check for new games to monitor.
GameWindow time.Duration // Maximum window to look for games to monitor.
IgnoredGames []common.Address // Games to exclude from monitoring
MetricsConfig opmetrics.CLIConfig
PprofConfig oppprof.CLIConfig
......
......@@ -57,6 +57,11 @@ var (
EnvVars: prefixEnvVars("GAME_WINDOW"),
Value: config.DefaultGameWindow,
}
IgnoredGamesFlag = &cli.StringSliceFlag{
Name: "ignored-games",
Usage: "List of game addresses to exclude from monitoring.",
EnvVars: prefixEnvVars("IGNORED_GAMES"),
}
)
// requiredFlags are checked by [CheckRequired]
......@@ -71,6 +76,7 @@ var optionalFlags = []cli.Flag{
HonestActorsFlag,
MonitorIntervalFlag,
GameWindowFlag,
IgnoredGamesFlag,
}
func init() {
......@@ -114,6 +120,17 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) {
}
}
var ignoredGames []common.Address
if ctx.IsSet(IgnoredGamesFlag.Name) {
for _, addrStr := range ctx.StringSlice(IgnoredGamesFlag.Name) {
game, err := opservice.ParseAddress(addrStr)
if err != nil {
return nil, fmt.Errorf("invalid ignored game address: %w", err)
}
ignoredGames = append(ignoredGames, game)
}
}
metricsConfig := opmetrics.ReadCLIConfig(ctx)
pprofConfig := oppprof.ReadCLIConfig(ctx)
......@@ -125,6 +142,7 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) {
RollupRpc: ctx.String(RollupRpcFlag.Name),
MonitorInterval: ctx.Duration(MonitorIntervalFlag.Name),
GameWindow: ctx.Duration(GameWindowFlag.Name),
IgnoredGames: ignoredGames,
MetricsConfig: metricsConfig,
PprofConfig: pprofConfig,
......
......@@ -85,6 +85,8 @@ type Metricer interface {
RecordGameAgreement(status GameAgreementStatus, count int)
RecordIgnoredGames(count int)
RecordBondCollateral(addr common.Address, required *big.Int, available *big.Int)
caching.Metrics
......@@ -120,6 +122,7 @@ type Metrics struct {
claimResolutionDelayMax prometheus.Gauge
gamesAgreement prometheus.GaugeVec
ignoredGames prometheus.Gauge
requiredCollateral prometheus.GaugeVec
availableCollateral prometheus.GaugeVec
......@@ -215,6 +218,11 @@ func NewMetrics() *Metrics {
"result_correctness",
"root_agreement",
}),
ignoredGames: factory.NewGauge(prometheus.GaugeOpts{
Namespace: Namespace,
Name: "ignored_games",
Help: "Number of games present in the game window but ignored via config",
}),
requiredCollateral: *factory.NewGaugeVec(prometheus.GaugeOpts{
Namespace: Namespace,
Name: "bond_collateral_required",
......@@ -350,6 +358,10 @@ func (m *Metrics) RecordGameAgreement(status GameAgreementStatus, count int) {
m.gamesAgreement.WithLabelValues(labelValuesFor(status)...).Set(float64(count))
}
func (m *Metrics) RecordIgnoredGames(count int) {
m.ignoredGames.Set(float64(count))
}
func (m *Metrics) RecordBondCollateral(addr common.Address, required *big.Int, available *big.Int) {
balance := "sufficient"
if required.Cmp(available) > 0 {
......@@ -400,6 +412,7 @@ func labelValuesFor(status GameAgreementStatus) []string {
return asStrings("agree_challenger_wins", !inProgress, !correct, agree)
case DisagreeChallengerWins:
return asStrings("disagree_challenger_wins", !inProgress, correct, !agree)
default:
panic(fmt.Errorf("unknown game agreement status: %v", status))
}
......
......@@ -35,4 +35,6 @@ func (*NoopMetricsImpl) RecordOutputFetchTime(_ float64) {}
func (*NoopMetricsImpl) RecordGameAgreement(_ GameAgreementStatus, _ int) {}
func (*NoopMetricsImpl) RecordIgnoredGames(_ int) {}
func (i *NoopMetricsImpl) RecordBondCollateral(_ common.Address, _ *big.Int, _ *big.Int) {}
......@@ -26,28 +26,41 @@ type Extractor struct {
createContract CreateGameCaller
fetchGames FactoryGameFetcher
enrichers []Enricher
ignoredGames map[common.Address]bool
}
func NewExtractor(logger log.Logger, creator CreateGameCaller, fetchGames FactoryGameFetcher, enrichers ...Enricher) *Extractor {
func NewExtractor(logger log.Logger, creator CreateGameCaller, fetchGames FactoryGameFetcher, ignoredGames []common.Address, enrichers ...Enricher) *Extractor {
ignored := make(map[common.Address]bool)
for _, game := range ignoredGames {
ignored[game] = true
}
return &Extractor{
logger: logger,
createContract: creator,
fetchGames: fetchGames,
enrichers: enrichers,
ignoredGames: ignored,
}
}
func (e *Extractor) Extract(ctx context.Context, blockHash common.Hash, minTimestamp uint64) ([]*monTypes.EnrichedGameData, error) {
func (e *Extractor) Extract(ctx context.Context, blockHash common.Hash, minTimestamp uint64) ([]*monTypes.EnrichedGameData, int, error) {
games, err := e.fetchGames(ctx, blockHash, minTimestamp)
if err != nil {
return nil, fmt.Errorf("failed to load games: %w", err)
return nil, 0, fmt.Errorf("failed to load games: %w", err)
}
return e.enrichGames(ctx, blockHash, games), nil
enriched, ignored := e.enrichGames(ctx, blockHash, games)
return enriched, ignored, nil
}
func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, games []gameTypes.GameMetadata) []*monTypes.EnrichedGameData {
func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, games []gameTypes.GameMetadata) ([]*monTypes.EnrichedGameData, int) {
var enrichedGames []*monTypes.EnrichedGameData
ignored := 0
for _, game := range games {
if e.ignoredGames[game.Proxy] {
ignored++
e.logger.Warn("Ignoring game", "game", game.Proxy)
continue
}
caller, err := e.createContract(ctx, game)
if err != nil {
e.logger.Error("Failed to create game caller", "err", err)
......@@ -82,7 +95,7 @@ func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, game
}
enrichedGames = append(enrichedGames, enrichedGame)
}
return enrichedGames
return enrichedGames, ignored
}
func (e *Extractor) applyEnrichers(ctx context.Context, blockHash common.Hash, caller GameCaller, game *monTypes.EnrichedGameData) error {
......
......@@ -18,13 +18,16 @@ import (
"github.com/ethereum/go-ethereum/log"
)
var mockRootClaim = common.HexToHash("0x1234")
var (
mockRootClaim = common.HexToHash("0x1234")
ignoredGames = []common.Address{common.HexToAddress("0xdeadbeef")}
)
func TestExtractor_Extract(t *testing.T) {
t.Run("FetchGamesError", func(t *testing.T) {
extractor, _, games, _ := setupExtractorTest(t)
games.err = errors.New("boom")
_, err := extractor.Extract(context.Background(), common.Hash{}, 0)
_, _, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.ErrorIs(t, err, games.err)
require.Equal(t, 1, games.calls)
})
......@@ -33,8 +36,9 @@ func TestExtractor_Extract(t *testing.T) {
extractor, creator, games, logs := setupExtractorTest(t)
games.games = []gameTypes.GameMetadata{{}}
creator.err = errors.New("boom")
enriched, err := extractor.Extract(context.Background(), common.Hash{}, 0)
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err)
require.Zero(t, ignored)
require.Len(t, enriched, 0)
require.Equal(t, 1, games.calls)
require.Equal(t, 1, creator.calls)
......@@ -47,8 +51,9 @@ func TestExtractor_Extract(t *testing.T) {
extractor, creator, games, logs := setupExtractorTest(t)
games.games = []gameTypes.GameMetadata{{}}
creator.caller.metadataErr = errors.New("boom")
enriched, err := extractor.Extract(context.Background(), common.Hash{}, 0)
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err)
require.Zero(t, ignored)
require.Len(t, enriched, 0)
require.Equal(t, 1, games.calls)
require.Equal(t, 1, creator.calls)
......@@ -61,8 +66,9 @@ func TestExtractor_Extract(t *testing.T) {
extractor, creator, games, logs := setupExtractorTest(t)
games.games = []gameTypes.GameMetadata{{}}
creator.caller.claimsErr = errors.New("boom")
enriched, err := extractor.Extract(context.Background(), common.Hash{}, 0)
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err)
require.Zero(t, ignored)
require.Len(t, enriched, 0)
require.Equal(t, 1, games.calls)
require.Equal(t, 1, creator.calls)
......@@ -74,8 +80,9 @@ func TestExtractor_Extract(t *testing.T) {
t.Run("Success", func(t *testing.T) {
extractor, creator, games, _ := setupExtractorTest(t)
games.games = []gameTypes.GameMetadata{{}}
enriched, err := extractor.Extract(context.Background(), common.Hash{}, 0)
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err)
require.Zero(t, ignored)
require.Len(t, enriched, 1)
require.Equal(t, 1, games.calls)
require.Equal(t, 1, creator.calls)
......@@ -87,8 +94,9 @@ func TestExtractor_Extract(t *testing.T) {
enricher := &mockEnricher{err: errors.New("whoops")}
extractor, _, games, logs := setupExtractorTest(t, enricher)
games.games = []gameTypes.GameMetadata{{}}
enriched, err := extractor.Extract(context.Background(), common.Hash{}, 0)
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err)
require.Zero(t, ignored)
l := logs.FindLogs(testlog.NewMessageFilter("Failed to enrich game"))
require.Len(t, l, 1, "Should have logged error")
require.Len(t, enriched, 0, "Should not return games that failed to enrich")
......@@ -98,8 +106,9 @@ func TestExtractor_Extract(t *testing.T) {
enricher := &mockEnricher{}
extractor, _, games, _ := setupExtractorTest(t, enricher)
games.games = []gameTypes.GameMetadata{{}}
enriched, err := extractor.Extract(context.Background(), common.Hash{}, 0)
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err)
require.Zero(t, ignored)
require.Len(t, enriched, 1)
require.Equal(t, 1, enricher.calls)
})
......@@ -109,12 +118,31 @@ func TestExtractor_Extract(t *testing.T) {
enricher2 := &mockEnricher{}
extractor, _, games, _ := setupExtractorTest(t, enricher1, enricher2)
games.games = []gameTypes.GameMetadata{{}, {}}
enriched, err := extractor.Extract(context.Background(), common.Hash{}, 0)
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err)
require.Zero(t, ignored)
require.Len(t, enriched, 2)
require.Equal(t, 2, enricher1.calls)
require.Equal(t, 2, enricher2.calls)
})
t.Run("IgnoreGames", func(t *testing.T) {
enricher1 := &mockEnricher{}
extractor, _, games, logs := setupExtractorTest(t, enricher1)
// Two games, one of which is ignored
games.games = []gameTypes.GameMetadata{{Proxy: ignoredGames[0]}, {Proxy: common.Address{0xaa}}}
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err)
// Should ignore one and enrich the other
require.Equal(t, 1, ignored)
require.Len(t, enriched, 1)
require.Equal(t, 1, enricher1.calls)
require.Equal(t, enriched[0].Proxy, common.Address{0xaa})
require.NotNil(t, logs.FindLog(
testlog.NewLevelFilter(log.LevelWarn),
testlog.NewMessageFilter("Ignoring game"),
testlog.NewAttributesFilter("game", ignoredGames[0].Hex())))
})
}
func verifyLogs(t *testing.T, logs *testlog.CapturingHandler, createErr int, metadataErr int, claimsErr int, durationErr int) {
......@@ -142,6 +170,7 @@ func setupExtractorTest(t *testing.T, enrichers ...Enricher) (*Extractor, *mockG
logger,
creator.CreateGameCaller,
games.FetchGames,
ignoredGames,
enrichers...,
)
return extractor, creator, games, capturedLogs
......
......@@ -26,6 +26,7 @@ type OutputValidator interface {
type ForecastMetrics interface {
RecordClaimResolutionDelayMax(delay float64)
RecordGameAgreement(status metrics.GameAgreementStatus, count int)
RecordIgnoredGames(count int)
}
type forecast struct {
......@@ -42,17 +43,17 @@ func newForecast(logger log.Logger, metrics ForecastMetrics, validator OutputVal
}
}
func (f *forecast) Forecast(ctx context.Context, games []*monTypes.EnrichedGameData) {
func (f *forecast) Forecast(ctx context.Context, games []*monTypes.EnrichedGameData, ignoredCount int) {
batch := monTypes.ForecastBatch{}
for _, game := range games {
if err := f.forecastGame(ctx, game, &batch); err != nil {
f.logger.Error("Failed to forecast game", "err", err)
}
}
f.recordBatch(batch)
f.recordBatch(batch, ignoredCount)
}
func (f *forecast) recordBatch(batch monTypes.ForecastBatch) {
func (f *forecast) recordBatch(batch monTypes.ForecastBatch, ignoredCount int) {
f.metrics.RecordGameAgreement(metrics.AgreeDefenderWins, batch.AgreeDefenderWins)
f.metrics.RecordGameAgreement(metrics.DisagreeDefenderWins, batch.DisagreeDefenderWins)
f.metrics.RecordGameAgreement(metrics.AgreeChallengerWins, batch.AgreeChallengerWins)
......@@ -62,6 +63,8 @@ func (f *forecast) recordBatch(batch monTypes.ForecastBatch) {
f.metrics.RecordGameAgreement(metrics.DisagreeChallengerAhead, batch.DisagreeChallengerAhead)
f.metrics.RecordGameAgreement(metrics.AgreeDefenderAhead, batch.AgreeDefenderAhead)
f.metrics.RecordGameAgreement(metrics.DisagreeDefenderAhead, batch.DisagreeDefenderAhead)
f.metrics.RecordIgnoredGames(ignoredCount)
}
func (f *forecast) forecastGame(ctx context.Context, game *monTypes.EnrichedGameData, metrics *monTypes.ForecastBatch) error {
......
......@@ -30,7 +30,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("NoGames", func(t *testing.T) {
forecast, _, rollup, logs := setupForecastTest(t)
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{})
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{}, 0)
require.Equal(t, 0, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
......@@ -40,7 +40,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("RollupFetchFails", func(t *testing.T) {
forecast, _, rollup, logs := setupForecastTest(t)
rollup.err = errors.New("boom")
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{}})
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{}}, 0)
require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
......@@ -54,7 +54,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("ChallengerWonGame_Agree", func(t *testing.T) {
forecast, m, _, logs := setupForecastTest(t)
expectedGame := monTypes.EnrichedGameData{Status: types.GameStatusChallengerWon, RootClaim: mockRootClaim}
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame})
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame}, 0)
l := logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(lostGameLog))
require.NotNil(t, l)
require.Equal(t, expectedGame.Proxy, l.AttrValue("game"))
......@@ -69,7 +69,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("ChallengerWonGame_Disagree", func(t *testing.T) {
forecast, m, _, logs := setupForecastTest(t)
expectedGame := monTypes.EnrichedGameData{Status: types.GameStatusChallengerWon, RootClaim: common.Hash{0xbb}}
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame})
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame}, 0)
l := logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(lostGameLog))
require.Nil(t, l)
......@@ -81,7 +81,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("DefenderWonGame_Agree", func(t *testing.T) {
forecast, m, _, logs := setupForecastTest(t)
expectedGame := monTypes.EnrichedGameData{Status: types.GameStatusDefenderWon, RootClaim: mockRootClaim}
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame})
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame}, 0)
l := logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(lostGameLog))
require.Nil(t, l)
......@@ -93,7 +93,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("DefenderWonGame_Disagree", func(t *testing.T) {
forecast, m, _, logs := setupForecastTest(t)
expectedGame := monTypes.EnrichedGameData{Status: types.GameStatusDefenderWon, RootClaim: common.Hash{0xbb}}
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame})
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame}, 0)
l := logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(lostGameLog))
require.NotNil(t, l)
require.Equal(t, expectedGame.Proxy, l.AttrValue("game"))
......@@ -107,14 +107,14 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("SingleGame", func(t *testing.T) {
forecast, _, rollup, logs := setupForecastTest(t)
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{}})
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{}}, 0)
require.Equal(t, 1, rollup.calls)
require.Nil(t, logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(failedForecastLog)))
})
t.Run("MultipleGames", func(t *testing.T) {
forecast, _, rollup, logs := setupForecastTest(t)
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{}, {}, {}})
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{}, {}, {}}, 0)
require.Equal(t, 3, rollup.calls)
require.Nil(t, logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(failedForecastLog)))
})
......@@ -130,7 +130,7 @@ func TestForecast_Forecast_EndLogs(t *testing.T) {
RootClaim: mockRootClaim,
Claims: createDeepClaimList()[:1],
}}
forecast.Forecast(context.Background(), games)
forecast.Forecast(context.Background(), games, 0)
require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
......@@ -151,7 +151,7 @@ func TestForecast_Forecast_EndLogs(t *testing.T) {
RootClaim: mockRootClaim,
Claims: createDeepClaimList()[:2],
}}
forecast.Forecast(context.Background(), games)
forecast.Forecast(context.Background(), games, 0)
require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
......@@ -170,7 +170,7 @@ func TestForecast_Forecast_EndLogs(t *testing.T) {
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{
Status: types.GameStatusInProgress,
Claims: createDeepClaimList()[:2],
}})
}}, 0)
require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
......@@ -189,7 +189,7 @@ func TestForecast_Forecast_EndLogs(t *testing.T) {
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{
Status: types.GameStatusInProgress,
Claims: createDeepClaimList()[:1],
}})
}}, 0)
require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
......@@ -205,7 +205,7 @@ func TestForecast_Forecast_EndLogs(t *testing.T) {
}
func TestForecast_Forecast_MultipleGames(t *testing.T) {
forecast, _, rollup, logs := setupForecastTest(t)
forecast, m, rollup, logs := setupForecastTest(t)
gameStatus := []types.GameStatus{
types.GameStatusChallengerWon,
types.GameStatusInProgress,
......@@ -247,20 +247,27 @@ func TestForecast_Forecast_MultipleGames(t *testing.T) {
RootClaim: rootClaims[i],
}
}
forecast.Forecast(context.Background(), games)
forecast.Forecast(context.Background(), games, 3)
require.Equal(t, len(games), rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
require.Nil(t, logs.FindLog(levelFilter, messageFilter))
require.Nil(t, logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(failedForecastLog)))
expectedMetrics := zeroGameAgreement()
expectedMetrics[metrics.AgreeChallengerAhead] = 1
expectedMetrics[metrics.DisagreeChallengerAhead] = 1
expectedMetrics[metrics.AgreeDefenderAhead] = 1
expectedMetrics[metrics.DisagreeDefenderAhead] = 1
expectedMetrics[metrics.DisagreeDefenderWins] = 2
expectedMetrics[metrics.DisagreeChallengerWins] = 3
require.Equal(t, expectedMetrics, m.gameAgreement)
require.Equal(t, 3, m.ignoredGames)
}
func setupForecastTest(t *testing.T) (*forecast, *mockForecastMetrics, *stubOutputValidator, *testlog.CapturingHandler) {
logger, capturedLogs := testlog.CaptureLogger(t, log.LvlDebug)
validator := &stubOutputValidator{}
metrics := &mockForecastMetrics{
m := &mockForecastMetrics{
gameAgreement: zeroGameAgreement(),
}
return newForecast(logger, metrics, validator), metrics, validator, capturedLogs
return newForecast(logger, m, validator), m, validator, capturedLogs
}
func zeroGameAgreement() map[metrics.GameAgreementStatus]int {
......@@ -278,6 +285,7 @@ func zeroGameAgreement() map[metrics.GameAgreementStatus]int {
type mockForecastMetrics struct {
gameAgreement map[metrics.GameAgreementStatus]int
ignoredGames int
claimResolutionDelayMax float64
}
......@@ -285,6 +293,10 @@ func (m *mockForecastMetrics) RecordGameAgreement(status metrics.GameAgreementSt
m.gameAgreement[status] = count
}
func (m *mockForecastMetrics) RecordIgnoredGames(count int) {
m.ignoredGames = count
}
func (m *mockForecastMetrics) RecordClaimResolutionDelayMax(delay float64) {
m.claimResolutionDelayMax = delay
}
......
......@@ -13,14 +13,14 @@ import (
"github.com/ethereum/go-ethereum/log"
)
type Forecast func(ctx context.Context, games []*types.EnrichedGameData)
type Forecast func(ctx context.Context, games []*types.EnrichedGameData, ignoredCount int)
type Bonds func(games []*types.EnrichedGameData)
type Resolutions func(games []*types.EnrichedGameData)
type MonitorClaims func(games []*types.EnrichedGameData)
type MonitorWithdrawals func(games []*types.EnrichedGameData)
type BlockHashFetcher func(ctx context.Context, number *big.Int) (common.Hash, error)
type BlockNumberFetcher func(ctx context.Context) (uint64, error)
type Extract func(ctx context.Context, blockHash common.Hash, minTimestamp uint64) ([]*types.EnrichedGameData, error)
type Extract func(ctx context.Context, blockHash common.Hash, minTimestamp uint64) ([]*types.EnrichedGameData, int, error)
type RecordClaimResolutionDelayMax func([]*types.EnrichedGameData)
type gameMonitor struct {
......@@ -91,13 +91,13 @@ func (m *gameMonitor) monitorGames() error {
return fmt.Errorf("failed to fetch block hash: %w", err)
}
minGameTimestamp := clock.MinCheckedTimestamp(m.clock, m.gameWindow)
enrichedGames, err := m.extract(m.ctx, blockHash, minGameTimestamp)
enrichedGames, ignored, err := m.extract(m.ctx, blockHash, minGameTimestamp)
if err != nil {
return fmt.Errorf("failed to load games: %w", err)
}
m.resolutions(enrichedGames)
m.delays(enrichedGames)
m.forecast(m.ctx, enrichedGames)
m.forecast(m.ctx, enrichedGames, ignored)
m.bonds(enrichedGames)
m.claims(enrichedGames)
m.withdrawals(enrichedGames)
......
......@@ -182,7 +182,7 @@ type mockForecast struct {
calls int
}
func (m *mockForecast) Forecast(ctx context.Context, games []*monTypes.EnrichedGameData) {
func (m *mockForecast) Forecast(_ context.Context, _ []*monTypes.EnrichedGameData, _ int) {
m.calls++
}
......@@ -195,23 +195,24 @@ func (m *mockBonds) CheckBonds(_ []*monTypes.EnrichedGameData) {
}
type mockExtractor struct {
fetchErr error
calls int
maxSuccess int
games []*monTypes.EnrichedGameData
fetchErr error
calls int
maxSuccess int
games []*monTypes.EnrichedGameData
ignoredCount int
}
func (m *mockExtractor) Extract(
_ context.Context,
_ common.Hash,
_ uint64,
) ([]*monTypes.EnrichedGameData, error) {
) ([]*monTypes.EnrichedGameData, int, error) {
m.calls++
if m.fetchErr != nil {
return nil, m.fetchErr
return nil, 0, m.fetchErr
}
if m.calls > m.maxSuccess && m.maxSuccess != 0 {
return nil, mockErr
return nil, 0, mockErr
}
return m.games, nil
return m.games, m.ignoredCount, nil
}
......@@ -96,7 +96,7 @@ func (s *Service) initFromConfig(ctx context.Context, cfg *config.Config) error
s.initGameCallerCreator() // Must be called before initForecast
s.initDelayCalculator()
s.initExtractor()
s.initExtractor(cfg)
s.initForecast(cfg)
s.initBonds()
......@@ -133,11 +133,12 @@ func (s *Service) initDelayCalculator() {
s.delays = resolution.NewDelayCalculator(s.metrics, s.cl)
}
func (s *Service) initExtractor() {
func (s *Service) initExtractor(cfg *config.Config) {
s.extractor = extract.NewExtractor(
s.logger,
s.game.CreateContract,
s.factoryContract.GetGamesAtOrAfter,
cfg.IgnoredGames,
extract.NewClaimEnricher(),
extract.NewRecipientEnricher(), // Must be called before WithdrawalsEnricher
extract.NewWithdrawalsEnricher(),
......
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