diff --git a/block/balance.go b/block/balance.go new file mode 100644 index 000000000..9c0e301fe --- /dev/null +++ b/block/balance.go @@ -0,0 +1,93 @@ +package block + +import ( + "context" + "fmt" + "strconv" + "sync" + "time" + + "github.com/cockroachdb/errors" + "github.com/dymensionxyz/dymint/da" + "github.com/dymensionxyz/dymint/types" +) + +const CheckBalancesInterval = 3 * time.Minute + +// MonitorBalances checks the balances of the node and updates the gauges for prometheus +func (m *Manager) MonitorBalances(ctx context.Context) error { + ticker := time.NewTicker(CheckBalancesInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return nil + case <-ticker.C: + m.logger.Info("Checking balances.") + balances, err := m.checkBalances() + + if balances.DA != nil { + if amountFloat, errDA := strconv.ParseFloat(balances.DA.Amount.String(), 64); errDA == nil { + types.DaLayerBalanceGauge.Set(amountFloat) + } else { + m.logger.Error("Parsing DA balance amount", "error", errDA) + } + } + + if balances.SL != nil { + if amountFloat, errSL := strconv.ParseFloat(balances.SL.Amount.String(), 64); errSL == nil { + types.HubLayerBalanceGauge.Set(amountFloat) + } else { + m.logger.Error("Parsing SL balance amount", "error", errSL) + } + } + + if err != nil { + m.logger.Error("Checking balances", "error", err) + } + } + } +} + +type Balances struct { + DA *da.Balance + SL *types.Balance +} + +func (m *Manager) checkBalances() (*Balances, error) { + balances := &Balances{} + var wg sync.WaitGroup + wg.Add(2) + + var errDA, errSL error + + go func() { + defer wg.Done() + balance, err := m.DAClient.GetSignerBalance() + if err != nil { + errDA = fmt.Errorf("get DA signer balance: %w", err) + return + } + balances.DA = &balance + }() + + go func() { + defer wg.Done() + balance, err := m.SLClient.GetSignerBalance() + if err != nil { + errSL = fmt.Errorf("get SL signer balance: %w", err) + return + } + balances.SL = &balance + }() + + wg.Wait() + + errs := errors.Join(errDA, errSL) + if errs != nil { + return balances, fmt.Errorf("errors checking balances: %w", errs) + } + + return balances, nil +} diff --git a/block/manager.go b/block/manager.go index 7e66ba870..378c314b3 100644 --- a/block/manager.go +++ b/block/manager.go @@ -278,6 +278,10 @@ func (m *Manager) Start(ctx context.Context) error { return m.MonitorForkUpdateLoop(ctx) }) + uerrors.ErrGroupGoLog(eg, m.logger, func() error { + return m.MonitorBalances(ctx) + }) + // run based on the node role if !amIProposer { return m.runAsFullNode(ctx, eg) diff --git a/da/avail/avail.go b/da/avail/avail.go index ea932c756..d1a84e64d 100644 --- a/da/avail/avail.go +++ b/da/avail/avail.go @@ -102,7 +102,7 @@ func WithBatchRetryAttempts(attempts uint) da.Option { } // Init initializes DataAvailabilityLayerClient instance. -func (c *DataAvailabilityLayerClient) Init(config []byte, pubsubServer *pubsub.Server, kvStore store.KV, logger types.Logger, options ...da.Option) error { +func (c *DataAvailabilityLayerClient) Init(config []byte, pubsubServer *pubsub.Server, _ store.KV, logger types.Logger, options ...da.Option) error { c.logger = logger c.synced = make(chan struct{}, 1) @@ -442,3 +442,8 @@ func (c *DataAvailabilityLayerClient) getHeightFromHash(hash availtypes.Hash) (u func (d *DataAvailabilityLayerClient) GetMaxBlobSizeBytes() uint32 { return maxBlobSize } + +// GetBalance returns the balance for a specific address +func (c *DataAvailabilityLayerClient) GetSignerBalance() (da.Balance, error) { + return da.Balance{}, nil +} diff --git a/da/celestia/celestia.go b/da/celestia/celestia.go index 0f29ad4cf..05d9a827f 100644 --- a/da/celestia/celestia.go +++ b/da/celestia/celestia.go @@ -72,7 +72,7 @@ func WithSubmitBackoff(c uretry.BackoffConfig) da.Option { } // Init initializes DataAvailabilityLayerClient instance. -func (c *DataAvailabilityLayerClient) Init(config []byte, pubsubServer *pubsub.Server, kvStore store.KV, logger types.Logger, options ...da.Option) error { +func (c *DataAvailabilityLayerClient) Init(config []byte, pubsubServer *pubsub.Server, _ store.KV, logger types.Logger, options ...da.Option) error { c.logger = logger c.synced = make(chan struct{}, 1) var err error @@ -605,3 +605,21 @@ func (c *DataAvailabilityLayerClient) sync(rpc *openrpc.Client) { func (d *DataAvailabilityLayerClient) GetMaxBlobSizeBytes() uint32 { return maxBlobSizeBytes } + +// GetSignerBalance returns the balance for a specific address +func (d *DataAvailabilityLayerClient) GetSignerBalance() (da.Balance, error) { + ctx, cancel := context.WithTimeout(d.ctx, d.config.Timeout) + defer cancel() + + balance, err := d.rpc.GetSignerBalance(ctx) + if err != nil { + return da.Balance{}, fmt.Errorf("get balance: %w", err) + } + + daBalance := da.Balance{ + Amount: balance.Amount, + Denom: balance.Denom, + } + + return daBalance, nil +} diff --git a/da/celestia/rpc.go b/da/celestia/rpc.go index 74673e6a6..f4dac9d64 100644 --- a/da/celestia/rpc.go +++ b/da/celestia/rpc.go @@ -7,6 +7,7 @@ import ( "github.com/celestiaorg/celestia-openrpc/types/blob" "github.com/celestiaorg/celestia-openrpc/types/header" "github.com/celestiaorg/celestia-openrpc/types/share" + "github.com/celestiaorg/celestia-openrpc/types/state" "github.com/dymensionxyz/dymint/da/celestia/types" ) @@ -35,7 +36,7 @@ func (c *OpenRPC) Submit(ctx context.Context, blobs []*blob.Blob, options *blob. return c.rpc.Blob.Submit(ctx, blobs, options) } -// Getting proof for submitted blob +// GetProof gets the proof for a specific share commitment. func (c *OpenRPC) GetProof(ctx context.Context, height uint64, namespace share.Namespace, commitment blob.Commitment) (*blob.Proof, error) { return c.rpc.Blob.GetProof(ctx, height, namespace, commitment) } @@ -45,12 +46,17 @@ func (c *OpenRPC) Get(ctx context.Context, height uint64, namespace share.Namesp return c.rpc.Blob.Get(ctx, height, namespace, commitment) } -// Get extended Celestia headers for a specific height +// GetByHeight gets the header by height func (c *OpenRPC) GetByHeight(ctx context.Context, height uint64) (*header.ExtendedHeader, error) { return c.rpc.Header.GetByHeight(ctx, height) } -// Get extended Celestia headers for a specific height +// Included checks if a blob is included in the chain func (c *OpenRPC) Included(ctx context.Context, height uint64, namespace share.Namespace, proof *blob.Proof, commitment blob.Commitment) (bool, error) { return c.rpc.Blob.Included(ctx, height, namespace, proof, commitment) } + +// GetSignerBalance balance for a specific address +func (c *OpenRPC) GetSignerBalance(ctx context.Context) (*state.Balance, error) { + return c.rpc.State.Balance(ctx) +} diff --git a/da/celestia/types/rpc.go b/da/celestia/types/rpc.go index efde9c9a4..2949f65ec 100644 --- a/da/celestia/types/rpc.go +++ b/da/celestia/types/rpc.go @@ -6,6 +6,7 @@ import ( "github.com/celestiaorg/celestia-openrpc/types/blob" "github.com/celestiaorg/celestia-openrpc/types/header" "github.com/celestiaorg/celestia-openrpc/types/share" + "github.com/celestiaorg/celestia-openrpc/types/state" ) type CelestiaRPCClient interface { @@ -18,4 +19,7 @@ type CelestiaRPCClient interface { /* --------------------------------- header --------------------------------- */ GetByHeight(ctx context.Context, height uint64) (*header.ExtendedHeader, error) + + /* ---------------------------------- state --------------------------------- */ + GetSignerBalance(ctx context.Context) (*state.Balance, error) } diff --git a/da/da.go b/da/da.go index 14dec8d9f..98bdff5c1 100644 --- a/da/da.go +++ b/da/da.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "cosmossdk.io/math" "github.com/celestiaorg/celestia-openrpc/types/blob" "github.com/cometbft/cometbft/crypto/merkle" "github.com/dymensionxyz/dymint/store" @@ -74,6 +75,11 @@ type DASubmitMetaData struct { Root []byte } +type Balance struct { + Amount math.Int + Denom string +} + const PathSeparator = "|" // ToPath converts a DAMetaData to a path. @@ -221,6 +227,9 @@ type DataAvailabilityLayerClient interface { // Returns the maximum allowed blob size in the DA, used to check the max batch size configured GetMaxBlobSizeBytes() uint32 + + // GetSignerBalance returns the balance for a specific address + GetSignerBalance() (Balance, error) } // BatchRetriever is additional interface that can be implemented by Data Availability Layer Client that is able to retrieve diff --git a/da/grpc/grpc.go b/da/grpc/grpc.go index 4724d37f2..8636cf583 100644 --- a/da/grpc/grpc.go +++ b/da/grpc/grpc.go @@ -5,6 +5,7 @@ import ( "encoding/json" "strconv" + "cosmossdk.io/math" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -153,3 +154,10 @@ func (d *DataAvailabilityLayerClient) RetrieveBatches(daMetaData *da.DASubmitMet Batches: batches, } } + +func (d *DataAvailabilityLayerClient) GetSignerBalance() (da.Balance, error) { + return da.Balance{ + Amount: math.ZeroInt(), + Denom: "adym", + }, nil +} diff --git a/da/local/local.go b/da/local/local.go index 87aab4ada..3852b2797 100644 --- a/da/local/local.go +++ b/da/local/local.go @@ -7,6 +7,7 @@ import ( "sync/atomic" "time" + "cosmossdk.io/math" "github.com/dymensionxyz/dymint/da" "github.com/dymensionxyz/dymint/store" "github.com/dymensionxyz/dymint/types" @@ -181,3 +182,10 @@ func (m *DataAvailabilityLayerClient) updateDAHeight() { func (d *DataAvailabilityLayerClient) GetMaxBlobSizeBytes() uint32 { return maxBlobSize } + +func (m *DataAvailabilityLayerClient) GetSignerBalance() (da.Balance, error) { + return da.Balance{ + Amount: math.ZeroInt(), + Denom: "adym", + }, nil +} diff --git a/go.mod b/go.mod index f359a0a2d..20b4409f7 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/centrifuge/go-substrate-rpc-client/v4 v4.0.12 github.com/cosmos/cosmos-sdk v0.46.16 github.com/dgraph-io/badger/v4 v4.3.0 - github.com/dymensionxyz/cosmosclient v0.4.2-beta.0.20240821081230-b4018b2bac13 + github.com/dymensionxyz/cosmosclient v0.4.2-beta.0.20241121093220-e0d7ad456fbd github.com/dymensionxyz/dymension-rdk v1.6.1-0.20241119103059-def6322e4345 github.com/dymensionxyz/gerr-cosmos v1.0.0 github.com/go-kit/kit v0.12.0 @@ -257,7 +257,7 @@ require ( ) require ( - cosmossdk.io/math v1.3.0 // indirect + cosmossdk.io/math v1.3.0 github.com/DataDog/zstd v1.5.5 // indirect github.com/Jorropo/jsync v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect diff --git a/go.sum b/go.sum index 435e18a78..91ff913de 100644 --- a/go.sum +++ b/go.sum @@ -326,8 +326,8 @@ github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQx github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/dymensionxyz/cometbft v0.34.29-0.20241104165035-feade34f8f89 h1:rGkCcx4dWX9mxAUrq7zrdOc44XddMY/nM6kqYTWjerY= github.com/dymensionxyz/cometbft v0.34.29-0.20241104165035-feade34f8f89/go.mod h1:L9shMfbkZ8B+7JlwANEr+NZbBcn+hBpwdbeYvA5rLCw= -github.com/dymensionxyz/cosmosclient v0.4.2-beta.0.20240821081230-b4018b2bac13 h1:u5yeve5jZR6TdRjjR+vYT/8PWKbhwCZxUmAu+/Tnxyg= -github.com/dymensionxyz/cosmosclient v0.4.2-beta.0.20240821081230-b4018b2bac13/go.mod h1:jabDQYXrccscSE0fXkh7eQFYPWJCRiuWKonFGObVq6s= +github.com/dymensionxyz/cosmosclient v0.4.2-beta.0.20241121093220-e0d7ad456fbd h1:V89QyOFM84o9w0iFdctMU6So8SS/Xt32JWAXGqJduT0= +github.com/dymensionxyz/cosmosclient v0.4.2-beta.0.20241121093220-e0d7ad456fbd/go.mod h1:3weqpVj/TqTFpC0LjEB3H+HZSpm7BrQ1QkEg1Ahy6KY= github.com/dymensionxyz/dymension-rdk v1.6.1-0.20241119103059-def6322e4345 h1:FcHidPgGEHh9ELwodNJkGcHqsG+mdPiGdughzG4W+X8= github.com/dymensionxyz/dymension-rdk v1.6.1-0.20241119103059-def6322e4345/go.mod h1:y89w1OG4C4aF7yyW8bv9PwV3o1KkCx1hyt34ap04Rnk= github.com/dymensionxyz/evmos/v12 v12.1.6-dymension-v0.3 h1:vmAdUGUc4rTIiO3Phezr7vGq+0uPDVKSA4WAe8+yl6w= diff --git a/mocks/github.com/dymensionxyz/dymint/da/celestia/types/mock_CelestiaRPCClient.go b/mocks/github.com/dymensionxyz/dymint/da/celestia/types/mock_CelestiaRPCClient.go index 5994cf817..e461893d6 100644 --- a/mocks/github.com/dymensionxyz/dymint/da/celestia/types/mock_CelestiaRPCClient.go +++ b/mocks/github.com/dymensionxyz/dymint/da/celestia/types/mock_CelestiaRPCClient.go @@ -11,6 +11,8 @@ import ( mock "github.com/stretchr/testify/mock" + sdk "github.com/celestiaorg/celestia-openrpc/types/sdk" + share "github.com/celestiaorg/celestia-openrpc/types/share" ) @@ -268,6 +270,64 @@ func (_c *MockCelestiaRPCClient_GetProof_Call) RunAndReturn(run func(context.Con return _c } +// GetSignerBalance provides a mock function with given fields: ctx +func (_m *MockCelestiaRPCClient) GetSignerBalance(ctx context.Context) (*sdk.Coin, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetSignerBalance") + } + + var r0 *sdk.Coin + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*sdk.Coin, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *sdk.Coin); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*sdk.Coin) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockCelestiaRPCClient_GetSignerBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSignerBalance' +type MockCelestiaRPCClient_GetSignerBalance_Call struct { + *mock.Call +} + +// GetSignerBalance is a helper method to define mock.On call +// - ctx context.Context +func (_e *MockCelestiaRPCClient_Expecter) GetSignerBalance(ctx interface{}) *MockCelestiaRPCClient_GetSignerBalance_Call { + return &MockCelestiaRPCClient_GetSignerBalance_Call{Call: _e.mock.On("GetSignerBalance", ctx)} +} + +func (_c *MockCelestiaRPCClient_GetSignerBalance_Call) Run(run func(ctx context.Context)) *MockCelestiaRPCClient_GetSignerBalance_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockCelestiaRPCClient_GetSignerBalance_Call) Return(_a0 *sdk.Coin, _a1 error) *MockCelestiaRPCClient_GetSignerBalance_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockCelestiaRPCClient_GetSignerBalance_Call) RunAndReturn(run func(context.Context) (*sdk.Coin, error)) *MockCelestiaRPCClient_GetSignerBalance_Call { + _c.Call.Return(run) + return _c +} + // Included provides a mock function with given fields: ctx, height, namespace, proof, commitment func (_m *MockCelestiaRPCClient) Included(ctx context.Context, height uint64, namespace share.Namespace, proof *blob.Proof, commitment blob.Commitment) (bool, error) { ret := _m.Called(ctx, height, namespace, proof, commitment) diff --git a/mocks/github.com/dymensionxyz/dymint/da/mock_DataAvailabilityLayerClient.go b/mocks/github.com/dymensionxyz/dymint/da/mock_DataAvailabilityLayerClient.go index 3478b4c1a..0c20e64e4 100644 --- a/mocks/github.com/dymensionxyz/dymint/da/mock_DataAvailabilityLayerClient.go +++ b/mocks/github.com/dymensionxyz/dymint/da/mock_DataAvailabilityLayerClient.go @@ -162,6 +162,61 @@ func (_c *MockDataAvailabilityLayerClient_GetMaxBlobSizeBytes_Call) RunAndReturn return _c } +// GetSignerBalance provides a mock function with given fields: +func (_m *MockDataAvailabilityLayerClient) GetSignerBalance() (da.Balance, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetSignerBalance") + } + + var r0 da.Balance + var r1 error + if rf, ok := ret.Get(0).(func() (da.Balance, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() da.Balance); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(da.Balance) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockDataAvailabilityLayerClient_GetSignerBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSignerBalance' +type MockDataAvailabilityLayerClient_GetSignerBalance_Call struct { + *mock.Call +} + +// GetSignerBalance is a helper method to define mock.On call +func (_e *MockDataAvailabilityLayerClient_Expecter) GetSignerBalance() *MockDataAvailabilityLayerClient_GetSignerBalance_Call { + return &MockDataAvailabilityLayerClient_GetSignerBalance_Call{Call: _e.mock.On("GetSignerBalance")} +} + +func (_c *MockDataAvailabilityLayerClient_GetSignerBalance_Call) Run(run func()) *MockDataAvailabilityLayerClient_GetSignerBalance_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockDataAvailabilityLayerClient_GetSignerBalance_Call) Return(_a0 da.Balance, _a1 error) *MockDataAvailabilityLayerClient_GetSignerBalance_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockDataAvailabilityLayerClient_GetSignerBalance_Call) RunAndReturn(run func() (da.Balance, error)) *MockDataAvailabilityLayerClient_GetSignerBalance_Call { + _c.Call.Return(run) + return _c +} + // Init provides a mock function with given fields: config, pubsubServer, kvStore, logger, options func (_m *MockDataAvailabilityLayerClient) Init(config []byte, pubsubServer *pubsub.Server, kvStore store.KV, logger types.Logger, options ...da.Option) error { _va := make([]interface{}, len(options)) diff --git a/mocks/github.com/dymensionxyz/dymint/settlement/dymension/mock_CosmosClient.go b/mocks/github.com/dymensionxyz/dymint/settlement/dymension/mock_CosmosClient.go index 03ca2d67a..d5c2bfda4 100644 --- a/mocks/github.com/dymensionxyz/dymint/settlement/dymension/mock_CosmosClient.go +++ b/mocks/github.com/dymensionxyz/dymint/settlement/dymension/mock_CosmosClient.go @@ -254,6 +254,66 @@ func (_c *MockCosmosClient_GetAccount_Call) RunAndReturn(run func(string) (cosmo return _c } +// GetBalance provides a mock function with given fields: ctx, accountName, denom +func (_m *MockCosmosClient) GetBalance(ctx context.Context, accountName string, denom string) (*types.Coin, error) { + ret := _m.Called(ctx, accountName, denom) + + if len(ret) == 0 { + panic("no return value specified for GetBalance") + } + + var r0 *types.Coin + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (*types.Coin, error)); ok { + return rf(ctx, accountName, denom) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) *types.Coin); ok { + r0 = rf(ctx, accountName, denom) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Coin) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, accountName, denom) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockCosmosClient_GetBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBalance' +type MockCosmosClient_GetBalance_Call struct { + *mock.Call +} + +// GetBalance is a helper method to define mock.On call +// - ctx context.Context +// - accountName string +// - denom string +func (_e *MockCosmosClient_Expecter) GetBalance(ctx interface{}, accountName interface{}, denom interface{}) *MockCosmosClient_GetBalance_Call { + return &MockCosmosClient_GetBalance_Call{Call: _e.mock.On("GetBalance", ctx, accountName, denom)} +} + +func (_c *MockCosmosClient_GetBalance_Call) Run(run func(ctx context.Context, accountName string, denom string)) *MockCosmosClient_GetBalance_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string)) + }) + return _c +} + +func (_c *MockCosmosClient_GetBalance_Call) Return(_a0 *types.Coin, _a1 error) *MockCosmosClient_GetBalance_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockCosmosClient_GetBalance_Call) RunAndReturn(run func(context.Context, string, string) (*types.Coin, error)) *MockCosmosClient_GetBalance_Call { + _c.Call.Return(run) + return _c +} + // GetRollappClient provides a mock function with given fields: func (_m *MockCosmosClient) GetRollappClient() rollapp.QueryClient { ret := _m.Called() diff --git a/mocks/github.com/dymensionxyz/dymint/settlement/mock_ClientI.go b/mocks/github.com/dymensionxyz/dymint/settlement/mock_ClientI.go index 68fc48d7e..a0a488cbf 100644 --- a/mocks/github.com/dymensionxyz/dymint/settlement/mock_ClientI.go +++ b/mocks/github.com/dymensionxyz/dymint/settlement/mock_ClientI.go @@ -708,6 +708,61 @@ func (_c *MockClientI_GetSequencerByAddress_Call) RunAndReturn(run func(string) return _c } +// GetSignerBalance provides a mock function with given fields: +func (_m *MockClientI) GetSignerBalance() (types.Balance, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetSignerBalance") + } + + var r0 types.Balance + var r1 error + if rf, ok := ret.Get(0).(func() (types.Balance, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() types.Balance); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(types.Balance) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClientI_GetSignerBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSignerBalance' +type MockClientI_GetSignerBalance_Call struct { + *mock.Call +} + +// GetSignerBalance is a helper method to define mock.On call +func (_e *MockClientI_Expecter) GetSignerBalance() *MockClientI_GetSignerBalance_Call { + return &MockClientI_GetSignerBalance_Call{Call: _e.mock.On("GetSignerBalance")} +} + +func (_c *MockClientI_GetSignerBalance_Call) Run(run func()) *MockClientI_GetSignerBalance_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockClientI_GetSignerBalance_Call) Return(_a0 types.Balance, _a1 error) *MockClientI_GetSignerBalance_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockClientI_GetSignerBalance_Call) RunAndReturn(run func() (types.Balance, error)) *MockClientI_GetSignerBalance_Call { + _c.Call.Return(run) + return _c +} + // Init provides a mock function with given fields: config, rollappId, _a2, logger, options func (_m *MockClientI) Init(config settlement.Config, rollappId string, _a2 *pubsub.Server, logger types.Logger, options ...settlement.Option) error { _va := make([]interface{}, len(options)) diff --git a/settlement/dymension/cosmosclient.go b/settlement/dymension/cosmosclient.go index db69e477a..3e90eb499 100644 --- a/settlement/dymension/cosmosclient.go +++ b/settlement/dymension/cosmosclient.go @@ -32,6 +32,7 @@ type CosmosClient interface { GetRollappClient() rollapptypes.QueryClient GetSequencerClient() sequencertypes.QueryClient GetAccount(accountName string) (cosmosaccount.Account, error) + GetBalance(ctx context.Context, accountName string, denom string) (*sdktypes.Coin, error) } type cosmosClient struct { @@ -86,3 +87,12 @@ func (c *cosmosClient) GetAccount(accountName string) (cosmosaccount.Account, er } return acc, err } + +func (c *cosmosClient) GetBalance(ctx context.Context, address string, denom string) (*sdktypes.Coin, error) { + balance, err := c.Client.Balance(ctx, address, denom) + if err != nil { + return &sdktypes.Coin{}, err + } + + return balance.Balance, nil +} diff --git a/settlement/dymension/dymension.go b/settlement/dymension/dymension.go index 90348112c..2041dc0de 100644 --- a/settlement/dymension/dymension.go +++ b/settlement/dymension/dymension.go @@ -113,7 +113,7 @@ func (c *Client) Stop() error { // SubmitBatch posts a batch to the Dymension Hub. it tries to post the batch until it is accepted by the settlement layer. // it emits success and failure events to the event bus accordingly. -func (c *Client) SubmitBatch(batch *types.Batch, daClient da.Client, daResult *da.ResultSubmitBatch) error { +func (c *Client) SubmitBatch(batch *types.Batch, _ da.Client, daResult *da.ResultSubmitBatch) error { msgUpdateState, err := c.convertBatchToMsgUpdateState(batch, daResult) if err != nil { return fmt.Errorf("convert batch to msg update state: %w", err) @@ -725,3 +725,29 @@ func (c *Client) getLatestProposer() (string, error) { } return proposerAddr, nil } + +func (c *Client) GetSignerBalance() (types.Balance, error) { + account, err := c.cosmosClient.GetAccount(c.config.DymAccountName) + if err != nil { + return types.Balance{}, fmt.Errorf("obtain account: %w", err) + } + + addr, err := account.Address(addressPrefix) + if err != nil { + return types.Balance{}, fmt.Errorf("derive address: %w", err) + } + + denom := "adym" + + res, err := c.cosmosClient.GetBalance(c.ctx, addr, denom) + if err != nil { + return types.Balance{}, fmt.Errorf("get balance: %w", err) + } + + balance := types.Balance{ + Amount: res.Amount, + Denom: res.Denom, + } + + return balance, nil +} diff --git a/settlement/grpc/grpc.go b/settlement/grpc/grpc.go index 57f7c64df..64fc61de9 100644 --- a/settlement/grpc/grpc.go +++ b/settlement/grpc/grpc.go @@ -12,6 +12,7 @@ import ( "sync/atomic" "time" + "cosmossdk.io/math" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" @@ -395,3 +396,10 @@ func (c *Client) retrieveBatchAtStateIndex(slStateIndex uint64) (*settlement.Res } return &batchResult, nil } + +func (c *Client) GetSignerBalance() (types.Balance, error) { + return types.Balance{ + Amount: math.ZeroInt(), + Denom: "adym", + }, nil +} diff --git a/settlement/local/local.go b/settlement/local/local.go index 49c18b9c3..33c152cf1 100644 --- a/settlement/local/local.go +++ b/settlement/local/local.go @@ -12,6 +12,7 @@ import ( "sync" "time" + "cosmossdk.io/math" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" @@ -344,3 +345,10 @@ func keyFromIndex(ix uint64) []byte { binary.BigEndian.PutUint64(b, ix) return b } + +func (c *Client) GetSignerBalance() (types.Balance, error) { + return types.Balance{ + Amount: math.ZeroInt(), + Denom: "adym", + }, nil +} diff --git a/settlement/settlement.go b/settlement/settlement.go index 9859dafcb..c98957318 100644 --- a/settlement/settlement.go +++ b/settlement/settlement.go @@ -98,4 +98,6 @@ type ClientI interface { GetRollapp() (*types.Rollapp, error) // GetObsoleteDrs returns the list of deprecated DRS. GetObsoleteDrs() ([]uint32, error) + // GetSignerBalance returns the balance of the signer. + GetSignerBalance() (types.Balance, error) } diff --git a/types/balance.go b/types/balance.go new file mode 100644 index 000000000..b31c09ac1 --- /dev/null +++ b/types/balance.go @@ -0,0 +1,8 @@ +package types + +import "cosmossdk.io/math" + +type Balance struct { + Amount math.Int + Denom string +} diff --git a/types/metrics.go b/types/metrics.go index 0b5bb5813..37f79fe46 100644 --- a/types/metrics.go +++ b/types/metrics.go @@ -88,3 +88,13 @@ var RollappConsecutiveFailedDASubmission = promauto.NewGauge(prometheus.GaugeOpt Name: "rollapp_consecutive_failed_da_submissions", Help: "The number of consecutive times the da fails to submit.", }) + +var DaLayerBalanceGauge = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "da_layer_balance", + Help: "The balance of the DA layer.", +}) + +var HubLayerBalanceGauge = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "hub_layer_balance", + Help: "The balance of the hub layer.", +})