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
25 nodes []config.VoteRewardConfig
27 overReadCH chan struct{}
28 voteResults map[string]*VoteResult
29 voterRewards map[string]*VoterReward
30 coinBaseReward map[string]uint64
34 func NewVote(nodes []config.VoteRewardConfig, ch chan VoteInfo, overReadCH chan struct{}, period uint64) *Vote {
38 overReadCH: overReadCH,
39 voteResults: make(map[string]*VoteResult),
40 voterRewards: make(map[string]*VoterReward),
41 coinBaseReward: make(map[string]uint64),
46 func (v *Vote) Start() {
47 // get coinbase reward
48 if err := v.getCoinbaseReward(); err != nil {
49 panic(errors.Wrap(err, "get coinbase reward"))
56 v.sendRewardTransaction()
59 func (v *Vote) getCoinbaseReward() error {
62 ip: fmt.Sprintf("http://%s:%d", v.nodes[0].Host, v.nodes[0].Port),
64 coinbaseTx, err := tx.GetCoinbaseTx(1200 * v.period)
68 for _, output := range coinbaseTx.Outputs {
69 voteOutput, ok := output.TypedOutput.(*types.IntraChainOutput)
71 return errors.New("Output type error")
73 address := common.GetAddressFromControlProgram(voteOutput.ControlProgram)
74 for _, node := range v.nodes {
75 if address == node.MiningAddress {
76 v.coinBaseReward[node.XPub] = voteOutput.Amount
84 func (v *Vote) countVotes() {
88 case voteInfo := <-v.ch:
89 bigBlockNum := big.NewInt(0).SetUint64(voteInfo.VoteBlockNum)
90 bigVoteNum := big.NewInt(0).SetUint64(voteInfo.VoteNum)
91 bigVoteNum = bigBlockNum.Mul(bigBlockNum, bigVoteNum)
93 if value, ok := v.voteResults[voteInfo.XPub]; ok {
94 value.Votes[voteInfo.Address] = bigVoteNum.Add(bigVoteNum, value.Votes[voteInfo.Address])
96 voteResult := &VoteResult{
97 Votes: make(map[string]*big.Int),
98 VoteTotal: big.NewInt(0),
101 voteResult.Votes[voteInfo.Address] = bigVoteNum
102 v.voteResults[voteInfo.XPub] = voteResult
105 v.voteResults[voteInfo.XPub].VoteTotal = bigVoteNum.Add(bigVoteNum, v.voteResults[voteInfo.XPub].VoteTotal)
112 func (v *Vote) countReward() {
113 for xpub, votes := range v.voteResults {
114 coinbaseReward, ok := v.coinBaseReward[xpub]
116 log.Errorf("%s doesn't have a coinbase reward \n", xpub)
119 for address, vote := range votes.Votes {
120 if value, ok := v.voterRewards[xpub]; ok {
121 coinBaseReward := big.NewInt(0).SetUint64(coinbaseReward)
122 value.rewards[address] = vote.Mul(vote, coinBaseReward).Div(vote, votes.VoteTotal)
124 reward := &VoterReward{
125 rewards: make(map[string]*big.Int),
127 coinBaseReward := big.NewInt(0).SetUint64(coinbaseReward)
128 reward.rewards[address] = vote.Mul(vote, coinBaseReward).Div(vote, votes.VoteTotal)
129 v.voterRewards[xpub] = reward
136 func (v *Vote) sendRewardTransaction() error {
137 for _, node := range v.nodes {
138 coinbaseReward, ok := v.coinBaseReward[node.XPub]
140 log.Errorf("%s doesn't have a coinbase reward \n", node.XPub)
144 if voterRewards, ok := v.voterRewards[node.XPub]; ok {
145 txID, err := v.sendReward(coinbaseReward, node, voterRewards)
149 log.Info("tx_id: ", txID)
156 func (v *Vote) sendReward(coinbaseReward uint64, node config.VoteRewardConfig, voterReward *VoterReward) (string, error) {
157 var outputAction string
159 inputAction := fmt.Sprintf(inputActionFmt, coinbaseReward, node.AccountID)
162 for address, amount := range voterReward.rewards {
163 outputAction += fmt.Sprintf(outputActionFmt, amount.Uint64(), address)
165 if index != len(voterReward.rewards) {
170 ip: fmt.Sprintf("http://%s:%d", node.Host, node.Port),
173 tmpl, err := tx.buildTx(inputAction, outputAction)
178 tmpl, err = tx.signTx(node.Passwd, *tmpl)
183 return tx.SubmitTx(tmpl.Transaction)