github.com/cosmos/cosmos-sdk@v0.50.10/client/grpc_query.go (about)

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