From 344fd73b0b811fe99b2201623b5036e8e08f4330 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 26 Mar 2022 21:04:52 -0400 Subject: [PATCH] feat: grpc-only mode (backport #11430) (#11460) --- CHANGELOG.md | 1 + server/start.go | 119 ++++++++++++++++++++++++++++-------------------- 2 files changed, 70 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcc5f4c88e54..d57ef81ae107 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* [\#11430](https://github.com/cosmos/cosmos-sdk/pull/11430) Introduce a new `grpc-only` flag, such that when enabled, will start the node in a query-only mode. Note, gRPC MUST be enabled with this flag. * (x/bank) [\#11417](https://github.com/cosmos/cosmos-sdk/pull/11417) Introduce a new `SpendableBalances` gRPC query that retrieves an account's total (paginated) spendable balances. * (x/bank) [\#10771](https://github.com/cosmos/cosmos-sdk/pull/10771) Add safety check on bank module perms to allow module-specific mint restrictions (e.g. only minting a certain denom). * (x/bank) [\#10771](https://github.com/cosmos/cosmos-sdk/pull/10771) Add `bank.BankKeeper.WithMintCoinsRestriction` function to restrict use of bank `MintCoins` usage. This function is not on the bank `Keeper` interface, so it's not API-breaking, but only additive on the keeper implementation. diff --git a/server/start.go b/server/start.go index 6ff040da4b57..2fb1a96c11b3 100644 --- a/server/start.go +++ b/server/start.go @@ -9,11 +9,7 @@ import ( "runtime/pprof" "time" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/spf13/cobra" - "google.golang.org/grpc" - "github.com/tendermint/tendermint/abci/server" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" tmos "github.com/tendermint/tendermint/libs/os" @@ -22,21 +18,22 @@ import ( pvm "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/rpc/client/local" - - "github.com/cosmos/cosmos-sdk/server/rosetta" - crgserver "github.com/cosmos/cosmos-sdk/server/rosetta/lib/server" + "google.golang.org/grpc" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server/api" "github.com/cosmos/cosmos-sdk/server/config" servergrpc "github.com/cosmos/cosmos-sdk/server/grpc" + "github.com/cosmos/cosmos-sdk/server/rosetta" + crgserver "github.com/cosmos/cosmos-sdk/server/rosetta/lib/server" "github.com/cosmos/cosmos-sdk/server/types" storetypes "github.com/cosmos/cosmos-sdk/store/types" ) -// Tendermint full-node start flags const ( + // Tendermint full-node start flags flagWithTendermint = "with-tendermint" flagAddress = "address" flagTransport = "transport" @@ -56,22 +53,19 @@ const ( FlagPruningInterval = "pruning-interval" FlagIndexEvents = "index-events" FlagMinRetainBlocks = "min-retain-blocks" -) -// GRPC-related flags. -const ( + // state sync-related flags + FlagStateSyncSnapshotInterval = "state-sync.snapshot-interval" + FlagStateSyncSnapshotKeepRecent = "state-sync.snapshot-keep-recent" + + // gRPC-related flags + flagGRPCOnly = "grpc-only" flagGRPCEnable = "grpc.enable" flagGRPCAddress = "grpc.address" flagGRPCWebEnable = "grpc-web.enable" flagGRPCWebAddress = "grpc-web.address" ) -// State sync-related flags. -const ( - FlagStateSyncSnapshotInterval = "state-sync.snapshot-interval" - FlagStateSyncSnapshotKeepRecent = "state-sync.snapshot-keep-recent" -) - // StartCmd runs the service passed in, either stand-alone or in-process with // Tendermint. func StartCmd(appCreator types.AppCreator, defaultNodeHome string) *cobra.Command { @@ -99,6 +93,11 @@ will not be able to commit subsequent blocks. For profiling and benchmarking purposes, CPU profiling can be enabled via the '--cpu-profile' flag which accepts a path for the resulting pprof file. + +The node may be started in a 'query only' mode where only the gRPC and JSON HTTP +API services are enabled via the 'grpc-only' flag. In this mode, Tendermint is +bypassed and can be used when legacy queries are needed after an on-chain upgrade +is performed. Note, when enabled, gRPC will also be automatically enabled. `, PreRunE: func(cmd *cobra.Command, _ []string) error { serverCtx := GetServerContextFromCmd(cmd) @@ -123,8 +122,6 @@ which accepts a path for the resulting pprof file. return startStandAlone(serverCtx, appCreator) } - serverCtx.Logger.Info("starting ABCI with Tendermint") - // amino is needed here for backwards compatibility of REST routes err = startInProcess(serverCtx, clientCtx, appCreator) errCode, ok := err.(ErrorCode) @@ -156,6 +153,7 @@ which accepts a path for the resulting pprof file. cmd.Flags().Uint(FlagInvCheckPeriod, 0, "Assert registered invariants every N blocks") cmd.Flags().Uint64(FlagMinRetainBlocks, 0, "Minimum block height offset during ABCI commit to prune Tendermint blocks") + cmd.Flags().Bool(flagGRPCOnly, false, "Start the node in gRPC query only mode (no Tendermint process is started)") cmd.Flags().Bool(flagGRPCEnable, true, "Define if the gRPC server should be enabled") cmd.Flags().String(flagGRPCAddress, config.DefaultGRPCAddress, "the gRPC server address to listen on") @@ -210,7 +208,6 @@ func startStandAlone(ctx *Context, appCreator types.AppCreator) error { return WaitForQuitSignals() } -// legacyAminoCdc is used for the legacy REST API func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.AppCreator) error { cfg := ctx.Config home := cfg.RootDir @@ -260,30 +257,38 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App } genDocProvider := node.DefaultGenesisDocProviderFunc(cfg) - tmNode, err := node.NewNode( - cfg, - pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()), - nodeKey, - proxy.NewLocalClientCreator(app), - genDocProvider, - node.DefaultDBProvider, - node.DefaultMetricsProvider(cfg.Instrumentation), - ctx.Logger, + + var ( + tmNode *node.Node + gRPCOnly = ctx.Viper.GetBool(flagGRPCOnly) ) - if err != nil { - return err - } - ctx.Logger.Debug("initialization: tmNode created") - if err := tmNode.Start(); err != nil { - return err + if gRPCOnly { + ctx.Logger.Info("starting node in gRPC only mode; Tendermint is disabled") + config.GRPC.Enable = true + } else { + ctx.Logger.Info("starting node with ABCI Tendermint in-process") + + tmNode, err = node.NewNode( + cfg, + pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()), + nodeKey, + proxy.NewLocalClientCreator(app), + genDocProvider, + node.DefaultDBProvider, + node.DefaultMetricsProvider(cfg.Instrumentation), + ctx.Logger, + ) + + if err := tmNode.Start(); err != nil { + return err + } } - ctx.Logger.Debug("initialization: tmNode started") // Add the tx service to the gRPC router. We only need to register this // service if API or gRPC is enabled, and avoid doing so in the general // case, because it spawns a new local tendermint RPC client. - if config.API.Enable || config.GRPC.Enable { + if (config.API.Enable || config.GRPC.Enable) && tmNode != nil { clientCtx = clientCtx.WithClient(local.New(tmNode)) app.RegisterTxService(clientCtx) @@ -297,9 +302,7 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App return err } - clientCtx := clientCtx. - WithHomeDir(home). - WithChainID(genDoc.ChainID) + clientCtx := clientCtx.WithHomeDir(home).WithChainID(genDoc.ChainID) apiSrv = api.New(clientCtx, ctx.Logger.With("module", "api-server")) app.RegisterAPIRoutes(apiSrv, config.API) @@ -314,6 +317,7 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App select { case err := <-errCh: return err + case <-time.After(types.ServerStartTime): // assume server started successfully } } @@ -322,11 +326,13 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App grpcSrv *grpc.Server grpcWebSrv *http.Server ) + if config.GRPC.Enable { grpcSrv, err = servergrpc.StartGRPCServer(clientCtx, app, config.GRPC.Address) if err != nil { return err } + if config.GRPCWeb.Enable { grpcWebSrv, err = servergrpc.StartGRPCWeb(grpcSrv, config) if err != nil { @@ -336,28 +342,40 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App } } + // At this point it is safe to block the process if we're in gRPC only mode as + // we do not need to start Rosetta or handle any Tendermint related processes. + if gRPCOnly { + // wait for signal capture and gracefully return + return WaitForQuitSignals() + } + var rosettaSrv crgserver.Server if config.Rosetta.Enable { offlineMode := config.Rosetta.Offline - if !config.GRPC.Enable { // If GRPC is not enabled rosetta cannot work in online mode, so it works in offline mode. + + // If GRPC is not enabled rosetta cannot work in online mode, so it works in + // offline mode. + if !config.GRPC.Enable { offlineMode = true } conf := &rosetta.Config{ - Blockchain: config.Rosetta.Blockchain, - Network: config.Rosetta.Network, - TendermintRPC: ctx.Config.RPC.ListenAddress, - GRPCEndpoint: config.GRPC.Address, - Addr: config.Rosetta.Address, - Retries: config.Rosetta.Retries, - Offline: offlineMode, + Blockchain: config.Rosetta.Blockchain, + Network: config.Rosetta.Network, + TendermintRPC: ctx.Config.RPC.ListenAddress, + GRPCEndpoint: config.GRPC.Address, + Addr: config.Rosetta.Address, + Retries: config.Rosetta.Retries, + Offline: offlineMode, + Codec: clientCtx.Codec.(*codec.ProtoCodec), + InterfaceRegistry: clientCtx.InterfaceRegistry, } - conf.WithCodec(clientCtx.InterfaceRegistry, clientCtx.Codec.(*codec.ProtoCodec)) rosettaSrv, err = rosetta.ServerFromConfig(conf) if err != nil { return err } + errCh := make(chan error) go func() { if err := rosettaSrv.Start(); err != nil { @@ -368,6 +386,7 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App select { case err := <-errCh: return err + case <-time.After(types.ServerStartTime): // assume server started successfully } } @@ -395,6 +414,6 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App ctx.Logger.Info("exiting...") }() - // Wait for SIGINT or SIGTERM signal + // wait for signal capture and gracefully return return WaitForQuitSignals() }