From 3ebc0170a79442509c642c0003dfaea7614e3220 Mon Sep 17 00:00:00 2001 From: saito Date: Thu, 30 May 2024 13:10:53 +0800 Subject: [PATCH] [ioctl][ws][#4] prover sub commands to support interaction with ws contracts (#4282) * feat(ioctl/ws): sync latest w3bstream contract abis and generate go code * feat(ioctl/config): add ws contract address configurations * feat(ioctl/ws): ioctl support ws project contracts interactions * feat(ioctl/ws): ioctl support ws prover contracts interactions --- ioctl/cmd/ws/wsprover.go | 89 ++++++++++++++++++++++++++ ioctl/cmd/ws/wsproverquery.go | 105 +++++++++++++++++++++++++++++++ ioctl/cmd/ws/wsproverregister.go | 54 ++++++++++++++++ ioctl/cmd/ws/wsproverstate.go | 96 ++++++++++++++++++++++++++++ ioctl/cmd/ws/wsprovertransfer.go | 70 +++++++++++++++++++++ ioctl/cmd/ws/wsproverupdate.go | 66 +++++++++++++++++++ 6 files changed, 480 insertions(+) create mode 100644 ioctl/cmd/ws/wsprover.go create mode 100644 ioctl/cmd/ws/wsproverquery.go create mode 100644 ioctl/cmd/ws/wsproverregister.go create mode 100644 ioctl/cmd/ws/wsproverstate.go create mode 100644 ioctl/cmd/ws/wsprovertransfer.go create mode 100644 ioctl/cmd/ws/wsproverupdate.go diff --git a/ioctl/cmd/ws/wsprover.go b/ioctl/cmd/ws/wsprover.go new file mode 100644 index 0000000000..fc1f40afe6 --- /dev/null +++ b/ioctl/cmd/ws/wsprover.go @@ -0,0 +1,89 @@ +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" + "github.com/iotexproject/iotex-core/ioctl/flag" +) + +var wsProverCmd = &cobra.Command{ + Use: "prover", + Short: config.TranslateInLang(map[config.Language]string{ + config.English: "w3bstream prover management", + config.Chinese: "w3bstream prover 节点管理", + }, config.UILanguage), +} + +var ( + proverID = flag.NewUint64VarP("id", "", 0, config.TranslateInLang(_flagProverIDUsages, config.UILanguage)) + proverNodeType = flag.NewUint64VarP("node-type", "", 0, config.TranslateInLang(_flagProverNodeTypeUsages, config.UILanguage)) + proverOperator = flag.NewStringVarP("operator", "", "", config.TranslateInLang(_flagProverOperatorUsages, config.UILanguage)) +) + +var ( + _flagProverIDUsages = map[config.Language]string{ + config.English: "prover id", + config.Chinese: "prover(计算节点) ID", + } + _flagProverNodeTypeUsages = map[config.Language]string{ + config.English: "prover node type", + config.Chinese: "prover(计算节点) 节点类型", + } + _flagProverOperatorUsages = map[config.Language]string{ + config.English: "prover node operator", + config.Chinese: "prover(计算节点)操作者", + } +) + +var ( + //go:embed contracts/abis/W3bstreamProver.json + proverStoreJSON []byte + proverStoreAddress string + proverStoreABI abi.ABI + + //go:embed contracts/abis/FleetManagement.json + fleetManagementJSON []byte + fleetManagementAddress string + fleetManagementABI abi.ABI +) + +const ( + funcProverRegister = "register" + funcUpdateProver = "updateNodeType" + funcQueryProverNodeType = "nodeType" + funcQueryProverIsPaused = "isPaused" + funcQueryProverOperator = "operator" + funcQueryProverOwner = "prover" + funcPauseProver = "pause" + funcResumeProver = "resume" + funcChangeProverOwner = "changeOperator" +) + +const ( + eventOnProverRegistered = "Transfer" + eventOnProverUpdated = "NodeTypeUpdated" + eventOnProverPaused = "ProverPaused" + eventOnProverResumed = "ProverResumed" + eventOnProverOwnerChanged = "OperatorSet" +) + +func init() { + var err error + proverStoreABI, err = abi.JSON(bytes.NewReader(proverStoreJSON)) + if err != nil { + panic(err) + } + proverStoreAddress = config.ReadConfig.WsProverStoreContract + fleetManagementABI, err = abi.JSON(bytes.NewReader(fleetManagementJSON)) + if err != nil { + panic(err) + } + fleetManagementAddress = config.ReadConfig.WsFleetManagementContract + + WsCmd.AddCommand(wsProverCmd) +} diff --git a/ioctl/cmd/ws/wsproverquery.go b/ioctl/cmd/ws/wsproverquery.go new file mode 100644 index 0000000000..c4019194c4 --- /dev/null +++ b/ioctl/cmd/ws/wsproverquery.go @@ -0,0 +1,105 @@ +package ws + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/iotexproject/iotex-core/ioctl/config" + "github.com/iotexproject/iotex-core/ioctl/output" + "github.com/iotexproject/iotex-core/ioctl/util" +) + +var wsProverQueryCmd = &cobra.Command{ + Use: "query", + Short: config.TranslateInLang(map[config.Language]string{ + config.English: "query prover", + config.Chinese: "查询prover节点信息", + }, config.UILanguage), + RunE: func(cmd *cobra.Command, args []string) error { + out, err := queryProver(big.NewInt(int64(proverID.Value().(uint64)))) + if err != nil { + return output.PrintError(err) + } + output.PrintResult(output.JSONString(out)) + return nil + }, +} + +func init() { + proverID.RegisterCommand(wsProverQueryCmd) + proverID.MarkFlagRequired(wsProverQueryCmd) + + wsProverCmd.AddCommand(wsProverQueryCmd) +} + +func queryProver(proverID *big.Int) (any, error) { + caller, err := NewContractCaller(proverStoreABI, proverStoreAddress) + if err != nil { + return nil, errors.Wrap(err, "failed to new contract caller") + } + result := NewContractResult(&proverStoreABI, funcQueryProverNodeType, new(big.Int)) + if err = caller.Read(funcQueryProverNodeType, []any{proverID}, result); err != nil { + return nil, errors.Wrapf(err, "failed to read contract: %s", funcQueryProverNodeType) + } + + nodeType, err := result.Result() + if err != nil { + return nil, err + } + + result = NewContractResult(&proverStoreABI, funcQueryProverIsPaused, new(bool)) + if err = caller.Read(funcQueryProverIsPaused, []any{proverID}, result); err != nil { + return nil, errors.Wrapf(err, "failed to read contract: %s", funcQueryProverIsPaused) + } + isPaused, err := result.Result() + if err != nil { + return nil, err + } + + result = NewContractResult(&proverStoreABI, funcQueryProverOperator, &common.Address{}) + if err = caller.Read(funcQueryProverOperator, []any{proverID}, result); err != nil { + return nil, errors.Wrapf(err, "failed to read contract: %s", funcQueryProverOperator) + } + operator, err := result.Result() + if err != nil { + return nil, err + } + + result = NewContractResult(&proverStoreABI, funcQueryProverOwner, &common.Address{}) + if err = caller.Read(funcQueryProverOwner, []any{proverID}, result); err != nil { + return nil, errors.Wrapf(err, "failed to read contract: %s", funcQueryProverOwner) + } + owner, err := result.Result() + if err != nil { + return nil, err + } + + operatorAddr, err := util.Address((*operator.(*common.Address)).String()) + if err != nil { + return nil, errors.Wrapf(err, "failed to convert operator address") + } + + ownerAddr, err := util.Address((*owner.(*common.Address)).String()) + if err != nil { + return nil, errors.Wrapf(err, "failed to convert prover address") + } + + return &struct { + ProverID uint64 `json:"proverID"` + Prover string `json:"owner"` + NodeType uint64 `json:"nodeType"` + IsPaused bool `json:"isPaused"` + Operator string `json:"operator"` + }{ + ProverID: proverID.Uint64(), + Prover: ownerAddr, + NodeType: nodeType.(*big.Int).Uint64(), + IsPaused: *isPaused.(*bool), + Operator: operatorAddr, + }, nil + +} diff --git a/ioctl/cmd/ws/wsproverregister.go b/ioctl/cmd/ws/wsproverregister.go new file mode 100644 index 0000000000..d0c579b3d3 --- /dev/null +++ b/ioctl/cmd/ws/wsproverregister.go @@ -0,0 +1,54 @@ +package ws + +import ( + "math/big" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/iotexproject/iotex-core/ioctl/cmd/ws/contracts" + "github.com/iotexproject/iotex-core/ioctl/config" + "github.com/iotexproject/iotex-core/ioctl/output" +) + +var wsProverRegisterCmd = &cobra.Command{ + Use: "register", + Short: config.TranslateInLang(map[config.Language]string{ + config.English: "register prover", + config.Chinese: "注册prover节点", + }, config.UILanguage), + RunE: func(cmd *cobra.Command, args []string) error { + out, err := registerProver() + if err != nil { + return output.PrintError(err) + } + output.PrintResult(output.JSONString(out)) + return nil + }, +} + +func init() { + transferAmount.RegisterCommand(wsProverRegisterCmd) + + wsProverCmd.AddCommand(wsProverRegisterCmd) +} + +func registerProver() (any, error) { + caller, err := NewContractCaller(fleetManagementABI, fleetManagementAddress) + if err != nil { + return nil, errors.Wrap(err, "failed to create contract caller") + } + caller.SetAmount(big.NewInt(int64(transferAmount.Value().(uint64)))) + + value := new(contracts.W3bstreamProverTransfer) + result := NewContractResult(&proverStoreABI, eventOnProverRegistered, value) + if _, err = caller.CallAndRetrieveResult(funcProverRegister, nil, result); err != nil { + return nil, errors.Wrap(err, "failed to call contract") + } + + if _, err = result.Result(); err != nil { + return nil, err + } + + return queryProver(value.TokenId) +} diff --git a/ioctl/cmd/ws/wsproverstate.go b/ioctl/cmd/ws/wsproverstate.go new file mode 100644 index 0000000000..c2abda5b58 --- /dev/null +++ b/ioctl/cmd/ws/wsproverstate.go @@ -0,0 +1,96 @@ +package ws + +import ( + "fmt" + "math/big" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/iotexproject/iotex-core/ioctl/config" + "github.com/iotexproject/iotex-core/ioctl/output" +) + +var wsProverPauseCmd = &cobra.Command{ + Use: "pause", + Short: config.TranslateInLang(map[config.Language]string{ + config.English: "pause prover", + config.Chinese: "停止prover", + }, config.UILanguage), + RunE: func(cmd *cobra.Command, args []string) error { + id := big.NewInt(int64(proverID.Value().(uint64))) + out, err := pauseProver(id) + if err != nil { + return output.PrintError(err) + } + output.PrintResult(fmt.Sprintf("prover %d paused", id)) + output.PrintResult(output.JSONString(out)) + return nil + }, +} + +var wsProverResumeCmd = &cobra.Command{ + Use: "resume", + Short: config.TranslateInLang(map[config.Language]string{ + config.English: "resume prover", + config.Chinese: "启动prover", + }, config.UILanguage), + RunE: func(cmd *cobra.Command, args []string) error { + id := big.NewInt(int64(proverID.Value().(uint64))) + out, err := resumeProver(id) + if err != nil { + return output.PrintError(err) + } + output.PrintResult(fmt.Sprintf("prover %d resumed", id)) + output.PrintResult(output.JSONString(out)) + return nil + }, +} + +func init() { + proverID.RegisterCommand(wsProverPauseCmd) + proverID.MarkFlagRequired(wsProverPauseCmd) + + proverID.RegisterCommand(wsProverResumeCmd) + proverID.MarkFlagRequired(wsProverResumeCmd) + + wsProverCmd.AddCommand(wsProverPauseCmd) + wsProverCmd.AddCommand(wsProverResumeCmd) +} + +func pauseProver(proverID *big.Int) (any, error) { + caller, err := NewContractCaller(proverStoreABI, proverStoreAddress) + if err != nil { + return nil, errors.Wrap(err, "failed to new contract caller") + } + + result := NewContractResult(&proverStoreABI, eventOnProverPaused, nil) + _, err = caller.CallAndRetrieveResult(funcPauseProver, []any{proverID}, result) + if err != nil { + return nil, errors.Wrap(err, "failed to call contract") + } + _, err = result.Result() + if err != nil { + return nil, err + } + return queryProver(proverID) +} + +func resumeProver(proverID *big.Int) (any, error) { + caller, err := NewContractCaller(proverStoreABI, proverStoreAddress) + if err != nil { + return nil, errors.Wrap(err, "failed to new contract caller") + } + + result := NewContractResult(&proverStoreABI, eventOnProverResumed, nil) + _, err = caller.CallAndRetrieveResult(funcResumeProver, []any{proverID}, result) + if err != nil { + return nil, errors.Wrap(err, "failed to call contract") + } + _, err = result.Result() + if err != nil { + return nil, err + } + + return queryProver(proverID) +} diff --git a/ioctl/cmd/ws/wsprovertransfer.go b/ioctl/cmd/ws/wsprovertransfer.go new file mode 100644 index 0000000000..4f0548581f --- /dev/null +++ b/ioctl/cmd/ws/wsprovertransfer.go @@ -0,0 +1,70 @@ +package ws + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/iotexproject/iotex-address/address" + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/iotexproject/iotex-core/ioctl/cmd/ws/contracts" + "github.com/iotexproject/iotex-core/ioctl/config" + "github.com/iotexproject/iotex-core/ioctl/output" +) + +var wsProverTransferCmd = &cobra.Command{ + Use: "transfer", + Short: config.TranslateInLang(map[config.Language]string{ + config.English: "transfer prover operator", + config.Chinese: "更换prover操作者", + }, config.UILanguage), + RunE: func(cmd *cobra.Command, args []string) error { + operator := proverOperator.Value().(string) + addr, err := address.FromString(operator) + if err != nil { + return output.PrintError(errors.Wrapf(err, "invalid operator address: %s", operator)) + } + id := big.NewInt(int64(proverID.Value().(uint64))) + newoperator := common.BytesToAddress(addr.Bytes()) + + output.PrintResult(fmt.Sprintf("transfer prover %d operator to %s", id.Int64(), operator)) + + out, err := transfer(id, newoperator) + if err != nil { + return output.PrintError(err) + } + output.PrintResult(output.JSONString(out)) + return nil + }, +} + +func init() { + proverID.RegisterCommand(wsProverTransferCmd) + proverID.MarkFlagRequired(wsProverTransferCmd) + + proverOperator.RegisterCommand(wsProverTransferCmd) + proverOperator.MarkFlagRequired(wsProverTransferCmd) + + wsProverCmd.AddCommand(wsProverTransferCmd) +} + +func transfer(proverID *big.Int, operator common.Address) (any, error) { + caller, err := NewContractCaller(proverStoreABI, proverStoreAddress) + if err != nil { + return nil, errors.Wrap(err, "failed to create contract caller") + } + + value := new(contracts.W3bstreamProverOperatorSet) + result := NewContractResult(&proverStoreABI, eventOnProverOwnerChanged, value) + if _, err = caller.CallAndRetrieveResult(funcChangeProverOwner, []any{proverID, operator}, result); err != nil { + return nil, errors.Wrap(err, "failed to call contract") + } + + if _, err = result.Result(); err != nil { + return nil, err + } + + return queryProver(proverID) +} diff --git a/ioctl/cmd/ws/wsproverupdate.go b/ioctl/cmd/ws/wsproverupdate.go new file mode 100644 index 0000000000..1de499039a --- /dev/null +++ b/ioctl/cmd/ws/wsproverupdate.go @@ -0,0 +1,66 @@ +package ws + +import ( + "math/big" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/iotexproject/iotex-core/ioctl/cmd/ws/contracts" + "github.com/iotexproject/iotex-core/ioctl/config" + "github.com/iotexproject/iotex-core/ioctl/output" +) + +var wsProverUpdateCmd = &cobra.Command{ + Use: "update", + Short: config.TranslateInLang(map[config.Language]string{ + config.English: "update prover", + config.Chinese: "更新prover节点类型", + }, config.UILanguage), + RunE: func(cmd *cobra.Command, args []string) error { + out, err := updateProver( + big.NewInt(int64(proverID.Value().(uint64))), + big.NewInt(int64(proverNodeType.Value().(uint64))), + ) + if err != nil { + return output.PrintError(err) + } + output.PrintResult(output.JSONString(out)) + return nil + }, +} + +func init() { + proverID.RegisterCommand(wsProverUpdateCmd) + proverID.MarkFlagRequired(wsProverUpdateCmd) + + proverNodeType.RegisterCommand(wsProverUpdateCmd) + proverNodeType.MarkFlagRequired(wsProverUpdateCmd) + + wsProverCmd.AddCommand(wsProverUpdateCmd) +} + +func updateProver(proverID, nodeType *big.Int) (any, error) { + caller, err := NewContractCaller(proverStoreABI, proverStoreAddress) + if err != nil { + return nil, errors.Wrap(err, "failed to create contract caller") + } + + value := new(contracts.W3bstreamProverNodeTypeUpdated) + result := NewContractResult(&proverStoreABI, eventOnProverUpdated, value) + if _, err = caller.CallAndRetrieveResult(funcUpdateProver, []any{proverID, nodeType}, result); err != nil { + return nil, errors.Wrap(err, "failed to call contract") + } + + if _, err = result.Result(); err != nil { + return nil, err + } + + return &struct { + ProverID *big.Int `json:"proverID"` + NodeType *big.Int `json:"nodeType"` + }{ + ProverID: value.Id, + NodeType: value.Typ, + }, nil +}