github.com/Finschia/finschia-sdk@v0.48.1/server/start.go (about) 1 package server 2 3 // DONTCOVER 4 5 import ( 6 "fmt" 7 "net/http" 8 "os" 9 "runtime/pprof" 10 "strings" 11 "time" 12 13 "github.com/spf13/cobra" 14 "google.golang.org/grpc" 15 16 "github.com/Finschia/ostracon/abci/server" 17 ostcmd "github.com/Finschia/ostracon/cmd/ostracon/commands" 18 "github.com/Finschia/ostracon/config" 19 ostos "github.com/Finschia/ostracon/libs/os" 20 "github.com/Finschia/ostracon/node" 21 "github.com/Finschia/ostracon/p2p" 22 pvm "github.com/Finschia/ostracon/privval" 23 "github.com/Finschia/ostracon/proxy" 24 "github.com/Finschia/ostracon/rpc/client/local" 25 26 "github.com/Finschia/finschia-sdk/client" 27 "github.com/Finschia/finschia-sdk/client/flags" 28 "github.com/Finschia/finschia-sdk/codec" 29 "github.com/Finschia/finschia-sdk/server/api" 30 serverconfig "github.com/Finschia/finschia-sdk/server/config" 31 servergrpc "github.com/Finschia/finschia-sdk/server/grpc" 32 "github.com/Finschia/finschia-sdk/server/rosetta" 33 crgserver "github.com/Finschia/finschia-sdk/server/rosetta/lib/server" 34 "github.com/Finschia/finschia-sdk/server/types" 35 "github.com/Finschia/finschia-sdk/store/cache" 36 "github.com/Finschia/finschia-sdk/store/iavl" 37 storetypes "github.com/Finschia/finschia-sdk/store/types" 38 "github.com/Finschia/finschia-sdk/telemetry" 39 ) 40 41 // Ostracon full-node start flags 42 const ( 43 flagWithOstracon = "with-ostracon" 44 flagAddress = "address" 45 flagTransport = "transport" 46 flagTraceStore = "trace-store" 47 flagCPUProfile = "cpu-profile" 48 FlagMinGasPrices = "minimum-gas-prices" 49 FlagHaltHeight = "halt-height" 50 FlagHaltTime = "halt-time" 51 FlagInterBlockCache = "inter-block-cache" 52 FlagInterBlockCacheSize = "inter-block-cache-size" 53 FlagUnsafeSkipUpgrades = "unsafe-skip-upgrades" 54 FlagTrace = "trace" 55 FlagInvCheckPeriod = "inv-check-period" 56 FlagPrometheus = "prometheus" 57 FlagChanCheckTxSize = "chan-check-tx-size" 58 59 FlagPruning = "pruning" 60 FlagPruningKeepRecent = "pruning-keep-recent" 61 FlagPruningKeepEvery = "pruning-keep-every" 62 FlagPruningInterval = "pruning-interval" 63 FlagIndexEvents = "index-events" 64 FlagMinRetainBlocks = "min-retain-blocks" 65 FlagIAVLCacheSize = "iavl-cache-size" 66 FlagIAVLFastNode = "iavl-disable-fastnode" 67 68 // state sync-related flags 69 FlagStateSyncSnapshotInterval = "state-sync.snapshot-interval" 70 FlagStateSyncSnapshotKeepRecent = "state-sync.snapshot-keep-recent" 71 72 // gRPC-related flags 73 flagGRPCOnly = "grpc-only" 74 flagGRPCEnable = "grpc.enable" 75 flagGRPCAddress = "grpc.address" 76 flagGRPCWebEnable = "grpc-web.enable" 77 flagGRPCWebAddress = "grpc-web.address" 78 ) 79 80 // StartCmd runs the service passed in, either stand-alone or in-process with 81 // Ostracon. 82 func StartCmd(appCreator types.AppCreator, defaultNodeHome string) *cobra.Command { 83 cmd := &cobra.Command{ 84 Use: "start", 85 Short: "Run the full node", 86 Long: `Run the full node application with Ostracon in or out of process. By 87 default, the application will run with Ostracon in process. 88 89 Pruning options can be provided via the '--pruning' flag or alternatively with '--pruning-keep-recent', 90 'pruning-keep-every', and 'pruning-interval' together. 91 92 For '--pruning' the options are as follows: 93 94 default: the last 100 states are kept in addition to every 500th state; pruning at 10 block intervals 95 nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node) 96 everything: all saved states will be deleted, storing only the current and previous state; pruning at 10 block intervals 97 custom: allow pruning options to be manually specified through 'pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval' 98 99 Node halting configurations exist in the form of two flags: '--halt-height' and '--halt-time'. During 100 the ABCI Commit phase, the node will check if the current block height is greater than or equal to 101 the halt-height or if the current block time is greater than or equal to the halt-time. If so, the 102 node will attempt to gracefully shutdown and the block will not be committed. In addition, the node 103 will not be able to commit subsequent blocks. 104 105 For profiling and benchmarking purposes, CPU profiling can be enabled via the '--cpu-profile' flag 106 which accepts a path for the resulting pprof file. 107 108 The node may be started in a 'query only' mode where only the gRPC and JSON HTTP 109 API services are enabled via the 'grpc-only' flag. In this mode, Tendermint is 110 bypassed and can be used when legacy queries are needed after an on-chain upgrade 111 is performed. Note, when enabled, gRPC will also be automatically enabled. 112 `, 113 PreRunE: func(cmd *cobra.Command, _ []string) error { 114 serverCtx := GetServerContextFromCmd(cmd) 115 116 // Bind flags to the Context's Viper so the app construction can set 117 // options accordingly. 118 if err := serverCtx.Viper.BindPFlags(cmd.Flags()); err != nil { 119 return err 120 } 121 122 _, err := GetPruningOptionsFromFlags(serverCtx.Viper) 123 return err 124 }, 125 RunE: func(cmd *cobra.Command, _ []string) error { 126 serverCtx := GetServerContextFromCmd(cmd) 127 clientCtx, err := client.GetClientQueryContext(cmd) 128 if err != nil { 129 return err 130 } 131 132 withOST, _ := cmd.Flags().GetBool(flagWithOstracon) 133 if !withOST { 134 serverCtx.Logger.Info("starting ABCI without Ostracon") 135 return startStandAlone(serverCtx, appCreator) 136 } 137 138 serverCtx.Logger.Info("starting ABCI with Ostracon") 139 140 // amino is needed here for backwards compatibility of REST routes 141 err = startInProcess(serverCtx, clientCtx, appCreator) 142 errCode, ok := err.(ErrorCode) 143 if !ok { 144 return err 145 } 146 147 serverCtx.Logger.Debug(fmt.Sprintf("received quit signal: %d", errCode.Code)) 148 return nil 149 }, 150 } 151 152 cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") 153 cmd.Flags().Bool(flagWithOstracon, true, "Run abci app embedded in-process with ostracon") 154 cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address") 155 cmd.Flags().String(flagTransport, "socket", "Transport protocol: socket, grpc") 156 cmd.Flags().String(flagTraceStore, "", "Enable KVStore tracing to an output file") 157 cmd.Flags().String(FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)") 158 cmd.Flags().IntSlice(FlagUnsafeSkipUpgrades, []int{}, "Skip a set of upgrade heights to continue the old binary") 159 cmd.Flags().Uint64(FlagHaltHeight, 0, "Block height at which to gracefully halt the chain and shutdown the node") 160 cmd.Flags().Uint64(FlagHaltTime, 0, "Minimum block time (in Unix seconds) at which to gracefully halt the chain and shutdown the node") 161 cmd.Flags().Bool(FlagInterBlockCache, true, "Enable inter-block caching") 162 cmd.Flags().Int(FlagInterBlockCacheSize, cache.DefaultCommitKVStoreCacheSize, "The maximum bytes size of the inter-block cache") 163 cmd.Flags().Int(FlagIAVLCacheSize, iavl.DefaultIAVLCacheSize, "The maximum units size of the iavl node cache (1 unit is 128 bytes).") 164 cmd.Flags().String(flagCPUProfile, "", "Enable CPU profiling and write to the provided file") 165 cmd.Flags().Bool(FlagTrace, false, "Provide full stack traces for errors in ABCI Log") 166 cmd.Flags().String(FlagPruning, storetypes.PruningOptionDefault, "Pruning strategy (default|nothing|everything|custom)") 167 cmd.Flags().Uint64(FlagPruningKeepRecent, 0, "Number of recent heights to keep on disk (ignored if pruning is not 'custom')") 168 cmd.Flags().Uint64(FlagPruningKeepEvery, 0, "Offset heights to keep on disk after 'keep-every' (ignored if pruning is not 'custom')") 169 cmd.Flags().Uint64(FlagPruningInterval, 0, "Height interval at which pruned heights are removed from disk (ignored if pruning is not 'custom')") 170 cmd.Flags().Uint(FlagInvCheckPeriod, 0, "Assert registered invariants every N blocks") 171 cmd.Flags().Uint64(FlagMinRetainBlocks, 0, "Minimum block height offset during ABCI commit to prune Ostracon blocks") 172 173 cmd.Flags().Bool(flagGRPCOnly, false, "Start the node in gRPC query only mode (no Tendermint process is started)") 174 cmd.Flags().Bool(flagGRPCEnable, true, "Define if the gRPC server should be enabled") 175 cmd.Flags().String(flagGRPCAddress, serverconfig.DefaultGRPCAddress, "the gRPC server address to listen on") 176 177 cmd.Flags().Bool(flagGRPCWebEnable, true, "Define if the gRPC-Web server should be enabled. (Note: gRPC must also be enabled.)") 178 cmd.Flags().String(flagGRPCWebAddress, serverconfig.DefaultGRPCWebAddress, "The gRPC-Web server address to listen on") 179 180 cmd.Flags().Uint64(FlagStateSyncSnapshotInterval, 0, "State sync snapshot interval") 181 cmd.Flags().Uint32(FlagStateSyncSnapshotKeepRecent, 2, "State sync snapshot to keep") 182 183 cmd.Flags().Bool(FlagIAVLFastNode, true, "Enable fast node for IAVL tree") 184 185 cmd.Flags().Bool(FlagPrometheus, false, "Enable prometheus metric for app") 186 187 cmd.Flags().Uint(FlagChanCheckTxSize, serverconfig.DefaultChanCheckTxSize, "The size of the channel check tx") 188 189 // add support for all Ostracon-specific command line options 190 ostcmd.AddNodeFlags(cmd) 191 return cmd 192 } 193 194 func startStandAlone(ctx *Context, appCreator types.AppCreator) error { 195 addr := ctx.Viper.GetString(flagAddress) 196 transport := ctx.Viper.GetString(flagTransport) 197 home := ctx.Viper.GetString(flags.FlagHome) 198 199 db, err := openDB(home) 200 if err != nil { 201 return err 202 } 203 204 traceWriterFile := ctx.Viper.GetString(flagTraceStore) 205 traceWriter, err := openTraceWriter(traceWriterFile) 206 if err != nil { 207 return err 208 } 209 210 app := appCreator(ctx.Logger, db, traceWriter, ctx.Viper) 211 212 config, err := serverconfig.GetConfig(ctx.Viper) 213 if err != nil { 214 return err 215 } 216 217 _, err = startTelemetry(config) 218 if err != nil { 219 return err 220 } 221 222 svr, err := server.NewServer(addr, transport, app) 223 if err != nil { 224 return fmt.Errorf("error creating listener: %v", err) 225 } 226 227 svr.SetLogger(ctx.Logger.With("module", "abci-server")) 228 229 err = svr.Start() 230 if err != nil { 231 ostos.Exit(err.Error()) 232 } 233 234 defer func() { 235 if err = svr.Stop(); err != nil { 236 ostos.Exit(err.Error()) 237 } 238 }() 239 240 // Wait for SIGINT or SIGTERM signal 241 return WaitForQuitSignals() 242 } 243 244 func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.AppCreator) error { 245 cfg := ctx.Config 246 home := cfg.RootDir 247 var cpuProfileCleanup func() 248 249 if cpuProfile := ctx.Viper.GetString(flagCPUProfile); cpuProfile != "" { 250 f, err := os.Create(cpuProfile) 251 if err != nil { 252 return err 253 } 254 255 ctx.Logger.Info("starting CPU profiler", "profile", cpuProfile) 256 if err := pprof.StartCPUProfile(f); err != nil { 257 return err 258 } 259 260 cpuProfileCleanup = func() { 261 ctx.Logger.Info("stopping CPU profiler", "profile", cpuProfile) 262 pprof.StopCPUProfile() 263 f.Close() 264 } 265 } 266 267 traceWriterFile := ctx.Viper.GetString(flagTraceStore) 268 db, err := openDB(home) 269 if err != nil { 270 return err 271 } 272 273 traceWriter, err := openTraceWriter(traceWriterFile) 274 if err != nil { 275 return err 276 } 277 278 config, err := serverconfig.GetConfig(ctx.Viper) 279 if err != nil { 280 return err 281 } 282 283 if err := config.ValidateBasic(); err != nil { 284 ctx.Logger.Error("WARNING: The minimum-gas-prices config in app.toml is set to the empty string. " + 285 "This defaults to 0 in the current version, but will error in the next version " + 286 "(SDK v0.45). Please explicitly put the desired minimum-gas-prices in your app.toml.") 287 } 288 289 app := appCreator(ctx.Logger, db, traceWriter, ctx.Viper) 290 291 nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile()) 292 if err != nil { 293 return err 294 } 295 296 genDocProvider := node.DefaultGenesisDocProviderFunc(cfg) 297 298 var ( 299 ocNode *node.Node 300 gRPCOnly = ctx.Viper.GetBool(flagGRPCOnly) 301 ) 302 303 if gRPCOnly { 304 ctx.Logger.Info("starting node in gRPC only mode; Ostracon is disabled") 305 config.GRPC.Enable = true 306 } else { 307 ctx.Logger.Info("starting node with ABCI Ostracon in-process") 308 309 pv := genPvFileOnlyWhenKmsAddressEmpty(cfg) 310 311 ocNode, err = node.NewNode( 312 cfg, 313 pv, 314 nodeKey, 315 proxy.NewLocalClientCreator(app), 316 genDocProvider, 317 node.DefaultDBProvider, 318 node.DefaultMetricsProvider(cfg.Instrumentation), 319 ctx.Logger, 320 ) 321 if err != nil { 322 return err 323 } 324 ctx.Logger.Debug("initialization: ocNode created") 325 if err := ocNode.Start(); err != nil { 326 return err 327 } 328 ctx.Logger.Debug("initialization: ocNode started") 329 } 330 331 // Add the tx service to the gRPC router. We only need to register this 332 // service if API or gRPC is enabled, and avoid doing so in the general 333 // case, because it spawns a new local ostracon RPC client. 334 if (config.API.Enable || config.GRPC.Enable) && ocNode != nil { 335 clientCtx = clientCtx.WithClient(local.New(ocNode)) 336 337 app.RegisterTxService(clientCtx) 338 app.RegisterTendermintService(clientCtx) 339 340 if a, ok := app.(types.ApplicationQueryService); ok { 341 a.RegisterNodeService(clientCtx) 342 } 343 } 344 345 metrics, err := startTelemetry(config) 346 if err != nil { 347 return err 348 } 349 350 var apiSrv *api.Server 351 if config.API.Enable { 352 genDoc, err := genDocProvider() 353 if err != nil { 354 return err 355 } 356 357 clientCtx := clientCtx.WithHomeDir(home).WithChainID(genDoc.ChainID) 358 359 apiSrv = api.New(clientCtx, ctx.Logger.With("module", "api-server")) 360 app.RegisterAPIRoutes(apiSrv, config.API) 361 if config.Telemetry.Enabled { 362 apiSrv.SetTelemetry(metrics) 363 } 364 errCh := make(chan error) 365 366 go func() { 367 if err := apiSrv.Start(config); err != nil { 368 errCh <- err 369 } 370 }() 371 372 select { 373 case err := <-errCh: 374 return err 375 376 case <-time.After(types.ServerStartTime): // assume server started successfully 377 } 378 } 379 380 var ( 381 grpcSrv *grpc.Server 382 grpcWebSrv *http.Server 383 ) 384 385 if config.GRPC.Enable { 386 grpcSrv, err = servergrpc.StartGRPCServer(clientCtx, app, config.GRPC) 387 if err != nil { 388 return err 389 } 390 391 if config.GRPCWeb.Enable { 392 grpcWebSrv, err = servergrpc.StartGRPCWeb(grpcSrv, config) 393 if err != nil { 394 ctx.Logger.Error("failed to start grpc-web http server: ", err) 395 return err 396 } 397 } 398 } 399 400 // At this point it is safe to block the process if we're in gRPC only mode as 401 // we do not need to start Rosetta or handle any Tendermint related processes. 402 if gRPCOnly { 403 // wait for signal capture and gracefully return 404 return WaitForQuitSignals() 405 } 406 407 var rosettaSrv crgserver.Server 408 if config.Rosetta.Enable { 409 offlineMode := config.Rosetta.Offline 410 411 // If GRPC is not enabled rosetta cannot work in online mode, so it works in 412 // offline mode. 413 if !config.GRPC.Enable { 414 offlineMode = true 415 } 416 417 conf := &rosetta.Config{ 418 Blockchain: config.Rosetta.Blockchain, 419 Network: config.Rosetta.Network, 420 TendermintRPC: ctx.Config.RPC.ListenAddress, 421 GRPCEndpoint: config.GRPC.Address, 422 Addr: config.Rosetta.Address, 423 Retries: config.Rosetta.Retries, 424 Offline: offlineMode, 425 Codec: clientCtx.Codec.(*codec.ProtoCodec), 426 InterfaceRegistry: clientCtx.InterfaceRegistry, 427 } 428 429 rosettaSrv, err = rosetta.ServerFromConfig(conf) 430 if err != nil { 431 return err 432 } 433 434 errCh := make(chan error) 435 go func() { 436 if err := rosettaSrv.Start(); err != nil { 437 errCh <- err 438 } 439 }() 440 441 select { 442 case err := <-errCh: 443 return err 444 445 case <-time.After(types.ServerStartTime): // assume server started successfully 446 } 447 } 448 449 defer func() { 450 if ocNode.IsRunning() { 451 _ = ocNode.Stop() 452 } 453 454 if cpuProfileCleanup != nil { 455 cpuProfileCleanup() 456 } 457 458 if apiSrv != nil { 459 _ = apiSrv.Close() 460 } 461 462 if grpcSrv != nil { 463 grpcSrv.Stop() 464 if grpcWebSrv != nil { 465 grpcWebSrv.Close() 466 } 467 } 468 469 ctx.Logger.Info("exiting...") 470 }() 471 472 // wait for signal capture and gracefully return 473 return WaitForQuitSignals() 474 } 475 476 func genPvFileOnlyWhenKmsAddressEmpty(cfg *config.Config) *pvm.FilePV { 477 if len(strings.TrimSpace(cfg.PrivValidatorListenAddr)) == 0 { 478 return pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) 479 } 480 return nil 481 } 482 483 func startTelemetry(cfg serverconfig.Config) (*telemetry.Metrics, error) { 484 if !cfg.Telemetry.Enabled { 485 return nil, nil 486 } 487 return telemetry.New(cfg.Telemetry) 488 }