diff --git a/cmd/frostfs-adm/internal/modules/morph/ape_util.go b/cmd/frostfs-adm/internal/modules/morph/ape_util.go index 30668532..6b174232 100644 --- a/cmd/frostfs-adm/internal/modules/morph/ape_util.go +++ b/cmd/frostfs-adm/internal/modules/morph/ape_util.go @@ -97,7 +97,7 @@ func newPolicyContractInterface(cmd *cobra.Command) (*morph.ContractStorage, *ac nnsCs, err := r.GetContractByID(1) commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err) - ch, err = nnsResolveHash(inv, nnsCs.Hash, domainOf(policyContract)) + ch, err = morphUtil.NNSResolveHash(inv, nnsCs.Hash, domainOf(policyContract)) commonCmd.ExitOnErr(cmd, "unable to resolve policy contract hash: %w", err) return morph.NewContractStorage(ac, ch), ac diff --git a/cmd/frostfs-adm/internal/modules/morph/balance.go b/cmd/frostfs-adm/internal/modules/morph/balance.go index b4259e11..71bdebc7 100644 --- a/cmd/frostfs-adm/internal/modules/morph/balance.go +++ b/cmd/frostfs-adm/internal/modules/morph/balance.go @@ -64,7 +64,7 @@ func dumpBalances(cmd *cobra.Command, _ []string) error { return fmt.Errorf("can't get NNS contract info: %w", err) } - nmHash, err = nnsResolveHash(inv, nnsCs.Hash, domainOf(netmapContract)) + nmHash, err = morphUtil.NNSResolveHash(inv, nnsCs.Hash, domainOf(netmapContract)) if err != nil { return fmt.Errorf("can't get netmap contract hash: %w", err) } @@ -137,7 +137,7 @@ func printStorageNodeBalances(cmd *cobra.Command, inv *invoker.Invoker, nmHash u } func printProxyContractBalance(cmd *cobra.Command, inv *invoker.Invoker, nnsHash util.Uint160) error { - h, err := nnsResolveHash(inv, nnsHash, domainOf(proxyContract)) + h, err := morphUtil.NNSResolveHash(inv, nnsHash, domainOf(proxyContract)) if err != nil { return fmt.Errorf("can't get hash of the proxy contract: %w", err) } @@ -170,7 +170,7 @@ func printAlphabetContractBalances(cmd *cobra.Command, c morphUtil.Client, inv * } for i := range alphaList { - h, err := parseNNSResolveResult(alphaRes.Stack[i]) + h, err := morphUtil.ParseNNSResolveResult(alphaRes.Stack[i]) if err != nil { return fmt.Errorf("can't fetch the alphabet contract #%d hash: %w", i, err) } diff --git a/cmd/frostfs-adm/internal/modules/morph/config.go b/cmd/frostfs-adm/internal/modules/morph/config.go index d9ddcb8a..a8762ac1 100644 --- a/cmd/frostfs-adm/internal/modules/morph/config.go +++ b/cmd/frostfs-adm/internal/modules/morph/config.go @@ -38,7 +38,7 @@ func dumpNetworkConfig(cmd *cobra.Command, _ []string) error { return fmt.Errorf("can't get NNS contract info: %w", err) } - nmHash, err := nnsResolveHash(inv, cs.Hash, domainOf(netmapContract)) + nmHash, err := util.NNSResolveHash(inv, cs.Hash, domainOf(netmapContract)) if err != nil { return fmt.Errorf("can't get netmap contract hash: %w", err) } @@ -96,7 +96,7 @@ func setConfigCmd(cmd *cobra.Command, args []string) error { return fmt.Errorf("can't get NNS contract info: %w", err) } - nmHash, err := nnsResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, domainOf(netmapContract)) + nmHash, err := util.NNSResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, domainOf(netmapContract)) if err != nil { return fmt.Errorf("can't get netmap contract hash: %w", err) } diff --git a/cmd/frostfs-adm/internal/modules/morph/container.go b/cmd/frostfs-adm/internal/modules/morph/container.go index b9db4c4e..b93bff8e 100644 --- a/cmd/frostfs-adm/internal/modules/morph/container.go +++ b/cmd/frostfs-adm/internal/modules/morph/container.go @@ -36,7 +36,7 @@ func getContainerContractHash(cmd *cobra.Command, inv *invoker.Invoker) (util.Ui if err != nil { return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err) } - ch, err = nnsResolveHash(inv, nnsCs.Hash, domainOf(containerContract)) + ch, err = morphUtil.NNSResolveHash(inv, nnsCs.Hash, domainOf(containerContract)) if err != nil { return util.Uint160{}, err } @@ -307,7 +307,7 @@ func fetchContainerContractHash(wCtx *initializeContext) (util.Uint160, error) { return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err) } - ch, err := nnsResolveHash(wCtx.ReadOnlyInvoker, nnsCs.Hash, domainOf(containerContract)) + ch, err := morphUtil.NNSResolveHash(wCtx.ReadOnlyInvoker, nnsCs.Hash, domainOf(containerContract)) if err != nil { return util.Uint160{}, fmt.Errorf("can't fetch container contract hash: %w", err) } diff --git a/cmd/frostfs-adm/internal/modules/morph/deploy.go b/cmd/frostfs-adm/internal/modules/morph/deploy.go index db54a8db..a29002d2 100644 --- a/cmd/frostfs-adm/internal/modules/morph/deploy.go +++ b/cmd/frostfs-adm/internal/modules/morph/deploy.go @@ -89,7 +89,7 @@ func deployContractCmd(cmd *cobra.Command, args []string) error { domain := ctrName + "." + zone isUpdate, _ := cmd.Flags().GetBool(updateFlag) if isUpdate { - cs.Hash, err = nnsResolveHash(c.ReadOnlyInvoker, nnsCs.Hash, domain) + cs.Hash, err = util.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/frostfs-adm/internal/modules/morph/dump_hashes.go b/cmd/frostfs-adm/internal/modules/morph/dump_hashes.go index 60e9de9c..f4c276f5 100644 --- a/cmd/frostfs-adm/internal/modules/morph/dump_hashes.go +++ b/cmd/frostfs-adm/internal/modules/morph/dump_hashes.go @@ -79,7 +79,7 @@ func dumpContractHashes(cmd *cobra.Command, _ []string) error { for i := 0; i < irSize; i++ { info := contractDumpInfo{name: fmt.Sprintf("alphabet %d", i)} - if h, err := parseNNSResolveResult(alphaRes.Stack[i]); err == nil { + if h, err := morphUtil.ParseNNSResolveResult(alphaRes.Stack[i]); err == nil { info.hash = h } infos = append(infos, info) @@ -98,7 +98,7 @@ func dumpContractHashes(cmd *cobra.Command, _ []string) error { info := contractDumpInfo{name: ctrName} if len(res.Stack) != 0 { - if h, err := parseNNSResolveResult(res.Stack[0]); err == nil { + if h, err := morphUtil.ParseNNSResolveResult(res.Stack[0]); err == nil { info.hash = h } } @@ -133,7 +133,7 @@ func dumpCustomZoneHashes(cmd *cobra.Command, nnsHash util.Uint160, zone string, return } - h, err := nnsResolveHash(inv, nnsHash, string(bs)) + h, err := morphUtil.NNSResolveHash(inv, nnsHash, string(bs)) if err != nil { cmd.PrintErrf("Could not resolve name %s: %v\n", string(bs), err) return diff --git a/cmd/frostfs-adm/internal/modules/morph/epoch.go b/cmd/frostfs-adm/internal/modules/morph/epoch.go index e491e8b8..86f4f0fe 100644 --- a/cmd/frostfs-adm/internal/modules/morph/epoch.go +++ b/cmd/frostfs-adm/internal/modules/morph/epoch.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + util2 "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpcclient/management" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" @@ -27,7 +28,7 @@ func forceNewEpochCmd(cmd *cobra.Command, _ []string) error { return fmt.Errorf("can't get NNS contract info: %w", err) } - nmHash, err := nnsResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, domainOf(netmapContract)) + nmHash, err := util2.NNSResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, domainOf(netmapContract)) if err != nil { return fmt.Errorf("can't get netmap contract hash: %w", err) } diff --git a/cmd/frostfs-adm/internal/modules/morph/frostfsid.go b/cmd/frostfs-adm/internal/modules/morph/frostfsid.go index 8150dcb5..a420b077 100644 --- a/cmd/frostfs-adm/internal/modules/morph/frostfsid.go +++ b/cmd/frostfs-adm/internal/modules/morph/frostfsid.go @@ -431,7 +431,7 @@ func newFrostfsIDClient(cmd *cobra.Command) (*frostfsidClient, error) { return nil, fmt.Errorf("can't get NNS contract info: %w", err) } - ffsidHash, err := nnsResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, domainOf(frostfsIDContract)) + ffsidHash, err := morphUtil.NNSResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, domainOf(frostfsIDContract)) if err != nil { return nil, fmt.Errorf("can't get proxy contract hash: %w", err) } diff --git a/cmd/frostfs-adm/internal/modules/morph/initialize_deploy.go b/cmd/frostfs-adm/internal/modules/morph/initialize_deploy.go index 9f0e929a..fe65a284 100644 --- a/cmd/frostfs-adm/internal/modules/morph/initialize_deploy.go +++ b/cmd/frostfs-adm/internal/modules/morph/initialize_deploy.go @@ -203,7 +203,7 @@ func (c *initializeContext) deployOrUpdateContracts(w *io2.BufBinWriter, nnsHash cs := c.getContract(ctrName) method := updateMethodName - ctrHash, err := nnsResolveHash(c.ReadOnlyInvoker, nnsHash, domainOf(ctrName)) + ctrHash, err := morphUtil.NNSResolveHash(c.ReadOnlyInvoker, nnsHash, domainOf(ctrName)) if err != nil { if errors.Is(err, errMissingNNSRecord) { // if contract not found we deploy it instead of update @@ -267,7 +267,7 @@ func (c *initializeContext) deployAlphabetAccounts(nnsHash util.Uint160, w *io2. // alphabet contracts should be deployed by individual nodes to get different hashes. for i, acc := range c.Accounts { - ctrHash, err := nnsResolveHash(c.ReadOnlyInvoker, nnsHash, getAlphabetNNSDomain(i)) + ctrHash, err := morphUtil.NNSResolveHash(c.ReadOnlyInvoker, nnsHash, getAlphabetNNSDomain(i)) if err != nil { return nil, fmt.Errorf("can't resolve hash for contract update: %w", err) } @@ -609,7 +609,7 @@ func (c *initializeContext) getFrostfsIDAdminFromContract() (util.Uint160, bool, if err != nil { return util.Uint160{}, false, fmt.Errorf("get nns contract: %w", err) } - fidHash, err := nnsResolveHash(c.ReadOnlyInvoker, cs.Hash, domainOf(frostfsIDContract)) + fidHash, err := morphUtil.NNSResolveHash(c.ReadOnlyInvoker, cs.Hash, domainOf(frostfsIDContract)) if err != nil { return util.Uint160{}, false, fmt.Errorf("resolve frostfsid contract hash: %w", err) } @@ -638,7 +638,7 @@ func (c *initializeContext) getNetConfigFromNetmapContract() ([]stackitem.Item, if err != nil { return nil, fmt.Errorf("get nns contract: %w", err) } - nmHash, err := nnsResolveHash(c.ReadOnlyInvoker, cs.Hash, domainOf(netmapContract)) + nmHash, err := morphUtil.NNSResolveHash(c.ReadOnlyInvoker, cs.Hash, domainOf(netmapContract)) if err != nil { return nil, fmt.Errorf("can't get netmap contract hash: %w", err) } diff --git a/cmd/frostfs-adm/internal/modules/morph/initialize_nns.go b/cmd/frostfs-adm/internal/modules/morph/initialize_nns.go index 7364ba0e..8267fd04 100644 --- a/cmd/frostfs-adm/internal/modules/morph/initialize_nns.go +++ b/cmd/frostfs-adm/internal/modules/morph/initialize_nns.go @@ -184,7 +184,7 @@ func (c *initializeContext) nnsRegisterDomainScript(nnsHash, expectedHash util.U return bw.Bytes(), false, nil } - s, err := nnsResolveHash(c.ReadOnlyInvoker, nnsHash, domain) + s, err := morphUtil.NNSResolveHash(c.ReadOnlyInvoker, nnsHash, domain) if err != nil { return nil, false, err } @@ -220,21 +220,8 @@ func (c *initializeContext) nnsRootRegistered(nnsHash util.Uint160, zone string) var errMissingNNSRecord = errors.New("missing NNS record") -// Returns errMissingNNSRecord if invocation fault exception contains "token not found". -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(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (stackitem.Item, error) { - return unwrap.Item(inv.Call(nnsHash, "resolve", domain, int64(nns.TXT))) -} - func nnsResolveKey(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (*keys.PublicKey, error) { - res, err := nnsResolve(inv, nnsHash, domain) + res, err := morphUtil.NNSResolve(inv, nnsHash, domain) if err != nil { return nil, err } @@ -257,38 +244,6 @@ func nnsResolveKey(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (* return nil, errors.New("no valid keys are found") } -// parseNNSResolveResult parses the result of resolving NNS record. -// It works with multiple formats (corresponding to multiple NNS versions). -// If array of hashes is provided, it returns only the first one. -func parseNNSResolveResult(res stackitem.Item) (util.Uint160, error) { - arr, ok := res.Value().([]stackitem.Item) - if !ok { - arr = []stackitem.Item{res} - } - if _, ok := res.Value().(stackitem.Null); ok || len(arr) == 0 { - return util.Uint160{}, errors.New("NNS record is missing") - } - for i := range arr { - bs, err := arr[i].TryBytes() - if err != nil { - continue - } - - // We support several formats for hash encoding, this logic should be maintained in sync - // with nnsResolve from pkg/morph/client/nns.go - h, err := util.Uint160DecodeStringLE(string(bs)) - if err == nil { - return h, nil - } - - h, err = address.StringToUint160(string(bs)) - if err == nil { - return h, nil - } - } - return util.Uint160{}, errors.New("no valid hashes are found") -} - func nnsIsAvailable(c morphUtil.Client, nnsHash util.Uint160, name string) (bool, error) { switch c.(type) { case *rpcclient.Client: diff --git a/cmd/frostfs-adm/internal/modules/morph/netmap_candidates.go b/cmd/frostfs-adm/internal/modules/morph/netmap_candidates.go index 7d31d181..cf961cb4 100644 --- a/cmd/frostfs-adm/internal/modules/morph/netmap_candidates.go +++ b/cmd/frostfs-adm/internal/modules/morph/netmap_candidates.go @@ -21,7 +21,7 @@ func listNetmapCandidatesNodes(cmd *cobra.Command, _ []string) { cs, err := r.GetContractByID(1) commonCmd.ExitOnErr(cmd, "can't get NNS contract info: %w", err) - nmHash, err := nnsResolveHash(inv, cs.Hash, domainOf(netmapContract)) + nmHash, err := util.NNSResolveHash(inv, cs.Hash, domainOf(netmapContract)) commonCmd.ExitOnErr(cmd, "can't get netmap contract hash: %w", err) res, err := inv.Call(nmHash, "netmapCandidates") diff --git a/cmd/frostfs-adm/internal/modules/morph/proxy.go b/cmd/frostfs-adm/internal/modules/morph/proxy.go index 802a6124..5a70bfe6 100644 --- a/cmd/frostfs-adm/internal/modules/morph/proxy.go +++ b/cmd/frostfs-adm/internal/modules/morph/proxy.go @@ -3,6 +3,7 @@ package morph import ( "fmt" + util2 "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util" commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" @@ -46,7 +47,7 @@ func processAccount(cmd *cobra.Command, addr util.Uint160, method string) error return fmt.Errorf("can't get NNS contract info: %w", err) } - proxyHash, err := nnsResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, domainOf(proxyContract)) + proxyHash, err := util2.NNSResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, domainOf(proxyContract)) if err != nil { return fmt.Errorf("can't get proxy contract hash: %w", err) } diff --git a/cmd/frostfs-adm/internal/modules/morph/remove_node.go b/cmd/frostfs-adm/internal/modules/morph/remove_node.go index ea958f2f..0ae9a1ef 100644 --- a/cmd/frostfs-adm/internal/modules/morph/remove_node.go +++ b/cmd/frostfs-adm/internal/modules/morph/remove_node.go @@ -5,6 +5,7 @@ import ( "fmt" netmapcontract "git.frostfs.info/TrueCloudLab/frostfs-contract/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpcclient/management" @@ -40,7 +41,7 @@ func removeNodesCmd(cmd *cobra.Command, args []string) error { return fmt.Errorf("can't get NNS contract info: %w", err) } - nmHash, err := nnsResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, domainOf(netmapContract)) + nmHash, err := util.NNSResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, domainOf(netmapContract)) if err != nil { return fmt.Errorf("can't get netmap contract hash: %w", err) } diff --git a/cmd/frostfs-adm/internal/modules/morph/util/initialize.go b/cmd/frostfs-adm/internal/modules/morph/util/initialize.go index d15d99b9..7b195aeb 100644 --- a/cmd/frostfs-adm/internal/modules/morph/util/initialize.go +++ b/cmd/frostfs-adm/internal/modules/morph/util/initialize.go @@ -1,11 +1,18 @@ package util import ( + "errors" "fmt" "time" + "git.frostfs.info/TrueCloudLab/frostfs-contract/nns" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "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/trigger" + "github.com/nspcc-dev/neo-go/pkg/util" + "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/cobra" @@ -88,3 +95,48 @@ func GetWalletAccount(w *wallet.Wallet, typ string) (*wallet.Account, error) { } return nil, fmt.Errorf("account for '%s' not found", typ) } + +func NNSResolve(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (stackitem.Item, error) { + return unwrap.Item(inv.Call(nnsHash, "resolve", domain, int64(nns.TXT))) +} + +// ParseNNSResolveResult parses the result of resolving NNS record. +// It works with multiple formats (corresponding to multiple NNS versions). +// If array of hashes is provided, it returns only the first one. +func ParseNNSResolveResult(res stackitem.Item) (util.Uint160, error) { + arr, ok := res.Value().([]stackitem.Item) + if !ok { + arr = []stackitem.Item{res} + } + if _, ok := res.Value().(stackitem.Null); ok || len(arr) == 0 { + return util.Uint160{}, errors.New("NNS record is missing") + } + for i := range arr { + bs, err := arr[i].TryBytes() + if err != nil { + continue + } + + // We support several formats for hash encoding, this logic should be maintained in sync + // with NNSResolve from pkg/morph/client/nns.go + h, err := util.Uint160DecodeStringLE(string(bs)) + if err == nil { + return h, nil + } + + h, err = address.StringToUint160(string(bs)) + if err == nil { + return h, nil + } + } + return util.Uint160{}, errors.New("no valid hashes are found") +} + +// NNSResolveHash Returns errMissingNNSRecord if invocation fault exception contains "token not found". +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) +}