diff --git a/cmd/skywire-cli/commands/reward/root.go b/cmd/skywire-cli/commands/reward/root.go index 91eb1e72e3..374cc405c4 100644 --- a/cmd/skywire-cli/commands/reward/root.go +++ b/cmd/skywire-cli/commands/reward/root.go @@ -3,9 +3,14 @@ package clireward import ( "fmt" + "log" "os" + "sort" + "strconv" "strings" + "time" + "github.com/bitfield/script" coincipher "github.com/skycoin/skycoin/src/cipher" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -29,7 +34,6 @@ var ( ) func init() { - rewardCmd.Flags().SortFlags = false if defaultRewardAddress == "" { //default is genesis address for skycoin blockchain ; for testing @@ -196,3 +200,162 @@ func readRewardFile(cmdFlags *pflag.FlagSet) { output := fmt.Sprintf("Reward address file:\n %s\nreward address:\n %s\n", output, dat) internal.PrintOutput(cmdFlags, output, output) } + +const yearlyTotalRewards int = 408000 + +var ( + yearlyTotal int + surveyPath string + wdate = time.Now().AddDate(0, 0, -1).Format("2006-01-02") + utfile string + disallowArchitectures string +) + +type nodeinfo struct { + SkyAddr string `json:"skycoin_address"` + PK string `json:"public_key"` + Arch string `json:"go_arch"` + IPAddr string `json:"ip_address"` + Share float64 `json:"reward_share"` + Reward float64 `json:"reward_amount"` +} + +type ipCount struct { + IP string + Count int +} + +type rewardData struct { + SkyAddr string + Reward float64 +} + +func init() { + RootCmd.AddCommand(rewardCalcCmd) + rewardCalcCmd.Flags().SortFlags = false + rewardCalcCmd.Flags().StringVarP(&wdate, "date", "d", wdate, "date for which to calculate reward") + rewardCalcCmd.Flags().StringVarP(&disallowArchitectures, "noarch", "n", "amd64", "disallowed architectures, comma separated") + rewardCalcCmd.Flags().IntVarP(&yearlyTotal, "year", "y", yearlyTotalRewards, "yearly total rewards") + rewardCalcCmd.Flags().StringVarP(&utfile, "utfile", "u", "ut.txt", "uptime tracker data file") + rewardCalcCmd.Flags().StringVarP(&surveyPath, "path", "p", "./log_collecting", "path to the surveys ") +} + +var rewardCalcCmd = &cobra.Command{ + Use: "calc", + Short: "calculate rewards from uptime data & collected surveys", + Long: ` +Collect surveys: skywire-cli log +Fetch uptimes: skywire-cli ut > ut.txt`, + Run: func(cmd *cobra.Command, args []string) { + _, err := os.Stat(surveyPath) + if os.IsNotExist(err) { + log.Fatal("the path to the surveys does not exist\n", err, "\nfetch the surveys with:\n$ skywire-cli log") + } + _, err = os.Stat(utfile) + if os.IsNotExist(err) { + log.Fatal("uptime tracker data file not found\n", err, "\nfetch the uptime tracker data with:\n$ skywire-cli ut > ut.txt") + } + + archMap := make(map[string]struct{}) + for _, disallowedarch := range strings.Split(disallowArchitectures, ",") { + if disallowedarch != "" { + archMap[disallowedarch] = struct{}{} + } + } + res, _ := script.File(utfile).Match(strings.TrimRight(wdate, "\n")).Column(1).Slice() //nolint + var nodesInfos []nodeinfo + for _, pk := range res { + nodeInfo := fmt.Sprintf("%s/%s/node-info.json", surveyPath, pk) + ip, _ := script.File(nodeInfo).JQ(`."ip.skycoin.com".ip_address`).Replace(" ", "").Replace(`"`, "").String() //nolint + ip = strings.TrimRight(ip, "\n") + sky, _ := script.File(nodeInfo).JQ(".skycoin_address").Replace(" ", "").Replace(`"`, "").String() //nolint + sky = strings.TrimRight(sky, "\n") + arch, _ := script.File(nodeInfo).JQ(".go_arch").Replace(" ", "").Replace(`"`, "").String() //nolint + arch = strings.TrimRight(arch, "\n") + if _, disallowed := archMap[arch]; !disallowed && ip != "" && strings.Count(ip, ".") == 3 && sky != "" { + ni := nodeinfo{ + IPAddr: ip, + SkyAddr: sky, + PK: pk, + Arch: arch, + } + nodesInfos = append(nodesInfos, ni) + } + } + daysThisMonth := float64(time.Date(time.Now().Year(), time.Now().Month()+1, 0, 0, 0, 0, 0, time.UTC).Day()) + daysThisYear := float64(int(time.Date(time.Now().Year(), 12, 31, 23, 59, 59, 999999999, time.UTC).Sub(time.Date(time.Now().Year(), 1, 1, 0, 0, 0, 0, time.UTC)).Hours()) / 24) + monthReward := (float64(yearlyTotal) / daysThisYear) * daysThisMonth + dayReward := monthReward / daysThisMonth + wdate = strings.ReplaceAll(wdate, " ", "0") + fmt.Printf("date: %s\n", wdate) + fmt.Printf("days this month: %.4f\n", daysThisMonth) + fmt.Printf("days in the year: %.4f\n", daysThisYear) + fmt.Printf("this month's rewards: %.4f\n", monthReward) + fmt.Printf("reward total: %.4f\n", dayReward) + + uniqueIP, _ := script.Echo(func() string { //nolint + var inputStr strings.Builder + for _, ni := range nodesInfos { + inputStr.WriteString(fmt.Sprintf("%s\n", ni.IPAddr)) + } + return inputStr.String() + }()).Freq().String() //nolint + var ipCounts []ipCount + lines := strings.Split(uniqueIP, "\n") + for _, line := range lines { + if line != "" { + fields := strings.Fields(line) + if len(fields) == 2 { + count, _ := strconv.Atoi(fields[0]) //nolint + ipCounts = append(ipCounts, ipCount{ + IP: fields[1], + Count: count, + }) + } + } + } + totalValidShares := 0 + for _, ipCount := range ipCounts { + if ipCount.Count <= 8 { + totalValidShares += ipCount.Count + } else { + totalValidShares += 8 + } + } + fmt.Printf("Total valid shares: %d\n", totalValidShares) + + for i, ni := range nodesInfos { + for _, ipCount := range ipCounts { + if ni.IPAddr == ipCount.IP { + if ipCount.Count <= 8 { + nodesInfos[i].Share = 1.0 + } else { + nodesInfos[i].Share = 8.0 / float64(ipCount.Count) + } + break + } + } + nodesInfos[i].Reward = nodesInfos[i].Share / float64(totalValidShares) * dayReward + } + + fmt.Println("IP, Skycoin Address, Skywire Public Key, Architecture, Reward Shares, Reward $SKY amout") + for _, ni := range nodesInfos { + fmt.Printf("%s, %s, %s, %s, %4f, %4f\n", ni.IPAddr, ni.SkyAddr, ni.PK, ni.Arch, ni.Share, ni.Reward) + } + rewardSumBySkyAddr := make(map[string]float64) + for _, ni := range nodesInfos { + rewardSumBySkyAddr[ni.SkyAddr] += ni.Reward + } + var sortedSkyAddrs []rewardData + for skyAddr, rewardSum := range rewardSumBySkyAddr { + sortedSkyAddrs = append(sortedSkyAddrs, rewardData{SkyAddr: skyAddr, Reward: rewardSum}) + } + sort.Slice(sortedSkyAddrs, func(i, j int) bool { + return sortedSkyAddrs[i].Reward > sortedSkyAddrs[j].Reward + }) + fmt.Println("Skycoin Address, Reward Amount") + for _, skyAddrReward := range sortedSkyAddrs { + fmt.Printf("%s, %.4f\n", skyAddrReward.SkyAddr, skyAddrReward.Reward) + } + }, +}