github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/baseapp/baseapp_ibc_adapter.go (about)

     1  package baseapp
     2  
     3  import (
     4  	"context"
     5  	"strconv"
     6  
     7  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec/types"
     8  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     9  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    10  	grpctypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/grpc"
    11  	abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types"
    12  	gogogrpc "github.com/gogo/protobuf/grpc"
    13  	grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware"
    14  	grpcrecovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
    15  	"google.golang.org/grpc"
    16  	"google.golang.org/grpc/codes"
    17  	"google.golang.org/grpc/metadata"
    18  	"google.golang.org/grpc/status"
    19  	grpcstatus "google.golang.org/grpc/status"
    20  )
    21  
    22  // SetInterfaceRegistry sets the InterfaceRegistry.
    23  func (app *BaseApp) SetInterfaceRegistry(registry types.InterfaceRegistry) {
    24  	app.interfaceRegistry = registry
    25  	app.grpcQueryRouter.SetInterfaceRegistry(registry)
    26  	app.msgServiceRouter.SetInterfaceRegistry(registry)
    27  }
    28  
    29  // MountMemoryStores mounts all in-memory KVStores with the BaseApp's internal
    30  // commit multi-store.
    31  func (app *BaseApp) MountMemoryStores(keys map[string]*sdk.MemoryStoreKey) {
    32  	for _, memKey := range keys {
    33  		app.MountStore(memKey, sdk.StoreTypeMemory)
    34  	}
    35  }
    36  
    37  func (app *BaseApp) handleQueryGRPC(handler GRPCQueryHandler, req abci.RequestQuery) abci.ResponseQuery {
    38  	ctx, err := app.createQueryContext(req.Height, req.Prove)
    39  	if err != nil {
    40  		return sdkerrors.QueryResult(err)
    41  	}
    42  
    43  	res, err := handler(ctx, req)
    44  	if err != nil {
    45  		res = sdkerrors.QueryResult(gRPCErrorToSDKError(err))
    46  		res.Height = req.Height
    47  		return res
    48  	}
    49  
    50  	return res
    51  }
    52  
    53  func (app *BaseApp) createQueryContext(height int64, prove bool) (sdk.Context, error) {
    54  	if err := checkNegativeHeight(height); err != nil {
    55  		return sdk.Context{}, err
    56  	}
    57  
    58  	// when a client did not provide a query height, manually inject the latest
    59  	if height == 0 {
    60  		height = app.LastBlockHeight()
    61  	}
    62  
    63  	if height <= 1 && prove {
    64  		return sdk.Context{},
    65  			sdkerrors.Wrap(
    66  				sdkerrors.ErrInvalidRequest,
    67  				"cannot query with proof when height <= 1; please provide a valid height",
    68  			)
    69  	}
    70  
    71  	cacheMS, err := app.cms.CacheMultiStoreWithVersion(height)
    72  	if err != nil {
    73  		return sdk.Context{},
    74  			sdkerrors.Wrapf(
    75  				sdkerrors.ErrInvalidRequest,
    76  				"failed to load state at height %d; %s (latest height: %d)", height, err, app.LastBlockHeight(),
    77  			)
    78  	}
    79  
    80  	// branch the commit-multistore for safety
    81  	ctx := sdk.NewContext(
    82  		cacheMS, app.checkState.ctx.BlockHeader(), true, app.logger,
    83  	)
    84  	ctx.SetMinGasPrices(app.minGasPrices)
    85  
    86  	return ctx, nil
    87  }
    88  
    89  func checkNegativeHeight(height int64) error {
    90  	if height < 0 {
    91  		// Reject invalid heights.
    92  		return sdkerrors.Wrap(
    93  			sdkerrors.ErrInvalidRequest,
    94  			"cannot query with height < 0; please provide a valid height",
    95  		)
    96  	}
    97  	return nil
    98  }
    99  
   100  func gRPCErrorToSDKError(err error) error {
   101  	status, ok := grpcstatus.FromError(err)
   102  	if !ok {
   103  		return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
   104  	}
   105  
   106  	switch status.Code() {
   107  	case codes.NotFound:
   108  		return sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, err.Error())
   109  	case codes.InvalidArgument:
   110  		return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
   111  	case codes.FailedPrecondition:
   112  		return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
   113  	case codes.Unauthenticated:
   114  		return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, err.Error())
   115  	default:
   116  		return sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, err.Error())
   117  	}
   118  }
   119  
   120  func (app *BaseApp) RegisterGRPCServer(server gogogrpc.Server) {
   121  	// Define an interceptor for all gRPC queries: this interceptor will create
   122  	// a new sdk.Context, and pass it into the query handler.
   123  	interceptor := func(grpcCtx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
   124  		// If there's some metadata in the context, retrieve it.
   125  		md, ok := metadata.FromIncomingContext(grpcCtx)
   126  		if !ok {
   127  			return nil, status.Error(codes.Internal, "unable to retrieve metadata")
   128  		}
   129  
   130  		// Get height header from the request context, if present.
   131  		var height int64
   132  		if heightHeaders := md.Get(grpctypes.GRPCBlockHeightHeader); len(heightHeaders) == 1 {
   133  			height, err = strconv.ParseInt(heightHeaders[0], 10, 64)
   134  			if err != nil {
   135  				return nil, sdkerrors.Wrapf(
   136  					sdkerrors.ErrInvalidRequest,
   137  					"Baseapp.RegisterGRPCServer: invalid height header %q: %v", grpctypes.GRPCBlockHeightHeader, err)
   138  			}
   139  			if err := checkNegativeHeight(height); err != nil {
   140  				return nil, err
   141  			}
   142  		}
   143  
   144  		// Create the sdk.Context. Passing false as 2nd arg, as we can't
   145  		// actually support proofs with gRPC right now.
   146  		sdkCtx, err := app.createQueryContext(height, false)
   147  		if err != nil {
   148  			return nil, err
   149  		}
   150  
   151  		// Add relevant gRPC headers
   152  		if height == 0 {
   153  			height = sdkCtx.BlockHeight() // If height was not set in the request, set it to the latest
   154  		}
   155  
   156  		// Attach the sdk.Context into the gRPC's context.Context.
   157  		grpcCtx = context.WithValue(grpcCtx, sdk.SdkContextKey, sdkCtx)
   158  
   159  		md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(height, 10))
   160  		if err = grpc.SetHeader(grpcCtx, md); err != nil {
   161  			app.logger.Error("failed to set gRPC header", "err", err)
   162  		}
   163  
   164  		return handler(grpcCtx, req)
   165  	}
   166  
   167  	// Loop through all services and methods, add the interceptor, and register
   168  	// the service.
   169  	for _, data := range app.GRPCQueryRouter().serviceData {
   170  		desc := data.serviceDesc
   171  		newMethods := make([]grpc.MethodDesc, len(desc.Methods))
   172  
   173  		for i, method := range desc.Methods {
   174  			methodHandler := method.Handler
   175  			newMethods[i] = grpc.MethodDesc{
   176  				MethodName: method.MethodName,
   177  				Handler: func(srv interface{}, ctx context.Context, dec func(interface{}) error, _ grpc.UnaryServerInterceptor) (interface{}, error) {
   178  					return methodHandler(srv, ctx, dec, grpcmiddleware.ChainUnaryServer(
   179  						grpcrecovery.UnaryServerInterceptor(),
   180  						interceptor,
   181  					))
   182  				},
   183  			}
   184  		}
   185  
   186  		newDesc := &grpc.ServiceDesc{
   187  			ServiceName: desc.ServiceName,
   188  			HandlerType: desc.HandlerType,
   189  			Methods:     newMethods,
   190  			Streams:     desc.Streams,
   191  			Metadata:    desc.Metadata,
   192  		}
   193  
   194  		server.RegisterService(newDesc, data.handler)
   195  	}
   196  }
   197  
   198  // it is like hooker ,grap the request and do sth....(like redirect the path or anything else)
   199  type Interceptor interface {
   200  	Intercept(req *abci.RequestQuery)
   201  }
   202  
   203  var (
   204  	_ Interceptor = (*functionInterceptor)(nil)
   205  )
   206  
   207  type functionInterceptor struct {
   208  	hookF func(req *abci.RequestQuery)
   209  }
   210  
   211  func (f *functionInterceptor) Intercept(req *abci.RequestQuery) {
   212  	f.hookF(req)
   213  }
   214  
   215  func NewRedirectInterceptor(redirectPath string) Interceptor {
   216  	return newFunctionInterceptor(func(req *abci.RequestQuery) {
   217  		req.Path = redirectPath
   218  	})
   219  }
   220  
   221  func newFunctionInterceptor(f func(req *abci.RequestQuery)) *functionInterceptor {
   222  	return &functionInterceptor{hookF: f}
   223  }