6 "github.com/jinzhu/gorm"
7 log "github.com/sirupsen/logrus"
9 "github.com/vapor/errors"
10 "github.com/vapor/protocol/bc"
11 "github.com/vapor/protocol/bc/types"
12 "github.com/vapor/toolbar/common"
13 "github.com/vapor/toolbar/common/service"
14 "github.com/vapor/toolbar/reward/config"
15 "github.com/vapor/toolbar/reward/database/orm"
18 type ChainKeeper struct {
25 func NewChainKeeper(db *gorm.DB, cfg *config.Config, syncHeight uint64) (*ChainKeeper, error) {
26 keeper := &ChainKeeper{
29 node: service.NewNode(cfg.Chain.Upstream),
30 syncHeight: syncHeight,
33 blockState := &orm.BlockState{}
34 if err := db.First(blockState).Error; err == gorm.ErrRecordNotFound {
35 blockStr, _, err := keeper.node.GetBlockByHeight(0)
37 return nil, errors.Wrap(err, "Failed to get genenis block")
39 block := &types.Block{}
40 if err := block.UnmarshalText([]byte(blockStr)); err != nil {
41 return nil, errors.Wrap(err, "unmarshal block")
43 if err := keeper.initBlockState(db, block); err != nil {
44 return nil, errors.Wrap(err, "Failed to insert blockState")
46 } else if err != nil {
47 return nil, errors.Wrap(err, "Failed to get blockState")
53 func (c *ChainKeeper) Start() error {
55 blockState := &orm.BlockState{}
56 if c.db.First(blockState).RecordNotFound() {
57 return errors.New("The query blockState record is empty empty on process block")
60 if blockState.Height >= c.syncHeight {
64 if err := c.syncBlock(blockState); err != nil {
71 func (c *ChainKeeper) syncBlock(blockState *orm.BlockState) error {
72 height, err := c.node.GetBlockCount()
77 if height == blockState.Height {
81 nextBlockStr, txStatus, err := c.node.GetBlockByHeight(blockState.Height + 1)
86 nextBlock := &types.Block{}
87 if err := nextBlock.UnmarshalText([]byte(nextBlockStr)); err != nil {
88 return errors.New("Unmarshal nextBlock")
91 // Normal case, the previous hash of next block equals to the hash of current block,
92 // just sync to database directly.
93 if nextBlock.PreviousBlockHash.String() == blockState.BlockHash {
94 return c.AttachBlock(nextBlock, txStatus)
97 log.WithField("block height", blockState.Height).Debug("the prev hash of remote is not equals the hash of current best block, must rollback")
98 currentBlockStr, txStatus, err := c.node.GetBlockByHash(blockState.BlockHash)
103 currentBlock := &types.Block{}
104 if err := nextBlock.UnmarshalText([]byte(currentBlockStr)); err != nil {
105 return errors.New("Unmarshal currentBlock")
108 return c.DetachBlock(currentBlock, txStatus)
111 func (c *ChainKeeper) AttachBlock(block *types.Block, txStatus *bc.TransactionStatus) error {
112 ormDB := c.db.Begin()
113 for pos, tx := range block.Transactions {
114 statusFail, err := txStatus.GetStatus(pos)
120 log.WithFields(log.Fields{"block height": block.Height, "statusFail": statusFail}).Debug("AttachBlock")
124 for _, input := range tx.Inputs {
125 vetoInput, ok := input.TypedInput.(*types.VetoInput)
130 outputID, err := input.SpentOutputID()
135 VoterAddress: common.GetAddressFromControlProgram(vetoInput.ControlProgram),
136 OutputID: outputID.String(),
139 db := ormDB.Model(&orm.Utxo{}).Where(utxo).Update("veto_height", block.Height)
140 if err := db.Error; err != nil {
145 if db.RowsAffected != 1 {
147 return ErrInconsistentDB
152 for index, output := range tx.Outputs {
153 voteOutput, ok := output.TypedOutput.(*types.VoteOutput)
157 pubkey := hex.EncodeToString(voteOutput.Vote)
158 outputID := tx.OutputID(index)
161 VoterAddress: common.GetAddressFromControlProgram(voteOutput.ControlProgram),
162 VoteHeight: block.Height,
163 VoteNum: voteOutput.Amount,
165 OutputID: outputID.String(),
168 if err := ormDB.Save(utxo).Error; err != nil {
175 if err := c.updateBlockState(ormDB, block); err != nil {
180 return ormDB.Commit().Error
183 func (c *ChainKeeper) DetachBlock(block *types.Block, txStatus *bc.TransactionStatus) error {
184 ormDB := c.db.Begin()
187 VoteHeight: block.Height,
190 if err := ormDB.Where(utxo).Delete(&orm.Utxo{}).Error; err != nil {
196 VetoHeight: block.Height,
200 if err := ormDB.Where(utxo).Update("veto_height", 0).Error; err != nil {
205 preBlockStr, _, err := c.node.GetBlockByHeight(block.Height + 1)
210 preBlock := &types.Block{}
211 if err := preBlock.UnmarshalText([]byte(preBlockStr)); err != nil {
212 return errors.New("Unmarshal preBlock")
215 if err := c.updateBlockState(ormDB, preBlock); err != nil {
220 return ormDB.Commit().Error
223 func (c *ChainKeeper) initBlockState(db *gorm.DB, block *types.Block) error {
224 blockHash := block.Hash()
225 blockState := &orm.BlockState{
226 Height: block.Height,
227 BlockHash: blockHash.String(),
230 return db.Save(blockState).Error
233 func (c *ChainKeeper) updateBlockState(db *gorm.DB, block *types.Block) error {
235 blockHash := block.Hash()
236 blockState := &orm.BlockState{
237 Height: block.Height,
238 BlockHash: blockHash.String(),
241 u := db.Model(&orm.BlockState{}).Updates(blockState)
243 if err := u.Error; err != nil {
247 if u.RowsAffected != 1 {
248 return ErrInconsistentDB