Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ioctl] Refactor nodereward command in new ioctl #3416

Merged
merged 12 commits into from
Jun 25, 2022
239 changes: 120 additions & 119 deletions ioctl/newcmd/node/nodereward.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2019 IoTeX Foundation
// Copyright (c) 2022 IoTeX Foundation
// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no
// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent
// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache
Expand All @@ -8,165 +8,166 @@ package node

import (
"context"
"fmt"
"math/big"

"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
"github.com/iotexproject/iotex-proto/golang/iotexapi"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"google.golang.org/grpc/status"

LuckyPigeon marked this conversation as resolved.
Show resolved Hide resolved
"github.com/iotexproject/iotex-proto/golang/iotexapi"

"github.com/iotexproject/iotex-core/ioctl"
"github.com/iotexproject/iotex-core/ioctl/config"
"github.com/iotexproject/iotex-core/ioctl/output"
"github.com/iotexproject/iotex-core/ioctl/util"
)

// Multi-language support
var (
_rewardUses = map[config.Language]string{
config.English: "reward [ALIAS|DELEGATE_ADDRESS]",
config.Chinese: "reward [别名|委托地址]",
_rewardCmdUses = map[config.Language]string{
config.English: "reward unclaimed|pool [ALIAS|DELEGATE_ADDRESS]",
config.Chinese: "reward 未支取|奖金池 [别名|委托地址]",
}
_rewardShorts = map[config.Language]string{
_rewardCmdShorts = map[config.Language]string{
config.English: "Query rewards",
config.Chinese: "查询奖励",
}
_rewardPoolMessageTranslations = map[config.Language]string{
config.English: "Available Reward: %s IOTX Total Reward: %s IOTX",
config.Chinese: "可用奖金: %s IOTX 总奖金: %s IOTX",
_rewardPoolLong = map[config.Language]string{
config.English: "ioctl node reward pool returns unclaimed and available Rewards in fund pool.\nTotalUnclaimed is the amount of all delegates that have been issued but are not claimed;\nTotalAvailable is the amount of balance that has not been issued to anyone.\n\nioctl node reward unclaimed [ALIAS|DELEGATE_ADDRESS] returns unclaimed rewards of a specific delegate.",
config.Chinese: "ioctl node reward 返回奖金池中的未支取奖励和可获取的奖励. TotalUnclaimed是所有代表已被发放但未支取的奖励的总和; TotalAvailable 是奖金池中未被发放的奖励的总和.\n\nioctl node [ALIAS|DELEGATE_ADDRESS] 返回特定代表的已被发放但未支取的奖励.",
}
)

// NewNodeRewardCmd represents the node reward command
func NewNodeRewardCmd(c ioctl.Client) *cobra.Command {
use, _ := c.SelectTranslation(_rewardUses)
short, _ := c.SelectTranslation(_rewardShorts)
rewardPoolMessageTranslation, _ := c.SelectTranslation(_rewardPoolMessageTranslations)
nc := &cobra.Command{
func NewNodeRewardCmd(client ioctl.Client) *cobra.Command {
LuckyPigeon marked this conversation as resolved.
Show resolved Hide resolved
use, _ := client.SelectTranslation(_rewardCmdUses)
short, _ := client.SelectTranslation(_rewardCmdShorts)
long, _ := client.SelectTranslation(_rewardPoolLong)

return &cobra.Command{
Use: use,
Short: short,
Args: cobra.MaximumNArgs(1),
Args: cobra.RangeArgs(1, 2),
Long: long,
RunE: func(cmd *cobra.Command, args []string) error {
cmd.SilenceUsage = true
var err error
if len(args) == 0 {

apiClient, err := c.APIServiceClient()
if err != nil {
return err
}

response, err := apiClient.ReadState(
context.Background(),
&iotexapi.ReadStateRequest{
ProtocolID: []byte("rewarding"),
MethodName: []byte("AvailableBalance"),
},
)

if err != nil {
sta, ok := status.FromError(err)
if ok {
return output.NewError(output.APIError, sta.Message(), nil)
}
return output.NewError(output.NetworkError, "failed to invoke ReadState api", err)
}

availableRewardRau, ok := new(big.Int).SetString(string(response.Data), 10)
if !ok {
return output.NewError(output.ConvertError, "failed to convert string into big int", err)
switch args[0] {
case "pool":
if len(args) != 1 {
return errors.New("wrong number of arg(s) for ioctl node reward pool command. \nRun 'ioctl node reward --help' for usage")
}

response, err = apiClient.ReadState(
context.Background(),
&iotexapi.ReadStateRequest{
ProtocolID: []byte("rewarding"),
MethodName: []byte("TotalBalance"),
},
)
totalUnclaimed, totalAvailable, totalBalance, err := rewardPool(client)
if err != nil {
sta, ok := status.FromError(err)
if ok {
return output.NewError(output.APIError, sta.Message(), nil)
}
return output.NewError(output.NetworkError, "failed to invoke ReadState api", err)
}
totalRewardRau, ok := new(big.Int).SetString(string(response.Data), 10)
if !ok {
return output.NewError(output.ConvertError, "failed to convert string into big int", err)
}

message := rewardPoolMessage{
AvailableReward: util.RauToString(availableRewardRau, util.IotxDecimalNum),
TotalReward: util.RauToString(totalRewardRau, util.IotxDecimalNum),
return err
}
fmt.Println(message.String(rewardPoolMessageTranslation))

} else {
arg := args[0]
address, err := c.Address(arg)
if err != nil {
return output.NewError(output.AddressError, "failed to get address", err)
cmd.Printf("Total Unclaimed:\t %s IOTX\nTotal Available:\t %s IOTX\nTotal Balance:\t\t %s IOTX\n",
totalUnclaimed, totalAvailable, totalBalance)
case "unclaimed":
if len(args) != 2 {
return errors.New("wrong number of arg(s) for ioctl node reward unclaimed [ALIAS|DELEGATE_ADDRESS] command. \nRun 'ioctl node reward --help' for usage")
}
apiClient, err := c.APIServiceClient()
address, reward, err := reward(client, args[1])
if err != nil {
return err
}

response, err := apiClient.ReadState(
context.Background(),
&iotexapi.ReadStateRequest{
ProtocolID: []byte("rewarding"),
MethodName: []byte("UnclaimedBalance"),
Arguments: [][]byte{[]byte(address)},
},
)
if err != nil {
sta, ok := status.FromError(err)
if ok {
return output.NewError(output.APIError, sta.Message(), nil)
}
return output.NewError(output.NetworkError, "failed to get version from server", err)
}
rewardRau, ok := new(big.Int).SetString(string(response.Data), 10)
if !ok {
return output.NewError(output.ConvertError, "failed to convert string into big int", err)
}
message := rewardMessage{Address: address, Reward: util.RauToString(rewardRau, util.IotxDecimalNum)}
fmt.Println(message.String())

cmd.Printf("%s: %s IOTX\n", address, reward)
default:
return errors.New("unknown command. \nRun 'ioctl node reward --help' for usage")
}
return output.PrintError(err)
return nil
},
}
return nc
}

type rewardPoolMessage struct {
AvailableReward string `json:"availableReward"`
TotalReward string `json:"totalReward"`
}
func rewardPool(client ioctl.Client) (string, string, string, error) {
apiClient, err := client.APIServiceClient()
if err != nil {
return "", "", "", err
}
ctx := context.Background()
jwtMD, err := util.JwtAuth()
if err == nil {
ctx = metautils.NiceMD(jwtMD).ToOutgoing(ctx)
}
response, err := apiClient.ReadState(
ctx,
&iotexapi.ReadStateRequest{
ProtocolID: []byte("rewarding"),
MethodName: []byte("AvailableBalance"),
},
)
if err != nil {
sta, ok := status.FromError(err)
if ok {
return "", "", "", errors.New(sta.Message())
}
return "", "", "", errors.Wrap(err, "failed to invoke ReadState api")
}

func (m *rewardPoolMessage) String(trans ...string) string {
availableRewardRau, ok := new(big.Int).SetString(string(response.Data), 10)
if !ok {
return "", "", "", errors.New("failed to convert string into big int")
}

if output.Format == "" {
message := fmt.Sprintf(trans[0],
m.AvailableReward, m.TotalReward)
return message
response, err = apiClient.ReadState(
context.Background(),
&iotexapi.ReadStateRequest{
ProtocolID: []byte("rewarding"),
MethodName: []byte("TotalBalance"),
},
)
if err != nil {
sta, ok := status.FromError(err)
if ok {
return "", "", "", errors.New(sta.Message())
}
return "", "", "", errors.Wrap(err, "failed to invoke ReadState api")
}
return output.FormatStringWithTrans(output.Result, m)
}
totalRewardRau, ok := new(big.Int).SetString(string(response.Data), 10)
if !ok {
return "", "", "", errors.New("failed to convert string into big int")
}

totalUnclaimedRewardRau := big.NewInt(0)
totalUnclaimedRewardRau.Sub(totalRewardRau, availableRewardRau)

type rewardMessage struct {
Address string `json:"address"`
Reward string `json:"reward"`
return util.RauToString(totalUnclaimedRewardRau, util.IotxDecimalNum),
util.RauToString(availableRewardRau, util.IotxDecimalNum),
util.RauToString(totalRewardRau, util.IotxDecimalNum),
err
}

func (m *rewardMessage) String(trans ...string) string {
if output.Format == "" {
message := fmt.Sprintf("%s: %s IOTX", m.Address, m.Reward)
return message
func reward(client ioctl.Client, arg string) (string, string, error) {
address, err := client.Address(arg)
if err != nil {
return "", "", errors.Wrap(err, "failed to get address")
}
apiClient, err := client.APIServiceClient()
if err != nil {
return "", "", errors.Wrap(err, "failed to connect to endpoint")
}
ctx := context.Background()
jwtMD, err := util.JwtAuth()
if err == nil {
ctx = metautils.NiceMD(jwtMD).ToOutgoing(ctx)
}
response, err := apiClient.ReadState(
ctx,
&iotexapi.ReadStateRequest{
ProtocolID: []byte("rewarding"),
MethodName: []byte("UnclaimedBalance"),
Arguments: [][]byte{[]byte(address)},
},
)
if err != nil {
sta, ok := status.FromError(err)
if ok {
return "", "", errors.New(sta.Message())
}
return "", "", errors.Wrap(err, "failed to get version from server")
}
rewardRau, ok := new(big.Int).SetString(string(response.Data), 10)
if !ok {
return "", "", errors.New("failed to convert string into big int")
}
return output.FormatStringWithTrans(output.Result, m)
return address, util.RauToString(rewardRau, util.IotxDecimalNum), err
}
Loading