diff --git a/CHANGELOG.md b/CHANGELOG.md index 7288975cebf4..ce85e98b7fd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -102,6 +102,7 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i * (baseapp) [#20208](https://github.com/cosmos/cosmos-sdk/pull/20208) Skip running validateBasic for rechecking txs. * (baseapp) [#20380](https://github.com/cosmos/cosmos-sdk/pull/20380) Enhanced OfferSnapshot documentation. * (client) [#20771](https://github.com/cosmos/cosmos-sdk/pull/20771) Remove `ReadDefaultValuesFromDefaultClientConfig` from `client` package. (It was introduced in `v0.50.6` as a quick fix). +* (grpcserver) [#20945](https://github.com/cosmos/cosmos-sdk/pull/20945) Adds error handling for out-of-gas panics in grpc query handlers. ### Bug Fixes diff --git a/baseapp/grpcserver.go b/baseapp/grpcserver.go index b1befabe9e86..c162bebf1ee3 100644 --- a/baseapp/grpcserver.go +++ b/baseapp/grpcserver.go @@ -14,6 +14,7 @@ import ( "google.golang.org/grpc/status" errorsmod "cosmossdk.io/errors" + storetypes "cosmossdk.io/store/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -67,6 +68,18 @@ func (app *BaseApp) RegisterGRPCServer(server gogogrpc.Server) { app.logger.Debug("gRPC query received of type: " + fmt.Sprintf("%#v", req)) + // Catch an OutOfGasPanic caused in the query handlers + defer func() { + if r := recover(); r != nil { + switch rType := r.(type) { + case storetypes.ErrorOutOfGas: + err = errorsmod.Wrapf(sdkerrors.ErrOutOfGas, "Query gas limit exceeded: %v, out of gas in location: %v", sdkCtx.GasMeter().Limit(), rType.Descriptor) + default: + panic(r) + } + } + }() + return handler(grpcCtx, req) } diff --git a/runtime/v2/app.go b/runtime/v2/app.go index 8df0bdb7dedf..014849c497b0 100644 --- a/runtime/v2/app.go +++ b/runtime/v2/app.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" + gogoproto "github.com/cosmos/gogoproto/proto" "golang.org/x/exp/slices" runtimev2 "cosmossdk.io/api/cosmos/app/runtime/v2" @@ -44,6 +45,10 @@ type App[T transaction.Tx] struct { interfaceRegistrar registry.InterfaceRegistrar amino legacy.Amino moduleManager *MM[T] + + // GRPCQueryDecoders maps gRPC method name to a function that decodes the request + // bytes into a gogoproto.Message, which then can be passed to appmanager. + GRPCQueryDecoders map[string]func(requestBytes []byte) (gogoproto.Message, error) } // Logger returns the app logger. @@ -109,3 +114,7 @@ func (a *App[T]) ExecuteGenesisTx(_ []byte) error { func (a *App[T]) GetAppManager() *appmanager.AppManager[T] { return a.AppManager } + +func (a *App[T]) GetGRPCQueryDecoders() map[string]func(requestBytes []byte) (gogoproto.Message, error) { + return a.GRPCQueryDecoders +} diff --git a/runtime/v2/manager.go b/runtime/v2/manager.go index 0b139c95bc35..ff9102085bad 100644 --- a/runtime/v2/manager.go +++ b/runtime/v2/manager.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "reflect" "sort" gogoproto "github.com/cosmos/gogoproto/proto" @@ -555,17 +556,28 @@ func (m *MM[T]) assertNoForgottenModules( func registerServices[T transaction.Tx](s appmodule.HasServices, app *App[T], registry *protoregistry.Files) error { c := &configurator{ - stfQueryRouter: app.queryRouterBuilder, - stfMsgRouter: app.msgRouterBuilder, - registry: registry, - err: nil, + grpcQueryDecoders: map[string]func([]byte) (gogoproto.Message, error){}, + stfQueryRouter: app.queryRouterBuilder, + stfMsgRouter: app.msgRouterBuilder, + registry: registry, + err: nil, } - return s.RegisterServices(c) + + err := s.RegisterServices(c) + if err != nil { + return fmt.Errorf("unable to register services: %w", err) + } + app.GRPCQueryDecoders = c.grpcQueryDecoders + return nil } var _ grpc.ServiceRegistrar = (*configurator)(nil) type configurator struct { + // grpcQueryDecoders is required because module expose queries through gRPC + // this provides a way to route to modules using gRPC. + grpcQueryDecoders map[string]func([]byte) (gogoproto.Message, error) + stfQueryRouter *stf.MsgRouterBuilder stfMsgRouter *stf.MsgRouterBuilder registry *protoregistry.Files @@ -596,17 +608,28 @@ func (c *configurator) RegisterService(sd *grpc.ServiceDesc, ss interface{}) { func (c *configurator) registerQueryHandlers(sd *grpc.ServiceDesc, ss interface{}) error { for _, md := range sd.Methods { // TODO(tip): what if a query is not deterministic? - err := registerMethod(c.stfQueryRouter, sd, md, ss) + requestFullName, err := registerMethod(c.stfQueryRouter, sd, md, ss) if err != nil { return fmt.Errorf("unable to register query handler %s: %w", md.MethodName, err) } + + // register gRPC query method. + typ := gogoproto.MessageType(requestFullName) + if typ == nil { + return fmt.Errorf("unable to find message in gogotype registry: %w", err) + } + decoderFunc := func(bytes []byte) (gogoproto.Message, error) { + msg := reflect.New(typ.Elem()).Interface().(gogoproto.Message) + return msg, gogoproto.Unmarshal(bytes, msg) + } + c.grpcQueryDecoders[md.MethodName] = decoderFunc } return nil } func (c *configurator) registerMsgHandlers(sd *grpc.ServiceDesc, ss interface{}) error { for _, md := range sd.Methods { - err := registerMethod(c.stfMsgRouter, sd, md, ss) + _, err := registerMethod(c.stfMsgRouter, sd, md, ss) if err != nil { return fmt.Errorf("unable to register msg handler %s: %w", md.MethodName, err) } @@ -633,13 +656,13 @@ func registerMethod( sd *grpc.ServiceDesc, md grpc.MethodDesc, ss interface{}, -) error { +) (string, error) { requestName, err := requestFullNameFromMethodDesc(sd, md) if err != nil { - return err + return "", err } - return stfRouter.RegisterHandler(string(requestName), func( + return string(requestName), stfRouter.RegisterHandler(string(requestName), func( ctx context.Context, msg appmodulev2.Message, ) (resp appmodulev2.Message, err error) { diff --git a/server/v2/api/grpc/server.go b/server/v2/api/grpc/server.go index 20397ea7a89d..7f73472a330f 100644 --- a/server/v2/api/grpc/server.go +++ b/server/v2/api/grpc/server.go @@ -14,7 +14,7 @@ import ( "cosmossdk.io/server/v2/api/grpc/gogoreflection" ) -type GRPCServer[AppT serverv2.AppI[T], T transaction.Tx] struct { +type GRPCServer[T transaction.Tx] struct { logger log.Logger config *Config cfgOptions []CfgOption @@ -23,15 +23,15 @@ type GRPCServer[AppT serverv2.AppI[T], T transaction.Tx] struct { } // New creates a new grpc server. -func New[AppT serverv2.AppI[T], T transaction.Tx](cfgOptions ...CfgOption) *GRPCServer[AppT, T] { - return &GRPCServer[AppT, T]{ +func New[T transaction.Tx](cfgOptions ...CfgOption) *GRPCServer[T] { + return &GRPCServer[T]{ cfgOptions: cfgOptions, } } // Init returns a correctly configured and initialized gRPC server. // Note, the caller is responsible for starting the server. -func (s *GRPCServer[AppT, T]) Init(appI AppT, v *viper.Viper, logger log.Logger) error { +func (s *GRPCServer[T]) Init(appI serverv2.AppI[T], v *viper.Viper, logger log.Logger) error { cfg := s.Config().(*Config) if v != nil { if err := v.Sub(s.Name()).Unmarshal(&cfg); err != nil { @@ -57,11 +57,11 @@ func (s *GRPCServer[AppT, T]) Init(appI AppT, v *viper.Viper, logger log.Logger) return nil } -func (s *GRPCServer[AppT, T]) Name() string { +func (s *GRPCServer[T]) Name() string { return "grpc" } -func (s *GRPCServer[AppT, T]) Config() any { +func (s *GRPCServer[T]) Config() any { if s.config == nil || s.config == (&Config{}) { cfg := DefaultConfig() // overwrite the default config with the provided options @@ -75,7 +75,7 @@ func (s *GRPCServer[AppT, T]) Config() any { return s.config } -func (s *GRPCServer[AppT, T]) Start(ctx context.Context) error { +func (s *GRPCServer[T]) Start(ctx context.Context) error { if !s.config.Enable { return nil } @@ -102,7 +102,7 @@ func (s *GRPCServer[AppT, T]) Start(ctx context.Context) error { return err } -func (s *GRPCServer[AppT, T]) Stop(ctx context.Context) error { +func (s *GRPCServer[T]) Stop(ctx context.Context) error { if !s.config.Enable { return nil } diff --git a/server/v2/api/grpcgateway/server.go b/server/v2/api/grpcgateway/server.go index 220e8b32f617..7a5693f8c5be 100644 --- a/server/v2/api/grpcgateway/server.go +++ b/server/v2/api/grpcgateway/server.go @@ -18,16 +18,14 @@ import ( serverv2 "cosmossdk.io/server/v2" ) -var _ serverv2.ServerComponent[ - serverv2.AppI[transaction.Tx], transaction.Tx, -] = (*GRPCGatewayServer[serverv2.AppI[transaction.Tx], transaction.Tx])(nil) +var _ serverv2.ServerComponent[transaction.Tx] = (*GRPCGatewayServer[transaction.Tx])(nil) const ( // GRPCBlockHeightHeader is the gRPC header for block height. GRPCBlockHeightHeader = "x-cosmos-block-height" ) -type GRPCGatewayServer[AppT serverv2.AppI[T], T transaction.Tx] struct { +type GRPCGatewayServer[T transaction.Tx] struct { logger log.Logger config *Config cfgOptions []CfgOption @@ -37,7 +35,7 @@ type GRPCGatewayServer[AppT serverv2.AppI[T], T transaction.Tx] struct { } // New creates a new gRPC-gateway server. -func New[AppT serverv2.AppI[T], T transaction.Tx](grpcSrv *grpc.Server, ir jsonpb.AnyResolver, cfgOptions ...CfgOption) *GRPCGatewayServer[AppT, T] { +func New[T transaction.Tx](grpcSrv *grpc.Server, ir jsonpb.AnyResolver, cfgOptions ...CfgOption) *GRPCGatewayServer[T] { // The default JSON marshaller used by the gRPC-Gateway is unable to marshal non-nullable non-scalar fields. // Using the gogo/gateway package with the gRPC-Gateway WithMarshaler option fixes the scalar field marshaling issue. marshalerOption := &gateway.JSONPb{ @@ -47,7 +45,7 @@ func New[AppT serverv2.AppI[T], T transaction.Tx](grpcSrv *grpc.Server, ir jsonp AnyResolver: ir, } - return &GRPCGatewayServer[AppT, T]{ + return &GRPCGatewayServer[T]{ GRPCSrv: grpcSrv, GRPCGatewayRouter: runtime.NewServeMux( // Custom marshaler option is required for gogo proto @@ -65,11 +63,11 @@ func New[AppT serverv2.AppI[T], T transaction.Tx](grpcSrv *grpc.Server, ir jsonp } } -func (g *GRPCGatewayServer[AppT, T]) Name() string { +func (g *GRPCGatewayServer[T]) Name() string { return "grpc-gateway" } -func (s *GRPCGatewayServer[AppT, T]) Config() any { +func (s *GRPCGatewayServer[T]) Config() any { if s.config == nil || s.config == (&Config{}) { cfg := DefaultConfig() // overwrite the default config with the provided options @@ -83,7 +81,7 @@ func (s *GRPCGatewayServer[AppT, T]) Config() any { return s.config } -func (s *GRPCGatewayServer[AppT, T]) Init(appI AppT, v *viper.Viper, logger log.Logger) error { +func (s *GRPCGatewayServer[T]) Init(appI serverv2.AppI[transaction.Tx], v *viper.Viper, logger log.Logger) error { cfg := s.Config().(*Config) if v != nil { if err := v.Sub(s.Name()).Unmarshal(&cfg); err != nil { @@ -100,7 +98,7 @@ func (s *GRPCGatewayServer[AppT, T]) Init(appI AppT, v *viper.Viper, logger log. return nil } -func (s *GRPCGatewayServer[AppT, T]) Start(ctx context.Context) error { +func (s *GRPCGatewayServer[T]) Start(ctx context.Context) error { if !s.config.Enable { return nil } @@ -110,7 +108,7 @@ func (s *GRPCGatewayServer[AppT, T]) Start(ctx context.Context) error { return nil } -func (s *GRPCGatewayServer[AppT, T]) Stop(ctx context.Context) error { +func (s *GRPCGatewayServer[T]) Stop(ctx context.Context) error { if !s.config.Enable { return nil } @@ -119,7 +117,7 @@ func (s *GRPCGatewayServer[AppT, T]) Stop(ctx context.Context) error { } // Register implements registers a grpc-gateway server -func (s *GRPCGatewayServer[AppT, T]) Register(r mux.Router) error { +func (s *GRPCGatewayServer[T]) Register(r mux.Router) error { // configure grpc-gatway server r.PathPrefix("/").Handler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { // Fall back to grpc gateway server. diff --git a/server/v2/cometbft/abci.go b/server/v2/cometbft/abci.go index c500d47e1edb..f63953b0c8d3 100644 --- a/server/v2/cometbft/abci.go +++ b/server/v2/cometbft/abci.go @@ -9,6 +9,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" abciproto "github.com/cometbft/cometbft/api/cometbft/abci/v1" + gogoproto "github.com/cosmos/gogoproto/proto" coreappmgr "cosmossdk.io/core/app" "cosmossdk.io/core/comet" @@ -31,6 +32,9 @@ import ( var _ abci.Application = (*Consensus[transaction.Tx])(nil) type Consensus[T transaction.Tx] struct { + // legacy support for gRPC + grpcQueryDecoders map[string]func(requestBytes []byte) (gogoproto.Message, error) + app *appmanager.AppManager[T] cfg Config store types.Store @@ -56,18 +60,28 @@ type Consensus[T transaction.Tx] struct { func NewConsensus[T transaction.Tx]( app *appmanager.AppManager[T], mp mempool.Mempool[T], + grpcQueryDecoders map[string]func(requestBytes []byte) (gogoproto.Message, error), store types.Store, cfg Config, txCodec transaction.Codec[T], logger log.Logger, ) *Consensus[T] { return &Consensus[T]{ - mempool: mp, - store: store, - app: app, - cfg: cfg, - txCodec: txCodec, - logger: logger, + grpcQueryDecoders: grpcQueryDecoders, + app: app, + cfg: cfg, + store: store, + logger: logger, + txCodec: txCodec, + streaming: streaming.Manager{}, + snapshotManager: nil, + mempool: mp, + lastCommittedHeight: atomic.Int64{}, + prepareProposalHandler: nil, + processProposalHandler: nil, + verifyVoteExt: nil, + extendVote: nil, + chainID: "", } } @@ -150,18 +164,16 @@ func (c *Consensus[T]) Info(ctx context.Context, _ *abciproto.InfoRequest) (*abc // Query implements types.Application. // It is called by cometbft to query application state. -func (c *Consensus[T]) Query(ctx context.Context, req *abciproto.QueryRequest) (*abciproto.QueryResponse, error) { - // follow the query path from here - decodedMsg, err := c.txCodec.Decode(req.Data) - protoMsg, ok := any(decodedMsg).(transaction.Msg) - if !ok { - return nil, fmt.Errorf("decoded type T %T must implement core/transaction.Msg", decodedMsg) - } - - // if no error is returned then we can handle the query with the appmanager - // otherwise it is a KV store query - if err == nil { - res, err := c.app.Query(ctx, uint64(req.Height), protoMsg) +func (c *Consensus[T]) Query(ctx context.Context, req *abciproto.QueryRequest) (resp *abciproto.QueryResponse, err error) { + // check if it's a gRPC method + grpcQueryDecoder, isGRPC := c.grpcQueryDecoders[req.Path] + if isGRPC { + protoRequest, err := grpcQueryDecoder(req.Data) + if err != nil { + return nil, fmt.Errorf("unable to decode gRPC request with path %s from ABCI.Query: %w", req.Path, err) + } + res, err := c.app.Query(ctx, uint64(req.Height), protoRequest) + if err != nil { resp := queryResult(err) resp.Height = req.Height @@ -179,8 +191,6 @@ func (c *Consensus[T]) Query(ctx context.Context, req *abciproto.QueryRequest) ( return QueryResult(errorsmod.Wrap(cometerrors.ErrUnknownRequest, "no query path provided"), c.cfg.Trace), nil } - var resp *abciproto.QueryResponse - switch path[0] { case cmtservice.QueryPathApp: resp, err = c.handlerQueryApp(ctx, path, req) @@ -391,7 +401,7 @@ func (c *Consensus[T]) FinalizeBlock( // ProposerAddress: req.ProposerAddress, // LastCommit: req.DecidedLastCommit, // }, - //} + // } // // ctx = context.WithValue(ctx, corecontext.CometInfoKey, &comet.Info{ // Evidence: sdktypes.ToSDKEvidence(req.Misbehavior), diff --git a/server/v2/cometbft/commands.go b/server/v2/cometbft/commands.go index 982eff6ae4c1..7c72cfe56ecc 100644 --- a/server/v2/cometbft/commands.go +++ b/server/v2/cometbft/commands.go @@ -28,7 +28,7 @@ import ( "github.com/cosmos/cosmos-sdk/version" ) -func (s *CometBFTServer[AppT, T]) rpcClient(cmd *cobra.Command) (rpc.CometRPC, error) { +func (s *CometBFTServer[T]) rpcClient(cmd *cobra.Command) (rpc.CometRPC, error) { if s.config.Standalone { client, err := rpchttp.New(client.GetConfigFromCmd(cmd).RPC.ListenAddress) if err != nil { @@ -51,7 +51,7 @@ func (s *CometBFTServer[AppT, T]) rpcClient(cmd *cobra.Command) (rpc.CometRPC, e } // StatusCommand returns the command to return the status of the network. -func (s *CometBFTServer[AppT, T]) StatusCommand() *cobra.Command { +func (s *CometBFTServer[T]) StatusCommand() *cobra.Command { cmd := &cobra.Command{ Use: "status", Short: "Query remote node for status", @@ -82,7 +82,7 @@ func (s *CometBFTServer[AppT, T]) StatusCommand() *cobra.Command { } // ShowNodeIDCmd - ported from CometBFT, dump node ID to stdout -func (s *CometBFTServer[AppT, T]) ShowNodeIDCmd() *cobra.Command { +func (s *CometBFTServer[T]) ShowNodeIDCmd() *cobra.Command { return &cobra.Command{ Use: "show-node-id", Short: "Show this node's ID", @@ -100,7 +100,7 @@ func (s *CometBFTServer[AppT, T]) ShowNodeIDCmd() *cobra.Command { } // ShowValidatorCmd - ported from CometBFT, show this node's validator info -func (s *CometBFTServer[AppT, T]) ShowValidatorCmd() *cobra.Command { +func (s *CometBFTServer[T]) ShowValidatorCmd() *cobra.Command { cmd := cobra.Command{ Use: "show-validator", Short: "Show this node's CometBFT validator info", @@ -134,7 +134,7 @@ func (s *CometBFTServer[AppT, T]) ShowValidatorCmd() *cobra.Command { } // ShowAddressCmd - show this node's validator address -func (s *CometBFTServer[AppT, T]) ShowAddressCmd() *cobra.Command { +func (s *CometBFTServer[T]) ShowAddressCmd() *cobra.Command { cmd := &cobra.Command{ Use: "show-address", Short: "Shows this node's CometBFT validator consensus address", @@ -153,7 +153,7 @@ func (s *CometBFTServer[AppT, T]) ShowAddressCmd() *cobra.Command { } // VersionCmd prints CometBFT and ABCI version numbers. -func (s *CometBFTServer[AppT, T]) VersionCmd() *cobra.Command { +func (s *CometBFTServer[T]) VersionCmd() *cobra.Command { return &cobra.Command{ Use: "version", Short: "Print CometBFT libraries' version", @@ -181,7 +181,7 @@ func (s *CometBFTServer[AppT, T]) VersionCmd() *cobra.Command { } // QueryBlocksCmd returns a command to search through blocks by events. -func (s *CometBFTServer[AppT, T]) QueryBlocksCmd() *cobra.Command { +func (s *CometBFTServer[T]) QueryBlocksCmd() *cobra.Command { cmd := &cobra.Command{ Use: "blocks", Short: "Query for paginated blocks that match a set of events", @@ -231,7 +231,7 @@ for. Each module documents its respective events under 'xx_events.md'. } // QueryBlockCmd implements the default command for a Block query. -func (s *CometBFTServer[AppT, T]) QueryBlockCmd() *cobra.Command { +func (s *CometBFTServer[T]) QueryBlockCmd() *cobra.Command { cmd := &cobra.Command{ Use: "block --type=[height|hash] [height|hash]", Short: "Query for a committed block by height, hash, or event(s)", @@ -318,7 +318,7 @@ $ %s query block --%s=%s } // QueryBlockResultsCmd implements the default command for a BlockResults query. -func (s *CometBFTServer[AppT, T]) QueryBlockResultsCmd() *cobra.Command { +func (s *CometBFTServer[T]) QueryBlockResultsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "block-results [height]", Short: "Query for a committed block's results by height", @@ -383,7 +383,7 @@ func parseOptionalHeight(heightStr string) (*int64, error) { return &tmp, nil } -func (s *CometBFTServer[AppT, T]) BootstrapStateCmd() *cobra.Command { +func (s *CometBFTServer[T]) BootstrapStateCmd() *cobra.Command { cmd := &cobra.Command{ Use: "bootstrap-state", Short: "Bootstrap CometBFT state at an arbitrary block height using a light client", diff --git a/server/v2/cometbft/server.go b/server/v2/cometbft/server.go index 1b1f0ff6637e..9b84a776400c 100644 --- a/server/v2/cometbft/server.go +++ b/server/v2/cometbft/server.go @@ -30,14 +30,12 @@ import ( ) var ( - _ serverv2.ServerComponent[ - serverv2.AppI[transaction.Tx], transaction.Tx, - ] = (*CometBFTServer[serverv2.AppI[transaction.Tx], transaction.Tx])(nil) - _ serverv2.HasCLICommands = (*CometBFTServer[serverv2.AppI[transaction.Tx], transaction.Tx])(nil) - _ serverv2.HasStartFlags = (*CometBFTServer[serverv2.AppI[transaction.Tx], transaction.Tx])(nil) + _ serverv2.ServerComponent[transaction.Tx] = (*CometBFTServer[transaction.Tx])(nil) + _ serverv2.HasCLICommands = (*CometBFTServer[transaction.Tx])(nil) + _ serverv2.HasStartFlags = (*CometBFTServer[transaction.Tx])(nil) ) -type CometBFTServer[AppT serverv2.AppI[T], T transaction.Tx] struct { +type CometBFTServer[T transaction.Tx] struct { Node *node.Node Consensus *Consensus[T] @@ -48,21 +46,21 @@ type CometBFTServer[AppT serverv2.AppI[T], T transaction.Tx] struct { cmtConfigOptions []CmtCfgOption } -func New[AppT serverv2.AppI[T], T transaction.Tx](txCodec transaction.Codec[T], options ServerOptions[T], cfgOptions ...CmtCfgOption) *CometBFTServer[AppT, T] { - return &CometBFTServer[AppT, T]{ +func New[T transaction.Tx](txCodec transaction.Codec[T], options ServerOptions[T], cfgOptions ...CmtCfgOption) *CometBFTServer[T] { + return &CometBFTServer[T]{ initTxCodec: txCodec, options: options, cmtConfigOptions: cfgOptions, } } -func (s *CometBFTServer[AppT, T]) Init(appI AppT, v *viper.Viper, logger log.Logger) error { +func (s *CometBFTServer[T]) Init(appI serverv2.AppI[T], v *viper.Viper, logger log.Logger) error { s.config = Config{CmtConfig: GetConfigFromViper(v), ConsensusAuthority: appI.GetConsensusAuthority()} s.logger = logger.With(log.ModuleKey, s.Name()) // create consensus store := appI.GetStore().(types.Store) - consensus := NewConsensus[T](appI.GetAppManager(), s.options.Mempool, store, s.config, s.initTxCodec, s.logger) + consensus := NewConsensus[T](appI.GetAppManager(), s.options.Mempool, appI.GetGRPCQueryDecoders(), store, s.config, s.initTxCodec, s.logger) consensus.prepareProposalHandler = s.options.PrepareProposalHandler consensus.processProposalHandler = s.options.ProcessProposalHandler @@ -85,11 +83,11 @@ func (s *CometBFTServer[AppT, T]) Init(appI AppT, v *viper.Viper, logger log.Log return nil } -func (s *CometBFTServer[AppT, T]) Name() string { +func (s *CometBFTServer[T]) Name() string { return "comet" } -func (s *CometBFTServer[AppT, T]) Start(ctx context.Context) error { +func (s *CometBFTServer[T]) Start(ctx context.Context) error { viper := ctx.Value(corectx.ViperContextKey).(*viper.Viper) cometConfig := GetConfigFromViper(viper) @@ -128,7 +126,7 @@ func (s *CometBFTServer[AppT, T]) Start(ctx context.Context) error { return s.Node.Start() } -func (s *CometBFTServer[AppT, T]) Stop(context.Context) error { +func (s *CometBFTServer[T]) Stop(context.Context) error { if s.Node != nil && s.Node.IsRunning() { return s.Node.Stop() } @@ -174,7 +172,7 @@ func getGenDocProvider(cfg *cmtcfg.Config) func() (node.ChecksummedGenesisDoc, e } } -func (s *CometBFTServer[AppT, T]) StartCmdFlags() *pflag.FlagSet { +func (s *CometBFTServer[T]) StartCmdFlags() *pflag.FlagSet { flags := pflag.NewFlagSet("cometbft", pflag.ExitOnError) flags.Bool(FlagWithComet, true, "Run abci app embedded in-process with CometBFT") flags.String(FlagAddress, "tcp://127.0.0.1:26658", "Listen address") @@ -189,7 +187,7 @@ func (s *CometBFTServer[AppT, T]) StartCmdFlags() *pflag.FlagSet { return flags } -func (s *CometBFTServer[AppT, T]) CLICommands() serverv2.CLIConfig { +func (s *CometBFTServer[T]) CLICommands() serverv2.CLIConfig { return serverv2.CLIConfig{ Commands: []*cobra.Command{ s.StatusCommand(), @@ -208,7 +206,7 @@ func (s *CometBFTServer[AppT, T]) CLICommands() serverv2.CLIConfig { } } -func (s *CometBFTServer[AppT, T]) WriteDefaultConfigAt(configPath string) error { +func (s *CometBFTServer[T]) WriteDefaultConfigAt(configPath string) error { cometConfig := cmtcfg.DefaultConfig() for _, opt := range s.cmtConfigOptions { opt(cometConfig) diff --git a/server/v2/commands.go b/server/v2/commands.go index db385a832d81..dd62c1518ac6 100644 --- a/server/v2/commands.go +++ b/server/v2/commands.go @@ -35,11 +35,11 @@ func Execute(rootCmd *cobra.Command, envPrefix, defaultHome string) error { // AddCommands add the server commands to the root command // It configure the config handling and the logger handling -func AddCommands[AppT AppI[T], T transaction.Tx]( +func AddCommands[T transaction.Tx]( rootCmd *cobra.Command, - newApp AppCreator[AppT, T], + newApp AppCreator[T], logger log.Logger, - components ...ServerComponent[AppT, T], + components ...ServerComponent[T], ) error { if len(components) == 0 { return errors.New("no components provided") @@ -96,9 +96,9 @@ func AddCommands[AppT AppI[T], T transaction.Tx]( } // createStartCommand creates the start command for the application. -func createStartCommand[AppT AppI[T], T transaction.Tx]( - server *Server[AppT, T], - newApp AppCreator[AppT, T], +func createStartCommand[T transaction.Tx]( + server *Server[T], + newApp AppCreator[T], ) *cobra.Command { flags := server.StartFlags() @@ -146,7 +146,7 @@ func createStartCommand[AppT AppI[T], T transaction.Tx]( } // configHandle writes the default config to the home directory if it does not exist and sets the server context -func configHandle[AppT AppI[T], T transaction.Tx](s *Server[AppT, T], cmd *cobra.Command) error { +func configHandle[T transaction.Tx](s *Server[T], cmd *cobra.Command) error { home, err := cmd.Flags().GetString(FlagHome) if err != nil { return err diff --git a/server/v2/server.go b/server/v2/server.go index c8db2773bdbb..7dd4438097ca 100644 --- a/server/v2/server.go +++ b/server/v2/server.go @@ -18,12 +18,12 @@ import ( ) // ServerComponent is a server module that can be started and stopped. -type ServerComponent[AppT AppI[T], T transaction.Tx] interface { +type ServerComponent[T transaction.Tx] interface { Name() string Start(context.Context) error Stop(context.Context) error - Init(AppT, *viper.Viper, log.Logger) error + Init(AppI[T], *viper.Viper, log.Logger) error } // HasCLICommands is a server module that has CLI commands. @@ -41,7 +41,7 @@ type HasStartFlags interface { StartCmdFlags() *pflag.FlagSet } -var _ ServerComponent[AppI[transaction.Tx], transaction.Tx] = (*Server[AppI[transaction.Tx], transaction.Tx])(nil) +var _ ServerComponent[transaction.Tx] = (*Server[transaction.Tx])(nil) // ReadConfig returns a viper instance of the config file func ReadConfig(configPath string) (*viper.Viper, error) { @@ -63,26 +63,27 @@ func ReadConfig(configPath string) (*viper.Viper, error) { return v, nil } -type Server[AppT AppI[T], T transaction.Tx] struct { +type Server[T transaction.Tx] struct { logger log.Logger - components []ServerComponent[AppT, T] + components []ServerComponent[T] } -func NewServer[AppT AppI[T], T transaction.Tx]( - logger log.Logger, components ...ServerComponent[AppT, T], -) *Server[AppT, T] { - return &Server[AppT, T]{ +func NewServer[T transaction.Tx]( + logger log.Logger, + components ...ServerComponent[T], +) *Server[T] { + return &Server[T]{ logger: logger, components: components, } } -func (s *Server[AppT, T]) Name() string { +func (s *Server[T]) Name() string { return "server" } // Start starts all components concurrently. -func (s *Server[AppT, T]) Start(ctx context.Context) error { +func (s *Server[T]) Start(ctx context.Context) error { s.logger.Info("starting servers...") g, ctx := errgroup.WithContext(ctx) @@ -103,7 +104,7 @@ func (s *Server[AppT, T]) Start(ctx context.Context) error { } // Stop stops all components concurrently. -func (s *Server[AppT, T]) Stop(ctx context.Context) error { +func (s *Server[T]) Stop(ctx context.Context) error { s.logger.Info("stopping servers...") g, ctx := errgroup.WithContext(ctx) @@ -118,7 +119,7 @@ func (s *Server[AppT, T]) Stop(ctx context.Context) error { } // CLICommands returns all CLI commands of all components. -func (s *Server[AppT, T]) CLICommands() CLIConfig { +func (s *Server[T]) CLICommands() CLIConfig { compart := func(name string, cmds ...*cobra.Command) *cobra.Command { if len(cmds) == 1 && strings.HasPrefix(cmds[0].Use, name) { return cmds[0] @@ -156,7 +157,7 @@ func (s *Server[AppT, T]) CLICommands() CLIConfig { } // Configs returns all configs of all server components. -func (s *Server[AppT, T]) Configs() map[string]any { +func (s *Server[T]) Configs() map[string]any { cfgs := make(map[string]any) for _, mod := range s.components { if configmod, ok := mod.(HasConfig); ok { @@ -170,8 +171,8 @@ func (s *Server[AppT, T]) Configs() map[string]any { // Init initializes all server components with the provided application, configuration, and logger. // It returns an error if any component fails to initialize. -func (s *Server[AppT, T]) Init(appI AppT, v *viper.Viper, logger log.Logger) error { - var components []ServerComponent[AppT, T] +func (s *Server[T]) Init(appI AppI[T], v *viper.Viper, logger log.Logger) error { + var components []ServerComponent[T] for _, mod := range s.components { mod := mod if err := mod.Init(appI, v, logger); err != nil { @@ -187,7 +188,7 @@ func (s *Server[AppT, T]) Init(appI AppT, v *viper.Viper, logger log.Logger) err // WriteConfig writes the config to the given path. // Note: it does not use viper.WriteConfigAs because we do not want to store flag values in the config. -func (s *Server[AppT, T]) WriteConfig(configPath string) error { +func (s *Server[T]) WriteConfig(configPath string) error { cfgs := s.Configs() b, err := toml.Marshal(cfgs) if err != nil { @@ -219,7 +220,7 @@ func (s *Server[AppT, T]) WriteConfig(configPath string) error { } // StartFlags returns all flags of all server components. -func (s *Server[AppT, T]) StartFlags() []*pflag.FlagSet { +func (s *Server[T]) StartFlags() []*pflag.FlagSet { flags := []*pflag.FlagSet{} for _, mod := range s.components { if startmod, ok := mod.(HasStartFlags); ok { diff --git a/server/v2/server_test.go b/server/v2/server_test.go index 2155e0e1af8e..37224d6d5fc6 100644 --- a/server/v2/server_test.go +++ b/server/v2/server_test.go @@ -58,7 +58,7 @@ func TestServer(t *testing.T) { } logger := log.NewLogger(os.Stdout) - grpcServer := grpc.New[serverv2.AppI[transaction.Tx], transaction.Tx]() + grpcServer := grpc.New[transaction.Tx]() if err := grpcServer.Init(&mockApp[transaction.Tx]{}, v, logger); err != nil { t.Log(err) t.Fail() diff --git a/server/v2/types.go b/server/v2/types.go index 1a22abf8e16f..3382d1b27b6b 100644 --- a/server/v2/types.go +++ b/server/v2/types.go @@ -1,6 +1,7 @@ package serverv2 import ( + gogoproto "github.com/cosmos/gogoproto/proto" "github.com/spf13/viper" coreapp "cosmossdk.io/core/app" @@ -9,11 +10,12 @@ import ( "cosmossdk.io/server/v2/appmanager" ) -type AppCreator[AppT AppI[T], T transaction.Tx] func(log.Logger, *viper.Viper) AppT +type AppCreator[T transaction.Tx] func(log.Logger, *viper.Viper) AppI[T] type AppI[T transaction.Tx] interface { GetAppManager() *appmanager.AppManager[T] GetConsensusAuthority() string InterfaceRegistry() coreapp.InterfaceRegistry + GetGRPCQueryDecoders() map[string]func(requestBytes []byte) (gogoproto.Message, error) GetStore() any } diff --git a/simapp/v2/app_di.go b/simapp/v2/app_di.go index b1e05095d904..5f4b2455bc8d 100644 --- a/simapp/v2/app_di.go +++ b/simapp/v2/app_di.go @@ -224,7 +224,6 @@ func NewSimApp[T transaction.Tx]( if err := app.LoadLatest(); err != nil { panic(err) } - return app } diff --git a/simapp/v2/go.mod b/simapp/v2/go.mod index 8fc91127cfb2..1d641dca6ca7 100644 --- a/simapp/v2/go.mod +++ b/simapp/v2/go.mod @@ -4,7 +4,7 @@ go 1.22.2 require ( cosmossdk.io/api v0.7.5 - cosmossdk.io/collections v0.4.0 // indirect + cosmossdk.io/client/v2 v2.0.0-00010101000000-000000000000 cosmossdk.io/core v0.12.1-0.20231114100755-569e3ff6a0d7 cosmossdk.io/depinject v1.0.0-alpha.4 cosmossdk.io/log v1.3.1 @@ -13,6 +13,7 @@ require ( cosmossdk.io/server/v2 v2.0.0-00010101000000-000000000000 cosmossdk.io/server/v2/cometbft v0.0.0-00010101000000-000000000000 cosmossdk.io/store/v2 v2.0.0 + cosmossdk.io/tools/confix v0.0.0-00010101000000-000000000000 cosmossdk.io/x/accounts v0.0.0-20240226161501-23359a0b6d91 cosmossdk.io/x/auth v0.0.0-00010101000000-000000000000 cosmossdk.io/x/authz v0.0.0-00010101000000-000000000000 @@ -29,28 +30,18 @@ require ( cosmossdk.io/x/protocolpool v0.0.0-20230925135524-a1bc045b3190 cosmossdk.io/x/slashing v0.0.0-00010101000000-000000000000 cosmossdk.io/x/staking v0.0.0-00010101000000-000000000000 - cosmossdk.io/x/tx v0.13.3 // indirect cosmossdk.io/x/upgrade v0.0.0-20230613133644-0a778132a60f github.com/cometbft/cometbft v1.0.0-rc1 github.com/cosmos/cosmos-db v1.0.2 // this version is not used as it is always replaced by the latest Cosmos SDK version github.com/cosmos/cosmos-sdk v0.51.0 - github.com/cosmos/gogoproto v1.5.0 // indirect - github.com/golang/mock v1.6.0 // indirect - github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 - golang.org/x/sync v0.7.0 // indirect google.golang.org/protobuf v1.34.2 ) -require ( - cosmossdk.io/client/v2 v2.0.0-00010101000000-000000000000 - cosmossdk.io/tools/confix v0.0.0-00010101000000-000000000000 -) - require ( buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 // indirect buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 // indirect @@ -60,6 +51,7 @@ require ( cloud.google.com/go/compute/metadata v0.3.0 // indirect cloud.google.com/go/iam v1.1.8 // indirect cloud.google.com/go/storage v1.42.0 // indirect + cosmossdk.io/collections v0.4.0 // indirect cosmossdk.io/core/testing v0.0.0-00010101000000-000000000000 // indirect cosmossdk.io/errors v1.0.1 // indirect cosmossdk.io/schema v0.1.1 // indirect @@ -69,6 +61,7 @@ require ( cosmossdk.io/x/accounts/defaults/lockup v0.0.0-20240417181816-5e7aae0db1f5 // indirect cosmossdk.io/x/accounts/defaults/multisig v0.0.0-00010101000000-000000000000 // indirect cosmossdk.io/x/epochs v0.0.0-20240522060652-a1ae4c3e0337 // indirect + cosmossdk.io/x/tx v0.13.3 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.2 // indirect @@ -97,6 +90,7 @@ require ( github.com/cosmos/crypto v0.1.1 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect + github.com/cosmos/gogoproto v1.5.0 // indirect github.com/cosmos/iavl v1.2.0 // indirect github.com/cosmos/ics23/go v0.10.0 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect @@ -125,6 +119,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect @@ -200,6 +195,7 @@ require ( github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/supranational/blst v0.3.12 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect @@ -223,6 +219,7 @@ require ( golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.27.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/term v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect diff --git a/simapp/v2/simdv2/cmd/commands.go b/simapp/v2/simdv2/cmd/commands.go index 017f601605bf..8f2edc75b8ef 100644 --- a/simapp/v2/simdv2/cmd/commands.go +++ b/simapp/v2/simdv2/cmd/commands.go @@ -72,13 +72,13 @@ func (t *temporaryTxDecoder[T]) DecodeJSON(bz []byte) (T, error) { return out, nil } -func newApp[AppT serverv2.AppI[T], T transaction.Tx]( +func newApp[T transaction.Tx]( logger log.Logger, viper *viper.Viper, -) AppT { - return serverv2.AppI[T](simapp.NewSimApp[T](logger, viper)).(AppT) +) serverv2.AppI[T] { + return serverv2.AppI[T](simapp.NewSimApp[T](logger, viper)) } -func initRootCmd[AppT serverv2.AppI[T], T transaction.Tx]( +func initRootCmd[T transaction.Tx]( rootCmd *cobra.Command, txConfig client.TxConfig, moduleManager *runtimev2.MM[T], @@ -114,8 +114,8 @@ func initRootCmd[AppT serverv2.AppI[T], T transaction.Tx]( rootCmd, newApp, logger, - cometbft.New[AppT, T](&temporaryTxDecoder[T]{txConfig}, cometbft.DefaultServerOptions[T]()), - grpc.New[AppT, T](), + cometbft.New[T](&temporaryTxDecoder[T]{txConfig}, cometbft.DefaultServerOptions[T]()), + grpc.New[T](), ); err != nil { panic(err) } diff --git a/simapp/v2/simdv2/cmd/root_di.go b/simapp/v2/simdv2/cmd/root_di.go index 61c355180386..6051bede8e9c 100644 --- a/simapp/v2/simdv2/cmd/root_di.go +++ b/simapp/v2/simdv2/cmd/root_di.go @@ -12,7 +12,6 @@ import ( "cosmossdk.io/depinject" "cosmossdk.io/log" "cosmossdk.io/runtime/v2" - serverv2 "cosmossdk.io/server/v2" "cosmossdk.io/simapp/v2" "cosmossdk.io/x/auth/tx" authtxconfig "cosmossdk.io/x/auth/tx/config" @@ -26,7 +25,7 @@ import ( ) // NewRootCmd creates a new root command for simd. It is called once in the main function. -func NewRootCmd[AppT serverv2.AppI[T], T transaction.Tx]() *cobra.Command { +func NewRootCmd[T transaction.Tx]() *cobra.Command { var ( autoCliOpts autocli.AppOptions moduleManager *runtime.MM[T] @@ -81,7 +80,7 @@ func NewRootCmd[AppT serverv2.AppI[T], T transaction.Tx]() *cobra.Command { }, } - initRootCmd[AppT, T](rootCmd, clientCtx.TxConfig, moduleManager) + initRootCmd[T](rootCmd, clientCtx.TxConfig, moduleManager) if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil { panic(err) } diff --git a/simapp/v2/simdv2/cmd/root_test.go b/simapp/v2/simdv2/cmd/root_test.go index ebca74ba70b9..a945a9ee8c85 100644 --- a/simapp/v2/simdv2/cmd/root_test.go +++ b/simapp/v2/simdv2/cmd/root_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/require" "cosmossdk.io/core/transaction" - serverv2 "cosmossdk.io/server/v2" "cosmossdk.io/simapp/v2" "cosmossdk.io/simapp/v2/simdv2/cmd" @@ -17,7 +16,7 @@ import ( ) func TestInitCmd(t *testing.T) { - rootCmd := cmd.NewRootCmd[serverv2.AppI[transaction.Tx], transaction.Tx]() + rootCmd := cmd.NewRootCmd[transaction.Tx]() rootCmd.SetArgs([]string{ "init", // Test the init cmd "simapp-test", // Moniker @@ -30,7 +29,7 @@ func TestInitCmd(t *testing.T) { func TestHomeFlagRegistration(t *testing.T) { homeDir := "/tmp/foo" - rootCmd := cmd.NewRootCmd[serverv2.AppI[transaction.Tx], transaction.Tx]() + rootCmd := cmd.NewRootCmd[transaction.Tx]() rootCmd.SetArgs([]string{ "query", fmt.Sprintf("--%s", flags.FlagHome), diff --git a/simapp/v2/simdv2/cmd/testnet.go b/simapp/v2/simdv2/cmd/testnet.go index 61387b42301a..407aedc5d2b3 100644 --- a/simapp/v2/simdv2/cmd/testnet.go +++ b/simapp/v2/simdv2/cmd/testnet.go @@ -336,8 +336,8 @@ func initTestnetFiles[T transaction.Tx]( } // Write server config - cometServer := cometbft.New[serverv2.AppI[T], T](&temporaryTxDecoder[T]{clientCtx.TxConfig}, cometbft.ServerOptions[T]{}, cometbft.OverwriteDefaultCometConfig(nodeConfig)) - grpcServer := grpc.New[serverv2.AppI[T], T](grpc.OverwriteDefaultConfig(grpcConfig)) + cometServer := cometbft.New[T](&temporaryTxDecoder[T]{clientCtx.TxConfig}, cometbft.ServerOptions[T]{}, cometbft.OverwriteDefaultCometConfig(nodeConfig)) + grpcServer := grpc.New[T](grpc.OverwriteDefaultConfig(grpcConfig)) server := serverv2.NewServer(log.NewNopLogger(), cometServer, grpcServer) err = server.WriteConfig(filepath.Join(nodeDir, "config")) if err != nil { diff --git a/simapp/v2/simdv2/main.go b/simapp/v2/simdv2/main.go index 6d0502cc676f..eb7d9a1b005b 100644 --- a/simapp/v2/simdv2/main.go +++ b/simapp/v2/simdv2/main.go @@ -12,7 +12,7 @@ import ( ) func main() { - rootCmd := cmd.NewRootCmd[serverv2.AppI[transaction.Tx], transaction.Tx]() + rootCmd := cmd.NewRootCmd[transaction.Tx]() if err := serverv2.Execute(rootCmd, clientv2helpers.EnvPrefix, simapp.DefaultNodeHome); err != nil { fmt.Fprintln(rootCmd.OutOrStderr(), err) os.Exit(1) diff --git a/tests/integration/server/grpc/out_of_gas_test.go b/tests/integration/server/grpc/out_of_gas_test.go new file mode 100644 index 000000000000..f662d2a25ff8 --- /dev/null +++ b/tests/integration/server/grpc/out_of_gas_test.go @@ -0,0 +1,98 @@ +package grpc_test + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + + _ "cosmossdk.io/x/accounts" + _ "cosmossdk.io/x/auth" + _ "cosmossdk.io/x/auth/tx/config" + _ "cosmossdk.io/x/bank" + banktypes "cosmossdk.io/x/bank/types" + _ "cosmossdk.io/x/consensus" + _ "cosmossdk.io/x/staking" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/testutil/configurator" + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +type IntegrationTestOutOfGasSuite struct { + suite.Suite + + cfg network.Config + network network.NetworkI + conn *grpc.ClientConn +} + +func (s *IntegrationTestOutOfGasSuite) SetupSuite() { + var err error + s.T().Log("setting up integration test suite") + + s.cfg, err = network.DefaultConfigWithAppConfigWithQueryGasLimit(configurator.NewAppConfig( + configurator.AccountsModule(), + configurator.AuthModule(), + configurator.BankModule(), + configurator.GenutilModule(), + configurator.StakingModule(), + configurator.ConsensusModule(), + configurator.TxModule(), + ), 10) + s.NoError(err) + s.cfg.NumValidators = 1 + + s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg) + s.Require().NoError(err) + + _, err = s.network.WaitForHeight(2) + s.Require().NoError(err) + + val0 := s.network.GetValidators()[0] + s.conn, err = grpc.NewClient( + val0.GetAppConfig().GRPC.Address, + grpc.WithInsecure(), //nolint:staticcheck // ignore SA1019, we don't need to use a secure connection for tests + grpc.WithDefaultCallOptions(grpc.ForceCodec(codec.NewProtoCodec(s.cfg.InterfaceRegistry).GRPCCodec())), + ) + s.Require().NoError(err) +} + +func (s *IntegrationTestOutOfGasSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.conn.Close() + s.network.Cleanup() +} + +func (s *IntegrationTestOutOfGasSuite) TestGRPCServer_TestService() { + // gRPC query to test service should work + testClient := testdata.NewQueryClient(s.conn) + testRes, err := testClient.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"}) + s.Require().NoError(err) + s.Require().Equal("hello", testRes.Message) +} + +func (s *IntegrationTestOutOfGasSuite) TestGRPCServer_BankBalance_OutOfGas() { + val0 := s.network.GetValidators()[0] + + // gRPC query to bank service should work + denom := fmt.Sprintf("%stoken", val0.GetMoniker()) + bankClient := banktypes.NewQueryClient(s.conn) + var header metadata.MD + _, err := bankClient.Balance( + context.Background(), + &banktypes.QueryBalanceRequest{Address: val0.GetAddress().String(), Denom: denom}, + grpc.Header(&header), // Also fetch grpc header + ) + + s.Require().ErrorContains(err, sdkerrors.ErrOutOfGas.Error()) +} + +func TestIntegrationTestOutOfGasSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestOutOfGasSuite)) +} diff --git a/testutil/network/network.go b/testutil/network/network.go index 0d0e942a9fb2..fb2bbd54ae69 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -164,6 +164,10 @@ func DefaultConfig(factory TestFixtureFactory) Config { } func DefaultConfigWithAppConfig(appConfig depinject.Config) (Config, error) { + return DefaultConfigWithAppConfigWithQueryGasLimit(appConfig, 0) +} + +func DefaultConfigWithAppConfigWithQueryGasLimit(appConfig depinject.Config, queryGasLimit uint64) (Config, error) { var ( appBuilder *runtime.AppBuilder txConfig client.TxConfig @@ -221,6 +225,7 @@ func DefaultConfigWithAppConfig(appConfig depinject.Config) (Config, error) { baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)), baseapp.SetMinGasPrices(val.GetAppConfig().MinGasPrices), baseapp.SetChainID(cfg.ChainID), + baseapp.SetQueryGasLimit(queryGasLimit), ) testdata.RegisterQueryServer(app.GRPCQueryRouter(), testdata.QueryImpl{}) diff --git a/tools/confix/go.mod b/tools/confix/go.mod index fbd87702c8fc..fc186fed6c14 100644 --- a/tools/confix/go.mod +++ b/tools/confix/go.mod @@ -3,7 +3,7 @@ module cosmossdk.io/tools/confix go 1.21 require ( - github.com/cosmos/cosmos-sdk v0.50.7 + github.com/cosmos/cosmos-sdk v0.50.8 github.com/creachadair/atomicfile v0.3.4 github.com/creachadair/tomledit v0.0.26 github.com/pelletier/go-toml/v2 v2.2.2 @@ -39,7 +39,7 @@ require ( github.com/cockroachdb/pebble v1.1.0 // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect - github.com/cometbft/cometbft v0.38.8 // indirect + github.com/cometbft/cometbft v0.38.9 // indirect github.com/cometbft/cometbft-db v0.9.1 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-db v1.0.2 // indirect diff --git a/tools/confix/go.sum b/tools/confix/go.sum index cd83e4075460..5737494ce2ce 100644 --- a/tools/confix/go.sum +++ b/tools/confix/go.sum @@ -125,8 +125,8 @@ github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/cometbft/cometbft v0.38.8 h1:XyJ9Cu3xqap6xtNxiemrO8roXZ+KS2Zlu7qQ0w1trvU= -github.com/cometbft/cometbft v0.38.8/go.mod h1:xOoGZrtUT+A5izWfHSJgl0gYZUE7lu7Z2XIS1vWG/QQ= +github.com/cometbft/cometbft v0.38.9 h1:cJBJBG0mPKz+sqelCi/hlfZjadZQGdDNnu6YQ1ZsUHQ= +github.com/cometbft/cometbft v0.38.9/go.mod h1:xOoGZrtUT+A5izWfHSJgl0gYZUE7lu7Z2XIS1vWG/QQ= github.com/cometbft/cometbft-db v0.9.1 h1:MIhVX5ja5bXNHF8EYrThkG9F7r9kSfv8BX4LWaxWJ4M= github.com/cometbft/cometbft-db v0.9.1/go.mod h1:iliyWaoV0mRwBJoizElCwwRA9Tf7jZJOURcRZF9m60U= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= @@ -143,8 +143,8 @@ github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAK github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= -github.com/cosmos/cosmos-sdk v0.50.7 h1:LsBGKxifENR/DN4E1RZaitsyL93HU44x0p8EnMHp4V4= -github.com/cosmos/cosmos-sdk v0.50.7/go.mod h1:84xDDJEHttRT7NDGwBaUOLVOMN0JNE9x7NbsYIxXs1s= +github.com/cosmos/cosmos-sdk v0.50.8 h1:2UJHssUaGHTl4/dFp8xyREKAnfiRU6VVfqtKG9n8w5g= +github.com/cosmos/cosmos-sdk v0.50.8/go.mod h1:Zb+DgHtiByNwgj71IlJBXwOq6dLhtyAq3AgqpXm/jHo= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= diff --git a/x/auth/CHANGELOG.md b/x/auth/CHANGELOG.md index 6fe1a31ff4c5..594f89f87267 100644 --- a/x/auth/CHANGELOG.md +++ b/x/auth/CHANGELOG.md @@ -63,4 +63,5 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [#19148](https://github.com/cosmos/cosmos-sdk/pull/19148) Checks the consumed gas for verifying a multisig pubKey signature during simulation. * [#19239](https://github.com/cosmos/cosmos-sdk/pull/19239) Sets from flag in multi-sign command to avoid no key name provided error. * [#19099](https://github.com/cosmos/cosmos-sdk/pull/19099) `verifyIsOnCurve` now checks if we are simulating to avoid malformed public key error. -* [#20323](https://github.com/cosmos/cosmos-sdk/pull/20323) Ignore undecodable txs in GetBlocksWithTxs. \ No newline at end of file +* [#20323](https://github.com/cosmos/cosmos-sdk/pull/20323) Ignore undecodable txs in GetBlocksWithTxs. +* [#20963](https://github.com/cosmos/cosmos-sdk/pull/20963) UseGrantedFees used to return error with raw addresses. Now it uses addresses in string format. \ No newline at end of file diff --git a/x/auth/ante/fee.go b/x/auth/ante/fee.go index 21b5300fd53c..298fae30a4cc 100644 --- a/x/auth/ante/fee.go +++ b/x/auth/ante/fee.go @@ -98,7 +98,15 @@ func (dfd DeductFeeDecorator) checkDeductFee(ctx sdk.Context, sdkTx sdk.Tx, fee } else if !bytes.Equal(feeGranterAddr, feePayer) { err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranterAddr, feePayer, fee, sdkTx.GetMsgs()) if err != nil { - return errorsmod.Wrapf(err, "%s does not allow to pay fees for %s", feeGranter, feePayer) + granterAddr, acErr := dfd.accountKeeper.AddressCodec().BytesToString(feeGranter) + if acErr != nil { + return errorsmod.Wrapf(err, "%s, feeGranter does not allow to pay fees", acErr.Error()) + } + payerAddr, acErr := dfd.accountKeeper.AddressCodec().BytesToString(feePayer) + if acErr != nil { + return errorsmod.Wrapf(err, "%s, feeGranter does not allow to pay fees", acErr.Error()) + } + return errorsmod.Wrapf(err, "%s does not allow to pay fees for %s", granterAddr, payerAddr) } }