From b1e286cc3a5871db70eebf7a54fbf454d1a5152e Mon Sep 17 00:00:00 2001 From: saito Date: Thu, 30 May 2024 15:23:02 +0800 Subject: [PATCH] [ioctl][ws][#5] project device sub commands to support interaction with ws contracts (#4283) * feat(ioctl/ws): ioctl support ws project device contract interactions --------- Co-authored-by: CoderZhi --- ioctl/cmd/ws/wsdevice.go | 55 ++++++++++++++++++ ioctl/cmd/ws/wsdeviceapprove.go | 91 ++++++++++++++++++++++++++++++ ioctl/cmd/ws/wsdeviceapproved.go | 93 +++++++++++++++++++++++++++++++ ioctl/cmd/ws/wsdeviceunapprove.go | 91 ++++++++++++++++++++++++++++++ 4 files changed, 330 insertions(+) create mode 100644 ioctl/cmd/ws/wsdevice.go create mode 100644 ioctl/cmd/ws/wsdeviceapprove.go create mode 100644 ioctl/cmd/ws/wsdeviceapproved.go create mode 100644 ioctl/cmd/ws/wsdeviceunapprove.go diff --git a/ioctl/cmd/ws/wsdevice.go b/ioctl/cmd/ws/wsdevice.go new file mode 100644 index 0000000000..2c37e5ac9c --- /dev/null +++ b/ioctl/cmd/ws/wsdevice.go @@ -0,0 +1,55 @@ +package ws + +import ( + "bytes" + _ "embed" // used to embed contract abi + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/spf13/cobra" + + "github.com/iotexproject/iotex-core/ioctl/config" +) + +var ( + _flagDevicesUsages = map[config.Language]string{ + config.English: "device address for the project, separate multiple addresses with commas", + config.Chinese: "该project的设备地址,多个地址用逗号分割", + } +) + +var wsProjectDeviceCmd = &cobra.Command{ + Use: "device", + Short: config.TranslateInLang(map[config.Language]string{ + config.English: "w3bstream device management", + config.Chinese: "w3bstream device 节点管理", + }, config.UILanguage), +} + +var ( + //go:embed contracts/abis/ProjectDevice.json + projectDeviceJSON []byte + projectDeviceAddress string + projectDeviceABI abi.ABI +) + +const ( + funcDevicesApprove = "approve" + funcDevicesUnapprove = "unapprove" + funcDevicesApproved = "approved" +) + +const ( + eventOnApprove = "Approve" + eventOnUnapprove = "Unapprove" +) + +func init() { + var err error + projectDeviceABI, err = abi.JSON(bytes.NewReader(projectDeviceJSON)) + if err != nil { + panic(err) + } + projectDeviceAddress = config.ReadConfig.WsProjectDevicesContract + + WsCmd.AddCommand(wsProjectDeviceCmd) +} diff --git a/ioctl/cmd/ws/wsdeviceapprove.go b/ioctl/cmd/ws/wsdeviceapprove.go new file mode 100644 index 0000000000..2d8bda9bee --- /dev/null +++ b/ioctl/cmd/ws/wsdeviceapprove.go @@ -0,0 +1,91 @@ +package ws + +import ( + "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/iotexproject/iotex-address/address" + "github.com/iotexproject/iotex-core/ioctl/cmd/ws/contracts" + "github.com/iotexproject/iotex-core/ioctl/config" + "github.com/iotexproject/iotex-core/ioctl/output" +) + +var ( + // wsProjectDeviceApprove represents approve device to send message to w3bstream project command + wsProjectDeviceApprove = &cobra.Command{ + Use: "approve", + Short: config.TranslateInLang(wsProjectDeviceApproveShorts, config.UILanguage), + RunE: func(cmd *cobra.Command, args []string) error { + projectID, err := cmd.Flags().GetUint64("project-id") + if err != nil { + return errors.Wrap(err, "failed to get flag project-id") + } + devices, err := cmd.Flags().GetString("devices") + if err != nil { + return errors.Wrap(err, "failed to get flag devices") + } + + out, err := approveProjectDevice(projectID, devices) + if err != nil { + return output.PrintError(err) + } + output.PrintResult(out) + return nil + }, + } + + wsProjectDeviceApproveShorts = map[config.Language]string{ + config.English: "approve devices for w3bstream project", + config.Chinese: "授权w3bstream项目的设备", + } +) + +func init() { + wsProjectDeviceApprove.Flags().Uint64P("project-id", "", 0, config.TranslateInLang(_flagProjectIDUsages, config.UILanguage)) + wsProjectDeviceApprove.Flags().StringP("devices", "", "", config.TranslateInLang(_flagDevicesUsages, config.UILanguage)) + + _ = wsProjectDeviceApprove.MarkFlagRequired("project-id") + _ = wsProjectDeviceApprove.MarkFlagRequired("devices") + + wsProjectDeviceCmd.AddCommand(wsProjectDeviceApprove) +} + +func approveProjectDevice(projectID uint64, devices string) (string, error) { + deviceArr := []string{devices} + if strings.Contains(devices, ",") { + deviceArr = strings.Split(devices, ",") + } + + deviceAddress := make([]common.Address, len(deviceArr)) + for _, device := range deviceArr { + addr, err := address.FromString(device) + if err != nil { + return "", errors.Wrapf(err, "invalid device address: %s", device) + } + deviceAddress = append(deviceAddress, common.BytesToAddress(addr.Bytes())) + } + + caller, err := NewContractCaller(projectDeviceABI, projectDeviceAddress) + if err != nil { + return "", errors.Wrap(err, "failed to create contract caller") + } + + value := new(contracts.ProjectDeviceApprove) + result := NewContractResult(&projectDeviceABI, eventOnApprove, value) + if _, err = caller.CallAndRetrieveResult(funcDevicesApprove, []any{ + big.NewInt(int64(projectID)), + deviceAddress, + }, result); err != nil { + return "", errors.Wrap(err, "failed to read contract") + } + if _, err = result.Result(); err != nil { + return "", err + } + + return fmt.Sprintf("approve %d device", len(deviceAddress)), nil +} diff --git a/ioctl/cmd/ws/wsdeviceapproved.go b/ioctl/cmd/ws/wsdeviceapproved.go new file mode 100644 index 0000000000..e7ea33a3e0 --- /dev/null +++ b/ioctl/cmd/ws/wsdeviceapproved.go @@ -0,0 +1,93 @@ +package ws + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/iotexproject/iotex-address/address" + "github.com/iotexproject/iotex-core/ioctl/config" + "github.com/iotexproject/iotex-core/ioctl/output" +) + +var ( + // wsProjectDeviceApproved represents unapprove device to send message to w3bstream project command + wsProjectDeviceApproved = &cobra.Command{ + Use: "approved", + Short: config.TranslateInLang(wsProjectDeviceApprovedShorts, config.UILanguage), + RunE: func(cmd *cobra.Command, args []string) error { + projectID, err := cmd.Flags().GetUint64("project-id") + if err != nil { + return errors.Wrap(err, "failed to get flag project-id") + } + device, err := cmd.Flags().GetString("device") + if err != nil { + return errors.Wrap(err, "failed to get flag devices") + } + + out, err := approvedProjectDevice(projectID, device) + if err != nil { + return output.PrintError(err) + } + output.PrintResult(output.JSONString(out)) + return nil + }, + } + + wsProjectDeviceApprovedShorts = map[config.Language]string{ + config.English: "approved devices for w3bstream project", + config.Chinese: "查询是否授权w3bstream项目的设备", + } + + _flagDeviceUsages = map[config.Language]string{ + config.English: "device address for the project", + config.Chinese: "该project的设备地址", + } +) + +func init() { + wsProjectDeviceApproved.Flags().Uint64P("project-id", "", 0, config.TranslateInLang(_flagProjectIDUsages, config.UILanguage)) + wsProjectDeviceApproved.Flags().StringP("device", "", "", config.TranslateInLang(_flagDeviceUsages, config.UILanguage)) + + _ = wsProjectDeviceApproved.MarkFlagRequired("project-id") + _ = wsProjectDeviceApproved.MarkFlagRequired("device") + + wsProjectDeviceCmd.AddCommand(wsProjectDeviceApproved) +} + +func approvedProjectDevice(projectID uint64, device string) (any, error) { + addr, err := address.FromString(device) + if err != nil { + return nil, errors.Wrapf(err, "invalid device address: %s", device) + } + deviceAddress := common.BytesToAddress(addr.Bytes()) + + caller, err := NewContractCaller(projectDeviceABI, projectDeviceAddress) + if err != nil { + return nil, errors.Wrap(err, "failed to create contract caller") + } + + result := NewContractResult(&projectDeviceABI, funcDevicesApproved, new(bool)) + if err = caller.Read(funcDevicesApproved, []any{ + big.NewInt(int64(projectID)), + deviceAddress, + }, result); err != nil { + return nil, errors.Wrap(err, "failed to read contract") + } + approved, err := result.Result() + if err != nil { + return nil, err + } + + return &struct { + ProjectID uint64 `json:"projectID"` + Device string `json:"device"` + Approved bool `json:"approved"` + }{ + ProjectID: projectID, + Device: device, + Approved: *(approved.(*bool)), + }, nil +} diff --git a/ioctl/cmd/ws/wsdeviceunapprove.go b/ioctl/cmd/ws/wsdeviceunapprove.go new file mode 100644 index 0000000000..f85cca623a --- /dev/null +++ b/ioctl/cmd/ws/wsdeviceunapprove.go @@ -0,0 +1,91 @@ +package ws + +import ( + "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/iotexproject/iotex-address/address" + "github.com/iotexproject/iotex-core/ioctl/cmd/ws/contracts" + "github.com/iotexproject/iotex-core/ioctl/config" + "github.com/iotexproject/iotex-core/ioctl/output" +) + +var ( + // wsProjectDeviceUnapprove represents unapprove device to send message to w3bstream project command + wsProjectDeviceUnapprove = &cobra.Command{ + Use: "unapprove", + Short: config.TranslateInLang(wsProjectDeviceUnapproveShorts, config.UILanguage), + RunE: func(cmd *cobra.Command, args []string) error { + projectID, err := cmd.Flags().GetUint64("project-id") + if err != nil { + return errors.Wrap(err, "failed to get flag project-id") + } + devices, err := cmd.Flags().GetString("devices") + if err != nil { + return errors.Wrap(err, "failed to get flag devices") + } + + out, err := unapproveProjectDevice(projectID, devices) + if err != nil { + return output.PrintError(err) + } + output.PrintResult(out) + return nil + }, + } + + wsProjectDeviceUnapproveShorts = map[config.Language]string{ + config.English: "unapprove devices for w3bstream project", + config.Chinese: "取消授权w3bstream项目的设备", + } +) + +func init() { + wsProjectDeviceUnapprove.Flags().Uint64P("project-id", "", 0, config.TranslateInLang(_flagProjectIDUsages, config.UILanguage)) + wsProjectDeviceUnapprove.Flags().StringP("devices", "", "", config.TranslateInLang(_flagDevicesUsages, config.UILanguage)) + + _ = wsProjectDeviceUnapprove.MarkFlagRequired("project-id") + _ = wsProjectDeviceUnapprove.MarkFlagRequired("devices") + + wsProjectDeviceCmd.AddCommand(wsProjectDeviceUnapprove) +} + +func unapproveProjectDevice(projectID uint64, devices string) (string, error) { + deviceArr := []string{devices} + if strings.Contains(devices, ",") { + deviceArr = strings.Split(devices, ",") + } + + deviceAddress := make([]common.Address, len(deviceArr)) + for _, device := range deviceArr { + addr, err := address.FromString(device) + if err != nil { + return "", errors.Wrapf(err, "invalid device address: %s", device) + } + deviceAddress = append(deviceAddress, common.BytesToAddress(addr.Bytes())) + } + + caller, err := NewContractCaller(projectDeviceABI, projectDeviceAddress) + if err != nil { + return "", errors.Wrap(err, "failed to create contract caller") + } + + value := new(contracts.ProjectDeviceUnapprove) + result := NewContractResult(&projectDeviceABI, eventOnUnapprove, value) + if _, err = caller.CallAndRetrieveResult(funcDevicesUnapprove, []any{ + big.NewInt(int64(projectID)), + deviceAddress, + }, result); err != nil { + return "", errors.Wrap(err, "failed to read contract") + } + if _, err = result.Result(); err != nil { + return "", err + } + + return fmt.Sprintf("unapprove %d device", len(deviceAddress)), nil +}