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 }