2020-06-08 06:41:30 -07:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
gocontext "context"
|
|
|
|
"fmt"
|
2020-12-02 09:50:40 -08:00
|
|
|
"reflect"
|
2020-08-12 07:42:10 -07:00
|
|
|
"strconv"
|
2020-06-08 06:41:30 -07:00
|
|
|
|
|
|
|
gogogrpc "github.com/gogo/protobuf/grpc"
|
2020-08-12 07:42:10 -07:00
|
|
|
abci "github.com/tendermint/tendermint/abci/types"
|
2020-06-08 06:41:30 -07:00
|
|
|
"google.golang.org/grpc"
|
|
|
|
"google.golang.org/grpc/encoding"
|
|
|
|
"google.golang.org/grpc/encoding/proto"
|
2020-08-12 07:42:10 -07:00
|
|
|
"google.golang.org/grpc/metadata"
|
2020-07-06 10:03:45 -07:00
|
|
|
|
|
|
|
"github.com/cosmos/cosmos-sdk/codec/types"
|
2020-11-03 10:35:22 -08:00
|
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
2020-12-02 09:50:40 -08:00
|
|
|
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
|
|
|
|
"github.com/cosmos/cosmos-sdk/types/tx"
|
2020-06-08 06:41:30 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
var _ gogogrpc.ClientConn = Context{}
|
|
|
|
|
|
|
|
var protoCodec = encoding.GetCodec(proto.Name)
|
|
|
|
|
|
|
|
// Invoke implements the grpc ClientConn.Invoke method
|
2021-02-15 02:01:44 -08:00
|
|
|
func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err error) {
|
2020-12-02 09:50:40 -08:00
|
|
|
// Two things can happen here:
|
|
|
|
// 1. either we're broadcasting a Tx, in which call we call Tendermint's broadcast endpoint directly,
|
|
|
|
// 2. or we are querying for state, in which case we call ABCI's Query.
|
|
|
|
|
2021-02-15 02:01:44 -08:00
|
|
|
// In both cases, we don't allow empty request req (it will panic unexpectedly).
|
|
|
|
if reflect.ValueOf(req).IsNil() {
|
2020-12-02 09:50:40 -08:00
|
|
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "request cannot be nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Case 1. Broadcasting a Tx.
|
2021-02-15 02:01:44 -08:00
|
|
|
if reqProto, ok := req.(*tx.BroadcastTxRequest); ok {
|
2020-12-02 09:50:40 -08:00
|
|
|
if !ok {
|
2021-02-15 02:01:44 -08:00
|
|
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxRequest)(nil), req)
|
2020-12-02 09:50:40 -08:00
|
|
|
}
|
2021-02-15 02:01:44 -08:00
|
|
|
resProto, ok := reply.(*tx.BroadcastTxResponse)
|
2020-12-02 09:50:40 -08:00
|
|
|
if !ok {
|
2021-02-15 02:01:44 -08:00
|
|
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxResponse)(nil), req)
|
2020-12-02 09:50:40 -08:00
|
|
|
}
|
|
|
|
|
2021-02-15 02:01:44 -08:00
|
|
|
broadcastRes, err := TxServiceBroadcast(grpcCtx, ctx, reqProto)
|
2020-12-02 09:50:40 -08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-15 02:01:44 -08:00
|
|
|
*resProto = *broadcastRes
|
2020-12-02 09:50:40 -08:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Case 2. Querying state.
|
2021-02-15 02:01:44 -08:00
|
|
|
inMd, _ := metadata.FromOutgoingContext(grpcCtx)
|
|
|
|
abciRes, outMd, err := RunGRPCQuery(ctx, grpcCtx, method, req, inMd)
|
2020-06-08 06:41:30 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-25 08:44:13 -07:00
|
|
|
|
2021-02-15 02:01:44 -08:00
|
|
|
err = protoCodec.Unmarshal(abciRes.Value, reply)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, callOpt := range opts {
|
|
|
|
header, ok := callOpt.(grpc.HeaderCallOption)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
*header.HeaderAddr = outMd
|
|
|
|
}
|
|
|
|
|
|
|
|
if ctx.InterfaceRegistry != nil {
|
|
|
|
return types.UnpackInterfaces(reply, ctx.InterfaceRegistry)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewStream implements the grpc ClientConn.NewStream method
|
|
|
|
func (Context) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
|
|
|
|
return nil, fmt.Errorf("streaming rpc not supported")
|
|
|
|
}
|
|
|
|
|
|
|
|
// RunGRPCQuery runs a gRPC query from the clientCtx, given all necessary
|
|
|
|
// arguments for the gRPC method, and returns the ABCI response. It is used
|
|
|
|
// to factorize code between client (Invoke) and server (RegisterGRPCServer)
|
|
|
|
// gRPC handlers.
|
|
|
|
func RunGRPCQuery(ctx Context, grpcCtx gocontext.Context, method string, req interface{}, md metadata.MD) (abci.ResponseQuery, metadata.MD, error) {
|
|
|
|
reqBz, err := protoCodec.Marshal(req)
|
|
|
|
if err != nil {
|
|
|
|
return abci.ResponseQuery{}, nil, err
|
|
|
|
}
|
|
|
|
|
2020-08-25 08:44:13 -07:00
|
|
|
// parse height header
|
|
|
|
if heights := md.Get(grpctypes.GRPCBlockHeightHeader); len(heights) > 0 {
|
|
|
|
height, err := strconv.ParseInt(heights[0], 10, 64)
|
|
|
|
if err != nil {
|
2021-02-15 02:01:44 -08:00
|
|
|
return abci.ResponseQuery{}, nil, err
|
2020-08-25 08:44:13 -07:00
|
|
|
}
|
2020-11-03 10:35:22 -08:00
|
|
|
if height < 0 {
|
2021-02-15 02:01:44 -08:00
|
|
|
return abci.ResponseQuery{}, nil, sdkerrors.Wrapf(
|
2020-11-03 10:35:22 -08:00
|
|
|
sdkerrors.ErrInvalidRequest,
|
|
|
|
"client.Context.Invoke: height (%d) from %q must be >= 0", height, grpctypes.GRPCBlockHeightHeader)
|
|
|
|
}
|
2020-08-25 08:44:13 -07:00
|
|
|
|
|
|
|
ctx = ctx.WithHeight(height)
|
|
|
|
}
|
|
|
|
|
2021-02-15 02:01:44 -08:00
|
|
|
abciReq := abci.RequestQuery{
|
2020-08-12 07:42:10 -07:00
|
|
|
Path: method,
|
|
|
|
Data: reqBz,
|
|
|
|
}
|
|
|
|
|
2021-02-15 02:01:44 -08:00
|
|
|
abciRes, err := ctx.QueryABCI(abciReq)
|
2020-06-08 06:41:30 -07:00
|
|
|
if err != nil {
|
2021-02-15 02:01:44 -08:00
|
|
|
return abci.ResponseQuery{}, nil, err
|
2020-07-03 09:42:12 -07:00
|
|
|
}
|
|
|
|
|
2020-08-12 07:42:10 -07:00
|
|
|
// 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.
|
2021-02-15 02:01:44 -08:00
|
|
|
md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(abciRes.Height, 10))
|
2020-12-02 09:50:40 -08:00
|
|
|
|
2021-02-15 02:01:44 -08:00
|
|
|
return abciRes, md, nil
|
2020-12-02 09:50:40 -08:00
|
|
|
}
|