Commit 84723638 authored by refcell's avatar refcell Committed by GitHub

feat(op-dispute-mon): Contract Creation Failure Metric (#10447)

* feat(op-dispute-mon): contract creation failure metric

* fix(op-dispute-mon): log the game address for easier investigation
parent 700a1dbc
...@@ -107,6 +107,8 @@ type Metricer interface { ...@@ -107,6 +107,8 @@ type Metricer interface {
RecordInfo(version string) RecordInfo(version string)
RecordUp() RecordUp()
RecordFailedGames(count int)
RecordHonestActorClaims(address common.Address, stats *HonestActorData) RecordHonestActorClaims(address common.Address, stats *HonestActorData)
RecordGameResolutionStatus(status ResolutionStatus, count int) RecordGameResolutionStatus(status ResolutionStatus, count int)
...@@ -161,6 +163,7 @@ type Metrics struct { ...@@ -161,6 +163,7 @@ type Metrics struct {
gamesAgreement prometheus.GaugeVec gamesAgreement prometheus.GaugeVec
latestInvalidProposal prometheus.Gauge latestInvalidProposal prometheus.Gauge
ignoredGames prometheus.Gauge ignoredGames prometheus.Gauge
failedGames prometheus.Gauge
requiredCollateral prometheus.GaugeVec requiredCollateral prometheus.GaugeVec
availableCollateral prometheus.GaugeVec availableCollateral prometheus.GaugeVec
...@@ -280,6 +283,11 @@ func NewMetrics() *Metrics { ...@@ -280,6 +283,11 @@ func NewMetrics() *Metrics {
"delayedWETH", "delayedWETH",
"balance", "balance",
}), }),
failedGames: factory.NewGauge(prometheus.GaugeOpts{
Namespace: Namespace,
Name: "failed_games",
Help: "Number of games present in the game window but failed to be monitored",
}),
availableCollateral: *factory.NewGaugeVec(prometheus.GaugeOpts{ availableCollateral: *factory.NewGaugeVec(prometheus.GaugeOpts{
Namespace: Namespace, Namespace: Namespace,
Name: "bond_collateral_available", Name: "bond_collateral_available",
...@@ -425,6 +433,10 @@ func (m *Metrics) RecordIgnoredGames(count int) { ...@@ -425,6 +433,10 @@ func (m *Metrics) RecordIgnoredGames(count int) {
m.ignoredGames.Set(float64(count)) m.ignoredGames.Set(float64(count))
} }
func (m *Metrics) RecordFailedGames(count int) {
m.failedGames.Set(float64(count))
}
func (m *Metrics) RecordBondCollateral(addr common.Address, required *big.Int, available *big.Int) { func (m *Metrics) RecordBondCollateral(addr common.Address, required *big.Int, available *big.Int) {
balance := "sufficient" balance := "sufficient"
if required.Cmp(available) > 0 { if required.Cmp(available) > 0 {
......
...@@ -37,4 +37,6 @@ func (*NoopMetricsImpl) RecordLatestInvalidProposal(_ uint64) {} ...@@ -37,4 +37,6 @@ func (*NoopMetricsImpl) RecordLatestInvalidProposal(_ uint64) {}
func (*NoopMetricsImpl) RecordIgnoredGames(_ int) {} func (*NoopMetricsImpl) RecordIgnoredGames(_ int) {}
func (i *NoopMetricsImpl) RecordBondCollateral(_ common.Address, _ *big.Int, _ *big.Int) {} func (*NoopMetricsImpl) RecordFailedGames(_ int) {}
func (*NoopMetricsImpl) RecordBondCollateral(_ common.Address, _ *big.Int, _ *big.Int) {}
...@@ -51,7 +51,7 @@ func (g *GameCallerCreator) CreateContract(ctx context.Context, game gameTypes.G ...@@ -51,7 +51,7 @@ func (g *GameCallerCreator) CreateContract(ctx context.Context, game gameTypes.G
return fdg, nil return fdg, nil
} }
switch game.GameType { switch game.GameType {
case faultTypes.CannonGameType, faultTypes.AsteriscGameType, faultTypes.AlphabetGameType: case faultTypes.CannonGameType, faultTypes.PermissionedGameType, faultTypes.AsteriscGameType, faultTypes.AlphabetGameType:
fdg, err := contracts.NewFaultDisputeGameContract(ctx, g.m, game.Proxy, g.caller) fdg, err := contracts.NewFaultDisputeGameContract(ctx, g.m, game.Proxy, g.caller)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create fault dispute game contract: %w", err) return nil, fmt.Errorf("failed to create fault dispute game contract: %w", err)
......
...@@ -43,18 +43,19 @@ func NewExtractor(logger log.Logger, creator CreateGameCaller, fetchGames Factor ...@@ -43,18 +43,19 @@ func NewExtractor(logger log.Logger, creator CreateGameCaller, fetchGames Factor
} }
} }
func (e *Extractor) Extract(ctx context.Context, blockHash common.Hash, minTimestamp uint64) ([]*monTypes.EnrichedGameData, int, error) { func (e *Extractor) Extract(ctx context.Context, blockHash common.Hash, minTimestamp uint64) ([]*monTypes.EnrichedGameData, int, int, error) {
games, err := e.fetchGames(ctx, blockHash, minTimestamp) games, err := e.fetchGames(ctx, blockHash, minTimestamp)
if err != nil { if err != nil {
return nil, 0, fmt.Errorf("failed to load games: %w", err) return nil, 0, 0, fmt.Errorf("failed to load games: %w", err)
} }
enriched, ignored := e.enrichGames(ctx, blockHash, games) enriched, ignored, failed := e.enrichGames(ctx, blockHash, games)
return enriched, ignored, nil return enriched, ignored, failed, nil
} }
func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, games []gameTypes.GameMetadata) ([]*monTypes.EnrichedGameData, int) { func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, games []gameTypes.GameMetadata) ([]*monTypes.EnrichedGameData, int, int) {
var enrichedGames []*monTypes.EnrichedGameData var enrichedGames []*monTypes.EnrichedGameData
ignored := 0 ignored := 0
failed := 0
for _, game := range games { for _, game := range games {
if e.ignoredGames[game.Proxy] { if e.ignoredGames[game.Proxy] {
ignored++ ignored++
...@@ -63,7 +64,8 @@ func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, game ...@@ -63,7 +64,8 @@ func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, game
} }
caller, err := e.createContract(ctx, game) caller, err := e.createContract(ctx, game)
if err != nil { if err != nil {
e.logger.Error("Failed to create game caller", "err", err) e.logger.Error("Failed to create game caller", "game", game.Proxy, "err", err)
failed++
continue continue
} }
l1Head, l2BlockNum, rootClaim, status, duration, err := caller.GetGameMetadata(ctx, rpcblock.ByHash(blockHash)) l1Head, l2BlockNum, rootClaim, status, duration, err := caller.GetGameMetadata(ctx, rpcblock.ByHash(blockHash))
...@@ -95,7 +97,7 @@ func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, game ...@@ -95,7 +97,7 @@ func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, game
} }
enrichedGames = append(enrichedGames, enrichedGame) enrichedGames = append(enrichedGames, enrichedGame)
} }
return enrichedGames, ignored return enrichedGames, ignored, failed
} }
func (e *Extractor) applyEnrichers(ctx context.Context, blockHash common.Hash, caller GameCaller, game *monTypes.EnrichedGameData) error { func (e *Extractor) applyEnrichers(ctx context.Context, blockHash common.Hash, caller GameCaller, game *monTypes.EnrichedGameData) error {
......
...@@ -27,7 +27,7 @@ func TestExtractor_Extract(t *testing.T) { ...@@ -27,7 +27,7 @@ func TestExtractor_Extract(t *testing.T) {
t.Run("FetchGamesError", func(t *testing.T) { t.Run("FetchGamesError", func(t *testing.T) {
extractor, _, games, _ := setupExtractorTest(t) extractor, _, games, _ := setupExtractorTest(t)
games.err = errors.New("boom") 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.ErrorIs(t, err, games.err)
require.Equal(t, 1, games.calls) require.Equal(t, 1, games.calls)
}) })
...@@ -36,8 +36,9 @@ func TestExtractor_Extract(t *testing.T) { ...@@ -36,8 +36,9 @@ func TestExtractor_Extract(t *testing.T) {
extractor, creator, games, logs := setupExtractorTest(t) extractor, creator, games, logs := setupExtractorTest(t)
games.games = []gameTypes.GameMetadata{{}} games.games = []gameTypes.GameMetadata{{}}
creator.err = errors.New("boom") creator.err = errors.New("boom")
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0) enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, failed)
require.Zero(t, ignored) require.Zero(t, ignored)
require.Len(t, enriched, 0) require.Len(t, enriched, 0)
require.Equal(t, 1, games.calls) require.Equal(t, 1, games.calls)
...@@ -51,9 +52,10 @@ func TestExtractor_Extract(t *testing.T) { ...@@ -51,9 +52,10 @@ func TestExtractor_Extract(t *testing.T) {
extractor, creator, games, logs := setupExtractorTest(t) extractor, creator, games, logs := setupExtractorTest(t)
games.games = []gameTypes.GameMetadata{{}} games.games = []gameTypes.GameMetadata{{}}
creator.caller.metadataErr = errors.New("boom") creator.caller.metadataErr = errors.New("boom")
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0) enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err) require.NoError(t, err)
require.Zero(t, ignored) require.Zero(t, ignored)
require.Zero(t, failed)
require.Len(t, enriched, 0) require.Len(t, enriched, 0)
require.Equal(t, 1, games.calls) require.Equal(t, 1, games.calls)
require.Equal(t, 1, creator.calls) require.Equal(t, 1, creator.calls)
...@@ -66,9 +68,10 @@ func TestExtractor_Extract(t *testing.T) { ...@@ -66,9 +68,10 @@ func TestExtractor_Extract(t *testing.T) {
extractor, creator, games, logs := setupExtractorTest(t) extractor, creator, games, logs := setupExtractorTest(t)
games.games = []gameTypes.GameMetadata{{}} games.games = []gameTypes.GameMetadata{{}}
creator.caller.claimsErr = errors.New("boom") creator.caller.claimsErr = errors.New("boom")
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0) enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err) require.NoError(t, err)
require.Zero(t, ignored) require.Zero(t, ignored)
require.Zero(t, failed)
require.Len(t, enriched, 0) require.Len(t, enriched, 0)
require.Equal(t, 1, games.calls) require.Equal(t, 1, games.calls)
require.Equal(t, 1, creator.calls) require.Equal(t, 1, creator.calls)
...@@ -80,9 +83,10 @@ func TestExtractor_Extract(t *testing.T) { ...@@ -80,9 +83,10 @@ func TestExtractor_Extract(t *testing.T) {
t.Run("Success", func(t *testing.T) { t.Run("Success", func(t *testing.T) {
extractor, creator, games, _ := setupExtractorTest(t) extractor, creator, games, _ := setupExtractorTest(t)
games.games = []gameTypes.GameMetadata{{}} games.games = []gameTypes.GameMetadata{{}}
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0) enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err) require.NoError(t, err)
require.Zero(t, ignored) require.Zero(t, ignored)
require.Zero(t, failed)
require.Len(t, enriched, 1) require.Len(t, enriched, 1)
require.Equal(t, 1, games.calls) require.Equal(t, 1, games.calls)
require.Equal(t, 1, creator.calls) require.Equal(t, 1, creator.calls)
...@@ -94,9 +98,10 @@ func TestExtractor_Extract(t *testing.T) { ...@@ -94,9 +98,10 @@ func TestExtractor_Extract(t *testing.T) {
enricher := &mockEnricher{err: errors.New("whoops")} enricher := &mockEnricher{err: errors.New("whoops")}
extractor, _, games, logs := setupExtractorTest(t, enricher) extractor, _, games, logs := setupExtractorTest(t, enricher)
games.games = []gameTypes.GameMetadata{{}} games.games = []gameTypes.GameMetadata{{}}
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0) enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err) require.NoError(t, err)
require.Zero(t, ignored) require.Zero(t, ignored)
require.Zero(t, failed)
l := logs.FindLogs(testlog.NewMessageFilter("Failed to enrich game")) l := logs.FindLogs(testlog.NewMessageFilter("Failed to enrich game"))
require.Len(t, l, 1, "Should have logged error") require.Len(t, l, 1, "Should have logged error")
require.Len(t, enriched, 0, "Should not return games that failed to enrich") require.Len(t, enriched, 0, "Should not return games that failed to enrich")
...@@ -106,9 +111,10 @@ func TestExtractor_Extract(t *testing.T) { ...@@ -106,9 +111,10 @@ func TestExtractor_Extract(t *testing.T) {
enricher := &mockEnricher{} enricher := &mockEnricher{}
extractor, _, games, _ := setupExtractorTest(t, enricher) extractor, _, games, _ := setupExtractorTest(t, enricher)
games.games = []gameTypes.GameMetadata{{}} games.games = []gameTypes.GameMetadata{{}}
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0) enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err) require.NoError(t, err)
require.Zero(t, ignored) require.Zero(t, ignored)
require.Zero(t, failed)
require.Len(t, enriched, 1) require.Len(t, enriched, 1)
require.Equal(t, 1, enricher.calls) require.Equal(t, 1, enricher.calls)
}) })
...@@ -118,9 +124,10 @@ func TestExtractor_Extract(t *testing.T) { ...@@ -118,9 +124,10 @@ func TestExtractor_Extract(t *testing.T) {
enricher2 := &mockEnricher{} enricher2 := &mockEnricher{}
extractor, _, games, _ := setupExtractorTest(t, enricher1, enricher2) extractor, _, games, _ := setupExtractorTest(t, enricher1, enricher2)
games.games = []gameTypes.GameMetadata{{}, {}} games.games = []gameTypes.GameMetadata{{}, {}}
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0) enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err) require.NoError(t, err)
require.Zero(t, ignored) require.Zero(t, ignored)
require.Zero(t, failed)
require.Len(t, enriched, 2) require.Len(t, enriched, 2)
require.Equal(t, 2, enricher1.calls) require.Equal(t, 2, enricher1.calls)
require.Equal(t, 2, enricher2.calls) require.Equal(t, 2, enricher2.calls)
...@@ -131,10 +138,11 @@ func TestExtractor_Extract(t *testing.T) { ...@@ -131,10 +138,11 @@ func TestExtractor_Extract(t *testing.T) {
extractor, _, games, logs := setupExtractorTest(t, enricher1) extractor, _, games, logs := setupExtractorTest(t, enricher1)
// Two games, one of which is ignored // Two games, one of which is ignored
games.games = []gameTypes.GameMetadata{{Proxy: ignoredGames[0]}, {Proxy: common.Address{0xaa}}} games.games = []gameTypes.GameMetadata{{Proxy: ignoredGames[0]}, {Proxy: common.Address{0xaa}}}
enriched, ignored, err := extractor.Extract(context.Background(), common.Hash{}, 0) enriched, ignored, failed, err := extractor.Extract(context.Background(), common.Hash{}, 0)
require.NoError(t, err) require.NoError(t, err)
// Should ignore one and enrich the other // Should ignore one and enrich the other
require.Equal(t, 1, ignored) require.Equal(t, 1, ignored)
require.Zero(t, failed)
require.Len(t, enriched, 1) require.Len(t, enriched, 1)
require.Equal(t, 1, enricher1.calls) require.Equal(t, 1, enricher1.calls)
require.Equal(t, enriched[0].Proxy, common.Address{0xaa}) require.Equal(t, enriched[0].Proxy, common.Address{0xaa})
......
...@@ -26,6 +26,7 @@ type ForecastMetrics interface { ...@@ -26,6 +26,7 @@ type ForecastMetrics interface {
RecordGameAgreement(status metrics.GameAgreementStatus, count int) RecordGameAgreement(status metrics.GameAgreementStatus, count int)
RecordLatestInvalidProposal(timestamp uint64) RecordLatestInvalidProposal(timestamp uint64)
RecordIgnoredGames(count int) RecordIgnoredGames(count int)
RecordFailedGames(count int)
} }
type forecastBatch struct { type forecastBatch struct {
...@@ -56,17 +57,17 @@ func NewForecast(logger log.Logger, metrics ForecastMetrics, validator OutputVal ...@@ -56,17 +57,17 @@ func NewForecast(logger log.Logger, metrics ForecastMetrics, validator OutputVal
} }
} }
func (f *Forecast) Forecast(ctx context.Context, games []*monTypes.EnrichedGameData, ignoredCount int) { func (f *Forecast) Forecast(ctx context.Context, games []*monTypes.EnrichedGameData, ignoredCount, failedCount int) {
batch := forecastBatch{} batch := forecastBatch{}
for _, game := range games { for _, game := range games {
if err := f.forecastGame(ctx, game, &batch); err != nil { if err := f.forecastGame(ctx, game, &batch); err != nil {
f.logger.Error("Failed to forecast game", "err", err) f.logger.Error("Failed to forecast game", "err", err)
} }
} }
f.recordBatch(batch, ignoredCount) f.recordBatch(batch, ignoredCount, failedCount)
} }
func (f *Forecast) recordBatch(batch forecastBatch, ignoredCount int) { func (f *Forecast) recordBatch(batch forecastBatch, ignoredCount, failedCount int) {
f.metrics.RecordGameAgreement(metrics.AgreeDefenderWins, batch.AgreeDefenderWins) f.metrics.RecordGameAgreement(metrics.AgreeDefenderWins, batch.AgreeDefenderWins)
f.metrics.RecordGameAgreement(metrics.DisagreeDefenderWins, batch.DisagreeDefenderWins) f.metrics.RecordGameAgreement(metrics.DisagreeDefenderWins, batch.DisagreeDefenderWins)
f.metrics.RecordGameAgreement(metrics.AgreeChallengerWins, batch.AgreeChallengerWins) f.metrics.RecordGameAgreement(metrics.AgreeChallengerWins, batch.AgreeChallengerWins)
...@@ -80,6 +81,7 @@ func (f *Forecast) recordBatch(batch forecastBatch, ignoredCount int) { ...@@ -80,6 +81,7 @@ func (f *Forecast) recordBatch(batch forecastBatch, ignoredCount int) {
f.metrics.RecordLatestInvalidProposal(batch.LatestInvalidProposal) f.metrics.RecordLatestInvalidProposal(batch.LatestInvalidProposal)
f.metrics.RecordIgnoredGames(ignoredCount) f.metrics.RecordIgnoredGames(ignoredCount)
f.metrics.RecordFailedGames(failedCount)
} }
func (f *Forecast) forecastGame(ctx context.Context, game *monTypes.EnrichedGameData, metrics *forecastBatch) error { func (f *Forecast) forecastGame(ctx context.Context, game *monTypes.EnrichedGameData, metrics *forecastBatch) error {
......
...@@ -30,7 +30,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) { ...@@ -30,7 +30,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("NoGames", func(t *testing.T) { t.Run("NoGames", func(t *testing.T) {
forecast, _, rollup, logs := setupForecastTest(t) forecast, _, rollup, logs := setupForecastTest(t)
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{}, 0) forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{}, 0, 0)
require.Equal(t, 0, rollup.calls) require.Equal(t, 0, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError) levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog) messageFilter := testlog.NewMessageFilter(failedForecastLog)
...@@ -40,7 +40,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) { ...@@ -40,7 +40,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("RollupFetchFails", func(t *testing.T) { t.Run("RollupFetchFails", func(t *testing.T) {
forecast, _, rollup, logs := setupForecastTest(t) forecast, _, rollup, logs := setupForecastTest(t)
rollup.err = errors.New("boom") rollup.err = errors.New("boom")
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{}}, 0) forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{}}, 0, 0)
require.Equal(t, 1, rollup.calls) require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError) levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog) messageFilter := testlog.NewMessageFilter(failedForecastLog)
...@@ -54,7 +54,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) { ...@@ -54,7 +54,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("ChallengerWonGame_Agree", func(t *testing.T) { t.Run("ChallengerWonGame_Agree", func(t *testing.T) {
forecast, m, _, logs := setupForecastTest(t) forecast, m, _, logs := setupForecastTest(t)
expectedGame := monTypes.EnrichedGameData{Status: types.GameStatusChallengerWon, RootClaim: mockRootClaim} expectedGame := monTypes.EnrichedGameData{Status: types.GameStatusChallengerWon, RootClaim: mockRootClaim}
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame}, 0) forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame}, 0, 0)
l := logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(lostGameLog)) l := logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(lostGameLog))
require.NotNil(t, l) require.NotNil(t, l)
require.Equal(t, expectedGame.Proxy, l.AttrValue("game")) require.Equal(t, expectedGame.Proxy, l.AttrValue("game"))
...@@ -69,7 +69,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) { ...@@ -69,7 +69,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("ChallengerWonGame_Disagree", func(t *testing.T) { t.Run("ChallengerWonGame_Disagree", func(t *testing.T) {
forecast, m, _, logs := setupForecastTest(t) forecast, m, _, logs := setupForecastTest(t)
expectedGame := monTypes.EnrichedGameData{Status: types.GameStatusChallengerWon, RootClaim: common.Hash{0xbb}} expectedGame := monTypes.EnrichedGameData{Status: types.GameStatusChallengerWon, RootClaim: common.Hash{0xbb}}
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame}, 0) forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame}, 0, 0)
l := logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(lostGameLog)) l := logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(lostGameLog))
require.Nil(t, l) require.Nil(t, l)
...@@ -81,7 +81,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) { ...@@ -81,7 +81,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("DefenderWonGame_Agree", func(t *testing.T) { t.Run("DefenderWonGame_Agree", func(t *testing.T) {
forecast, m, _, logs := setupForecastTest(t) forecast, m, _, logs := setupForecastTest(t)
expectedGame := monTypes.EnrichedGameData{Status: types.GameStatusDefenderWon, RootClaim: mockRootClaim} expectedGame := monTypes.EnrichedGameData{Status: types.GameStatusDefenderWon, RootClaim: mockRootClaim}
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame}, 0) forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame}, 0, 0)
l := logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(lostGameLog)) l := logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(lostGameLog))
require.Nil(t, l) require.Nil(t, l)
...@@ -93,7 +93,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) { ...@@ -93,7 +93,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("DefenderWonGame_Disagree", func(t *testing.T) { t.Run("DefenderWonGame_Disagree", func(t *testing.T) {
forecast, m, _, logs := setupForecastTest(t) forecast, m, _, logs := setupForecastTest(t)
expectedGame := monTypes.EnrichedGameData{Status: types.GameStatusDefenderWon, RootClaim: common.Hash{0xbb}} expectedGame := monTypes.EnrichedGameData{Status: types.GameStatusDefenderWon, RootClaim: common.Hash{0xbb}}
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame}, 0) forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{&expectedGame}, 0, 0)
l := logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(lostGameLog)) l := logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(lostGameLog))
require.NotNil(t, l) require.NotNil(t, l)
require.Equal(t, expectedGame.Proxy, l.AttrValue("game")) require.Equal(t, expectedGame.Proxy, l.AttrValue("game"))
...@@ -107,14 +107,14 @@ func TestForecast_Forecast_BasicTests(t *testing.T) { ...@@ -107,14 +107,14 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Run("SingleGame", func(t *testing.T) { t.Run("SingleGame", func(t *testing.T) {
forecast, _, rollup, logs := setupForecastTest(t) forecast, _, rollup, logs := setupForecastTest(t)
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{}}, 0) forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{}}, 0, 0)
require.Equal(t, 1, rollup.calls) require.Equal(t, 1, rollup.calls)
require.Nil(t, logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(failedForecastLog))) require.Nil(t, logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(failedForecastLog)))
}) })
t.Run("MultipleGames", func(t *testing.T) { t.Run("MultipleGames", func(t *testing.T) {
forecast, _, rollup, logs := setupForecastTest(t) forecast, _, rollup, logs := setupForecastTest(t)
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{}, {}, {}}, 0) forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{}, {}, {}}, 0, 0)
require.Equal(t, 3, rollup.calls) require.Equal(t, 3, rollup.calls)
require.Nil(t, logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(failedForecastLog))) require.Nil(t, logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(failedForecastLog)))
}) })
...@@ -130,7 +130,7 @@ func TestForecast_Forecast_EndLogs(t *testing.T) { ...@@ -130,7 +130,7 @@ func TestForecast_Forecast_EndLogs(t *testing.T) {
RootClaim: mockRootClaim, RootClaim: mockRootClaim,
Claims: createDeepClaimList()[:1], Claims: createDeepClaimList()[:1],
}} }}
forecast.Forecast(context.Background(), games, 0) forecast.Forecast(context.Background(), games, 0, 0)
require.Equal(t, 1, rollup.calls) require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError) levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog) messageFilter := testlog.NewMessageFilter(failedForecastLog)
...@@ -151,7 +151,7 @@ func TestForecast_Forecast_EndLogs(t *testing.T) { ...@@ -151,7 +151,7 @@ func TestForecast_Forecast_EndLogs(t *testing.T) {
RootClaim: mockRootClaim, RootClaim: mockRootClaim,
Claims: createDeepClaimList()[:2], Claims: createDeepClaimList()[:2],
}} }}
forecast.Forecast(context.Background(), games, 0) forecast.Forecast(context.Background(), games, 0, 0)
require.Equal(t, 1, rollup.calls) require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError) levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog) messageFilter := testlog.NewMessageFilter(failedForecastLog)
...@@ -170,7 +170,7 @@ func TestForecast_Forecast_EndLogs(t *testing.T) { ...@@ -170,7 +170,7 @@ func TestForecast_Forecast_EndLogs(t *testing.T) {
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{ forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{
Status: types.GameStatusInProgress, Status: types.GameStatusInProgress,
Claims: createDeepClaimList()[:2], Claims: createDeepClaimList()[:2],
}}, 0) }}, 0, 0)
require.Equal(t, 1, rollup.calls) require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError) levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog) messageFilter := testlog.NewMessageFilter(failedForecastLog)
...@@ -189,7 +189,7 @@ func TestForecast_Forecast_EndLogs(t *testing.T) { ...@@ -189,7 +189,7 @@ func TestForecast_Forecast_EndLogs(t *testing.T) {
forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{ forecast.Forecast(context.Background(), []*monTypes.EnrichedGameData{{
Status: types.GameStatusInProgress, Status: types.GameStatusInProgress,
Claims: createDeepClaimList()[:1], Claims: createDeepClaimList()[:1],
}}, 0) }}, 0, 0)
require.Equal(t, 1, rollup.calls) require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError) levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog) messageFilter := testlog.NewMessageFilter(failedForecastLog)
...@@ -250,7 +250,7 @@ func TestForecast_Forecast_MultipleGames(t *testing.T) { ...@@ -250,7 +250,7 @@ func TestForecast_Forecast_MultipleGames(t *testing.T) {
}, },
} }
} }
forecast.Forecast(context.Background(), games, 3) forecast.Forecast(context.Background(), games, 3, 4)
require.Equal(t, len(games), rollup.calls) require.Equal(t, len(games), rollup.calls)
require.Nil(t, logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(failedForecastLog))) require.Nil(t, logs.FindLog(testlog.NewLevelFilter(log.LevelError), testlog.NewMessageFilter(failedForecastLog)))
expectedMetrics := zeroGameAgreement() expectedMetrics := zeroGameAgreement()
...@@ -263,6 +263,7 @@ func TestForecast_Forecast_MultipleGames(t *testing.T) { ...@@ -263,6 +263,7 @@ func TestForecast_Forecast_MultipleGames(t *testing.T) {
expectedMetrics[metrics.DisagreeChallengerWins] = 2 expectedMetrics[metrics.DisagreeChallengerWins] = 2
require.Equal(t, expectedMetrics, m.gameAgreement) require.Equal(t, expectedMetrics, m.gameAgreement)
require.Equal(t, 3, m.ignoredGames) require.Equal(t, 3, m.ignoredGames)
require.Equal(t, 4, m.contractCreationFails)
require.EqualValues(t, 7, m.latestInvalidProposal) require.EqualValues(t, 7, m.latestInvalidProposal)
} }
...@@ -292,6 +293,11 @@ type mockForecastMetrics struct { ...@@ -292,6 +293,11 @@ type mockForecastMetrics struct {
gameAgreement map[metrics.GameAgreementStatus]int gameAgreement map[metrics.GameAgreementStatus]int
ignoredGames int ignoredGames int
latestInvalidProposal uint64 latestInvalidProposal uint64
contractCreationFails int
}
func (m *mockForecastMetrics) RecordFailedGames(count int) {
m.contractCreationFails = count
} }
func (m *mockForecastMetrics) RecordGameAgreement(status metrics.GameAgreementStatus, count int) { func (m *mockForecastMetrics) RecordGameAgreement(status metrics.GameAgreementStatus, count int) {
......
...@@ -13,14 +13,14 @@ import ( ...@@ -13,14 +13,14 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
type ForecastResolution func(ctx context.Context, games []*types.EnrichedGameData, ignoredCount int) type ForecastResolution func(ctx context.Context, games []*types.EnrichedGameData, ignoredCount, failedCount int)
type Bonds func(games []*types.EnrichedGameData) type Bonds func(games []*types.EnrichedGameData)
type Resolutions func(games []*types.EnrichedGameData) type Resolutions func(games []*types.EnrichedGameData)
type MonitorClaims func(games []*types.EnrichedGameData) type MonitorClaims func(games []*types.EnrichedGameData)
type MonitorWithdrawals func(games []*types.EnrichedGameData) type MonitorWithdrawals func(games []*types.EnrichedGameData)
type BlockHashFetcher func(ctx context.Context, number *big.Int) (common.Hash, error) type BlockHashFetcher func(ctx context.Context, number *big.Int) (common.Hash, error)
type BlockNumberFetcher func(ctx context.Context) (uint64, error) type BlockNumberFetcher func(ctx context.Context) (uint64, error)
type Extract func(ctx context.Context, blockHash common.Hash, minTimestamp uint64) ([]*types.EnrichedGameData, int, error) type Extract func(ctx context.Context, blockHash common.Hash, minTimestamp uint64) ([]*types.EnrichedGameData, int, int, error)
type gameMonitor struct { type gameMonitor struct {
logger log.Logger logger log.Logger
...@@ -87,12 +87,12 @@ func (m *gameMonitor) monitorGames() error { ...@@ -87,12 +87,12 @@ func (m *gameMonitor) monitorGames() error {
return fmt.Errorf("failed to fetch block hash: %w", err) return fmt.Errorf("failed to fetch block hash: %w", err)
} }
minGameTimestamp := clock.MinCheckedTimestamp(m.clock, m.gameWindow) minGameTimestamp := clock.MinCheckedTimestamp(m.clock, m.gameWindow)
enrichedGames, ignored, err := m.extract(m.ctx, blockHash, minGameTimestamp) enrichedGames, ignored, failed, err := m.extract(m.ctx, blockHash, minGameTimestamp)
if err != nil { if err != nil {
return fmt.Errorf("failed to load games: %w", err) return fmt.Errorf("failed to load games: %w", err)
} }
m.resolutions(enrichedGames) m.resolutions(enrichedGames)
m.forecast(m.ctx, enrichedGames, ignored) m.forecast(m.ctx, enrichedGames, ignored, failed)
m.bonds(enrichedGames) m.bonds(enrichedGames)
m.claims(enrichedGames) m.claims(enrichedGames)
m.withdrawals(enrichedGames) m.withdrawals(enrichedGames)
......
...@@ -170,7 +170,7 @@ type mockForecast struct { ...@@ -170,7 +170,7 @@ type mockForecast struct {
calls int calls int
} }
func (m *mockForecast) Forecast(_ context.Context, _ []*monTypes.EnrichedGameData, _ int) { func (m *mockForecast) Forecast(_ context.Context, _ []*monTypes.EnrichedGameData, _, _ int) {
m.calls++ m.calls++
} }
...@@ -188,19 +188,20 @@ type mockExtractor struct { ...@@ -188,19 +188,20 @@ type mockExtractor struct {
maxSuccess int maxSuccess int
games []*monTypes.EnrichedGameData games []*monTypes.EnrichedGameData
ignoredCount int ignoredCount int
failedCount int
} }
func (m *mockExtractor) Extract( func (m *mockExtractor) Extract(
_ context.Context, _ context.Context,
_ common.Hash, _ common.Hash,
_ uint64, _ uint64,
) ([]*monTypes.EnrichedGameData, int, error) { ) ([]*monTypes.EnrichedGameData, int, int, error) {
m.calls++ m.calls++
if m.fetchErr != nil { if m.fetchErr != nil {
return nil, 0, m.fetchErr return nil, 0, 0, m.fetchErr
} }
if m.calls > m.maxSuccess && m.maxSuccess != 0 { if m.calls > m.maxSuccess && m.maxSuccess != 0 {
return nil, 0, mockErr return nil, 0, 0, mockErr
} }
return m.games, m.ignoredCount, nil return m.games, m.ignoredCount, m.failedCount, 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