diff --git a/cmd/neofs-adm/internal/modules/morph/balance.go b/cmd/neofs-adm/internal/modules/morph/balance.go index 480a3e9d..7ce05682 100644 --- a/cmd/neofs-adm/internal/modules/morph/balance.go +++ b/cmd/neofs-adm/internal/modules/morph/balance.go @@ -13,6 +13,8 @@ import ( "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" @@ -55,6 +57,8 @@ func dumpBalances(cmd *cobra.Command, _ []string) error { return err } + inv := invoker.New(c, nil) + ns, err := getNativeHashes(c) if err != nil { return fmt.Errorf("can't fetch the list of native contracts: %w", err) @@ -76,7 +80,7 @@ func dumpBalances(cmd *cobra.Command, _ []string) error { return fmt.Errorf("can't get NNS contract info: %w", err) } - nmHash, err = nnsResolveHash(c, nnsCs.Hash, netmapContract+".neofs") + nmHash, err = nnsResolveHash(inv, nnsCs.Hash, netmapContract+".neofs") if err != nil { return fmt.Errorf("can't get netmap contract hash: %w", err) } @@ -87,18 +91,14 @@ func dumpBalances(cmd *cobra.Command, _ []string) error { return err } - if err := fetchBalances(c, gasHash, irList); err != nil { + if err := fetchBalances(inv, gasHash, irList); err != nil { return err } printBalances(cmd, "Inner ring nodes balances:", irList) if dumpStorage { - res, err := invokeFunction(c, nmHash, "netmap", []interface{}{}, nil) - if err != nil || res.State != vmstate.Halt.String() || len(res.Stack) == 0 { - return errors.New("can't fetch the list of storage nodes") - } - arr, ok := res.Stack[0].Value().([]stackitem.Item) - if !ok { + arr, err := unwrap.Array(inv.Call(nmHash, "netmap")) + if err != nil { return errors.New("can't fetch the list of storage nodes") } @@ -123,20 +123,20 @@ func dumpBalances(cmd *cobra.Command, _ []string) error { snList[i].scriptHash = pub.GetScriptHash() } - if err := fetchBalances(c, gasHash, snList); err != nil { + if err := fetchBalances(inv, gasHash, snList); err != nil { return err } printBalances(cmd, "\nStorage node balances:", snList) } if dumpProxy { - h, err := nnsResolveHash(c, nnsCs.Hash, proxyContract+".neofs") + h, err := nnsResolveHash(inv, nnsCs.Hash, proxyContract+".neofs") if err != nil { return fmt.Errorf("can't get hash of the proxy contract: %w", err) } proxyList := []accBalancePair{{scriptHash: h}} - if err := fetchBalances(c, gasHash, proxyList); err != nil { + if err := fetchBalances(inv, gasHash, proxyList); err != nil { return err } printBalances(cmd, "\nProxy contract balance:", proxyList) @@ -168,7 +168,7 @@ func dumpBalances(cmd *cobra.Command, _ []string) error { alphaList[i].scriptHash = h } - if err := fetchBalances(c, gasHash, alphaList); err != nil { + if err := fetchBalances(inv, gasHash, alphaList); err != nil { return err } printBalances(cmd, "\nAlphabet contracts balances:", alphaList) @@ -180,13 +180,15 @@ func dumpBalances(cmd *cobra.Command, _ []string) error { func fetchIRNodes(c Client, nmHash, desigHash util.Uint160) ([]accBalancePair, error) { var irList []accBalancePair + inv := invoker.New(c, nil) + if notaryEnabled { height, err := c.GetBlockCount() if err != nil { return nil, fmt.Errorf("can't get block height: %w", err) } - arr, err := getDesignatedByRole(c, desigHash, noderoles.NeoFSAlphabet, height) + arr, err := getDesignatedByRole(inv, desigHash, noderoles.NeoFSAlphabet, height) if err != nil { return nil, errors.New("can't fetch list of IR nodes from the netmap contract") } @@ -196,27 +198,14 @@ func fetchIRNodes(c Client, nmHash, desigHash util.Uint160) ([]accBalancePair, e irList[i].scriptHash = arr[i].GetScriptHash() } } else { - res, err := invokeFunction(c, nmHash, "innerRingList", []interface{}{}, nil) - if err != nil || res.State != vmstate.Halt.String() || len(res.Stack) == 0 { + arr, err := unwrap.ArrayOfBytes(inv.Call(nmHash, "innerRingList")) + if err != nil { return nil, errors.New("can't fetch list of IR nodes from the netmap contract") } - arr, ok := res.Stack[0].Value().([]stackitem.Item) - if !ok || len(arr) == 0 { - return nil, errors.New("can't fetch list of IR nodes: invalid response") - } - irList = make([]accBalancePair, len(arr)) for i := range arr { - node, ok := arr[i].Value().([]stackitem.Item) - if !ok || len(arr) == 0 { - return nil, errors.New("can't fetch list of IR nodes: invalid response") - } - bs, err := node[0].TryBytes() - if err != nil { - return nil, fmt.Errorf("can't fetch list of IR nodes: %w", err) - } - pub, err := keys.NewPublicKeyFromBytes(bs, elliptic.P256()) + pub, err := keys.NewPublicKeyFromBytes(arr[i], elliptic.P256()) if err != nil { return nil, fmt.Errorf("can't parse IR node public key: %w", err) } @@ -241,7 +230,7 @@ func printBalances(cmd *cobra.Command, prefix string, accounts []accBalancePair) } } -func fetchBalances(c Client, gasHash util.Uint160, accounts []accBalancePair) error { +func fetchBalances(c *invoker.Invoker, gasHash util.Uint160, accounts []accBalancePair) error { w := io.NewBufBinWriter() for i := range accounts { emit.AppCall(w.BinWriter, gasHash, "balanceOf", callflag.ReadStates, accounts[i].scriptHash) @@ -250,7 +239,7 @@ func fetchBalances(c Client, gasHash util.Uint160, accounts []accBalancePair) er panic(w.Err) } - res, err := c.InvokeScript(w.Bytes(), nil) + res, err := c.Run(w.Bytes()) if err != nil || res.State != vmstate.Halt.String() || len(res.Stack) != len(accounts) { return errors.New("can't fetch account balances") } diff --git a/cmd/neofs-adm/internal/modules/morph/config.go b/cmd/neofs-adm/internal/modules/morph/config.go index 3e7da25c..03c4625b 100644 --- a/cmd/neofs-adm/internal/modules/morph/config.go +++ b/cmd/neofs-adm/internal/modules/morph/config.go @@ -11,10 +11,11 @@ import ( "text/tabwriter" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" - "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -27,26 +28,23 @@ func dumpNetworkConfig(cmd *cobra.Command, _ []string) error { return fmt.Errorf("can't create N3 client: %w", err) } + inv := invoker.New(c, nil) + cs, err := c.GetContractStateByID(1) if err != nil { return fmt.Errorf("can't get NNS contract info: %w", err) } - nmHash, err := nnsResolveHash(c, cs.Hash, netmapContract+".neofs") + nmHash, err := nnsResolveHash(inv, cs.Hash, netmapContract+".neofs") if err != nil { return fmt.Errorf("can't get netmap contract hash: %w", err) } - res, err := invokeFunction(c, nmHash, "listConfig", nil, nil) - if err != nil || res.State != vmstate.Halt.String() || len(res.Stack) == 0 { + arr, err := unwrap.Array(inv.Call(nmHash, "listConfig")) + if err != nil { return errors.New("can't fetch list of network config keys from the netmap contract") } - arr, ok := res.Stack[0].Value().([]stackitem.Item) - if !ok { - return errors.New("invalid ListConfig response from netmap contract") - } - buf := bytes.NewBuffer(nil) tw := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0) @@ -111,7 +109,7 @@ func setConfigCmd(cmd *cobra.Command, args []string) error { return fmt.Errorf("can't get NNS contract info: %w", err) } - nmHash, err := nnsResolveHash(wCtx.Client, cs.Hash, netmapContract+".neofs") + nmHash, err := nnsResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, netmapContract+".neofs") if err != nil { return fmt.Errorf("can't get netmap contract hash: %w", err) } diff --git a/cmd/neofs-adm/internal/modules/morph/container.go b/cmd/neofs-adm/internal/modules/morph/container.go index b7ae960d..4eb89d03 100644 --- a/cmd/neofs-adm/internal/modules/morph/container.go +++ b/cmd/neofs-adm/internal/modules/morph/container.go @@ -9,6 +9,8 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" @@ -31,6 +33,8 @@ func dumpContainers(cmd *cobra.Command, _ []string) error { return fmt.Errorf("can't create N3 client: %w", err) } + inv := invoker.New(c, nil) + nnsCs, err := c.GetContractStateByID(1) if err != nil { return fmt.Errorf("can't get NNS contract state: %w", err) @@ -42,30 +46,17 @@ func dumpContainers(cmd *cobra.Command, _ []string) error { ch, err = util.Uint160DecodeStringLE(s) } if err != nil { - ch, err = nnsResolveHash(c, nnsCs.Hash, containerContract+".neofs") + ch, err = nnsResolveHash(inv, nnsCs.Hash, containerContract+".neofs") if err != nil { return err } } - res, err := invokeFunction(c, ch, "list", []interface{}{""}, nil) + cids, err := unwrap.ArrayOfBytes(inv.Call(ch, "list", "")) if err != nil { return fmt.Errorf("%w: %v", errInvalidContainerResponse, err) } - var cids [][]byte - arr, ok := res.Stack[0].Value().([]stackitem.Item) - if !ok { - return fmt.Errorf("%w: not a struct", errInvalidContainerResponse) - } - for _, item := range arr { - id, err := item.TryBytes() - if err != nil { - return fmt.Errorf("%w: %v", errInvalidContainerResponse, err) - } - cids = append(cids, id) - } - isOK, err := getCIDFilterFunc(cmd) if err != nil { return err @@ -80,7 +71,7 @@ func dumpContainers(cmd *cobra.Command, _ []string) error { bw.Reset() emit.AppCall(bw.BinWriter, ch, "get", callflag.All, id) emit.AppCall(bw.BinWriter, ch, "eACL", callflag.All, id) - res, err := c.InvokeScript(bw.Bytes(), nil) + res, err := inv.Run(bw.Bytes()) if err != nil { return fmt.Errorf("can't get container info: %w", err) } @@ -129,7 +120,7 @@ func restoreContainers(cmd *cobra.Command, _ []string) error { return fmt.Errorf("can't get NNS contract state: %w", err) } - ch, err := nnsResolveHash(wCtx.Client, nnsCs.Hash, containerContract+".neofs") + ch, err := nnsResolveHash(wCtx.ReadOnlyInvoker, nnsCs.Hash, containerContract+".neofs") if err != nil { return fmt.Errorf("can't fetch container contract hash: %w", err) } diff --git a/cmd/neofs-adm/internal/modules/morph/deploy.go b/cmd/neofs-adm/internal/modules/morph/deploy.go index b164d85e..fea258a1 100644 --- a/cmd/neofs-adm/internal/modules/morph/deploy.go +++ b/cmd/neofs-adm/internal/modules/morph/deploy.go @@ -87,7 +87,7 @@ func deployContractCmd(cmd *cobra.Command, args []string) error { domain := ctrName + "." + zone isUpdate, _ := cmd.Flags().GetBool(updateFlag) if isUpdate { - cs.Hash, err = nnsResolveHash(c.Client, nnsCs.Hash, domain) + cs.Hash, err = nnsResolveHash(c.ReadOnlyInvoker, nnsCs.Hash, domain) if err != nil { return fmt.Errorf("can't fetch contract hash from NNS: %w", err) } diff --git a/cmd/neofs-adm/internal/modules/morph/dump_hashes.go b/cmd/neofs-adm/internal/modules/morph/dump_hashes.go index 349dd1bb..738b3590 100644 --- a/cmd/neofs-adm/internal/modules/morph/dump_hashes.go +++ b/cmd/neofs-adm/internal/modules/morph/dump_hashes.go @@ -6,10 +6,8 @@ import ( "strings" "text/tabwriter" - "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/io" - "github.com/nspcc-dev/neo-go/pkg/rpcclient" - "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" @@ -17,7 +15,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" - "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neofs-contract/nns" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -110,25 +107,9 @@ func dumpContractHashes(cmd *cobra.Command, _ []string) error { func dumpCustomZoneHashes(cmd *cobra.Command, nnsHash util.Uint160, zone string, c Client) error { const nnsMaxTokens = 100 + inv := invoker.New(c, nil) - // The actual signer is not important here. - account, err := wallet.NewAccount() - if err != nil { - return fmt.Errorf("can't create a temporary account: %w", err) - } - - signers := []actor.SignerAccount{{ - Signer: transaction.Signer{ - Account: account.PrivateKey().GetScriptHash(), - }, - Account: account, - }} - a, err := actor.New(c.(*rpcclient.Client), signers) - if err != nil { - return fmt.Errorf("can't get a list of NNS domains: %w", err) - } - - arr, err := unwrap.Array(a.CallAndExpandIterator(nnsHash, "tokens", nnsMaxTokens)) + arr, err := unwrap.Array(inv.CallAndExpandIterator(nnsHash, "tokens", nnsMaxTokens)) if err != nil { return fmt.Errorf("can't get a list of NNS domains: %w", err) } @@ -148,7 +129,7 @@ func dumpCustomZoneHashes(cmd *cobra.Command, nnsHash util.Uint160, zone string, continue } - h, err := nnsResolveHash(c, nnsHash, string(bs)) + h, err := nnsResolveHash(inv, nnsHash, string(bs)) if err != nil { continue } diff --git a/cmd/neofs-adm/internal/modules/morph/epoch.go b/cmd/neofs-adm/internal/modules/morph/epoch.go index 4e7637f7..bee04860 100644 --- a/cmd/neofs-adm/internal/modules/morph/epoch.go +++ b/cmd/neofs-adm/internal/modules/morph/epoch.go @@ -5,10 +5,10 @@ import ( "fmt" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" - "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -24,7 +24,7 @@ func forceNewEpochCmd(cmd *cobra.Command, args []string) error { return fmt.Errorf("can't get NNS contract info: %w", err) } - nmHash, err := nnsResolveHash(wCtx.Client, cs.Hash, netmapContract+".neofs") + nmHash, err := nnsResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, netmapContract+".neofs") if err != nil { return fmt.Errorf("can't get netmap contract hash: %w", err) } @@ -42,18 +42,13 @@ func forceNewEpochCmd(cmd *cobra.Command, args []string) error { } func emitNewEpochCall(bw *io.BufBinWriter, wCtx *initializeContext, nmHash util.Uint160) error { - res, err := invokeFunction(wCtx.Client, nmHash, "epoch", nil, nil) - if err != nil || res.State != vmstate.Halt.String() || len(res.Stack) == 0 { + curr, err := unwrap.Int64(wCtx.ReadOnlyInvoker.Call(nmHash, "epoch")) + if err != nil { return errors.New("can't fetch current epoch from the netmap contract") } - bi, err := res.Stack[0].TryInteger() - if err != nil { - return fmt.Errorf("can't parse current epoch: %w", err) - } - - newEpoch := bi.Int64() + 1 - wCtx.Command.Printf("Current epoch: %s, increase to %d.\n", bi, newEpoch) + newEpoch := curr + 1 + wCtx.Command.Printf("Current epoch: %d, increase to %d.\n", curr, newEpoch) // In NeoFS this is done via Notary contract. Here, however, we can form the // transaction locally. diff --git a/cmd/neofs-adm/internal/modules/morph/initialize.go b/cmd/neofs-adm/internal/modules/morph/initialize.go index b2b10b8c..dabc833b 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize.go @@ -183,8 +183,13 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex accounts[i] = acc } + cliCtx, err := defaultClientContext(c, committeeAcc) + if err != nil { + return nil, fmt.Errorf("client context: %w", err) + } + initCtx := &initializeContext{ - clientContext: *defaultClientContext(c), + clientContext: *cliCtx, ConsensusAcc: consensusAcc, CommitteeAcc: committeeAcc, ContractWallet: w, @@ -299,7 +304,7 @@ func (c *initializeContext) getSigner(tryGroup bool) transaction.Signer { return signer } - groupKey, err := nnsResolveKey(c.Client, nnsCs.Hash, morphClient.NNSGroupKeyName) + groupKey, err := nnsResolveKey(c.ReadOnlyInvoker, nnsCs.Hash, morphClient.NNSGroupKeyName) if err == nil { c.groupKey = groupKey @@ -320,12 +325,24 @@ func (c *clientContext) awaitTx(cmd *cobra.Command) error { } } + err := awaitTx(cmd, c.Client, c.Hashes) + c.Hashes = c.Hashes[:0] + + return err +} + +func awaitTx(cmd *cobra.Command, c Client, hashes []util.Uint256) error { cmd.Println("Waiting for transactions to persist...") - tick := time.NewTicker(c.PollInterval) + // improve TX awaiting process: + // https://github.com/nspcc-dev/neofs-node/issues/1741 + const pollInterval = time.Second + const waitDuration = 30 * time.Second + + tick := time.NewTicker(pollInterval) defer tick.Stop() - timer := time.NewTimer(c.WaitDuration) + timer := time.NewTimer(waitDuration) defer timer.Stop() at := trigger.Application @@ -333,8 +350,8 @@ func (c *clientContext) awaitTx(cmd *cobra.Command) error { var retErr error loop: - for i := range c.Hashes { - res, err := c.Client.GetApplicationLog(c.Hashes[i], &at) + for i := range hashes { + res, err := c.GetApplicationLog(hashes[i], &at) if err == nil { if retErr == nil && len(res.Executions) > 0 && res.Executions[0].VMState != vmstate.Halt { retErr = fmt.Errorf("tx %d persisted in %s state: %s", @@ -345,7 +362,7 @@ loop: for { select { case <-tick.C: - res, err := c.Client.GetApplicationLog(c.Hashes[i], &at) + res, err := c.GetApplicationLog(hashes[i], &at) if err == nil { if retErr == nil && len(res.Executions) > 0 && res.Executions[0].VMState != vmstate.Halt { retErr = fmt.Errorf("tx %d persisted in %s state: %s", @@ -359,50 +376,34 @@ loop: } } - c.Hashes = c.Hashes[:0] return retErr } // sendCommitteeTx creates transaction from script and sends it to RPC. -// Test invocation will be performed and the result will be checked. If tryGroup -// is false, global scope is used for the signer (useful when working with native -// contracts). +// If tryGroup is false, global scope is used for the signer (useful when +// working with native contracts). func (c *initializeContext) sendCommitteeTx(script []byte, tryGroup bool) error { - sigCount := len(c.CommitteeAcc.Contract.Parameters) + var act *actor.Actor + var err error - signers := make([]actor.SignerAccount, 0, sigCount) - signer := c.getSigner(tryGroup) - - for i, w := range c.Wallets { - if i == sigCount { - break - } - - acc, err := getWalletAccount(w, committeeAccountName) - if err != nil { - return fmt.Errorf("could not get %s account for %d wallet: %w", - committeeAccountName, i, err) - } - - signers = append(signers, actor.SignerAccount{ - Signer: signer, - Account: acc, - }) + if tryGroup { + act, err = actor.New(c.Client, []actor.SignerAccount{{ + Signer: c.getSigner(tryGroup), + Account: c.CommitteeAcc, + }}) + } else { + act, err = c.CommitteeAct, nil } - - act, err := actor.New(c.Client, signers) if err != nil { return fmt.Errorf("could not create actor: %w", err) } - txHash, _, err := act.SendTunedRun(script, []transaction.Attribute{{Type: transaction.HighPriority}}, nil) + tx, err := act.MakeUnsignedRun(script, []transaction.Attribute{{Type: transaction.HighPriority}}) if err != nil { - return fmt.Errorf("cound not send transaction: %w", err) + return fmt.Errorf("could not perform test invocation: %w", err) } - c.Hashes = append(c.Hashes, txHash) - - return nil + return c.multiSignAndSend(tx, committeeAccountName) } func getWalletAccount(w *wallet.Wallet, typ string) (*wallet.Account, error) { diff --git a/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go b/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go index 0c15d22b..d8e26469 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go @@ -181,7 +181,7 @@ func (c *initializeContext) updateContracts() error { // alphabet contracts should be deployed by individual nodes to get different hashes. for i, acc := range c.Accounts { - ctrHash, err := nnsResolveHash(c.Client, nnsHash, getAlphabetNNSDomain(i)) + ctrHash, err := nnsResolveHash(c.ReadOnlyInvoker, nnsHash, getAlphabetNNSDomain(i)) if err != nil { return fmt.Errorf("can't resolve hash for contract update: %w", err) } @@ -219,7 +219,7 @@ func (c *initializeContext) updateContracts() error { cs := c.getContract(ctrName) method := updateMethodName - ctrHash, err := nnsResolveHash(c.Client, nnsHash, ctrName+".neofs") + ctrHash, err := nnsResolveHash(c.ReadOnlyInvoker, nnsHash, ctrName+".neofs") if err != nil { if errors.Is(err, errMissingNNSRecord) { // if contract not found we deploy it instead of update @@ -240,17 +240,9 @@ func (c *initializeContext) updateContracts() error { } params := getContractDeployParameters(cs, c.getContractDeployData(ctrName, keysParam)) - signer := transaction.Signer{ - Account: c.CommitteeAcc.Contract.ScriptHash(), - Scopes: transaction.Global, - } - - res, err := invokeFunction(c.Client, invokeHash, method, params, []transaction.Signer{signer}) + res, err := c.CommitteeAct.MakeCall(invokeHash, method, params) if err != nil { - return fmt.Errorf("can't deploy %s contract: %w", ctrName, err) - } - if res.State != vmstate.Halt.String() { - return fmt.Errorf("can't deploy %s contract: %s", ctrName, res.FaultException) + return fmt.Errorf("deploy contract: %w", err) } w.WriteBytes(res.Script) @@ -343,18 +335,10 @@ func (c *initializeContext) deployContracts() error { } params := getContractDeployParameters(cs, c.getContractDeployData(ctrName, keysParam)) - signer := transaction.Signer{ - Account: c.CommitteeAcc.Contract.ScriptHash(), - Scopes: transaction.Global, - } - - res, err := invokeFunction(c.Client, mgmtHash, deployMethodName, params, []transaction.Signer{signer}) + res, err := c.CommitteeAct.MakeCall(mgmtHash, deployMethodName, params...) if err != nil { return fmt.Errorf("can't deploy %s contract: %w", ctrName, err) } - if res.State != vmstate.Halt.String() { - return fmt.Errorf("can't deploy %s contract: %s", ctrName, res.FaultException) - } if err := c.sendCommitteeTx(res.Script, false); err != nil { return err diff --git a/cmd/neofs-adm/internal/modules/morph/initialize_nns.go b/cmd/neofs-adm/internal/modules/morph/initialize_nns.go index 4cedfba3..d5dee7d7 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize_nns.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize_nns.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "strconv" - "strings" "time" "github.com/nspcc-dev/neo-go/pkg/core/state" @@ -13,6 +12,8 @@ import ( "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" @@ -107,7 +108,7 @@ func (c *initializeContext) emitUpdateNNSGroupScript(bw *io.BufBinWriter, nnsHas } if !isAvail { - currentPub, err := nnsResolveKey(c.Client, nnsHash, morphClient.NNSGroupKeyName) + currentPub, err := nnsResolveKey(c.ReadOnlyInvoker, nnsHash, morphClient.NNSGroupKeyName) if err != nil { return false, false, err } @@ -177,7 +178,7 @@ func (c *initializeContext) nnsRegisterDomainScript(nnsHash, expectedHash util.U return bw.Bytes(), false, nil } - s, err := nnsResolveHash(c.Client, nnsHash, domain) + s, err := nnsResolveHash(c.ReadOnlyInvoker, nnsHash, domain) if err != nil { return nil, false, err } @@ -203,52 +204,39 @@ func (c *initializeContext) nnsRegisterDomain(nnsHash, expectedHash util.Uint160 } func (c *initializeContext) nnsRootRegistered(nnsHash util.Uint160, zone string) (bool, error) { - params := []interface{}{"name." + zone} - res, err := invokeFunction(c.Client, nnsHash, "isAvailable", params, nil) + res, err := c.CommitteeAct.Call(nnsHash, "isAvailable", "name."+zone) if err != nil { return false, err } + return res.State == vmstate.Halt.String(), nil } var errMissingNNSRecord = errors.New("missing NNS record") // Returns errMissingNNSRecord if invocation fault exception contains "token not found". -func nnsResolveHash(c Client, nnsHash util.Uint160, domain string) (util.Uint160, error) { - item, err := nnsResolve(c, nnsHash, domain) +func nnsResolveHash(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (util.Uint160, error) { + item, err := nnsResolve(inv, nnsHash, domain) if err != nil { return util.Uint160{}, err } return parseNNSResolveResult(item) } -func nnsResolve(c Client, nnsHash util.Uint160, domain string) (stackitem.Item, error) { - result, err := invokeFunction(c, nnsHash, "resolve", []interface{}{domain, int64(nns.TXT)}, nil) - if err != nil { - return nil, fmt.Errorf("`resolve`: %w", err) - } - if result.State != vmstate.Halt.String() { - if strings.Contains(result.FaultException, "token not found") { - return nil, errMissingNNSRecord - } - return nil, fmt.Errorf("invocation failed: %s", result.FaultException) - } - if len(result.Stack) == 0 { - return nil, errors.New("result stack is empty") - } - return result.Stack[len(result.Stack)-1], nil +func nnsResolve(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (stackitem.Item, error) { + return unwrap.Item(inv.Call(nnsHash, "resolve", domain, int64(nns.TXT))) } -func nnsResolveKey(c Client, nnsHash util.Uint160, domain string) (*keys.PublicKey, error) { - item, err := nnsResolve(c, nnsHash, domain) +func nnsResolveKey(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (*keys.PublicKey, error) { + item, err := nnsResolve(inv, nnsHash, domain) if err != nil { return nil, err } - arr, ok := item.Value().([]stackitem.Item) - if !ok || len(arr) == 0 { + v, ok := item.Value().(stackitem.Null) + if ok { return nil, errors.New("NNS record is missing") } - bs, err := arr[0].TryBytes() + bs, err := v.TryBytes() if err != nil { return nil, errors.New("malformed response") } @@ -286,24 +274,16 @@ func parseNNSResolveResult(res stackitem.Item) (util.Uint160, error) { return util.Uint160{}, errors.New("no valid hashes are found") } -var errNNSIsAvailableInvalid = errors.New("`isAvailable`: invalid response") - func nnsIsAvailable(c Client, nnsHash util.Uint160, name string) (bool, error) { switch ct := c.(type) { case *rpcclient.Client: return ct.NNSIsAvailable(nnsHash, name) default: - res, err := invokeFunction(c, nnsHash, "isAvailable", []interface{}{name}, nil) + b, err := unwrap.Bool(invokeFunction(c, nnsHash, "isAvailable", []interface{}{name}, nil)) if err != nil { - return false, err - } - if len(res.Stack) == 0 { - return false, errNNSIsAvailableInvalid - } - b, err := res.Stack[0].TryBool() - if err != nil { - return b, errNNSIsAvailableInvalid + return false, fmt.Errorf("`isAvailable`: invalid response: %w", err) } + return b, nil } } diff --git a/cmd/neofs-adm/internal/modules/morph/initialize_register.go b/cmd/neofs-adm/internal/modules/morph/initialize_register.go index 66769fbe..e9e567de 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize_register.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize_register.go @@ -10,12 +10,11 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" - "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" ) // initialAlphabetNEOAmount represents the total amount of GAS distributed between alphabet nodes. @@ -24,16 +23,14 @@ const initialAlphabetNEOAmount = native.NEOTotalSupply func (c *initializeContext) registerCandidates() error { neoHash := c.nativeHash(nativenames.Neo) - res, err := invokeFunction(c.Client, neoHash, "getCandidates", nil, nil) + cc, err := unwrap.Array(c.ReadOnlyInvoker.Call(neoHash, "getCandidates")) if err != nil { - return err + return fmt.Errorf("`getCandidates`: %w", err) } - if res.State == vmstate.Halt.String() && len(res.Stack) > 0 { - arr, ok := res.Stack[0].Value().([]stackitem.Item) - if ok && len(arr) > 0 { - c.Command.Println("Candidates are already registered.") - return nil - } + + if len(cc) > 0 { + c.Command.Println("Candidates are already registered.") + return nil } regPrice, err := c.getCandidateRegisterPrice() @@ -75,7 +72,7 @@ func (c *initializeContext) registerCandidates() error { return fmt.Errorf("can't sign a transaction: %w", err) } - network, _ := c.Client.GetNetwork() + network := c.CommitteeAct.GetNetwork() for i := range c.Accounts { if err := c.Accounts[i].SignTx(network, tx); err != nil { return fmt.Errorf("can't sign a transaction: %w", err) diff --git a/cmd/neofs-adm/internal/modules/morph/initialize_roles.go b/cmd/neofs-adm/internal/modules/morph/initialize_roles.go index 7577b981..0ed924c6 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize_roles.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize_roles.go @@ -43,6 +43,6 @@ func (c *initializeContext) setRolesFinished() (bool, error) { } h := c.nativeHash(nativenames.Designation) - pubs, err := getDesignatedByRole(c.Client, h, noderoles.NeoFSAlphabet, height) + pubs, err := getDesignatedByRole(c.ReadOnlyInvoker, h, noderoles.NeoFSAlphabet, height) return len(pubs) == len(c.Wallets), err } diff --git a/cmd/neofs-adm/internal/modules/morph/local_client.go b/cmd/neofs-adm/internal/modules/morph/local_client.go index e8f23988..dba486b5 100644 --- a/cmd/neofs-adm/internal/modules/morph/local_client.go +++ b/cmd/neofs-adm/internal/modules/morph/local_client.go @@ -24,6 +24,8 @@ import ( "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/network/payload" "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" @@ -32,7 +34,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/spf13/viper" @@ -414,16 +415,9 @@ func invokeFunction(c Client, h util.Uint160, method string, parameters []interf var errGetDesignatedByRoleResponse = errors.New("`getDesignatedByRole`: invalid response") -func getDesignatedByRole(c Client, h util.Uint160, role noderoles.Role, u uint32) (keys.PublicKeys, error) { - res, err := invokeFunction(c, h, "getDesignatedByRole", []interface{}{int64(role), int64(u)}, nil) +func getDesignatedByRole(inv *invoker.Invoker, h util.Uint160, role noderoles.Role, u uint32) (keys.PublicKeys, error) { + arr, err := unwrap.Array(inv.Call(h, "getDesignatedByRole", int64(role), int64(u))) if err != nil { - return nil, err - } - if res.State != vmstate.Halt.String() || len(res.Stack) == 0 { - return nil, errGetDesignatedByRoleResponse - } - arr, ok := res.Stack[0].Value().([]stackitem.Item) - if !ok { return nil, errGetDesignatedByRoleResponse } diff --git a/cmd/neofs-adm/internal/modules/morph/n3client.go b/cmd/neofs-adm/internal/modules/morph/n3client.go index 2ced34c4..13b98715 100644 --- a/cmd/neofs-adm/internal/modules/morph/n3client.go +++ b/cmd/neofs-adm/internal/modules/morph/n3client.go @@ -14,6 +14,8 @@ import ( "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/network/payload" "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" @@ -47,10 +49,10 @@ type Client interface { } type clientContext struct { - Client Client - Hashes []util.Uint256 - WaitDuration time.Duration - PollInterval time.Duration + Client Client // a raw neo-go client OR a local chain implementation + CommitteeAct *actor.Actor // committee actor with the Global witness scope + ReadOnlyInvoker *invoker.Invoker // R/O contract invoker, does not contain any signer + Hashes []util.Uint256 } func getN3Client(v *viper.Viper) (Client, error) { @@ -79,12 +81,23 @@ func getN3Client(v *viper.Viper) (Client, error) { return c, nil } -func defaultClientContext(c Client) *clientContext { - return &clientContext{ - Client: c, - WaitDuration: time.Second * 30, - PollInterval: time.Second, +func defaultClientContext(c Client, committeeAcc *wallet.Account) (*clientContext, error) { + commAct, err := actor.New(c, []actor.SignerAccount{{ + Signer: transaction.Signer{ + Account: committeeAcc.Contract.ScriptHash(), + Scopes: transaction.Global, + }, + Account: committeeAcc, + }}) + if err != nil { + return nil, err } + + return &clientContext{ + Client: c, + CommitteeAct: commAct, + ReadOnlyInvoker: invoker.New(c, nil), + }, nil } func (c *clientContext) sendTx(tx *transaction.Transaction, cmd *cobra.Command, await bool) error { diff --git a/cmd/neofs-adm/internal/modules/morph/notary.go b/cmd/neofs-adm/internal/modules/morph/notary.go index c0a2c18c..abcfb3eb 100644 --- a/cmd/neofs-adm/internal/modules/morph/notary.go +++ b/cmd/neofs-adm/internal/modules/morph/notary.go @@ -12,6 +12,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -126,8 +127,5 @@ func depositNotary(cmd *cobra.Command, _ []string) error { return fmt.Errorf("could not send tx: %w", err) } - cc := defaultClientContext(c) - cc.Hashes = append(cc.Hashes, txHash) - - return cc.awaitTx(cmd) + return awaitTx(cmd, c, []util.Uint256{txHash}) } diff --git a/cmd/neofs-adm/internal/modules/morph/remove_node.go b/cmd/neofs-adm/internal/modules/morph/remove_node.go index b0761239..26c98c1b 100644 --- a/cmd/neofs-adm/internal/modules/morph/remove_node.go +++ b/cmd/neofs-adm/internal/modules/morph/remove_node.go @@ -37,7 +37,7 @@ func removeNodesCmd(cmd *cobra.Command, args []string) error { return fmt.Errorf("can't get NNS contract info: %w", err) } - nmHash, err := nnsResolveHash(wCtx.Client, cs.Hash, netmapContract+".neofs") + nmHash, err := nnsResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, netmapContract+".neofs") if err != nil { return fmt.Errorf("can't get netmap contract hash: %w", err) } diff --git a/cmd/neofs-adm/internal/modules/morph/subnet.go b/cmd/neofs-adm/internal/modules/morph/subnet.go index 52a304a4..1dc70f86 100644 --- a/cmd/neofs-adm/internal/modules/morph/subnet.go +++ b/cmd/neofs-adm/internal/modules/morph/subnet.go @@ -14,6 +14,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" @@ -765,11 +766,6 @@ func testInvokeMethod(key keys.PrivateKey, method string, args ...interface{}) ( return nil, fmt.Errorf("NNS contract resolving: %w", err) } - subnetHash, err := nnsResolveHash(c, nnsCs.Hash, subnetContract+".neofs") - if err != nil { - return nil, fmt.Errorf("subnet hash resolving: %w", err) - } - cosigner := []transaction.Signer{ { Account: key.PublicKey().GetScriptHash(), @@ -777,7 +773,14 @@ func testInvokeMethod(key keys.PrivateKey, method string, args ...interface{}) ( }, } - res, err := invokeFunction(c, subnetHash, method, args, cosigner) + inv := invoker.New(c, cosigner) + + subnetHash, err := nnsResolveHash(inv, nnsCs.Hash, subnetContract+".neofs") + if err != nil { + return nil, fmt.Errorf("subnet hash resolving: %w", err) + } + + res, err := inv.Call(subnetHash, method, args...) if err != nil { return nil, fmt.Errorf("invocation parameters prepararion: %w", err) } @@ -837,11 +840,6 @@ func invokeNonNotary(c Client, key keys.PrivateKey, method string, args ...inter return fmt.Errorf("NNS contract resolving: %w", err) } - subnetHash, err := nnsResolveHash(c, nnsCs.Hash, subnetContract+".neofs") - if err != nil { - return fmt.Errorf("subnet hash resolving: %w", err) - } - acc := wallet.NewAccountFromPrivateKey(&key) cosigner := []transaction.Signer{ @@ -858,7 +856,14 @@ func invokeNonNotary(c Client, key keys.PrivateKey, method string, args ...inter }, } - test, err := invokeFunction(c, subnetHash, method, args, cosigner) + inv := invoker.New(c, cosigner) + + subnetHash, err := nnsResolveHash(inv, nnsCs.Hash, subnetContract+".neofs") + if err != nil { + return fmt.Errorf("subnet hash resolving: %w", err) + } + + test, err := inv.Call(subnetHash, method, args...) if err != nil { return fmt.Errorf("test invocation: %w", err) } @@ -882,11 +887,6 @@ func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util. return fmt.Errorf("NNS contract resolving: %w", err) } - subnetHash, err := nnsResolveHash(c, nnsCs.Hash, subnetContract+".neofs") - if err != nil { - return fmt.Errorf("subnet hash resolving: %w", err) - } - alphabet, err := c.GetCommittee() if err != nil { return fmt.Errorf("alphabet list: %w", err) @@ -902,8 +902,15 @@ func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util. return fmt.Errorf("cosigners collecting: %w", err) } + inv := invoker.New(c, cosigners) + + subnetHash, err := nnsResolveHash(inv, nnsCs.Hash, subnetContract+".neofs") + if err != nil { + return fmt.Errorf("subnet hash resolving: %w", err) + } + // make test invocation of the method - test, err := invokeFunction(c, subnetHash, method, args, cosigners) + test, err := inv.Call(subnetHash, method, args...) if err != nil { return fmt.Errorf("test invocation: %w", err) } @@ -974,7 +981,7 @@ func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util. func notaryCosigners(c Client, notaryHash util.Uint160, nnsCs *state.Contract, key keys.PrivateKey, alphabetAccount util.Uint160) ([]transaction.Signer, error) { - proxyHash, err := nnsResolveHash(c, nnsCs.Hash, proxyContract+".neofs") + proxyHash, err := nnsResolveHash(invoker.New(c, nil), nnsCs.Hash, proxyContract+".neofs") if err != nil { return nil, fmt.Errorf("proxy hash resolving: %w", err) }