Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GRPC querier init and bank #5953

Closed
wants to merge 52 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
62c2ba1
Initial work on grpc query server example for bank
aaronc Mar 27, 2020
cb469d2
Add grpc gateway http annotations
aaronc Mar 27, 2020
1a745ed
Add RegisterQueryService stub to all modules
aaronc Mar 27, 2020
fe915f5
WIP on tests
aaronc Mar 27, 2020
fd01421
Fix tests
aaronc Mar 28, 2020
1c6b0b3
Update mocks
aaronc Mar 28, 2020
5fb75f4
Address golang CI issues
aaronc Apr 8, 2020
026decb
Address golang CI issues
aaronc Apr 8, 2020
d2c0691
Fix go.mod, regenerate proto files
aaronc Apr 8, 2020
524b4ea
Fix mock tests
aaronc Apr 8, 2020
db2f113
Run goimports
aaronc Apr 8, 2020
840720f
Merge branch 'master' into aaronc/grpc-querier-bank-init
aaronc Apr 8, 2020
e97bdb1
Run goimports
aaronc Apr 8, 2020
480ecb9
Fix buf lint errors
aaronc Apr 8, 2020
e3c127f
Fix test errors and goimports
aaronc Apr 8, 2020
579d184
Update module tests
aaronc Apr 8, 2020
f234330
Fix goimports
aaronc Apr 8, 2020
78e25a3
Merge branch 'master' into aaronc/grpc-querier-bank-init
aaronc Apr 8, 2020
93810b7
Update x/bank/keeper/querier_test.go
aaronc Apr 8, 2020
e0b04fe
Fix goimports
aaronc Apr 8, 2020
75fb560
Merge branch 'aaronc/grpc-querier-bank-init' of github.com:cosmos/cos…
aaronc Apr 8, 2020
5b2442d
Revert baseapp change
aaronc Apr 8, 2020
e8d04b9
Add tests and docs
aaronc Apr 8, 2020
d8910d7
Keep existing bank querier around (for now)
aaronc Apr 8, 2020
f6ce7d1
Merge branch 'master' into aaronc/grpc-querier-bank-init
aaronc Apr 13, 2020
f7821c1
Merge branch 'master' of github.com:cosmos/cosmos-sdk into aaronc/grp…
aaronc Apr 16, 2020
99e3631
Run proto-gen, fix build errors
aaronc Apr 16, 2020
5474bb6
Merge branch 'aaronc/grpc-querier-bank-init' of github.com:cosmos/cos…
aaronc Apr 16, 2020
74ee551
Tidying up + docs
aaronc Apr 16, 2020
fa9aa95
Revert deletion
aaronc Apr 16, 2020
cc03e73
Merge branch 'master' into aaronc/grpc-querier-bank-init
aaronc Apr 16, 2020
1351a68
Add Marshaler param to new cli cmd
aaronc Apr 16, 2020
64c2b99
Revert name change
aaronc Apr 16, 2020
608facd
Add grpc QueryRouter tests
aaronc Apr 16, 2020
2718e03
Fix lint error
aaronc Apr 16, 2020
1cb6c61
Fix tests
aaronc Apr 16, 2020
b905be0
Address review comments
aaronc Apr 17, 2020
6a293e3
Address review comments
aaronc Apr 17, 2020
17c318a
Update baseapp/queryrouter.go
aaronc Apr 17, 2020
6f58904
Update baseapp/queryrouter.go
aaronc Apr 17, 2020
2618e6f
Merge branch 'master' into aaronc/grpc-querier-bank-init
aaronc Apr 17, 2020
64f3b31
Fix build error
aaronc Apr 17, 2020
61cd6aa
Merge branch 'master' of github.com:cosmos/cosmos-sdk into aaronc/grp…
aaronc May 6, 2020
0911f55
fix build errors
aaronc May 6, 2020
29345f3
WIP
aaronc May 6, 2020
3946c8b
Merge branch 'master' of github.com:cosmos/cosmos-sdk into aaronc/grp…
aaronc May 15, 2020
2c14a54
Implement pagination
aaronc May 15, 2020
d94ac18
Docs
aaronc May 15, 2020
7729b52
Fixes, docs
aaronc May 15, 2020
e58a00f
Merge branch 'master' of github.com:cosmos/cosmos-sdk into aaronc/grp…
aaronc Jun 2, 2020
0deb60f
Update x/bank gRPC querier
aaronc Jun 2, 2020
9dbd660
Fixes
aaronc Jun 2, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions baseapp/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ func (app *BaseApp) halt() {
// Query implements the ABCI interface. It delegates to CommitMultiStore if it
// implements Queryable.
func (app *BaseApp) Query(req abci.RequestQuery) abci.ResponseQuery {
if grpcHandler := app.grpcRouter.Route(req.Path); grpcHandler != nil {
return handleQueryGRPC(app, grpcHandler, req)
}

path := splitPath(req.Path)
if len(path) == 0 {
sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "no query path provided"))
Expand All @@ -317,6 +321,51 @@ func (app *BaseApp) Query(req abci.RequestQuery) abci.ResponseQuery {
return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query path"))
}

func handleQueryGRPC(app *BaseApp, handler GRPCHandler, req abci.RequestQuery) abci.ResponseQuery {
// when a client did not provide a query height, manually inject the latest
if req.Height == 0 {
req.Height = app.LastBlockHeight()
}

if req.Height <= 1 && req.Prove {
return sdkerrors.QueryResult(
sdkerrors.Wrap(
sdkerrors.ErrInvalidRequest,
"cannot query with proof when height <= 1; please provide a valid height",
),
)
}

cacheMS, err := app.cms.CacheMultiStoreWithVersion(req.Height)
if err != nil {
return sdkerrors.QueryResult(
sdkerrors.Wrapf(
sdkerrors.ErrInvalidRequest,
"failed to load state at height %d; %s (latest height: %d)", req.Height, err, app.LastBlockHeight(),
),
)
}

// cache wrap the commit-multistore for safety
ctx := sdk.NewContext(
cacheMS, app.checkState.ctx.BlockHeader(), true, app.logger,
).WithMinGasPrices(app.minGasPrices)

res, err := handler(ctx, req)

if err != nil {
space, code, log := sdkerrors.ABCIInfo(err, false)
return abci.ResponseQuery{
Code: code,
Codespace: space,
Log: log,
Height: req.Height,
}
}

return res
}

func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery {
if len(path) >= 2 {
switch path[1] {
Expand Down
7 changes: 7 additions & 0 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"runtime/debug"
"strings"

"github.com/gogo/protobuf/grpc"

abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/libs/log"
Expand Down Expand Up @@ -48,6 +50,7 @@ type BaseApp struct { // nolint: maligned
storeLoader StoreLoader // function to handle store loading, may be overridden with SetStoreLoader()
router sdk.Router // handle any kind of message
queryRouter sdk.QueryRouter // router for redirecting query calls
grpcRouter *GRPCRouter // router for redirecting gRPC query calls
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx

anteHandler sdk.AnteHandler // ante handler for fee and auth
Expand Down Expand Up @@ -108,6 +111,7 @@ func NewBaseApp(
storeLoader: DefaultStoreLoader,
router: NewRouter(),
queryRouter: NewQueryRouter(),
grpcRouter: NewGRPCRouter(),
txDecoder: txDecoder,
fauxMerkleMode: false,
}
Expand Down Expand Up @@ -282,6 +286,9 @@ func (app *BaseApp) Router() sdk.Router {
// QueryRouter returns the QueryRouter of a BaseApp.
func (app *BaseApp) QueryRouter() sdk.QueryRouter { return app.queryRouter }

// GRPCRouter returns the GRPCRouter of a BaseApp.
func (app *BaseApp) GRPCRouter() grpc.Server { return app.grpcRouter }

// Seal seals a BaseApp. It prohibits any further modifications to a BaseApp.
func (app *BaseApp) Seal() { app.sealed = true }

Expand Down
108 changes: 108 additions & 0 deletions baseapp/grpcrouter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package baseapp

import (
gocontext "context"
"fmt"

gogogrpc "github.com/gogo/protobuf/grpc"
abci "github.com/tendermint/tendermint/abci/types"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding"
"google.golang.org/grpc/encoding/proto"

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

var protoCodec = encoding.GetCodec(proto.Name)

type GRPCRouter struct {
routes map[string]GRPCHandler
}

var _ gogogrpc.Server

func NewGRPCRouter() *GRPCRouter {
return &GRPCRouter{
routes: map[string]GRPCHandler{},
}
}

type GRPCHandler = func(ctx sdk.Context, req abci.RequestQuery) (abci.ResponseQuery, error)

// Route returns the Querier for a given query route path.
func (qrt *GRPCRouter) Route(path string) GRPCHandler {
handler, found := qrt.routes[path]
if !found {
return nil
}
return handler
}

// RegisterService implements the grpc Server.RegisterService method
func (qrt *GRPCRouter) RegisterService(sd *grpc.ServiceDesc, handler interface{}) {
// adds a top-level querier based on the GRPC service name
for _, method := range sd.Methods {
fqName := fmt.Sprintf("/%s/%s", sd.ServiceName, method.MethodName)
methodHandler := method.Handler

qrt.routes[fqName] = func(ctx sdk.Context, req abci.RequestQuery) (abci.ResponseQuery, error) {
res, err := methodHandler(handler, sdk.WrapSDKContext(ctx), func(i interface{}) error {
return protoCodec.Unmarshal(req.Data, i)
}, nil)
if err != nil {
return abci.ResponseQuery{}, err
}

resBytes, err := protoCodec.Marshal(res)
if err != nil {
return abci.ResponseQuery{}, err
}

return abci.ResponseQuery{
Height: req.Height,
Value: resBytes,
}, nil
}
}
}

// QueryServiceTestHelper provides a helper for making grpc query service
// rpc calls in unit tests. It implements both the grpc Server and ClientConn
// interfaces needed to register a query service server and create a query
// service client.
type QueryServiceTestHelper struct {
*GRPCRouter
ctx sdk.Context
}

// NewQueryServerTestHelper creates a new QueryServiceTestHelper that wraps
// the provided sdk.Context
func NewQueryServerTestHelper(ctx sdk.Context) *QueryServiceTestHelper {
return &QueryServiceTestHelper{GRPCRouter: NewGRPCRouter(), ctx: ctx}
}

// Invoke implements the grpc ClientConn.Invoke method
func (q *QueryServiceTestHelper) Invoke(_ gocontext.Context, method string, args, reply interface{}, _ ...grpc.CallOption) error {
querier := q.Route(method)
if querier == nil {
return fmt.Errorf("handler not found for %s", method)
}
reqBz, err := protoCodec.Marshal(args)
if err != nil {
return err
}
res, err := querier(q.ctx, abci.RequestQuery{Data: reqBz})

if err != nil {
return err
}
return protoCodec.Unmarshal(res.Value, reply)
}

// NewStream implements the grpc ClientConn.NewStream method
func (q *QueryServiceTestHelper) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
return nil, fmt.Errorf("not supported")
}

var _ gogogrpc.Server = &QueryServiceTestHelper{}
var _ gogogrpc.ClientConn = &QueryServiceTestHelper{}
49 changes: 49 additions & 0 deletions baseapp/grpcrouter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package baseapp

import (
"context"
"fmt"
"testing"

"github.com/stretchr/testify/require"

"github.com/cosmos/cosmos-sdk/codec/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
)

type testServer struct{}

func (e testServer) Echo(_ context.Context, req *testdata.EchoRequest) (*testdata.EchoResponse, error) {
return &testdata.EchoResponse{Message: req.Message}, nil
}

func (e testServer) SayHello(_ context.Context, request *testdata.SayHelloRequest) (*testdata.SayHelloResponse, error) {
greeting := fmt.Sprintf("Hello %s!", request.Name)
return &testdata.SayHelloResponse{Greeting: greeting}, nil
}

var _ testdata.TestServiceServer = testServer{}

func TestGRPCRouter(t *testing.T) {
qr := NewGRPCRouter()
testdata.RegisterTestServiceServer(qr, testServer{})
helper := &QueryServiceTestHelper{
GRPCRouter: qr,
ctx: sdk.Context{},
}
client := testdata.NewTestServiceClient(helper)

res, err := client.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"})
require.Nil(t, err)
require.NotNil(t, res)
require.Equal(t, "hello", res.Message)

require.Panics(t, func() {
_, _ = client.Echo(context.Background(), nil)
})

res2, err := client.SayHello(context.Background(), &testdata.SayHelloRequest{Name: "Foo"})
require.Nil(t, err)
require.NotNil(t, res)
require.Equal(t, "Hello Foo!", res2.Greeting)
}
2 changes: 2 additions & 0 deletions buf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ lint:
- UNARY_RPC
- COMMENT_FIELD
- PACKAGE_DIRECTORY_MATCH
- SERVICE_SUFFIX
ignore:
- third_party
- codec/testdata
Expand All @@ -20,3 +21,4 @@ breaking:
- FILE
ignore:
- third_party
- codec/testdata
38 changes: 38 additions & 0 deletions client/query.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package client

import (
"context"
"fmt"
"strings"

gogogrpc "github.com/gogo/protobuf/grpc"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding"
"google.golang.org/grpc/encoding/proto"

"github.com/pkg/errors"

abci "github.com/tendermint/tendermint/abci/types"
Expand Down Expand Up @@ -233,3 +239,35 @@ func parseQueryStorePath(path string) (storeName string, err error) {

return paths[1], nil
}

// QueryConn returns a new grpc ClientConn for making grpc query calls that
// get routed to the node's ABCI query handler
func (ctx Context) QueryConn() gogogrpc.ClientConn {
return cliQueryConn{ctx}
}

type cliQueryConn struct {
ctx Context
}

var _ gogogrpc.ClientConn = cliQueryConn{}

var protoCodec = encoding.GetCodec(proto.Name)

// Invoke implements the grpc ClientConn.Invoke method
func (c cliQueryConn) Invoke(_ context.Context, method string, args, reply interface{}, _ ...grpc.CallOption) error {
reqBz, err := protoCodec.Marshal(args)
if err != nil {
return err
}
resBz, _, err := c.ctx.QueryWithData(method, reqBz)
if err != nil {
return err
}
return protoCodec.Unmarshal(resBz, reply)
}

// NewStream implements the grpc ClientConn.NewStream method
func (c cliQueryConn) NewStream(context.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
return nil, fmt.Errorf("streaming rpc not supported")
}
Loading