From 399238d1b3d9bcd0ca0c87471806cfd51abf90b6 Mon Sep 17 00:00:00 2001 From: Dustin Xie Date: Tue, 20 Jun 2023 22:09:12 -0700 Subject: [PATCH] [ioctl] add bc delegate command --- ioctl/cmd/bc/bc.go | 1 + ioctl/cmd/bc/bcdelegate.go | 144 +++++++++++++++++++++++++++++++++ ioctl/cmd/node/nodedelegate.go | 2 +- 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 ioctl/cmd/bc/bcdelegate.go diff --git a/ioctl/cmd/bc/bc.go b/ioctl/cmd/bc/bc.go index bdfce75a71..c5d9462a50 100644 --- a/ioctl/cmd/bc/bc.go +++ b/ioctl/cmd/bc/bc.go @@ -50,6 +50,7 @@ func init() { BCCmd.AddCommand(_bcInfoCmd) BCCmd.AddCommand(_bcBucketListCmd) BCCmd.AddCommand(_bcBucketCmd) + BCCmd.AddCommand(_bcDelegateCmd) BCCmd.PersistentFlags().StringVar(&config.ReadConfig.Endpoint, "endpoint", config.ReadConfig.Endpoint, config.TranslateInLang(_flagEndpointUsages, config.UILanguage)) BCCmd.PersistentFlags().BoolVar(&config.Insecure, "insecure", config.Insecure, diff --git a/ioctl/cmd/bc/bcdelegate.go b/ioctl/cmd/bc/bcdelegate.go new file mode 100644 index 0000000000..99fc0ddd28 --- /dev/null +++ b/ioctl/cmd/bc/bcdelegate.go @@ -0,0 +1,144 @@ +// Copyright (c) 2022 IoTeX Foundation +// 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 License 2.0 that can be found in the LICENSE file. + +package bc + +import ( + "context" + "fmt" + "math/big" + "strings" + + "github.com/grpc-ecosystem/go-grpc-middleware/util/metautils" + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "github.com/spf13/cobra" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + + "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 ( + _bcDelegateCmdShorts = map[config.Language]string{ + config.English: "Get delegate information on IoTeX blockchain", + config.Chinese: "在IoTeX区块链上读取代表信息", + } + _bcDelegateUses = map[config.Language]string{ + config.English: "delegate [name]", + config.Chinese: "delegate [名字]", + } +) + +// _bcBucketCmd represents the bc Bucket command +var _bcDelegateCmd = &cobra.Command{ + Use: config.TranslateInLang(_bcDelegateUses, config.UILanguage), + Short: config.TranslateInLang(_bcDelegateCmdShorts, config.UILanguage), + Args: cobra.ExactArgs(1), + Example: `ioctl bc delegate name, to read delegate information by name`, + RunE: func(cmd *cobra.Command, args []string) (err error) { + cmd.SilenceUsage = true + err = getDelegate(args[0]) + return output.PrintError(err) + }, +} + +type delegateMessage struct { + Delegate *iotextypes.CandidateV2 `json:"delegate"` +} + +func (m *delegateMessage) delegateString() string { + var ( + d = m.Delegate + vote, _ = new(big.Int).SetString(d.TotalWeightedVotes, 10) + token, _ = new(big.Int).SetString(d.SelfStakingTokens, 10) + lines []string + ) + lines = append(lines, "{") + lines = append(lines, fmt.Sprintf(" name: %s", d.Name)) + lines = append(lines, fmt.Sprintf(" ownerAddress: %s", d.OwnerAddress)) + lines = append(lines, fmt.Sprintf(" operatorAddress: %s", d.OperatorAddress)) + lines = append(lines, fmt.Sprintf(" rewardAddress: %s", d.RewardAddress)) + lines = append(lines, fmt.Sprintf(" totalWeightedVotes: %s", util.RauToString(vote, util.IotxDecimalNum))) + lines = append(lines, fmt.Sprintf(" selfStakeBucketIdx: %d", d.SelfStakeBucketIdx)) + lines = append(lines, fmt.Sprintf(" selfStakingTokens: %s IOTX", util.RauToString(token, util.IotxDecimalNum))) + lines = append(lines, "}") + return strings.Join(lines, "\n") +} + +func (m *delegateMessage) String() string { + if output.Format == "" { + return m.delegateString() + } + return output.FormatString(output.Result, m) +} + +func getDelegate(arg string) error { + d, err := getDelegateByName(arg) + if err != nil { + return err + } + message := delegateMessage{ + Delegate: d, + } + fmt.Println(message.String()) + return nil +} + +func getDelegateByName(name string) (*iotextypes.CandidateV2, error) { + conn, err := util.ConnectToEndpoint(config.ReadConfig.SecureConnect && !config.Insecure) + if err != nil { + return nil, output.NewError(output.NetworkError, "failed to connect to endpoint", err) + } + defer conn.Close() + cli := iotexapi.NewAPIServiceClient(conn) + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_CANDIDATE_BY_NAME, + } + methodData, err := proto.Marshal(method) + if err != nil { + return nil, output.NewError(output.SerializationError, "failed to marshal read staking data method", err) + } + readStakingdataRequest := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_CandidateByName_{ + CandidateByName: &iotexapi.ReadStakingDataRequest_CandidateByName{ + CandName: name, + }, + }, + } + requestData, err := proto.Marshal(readStakingdataRequest) + if err != nil { + return nil, output.NewError(output.SerializationError, "failed to marshal read staking data request", err) + } + + request := &iotexapi.ReadStateRequest{ + ProtocolID: []byte("staking"), + MethodName: methodData, + Arguments: [][]byte{requestData}, + } + + ctx := context.Background() + jwtMD, err := util.JwtAuth() + if err == nil { + ctx = metautils.NiceMD(jwtMD).ToOutgoing(ctx) + } + + response, err := cli.ReadState(ctx, request) + if err != nil { + sta, ok := status.FromError(err) + if ok { + return nil, output.NewError(output.APIError, sta.Message(), nil) + } + return nil, output.NewError(output.NetworkError, "failed to invoke ReadState api", err) + } + delegate := iotextypes.CandidateV2{} + if err := proto.Unmarshal(response.Data, &delegate); err != nil { + return nil, output.NewError(output.SerializationError, "failed to unmarshal response", err) + } + return &delegate, nil +} diff --git a/ioctl/cmd/node/nodedelegate.go b/ioctl/cmd/node/nodedelegate.go index 546d27fed4..925eff8ecd 100644 --- a/ioctl/cmd/node/nodedelegate.go +++ b/ioctl/cmd/node/nodedelegate.go @@ -46,7 +46,7 @@ var ( } _delegateCmdShorts = map[config.Language]string{ config.English: "Print consensus delegates information in certain epoch", - config.Chinese: "打印在特定epoch内的公认代表的信息", + config.Chinese: "打印在特定epoch内的共识代表的信息", } _flagEpochNumUsages = map[config.Language]string{ config.English: "specify specific epoch",