github.com/iotexproject/iotex-core@v1.14.1-rc1/ioctl/cmd/bc/bcdelegate.go (about) 1 // Copyright (c) 2022 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package bc 7 8 import ( 9 "context" 10 "fmt" 11 "math/big" 12 "strings" 13 14 "github.com/grpc-ecosystem/go-grpc-middleware/util/metautils" 15 "github.com/iotexproject/iotex-proto/golang/iotexapi" 16 "github.com/iotexproject/iotex-proto/golang/iotextypes" 17 "github.com/pkg/errors" 18 "github.com/spf13/cobra" 19 "golang.org/x/exp/slices" 20 "google.golang.org/grpc/status" 21 "google.golang.org/protobuf/proto" 22 23 "github.com/iotexproject/iotex-core/ioctl/config" 24 "github.com/iotexproject/iotex-core/ioctl/output" 25 "github.com/iotexproject/iotex-core/ioctl/util" 26 ) 27 28 // Multi-language support 29 var ( 30 _bcDelegateCmdShorts = map[config.Language]string{ 31 config.English: "Get delegate information on IoTeX blockchain", 32 config.Chinese: "在IoTeX区块链上读取代表信息", 33 } 34 _bcDelegateUses = map[config.Language]string{ 35 config.English: "delegate [name|address]", 36 config.Chinese: "delegate [名字|地址]", 37 } 38 ) 39 40 // _bcBucketCmd represents the bc Bucket command 41 var _bcDelegateCmd = &cobra.Command{ 42 Use: config.TranslateInLang(_bcDelegateUses, config.UILanguage), 43 Short: config.TranslateInLang(_bcDelegateCmdShorts, config.UILanguage), 44 Args: cobra.ExactArgs(1), 45 Example: `ioctl bc delegate name, to read delegate information by name`, 46 RunE: func(cmd *cobra.Command, args []string) (err error) { 47 cmd.SilenceUsage = true 48 err = getDelegate(args[0]) 49 return output.PrintError(err) 50 }, 51 } 52 53 type delegateMessage struct { 54 Delegate *iotextypes.CandidateV2 `json:"delegate"` 55 } 56 57 func (m *delegateMessage) delegateString() string { 58 var ( 59 d = m.Delegate 60 vote, _ = new(big.Int).SetString(d.TotalWeightedVotes, 10) 61 token, _ = new(big.Int).SetString(d.SelfStakingTokens, 10) 62 lines []string 63 ) 64 lines = append(lines, "{") 65 lines = append(lines, fmt.Sprintf(" name: %s", d.Name)) 66 lines = append(lines, fmt.Sprintf(" ownerAddress: %s", d.OwnerAddress)) 67 lines = append(lines, fmt.Sprintf(" operatorAddress: %s", d.OperatorAddress)) 68 lines = append(lines, fmt.Sprintf(" rewardAddress: %s", d.RewardAddress)) 69 lines = append(lines, fmt.Sprintf(" totalWeightedVotes: %s", util.RauToString(vote, util.IotxDecimalNum))) 70 lines = append(lines, fmt.Sprintf(" selfStakeBucketIdx: %d", d.SelfStakeBucketIdx)) 71 lines = append(lines, fmt.Sprintf(" selfStakingTokens: %s IOTX", util.RauToString(token, util.IotxDecimalNum))) 72 lines = append(lines, "}") 73 return strings.Join(lines, "\n") 74 } 75 76 func (m *delegateMessage) String() string { 77 if output.Format == "" { 78 return m.delegateString() 79 } 80 return output.FormatString(output.Result, m) 81 } 82 83 func getDelegate(arg string) error { 84 var d *iotextypes.CandidateV2 85 addr, err := util.Address(arg) 86 if err == nil { 87 d, err = getDelegateByAddress(addr) 88 } else { 89 d, err = getDelegateByName(arg) 90 } 91 if err != nil { 92 return err 93 } 94 message := delegateMessage{ 95 Delegate: d, 96 } 97 fmt.Println(message.String()) 98 return nil 99 } 100 101 func getDelegateByName(name string) (*iotextypes.CandidateV2, error) { 102 conn, err := util.ConnectToEndpoint(config.ReadConfig.SecureConnect && !config.Insecure) 103 if err != nil { 104 return nil, output.NewError(output.NetworkError, "failed to connect to endpoint", err) 105 } 106 defer conn.Close() 107 cli := iotexapi.NewAPIServiceClient(conn) 108 method := &iotexapi.ReadStakingDataMethod{ 109 Method: iotexapi.ReadStakingDataMethod_CANDIDATE_BY_NAME, 110 } 111 methodData, err := proto.Marshal(method) 112 if err != nil { 113 return nil, output.NewError(output.SerializationError, "failed to marshal read staking data method", err) 114 } 115 readStakingdataRequest := &iotexapi.ReadStakingDataRequest{ 116 Request: &iotexapi.ReadStakingDataRequest_CandidateByName_{ 117 CandidateByName: &iotexapi.ReadStakingDataRequest_CandidateByName{ 118 CandName: name, 119 }, 120 }, 121 } 122 requestData, err := proto.Marshal(readStakingdataRequest) 123 if err != nil { 124 return nil, output.NewError(output.SerializationError, "failed to marshal read staking data request", err) 125 } 126 127 request := &iotexapi.ReadStateRequest{ 128 ProtocolID: []byte("staking"), 129 MethodName: methodData, 130 Arguments: [][]byte{requestData}, 131 } 132 133 ctx := context.Background() 134 jwtMD, err := util.JwtAuth() 135 if err == nil { 136 ctx = metautils.NiceMD(jwtMD).ToOutgoing(ctx) 137 } 138 139 response, err := cli.ReadState(ctx, request) 140 if err != nil { 141 sta, ok := status.FromError(err) 142 if ok { 143 return nil, output.NewError(output.APIError, sta.Message(), nil) 144 } 145 return nil, output.NewError(output.NetworkError, "failed to invoke ReadState api", err) 146 } 147 delegate := iotextypes.CandidateV2{} 148 if err := proto.Unmarshal(response.Data, &delegate); err != nil { 149 return nil, output.NewError(output.SerializationError, "failed to unmarshal response", err) 150 } 151 return &delegate, nil 152 } 153 154 func getDelegateByAddress(addr string) (*iotextypes.CandidateV2, error) { 155 conn, err := util.ConnectToEndpoint(config.ReadConfig.SecureConnect && !config.Insecure) 156 if err != nil { 157 return nil, output.NewError(output.NetworkError, "failed to connect to endpoint", err) 158 } 159 defer conn.Close() 160 cli := iotexapi.NewAPIServiceClient(conn) 161 162 readCandidatesLimit := 200 163 for i := uint32(0); ; i++ { 164 offset := i * uint32(readCandidatesLimit) 165 size := uint32(readCandidatesLimit) 166 candidateList, err := util.GetStakingCandidates(cli, offset, size) 167 if err != nil { 168 return nil, errors.Wrap(err, "failed to get candidates") 169 } 170 if idx := slices.IndexFunc(candidateList.Candidates, func(cand *iotextypes.CandidateV2) bool { 171 return cand.OperatorAddress == addr 172 }); idx >= 0 { 173 return candidateList.Candidates[idx], nil 174 } 175 if len(candidateList.Candidates) < readCandidatesLimit { 176 break 177 } 178 } 179 return nil, output.NewError(output.UndefinedError, "failed to find delegate", nil) 180 }