Skip to content

Commit

Permalink
Kava gRPC Client (#1784)
Browse files Browse the repository at this point in the history
* add grpc client

* add e2e tests for grpc client

* add grpc client readme

* doc update

* fix more doc issues

* remove util namespace & move grpc client to chain

* rename GrpcClient to Grpc

* add 3rd party query clients

* fix invalid url in readme

* update e2e tests to use grpc client (#1787)
  • Loading branch information
DracoLi committed Dec 13, 2023
1 parent c63fab1 commit ffd306e
Show file tree
Hide file tree
Showing 26 changed files with 552 additions and 117 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
- (community) [#1704] Add module params
- (community) [#1706] Add disable inflation upgrade
- (community) [#1745] Enable params update via governance with `MsgUpdateParams`
- (client) [#1784] Add Kava gRPC client

### Bug Fixes

Expand Down Expand Up @@ -72,6 +73,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
## [v0.24.1]

### Features

- (metrics) [#1668] Adds non-state breaking x/metrics module for custom telemetry.
- (metrics) [#1669] Add performance timing metrics to all Begin/EndBlockers
- (community) [#1751] Add `AnnualizedRewards` query endpoint
Expand Down Expand Up @@ -315,6 +317,7 @@ the [changelog](https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/CHANGELOG.md).
- [#257](https://github.com/Kava-Labs/kava/pulls/257) Include scripts to run
large-scale simulations remotely using aws-batch

[#1784]: https://github.com/Kava-Labs/kava/pull/1784
[#1776]: https://github.com/Kava-Labs/kava/pull/1776
[#1770]: https://github.com/Kava-Labs/kava/pull/1770
[#1755]: https://github.com/Kava-Labs/kava/pull/1755
Expand Down
74 changes: 74 additions & 0 deletions client/grpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Kava gRPC Client

The Kava gRPC client is a tool for making gRPC queries on a Kava chain.

## Features

- Easy-to-use gRPC client for the Kava chain.
- Access all query clients for Cosmos and Kava modules using `client.Query` (e.g., `client.Query.Bank.Balance`).
- Utilize utility functions for common queries (e.g., `client.BaseAccount(str)`).

## Usage

### Creating a new client

```go
package main

import (
kavaGrpc "github.com/kava-labs/kava/client/grpc"
)
grpcUrl := "https://grpc.kava.io:443"
client, err := kavaGrpc.NewClient(grpcUrl)
if err != nil {
panic(err)
}
```

### Making grpc queries

Query clients for both Cosmos and Kava modules are available via `client.Query`.

Example: Query Cosmos module `x/bank` for address balance

```go
import (
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)

rsp, err := client.Query.Bank.Balance(context.Background(), &banktypes.QueryBalanceRequest{
Address: "kava19rjk5qmmwywnzfccwzyn02jywgpwjqf60afj92",
Denom: "ukava",
})
```

Example: Query Kava module `x/evmutil` for params

```go
import (
evmutiltypes "github.com/kava-labs/kava/x/evmutil/types"
)

rsp, err := client.Query.Evmutil.Params(
context.Background(), &evmutiltypes.QueryParamsRequest{},
)
```

#### Query Utilities

Utility functions for common queries are available directly on the client.

Example: Util query to get a base account

```go
kavaAcc := "kava19rjk5qmmwywnzfccwzyn02jywgpwjqf60afj92"
rsp, err := client.BaseAccount(kavaAcc)
if err != nil {
panic(err)
}
fmt.Printf("account sequence for %s: %d\n", kavaAcc, rsp.Sequence)
```

## Query Tests

To test queries, a Kava node is required. Therefore, the e2e tests for the gRPC client queries can be found in the `tests/e2e` directory. Tests for new utility queries should be added as e2e tests under the `test/e2e` directory.
50 changes: 50 additions & 0 deletions client/grpc/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package grpc

import (
"errors"

"github.com/kava-labs/kava/client/grpc/query"
"github.com/kava-labs/kava/client/grpc/util"
)

// KavaGrpcClient enables the usage of kava grpc query clients and query utils
type KavaGrpcClient struct {
config KavaGrpcClientConfig

// Query clients for cosmos and kava modules
Query *query.QueryClient

// Utils for common queries (ie fetch an unpacked BaseAccount)
*util.Util
}

// KavaGrpcClientConfig is a configuration struct for a KavaGrpcClient
type KavaGrpcClientConfig struct {
// note: add future config options here
}

// NewClient creates a new KavaGrpcClient via a grpc url
func NewClient(grpcUrl string) (*KavaGrpcClient, error) {
return NewClientWithConfig(grpcUrl, NewDefaultConfig())
}

// NewClientWithConfig creates a new KavaGrpcClient via a grpc url and config
func NewClientWithConfig(grpcUrl string, config KavaGrpcClientConfig) (*KavaGrpcClient, error) {
if grpcUrl == "" {
return nil, errors.New("grpc url cannot be empty")
}
query, error := query.NewQueryClient(grpcUrl)
if error != nil {
return nil, error
}
client := &KavaGrpcClient{
Query: query,
Util: util.NewUtil(query),
config: config,
}
return client, nil
}

func NewDefaultConfig() KavaGrpcClientConfig {
return KavaGrpcClientConfig{}
}
15 changes: 15 additions & 0 deletions client/grpc/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package grpc_test

import (
"testing"

"github.com/kava-labs/kava/client/grpc"
"github.com/stretchr/testify/require"
)

func TestNewClient_InvalidEndpoint(t *testing.T) {
_, err := grpc.NewClient("invalid-url")
require.ErrorContains(t, err, "unknown grpc url scheme")
_, err = grpc.NewClient("")
require.ErrorContains(t, err, "grpc url cannot be empty")
}
19 changes: 5 additions & 14 deletions tests/util/grpc.go → client/grpc/query/connection.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
package util
package query

import (
"context"
"crypto/tls"
"fmt"
"net/url"
"strconv"

"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"

grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
)

// NewGrpcConnection parses a GRPC endpoint and creates a connection to it
func NewGrpcConnection(endpoint string) (*grpc.ClientConn, error) {
// newGrpcConnection parses a GRPC endpoint and creates a connection to it
func newGrpcConnection(ctx context.Context, endpoint string) (*grpc.ClientConn, error) {
grpcUrl, err := url.Parse(endpoint)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to parse grpc connection \"%s\": %v", endpoint, err)
}

var creds credentials.TransportCredentials
Expand All @@ -33,15 +29,10 @@ func NewGrpcConnection(endpoint string) (*grpc.ClientConn, error) {
}

secureOpt := grpc.WithTransportCredentials(creds)
grpcConn, err := grpc.Dial(grpcUrl.Host, secureOpt)
grpcConn, err := grpc.DialContext(ctx, grpcUrl.Host, secureOpt)
if err != nil {
return nil, err
}

return grpcConn, nil
}

func CtxAtHeight(height int64) context.Context {
heightStr := strconv.FormatInt(height, 10)
return metadata.AppendToOutgoingContext(context.Background(), grpctypes.GRPCBlockHeightHeader, heightStr)
}
7 changes: 7 additions & 0 deletions client/grpc/query/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
The query package includes Cosmos and Kava gRPC query clients.
To ensure that the `QueryClient` stays updated, add new module query clients
to the `QueryClient` whenever new modules with grpc queries are added to the Kava app.
*/
package query
132 changes: 132 additions & 0 deletions client/grpc/query/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package query

import (
"context"

"github.com/cosmos/cosmos-sdk/client/grpc/tmservice"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
authz "github.com/cosmos/cosmos-sdk/x/authz"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
govv1types "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
govv1beta1types "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
paramstypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal"
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"

ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
ibcclienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"

auctiontypes "github.com/kava-labs/kava/x/auction/types"
bep3types "github.com/kava-labs/kava/x/bep3/types"
cdptypes "github.com/kava-labs/kava/x/cdp/types"
committeetypes "github.com/kava-labs/kava/x/committee/types"
communitytypes "github.com/kava-labs/kava/x/community/types"
earntypes "github.com/kava-labs/kava/x/earn/types"
evmutiltypes "github.com/kava-labs/kava/x/evmutil/types"
hardtypes "github.com/kava-labs/kava/x/hard/types"
incentivetypes "github.com/kava-labs/kava/x/incentive/types"
issuancetypes "github.com/kava-labs/kava/x/issuance/types"
kavadisttypes "github.com/kava-labs/kava/x/kavadist/types"
liquidtypes "github.com/kava-labs/kava/x/liquid/types"
pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types"
savingstypes "github.com/kava-labs/kava/x/savings/types"
swaptypes "github.com/kava-labs/kava/x/swap/types"
)

// QueryClient is a wrapper with all Cosmos and Kava grpc query clients
type QueryClient struct {
// cosmos-sdk query clients

Tm tmservice.ServiceClient
Tx txtypes.ServiceClient
Auth authtypes.QueryClient
Authz authz.QueryClient
Bank banktypes.QueryClient
Distribution disttypes.QueryClient
Evidence evidencetypes.QueryClient
Gov govv1types.QueryClient
GovBeta govv1beta1types.QueryClient
Mint minttypes.QueryClient
Params paramstypes.QueryClient
Slashing slashingtypes.QueryClient
Staking stakingtypes.QueryClient
Upgrade upgradetypes.QueryClient

// 3rd party query clients

Evm evmtypes.QueryClient
Feemarket feemarkettypes.QueryClient
IbcClient ibcclienttypes.QueryClient
IbcTransfer ibctransfertypes.QueryClient

// kava module query clients

Auction auctiontypes.QueryClient
Bep3 bep3types.QueryClient
Cdp cdptypes.QueryClient
Committee committeetypes.QueryClient
Community communitytypes.QueryClient
Earn earntypes.QueryClient
Evmutil evmutiltypes.QueryClient
Hard hardtypes.QueryClient
Incentive incentivetypes.QueryClient
Issuance issuancetypes.QueryClient
Kavadist kavadisttypes.QueryClient
Liquid liquidtypes.QueryClient
Pricefeed pricefeedtypes.QueryClient
Savings savingstypes.QueryClient
Swap swaptypes.QueryClient
}

// NewQueryClient creates a new QueryClient and initializes all the module query clients
func NewQueryClient(grpcEndpoint string) (*QueryClient, error) {
conn, err := newGrpcConnection(context.Background(), grpcEndpoint)
if err != nil {
return &QueryClient{}, err
}
client := &QueryClient{
Tm: tmservice.NewServiceClient(conn),
Tx: txtypes.NewServiceClient(conn),
Auth: authtypes.NewQueryClient(conn),
Authz: authz.NewQueryClient(conn),
Bank: banktypes.NewQueryClient(conn),
Distribution: disttypes.NewQueryClient(conn),
Evidence: evidencetypes.NewQueryClient(conn),
Gov: govv1types.NewQueryClient(conn),
GovBeta: govv1beta1types.NewQueryClient(conn),
Mint: minttypes.NewQueryClient(conn),
Params: paramstypes.NewQueryClient(conn),
Slashing: slashingtypes.NewQueryClient(conn),
Staking: stakingtypes.NewQueryClient(conn),
Upgrade: upgradetypes.NewQueryClient(conn),

Evm: evmtypes.NewQueryClient(conn),
Feemarket: feemarkettypes.NewQueryClient(conn),
IbcClient: ibcclienttypes.NewQueryClient(conn),
IbcTransfer: ibctransfertypes.NewQueryClient(conn),

Auction: auctiontypes.NewQueryClient(conn),
Bep3: bep3types.NewQueryClient(conn),
Cdp: cdptypes.NewQueryClient(conn),
Committee: committeetypes.NewQueryClient(conn),
Community: communitytypes.NewQueryClient(conn),
Earn: earntypes.NewQueryClient(conn),
Evmutil: evmutiltypes.NewQueryClient(conn),
Hard: hardtypes.NewQueryClient(conn),
Incentive: incentivetypes.NewQueryClient(conn),
Issuance: issuancetypes.NewQueryClient(conn),
Kavadist: kavadisttypes.NewQueryClient(conn),
Liquid: liquidtypes.NewQueryClient(conn),
Pricefeed: pricefeedtypes.NewQueryClient(conn),
Savings: savingstypes.NewQueryClient(conn),
Swap: swaptypes.NewQueryClient(conn),
}
return client, nil
}
Loading

0 comments on commit ffd306e

Please sign in to comment.