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.

148 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/internal/inspect/rpc"
  9. rpccore "github.com/tendermint/tendermint/internal/rpc/core"
  10. "github.com/tendermint/tendermint/internal/state"
  11. "github.com/tendermint/tendermint/internal/state/indexer"
  12. "github.com/tendermint/tendermint/internal/state/indexer/sink"
  13. "github.com/tendermint/tendermint/internal/store"
  14. "github.com/tendermint/tendermint/libs/log"
  15. tmstrings "github.com/tendermint/tendermint/libs/strings"
  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(logger log.Logger, 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. ss := state.NewStore(sDB)
  72. return New(cfg.RPC, bs, ss, sinks, logger), nil
  73. }
  74. // Run starts the Inspector servers and blocks until the servers shut down. The passed
  75. // in context is used to control the lifecycle of the servers.
  76. func (ins *Inspector) Run(ctx context.Context) error {
  77. err := ins.eventBus.Start()
  78. if err != nil {
  79. return fmt.Errorf("error starting event bus: %s", err)
  80. }
  81. defer func() {
  82. err := ins.eventBus.Stop()
  83. if err != nil {
  84. ins.logger.Error("event bus stopped with error", "err", err)
  85. }
  86. }()
  87. err = ins.indexerService.Start()
  88. if err != nil {
  89. return fmt.Errorf("error starting indexer service: %s", err)
  90. }
  91. defer func() {
  92. err := ins.indexerService.Stop()
  93. if err != nil {
  94. ins.logger.Error("indexer service stopped with error", "err", err)
  95. }
  96. }()
  97. return startRPCServers(ctx, ins.config, ins.logger, ins.routes)
  98. }
  99. func startRPCServers(ctx context.Context, cfg *config.RPCConfig, logger log.Logger, routes rpccore.RoutesMap) error {
  100. g, tctx := errgroup.WithContext(ctx)
  101. listenAddrs := tmstrings.SplitAndTrimEmpty(cfg.ListenAddress, ",", " ")
  102. rh := rpc.Handler(cfg, routes, logger)
  103. for _, listenerAddr := range listenAddrs {
  104. server := rpc.Server{
  105. Logger: logger,
  106. Config: cfg,
  107. Handler: rh,
  108. Addr: listenerAddr,
  109. }
  110. if cfg.IsTLSEnabled() {
  111. keyFile := cfg.KeyFile()
  112. certFile := cfg.CertFile()
  113. listenerAddr := listenerAddr
  114. g.Go(func() error {
  115. logger.Info("RPC HTTPS server starting", "address", listenerAddr,
  116. "certfile", certFile, "keyfile", keyFile)
  117. err := server.ListenAndServeTLS(tctx, certFile, keyFile)
  118. if !errors.Is(err, net.ErrClosed) {
  119. return err
  120. }
  121. logger.Info("RPC HTTPS server stopped", "address", listenerAddr)
  122. return nil
  123. })
  124. } else {
  125. listenerAddr := listenerAddr
  126. g.Go(func() error {
  127. logger.Info("RPC HTTP server starting", "address", listenerAddr)
  128. err := server.ListenAndServe(tctx)
  129. if !errors.Is(err, net.ErrClosed) {
  130. return err
  131. }
  132. logger.Info("RPC HTTP server stopped", "address", listenerAddr)
  133. return nil
  134. })
  135. }
  136. }
  137. return g.Wait()
  138. }