OSDN Git Service

send standby node reward (#358)
authoriczc <iczcalan@gmail.com>
Fri, 26 Jul 2019 05:20:56 +0000 (13:20 +0800)
committerPaladz <yzhu101@uottawa.ca>
Fri, 26 Jul 2019 05:20:56 +0000 (13:20 +0800)
* send standby node reward

* delete unused

* fix error varname

* add sample json

* modify log module name

cmd/consensusreward/main.go [new file with mode: 0644]
cmd/consensusreward/sample.json [new file with mode: 0644]
toolbar/consensusreward/config/config.go [new file with mode: 0644]
toolbar/consensusreward/consensusreward.go [new file with mode: 0644]

diff --git a/cmd/consensusreward/main.go b/cmd/consensusreward/main.go
new file mode 100644 (file)
index 0000000..b718511
--- /dev/null
@@ -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 (file)
index 0000000..e610f60
--- /dev/null
@@ -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 (file)
index 0000000..eec2cbe
--- /dev/null
@@ -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 (file)
index 0000000..2649426
--- /dev/null
@@ -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)
+}