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 }