github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/toolbar/vote_reward/synchron/block_keeper.go (about)

     1  package synchron
     2  
     3  import (
     4  	"encoding/hex"
     5  
     6  	"github.com/jinzhu/gorm"
     7  
     8  	"github.com/bytom/bytom/errors"
     9  	"github.com/bytom/bytom/protocol/bc/types"
    10  	"github.com/bytom/bytom/toolbar/apinode"
    11  	"github.com/bytom/bytom/toolbar/common"
    12  	"github.com/bytom/bytom/toolbar/vote_reward/config"
    13  	"github.com/bytom/bytom/toolbar/vote_reward/database/orm"
    14  )
    15  
    16  var ErrInconsistentDB = errors.New("inconsistent db status")
    17  
    18  type ChainKeeper struct {
    19  	db           *gorm.DB
    20  	node         *apinode.Node
    21  	targetHeight uint64
    22  }
    23  
    24  func NewChainKeeper(db *gorm.DB, cfg *config.Config, targetHeight uint64) (*ChainKeeper, error) {
    25  	keeper := &ChainKeeper{
    26  		db:           db,
    27  		node:         apinode.NewNode(cfg.NodeIP),
    28  		targetHeight: targetHeight,
    29  	}
    30  
    31  	finalizedHeight, err := keeper.node.GetFinalizedHeight()
    32  	if err != nil {
    33  		return nil, errors.Wrap(err, "fail on get finalized height")
    34  	}
    35  
    36  	if targetHeight > finalizedHeight {
    37  		return nil, errors.New("reward end height is more than finalized height")
    38  	}
    39  
    40  	chainStatus := &orm.ChainStatus{}
    41  	if err := db.First(chainStatus).Error; err == nil {
    42  		return keeper, nil
    43  	} else if err != gorm.ErrRecordNotFound {
    44  		return nil, errors.Wrap(err, "fail on get chainStatus")
    45  	}
    46  
    47  	if err := keeper.initBlockState(); err != nil {
    48  		return nil, errors.Wrap(err, "fail on init chainStatus")
    49  	}
    50  	return keeper, nil
    51  }
    52  
    53  func (c *ChainKeeper) SyncBlock() error {
    54  	for {
    55  		chainStatus := &orm.ChainStatus{}
    56  		if err := c.db.First(chainStatus).Error; err != nil {
    57  			return errors.Wrap(err, "fail on syncBlock query chainStatus")
    58  		}
    59  
    60  		if chainStatus.BlockHeight >= c.targetHeight {
    61  			break
    62  		}
    63  
    64  		dbTX := c.db.Begin()
    65  		if err := c.syncChainStatus(dbTX, chainStatus); err != nil {
    66  			dbTX.Rollback()
    67  			return err
    68  		}
    69  
    70  		if err := dbTX.Commit().Error; err != nil {
    71  			return err
    72  		}
    73  	}
    74  	return nil
    75  }
    76  
    77  func (c *ChainKeeper) syncChainStatus(db *gorm.DB, chainStatus *orm.ChainStatus) error {
    78  	nextBlock, err := c.node.GetBlockByHeight(chainStatus.BlockHeight + 1)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	return c.AttachBlock(db, chainStatus, nextBlock)
    84  }
    85  
    86  func (c *ChainKeeper) AttachBlock(db *gorm.DB, chainStatus *orm.ChainStatus, block *types.Block) error {
    87  	for _, tx := range block.Transactions {
    88  		for _, input := range tx.Inputs {
    89  			if input.TypedInput.InputType() != types.VetoInputType {
    90  				continue
    91  			}
    92  
    93  			outputID, err := input.SpentOutputID()
    94  			if err != nil {
    95  				return err
    96  			}
    97  
    98  			result := db.Model(&orm.Utxo{}).Where(&orm.Utxo{OutputID: outputID.String()}).Update("veto_height", block.Height)
    99  			if err := result.Error; err != nil {
   100  				return err
   101  			} else if result.RowsAffected != 1 {
   102  				return ErrInconsistentDB
   103  			}
   104  		}
   105  
   106  		for i, output := range tx.Outputs {
   107  			voteOutput, ok := output.TypedOutput.(*types.VoteOutput)
   108  			if !ok {
   109  				continue
   110  			}
   111  
   112  			utxo := &orm.Utxo{
   113  				Xpub:        hex.EncodeToString(voteOutput.Vote),
   114  				VoteAddress: common.GetAddressFromControlProgram(output.ControlProgram),
   115  				VoteHeight:  block.Height,
   116  				VoteNum:     output.Amount,
   117  				OutputID:    tx.OutputID(i).String(),
   118  			}
   119  
   120  			if err := db.Save(utxo).Error; err != nil {
   121  				return err
   122  			}
   123  		}
   124  	}
   125  
   126  	return c.updateChainStatus(db, chainStatus, block)
   127  }
   128  
   129  func (c *ChainKeeper) initBlockState() error {
   130  	block, err := c.node.GetBlockByHeight(0)
   131  	if err != nil {
   132  		return errors.Wrap(err, "fail on get genenis block")
   133  	}
   134  
   135  	blockHash := block.Hash()
   136  	chainStatus := &orm.ChainStatus{
   137  		BlockHeight: block.Height,
   138  		BlockHash:   blockHash.String(),
   139  	}
   140  	return c.db.Save(chainStatus).Error
   141  }
   142  
   143  func (c *ChainKeeper) updateChainStatus(db *gorm.DB, chainStatus *orm.ChainStatus, block *types.Block) error {
   144  	blockHash := block.Hash()
   145  	result := db.Model(&orm.ChainStatus{}).Where(chainStatus).Updates(&orm.ChainStatus{
   146  		BlockHeight: block.Height,
   147  		BlockHash:   blockHash.String(),
   148  	})
   149  	if err := result.Error; err != nil {
   150  		return err
   151  	} else if result.RowsAffected != 1 {
   152  		return ErrInconsistentDB
   153  	}
   154  	return nil
   155  }