Skip to content

Commit

Permalink
[ioctl] Build did register command line into new ioctl (#3583)
Browse files Browse the repository at this point in the history
* [ioctl] build did command line into new ioctl
  • Loading branch information
LuckyPigeon authored Sep 7, 2022
1 parent f7c1905 commit 94aad86
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 29 deletions.
8 changes: 4 additions & 4 deletions ioctl/newcmd/action/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,11 +402,11 @@ func Execute(client ioctl.Client,
if err != nil {
return errors.Wrap(err, "failed to get gas price")
}
signer, err = Signer(client, signer)
sender, err := Signer(client, signer)
if err != nil {
return errors.Wrap(err, "failed to get signer address")
}
nonce, err = checkNonce(client, nonce, signer)
nonce, err = checkNonce(client, nonce, sender)
if err != nil {
return errors.Wrap(err, "failed to get nonce")
}
Expand All @@ -415,7 +415,7 @@ func Execute(client ioctl.Client,
return errors.Wrap(err, "failed to make a Execution instance")
}
if gasLimit == 0 {
tx, err = fixGasLimit(client, signer, tx)
tx, err = fixGasLimit(client, sender, tx)
if err != nil || tx == nil {
return errors.Wrap(err, "failed to fix Execution gas limit")
}
Expand All @@ -429,7 +429,7 @@ func Execute(client ioctl.Client,
SetGasPrice(gasPriceRau).
SetGasLimit(gasLimit).
SetAction(tx).Build(),
signer,
sender,
password,
nonce,
assumeYes,
Expand Down
27 changes: 2 additions & 25 deletions ioctl/newcmd/action/stake2withdraw.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,52 +56,29 @@ func NewStake2WithdrawCmd(client ioctl.Client) *cobra.Command {
}
}

signer, err := cmd.Flags().GetString(signerFlagLabel)
gasPrice, signer, password, nonce, gasLimit, assumeYes, err := GetWriteCommandFlag(cmd)
if err != nil {
return errors.Wrap(err, "failed to get flag signer")
return err
}
sender, err := Signer(client, signer)
if err != nil {
return errors.Wrap(err, "failed to get signed address")
}

gasLimit, err := cmd.Flags().GetUint64(gasLimitFlagLabel)
if err != nil {
return errors.Wrap(err, "failed to get flage gas-limit")
}
if gasLimit == 0 {
gasLimit = action.ReclaimStakeBaseIntrinsicGas + action.ReclaimStakePayloadGas*uint64(len(data))
}

gasPrice, err := cmd.Flags().GetString(gasPriceFlagLabel)
if err != nil {
return errors.Wrap(err, "failed to get flag gas-price")
}
gasPriceRau, err := gasPriceInRau(client, gasPrice)
if err != nil {
return errors.Wrap(err, "failed to get gas price")
}
nonce, err := cmd.Flags().GetUint64(nonceFlagLabel)
if err != nil {
return errors.Wrap(err, "failed to get flag nonce")
}
nonce, err = checkNonce(client, nonce, sender)
if err != nil {
return errors.Wrap(err, "failed to get nonce")
}

s2w, err := action.NewWithdrawStake(nonce, bucketIndex, data, gasLimit, gasPriceRau)
if err != nil {
return errors.Wrap(err, "failed to make a changeCandidate instance")
}
password, err := cmd.Flags().GetString(passwordFlagLabel)
if err != nil {
return errors.Wrap(err, "failed to get flag password")
}
assumeYes, err := cmd.Flags().GetBool(assumeYesFlagLabel)
if err != nil {
return errors.Wrap(err, "failed to get flag assume-yes")
}
return SendAction(
client,
cmd,
Expand Down
45 changes: 45 additions & 0 deletions ioctl/newcmd/did/did.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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 did

import (
"github.com/spf13/cobra"

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

// Multi-language support
var (
_dIDCmdShorts = map[config.Language]string{
config.English: "Manage Decentralized Identity of IoTeX blockchain",
config.Chinese: "管理IoTeX区块链上的去中心化数字身份",
}
)

const (
_registerDIDName = "registerDID"
_getHashName = "getHash"
_getURIName = "getURI"
_updateDIDName = "updateDID"
_deregisterDIDName = "deregisterDID"
// DIDABI is the did abi
DIDABI = `[{"constant": false,"inputs": [],"name": "deregisterDID","outputs": [],"payable": false,"stateMutability": "nonpayable","type": "function"},{"constant": true,"inputs": [{"internalType": "bytes","name": "did","type": "bytes"}],"name": "getHash","outputs": [{"internalType": "bytes32","name": "","type": "bytes32"}],"payable": false,"stateMutability": "view","type": "function"}, {"constant": true,"inputs": [{"internalType": "bytes","name": "did","type": "bytes"}],"name": "getURI","outputs": [{"internalType": "bytes","name": "","type": "bytes"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": false,"inputs": [{"internalType": "bytes32","name": "h","type": "bytes32"},{"internalType": "bytes","name": "uri","type": "bytes"}],"name": "registerDID","outputs": [],"payable": false,"stateMutability": "nonpayable","type": "function"},{"constant": false,"inputs": [{"internalType": "bytes32","name": "h","type": "bytes32"},{"internalType": "bytes","name": "uri","type": "bytes"}],"name": "updateDID","outputs": [],"payable": false,"stateMutability": "nonpayable","type": "function"}]`
)

// NewDidCmd represents the did command
func NewDidCmd(client ioctl.Client) *cobra.Command {
short, _ := client.SelectTranslation(_dIDCmdShorts)
cmd := &cobra.Command{
Use: "did",
Short: short,
}
cmd.AddCommand(NewDidRegisterCmd(client))
client.SetEndpointWithFlag(cmd.PersistentFlags().StringVar)
client.SetInsecureWithFlag(cmd.PersistentFlags().BoolVar)
return cmd
}
34 changes: 34 additions & 0 deletions ioctl/newcmd/did/did_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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 did

import (
"testing"

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"

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

func TestNewDidCmd(t *testing.T) {
require := require.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock_ioctlclient.NewMockClient(ctrl)

client.EXPECT().SelectTranslation(gomock.Any()).Return("did", config.English).AnyTimes()
client.EXPECT().SetEndpointWithFlag(gomock.Any()).Do(func(_ func(*string, string, string, string)) {})
client.EXPECT().SetInsecureWithFlag(gomock.Any()).Do(func(_ func(*bool, string, bool, string)) {})

cmd := NewDidCmd(client)
result, err := util.ExecuteCmd(cmd)
require.NoError(err)
require.Contains(result, "Available Commands")
}
75 changes: 75 additions & 0 deletions ioctl/newcmd/did/didregister.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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 did

import (
"encoding/hex"
"math/big"
"strings"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/pkg/errors"
"github.com/spf13/cobra"

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

// Multi-language support
var (
_registerCmdUses = map[config.Language]string{
config.English: "register (CONTRACT_ADDRESS|ALIAS) hash uri",
config.Chinese: "register (合约地址|别名) hash uri",
}
_registerCmdShorts = map[config.Language]string{
config.English: "Register DID on IoTeX blockchain",
config.Chinese: "Register 在IoTeX链上注册DID",
}
)

// NewDidRegisterCmd represents the did register command
func NewDidRegisterCmd(client ioctl.Client) *cobra.Command {
use, _ := client.SelectTranslation(_registerCmdUses)
short, _ := client.SelectTranslation(_registerCmdShorts)

cmd := &cobra.Command{
Use: use,
Short: short,
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
cmd.SilenceUsage = true
contract, err := client.Address(args[0])
if err != nil {
return errors.Wrap(err, "failed to get contract address")
}

hashSlice, err := hex.DecodeString(args[1])
if err != nil {
return errors.Wrap(err, "failed to decode data")
}
var hashArray [32]byte
copy(hashArray[:], hashSlice)
abi, err := abi.JSON(strings.NewReader(DIDABI))
if err != nil {
return errors.Wrap(err, "falied to parse abi")
}
bytecode, err := abi.Pack(_registerDIDName, hashArray, []byte(args[2]))
if err != nil {
return errors.Wrap(err, "invalid bytecode")
}

gasPrice, signer, password, nonce, gasLimit, assumeYes, err := action.GetWriteCommandFlag(cmd)
if err != nil {
return err
}
return action.Execute(client, cmd, contract, big.NewInt(0), bytecode, gasPrice, signer, password, nonce, gasLimit, assumeYes)
},
}
action.RegisterWriteCommand(client, cmd)
return cmd
}
85 changes: 85 additions & 0 deletions ioctl/newcmd/did/didregister_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// 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 did

import (
"testing"

"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/golang/mock/gomock"
"github.com/iotexproject/iotex-address/address"
"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/ioctl/config"
"github.com/iotexproject/iotex-core/ioctl/util"
"github.com/iotexproject/iotex-core/test/mock/mock_ioctlclient"
)

func TestNewDidRegisterCmd(t *testing.T) {
require := require.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock_ioctlclient.NewMockClient(ctrl)
apiServiceClient := mock_iotexapi.NewMockAPIServiceClient(ctrl)
payload := "0a10080118a08d062202313062040a023130124104dc4c548c3a478278a6a09ffa8b5c4b384368e49654b35a6961ee8288fc889cdc39e9f8194e41abdbfac248ef9dc3f37b131a36ee2c052d974c21c1d2cd56730b1a4161e219c2c5d5987f8a9efa33e8df0cde9d5541689fff05784cdc24f12e9d9ee8283a5aa720f494b949535b7969c07633dfb68c4ef9359eb16edb9abc6ebfadc801"

ks := keystore.NewKeyStore(t.TempDir(), 2, 1)
acc, err := ks.NewAccount("")
require.NoError(err)
accAddr, err := address.FromBytes(acc.Address.Bytes())
require.NoError(err)

client.EXPECT().SelectTranslation(gomock.Any()).Return("did", config.English).AnyTimes()
client.EXPECT().Address(gomock.Any()).Return(accAddr.String(), nil).Times(3)
client.EXPECT().Alias(gomock.Any()).Return("producer", nil).AnyTimes()
client.EXPECT().APIServiceClient().Return(apiServiceClient, nil).AnyTimes()
client.EXPECT().IsCryptoSm2().Return(false).Times(3)
client.EXPECT().ReadSecret().Return("", nil).Times(1)
client.EXPECT().AddressWithDefaultIfNotExist(gomock.Any()).Return(accAddr.String(), nil).Times(1)
client.EXPECT().NewKeyStore().Return(ks).Times(2)
client.EXPECT().AskToConfirm(gomock.Any()).Return(true, nil).Times(1)
client.EXPECT().Config().Return(config.Config{
Explorer: "iotexscan",
Endpoint: "testnet1",
}).Times(2)

accountResp := &iotexapi.GetAccountResponse{
AccountMeta: &iotextypes.AccountMeta{
IsContract: false,
PendingNonce: 10,
Balance: "100000000000000000000",
},
}
chainMetaResp := &iotexapi.GetChainMetaResponse{
ChainMeta: &iotextypes.ChainMeta{
ChainID: 0,
},
}
sendActionResp := &iotexapi.SendActionResponse{}
apiServiceClient.EXPECT().GetAccount(gomock.Any(), gomock.Any()).Return(accountResp, nil).Times(2)
apiServiceClient.EXPECT().GetChainMeta(gomock.Any(), gomock.Any()).Return(chainMetaResp, nil).Times(1)
apiServiceClient.EXPECT().SendAction(gomock.Any(), gomock.Any()).Return(sendActionResp, nil).Times(1)

t.Run("did register", func(t *testing.T) {
cmd := NewDidRegisterCmd(client)
result, err := util.ExecuteCmd(cmd, accAddr.String(), payload, "test", "--signer", accAddr.String())
require.NoError(err)
require.Contains(result, "Action has been sent to blockchain")
})

t.Run("failed to decode data", func(t *testing.T) {
expectedErr := errors.New("failed to decode data")

cmd := NewDidRegisterCmd(client)
_, err := util.ExecuteCmd(cmd, accAddr.String(), "test", "test", "--signer", accAddr.String())
require.Contains(err.Error(), expectedErr.Error())
})
}

0 comments on commit 94aad86

Please sign in to comment.