7 log "github.com/sirupsen/logrus"
9 "github.com/vapor/errors"
10 "github.com/vapor/protocol/bc/types"
11 "github.com/vapor/toolbar/common"
12 "github.com/vapor/toolbar/reward/config"
15 type voterReward struct {
16 rewards map[string]*big.Int
19 type voteResult struct {
20 Votes map[string]*big.Int
24 type coinBaseReward struct {
26 voteTotalReward *big.Int
30 nodes []config.VoteRewardConfig
32 overReadCH chan struct{}
34 voteResults map[string]*voteResult
35 voterRewards map[string]*voterReward
36 coinBaseReward map[string]*coinBaseReward
40 func NewVote(nodes []config.VoteRewardConfig, ch chan VoteInfo, overReadCH, quit chan struct{}, period uint64) *Vote {
44 overReadCH: overReadCH,
46 voteResults: make(map[string]*voteResult),
47 voterRewards: make(map[string]*voterReward),
48 coinBaseReward: make(map[string]*coinBaseReward),
53 func (v *Vote) Start() {
54 // get coinbase reward
55 if err := v.getCoinbaseReward(); err != nil {
56 panic(errors.Wrap(err, "get coinbase reward"))
63 if err := v.sendRewardTransaction(); err != nil {
68 func (v *Vote) getCoinbaseReward() error {
71 ip: fmt.Sprintf("http://%s:%d", v.nodes[0].Host, v.nodes[0].Port),
74 h, err := tx.GetCurrentHeight()
77 return errors.Wrap(err, "get block height")
79 if h >= 1200*v.period {
84 coinbaseTx, err := tx.GetCoinbaseTx(1200 * v.period)
89 for _, output := range coinbaseTx.Outputs {
90 output, ok := output.TypedOutput.(*types.IntraChainOutput)
93 return errors.New("Output type error")
95 address := common.GetAddressFromControlProgram(output.ControlProgram)
96 for _, node := range v.nodes {
97 if address == node.MiningAddress {
98 reward := &coinBaseReward{
99 totalReward: output.Amount,
101 ratioNumerator := big.NewInt(int64(node.RewardRatio))
102 ratioDenominator := big.NewInt(100)
103 coinBaseReward := big.NewInt(0).SetUint64(output.Amount)
104 reward.voteTotalReward = coinBaseReward.Mul(coinBaseReward, ratioNumerator).Div(coinBaseReward, ratioDenominator)
105 v.coinBaseReward[node.XPub] = reward
113 func (v *Vote) countVotes() {
117 case voteInfo := <-v.ch:
118 bigBlockNum := big.NewInt(0).SetUint64(voteInfo.VoteBlockNum)
119 bigVoteNum := big.NewInt(0).SetUint64(voteInfo.VoteNum)
120 bigVoteNum.Mul(bigVoteNum, bigBlockNum)
122 if value, ok := v.voteResults[voteInfo.XPub]; ok {
123 if vote, ok := value.Votes[voteInfo.Address]; ok {
124 vote.Add(vote, bigVoteNum)
126 value.Votes[voteInfo.Address] = bigVoteNum
129 voteResult := &voteResult{
130 Votes: make(map[string]*big.Int),
131 VoteTotal: big.NewInt(0),
134 voteResult.Votes[voteInfo.Address] = bigVoteNum
135 v.voteResults[voteInfo.XPub] = voteResult
137 voteTotal := v.voteResults[voteInfo.XPub].VoteTotal
138 voteTotal.Add(voteTotal, bigVoteNum)
139 v.voteResults[voteInfo.XPub].VoteTotal = voteTotal
146 func (v *Vote) countReward() {
147 for xpub, votes := range v.voteResults {
148 coinBaseReward, ok := v.coinBaseReward[xpub]
150 log.Errorf("%s doesn't have a coinbase reward \n", xpub)
154 for address, vote := range votes.Votes {
155 if value, ok := v.voterRewards[xpub]; ok {
156 mul := vote.Mul(vote, coinBaseReward.voteTotalReward)
157 amount := big.NewInt(0)
158 amount.Div(mul, votes.VoteTotal)
160 value.rewards[address] = amount
162 reward := &voterReward{
163 rewards: make(map[string]*big.Int),
166 mul := vote.Mul(vote, coinBaseReward.voteTotalReward)
167 amount := big.NewInt(0)
168 amount.Div(mul, votes.VoteTotal)
169 if amount.Uint64() > 0 {
170 reward.rewards[address] = amount
171 v.voterRewards[xpub] = reward
179 func (v *Vote) sendRewardTransaction() error {
180 for _, node := range v.nodes {
181 coinbaseReward, ok := v.coinBaseReward[node.XPub]
183 log.Errorf("%s doesn't have a coinbase reward \n", node.XPub)
187 if voterRewards, ok := v.voterRewards[node.XPub]; ok {
188 txID, err := v.sendReward(coinbaseReward.totalReward, node, voterRewards)
192 log.Info("tx_id: ", txID)
199 func (v *Vote) sendReward(coinbaseReward uint64, node config.VoteRewardConfig, voterReward *voterReward) (string, error) {
200 var outputAction string
202 inputAction := fmt.Sprintf(inputActionFmt, coinbaseReward, node.AccountID)
205 for address, amount := range voterReward.rewards {
207 outputAction += fmt.Sprintf(outputActionFmt, amount.Uint64(), address)
208 if index != len(voterReward.rewards) {
213 ip: fmt.Sprintf("http://%s:%d", node.Host, node.Port),
216 tmpl, err := tx.buildTx(inputAction, outputAction)
221 tmpl, err = tx.signTx(node.Passwd, *tmpl)
226 return tx.SubmitTx(tmpl.Transaction)