You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
4.7 KiB

  1. package inspect
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "net"
  7. "github.com/tendermint/tendermint/config"
  8. "github.com/tendermint/tendermint/inspect/rpc"
  9. "github.com/tendermint/tendermint/libs/log"
  10. tmstrings "github.com/tendermint/tendermint/libs/strings"
  11. rpccore "github.com/tendermint/tendermint/rpc/core"
  12. "github.com/tendermint/tendermint/state"
  13. "github.com/tendermint/tendermint/state/indexer"
  14. "github.com/tendermint/tendermint/state/indexer/sink"
  15. "github.com/tendermint/tendermint/store"
  16. "github.com/tendermint/tendermint/types"
  17. "golang.org/x/sync/errgroup"
  18. )
  19. // Inspector manages an RPC service that exports methods to debug a failed node.
  20. // After a node shuts down due to a consensus failure, it will no longer start
  21. // up its state cannot easily be inspected. An Inspector value provides a similar interface
  22. // to the node, using the underlying Tendermint data stores, without bringing up
  23. // any other components. A caller can query the Inspector service to inspect the
  24. // persisted state and debug the failure.
  25. type Inspector struct {
  26. routes rpccore.RoutesMap
  27. config *config.RPCConfig
  28. indexerService *indexer.Service
  29. eventBus *types.EventBus
  30. logger log.Logger
  31. }
  32. // New returns an Inspector that serves RPC on the specified BlockStore and StateStore.
  33. // The Inspector type does not modify the state or block stores.
  34. // The sinks are used to enable block and transaction querying via the RPC server.
  35. // The caller is responsible for starting and stopping the Inspector service.
  36. ///
  37. //nolint:lll
  38. func New(cfg *config.RPCConfig, bs state.BlockStore, ss state.Store, es []indexer.EventSink, logger log.Logger) *Inspector {
  39. routes := rpc.Routes(*cfg, ss, bs, es, logger)
  40. eb := types.NewEventBus()
  41. eb.SetLogger(logger.With("module", "events"))
  42. is := indexer.NewIndexerService(es, eb)
  43. is.SetLogger(logger.With("module", "txindex"))
  44. return &Inspector{
  45. routes: routes,
  46. config: cfg,
  47. logger: logger,
  48. eventBus: eb,
  49. indexerService: is,
  50. }
  51. }
  52. // NewFromConfig constructs an Inspector using the values defined in the passed in config.
  53. func NewFromConfig(cfg *config.Config) (*Inspector, error) {
  54. bsDB, err := config.DefaultDBProvider(&config.DBContext{ID: "blockstore", Config: cfg})
  55. if err != nil {
  56. return nil, err
  57. }
  58. bs := store.NewBlockStore(bsDB)
  59. sDB, err := config.DefaultDBProvider(&config.DBContext{ID: "state", Config: cfg})
  60. if err != nil {
  61. return nil, err
  62. }
  63. genDoc, err := types.GenesisDocFromFile(cfg.GenesisFile())
  64. if err != nil {
  65. return nil, err
  66. }
  67. sinks, err := sink.EventSinksFromConfig(cfg, config.DefaultDBProvider, genDoc.ChainID)
  68. if err != nil {
  69. return nil, err
  70. }
  71. logger := log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo, false)
  72. ss := state.NewStore(sDB)
  73. return New(cfg.RPC, bs, ss, sinks, logger), nil
  74. }
  75. // Run starts the Inspector servers and blocks until the servers shut down. The passed
  76. // in context is used to control the lifecycle of the servers.
  77. func (ins *Inspector) Run(ctx context.Context) error {
  78. err := ins.eventBus.Start()
  79. if err != nil {
  80. return fmt.Errorf("error starting event bus: %s", err)
  81. }
  82. defer func() {
  83. err := ins.eventBus.Stop()
  84. if err != nil {
  85. ins.logger.Error("event bus stopped with error", "err", err)
  86. }
  87. }()
  88. err = ins.indexerService.Start()
  89. if err != nil {
  90. return fmt.Errorf("error starting indexer service: %s", err)
  91. }
  92. defer func() {
  93. err := ins.indexerService.Stop()
  94. if err != nil {
  95. ins.logger.Error("indexer service stopped with error", "err", err)
  96. }
  97. }()
  98. return startRPCServers(ctx, ins.config, ins.logger, ins.routes)
  99. }
  100. func startRPCServers(ctx context.Context, cfg *config.RPCConfig, logger log.Logger, routes rpccore.RoutesMap) error {
  101. g, tctx := errgroup.WithContext(ctx)
  102. listenAddrs := tmstrings.SplitAndTrimEmpty(cfg.ListenAddress, ",", " ")
  103. rh := rpc.Handler(cfg, routes, logger)
  104. for _, listenerAddr := range listenAddrs {
  105. server := rpc.Server{
  106. Logger: logger,
  107. Config: cfg,
  108. Handler: rh,
  109. Addr: listenerAddr,
  110. }
  111. if cfg.IsTLSEnabled() {
  112. keyFile := cfg.KeyFile()
  113. certFile := cfg.CertFile()
  114. listenerAddr := listenerAddr
  115. g.Go(func() error {
  116. logger.Info("RPC HTTPS server starting", "address", listenerAddr,
  117. "certfile", certFile, "keyfile", keyFile)
  118. err := server.ListenAndServeTLS(tctx, certFile, keyFile)
  119. if !errors.Is(err, net.ErrClosed) {
  120. return err
  121. }
  122. logger.Info("RPC HTTPS server stopped", "address", listenerAddr)
  123. return nil
  124. })
  125. } else {
  126. listenerAddr := listenerAddr
  127. g.Go(func() error {
  128. logger.Info("RPC HTTP server starting", "address", listenerAddr)
  129. err := server.ListenAndServe(tctx)
  130. if !errors.Is(err, net.ErrClosed) {
  131. return err
  132. }
  133. logger.Info("RPC HTTP server stopped", "address", listenerAddr)
  134. return nil
  135. })
  136. }
  137. }
  138. return g.Wait()
  139. }