github.com/iotexproject/iotex-core@v1.14.1-rc1/blockchain/blockdao/blockindexer.go (about)

     1  // Copyright (c) 2019 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package blockdao
     7  
     8  import (
     9  	"context"
    10  	"time"
    11  
    12  	"github.com/pkg/errors"
    13  	"go.uber.org/zap"
    14  
    15  	"github.com/iotexproject/iotex-core/action/protocol"
    16  	"github.com/iotexproject/iotex-core/blockchain/block"
    17  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    18  	"github.com/iotexproject/iotex-core/pkg/log"
    19  )
    20  
    21  type (
    22  	// BlockIndexer defines an interface to accept block to build index
    23  	BlockIndexer interface {
    24  		Start(ctx context.Context) error
    25  		Stop(ctx context.Context) error
    26  		Height() (uint64, error)
    27  		PutBlock(context.Context, *block.Block) error
    28  		DeleteTipBlock(context.Context, *block.Block) error
    29  	}
    30  
    31  	// BlockIndexerWithStart defines an interface to accept block to build index from a start height
    32  	BlockIndexerWithStart interface {
    33  		BlockIndexer
    34  		// StartHeight returns the start height of the indexer
    35  		StartHeight() uint64
    36  	}
    37  
    38  	// BlockIndexerChecker defines a checker of block indexer
    39  	BlockIndexerChecker struct {
    40  		dao BlockDAO
    41  	}
    42  )
    43  
    44  // NewBlockIndexerChecker creates a new block indexer checker
    45  func NewBlockIndexerChecker(dao BlockDAO) *BlockIndexerChecker {
    46  	return &BlockIndexerChecker{dao: dao}
    47  }
    48  
    49  // CheckIndexer checks a block indexer against block dao
    50  func (bic *BlockIndexerChecker) CheckIndexer(ctx context.Context, indexer BlockIndexer, targetHeight uint64, progressReporter func(uint64)) error {
    51  	bcCtx, ok := protocol.GetBlockchainCtx(ctx)
    52  	if !ok {
    53  		return errors.New("failed to find blockchain ctx")
    54  	}
    55  	g, ok := genesis.ExtractGenesisContext(ctx)
    56  	if !ok {
    57  		return errors.New("failed to find genesis ctx")
    58  	}
    59  	tipHeight, err := indexer.Height()
    60  	if err != nil {
    61  		return err
    62  	}
    63  	daoTip, err := bic.dao.Height()
    64  	if err != nil {
    65  		return err
    66  	}
    67  	if tipHeight > daoTip {
    68  		return errors.New("indexer tip height cannot by higher than dao tip height")
    69  	}
    70  	tipBlk, err := bic.dao.GetBlockByHeight(tipHeight)
    71  	if err != nil {
    72  		return err
    73  	}
    74  	if targetHeight == 0 || targetHeight > daoTip {
    75  		targetHeight = daoTip
    76  	}
    77  	startHeight := tipHeight + 1
    78  	if indexerWS, ok := indexer.(BlockIndexerWithStart); ok {
    79  		indexStartHeight := indexerWS.StartHeight()
    80  		if indexStartHeight > startHeight {
    81  			startHeight = indexStartHeight
    82  		}
    83  	}
    84  	for i := startHeight; i <= targetHeight; i++ {
    85  		// ternimate if context is done
    86  		if err := ctx.Err(); err != nil {
    87  			return errors.Wrap(err, "terminate the indexer checking")
    88  		}
    89  		blk, err := bic.dao.GetBlockByHeight(i)
    90  		if err != nil {
    91  			return err
    92  		}
    93  		if blk.Receipts == nil {
    94  			blk.Receipts, err = bic.dao.GetReceipts(i)
    95  			if err != nil {
    96  				return err
    97  			}
    98  		}
    99  		producer := blk.PublicKey().Address()
   100  		if producer == nil {
   101  			return errors.New("failed to get address")
   102  		}
   103  		bcCtx.Tip.Height = tipBlk.Height()
   104  		if bcCtx.Tip.Height > 0 {
   105  			bcCtx.Tip.Hash = tipBlk.HashHeader()
   106  			bcCtx.Tip.Timestamp = tipBlk.Timestamp()
   107  		} else {
   108  			bcCtx.Tip.Hash = g.Hash()
   109  			bcCtx.Tip.Timestamp = time.Unix(g.Timestamp, 0)
   110  		}
   111  		for {
   112  			if err = indexer.PutBlock(protocol.WithBlockCtx(
   113  				protocol.WithBlockchainCtx(ctx, bcCtx),
   114  				protocol.BlockCtx{
   115  					BlockHeight:    i,
   116  					BlockTimeStamp: blk.Timestamp(),
   117  					Producer:       producer,
   118  					GasLimit:       g.BlockGasLimitByHeight(i),
   119  				},
   120  			), blk); err == nil {
   121  				break
   122  			}
   123  			if i < g.HawaiiBlockHeight && errors.Cause(err) == block.ErrDeltaStateMismatch {
   124  				log.L().Info("delta state mismatch", zap.Uint64("block", i))
   125  				continue
   126  			}
   127  			return err
   128  		}
   129  		if progressReporter != nil {
   130  			progressReporter(i)
   131  		}
   132  		tipBlk = blk
   133  	}
   134  	return nil
   135  }