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

op-dispute-mon: Add metric to report timestamp of last invalid proposal (#10373)

parent 8ed6612b
...@@ -92,6 +92,8 @@ type Metricer interface { ...@@ -92,6 +92,8 @@ type Metricer interface {
RecordGameAgreement(status GameAgreementStatus, count int) RecordGameAgreement(status GameAgreementStatus, count int)
RecordLatestInvalidProposal(timestamp uint64)
RecordIgnoredGames(count int) RecordIgnoredGames(count int)
RecordBondCollateral(addr common.Address, required *big.Int, available *big.Int) RecordBondCollateral(addr common.Address, required *big.Int, available *big.Int)
...@@ -127,8 +129,9 @@ type Metrics struct { ...@@ -127,8 +129,9 @@ type Metrics struct {
lastOutputFetch prometheus.Gauge lastOutputFetch prometheus.Gauge
gamesAgreement prometheus.GaugeVec gamesAgreement prometheus.GaugeVec
ignoredGames prometheus.Gauge latestInvalidProposal prometheus.Gauge
ignoredGames prometheus.Gauge
requiredCollateral prometheus.GaugeVec requiredCollateral prometheus.GaugeVec
availableCollateral prometheus.GaugeVec availableCollateral prometheus.GaugeVec
...@@ -228,6 +231,11 @@ func NewMetrics() *Metrics { ...@@ -228,6 +231,11 @@ func NewMetrics() *Metrics {
"result_correctness", "result_correctness",
"root_agreement", "root_agreement",
}), }),
latestInvalidProposal: factory.NewGauge(prometheus.GaugeOpts{
Namespace: Namespace,
Name: "latest_invalid_proposal",
Help: "Timestamp of the most recent game with an invalid root claim in unix seconds",
}),
ignoredGames: factory.NewGauge(prometheus.GaugeOpts{ ignoredGames: factory.NewGauge(prometheus.GaugeOpts{
Namespace: Namespace, Namespace: Namespace,
Name: "ignored_games", Name: "ignored_games",
...@@ -370,6 +378,10 @@ func (m *Metrics) RecordGameAgreement(status GameAgreementStatus, count int) { ...@@ -370,6 +378,10 @@ func (m *Metrics) RecordGameAgreement(status GameAgreementStatus, count int) {
m.gamesAgreement.WithLabelValues(labelValuesFor(status)...).Set(float64(count)) m.gamesAgreement.WithLabelValues(labelValuesFor(status)...).Set(float64(count))
} }
func (m *Metrics) RecordLatestInvalidProposal(timestamp uint64) {
m.latestInvalidProposal.Set(float64(timestamp))
}
func (m *Metrics) RecordIgnoredGames(count int) { func (m *Metrics) RecordIgnoredGames(count int) {
m.ignoredGames.Set(float64(count)) m.ignoredGames.Set(float64(count))
} }
......
...@@ -33,6 +33,8 @@ func (*NoopMetricsImpl) RecordOutputFetchTime(_ float64) {} ...@@ -33,6 +33,8 @@ func (*NoopMetricsImpl) RecordOutputFetchTime(_ float64) {}
func (*NoopMetricsImpl) RecordGameAgreement(_ GameAgreementStatus, _ int) {} func (*NoopMetricsImpl) RecordGameAgreement(_ GameAgreementStatus, _ int) {}
func (*NoopMetricsImpl) RecordLatestInvalidProposal(_ uint64) {}
func (*NoopMetricsImpl) RecordIgnoredGames(_ int) {} func (*NoopMetricsImpl) RecordIgnoredGames(_ int) {}
func (i *NoopMetricsImpl) RecordBondCollateral(_ common.Address, _ *big.Int, _ *big.Int) {} func (i *NoopMetricsImpl) RecordBondCollateral(_ common.Address, _ *big.Int, _ *big.Int) {}
...@@ -24,9 +24,24 @@ type OutputValidator interface { ...@@ -24,9 +24,24 @@ type OutputValidator interface {
type ForecastMetrics interface { type ForecastMetrics interface {
RecordGameAgreement(status metrics.GameAgreementStatus, count int) RecordGameAgreement(status metrics.GameAgreementStatus, count int)
RecordLatestInvalidProposal(timestamp uint64)
RecordIgnoredGames(count int) RecordIgnoredGames(count int)
} }
type forecastBatch struct {
AgreeDefenderAhead int
DisagreeDefenderAhead int
AgreeChallengerAhead int
DisagreeChallengerAhead int
AgreeDefenderWins int
DisagreeDefenderWins int
AgreeChallengerWins int
DisagreeChallengerWins int
LatestInvalidProposal uint64
}
type forecast struct { type forecast struct {
logger log.Logger logger log.Logger
metrics ForecastMetrics metrics ForecastMetrics
...@@ -42,7 +57,7 @@ func newForecast(logger log.Logger, metrics ForecastMetrics, validator OutputVal ...@@ -42,7 +57,7 @@ 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 int) {
batch := monTypes.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)
...@@ -51,7 +66,7 @@ func (f *forecast) Forecast(ctx context.Context, games []*monTypes.EnrichedGameD ...@@ -51,7 +66,7 @@ func (f *forecast) Forecast(ctx context.Context, games []*monTypes.EnrichedGameD
f.recordBatch(batch, ignoredCount) f.recordBatch(batch, ignoredCount)
} }
func (f *forecast) recordBatch(batch monTypes.ForecastBatch, ignoredCount int) { func (f *forecast) recordBatch(batch forecastBatch, ignoredCount 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)
...@@ -62,10 +77,12 @@ func (f *forecast) recordBatch(batch monTypes.ForecastBatch, ignoredCount int) { ...@@ -62,10 +77,12 @@ func (f *forecast) recordBatch(batch monTypes.ForecastBatch, ignoredCount int) {
f.metrics.RecordGameAgreement(metrics.AgreeDefenderAhead, batch.AgreeDefenderAhead) f.metrics.RecordGameAgreement(metrics.AgreeDefenderAhead, batch.AgreeDefenderAhead)
f.metrics.RecordGameAgreement(metrics.DisagreeDefenderAhead, batch.DisagreeDefenderAhead) f.metrics.RecordGameAgreement(metrics.DisagreeDefenderAhead, batch.DisagreeDefenderAhead)
f.metrics.RecordLatestInvalidProposal(batch.LatestInvalidProposal)
f.metrics.RecordIgnoredGames(ignoredCount) f.metrics.RecordIgnoredGames(ignoredCount)
} }
func (f *forecast) forecastGame(ctx context.Context, game *monTypes.EnrichedGameData, metrics *monTypes.ForecastBatch) error { func (f *forecast) forecastGame(ctx context.Context, game *monTypes.EnrichedGameData, metrics *forecastBatch) error {
// Check the root agreement. // Check the root agreement.
agreement, expected, err := f.validator.CheckRootAgreement(ctx, game.L1HeadNum, game.L2BlockNumber, game.RootClaim) agreement, expected, err := f.validator.CheckRootAgreement(ctx, game.L1HeadNum, game.L2BlockNumber, game.RootClaim)
if err != nil { if err != nil {
...@@ -75,6 +92,9 @@ func (f *forecast) forecastGame(ctx context.Context, game *monTypes.EnrichedGame ...@@ -75,6 +92,9 @@ func (f *forecast) forecastGame(ctx context.Context, game *monTypes.EnrichedGame
expectedResult := types.GameStatusDefenderWon expectedResult := types.GameStatusDefenderWon
if !agreement { if !agreement {
expectedResult = types.GameStatusChallengerWon expectedResult = types.GameStatusChallengerWon
if metrics.LatestInvalidProposal < game.Timestamp {
metrics.LatestInvalidProposal = game.Timestamp
}
} }
if game.Status != types.GameStatusInProgress { if game.Status != types.GameStatusInProgress {
......
...@@ -236,8 +236,8 @@ func TestForecast_Forecast_MultipleGames(t *testing.T) { ...@@ -236,8 +236,8 @@ func TestForecast_Forecast_MultipleGames(t *testing.T) {
{}, {},
mockRootClaim, mockRootClaim,
{}, {},
{}, {}, // Expected latest invalid proposal (will have timestamp 7)
{}, mockRootClaim,
} }
games := make([]*monTypes.EnrichedGameData, 9) games := make([]*monTypes.EnrichedGameData, 9)
for i := range games { for i := range games {
...@@ -245,6 +245,9 @@ func TestForecast_Forecast_MultipleGames(t *testing.T) { ...@@ -245,6 +245,9 @@ func TestForecast_Forecast_MultipleGames(t *testing.T) {
Status: gameStatus[i], Status: gameStatus[i],
Claims: claims[i], Claims: claims[i],
RootClaim: rootClaims[i], RootClaim: rootClaims[i],
GameMetadata: types.GameMetadata{
Timestamp: uint64(i),
},
} }
} }
forecast.Forecast(context.Background(), games, 3) forecast.Forecast(context.Background(), games, 3)
...@@ -255,10 +258,12 @@ func TestForecast_Forecast_MultipleGames(t *testing.T) { ...@@ -255,10 +258,12 @@ func TestForecast_Forecast_MultipleGames(t *testing.T) {
expectedMetrics[metrics.DisagreeChallengerAhead] = 1 expectedMetrics[metrics.DisagreeChallengerAhead] = 1
expectedMetrics[metrics.AgreeDefenderAhead] = 1 expectedMetrics[metrics.AgreeDefenderAhead] = 1
expectedMetrics[metrics.DisagreeDefenderAhead] = 1 expectedMetrics[metrics.DisagreeDefenderAhead] = 1
expectedMetrics[metrics.AgreeChallengerWins] = 1
expectedMetrics[metrics.DisagreeDefenderWins] = 2 expectedMetrics[metrics.DisagreeDefenderWins] = 2
expectedMetrics[metrics.DisagreeChallengerWins] = 3 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.EqualValues(t, 7, m.latestInvalidProposal)
} }
func setupForecastTest(t *testing.T) (*forecast, *mockForecastMetrics, *stubOutputValidator, *testlog.CapturingHandler) { func setupForecastTest(t *testing.T) (*forecast, *mockForecastMetrics, *stubOutputValidator, *testlog.CapturingHandler) {
...@@ -284,14 +289,19 @@ func zeroGameAgreement() map[metrics.GameAgreementStatus]int { ...@@ -284,14 +289,19 @@ func zeroGameAgreement() map[metrics.GameAgreementStatus]int {
} }
type mockForecastMetrics struct { type mockForecastMetrics struct {
gameAgreement map[metrics.GameAgreementStatus]int gameAgreement map[metrics.GameAgreementStatus]int
ignoredGames int ignoredGames int
latestInvalidProposal uint64
} }
func (m *mockForecastMetrics) RecordGameAgreement(status metrics.GameAgreementStatus, count int) { func (m *mockForecastMetrics) RecordGameAgreement(status metrics.GameAgreementStatus, count int) {
m.gameAgreement[status] = count m.gameAgreement[status] = count
} }
func (m *mockForecastMetrics) RecordLatestInvalidProposal(timestamp uint64) {
m.latestInvalidProposal = timestamp
}
func (m *mockForecastMetrics) RecordIgnoredGames(count int) { func (m *mockForecastMetrics) RecordIgnoredGames(count int) {
m.ignoredGames = count m.ignoredGames = count
} }
......
...@@ -54,15 +54,3 @@ type BidirectionalClaim struct { ...@@ -54,15 +54,3 @@ type BidirectionalClaim struct {
Claim *faultTypes.Claim Claim *faultTypes.Claim
Children []*BidirectionalClaim Children []*BidirectionalClaim
} }
type ForecastBatch struct {
AgreeDefenderAhead int
DisagreeDefenderAhead int
AgreeChallengerAhead int
DisagreeChallengerAhead int
AgreeDefenderWins int
DisagreeDefenderWins int
AgreeChallengerWins int
DisagreeChallengerWins int
}
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