github.com/aakash4dev/cometbft@v0.38.2/inspect/inspect.go (about)

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