github.com/PikeEcosystem/tendermint@v0.0.4/state/txindex/indexer_service.go (about)

     1  package txindex
     2  
     3  import (
     4  	"context"
     5  
     6  	abci "github.com/tendermint/tendermint/abci/types"
     7  
     8  	tmabci "github.com/PikeEcosystem/tendermint/abci/types"
     9  	"github.com/PikeEcosystem/tendermint/libs/service"
    10  	"github.com/PikeEcosystem/tendermint/state/indexer"
    11  	"github.com/PikeEcosystem/tendermint/types"
    12  )
    13  
    14  // XXX/TODO: These types should be moved to the indexer package.
    15  
    16  const (
    17  	subscriber = "IndexerService"
    18  )
    19  
    20  // IndexerService connects event bus, transaction and block indexers together in
    21  // order to index transactions and blocks coming from the event bus.
    22  type IndexerService struct {
    23  	service.BaseService
    24  
    25  	txIdxr    TxIndexer
    26  	blockIdxr indexer.BlockIndexer
    27  	eventBus  *types.EventBus
    28  }
    29  
    30  // NewIndexerService returns a new service instance.
    31  func NewIndexerService(
    32  	txIdxr TxIndexer,
    33  	blockIdxr indexer.BlockIndexer,
    34  	eventBus *types.EventBus,
    35  ) *IndexerService {
    36  
    37  	is := &IndexerService{txIdxr: txIdxr, blockIdxr: blockIdxr, eventBus: eventBus}
    38  	is.BaseService = *service.NewBaseService(nil, "IndexerService", is)
    39  	return is
    40  }
    41  
    42  // OnStart implements service.Service by subscribing for all transactions
    43  // and indexing them by events.
    44  func (is *IndexerService) OnStart() error {
    45  	// Use SubscribeUnbuffered here to ensure both subscriptions does not get
    46  	// canceled due to not pulling messages fast enough. Cause this might
    47  	// sometimes happen when there are no other subscribers.
    48  	blockHeadersSub, err := is.eventBus.SubscribeUnbuffered(
    49  		context.Background(),
    50  		subscriber,
    51  		types.EventQueryNewBlockHeader)
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	txsSub, err := is.eventBus.SubscribeUnbuffered(context.Background(), subscriber, types.EventQueryTx)
    57  	if err != nil {
    58  		return err
    59  	}
    60  
    61  	go func() {
    62  		for {
    63  			msg := <-blockHeadersSub.Out()
    64  			eventDataHeader := msg.Data().(types.EventDataNewBlockHeader)
    65  			height := eventDataHeader.Header.Height
    66  			batch := NewBatch(eventDataHeader.NumTxs)
    67  
    68  			for i := int64(0); i < eventDataHeader.NumTxs; i++ {
    69  				msg2 := <-txsSub.Out()
    70  				txResult := msg2.Data().(types.EventDataTx).TxResult
    71  
    72  				if err = batch.Add(&txResult); err != nil {
    73  					is.Logger.Error(
    74  						"failed to add tx to batch",
    75  						"height", height,
    76  						"index", txResult.Index,
    77  						"err", err,
    78  					)
    79  				}
    80  			}
    81  
    82  			if err := is.blockIdxr.Index(eventDataHeader); err != nil {
    83  				is.Logger.Error("failed to index block", "height", height, "err", err)
    84  			} else {
    85  				is.Logger.Info("indexed block", "height", height)
    86  			}
    87  
    88  			batch.Ops, err = DeduplicateBatch(batch.Ops, is.txIdxr)
    89  			if err != nil {
    90  				is.Logger.Error("deduplicate batch", "height", height)
    91  			}
    92  
    93  			if err = is.txIdxr.AddBatch(batch); err != nil {
    94  				is.Logger.Error("failed to index block txs", "height", height, "err", err)
    95  			} else {
    96  				is.Logger.Debug("indexed block txs", "height", height, "num_txs", eventDataHeader.NumTxs)
    97  			}
    98  		}
    99  	}()
   100  	return nil
   101  }
   102  
   103  // OnStop implements service.Service by unsubscribing from all transactions.
   104  func (is *IndexerService) OnStop() {
   105  	if is.eventBus.IsRunning() {
   106  		_ = is.eventBus.UnsubscribeAll(context.Background(), subscriber)
   107  	}
   108  }
   109  
   110  // DeduplicateBatch consider the case of duplicate txs.
   111  // if the current one under investigation is NOT OK, then we need to check
   112  // whether there's a previously indexed tx.
   113  // SKIP the current tx if the previously indexed record is found and successful.
   114  func DeduplicateBatch(ops []*abci.TxResult, txIdxr TxIndexer) ([]*abci.TxResult, error) {
   115  	result := make([]*abci.TxResult, 0, len(ops))
   116  
   117  	// keep track of successful txs in this block in order to suppress latter ones being indexed.
   118  	var successfulTxsInThisBlock = make(map[string]struct{})
   119  
   120  	for _, txResult := range ops {
   121  		hash := types.Tx(txResult.Tx).Hash()
   122  
   123  		if txResult.Result.IsOK() {
   124  			successfulTxsInThisBlock[string(hash)] = struct{}{}
   125  		} else {
   126  			// if it already appeared in current block and was successful, skip.
   127  			if _, found := successfulTxsInThisBlock[string(hash)]; found {
   128  				continue
   129  			}
   130  
   131  			// check if this tx hash is already indexed
   132  			old, err := txIdxr.Get(hash)
   133  
   134  			// if db op errored
   135  			// Not found is not an error
   136  			if err != nil {
   137  				return nil, err
   138  			}
   139  
   140  			// if it's already indexed in an older block and was successful, skip.
   141  			if old != nil && old.Result.Code == tmabci.CodeTypeOK {
   142  				continue
   143  			}
   144  		}
   145  
   146  		result = append(result, txResult)
   147  	}
   148  
   149  	return result, nil
   150  }