From 9f49a297b5f685cf1fd811a11feee69c6ec33e3a Mon Sep 17 00:00:00 2001 From: iczc Date: Fri, 26 Jul 2019 13:20:56 +0800 Subject: [PATCH] send standby node reward (#358) * send standby node reward * delete unused * fix error varname * add sample json * modify log module name --- cmd/consensusreward/main.go | 61 +++++++++++++++++++++++++ cmd/consensusreward/sample.json | 13 ++++++ toolbar/consensusreward/config/config.go | 32 ++++++++++++++ toolbar/consensusreward/consensusreward.go | 71 ++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+) create mode 100644 cmd/consensusreward/main.go create mode 100644 cmd/consensusreward/sample.json create mode 100644 toolbar/consensusreward/config/config.go create mode 100644 toolbar/consensusreward/consensusreward.go diff --git a/cmd/consensusreward/main.go b/cmd/consensusreward/main.go new file mode 100644 index 00000000..b7185116 --- /dev/null +++ b/cmd/consensusreward/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "time" + + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/tendermint/tmlibs/cli" + + "github.com/vapor/consensus" + "github.com/vapor/toolbar/consensusreward" + cfg "github.com/vapor/toolbar/consensusreward/config" +) + +const logModule = "consensusereward" + +var ( + rewardStartHeight uint64 + rewardEndHeight uint64 + configFile string +) + +var RootCmd = &cobra.Command{ + Use: "consensusreward", + Short: "distribution of reward.", + RunE: runReward, +} + +func init() { + RootCmd.Flags().Uint64Var(&rewardStartHeight, "reward_start_height", 0, "The starting height of the distributive income reward interval, It is a multiple of the dpos consensus cycle(1200). example: 1200") + RootCmd.Flags().Uint64Var(&rewardEndHeight, "reward_end_height", 0, "The end height of the distributive income reward interval, It is a multiple of the dpos consensus cycle(1200). example: 2400") + RootCmd.Flags().StringVar(&configFile, "config_file", "reward.json", "config file. default: reward.json") +} + +func runReward(cmd *cobra.Command, args []string) error { + startTime := time.Now() + config := &cfg.Config{} + if err := cfg.LoadConfigFile(configFile, config); err != nil { + log.WithFields(log.Fields{"module": logModule, "config": configFile, "error": err}).Fatal("Failded to load config file.") + } + if rewardStartHeight >= rewardEndHeight || rewardStartHeight%consensus.ActiveNetParams.RoundVoteBlockNums != 0 || rewardEndHeight%consensus.ActiveNetParams.RoundVoteBlockNums != 0 { + log.Fatal("Please check the height range, which must be multiple of the number of block rounds.") + } + + s := consensusreward.NewStandbyNodeReward(config, rewardStartHeight, rewardEndHeight) + if err := s.Settlement(); err != nil { + log.WithFields(log.Fields{"module": logModule, "error": err}).Fatal("Standby node rewards failure.") + } + + log.WithFields(log.Fields{ + "module": logModule, + "duration": time.Since(startTime), + }).Info("Standby node reward complete") + + return nil +} + +func main() { + cmd := cli.PrepareBaseCmd(RootCmd, "REWARD", "./") + cmd.Execute() +} diff --git a/cmd/consensusreward/sample.json b/cmd/consensusreward/sample.json new file mode 100644 index 00000000..e610f607 --- /dev/null +++ b/cmd/consensusreward/sample.json @@ -0,0 +1,13 @@ +{ + "node_ip": "http://127.0.0.1:9889", + "reward_config": { + "node": [ + { + "xpub": "0f8669abbd3cc0a167156188e428f940088d5b2f36bb3449df71d2bdc5e077814ea3f68628eef279ed435f51ee26cff00f8bd28fabfd500bedb2a9e369f5c825", + "address": "vp1ql4dakjlqhndda7xh0smz38j847e265fft3gz4u" + } + ], + "account_id": "", + "password": "" + } +} diff --git a/toolbar/consensusreward/config/config.go b/toolbar/consensusreward/config/config.go new file mode 100644 index 00000000..eec2cbe7 --- /dev/null +++ b/toolbar/consensusreward/config/config.go @@ -0,0 +1,32 @@ +package config + +import ( + "encoding/json" + "os" +) + +func LoadConfigFile(configFile string, config *Config) error { + file, err := os.Open(configFile) + if err != nil { + return err + } + defer file.Close() + + return json.NewDecoder(file).Decode(config) +} + +type Config struct { + NodeIP string `json:"node_ip"` + RewardConf *RewardConfig `json:"reward_config"` +} + +type RewardConfig struct { + Node []NodeConfig `json:"node"` + AccountID string `json:"account_id"` + Password string `json:"password"` +} + +type NodeConfig struct { + XPub string `json:"xpub"` + Address string `json:"address"` +} diff --git a/toolbar/consensusreward/consensusreward.go b/toolbar/consensusreward/consensusreward.go new file mode 100644 index 00000000..26494267 --- /dev/null +++ b/toolbar/consensusreward/consensusreward.go @@ -0,0 +1,71 @@ +package consensusreward + +import ( + "math/big" + + "github.com/vapor/consensus" + "github.com/vapor/errors" + "github.com/vapor/toolbar/apinode" + "github.com/vapor/toolbar/common" + "github.com/vapor/toolbar/consensusreward/config" +) + +const standbyNodesRewardForConsensusCycle = 7610350076 // 400000000000000 / (365 * 24 * 60 / (500 * 1200 / 1000 / 60)) + +type StandbyNodeReward struct { + cfg *config.Config + node *apinode.Node + xpubRewards map[string]uint64 + startHeight uint64 + endHeight uint64 +} + +func NewStandbyNodeReward(cfg *config.Config, startHeight, endHeight uint64) *StandbyNodeReward { + return &StandbyNodeReward{ + cfg: cfg, + node: apinode.NewNode(cfg.NodeIP), + xpubRewards: make(map[string]uint64), + startHeight: startHeight, + endHeight: endHeight, + } +} + +func (s *StandbyNodeReward) getStandbyNodeReward(height uint64) error { + voteInfos, err := s.node.GetVoteByHeight(height) + if err != nil { + return errors.Wrapf(err, "get alternative node reward") + } + + voteInfos = common.CalcStandByNodes(voteInfos) + totalVoteNum := uint64(0) + for _, voteInfo := range voteInfos { + totalVoteNum += voteInfo.VoteNum + } + + total := big.NewInt(0).SetUint64(totalVoteNum) + for _, voteInfo := range voteInfos { + amount := big.NewInt(0).SetUint64(standbyNodesRewardForConsensusCycle) + voteNum := big.NewInt(0).SetUint64(voteInfo.VoteNum) + s.xpubRewards[voteInfo.Vote] += amount.Mul(amount, voteNum).Div(amount, total).Uint64() + } + return nil +} + +func (s *StandbyNodeReward) Settlement() error { + for height := s.startHeight; height <= s.endHeight; height += consensus.ActiveNetParams.RoundVoteBlockNums { + if err := s.getStandbyNodeReward(height - consensus.ActiveNetParams.RoundVoteBlockNums); err != nil { + return err + } + } + rewards := map[string]uint64{} + for _, item := range s.cfg.RewardConf.Node { + if reward, ok := s.xpubRewards[item.XPub]; ok { + rewards[item.Address] = reward + } + } + + if len(rewards) == 0 { + return nil + } + return s.node.BatchSendBTM(s.cfg.RewardConf.AccountID, s.cfg.RewardConf.Password, rewards) +} -- 2.11.0