github.com/Finschia/finschia-sdk@v0.48.1/client/grpc_query.go (about)

     1  package client
     2  
     3  import (
     4  	gocontext "context"
     5  	"fmt"
     6  	"reflect"
     7  	"strconv"
     8  
     9  	gogogrpc "github.com/gogo/protobuf/grpc"
    10  	abci "github.com/tendermint/tendermint/abci/types"
    11  	"google.golang.org/grpc"
    12  	"google.golang.org/grpc/encoding"
    13  	"google.golang.org/grpc/encoding/proto"
    14  	"google.golang.org/grpc/metadata"
    15  
    16  	"github.com/Finschia/finschia-sdk/codec/types"
    17  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    18  	grpctypes "github.com/Finschia/finschia-sdk/types/grpc"
    19  	"github.com/Finschia/finschia-sdk/types/tx"
    20  )
    21  
    22  var _ gogogrpc.ClientConn = Context{}
    23  
    24  var protoCodec = encoding.GetCodec(proto.Name)
    25  
    26  // Invoke implements the grpc ClientConn.Invoke method
    27  func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err error) {
    28  	// Two things can happen here:
    29  	// 1. either we're broadcasting a Tx, in which call we call Tendermint's broadcast endpoint directly,
    30  	// 2. or we are querying for state, in which case we call ABCI's Query.
    31  
    32  	// In both cases, we don't allow empty request args (it will panic unexpectedly).
    33  	if reflect.ValueOf(req).IsNil() {
    34  		return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "request cannot be nil")
    35  	}
    36  
    37  	// Case 1. Broadcasting a Tx.
    38  	if reqProto, ok := req.(*tx.BroadcastTxRequest); ok {
    39  		res, ok := reply.(*tx.BroadcastTxResponse)
    40  		if !ok {
    41  			return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxResponse)(nil), req)
    42  		}
    43  
    44  		broadcastRes, err := TxServiceBroadcast(grpcCtx, ctx, reqProto)
    45  		if err != nil {
    46  			return err
    47  		}
    48  		*res = *broadcastRes
    49  
    50  		return err
    51  	}
    52  
    53  	// Case 2. Querying state.
    54  	reqBz, err := protoCodec.Marshal(req)
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	// parse height header
    60  	md, _ := metadata.FromOutgoingContext(grpcCtx)
    61  	if heights := md.Get(grpctypes.GRPCBlockHeightHeader); len(heights) > 0 {
    62  		height, err := strconv.ParseInt(heights[0], 10, 64)
    63  		if err != nil {
    64  			return err
    65  		}
    66  		if height < 0 {
    67  			return sdkerrors.Wrapf(
    68  				sdkerrors.ErrInvalidRequest,
    69  				"client.Context.Invoke: height (%d) from %q must be >= 0", height, grpctypes.GRPCBlockHeightHeader)
    70  		}
    71  
    72  		ctx = ctx.WithHeight(height)
    73  	}
    74  
    75  	abciReq := abci.RequestQuery{
    76  		Path:   method,
    77  		Data:   reqBz,
    78  		Height: ctx.Height,
    79  	}
    80  
    81  	res, err := ctx.QueryABCI(abciReq)
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	err = protoCodec.Unmarshal(res.Value, reply)
    87  	if err != nil {
    88  		return err
    89  	}
    90  
    91  	// Create header metadata. For now the headers contain:
    92  	// - block height
    93  	// We then parse all the call options, if the call option is a
    94  	// HeaderCallOption, then we manually set the value of that header to the
    95  	// metadata.
    96  	md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(res.Height, 10))
    97  	for _, callOpt := range opts {
    98  		header, ok := callOpt.(grpc.HeaderCallOption)
    99  		if !ok {
   100  			continue
   101  		}
   102  
   103  		*header.HeaderAddr = md
   104  	}
   105  
   106  	if ctx.InterfaceRegistry != nil {
   107  		return types.UnpackInterfaces(reply, ctx.InterfaceRegistry)
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  // NewStream implements the grpc ClientConn.NewStream method
   114  func (Context) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
   115  	return nil, fmt.Errorf("streaming rpc not supported")
   116  }