OSDN Git Service

send standby node reward
authoriczc <iczcalan@gmail.com>
Thu, 25 Jul 2019 12:14:46 +0000 (20:14 +0800)
committericzc <iczcalan@gmail.com>
Thu, 25 Jul 2019 12:14:46 +0000 (20:14 +0800)
cmd/consensusreward/main.go [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..6c0ab73
--- /dev/null
@@ -0,0 +1,72 @@
+package main
+
+import (
+       "os"
+       "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 = "reward"
+
+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 := os.Stat(configFile); os.IsNotExist(err) {
+               if err := cfg.ExportConfigFile(configFile, cfg.Default()); err != nil {
+                       log.WithFields(log.Fields{"module": logModule, "config": configFile, "error": err}).Fatal("fail on export federation file")
+               }
+               return nil
+       }
+       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 err := consensus.InitActiveNetParams(config.ChainID); err != nil {
+               log.WithFields(log.Fields{"module": logModule, "error": err}).Fatal("Init ActiveNetParams.")
+       }
+       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/toolbar/consensusreward/config/config.go b/toolbar/consensusreward/config/config.go
new file mode 100644 (file)
index 0000000..dda9e9f
--- /dev/null
@@ -0,0 +1,60 @@
+package config
+
+import (
+       "bytes"
+       "encoding/json"
+       "io/ioutil"
+       "os"
+)
+
+func ExportConfigFile(configFile string, config *Config) error {
+       buf := new(bytes.Buffer)
+
+       encoder := json.NewEncoder(buf)
+       encoder.SetIndent("", "  ")
+       if err := encoder.Encode(config); err != nil {
+               return err
+       }
+
+       return ioutil.WriteFile(configFile, buf.Bytes(), 0644)
+}
+
+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"`
+       ChainID    string        `json:"chain_id"`
+       RewardConf *RewardConfig `json:"reward_config"`
+}
+
+func Default() *Config {
+       return &Config{
+               RewardConf: &RewardConfig{
+                       Node: []NodeConfig{
+                               {
+                                       XPub:    "",
+                                       Address: "",
+                               },
+                       },
+               },
+       }
+}
+
+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..8fd9045
--- /dev/null
@@ -0,0 +1,93 @@
+package consensusreward
+
+import (
+       "math/big"
+
+       "github.com/vapor/consensus"
+       "github.com/vapor/errors"
+       apinode "github.com/vapor/toolbar/apinode"
+       "github.com/vapor/toolbar/common"
+       "github.com/vapor/toolbar/consensusreward/config"
+)
+
+var (
+       errNotStandbyNode = errors.New("No Standby Node")
+       errNotRewardTx    = errors.New("No reward transaction")
+)
+
+const standbyNodesRewardForConsensusCycle = 7610350076 // 400000000000000 / (365 * 24 * 60 / (500 * 1200 / 1000 / 60))
+
+type StandbyNodeReward struct {
+       cfg         *config.Config
+       node        *apinode.Node
+       rewards     map[string]uint64
+       xpubAddress map[string]string
+       startHeight uint64
+       endHeight   uint64
+}
+
+func NewStandbyNodeReward(cfg *config.Config, startHeight, endHeight uint64) *StandbyNodeReward {
+       s := &StandbyNodeReward{
+               cfg:         cfg,
+               node:        apinode.NewNode(cfg.NodeIP),
+               rewards:     make(map[string]uint64),
+               xpubAddress: make(map[string]string),
+               startHeight: startHeight,
+               endHeight:   endHeight,
+       }
+       for _, item := range cfg.RewardConf.Node {
+               s.xpubAddress[item.XPub] = item.Address
+       }
+       return s
+}
+
+func (s *StandbyNodeReward) getStandbyNodeReward(height uint64) (map[string]uint64, error) {
+       voteInfos, err := s.node.GetVoteByHeight(height)
+       if err != nil {
+               return nil, errors.Wrapf(err, "get alternative node reward")
+       }
+       voteInfos = common.CalcStandByNodes(voteInfos)
+       if len(voteInfos) == 0 {
+               return nil, errNotStandbyNode
+       }
+       totalVoteNum := uint64(0)
+       for _, voteInfo := range voteInfos {
+               totalVoteNum += voteInfo.VoteNum
+       }
+       total := big.NewInt(0).SetUint64(totalVoteNum)
+       xpubReward := make(map[string]uint64)
+       for _, voteInfo := range voteInfos {
+               amount := big.NewInt(0).SetUint64(standbyNodesRewardForConsensusCycle)
+               voteNum := big.NewInt(0).SetUint64(voteInfo.VoteNum)
+               xpubReward[voteInfo.Vote] = amount.Mul(amount, voteNum).Div(amount, total).Uint64()
+       }
+       return xpubReward, nil
+}
+
+func (s *StandbyNodeReward) Settlement() error {
+       if err := s.calcAllReward(); err != nil {
+               return err
+       }
+       return s.node.BatchSendBTM(s.cfg.RewardConf.AccountID, s.cfg.RewardConf.Password, s.rewards)
+}
+
+func (s *StandbyNodeReward) calcAllReward() error {
+       for height := s.startHeight; height <= s.endHeight; height += consensus.ActiveNetParams.RoundVoteBlockNums {
+               xpubReward, err := s.getStandbyNodeReward(height - consensus.ActiveNetParams.RoundVoteBlockNums)
+               if err == errNotStandbyNode {
+                       continue
+               }
+               if err != nil {
+                       return err
+               }
+               for xpub, amount := range xpubReward {
+                       if address, ok := s.xpubAddress[xpub]; ok {
+                               s.rewards[address] += amount
+                       }
+               }
+       }
+       if len(s.rewards) == 0 {
+               return errNotRewardTx
+       }
+       return nil
+}