github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/client/context/grpc_query.go (about) 1 package context 2 3 import ( 4 gocontext "context" 5 "fmt" 6 "reflect" 7 "strconv" 8 9 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec/types" 10 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 11 grpctypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/grpc" 12 abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types" 13 "golang.org/x/net/context" 14 "google.golang.org/grpc/codes" 15 "google.golang.org/grpc/status" 16 17 gogogrpc "github.com/gogo/protobuf/grpc" 18 "google.golang.org/grpc" 19 "google.golang.org/grpc/encoding" 20 "google.golang.org/grpc/encoding/proto" 21 "google.golang.org/grpc/metadata" 22 ) 23 24 var _ gogogrpc.ClientConn = CLIContext{} 25 26 var protoCodec = encoding.GetCodec(proto.Name) 27 28 // Invoke implements the grpc ClientConn.Invoke method 29 func (ctx CLIContext) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err error) { 30 // Two things can happen here: 31 // 1. either we're broadcasting a Tx, in which call we call Tendermint's broadcast endpoint directly, 32 // 2. or we are querying for state, in which case we call ABCI's Query. 33 34 // In both cases, we don't allow empty request args (it will panic unexpectedly). 35 if reflect.ValueOf(req).IsNil() { 36 return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "request cannot be nil") 37 } 38 39 // Case 1. Broadcasting a Tx. 40 if reqProto, ok := req.(TxRequest); ok { 41 broadcastRes, err := TxServiceBroadcast(grpcCtx, ctx, reqProto) 42 if err != nil { 43 return err 44 } 45 if resp, ok := reply.(TxResponse); ok { 46 reply = resp.HandleResponse(ctx.CodecProy, broadcastRes) 47 } else { 48 reply = broadcastRes 49 } 50 51 return err 52 } 53 54 // Case 2. Querying state. 55 reqBz, err := protoCodec.Marshal(req) 56 if err != nil { 57 return err 58 } 59 60 // parse height header 61 md, _ := metadata.FromOutgoingContext(grpcCtx) 62 if heights := md.Get(grpctypes.GRPCBlockHeightHeader); len(heights) > 0 { 63 height, err := strconv.ParseInt(heights[0], 10, 64) 64 if err != nil { 65 return err 66 } 67 if height < 0 { 68 return sdkerrors.Wrapf( 69 sdkerrors.ErrInvalidRequest, 70 "client.Context.Invoke: height (%d) from %q must be >= 0", height, grpctypes.GRPCBlockHeightHeader) 71 } 72 73 ctx = ctx.WithHeight(height) 74 } 75 76 abciReq := abci.RequestQuery{ 77 Path: method, 78 Data: reqBz, 79 Height: ctx.Height, 80 } 81 82 res, err := ctx.QueryABCI(abciReq) 83 if err != nil { 84 return err 85 } 86 87 err = protoCodec.Unmarshal(res.Value, reply) 88 if err != nil { 89 return err 90 } 91 92 // Create header metadata. For now the headers contain: 93 // - block height 94 // We then parse all the call options, if the call option is a 95 // HeaderCallOption, then we manually set the value of that header to the 96 // metadata. 97 md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(res.Height, 10)) 98 for _, callOpt := range opts { 99 header, ok := callOpt.(grpc.HeaderCallOption) 100 if !ok { 101 continue 102 } 103 104 *header.HeaderAddr = md 105 } 106 107 if ctx.InterfaceRegistry != nil { 108 return types.UnpackInterfaces(reply, ctx.InterfaceRegistry) 109 } 110 111 return nil 112 } 113 114 // NewStream implements the grpc ClientConn.NewStream method 115 func (CLIContext) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { 116 return nil, fmt.Errorf("streaming rpc not supported") 117 } 118 119 func TxServiceBroadcast(grpcCtx context.Context, clientCtx CLIContext, req TxRequest) (interface{}, error) { 120 if req == nil || req.GetData() == nil { 121 return nil, status.Error(codes.InvalidArgument, "invalid empty tx") 122 } 123 124 clientCtx = clientCtx.WithBroadcastMode(normalizeBroadcastMode(req.GetModeDetail())) 125 ret, err := clientCtx.BroadcastTx(req.GetData()) 126 if err != nil { 127 return nil, err 128 } 129 130 return ret, nil 131 } 132 133 // normalizeBroadcastMode converts a broadcast mode into a normalized string 134 // to be passed into the clientCtx. 135 func normalizeBroadcastMode(mode int32) string { 136 switch mode { 137 case 3: 138 return "async" 139 case 1: 140 return "block" 141 case 2: 142 return "sync" 143 default: 144 return "unspecified" 145 } 146 }