Skip to content

Commit

Permalink
gRPC-gateway routes as alternative to legacy tendermint REST endpoints (
Browse files Browse the repository at this point in the history
cosmos#7965)

* WIP

* WIP setup

* WIP setup testsuite

* add node_info

* add GetLatestBlock,GetBlockByHeight grpc endpoints

* add GetValidatorSetByHeight, GetLatestValidatorSet

* fix lint

* fix tests

* proto format

* Update Makefile

* review changes

* Makefile format

* handle nil pagination

* rename query.go to service.go

* format Makefile

* format Makefile

* review changes

* review changes

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
aleem1314 and mergify[bot] authored Nov 25, 2020
1 parent a9dd334 commit c58a892
Show file tree
Hide file tree
Showing 16 changed files with 5,153 additions and 9 deletions.
19 changes: 12 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -382,16 +382,17 @@ proto-lint:
proto-check-breaking:
@$(DOCKER_BUF) check breaking --against-input $(HTTPS_GIT)#branch=master

TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.0-rc6/proto/tendermint
GOGO_PROTO_URL = https://raw.githubusercontent.com/regen-network/protobuf/cosmos
COSMOS_PROTO_URL = https://raw.githubusercontent.com/regen-network/cosmos-proto/master
CONFIO_URL = https://raw.githubusercontent.com/confio/ics23/v0.6.3
TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.0-rc6/proto/tendermint
GOGO_PROTO_URL = https://raw.githubusercontent.com/regen-network/protobuf/cosmos
COSMOS_PROTO_URL = https://raw.githubusercontent.com/regen-network/cosmos-proto/master
CONFIO_URL = https://raw.githubusercontent.com/confio/ics23/v0.6.3

TM_CRYPTO_TYPES = third_party/proto/tendermint/crypto
TM_ABCI_TYPES = third_party/proto/tendermint/abci
TM_TYPES = third_party/proto/tendermint/types
TM_VERSION = third_party/proto/tendermint/version
TM_LIBS = third_party/proto/tendermint/libs/bits
TM_TYPES = third_party/proto/tendermint/types
TM_VERSION = third_party/proto/tendermint/version
TM_LIBS = third_party/proto/tendermint/libs/bits
TM_P2P = third_party/proto/tendermint/p2p

GOGO_PROTO_TYPES = third_party/proto/gogoproto
COSMOS_PROTO_TYPES = third_party/proto/cosmos_proto
Expand Down Expand Up @@ -419,6 +420,7 @@ proto-update-deps:
@curl -sSL $(TM_URL)/types/evidence.proto > $(TM_TYPES)/evidence.proto
@curl -sSL $(TM_URL)/types/params.proto > $(TM_TYPES)/params.proto
@curl -sSL $(TM_URL)/types/validator.proto > $(TM_TYPES)/validator.proto
@curl -sSL $(TM_URL)/types/block.proto > $(TM_TYPES)/block.proto

@mkdir -p $(TM_CRYPTO_TYPES)
@curl -sSL $(TM_URL)/crypto/proof.proto > $(TM_CRYPTO_TYPES)/proof.proto
Expand All @@ -427,6 +429,9 @@ proto-update-deps:
@mkdir -p $(TM_LIBS)
@curl -sSL $(TM_URL)/libs/bits/types.proto > $(TM_LIBS)/types.proto

@mkdir -p $(TM_P2P)
@curl -sSL $(TM_URL)/p2p/types.proto > $(TM_P2P)/types.proto

@mkdir -p $(CONFIO_TYPES)
@curl -sSL $(CONFIO_URL)/proofs.proto > $(CONFIO_TYPES)/proofs.proto
## insert go package option into proofs.proto file
Expand Down
19 changes: 19 additions & 0 deletions client/grpc/tmservice/block.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package tmservice

import (
"context"

ctypes "github.com/tendermint/tendermint/rpc/core/types"

"github.com/cosmos/cosmos-sdk/client"
)

func getBlock(clientCtx client.Context, height *int64) (*ctypes.ResultBlock, error) {
// get the node
node, err := clientCtx.GetNode()
if err != nil {
return nil, err
}

return node.Block(context.Background(), height)
}
206 changes: 206 additions & 0 deletions client/grpc/tmservice/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package tmservice

import (
"context"

gogogrpc "github.com/gogo/protobuf/grpc"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/rpc"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
qtypes "github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/version"
)

// This is the struct that we will implement all the handlers on.
type queryServer struct {
clientCtx client.Context
interfaceRegistry codectypes.InterfaceRegistry
}

var _ qtypes.ServiceServer = queryServer{}

// NewQueryServer creates a new tendermint query server.
func NewQueryServer(clientCtx client.Context, interfaceRegistry codectypes.InterfaceRegistry) qtypes.ServiceServer {
return queryServer{
clientCtx: clientCtx,
interfaceRegistry: interfaceRegistry,
}
}

// GetSyncing implements ServiceServer.GetSyncing
func (s queryServer) GetSyncing(_ context.Context, _ *qtypes.GetSyncingRequest) (*qtypes.GetSyncingResponse, error) {
status, err := getNodeStatus(s.clientCtx)
if err != nil {
return nil, err
}
return &qtypes.GetSyncingResponse{
Syncing: status.SyncInfo.CatchingUp,
}, nil
}

// GetLatestBlock implements ServiceServer.GetLatestBlock
func (s queryServer) GetLatestBlock(context.Context, *qtypes.GetLatestBlockRequest) (*qtypes.GetLatestBlockResponse, error) {
status, err := getBlock(s.clientCtx, nil)
if err != nil {
return nil, err
}

protoBlockID := status.BlockID.ToProto()
protoBlock, err := status.Block.ToProto()
if err != nil {
return nil, err
}

return &qtypes.GetLatestBlockResponse{
BlockId: &protoBlockID,
Block: protoBlock,
}, nil
}

// GetBlockByHeight implements ServiceServer.GetBlockByHeight
func (s queryServer) GetBlockByHeight(_ context.Context, req *qtypes.GetBlockByHeightRequest) (*qtypes.GetBlockByHeightResponse, error) {
chainHeight, err := rpc.GetChainHeight(s.clientCtx)
if err != nil {
return nil, err
}

if req.Height > chainHeight {
return nil, status.Error(codes.InvalidArgument, "requested block height is bigger then the chain length")
}

res, err := getBlock(s.clientCtx, &req.Height)
if err != nil {
return nil, err
}
protoBlockID := res.BlockID.ToProto()
protoBlock, err := res.Block.ToProto()
if err != nil {
return nil, err
}
return &qtypes.GetBlockByHeightResponse{
BlockId: &protoBlockID,
Block: protoBlock,
}, nil
}

// GetLatestValidatorSet implements ServiceServer.GetLatestValidatorSet
func (s queryServer) GetLatestValidatorSet(ctx context.Context, req *qtypes.GetLatestValidatorSetRequest) (*qtypes.GetLatestValidatorSetResponse, error) {
page, limit, err := qtypes.ParsePagination(req.Pagination)
if err != nil {
return nil, err
}

validatorsRes, err := rpc.GetValidators(s.clientCtx, nil, &page, &limit)
if err != nil {
return nil, err
}

outputValidatorsRes := &qtypes.GetLatestValidatorSetResponse{
BlockHeight: validatorsRes.BlockHeight,
Validators: make([]*qtypes.Validator, len(validatorsRes.Validators)),
}

for i, validator := range validatorsRes.Validators {
outputValidatorsRes.Validators[i] = &qtypes.Validator{
Address: validator.Address,
ProposerPriority: validator.ProposerPriority,
PubKey: validator.PubKey,
VotingPower: validator.VotingPower,
}
}
return outputValidatorsRes, nil
}

// GetValidatorSetByHeight implements ServiceServer.GetValidatorSetByHeight
func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *qtypes.GetValidatorSetByHeightRequest) (*qtypes.GetValidatorSetByHeightResponse, error) {
page, limit, err := qtypes.ParsePagination(req.Pagination)
if err != nil {
return nil, err
}

chainHeight, err := rpc.GetChainHeight(s.clientCtx)
if err != nil {
return nil, status.Error(codes.Internal, "failed to parse chain height")
}
if req.Height > chainHeight {
return nil, status.Error(codes.InvalidArgument, "requested block height is bigger then the chain length")
}

validatorsRes, err := rpc.GetValidators(s.clientCtx, &req.Height, &page, &limit)

if err != nil {
return nil, err
}

outputValidatorsRes := &qtypes.GetValidatorSetByHeightResponse{
BlockHeight: validatorsRes.BlockHeight,
Validators: make([]*qtypes.Validator, len(validatorsRes.Validators)),
}

for i, validator := range validatorsRes.Validators {
outputValidatorsRes.Validators[i] = &qtypes.Validator{
Address: validator.Address,
ProposerPriority: validator.ProposerPriority,
PubKey: validator.PubKey,
VotingPower: validator.VotingPower,
}
}
return outputValidatorsRes, nil
}

// GetNodeInfo implements ServiceServer.GetNodeInfo
func (s queryServer) GetNodeInfo(ctx context.Context, req *qtypes.GetNodeInfoRequest) (*qtypes.GetNodeInfoResponse, error) {
status, err := getNodeStatus(s.clientCtx)
if err != nil {
return nil, err
}

protoNodeInfo := status.NodeInfo.ToProto()
nodeInfo := version.NewInfo()

deps := make([]*qtypes.Module, len(nodeInfo.BuildDeps))

for i, dep := range nodeInfo.BuildDeps {
deps[i] = &qtypes.Module{
Path: dep.Path,
Sum: dep.Sum,
Version: dep.Version,
}
}

resp := qtypes.GetNodeInfoResponse{
DefaultNodeInfo: protoNodeInfo,
ApplicationVersion: &qtypes.VersionInfo{
AppName: nodeInfo.AppName,
Name: nodeInfo.Name,
GitCommit: nodeInfo.GitCommit,
GoVersion: nodeInfo.GoVersion,
Version: nodeInfo.Version,
BuildTags: nodeInfo.BuildTags,
BuildDeps: deps,
},
}
return &resp, nil
}

// RegisterTendermintService registers the tendermint queries on the gRPC router.
func RegisterTendermintService(
qrt gogogrpc.Server,
clientCtx client.Context,
interfaceRegistry codectypes.InterfaceRegistry,
) {
qtypes.RegisterServiceServer(
qrt,
NewQueryServer(clientCtx, interfaceRegistry),
)
}

// RegisterGRPCGatewayRoutes mounts the tendermint service's GRPC-gateway routes on the
// given Mux.
func RegisterGRPCGatewayRoutes(clientConn gogogrpc.ClientConn, mux *runtime.ServeMux) {
qtypes.RegisterServiceHandlerClient(context.Background(), mux, qtypes.NewServiceClient(clientConn))
}
Loading

0 comments on commit c58a892

Please sign in to comment.