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  }