Skip to content

Commit

Permalink
feat: use gRPC for queries
Browse files Browse the repository at this point in the history
fixes cosmos#10996

Signed-off-by: Artur Troian <troian.ap@gmail.com>
  • Loading branch information
troian authored and reuvenpo committed Feb 21, 2022
1 parent 27366ad commit 0421644
Show file tree
Hide file tree
Showing 12 changed files with 1,086 additions and 46 deletions.
226 changes: 225 additions & 1 deletion CHANGELOG.md

Large diffs are not rendered by default.

38 changes: 29 additions & 9 deletions client/cmd.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package client

import (
"context"
"fmt"
"strings"

"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/tendermint/tendermint/libs/cli"
"google.golang.org/grpc"
"google.golang.org/grpc/keepalive"

"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
Expand All @@ -21,7 +24,7 @@ const ClientContextKey = sdk.ContextKey("client.context")
// SetCmdClientContextHandler is to be used in a command pre-hook execution to
// read flags that populate a Context and sets that to the command's Context.
func SetCmdClientContextHandler(clientCtx Context, cmd *cobra.Command) (err error) {
clientCtx, err = ReadPersistentCommandFlags(clientCtx, cmd.Flags())
clientCtx, err = ReadPersistentCommandFlags(cmd.Context(), clientCtx, cmd.Flags())
if err != nil {
return err
}
Expand Down Expand Up @@ -87,7 +90,7 @@ func ValidateCmd(cmd *cobra.Command, args []string) error {
// - client.Context field not pre-populated & flag set: uses set flag value
// - client.Context field pre-populated & flag not set: uses pre-populated value
// - client.Context field pre-populated & flag set: uses set flag value
func ReadPersistentCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, error) {
func ReadPersistentCommandFlags(ctx context.Context, clientCtx Context, flagSet *pflag.FlagSet) (Context, error) {
if clientCtx.OutputFormat == "" || flagSet.Changed(cli.OutputFlag) {
output, _ := flagSet.GetString(cli.OutputFlag)
clientCtx = clientCtx.WithOutputFormat(output)
Expand Down Expand Up @@ -133,8 +136,8 @@ func ReadPersistentCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Cont
}
}

rpcURI, _ := flagSet.GetString(flags.FlagNode)
if clientCtx.Client == nil || flagSet.Changed(flags.FlagNode) {
rpcURI, _ := flagSet.GetString(flags.FlagNode)
if rpcURI != "" {
clientCtx = clientCtx.WithNodeURI(rpcURI)

Expand All @@ -147,6 +150,23 @@ func ReadPersistentCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Cont
}
}

if clientCtx.ClientConn == nil || flagSet.Changed(flags.FlagGRPCNode) {
grpcURI, _ := flagSet.GetString(flags.FlagGRPCNode)

if grpcURI != "" {
grpcKeepAlive, _ := flagSet.GetDuration(flags.FlagGRPCKeepAlive)
gcl, err := grpc.DialContext(ctx, grpcURI,
grpc.WithInsecure(),
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: grpcKeepAlive,
}),
)
if err == nil {
clientCtx = clientCtx.WithClientConn(gcl)
}
}
}

return clientCtx, nil
}

Expand All @@ -160,7 +180,7 @@ func ReadPersistentCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Cont
// - client.Context field not pre-populated & flag set: uses set flag value
// - client.Context field pre-populated & flag not set: uses pre-populated value
// - client.Context field pre-populated & flag set: uses set flag value
func readQueryCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, error) {
func readQueryCommandFlags(ctx context.Context, clientCtx Context, flagSet *pflag.FlagSet) (Context, error) {
if clientCtx.Height == 0 || flagSet.Changed(flags.FlagHeight) {
height, _ := flagSet.GetInt64(flags.FlagHeight)
clientCtx = clientCtx.WithHeight(height)
Expand All @@ -171,7 +191,7 @@ func readQueryCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context,
clientCtx = clientCtx.WithUseLedger(useLedger)
}

return ReadPersistentCommandFlags(clientCtx, flagSet)
return ReadPersistentCommandFlags(ctx, clientCtx, flagSet)
}

// readTxCommandFlags returns an updated Context with fields set based on flags
Expand All @@ -184,8 +204,8 @@ func readQueryCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context,
// - client.Context field not pre-populated & flag set: uses set flag value
// - client.Context field pre-populated & flag not set: uses pre-populated value
// - client.Context field pre-populated & flag set: uses set flag value
func readTxCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, error) {
clientCtx, err := ReadPersistentCommandFlags(clientCtx, flagSet)
func readTxCommandFlags(ctx context.Context, clientCtx Context, flagSet *pflag.FlagSet) (Context, error) {
clientCtx, err := ReadPersistentCommandFlags(ctx, clientCtx, flagSet)
if err != nil {
return clientCtx, err
}
Expand Down Expand Up @@ -263,7 +283,7 @@ func readTxCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, err
// - client.Context field pre-populated & flag set: uses set flag value
func GetClientQueryContext(cmd *cobra.Command) (Context, error) {
ctx := GetClientContextFromCmd(cmd)
return readQueryCommandFlags(ctx, cmd.Flags())
return readQueryCommandFlags(cmd.Context(), ctx, cmd.Flags())
}

// GetClientTxContext returns a Context from a command with fields set based on flags
Expand All @@ -275,7 +295,7 @@ func GetClientQueryContext(cmd *cobra.Command) (Context, error) {
// - client.Context field pre-populated & flag set: uses set flag value
func GetClientTxContext(cmd *cobra.Command) (Context, error) {
ctx := GetClientContextFromCmd(cmd)
return readTxCommandFlags(ctx, cmd.Flags())
return readTxCommandFlags(cmd.Context(), ctx, cmd.Flags())
}

// GetClientContextFromCmd returns a Context from a command or an empty Context
Expand Down
17 changes: 12 additions & 5 deletions client/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/config"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
"github.com/cosmos/cosmos-sdk/x/staking/client/cli"
)

Expand All @@ -25,9 +27,12 @@ const (
// initClientContext initiates client Context for tests
func initClientContext(t *testing.T, envVar string) (client.Context, func()) {
home := t.TempDir()
registry := testdata.NewTestInterfaceRegistry()

clientCtx := client.Context{}.
WithHomeDir(home).
WithViper("")
WithViper("").
WithCodec(codec.NewProtoCodec(registry))

clientCtx.Viper.BindEnv(nodeEnv)
if envVar != "" {
Expand All @@ -53,7 +58,7 @@ func TestConfigCmd(t *testing.T) {
_, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
require.NoError(t, err)

//./build/simd config node //http://localhost:1
// ./build/simd config node //http://localhost:1
b := bytes.NewBufferString("")
cmd.SetOut(b)
cmd.SetArgs([]string{"node"})
Expand All @@ -68,16 +73,18 @@ func TestConfigCmdEnvFlag(t *testing.T) {
defaultNode = "http://localhost:26657"
)

nogrpc := fmt.Sprintf("--%s=", flags.FlagGRPCNode)

tt := []struct {
name string
envVar string
args []string
expNode string
}{
{"env var is set with no flag", testNode1, []string{"validators"}, testNode1},
{"env var is set with a flag", testNode1, []string{"validators", fmt.Sprintf("--%s=%s", flags.FlagNode, testNode2)}, testNode2},
{"env var is not set with no flag", "", []string{"validators"}, defaultNode},
{"env var is not set with a flag", "", []string{"validators", fmt.Sprintf("--%s=%s", flags.FlagNode, testNode2)}, testNode2},
{"env var is set with a flag", testNode1, []string{"validators", nogrpc, fmt.Sprintf("--%s=%s", flags.FlagNode, testNode2)}, testNode2},
{"env var is not set with no flag", "", []string{"validators", nogrpc}, defaultNode},
{"env var is not set with a flag", "", []string{"validators", nogrpc, fmt.Sprintf("--%s=%s", flags.FlagNode, testNode2)}, testNode2},
}

for _, tc := range tt {
Expand Down
10 changes: 10 additions & 0 deletions client/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"

"github.com/spf13/viper"
"google.golang.org/grpc"

"gopkg.in/yaml.v2"

Expand Down Expand Up @@ -52,6 +53,7 @@ type Context struct {
NodeURI string
FeeGranter sdk.AccAddress
Viper *viper.Viper
ClientConn grpc.ClientConnInterface

// TODO: Deprecated (remove).
LegacyAmino *codec.LegacyAmino
Expand Down Expand Up @@ -140,6 +142,14 @@ func (ctx Context) WithClient(client rpcclient.Client) Context {
return ctx
}

// WithClientConn returns a copy of the context with an updated gRPC client
// instance. This is optional, if not set, then the client.Context will default
// to using Tendermint's RPC `abci_query` for making queries.
func (ctx Context) WithClientConn(rpc grpc.ClientConnInterface) Context {
ctx.ClientConn = rpc
return ctx
}

// WithUseLedger returns a copy of the context with an updated UseLedger flag.
func (ctx Context) WithUseLedger(useLedger bool) Context {
ctx.UseLedger = useLedger
Expand Down
7 changes: 7 additions & 0 deletions client/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package flags
import (
"fmt"
"strconv"
"time"

"github.com/spf13/cobra"
tmcli "github.com/tendermint/tendermint/libs/cli"
Expand Down Expand Up @@ -44,6 +45,8 @@ const (
FlagUseLedger = "ledger"
FlagChainID = "chain-id"
FlagNode = "node"
FlagGRPCNode = "grpc-node"
FlagGRPCKeepAlive = "grpc-keepalive"
FlagHeight = "height"
FlagGasAdjustment = "gas-adjustment"
FlagFrom = "from"
Expand Down Expand Up @@ -85,6 +88,8 @@ var LineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}}
// AddQueryFlagsToCmd adds common flags to a module query command.
func AddQueryFlagsToCmd(cmd *cobra.Command) {
cmd.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to Tendermint RPC interface for this chain")
cmd.Flags().String(FlagGRPCNode, "", "<host>:<port> to GRPC interface for this chain")
cmd.Flags().Duration(FlagGRPCKeepAlive, 20*time.Second, "set GRPC keepalive. defaults to 20s")
cmd.Flags().Int64(FlagHeight, 0, "Use a specific height to query state at (this can error if the node is pruning state)")
cmd.Flags().StringP(tmcli.OutputFlag, "o", "text", "Output format (text|json)")

Expand All @@ -102,6 +107,8 @@ func AddTxFlagsToCmd(cmd *cobra.Command) {
cmd.Flags().String(FlagFees, "", "Fees to pay along with transaction; eg: 1.0uscrt")
cmd.Flags().String(FlagGasPrices, "0.25uscrt", "Gas prices in decimal format to determine the transaction fee (e.g. 0.1uscrt)")
cmd.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
cmd.Flags().String(FlagGRPCNode, "", "<host>:<port> to GRPC interface for this chain")
cmd.Flags().Duration(FlagGRPCKeepAlive, 20*time.Second, "set GRPC keepalive. defaults to 20s")
cmd.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
cmd.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ")
cmd.Flags().StringP(FlagBroadcastMode, "b", BroadcastSync, "Transaction broadcasting mode (sync|async|block)")
Expand Down
68 changes: 40 additions & 28 deletions client/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,6 @@ func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply i
}

// Case 2. Querying state.
reqBz, err := protoCodec.Marshal(req)
if err != nil {
return err
}

// parse height header
md, _ := metadata.FromOutgoingContext(grpcCtx)
Expand All @@ -72,35 +68,51 @@ func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply i
ctx = ctx.WithHeight(height)
}

abciReq := abci.RequestQuery{
Path: method,
Data: reqBz,
Height: ctx.Height,
}
if ctx.ClientConn != nil {
if ctx.Height > 0 {
grpcCtx = metadata.AppendToOutgoingContext(grpcCtx, grpctypes.GRPCBlockHeightHeader, strconv.FormatUint(uint64(ctx.Height), 10))
}

res, err := ctx.QueryABCI(abciReq)
if err != nil {
return err
}
err = ctx.ClientConn.Invoke(grpcCtx, method, req, reply, opts...)
if err != nil {
return err
}
} else {
reqBz, err := protoCodec.Marshal(req)
if err != nil {
return err
}

err = protoCodec.Unmarshal(res.Value, reply)
if err != nil {
return err
}
abciReq := abci.RequestQuery{
Path: method,
Data: reqBz,
Height: ctx.Height,
}

// Create header metadata. For now the headers contain:
// - block height
// We then parse all the call options, if the call option is a
// HeaderCallOption, then we manually set the value of that header to the
// metadata.
md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(res.Height, 10))
for _, callOpt := range opts {
header, ok := callOpt.(grpc.HeaderCallOption)
if !ok {
continue
res, err := ctx.QueryABCI(abciReq)
if err != nil {
return err
}

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

// Create header metadata. For now the headers contain:
// - block height
// We then parse all the call options, if the call option is a
// HeaderCallOption, then we manually set the value of that header to the
// metadata.
md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(res.Height, 10))
for _, callOpt := range opts {
header, ok := callOpt.(grpc.HeaderCallOption)
if !ok {
continue
}

*header.HeaderAddr = md
}
}

if ctx.InterfaceRegistry != nil {
Expand Down
1 change: 1 addition & 0 deletions client/grpc_query_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build norace
// +build norace

package client_test
Expand Down
2 changes: 1 addition & 1 deletion contrib/rosetta/configuration/send_funds.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

set -e
addr=$(simd keys show fd -a --keyring-backend=test)
echo "12345678" | simd tx bank send "$addr" "$1" 100stake --chain-id="testing" --node tcp://cosmos:26657 --yes --keyring-backend=test
echo "12345678" | simd tx bank send "$addr" "$1" 100stake --chain-id="testing" --node tcp://cosmos:26657 --yes --keyring-backend=test
2 changes: 1 addition & 1 deletion contrib/rosetta/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ services:
working_dir: /rosetta
command: ["python3", "faucet.py"]
expose:
- 8080
- 8000

test_rosetta:
image: tendermintdev/rosetta-cli:v0.6.7
Expand Down
2 changes: 1 addition & 1 deletion simapp/simd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) {
cmd.SetOut(cmd.OutOrStdout())
cmd.SetErr(cmd.ErrOrStderr())

initClientCtx, err := client.ReadPersistentCommandFlags(initClientCtx, cmd.Flags())
initClientCtx, err := client.ReadPersistentCommandFlags(cmd.Context(), initClientCtx, cmd.Flags())
if err != nil {
return err
}
Expand Down
9 changes: 9 additions & 0 deletions testutil/network/util.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package network

import (
"context"
"encoding/json"
"path/filepath"
"time"
Expand All @@ -13,6 +14,7 @@ import (
"github.com/tendermint/tendermint/rpc/client/local"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
"google.golang.org/grpc"

"github.com/cosmos/cosmos-sdk/server/api"
servergrpc "github.com/cosmos/cosmos-sdk/server/grpc"
Expand Down Expand Up @@ -111,6 +113,13 @@ func startInProcess(cfg Config, val *Validator) error {
return err
}
}

gcl, err := grpc.DialContext(context.Background(), val.AppConfig.GRPC.Address, grpc.WithInsecure(), grpc.WithReturnConnectionError())
if err != nil {
return err
}

val.ClientCtx = val.ClientCtx.WithClientConn(gcl)
}

return nil
Expand Down
Loading

0 comments on commit 0421644

Please sign in to comment.