github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/cmd/cometbft/commands/reindex_event.go (about)

     1  package commands
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  
     8  	dbm "github.com/cometbft/cometbft-db"
     9  	"github.com/spf13/cobra"
    10  
    11  	abcitypes "github.com/badrootd/celestia-core/abci/types"
    12  	cmtcfg "github.com/badrootd/celestia-core/config"
    13  	"github.com/badrootd/celestia-core/libs/progressbar"
    14  	"github.com/badrootd/celestia-core/state"
    15  	"github.com/badrootd/celestia-core/state/indexer"
    16  	blockidxkv "github.com/badrootd/celestia-core/state/indexer/block/kv"
    17  	"github.com/badrootd/celestia-core/state/indexer/sink/psql"
    18  	"github.com/badrootd/celestia-core/state/txindex"
    19  	"github.com/badrootd/celestia-core/state/txindex/kv"
    20  	"github.com/badrootd/celestia-core/types"
    21  )
    22  
    23  const (
    24  	reindexFailed = "event re-index failed: "
    25  )
    26  
    27  var (
    28  	ErrHeightNotAvailable = errors.New("height is not available")
    29  	ErrInvalidRequest     = errors.New("invalid request")
    30  )
    31  
    32  // ReIndexEventCmd constructs a command to re-index events in a block height interval.
    33  var ReIndexEventCmd = &cobra.Command{
    34  	Use:     "reindex-event",
    35  	Aliases: []string{"reindex_event"},
    36  	Short:   "Re-index events to the event store backends",
    37  	Long: `
    38  reindex-event is an offline tooling to re-index block and tx events to the eventsinks.
    39  You can run this command when the event store backend dropped/disconnected or you want to
    40  replace the backend. The default start-height is 0, meaning the tooling will start
    41  reindex from the base block height(inclusive); and the default end-height is 0, meaning
    42  the tooling will reindex until the latest block height(inclusive). User can omit
    43  either or both arguments.
    44  
    45  Note: This operation requires ABCIResponses. Do not set DiscardABCIResponses to true if you
    46  want to use this command.
    47  	`,
    48  	Example: `
    49  	cometbft reindex-event
    50  	cometbft reindex-event --start-height 2
    51  	cometbft reindex-event --end-height 10
    52  	cometbft reindex-event --start-height 2 --end-height 10
    53  	`,
    54  	Run: func(cmd *cobra.Command, args []string) {
    55  		bs, ss, err := loadStateAndBlockStore(config)
    56  		if err != nil {
    57  			fmt.Println(reindexFailed, err)
    58  			return
    59  		}
    60  
    61  		if err := checkValidHeight(bs); err != nil {
    62  			fmt.Println(reindexFailed, err)
    63  			return
    64  		}
    65  
    66  		bi, ti, err := loadEventSinks(config)
    67  		if err != nil {
    68  			fmt.Println(reindexFailed, err)
    69  			return
    70  		}
    71  
    72  		riArgs := eventReIndexArgs{
    73  			startHeight:  startHeight,
    74  			endHeight:    endHeight,
    75  			blockIndexer: bi,
    76  			txIndexer:    ti,
    77  			blockStore:   bs,
    78  			stateStore:   ss,
    79  		}
    80  		if err := eventReIndex(cmd, riArgs); err != nil {
    81  			panic(fmt.Errorf("%s: %w", reindexFailed, err))
    82  		}
    83  
    84  		fmt.Println("event re-index finished")
    85  	},
    86  }
    87  
    88  var (
    89  	startHeight int64
    90  	endHeight   int64
    91  )
    92  
    93  func init() {
    94  	ReIndexEventCmd.Flags().Int64Var(&startHeight, "start-height", 0, "the block height would like to start for re-index")
    95  	ReIndexEventCmd.Flags().Int64Var(&endHeight, "end-height", 0, "the block height would like to finish for re-index")
    96  }
    97  
    98  func loadEventSinks(cfg *cmtcfg.Config) (indexer.BlockIndexer, txindex.TxIndexer, error) {
    99  	switch strings.ToLower(cfg.TxIndex.Indexer) {
   100  	case "null":
   101  		return nil, nil, errors.New("found null event sink, please check the tx-index section in the config.toml")
   102  	case "psql":
   103  		conn := cfg.TxIndex.PsqlConn
   104  		if conn == "" {
   105  			return nil, nil, errors.New("the psql connection settings cannot be empty")
   106  		}
   107  		es, err := psql.NewEventSink(conn, cfg.ChainID())
   108  		if err != nil {
   109  			return nil, nil, err
   110  		}
   111  		return es.BlockIndexer(), es.TxIndexer(), nil
   112  	case "kv":
   113  		store, err := dbm.NewDB("tx_index", dbm.BackendType(cfg.DBBackend), cfg.DBDir())
   114  		if err != nil {
   115  			return nil, nil, err
   116  		}
   117  
   118  		txIndexer := kv.NewTxIndex(store)
   119  		blockIndexer := blockidxkv.New(dbm.NewPrefixDB(store, []byte("block_events")))
   120  		return blockIndexer, txIndexer, nil
   121  	default:
   122  		return nil, nil, fmt.Errorf("unsupported event sink type: %s", cfg.TxIndex.Indexer)
   123  	}
   124  }
   125  
   126  type eventReIndexArgs struct {
   127  	startHeight  int64
   128  	endHeight    int64
   129  	blockIndexer indexer.BlockIndexer
   130  	txIndexer    txindex.TxIndexer
   131  	blockStore   state.BlockStore
   132  	stateStore   state.Store
   133  }
   134  
   135  func eventReIndex(cmd *cobra.Command, args eventReIndexArgs) error {
   136  	var bar progressbar.Bar
   137  	bar.NewOption(args.startHeight-1, args.endHeight)
   138  
   139  	fmt.Println("start re-indexing events:")
   140  	defer bar.Finish()
   141  	for i := args.startHeight; i <= args.endHeight; i++ {
   142  		select {
   143  		case <-cmd.Context().Done():
   144  			return fmt.Errorf("event re-index terminated at height %d: %w", i, cmd.Context().Err())
   145  		default:
   146  			b := args.blockStore.LoadBlock(i)
   147  			if b == nil {
   148  				return fmt.Errorf("not able to load block at height %d from the blockstore", i)
   149  			}
   150  
   151  			r, err := args.stateStore.LoadABCIResponses(i)
   152  			if err != nil {
   153  				return fmt.Errorf("not able to load ABCI Response at height %d from the statestore", i)
   154  			}
   155  
   156  			e := types.EventDataNewBlockHeader{
   157  				Header:           b.Header,
   158  				NumTxs:           int64(len(b.Txs)),
   159  				ResultBeginBlock: *r.BeginBlock,
   160  				ResultEndBlock:   *r.EndBlock,
   161  			}
   162  
   163  			var batch *txindex.Batch
   164  			if e.NumTxs > 0 {
   165  				batch = txindex.NewBatch(e.NumTxs)
   166  
   167  				for i := range b.Data.Txs {
   168  					tr := abcitypes.TxResult{
   169  						Height: b.Height,
   170  						Index:  uint32(i),
   171  						Tx:     b.Data.Txs[i],
   172  						Result: *(r.DeliverTxs[i]),
   173  					}
   174  
   175  					if err = batch.Add(&tr); err != nil {
   176  						return fmt.Errorf("adding tx to batch: %w", err)
   177  					}
   178  				}
   179  
   180  				if err := args.txIndexer.AddBatch(batch); err != nil {
   181  					return fmt.Errorf("tx event re-index at height %d failed: %w", i, err)
   182  				}
   183  			}
   184  
   185  			if err := args.blockIndexer.Index(e); err != nil {
   186  				return fmt.Errorf("block event re-index at height %d failed: %w", i, err)
   187  			}
   188  		}
   189  
   190  		bar.Play(i)
   191  	}
   192  
   193  	return nil
   194  }
   195  
   196  func checkValidHeight(bs state.BlockStore) error {
   197  	base := bs.Base()
   198  
   199  	if startHeight == 0 {
   200  		startHeight = base
   201  		fmt.Printf("set the start block height to the base height of the blockstore %d \n", base)
   202  	}
   203  
   204  	if startHeight < base {
   205  		return fmt.Errorf("%s (requested start height: %d, base height: %d)",
   206  			ErrHeightNotAvailable, startHeight, base)
   207  	}
   208  
   209  	height := bs.Height()
   210  
   211  	if startHeight > height {
   212  		return fmt.Errorf(
   213  			"%s (requested start height: %d, store height: %d)", ErrHeightNotAvailable, startHeight, height)
   214  	}
   215  
   216  	if endHeight == 0 || endHeight > height {
   217  		endHeight = height
   218  		fmt.Printf("set the end block height to the latest height of the blockstore %d \n", height)
   219  	}
   220  
   221  	if endHeight < base {
   222  		return fmt.Errorf(
   223  			"%s (requested end height: %d, base height: %d)", ErrHeightNotAvailable, endHeight, base)
   224  	}
   225  
   226  	if endHeight < startHeight {
   227  		return fmt.Errorf(
   228  			"%s (requested the end height: %d is less than the start height: %d)",
   229  			ErrInvalidRequest, startHeight, endHeight)
   230  	}
   231  
   232  	return nil
   233  }