Skip to content

Commit

Permalink
[ioctl] Build contract test bytecode command line into new ioctl (#3738)
Browse files Browse the repository at this point in the history
* [ioctl] build contract test bytecode command line into new ioctl

* build unittest to cover the modification

* format

Co-authored-by: huofei <68298506@qq.com>
  • Loading branch information
LuckyPigeon and huof6829 committed Dec 30, 2022
1 parent b56a665 commit 35c8528
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 0 deletions.
6 changes: 6 additions & 0 deletions ioctl/newcmd/contract/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
package contract

import (
"encoding/hex"

"github.com/ethereum/go-ethereum/common/compiler"
"github.com/pkg/errors"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -73,3 +75,7 @@ func checkCompilerVersion(solc *util.Solidity) bool {
}
return false
}

func decodeBytecode(bytecode string) ([]byte, error) {
return hex.DecodeString(util.TrimHexPrefix(bytecode))
}
82 changes: 82 additions & 0 deletions ioctl/newcmd/contract/contracttestbytecode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// 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 contract

import (
"math/big"

"github.com/iotexproject/iotex-address/address"
"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"
"github.com/iotexproject/iotex-core/ioctl/util"
)

// Multi-language support
var (
_testBytecodeCmdUses = map[config.Language]string{
config.English: "bytecode (CONTRACT_ADDRESS|ALIAS) PACKED_ARGUMENTS [AMOUNT_IOTX]",
config.Chinese: "bytecode (合约地址|别名) 已打包参数 [IOTX数量]",
}
_testBytecodeCmdShorts = map[config.Language]string{
config.English: "test smart contract on IoTeX blockchain with packed arguments",
config.Chinese: "传入bytecode测试IoTeX区块链上的智能合约",
}
)

// NewContractTestBytecodeCmd represents the contract test bytecode command
func NewContractTestBytecodeCmd(client ioctl.Client) *cobra.Command {
use, _ := client.SelectTranslation(_testBytecodeCmdUses)
short, _ := client.SelectTranslation(_testBytecodeCmdShorts)

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

contract, err := address.FromString(addr)
if err != nil {
return errors.Wrap(err, "failed to convert string into address")
}

bytecode, err := decodeBytecode(args[1])
if err != nil {
return errors.Wrap(err, "invalid bytecode")
}

amount := big.NewInt(0)
if len(args) == 3 {
amount, err = util.StringToRau(args[2], util.IotxDecimalNum)
if err != nil {
return errors.Wrap(err, "invalid amount")
}
}

_, signer, _, _, gasLimit, _, err := action.GetWriteCommandFlag(cmd)
if err != nil {
return err
}
result, err := action.Read(client, contract, amount.String(), bytecode, signer, gasLimit)
if err != nil {
return errors.Wrap(err, "failed to read contract")
}

cmd.Printf("return: %s\n", result)
return nil
},
}
action.RegisterWriteCommand(client, cmd)
return cmd
}
97 changes: 97 additions & 0 deletions ioctl/newcmd/contract/contracttestbytecode_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// 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 contract

import (
"encoding/hex"
"testing"

"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/golang/mock/gomock"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"

"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-core/ioctl/config"
"github.com/iotexproject/iotex-core/ioctl/util"
"github.com/iotexproject/iotex-core/test/identityset"
"github.com/iotexproject/iotex-core/test/mock/mock_ioctlclient"
)

func TestNewContractTestBytecodeCmd(t *testing.T) {
require := require.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock_ioctlclient.NewMockClient(ctrl)
apiServiceClient := mock_iotexapi.NewMockAPIServiceClient(ctrl)
addr := identityset.Address(0).String()

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("contract", config.English).Times(40)
client.EXPECT().SetEndpointWithFlag(gomock.Any()).AnyTimes()
client.EXPECT().SetInsecureWithFlag(gomock.Any()).AnyTimes()
client.EXPECT().Alias(gomock.Any()).Return("producer", nil).AnyTimes()
client.EXPECT().APIServiceClient().Return(apiServiceClient, nil).AnyTimes()
client.EXPECT().IsCryptoSm2().Return(false).AnyTimes()
client.EXPECT().ReadSecret().Return("", nil).AnyTimes()
client.EXPECT().Address(gomock.Any()).Return(accAddr.String(), nil).Times(4)
client.EXPECT().AddressWithDefaultIfNotExist(gomock.Any()).Return(accAddr.String(), nil).AnyTimes()
client.EXPECT().NewKeyStore().Return(ks).AnyTimes()
client.EXPECT().AskToConfirm(gomock.Any()).Return(true, nil).AnyTimes()
client.EXPECT().Config().Return(config.Config{
Explorer: "iotexscan",
Endpoint: "testnet1",
}).AnyTimes()

apiServiceClient.EXPECT().ReadContract(gomock.Any(), gomock.Any()).Return(&iotexapi.ReadContractResponse{
Data: hex.EncodeToString([]byte("60fe47b100000000000000000000000000000000000000000000000000000000")),
}, nil)

t.Run("compile contract", func(t *testing.T) {
cmd := NewContractTestBytecodeCmd(client)
result, err := util.ExecuteCmd(cmd, addr, "a9059cbb0000000000000000000000004867c4bada9553216bf296c4c64e9ff0749206490000000000000000000000000000000000000000000000000000000000000001")
require.NoError(err)
require.Contains(result, "return")
})

t.Run("failed to read contract", func(t *testing.T) {
expectedErr := errors.New("failed to read contract")
apiServiceClient.EXPECT().ReadContract(gomock.Any(), gomock.Any()).Return(nil, expectedErr)
cmd := NewContractTestBytecodeCmd(client)
_, err := util.ExecuteCmd(cmd, addr, "a9059cbb0000000000000000000000004867c4bada9553216bf296c4c64e9ff0749206490000000000000000000000000000000000000000000000000000000000000001")
require.Contains(err.Error(), expectedErr.Error())
})

t.Run("invalid amount", func(t *testing.T) {
expectedErr := errors.New("invalid amount")
cmd := NewContractTestBytecodeCmd(client)
_, err := util.ExecuteCmd(cmd, addr, "a9059cbb0000000000000000000000004867c4bada9553216bf296c4c64e9ff0749206490000000000000000000000000000000000000000000000000000000000000001", "test")
require.Contains(err.Error(), expectedErr.Error())
})

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

t.Run("failed to get contract address", func(t *testing.T) {
expectedErr := errors.New("failed to get contract address")
client.EXPECT().Address(gomock.Any()).Return("", expectedErr)
cmd := NewContractTestBytecodeCmd(client)
_, err := util.ExecuteCmd(cmd, "test", "")
require.Contains(err.Error(), expectedErr.Error())
})
}

0 comments on commit 35c8528

Please sign in to comment.