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] Build block bucketlist command line into new ioctl #3469

Merged
merged 28 commits into from
Jul 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a9a3d5f
[ioctl] build block bucketlist command line into new ioctl
LuckyPigeon Jun 24, 2022
70858af
refactor unit test to cover the modification
LuckyPigeon Jun 24, 2022
f2de9dc
Merge branch 'master' into dev_bc_bucketlist
LuckyPigeon Jun 24, 2022
c0aec69
Merge branch 'master' into dev_bc_bucketlist
LuckyPigeon Jun 25, 2022
4fc35ac
fix commit
LuckyPigeon Jun 25, 2022
b78ab97
combine getBucketListByVoter with getBucketListByCand
LuckyPigeon Jun 25, 2022
4d6f612
Merge branch 'master' into dev_bc_bucketlist
LuckyPigeon Jun 27, 2022
bab6d44
Merge branch 'master' into dev_bc_bucketlist
LuckyPigeon Jun 27, 2022
1527b0a
move getBucketList into cmd
LuckyPigeon Jun 27, 2022
68fc70f
Merge branch 'master' into dev_bc_bucketlist
LuckyPigeon Jun 29, 2022
7e416eb
combine two for rnage
LuckyPigeon Jun 29, 2022
e54f8e2
add unit test for GetBucketList
LuckyPigeon Jun 29, 2022
ae07db0
Merge branch 'master' into dev_bc_bucketlist
LuckyPigeon Jun 29, 2022
9d42f75
fix commit
LuckyPigeon Jun 30, 2022
30e100d
delete space line
huof6829 Jul 1, 2022
bd4c575
format
huof6829 Jul 1, 2022
eac2095
fix commit
LuckyPigeon Jul 1, 2022
dd715d5
Merge branch 'master' into dev_bc_bucketlist
LuckyPigeon Jul 1, 2022
7f66d56
Merge branch 'dev_bc_bucketlist' of github.com:LuckyPigeon/iotex-core…
LuckyPigeon Jul 1, 2022
1e65c81
modify constants
huof6829 Jul 2, 2022
3fc75f4
format
huof6829 Jul 2, 2022
f3a8c34
fix commit
LuckyPigeon Jul 2, 2022
ed30664
add data test
huof6829 Jul 2, 2022
0f9d19b
Merge branch 'master' into dev_bc_bucketlist
LuckyPigeon Jul 2, 2022
e66a43b
Merge branch 'master' into dev_bc_bucketlist
LuckyPigeon Jul 6, 2022
e052b6c
Merge branch 'master' into dev_bc_bucketlist
LuckyPigeon Jul 6, 2022
33f5f9c
Merge branch 'master' into dev_bc_bucketlist
Liuhaai Jul 6, 2022
dd616dd
Merge branch 'master' into dev_bc_bucketlist
Liuhaai Jul 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions ioctl/newcmd/bc/bc.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"

"github.com/iotexproject/iotex-core/ioctl"
"github.com/iotexproject/iotex-core/ioctl/config"
Expand Down Expand Up @@ -129,3 +130,46 @@ func GetProbationList(client ioctl.Client, epochNum uint64, epochStartHeight uin
}
return response, nil
}

// GetBucketList get bucket list
func GetBucketList(
client ioctl.Client,
methodName iotexapi.ReadStakingDataMethod_Name,
readStakingDataRequest *iotexapi.ReadStakingDataRequest,
) (*iotextypes.VoteBucketList, error) {
apiServiceClient, err := client.APIServiceClient()
if err != nil {
return nil, err
}
methodData, err := proto.Marshal(&iotexapi.ReadStakingDataMethod{Method: methodName})
if err != nil {
return nil, errors.Wrap(err, "failed to marshal read staking data method")
}
requestData, err := proto.Marshal(readStakingDataRequest)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal read staking data request")
}
request := &iotexapi.ReadStateRequest{
ProtocolID: []byte("staking"),
MethodName: methodData,
Arguments: [][]byte{requestData},
}
ctx := context.Background()
if jwtMD, err := util.JwtAuth(); err == nil {
ctx = metautils.NiceMD(jwtMD).ToOutgoing(ctx)
}

response, err := apiServiceClient.ReadState(ctx, request)
if err != nil {
sta, ok := status.FromError(err)
if ok {
return nil, errors.New(sta.Message())
}
return nil, errors.Wrap(err, "failed to invoke ReadState api")
}
bucketlist := iotextypes.VoteBucketList{}
if err := proto.Unmarshal(response.Data, &bucketlist); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal response")
}
return &bucketlist, nil
}
49 changes: 49 additions & 0 deletions ioctl/newcmd/bc/bc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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
// License 2.0 that can be found in the LICENSE file.

package bc

import (
"testing"

"github.com/golang/mock/gomock"
"github.com/iotexproject/iotex-proto/golang/iotexapi"
"github.com/iotexproject/iotex-proto/golang/iotexapi/mock_iotexapi"
"github.com/iotexproject/iotex-proto/golang/iotextypes"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"

"github.com/iotexproject/iotex-core/test/mock/mock_ioctlclient"
)

func TestGetBucketList(t *testing.T) {
require := require.New(t)
ctrl := gomock.NewController(t)
client := mock_ioctlclient.NewMockClient(ctrl)
apiServiceClient := mock_iotexapi.NewMockAPIServiceClient(ctrl)
request := &iotexapi.ReadStakingDataRequest{}
response := &iotexapi.ReadStateResponse{}
client.EXPECT().APIServiceClient().Return(apiServiceClient, nil).Times(2)

t.Run("get bucket list", func(t *testing.T) {
expectedValue := &iotextypes.VoteBucketList{
Buckets: []*iotextypes.VoteBucket{},
}
apiServiceClient.EXPECT().ReadState(gomock.Any(), gomock.Any()).Return(response, nil)

result, err := GetBucketList(client, iotexapi.ReadStakingDataMethod_BUCKETS_BY_CANDIDATE, request)
require.NoError(err)
require.Contains(result.String(), expectedValue.String())
})

t.Run("failed to invoke ReadState api", func(t *testing.T) {
expectedErr := errors.New("failed to invoke ReadState api")
apiServiceClient.EXPECT().ReadState(gomock.Any(), gomock.Any()).Return(nil, expectedErr)

_, err := GetBucketList(client, iotexapi.ReadStakingDataMethod_BUCKETS_BY_VOTER, request)
require.Contains(err.Error(), expectedErr.Error())
})
}
145 changes: 145 additions & 0 deletions ioctl/newcmd/bc/bcbucketlist.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// 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
// License 2.0 that can be found in the LICENSE file.

package bc

import (
"strconv"
"strings"

"github.com/iotexproject/iotex-proto/golang/iotexapi"
"github.com/iotexproject/iotex-proto/golang/iotextypes"
"github.com/pkg/errors"
"github.com/spf13/cobra"

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

// constants
const (
MethodVoter = "voter"
MethodCandidate = "cand"
)

// Multi-language support
var (
_bcBucketListCmdShorts = map[config.Language]string{
config.English: "Get bucket list with method and arg(s) on IoTeX blockchain",
config.Chinese: "根据方法和参数在IoTeX区块链上读取投票列表",
}
_bcBucketListCmdUses = map[config.Language]string{
config.English: "bucketlist <method> [arguments]",
config.Chinese: "bucketlist <方法> [参数]",
}
_bcBucketListCmdLongs = map[config.Language]string{
config.English: "Read bucket list\nValid methods: [" +
MethodVoter + ", " + MethodCandidate + "]",
config.Chinese: "根据方法和参数在IoTeX区块链上读取投票列表\n可用方法有:" +
MethodVoter + "," + MethodCandidate,
}
)

// NewBCBucketListCmd represents the bc bucketlist command
func NewBCBucketListCmd(client ioctl.Client) *cobra.Command {
use, _ := client.SelectTranslation(_bcBucketListCmdUses)
short, _ := client.SelectTranslation(_bcBucketListCmdShorts)
long, _ := client.SelectTranslation(_bcBucketListCmdLongs)

return &cobra.Command{
Use: use,
Short: short,
Long: long,
Args: cobra.MinimumNArgs(2),
Example: `ioctl bc bucketlist voter [VOTER_ADDRESS] [OFFSET] [LIMIT]
ioctl bc bucketlist cand [CANDIDATE_NAME] [OFFSET] [LIMIT]`,
RunE: func(cmd *cobra.Command, args []string) error {
cmd.SilenceUsage = true
LuckyPigeon marked this conversation as resolved.
Show resolved Hide resolved

var (
bl *iotextypes.VoteBucketList
address string
err error
)

offset, limit := uint64(0), uint64(1000)
method, addr := args[0], args[1]
s := args[2:]

if len(s) > 0 {
offset, err = strconv.ParseUint(s[0], 10, 64)
if err != nil {
return errors.Wrap(err, "invalid offset")
}
}
if len(s) > 1 {
limit, err = strconv.ParseUint(s[1], 10, 64)
if err != nil {
return errors.Wrap(err, "invalid limit")
}
}
switch method {
case MethodVoter:
address, err = client.AddressWithDefaultIfNotExist(addr)
if err != nil {
return err
}
bl, err = getBucketListByVoterAddress(client, address, uint32(offset), uint32(limit))
case MethodCandidate:
bl, err = getBucketListByCandidateName(client, addr, uint32(offset), uint32(limit))
default:
return errors.New("unknown <method>")
}
if err != nil {
return err
}
var lines []string
if len(bl.Buckets) == 0 {
lines = append(lines, "Empty bucketlist with given address")
} else {
for _, b := range bl.Buckets {
bucket, err := newBucket(b)
if err != nil {
return err
}
lines = append(lines, bucket.String())
}
}
cmd.Println(strings.Join(lines, "\n"))
return nil
},
}
}

func getBucketListByVoterAddress(client ioctl.Client, addr string, offset, limit uint32) (*iotextypes.VoteBucketList, error) {
readStakingdataRequest := &iotexapi.ReadStakingDataRequest{
Request: &iotexapi.ReadStakingDataRequest_BucketsByVoter{
BucketsByVoter: &iotexapi.ReadStakingDataRequest_VoteBucketsByVoter{
VoterAddress: addr,
Pagination: &iotexapi.PaginationParam{
Offset: offset,
Limit: limit,
},
},
},
}
return GetBucketList(client, iotexapi.ReadStakingDataMethod_BUCKETS_BY_VOTER, readStakingdataRequest)
}

func getBucketListByCandidateName(client ioctl.Client, candName string, offset, limit uint32) (*iotextypes.VoteBucketList, error) {
readStakingDataRequest := &iotexapi.ReadStakingDataRequest{
Request: &iotexapi.ReadStakingDataRequest_BucketsByCandidate{
BucketsByCandidate: &iotexapi.ReadStakingDataRequest_VoteBucketsByCandidate{
CandName: candName,
Pagination: &iotexapi.PaginationParam{
Offset: offset,
Limit: limit,
},
},
},
}
return GetBucketList(client, iotexapi.ReadStakingDataMethod_BUCKETS_BY_CANDIDATE, readStakingDataRequest)
}
104 changes: 104 additions & 0 deletions ioctl/newcmd/bc/bcbucketlist_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// 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
// License 2.0 that can be found in the LICENSE file.

package bc

import (
"testing"

"github.com/golang/mock/gomock"
"github.com/iotexproject/iotex-proto/golang/iotexapi"
"github.com/iotexproject/iotex-proto/golang/iotexapi/mock_iotexapi"
"github.com/iotexproject/iotex-proto/golang/iotextypes"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/iotexproject/iotex-core/ioctl/config"
"github.com/iotexproject/iotex-core/ioctl/util"
"github.com/iotexproject/iotex-core/test/mock/mock_ioctlclient"
"github.com/iotexproject/iotex-core/testutil"
)

func TestNewBCBucketListCmd(t *testing.T) {
require := require.New(t)
ctrl := gomock.NewController(t)
client := mock_ioctlclient.NewMockClient(ctrl)
apiServiceClient := mock_iotexapi.NewMockAPIServiceClient(ctrl)

client.EXPECT().SelectTranslation(gomock.Any()).Return("", config.English).Times(21)
client.EXPECT().APIServiceClient().Return(apiServiceClient, nil).Times(2)
client.EXPECT().Config().Return(config.Config{}).Times(2)

t.Run("get bucket list by voter", func(t *testing.T) {
client.EXPECT().AddressWithDefaultIfNotExist(gomock.Any()).Return("io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx", nil)
vblist, err := proto.Marshal(&iotextypes.VoteBucketList{
Buckets: []*iotextypes.VoteBucket{
{
Index: 1,
StakedAmount: "10",
UnstakeStartTime: timestamppb.New(testutil.TimestampNow()),
},
{
Index: 2,
StakedAmount: "20",
UnstakeStartTime: timestamppb.New(testutil.TimestampNow()),
},
},
})
require.NoError(err)
apiServiceClient.EXPECT().ReadState(gomock.Any(), gomock.All()).Return(&iotexapi.ReadStateResponse{
Data: vblist,
}, nil)

cmd := NewBCBucketListCmd(client)
result, err := util.ExecuteCmd(cmd, "voter", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx")
require.NoError(err)
require.Contains(result,
"index: 1",
"stakedAmount: 0.00000000000000001 IOTX",
"index: 2",
"stakedAmount: 0.00000000000000002 IOTX")
})

t.Run("get bucket list by candidate", func(t *testing.T) {
apiServiceClient.EXPECT().ReadState(gomock.Any(), gomock.All()).Return(&iotexapi.ReadStateResponse{}, nil)
cmd := NewBCBucketListCmd(client)
result, err := util.ExecuteCmd(cmd, "cand", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx")
require.NoError(err)
require.Equal("Empty bucketlist with given address\n", result)
})

t.Run("invalid voter address", func(t *testing.T) {
expectedErr := errors.New("cannot find address for alias test")
client.EXPECT().AddressWithDefaultIfNotExist(gomock.Any()).Return("", expectedErr)
cmd := NewBCBucketListCmd(client)
_, err := util.ExecuteCmd(cmd, "voter", "test")
require.Contains(err.Error(), expectedErr.Error())
})

t.Run("unknown method", func(t *testing.T) {
expectedErr := errors.New("unknown <method>")
cmd := NewBCBucketListCmd(client)
_, err := util.ExecuteCmd(cmd, "unknown", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx")
require.Equal(expectedErr.Error(), err.Error())
})

t.Run("invalid offset", func(t *testing.T) {
expectedErr := errors.New("invalid offset")
cmd := NewBCBucketListCmd(client)
_, err := util.ExecuteCmd(cmd, "voter", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx", "test")
require.Contains(err.Error(), expectedErr.Error())
})

t.Run("invalid limit", func(t *testing.T) {
expectedErr := errors.New("invalid limit")
cmd := NewBCBucketListCmd(client)
_, err := util.ExecuteCmd(cmd, "voter", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx", "0", "test")
require.Contains(err.Error(), expectedErr.Error())
})
}