1 package settlementvotereward
7 "github.com/jinzhu/gorm"
9 "github.com/vapor/consensus"
10 "github.com/vapor/errors"
11 "github.com/vapor/protocol/bc/types"
12 "github.com/vapor/toolbar/apinode"
13 "github.com/vapor/toolbar/common"
14 "github.com/vapor/toolbar/vote_reward/config"
17 type voteResult struct {
22 type SettlementReward struct {
23 rewardCfg *config.RewardConfig
26 rewards map[string]uint64
31 func NewSettlementReward(db *gorm.DB, cfg *config.Config, startHeight, endHeight uint64) *SettlementReward {
32 return &SettlementReward{
34 rewardCfg: cfg.RewardConf,
35 node: apinode.NewNode(cfg.NodeIP),
36 rewards: make(map[string]uint64),
37 startHeight: startHeight,
42 func (s *SettlementReward) getVoteResultFromDB(height uint64) (voteResults []*voteResult, err error) {
43 query := s.db.Table("utxos").Select("vote_address, sum(vote_num) as vote_num")
44 query = query.Where("(veto_height >= ? or veto_height = 0) and vote_height <= ? and xpub = ?", height-consensus.ActiveNetParams.RoundVoteBlockNums+1, height-consensus.ActiveNetParams.RoundVoteBlockNums, s.rewardCfg.XPub)
45 query = query.Group("vote_address")
46 if err := query.Scan(&voteResults).Error; err != nil {
50 return voteResults, nil
53 func (s *SettlementReward) Settlement() error {
54 for height := s.startHeight + consensus.ActiveNetParams.RoundVoteBlockNums; height <= s.endHeight; height += consensus.ActiveNetParams.RoundVoteBlockNums {
55 coinbaseHeight := height + 1
56 totalReward, err := s.getCoinbaseReward(coinbaseHeight)
58 return errors.Wrapf(err, "get total reward at coinbase_height: %d", coinbaseHeight)
61 voteResults, err := s.getVoteResultFromDB(height)
66 s.calcVoterRewards(voteResults, totalReward)
70 return s.node.BatchSendBTM(s.rewardCfg.AccountID, s.rewardCfg.Password, s.rewards)
73 func (s *SettlementReward) getCoinbaseReward(height uint64) (uint64, error) {
74 block, err := s.node.GetBlockByHeight(height)
79 miningControl, err := common.GetControlProgramFromAddress(s.rewardCfg.MiningAddress)
84 for _, output := range block.Transactions[0].Outputs {
85 output, ok := output.TypedOutput.(*types.IntraChainOutput)
87 return 0, errors.New("Output type error")
90 if output.Amount == 0 {
94 if bytes.Equal(miningControl, output.ControlProgram) {
95 amount := big.NewInt(0).SetUint64(output.Amount)
96 rewardRatio := big.NewInt(0).SetUint64(s.rewardCfg.RewardRatio)
97 amount.Mul(amount, rewardRatio).Div(amount, big.NewInt(100))
99 return amount.Uint64(), nil
102 return 0, errors.New("No reward found")
105 func (s *SettlementReward) calcVoterRewards(voteResults []*voteResult, totalReward uint64) {
106 totalVoteNum := uint64(0)
107 for _, voteResult := range voteResults {
108 totalVoteNum += voteResult.VoteNum
111 for _, voteResult := range voteResults {
112 // voteNum / totalVoteNum * totalReward
113 voteNum := big.NewInt(0).SetUint64(voteResult.VoteNum)
114 total := big.NewInt(0).SetUint64(totalVoteNum)
115 reward := big.NewInt(0).SetUint64(totalReward)
117 amount := voteNum.Mul(voteNum, reward).Div(voteNum, total).Uint64()
120 s.rewards[voteResult.VoteAddress] += amount