Commit e8145e7c authored by refcell.eth's avatar refcell.eth Committed by GitHub

feat(op-challenger): Bond Claiming (#9257)

* feat(op-challenger): bond claiming

* fix(op-challenger): Add a simple bond claim failure metric for tracking

* fix(op-challenger): add multiple error test to claimer and move claim scheduling to before allowlist filtering
parent a320015e
...@@ -90,7 +90,7 @@ const ( ...@@ -90,7 +90,7 @@ const (
// DefaultGameWindow is the default maximum time duration in the past // DefaultGameWindow is the default maximum time duration in the past
// that the challenger will look for games to progress. // that the challenger will look for games to progress.
// The default value is 11 days, which is a 4 day resolution buffer // The default value is 11 days, which is a 4 day resolution buffer
// plus the 7 day game finalization window. // and bond claiming buffer plus the 7 day game finalization window.
DefaultGameWindow = time.Duration(11 * 24 * time.Hour) DefaultGameWindow = time.Duration(11 * 24 * time.Hour)
DefaultMaxPendingTx = 10 DefaultMaxPendingTx = 10
) )
......
...@@ -131,8 +131,9 @@ var ( ...@@ -131,8 +131,9 @@ var (
Value: config.DefaultCannonInfoFreq, Value: config.DefaultCannonInfoFreq,
} }
GameWindowFlag = &cli.DurationFlag{ GameWindowFlag = &cli.DurationFlag{
Name: "game-window", Name: "game-window",
Usage: "The time window which the challenger will look for games to progress.", Usage: "The time window which the challenger will look for games to progress and claim bonds. " +
"This should include a buffer for the challenger to claim bonds for games outside the maximum game duration.",
EnvVars: prefixEnvVars("GAME_WINDOW"), EnvVars: prefixEnvVars("GAME_WINDOW"),
Value: config.DefaultGameWindow, Value: config.DefaultGameWindow,
} }
......
package claims
import (
"context"
"errors"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
var _ BondClaimer = (*claimer)(nil)
type BondClaimer interface {
ClaimBonds(ctx context.Context, games []types.GameMetadata) error
}
type BondClaimMetrics interface {
RecordBondClaimed(amount uint64)
}
type BondContract interface {
GetCredit(ctx context.Context, receipient common.Address) (*big.Int, error)
ClaimCredit(receipient common.Address) (txmgr.TxCandidate, error)
}
type claimer struct {
logger log.Logger
metrics BondClaimMetrics
caller *batching.MultiCaller
txSender types.TxSender
}
func NewBondClaimer(l log.Logger, m BondClaimMetrics, c *batching.MultiCaller, txSender types.TxSender) *claimer {
return &claimer{
logger: l,
metrics: m,
caller: c,
txSender: txSender,
}
}
func (c *claimer) ClaimBonds(ctx context.Context, games []types.GameMetadata) (err error) {
for _, game := range games {
err = errors.Join(err, c.claimBond(ctx, game.Proxy))
}
return err
}
func (c *claimer) claimBond(ctx context.Context, gameAddr common.Address) error {
c.logger.Debug("attempting to claim bonds for", "game", gameAddr)
contract, err := contracts.NewFaultDisputeGameContract(gameAddr, c.caller)
if err != nil {
return fmt.Errorf("failed to create contract: %w", err)
}
credit, err := contract.GetCredit(ctx, c.txSender.From())
if err != nil {
return fmt.Errorf("failed to get credit: %w", err)
}
if credit.Cmp(big.NewInt(0)) == 0 {
c.logger.Debug("no credit to claim", "game", gameAddr)
return nil
}
candidate, err := contract.ClaimCredit(c.txSender.From())
if err != nil {
return fmt.Errorf("failed to create credit claim tx: %w", err)
}
if _, err = c.txSender.SendAndWait("claim credit", candidate); err != nil {
return fmt.Errorf("failed to claim credit: %w", err)
}
c.metrics.RecordBondClaimed(credit.Uint64())
return nil
}
package claims
import (
"context"
"errors"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
batchingTest "github.com/ethereum-optimism/optimism/op-service/sources/batching/test"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
var (
methodCredit = "credit"
mockTxMgrSendError = errors.New("mock tx mgr send error")
)
func TestClaimer_ClaimBonds(t *testing.T) {
t.Run("MultipleBondClaimsSucceed", func(t *testing.T) {
gameAddr := common.HexToAddress("0x1234")
c, m, rpc, txSender := newTestClaimer(t, gameAddr)
rpc.SetResponse(gameAddr, methodCredit, batching.BlockLatest, []interface{}{txSender.From()}, []interface{}{big.NewInt(1)})
err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}, {Proxy: gameAddr}, {Proxy: gameAddr}})
require.NoError(t, err)
require.Equal(t, 3, txSender.sends)
require.Equal(t, 3, m.RecordBondClaimedCalls)
})
t.Run("BondClaimSucceeds", func(t *testing.T) {
gameAddr := common.HexToAddress("0x1234")
c, m, rpc, txSender := newTestClaimer(t, gameAddr)
rpc.SetResponse(gameAddr, methodCredit, batching.BlockLatest, []interface{}{txSender.From()}, []interface{}{big.NewInt(1)})
err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}})
require.NoError(t, err)
require.Equal(t, 1, txSender.sends)
require.Equal(t, 1, m.RecordBondClaimedCalls)
})
t.Run("BondClaimFails", func(t *testing.T) {
gameAddr := common.HexToAddress("0x1234")
c, m, rpc, txSender := newTestClaimer(t, gameAddr)
txSender.sendFails = true
rpc.SetResponse(gameAddr, methodCredit, batching.BlockLatest, []interface{}{txSender.From()}, []interface{}{big.NewInt(1)})
err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}})
require.ErrorIs(t, err, mockTxMgrSendError)
require.Equal(t, 1, txSender.sends)
require.Equal(t, 0, m.RecordBondClaimedCalls)
})
t.Run("ZeroCreditReturnsNil", func(t *testing.T) {
gameAddr := common.HexToAddress("0x1234")
c, m, rpc, txSender := newTestClaimer(t, gameAddr)
rpc.SetResponse(gameAddr, methodCredit, batching.BlockLatest, []interface{}{txSender.From()}, []interface{}{big.NewInt(0)})
err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}})
require.NoError(t, err)
require.Equal(t, 0, txSender.sends)
require.Equal(t, 0, m.RecordBondClaimedCalls)
})
t.Run("MultipleBondClaimFails", func(t *testing.T) {
gameAddr := common.HexToAddress("0x1234")
c, m, rpc, txSender := newTestClaimer(t, gameAddr)
rpc.SetResponse(gameAddr, methodCredit, batching.BlockLatest, []interface{}{txSender.From()}, []interface{}{big.NewInt(1)})
txSender.sendFails = true
err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}, {Proxy: gameAddr}, {Proxy: gameAddr}})
require.ErrorIs(t, err, mockTxMgrSendError)
require.Equal(t, 3, txSender.sends)
require.Equal(t, 0, m.RecordBondClaimedCalls)
})
}
func newTestClaimer(t *testing.T, gameAddr common.Address) (*claimer, *mockClaimMetrics, *batchingTest.AbiBasedRpc, *mockTxSender) {
logger := testlog.Logger(t, log.LvlDebug)
m := &mockClaimMetrics{}
txSender := &mockTxSender{}
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi()
require.NoError(t, err)
stubRpc := batchingTest.NewAbiBasedRpc(t, gameAddr, fdgAbi)
caller := batching.NewMultiCaller(stubRpc, 100)
c := NewBondClaimer(logger, m, caller, txSender)
return c, m, stubRpc, txSender
}
type mockClaimMetrics struct {
RecordBondClaimedCalls int
}
func (m *mockClaimMetrics) RecordBondClaimed(amount uint64) {
m.RecordBondClaimedCalls++
}
type mockTxSender struct {
sends int
sendFails bool
statusFail bool
}
func (s *mockTxSender) From() common.Address {
return common.HexToAddress("0x33333")
}
func (s *mockTxSender) SendAndWait(_ string, _ ...txmgr.TxCandidate) ([]*ethtypes.Receipt, error) {
s.sends++
if s.sendFails {
return nil, mockTxMgrSendError
}
if s.statusFail {
return []*ethtypes.Receipt{{Status: ethtypes.ReceiptStatusFailed}}, nil
}
return []*ethtypes.Receipt{{Status: ethtypes.ReceiptStatusSuccessful}}, nil
}
package claims
import (
"context"
"sync"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum/go-ethereum/log"
)
type BondClaimScheduler struct {
log log.Logger
metrics BondClaimSchedulerMetrics
ch chan schedulerMessage
claimer BondClaimer
cancel func()
wg sync.WaitGroup
}
type BondClaimSchedulerMetrics interface {
RecordBondClaimFailed()
}
type schedulerMessage struct {
blockNumber uint64
games []types.GameMetadata
}
func NewBondClaimScheduler(logger log.Logger, metrics BondClaimSchedulerMetrics, claimer BondClaimer) *BondClaimScheduler {
return &BondClaimScheduler{
log: logger,
metrics: metrics,
ch: make(chan schedulerMessage, 1),
claimer: claimer,
}
}
func (s *BondClaimScheduler) Start(ctx context.Context) {
ctx, cancel := context.WithCancel(ctx)
s.cancel = cancel
s.wg.Add(1)
go s.run(ctx)
}
func (s *BondClaimScheduler) Close() error {
s.cancel()
s.wg.Wait()
return nil
}
func (s *BondClaimScheduler) run(ctx context.Context) {
defer s.wg.Done()
for {
select {
case <-ctx.Done():
return
case msg := <-s.ch:
if err := s.claimer.ClaimBonds(ctx, msg.games); err != nil {
s.metrics.RecordBondClaimFailed()
s.log.Error("Failed to claim bonds", "blockNumber", msg.blockNumber, "err", err)
}
}
}
}
func (s *BondClaimScheduler) Schedule(blockNumber uint64, games []types.GameMetadata) error {
select {
case s.ch <- schedulerMessage{blockNumber, games}:
default:
s.log.Trace("Skipping game bond claim while claiming in progress")
}
return nil
}
...@@ -33,6 +33,8 @@ var ( ...@@ -33,6 +33,8 @@ var (
methodSplitDepth = "splitDepth" methodSplitDepth = "splitDepth"
methodL2BlockNumber = "l2BlockNumber" methodL2BlockNumber = "l2BlockNumber"
methodRequiredBond = "getRequiredBond" methodRequiredBond = "getRequiredBond"
methodClaimCredit = "claimCredit"
methodCredit = "credit"
) )
type FaultDisputeGameContract struct { type FaultDisputeGameContract struct {
...@@ -92,6 +94,19 @@ func (c *FaultDisputeGameContract) GetSplitDepth(ctx context.Context) (types.Dep ...@@ -92,6 +94,19 @@ func (c *FaultDisputeGameContract) GetSplitDepth(ctx context.Context) (types.Dep
return types.Depth(splitDepth.GetBigInt(0).Uint64()), nil return types.Depth(splitDepth.GetBigInt(0).Uint64()), nil
} }
func (c *FaultDisputeGameContract) GetCredit(ctx context.Context, receipient common.Address) (*big.Int, error) {
credit, err := c.multiCaller.SingleCall(ctx, batching.BlockLatest, c.contract.Call(methodCredit, receipient))
if err != nil {
return nil, fmt.Errorf("failed to retrieve credit: %w", err)
}
return credit.GetBigInt(0), nil
}
func (f *FaultDisputeGameContract) ClaimCredit(recipient common.Address) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodClaimCredit, recipient)
return call.ToTxCandidate()
}
func (c *FaultDisputeGameContract) GetRequiredBond(ctx context.Context, position types.Position) (*big.Int, error) { func (c *FaultDisputeGameContract) GetRequiredBond(ctx context.Context, position types.Position) (*big.Int, error) {
bond, err := c.multiCaller.SingleCall(ctx, batching.BlockLatest, c.contract.Call(methodRequiredBond, position.ToGIndex())) bond, err := c.multiCaller.SingleCall(ctx, batching.BlockLatest, c.contract.Call(methodRequiredBond, position.ToGIndex()))
if err != nil { if err != nil {
......
...@@ -23,6 +23,8 @@ type GameContract interface { ...@@ -23,6 +23,8 @@ type GameContract interface {
DefendTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error) DefendTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error)
StepTx(claimIdx uint64, isAttack bool, stateData []byte, proof []byte) (txmgr.TxCandidate, error) StepTx(claimIdx uint64, isAttack bool, stateData []byte, proof []byte) (txmgr.TxCandidate, error)
GetRequiredBond(ctx context.Context, position types.Position) (*big.Int, error) GetRequiredBond(ctx context.Context, position types.Position) (*big.Int, error)
GetCredit(ctx context.Context, receipient common.Address) (*big.Int, error)
ClaimCredit(receipient common.Address) (txmgr.TxCandidate, error)
} }
type Oracle interface { type Oracle interface {
...@@ -31,8 +33,7 @@ type Oracle interface { ...@@ -31,8 +33,7 @@ type Oracle interface {
// FaultResponder implements the [Responder] interface to send onchain transactions. // FaultResponder implements the [Responder] interface to send onchain transactions.
type FaultResponder struct { type FaultResponder struct {
log log.Logger log log.Logger
sender gameTypes.TxSender sender gameTypes.TxSender
contract GameContract contract GameContract
uploader preimages.PreimageUploader uploader preimages.PreimageUploader
......
...@@ -411,3 +411,11 @@ func (m *mockContract) UpdateOracleTx(_ context.Context, claimIdx uint64, data * ...@@ -411,3 +411,11 @@ func (m *mockContract) UpdateOracleTx(_ context.Context, claimIdx uint64, data *
func (m *mockContract) GetRequiredBond(_ context.Context, position types.Position) (*big.Int, error) { func (m *mockContract) GetRequiredBond(_ context.Context, position types.Position) (*big.Int, error) {
return big.NewInt(5), nil return big.NewInt(5), nil
} }
func (m *mockContract) GetCredit(_ context.Context, _ common.Address) (*big.Int, error) {
return big.NewInt(5), nil
}
func (m *mockContract) ClaimCredit(_ common.Address) (txmgr.TxCandidate, error) {
return txmgr.TxCandidate{TxData: ([]byte)("claimCredit")}, nil
}
...@@ -34,6 +34,10 @@ type preimageScheduler interface { ...@@ -34,6 +34,10 @@ type preimageScheduler interface {
Schedule(blockHash common.Hash, blockNumber uint64) error Schedule(blockHash common.Hash, blockNumber uint64) error
} }
type claimer interface {
Schedule(blockNumber uint64, games []types.GameMetadata) error
}
type gameMonitor struct { type gameMonitor struct {
logger log.Logger logger log.Logger
clock clock.Clock clock clock.Clock
...@@ -41,6 +45,7 @@ type gameMonitor struct { ...@@ -41,6 +45,7 @@ type gameMonitor struct {
scheduler gameScheduler scheduler gameScheduler
preimages preimageScheduler preimages preimageScheduler
gameWindow time.Duration gameWindow time.Duration
claimer claimer
fetchBlockNumber blockNumberFetcher fetchBlockNumber blockNumberFetcher
allowedGames []common.Address allowedGames []common.Address
l1HeadsSub ethereum.Subscription l1HeadsSub ethereum.Subscription
...@@ -67,6 +72,7 @@ func newGameMonitor( ...@@ -67,6 +72,7 @@ func newGameMonitor(
scheduler gameScheduler, scheduler gameScheduler,
preimages preimageScheduler, preimages preimageScheduler,
gameWindow time.Duration, gameWindow time.Duration,
claimer claimer,
fetchBlockNumber blockNumberFetcher, fetchBlockNumber blockNumberFetcher,
allowedGames []common.Address, allowedGames []common.Address,
l1Source MinimalSubscriber, l1Source MinimalSubscriber,
...@@ -78,6 +84,7 @@ func newGameMonitor( ...@@ -78,6 +84,7 @@ func newGameMonitor(
preimages: preimages, preimages: preimages,
source: source, source: source,
gameWindow: gameWindow, gameWindow: gameWindow,
claimer: claimer,
fetchBlockNumber: fetchBlockNumber, fetchBlockNumber: fetchBlockNumber,
allowedGames: allowedGames, allowedGames: allowedGames,
l1Source: &headSource{inner: l1Source}, l1Source: &headSource{inner: l1Source},
...@@ -113,6 +120,9 @@ func (m *gameMonitor) progressGames(ctx context.Context, blockHash common.Hash, ...@@ -113,6 +120,9 @@ func (m *gameMonitor) progressGames(ctx context.Context, blockHash common.Hash,
if err != nil { if err != nil {
return fmt.Errorf("failed to load games: %w", err) return fmt.Errorf("failed to load games: %w", err)
} }
if err := m.claimer.Schedule(blockNumber, games); err != nil {
return fmt.Errorf("failed to schedule bond claims: %w", err)
}
var gamesToPlay []types.GameMetadata var gamesToPlay []types.GameMetadata
for _, game := range games { for _, game := range games {
if !m.allowedGame(game.Proxy) { if !m.allowedGame(game.Proxy) {
......
...@@ -189,6 +189,7 @@ func setupMonitorTest( ...@@ -189,6 +189,7 @@ func setupMonitorTest(
sched := &stubScheduler{} sched := &stubScheduler{}
preimages := &stubPreimageScheduler{} preimages := &stubPreimageScheduler{}
mockHeadSource := &mockNewHeadSource{} mockHeadSource := &mockNewHeadSource{}
mockScheduler := &mockScheduler{}
monitor := newGameMonitor( monitor := newGameMonitor(
logger, logger,
clock.SystemClock, clock.SystemClock,
...@@ -196,6 +197,7 @@ func setupMonitorTest( ...@@ -196,6 +197,7 @@ func setupMonitorTest(
sched, sched,
preimages, preimages,
time.Duration(0), time.Duration(0),
mockScheduler,
fetchBlockNum, fetchBlockNum,
allowedGames, allowedGames,
mockHeadSource, mockHeadSource,
...@@ -242,6 +244,16 @@ func (m *mockNewHeadSource) EthSubscribe( ...@@ -242,6 +244,16 @@ func (m *mockNewHeadSource) EthSubscribe(
return m.sub, nil return m.sub, nil
} }
type mockScheduler struct {
scheduleErr error
scheduleCalls int
}
func (m *mockScheduler) Schedule(uint64, []types.GameMetadata) error {
m.scheduleCalls++
return m.scheduleErr
}
type mockSubscription struct { type mockSubscription struct {
errChan chan error errChan chan error
headers chan<- *ethtypes.Header headers chan<- *ethtypes.Header
...@@ -254,7 +266,8 @@ func (m *mockSubscription) Err() <-chan error { ...@@ -254,7 +266,8 @@ func (m *mockSubscription) Err() <-chan error {
} }
type stubGameSource struct { type stubGameSource struct {
games []types.GameMetadata fetchErr error
games []types.GameMetadata
} }
func (s *stubGameSource) FetchAllGamesAtBlock( func (s *stubGameSource) FetchAllGamesAtBlock(
...@@ -262,6 +275,9 @@ func (s *stubGameSource) FetchAllGamesAtBlock( ...@@ -262,6 +275,9 @@ func (s *stubGameSource) FetchAllGamesAtBlock(
_ uint64, _ uint64,
_ common.Hash, _ common.Hash,
) ([]types.GameMetadata, error) { ) ([]types.GameMetadata, error) {
if s.fetchErr != nil {
return nil, s.fetchErr
}
return s.games, nil return s.games, nil
} }
......
...@@ -16,6 +16,7 @@ import ( ...@@ -16,6 +16,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault" "github.com/ethereum-optimism/optimism/op-challenger/game/fault"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/claims"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/loader" "github.com/ethereum-optimism/optimism/op-challenger/game/loader"
"github.com/ethereum-optimism/optimism/op-challenger/game/registry" "github.com/ethereum-optimism/optimism/op-challenger/game/registry"
...@@ -49,6 +50,8 @@ type Service struct { ...@@ -49,6 +50,8 @@ type Service struct {
loader *loader.GameLoader loader *loader.GameLoader
claimer *claims.BondClaimScheduler
factoryContract *contracts.DisputeGameFactoryContract factoryContract *contracts.DisputeGameFactoryContract
registry *registry.GameTypeRegistry registry *registry.GameTypeRegistry
rollupClient *sources.RollupClient rollupClient *sources.RollupClient
...@@ -105,6 +108,9 @@ func (s *Service) initFromConfig(ctx context.Context, cfg *config.Config) error ...@@ -105,6 +108,9 @@ func (s *Service) initFromConfig(ctx context.Context, cfg *config.Config) error
if err := s.initGameLoader(); err != nil { if err := s.initGameLoader(); err != nil {
return fmt.Errorf("failed to init game loader: %w", err) return fmt.Errorf("failed to init game loader: %w", err)
} }
if err := s.initBondClaims(cfg); err != nil {
return fmt.Errorf("failed to init bond claiming: %w", err)
}
if err := s.registerGameTypes(ctx, cfg); err != nil { if err := s.registerGameTypes(ctx, cfg); err != nil {
return fmt.Errorf("failed to register game types: %w", err) return fmt.Errorf("failed to register game types: %w", err)
} }
...@@ -201,6 +207,13 @@ func (s *Service) initGameLoader() error { ...@@ -201,6 +207,13 @@ func (s *Service) initGameLoader() error {
return nil return nil
} }
func (s *Service) initBondClaims(cfg *config.Config) error {
caller := batching.NewMultiCaller(s.l1Client.Client(), batching.DefaultBatchSize)
claimer := claims.NewBondClaimer(s.logger, s.metrics, caller, s.txSender)
s.claimer = claims.NewBondClaimScheduler(s.logger, s.metrics, claimer)
return nil
}
func (s *Service) initRollupClient(ctx context.Context, cfg *config.Config) error { func (s *Service) initRollupClient(ctx context.Context, cfg *config.Config) error {
if cfg.RollupRpc == "" { if cfg.RollupRpc == "" {
return nil return nil
...@@ -240,7 +253,7 @@ func (s *Service) initLargePreimages() error { ...@@ -240,7 +253,7 @@ func (s *Service) initLargePreimages() error {
} }
func (s *Service) initMonitor(cfg *config.Config) { func (s *Service) initMonitor(cfg *config.Config) {
s.monitor = newGameMonitor(s.logger, s.cl, s.loader, s.sched, s.preimages, cfg.GameWindow, s.l1Client.BlockNumber, cfg.GameAllowlist, s.pollClient) s.monitor = newGameMonitor(s.logger, s.cl, s.loader, s.sched, s.preimages, cfg.GameWindow, s.claimer, s.l1Client.BlockNumber, cfg.GameAllowlist, s.pollClient)
} }
func (s *Service) Start(ctx context.Context) error { func (s *Service) Start(ctx context.Context) error {
......
...@@ -34,6 +34,9 @@ type Metricer interface { ...@@ -34,6 +34,9 @@ type Metricer interface {
RecordGameMove() RecordGameMove()
RecordCannonExecutionTime(t float64) RecordCannonExecutionTime(t float64)
RecordBondClaimFailed()
RecordBondClaimed(amount uint64)
RecordGamesStatus(inProgress, defenderWon, challengerWon int) RecordGamesStatus(inProgress, defenderWon, challengerWon int)
RecordGameUpdateScheduled() RecordGameUpdateScheduled()
...@@ -62,6 +65,9 @@ type Metrics struct { ...@@ -62,6 +65,9 @@ type Metrics struct {
executors prometheus.GaugeVec executors prometheus.GaugeVec
bondClaimFailures prometheus.Counter
bondsClaimed prometheus.Counter
highestActedL1Block prometheus.Gauge highestActedL1Block prometheus.Gauge
moves prometheus.Counter moves prometheus.Counter
...@@ -129,6 +135,16 @@ func NewMetrics() *Metrics { ...@@ -129,6 +135,16 @@ func NewMetrics() *Metrics {
[]float64{1.0, 10.0}, []float64{1.0, 10.0},
prometheus.ExponentialBuckets(30.0, 2.0, 14)...), prometheus.ExponentialBuckets(30.0, 2.0, 14)...),
}), }),
bondClaimFailures: factory.NewCounter(prometheus.CounterOpts{
Namespace: Namespace,
Name: "claim_failures",
Help: "Number of bond claims that failed",
}),
bondsClaimed: factory.NewCounter(prometheus.CounterOpts{
Namespace: Namespace,
Name: "bonds",
Help: "Number of bonds claimed by the challenge agent",
}),
trackedGames: *factory.NewGaugeVec(prometheus.GaugeOpts{ trackedGames: *factory.NewGaugeVec(prometheus.GaugeOpts{
Namespace: Namespace, Namespace: Namespace,
Name: "tracked_games", Name: "tracked_games",
...@@ -185,6 +201,14 @@ func (m *Metrics) RecordGameStep() { ...@@ -185,6 +201,14 @@ func (m *Metrics) RecordGameStep() {
m.steps.Add(1) m.steps.Add(1)
} }
func (m *Metrics) RecordBondClaimFailed() {
m.bondClaimFailures.Add(1)
}
func (m *Metrics) RecordBondClaimed(amount uint64) {
m.bondsClaimed.Add(float64(amount))
}
func (m *Metrics) RecordCannonExecutionTime(t float64) { func (m *Metrics) RecordCannonExecutionTime(t float64) {
m.cannonExecutionTime.Observe(t) m.cannonExecutionTime.Observe(t)
} }
......
...@@ -28,6 +28,9 @@ func (*NoopMetricsImpl) RecordGameStep() {} ...@@ -28,6 +28,9 @@ func (*NoopMetricsImpl) RecordGameStep() {}
func (*NoopMetricsImpl) RecordActedL1Block(_ uint64) {} func (*NoopMetricsImpl) RecordActedL1Block(_ uint64) {}
func (*NoopMetricsImpl) RecordBondClaimFailed() {}
func (*NoopMetricsImpl) RecordBondClaimed(uint64) {}
func (*NoopMetricsImpl) RecordCannonExecutionTime(t float64) {} func (*NoopMetricsImpl) RecordCannonExecutionTime(t float64) {}
func (*NoopMetricsImpl) RecordGamesStatus(inProgress, defenderWon, challengerWon int) {} func (*NoopMetricsImpl) RecordGamesStatus(inProgress, defenderWon, challengerWon 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