diff --git a/cmd/neofs-adm/internal/modules/morph/group.go b/cmd/neofs-adm/internal/modules/morph/group.go index 8a8f42d50..c86883c49 100644 --- a/cmd/neofs-adm/internal/modules/morph/group.go +++ b/cmd/neofs-adm/internal/modules/morph/group.go @@ -18,8 +18,6 @@ import ( const contractWalletName = "contract.json" -const groupKeyDomain = "group.neofs" - func initializeContractWallet(walletDir string) (*wallet.Wallet, error) { var ( password string diff --git a/cmd/neofs-adm/internal/modules/morph/initialize.go b/cmd/neofs-adm/internal/modules/morph/initialize.go index ad22ff093..9f6cfd97b 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize.go @@ -18,6 +18,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neofs-node/cmd/neofs-adm/internal/modules/config" "github.com/nspcc-dev/neofs-node/pkg/innerring" + morphClient "github.com/nspcc-dev/neofs-node/pkg/morph/client" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -266,7 +267,7 @@ func (c *initializeContext) getSigner() transaction.Signer { return signer } - groupKey, err := nnsResolveKey(c.Client, nnsCs.Hash, groupKeyDomain) + groupKey, err := nnsResolveKey(c.Client, nnsCs.Hash, morphClient.NNSGroupKeyName) if err == nil { c.groupKey = groupKey diff --git a/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go b/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go index 64326e3d4..d0857b908 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go @@ -28,6 +28,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neofs-node/pkg/innerring" + morphClient "github.com/nspcc-dev/neofs-node/pkg/morph/client" "github.com/spf13/viper" ) @@ -274,7 +275,7 @@ func (c *initializeContext) updateContracts() error { if err != nil { return err } - c.Command.Printf("NNS: Set %s -> %s\n", groupKeyDomain, hex.EncodeToString(groupKey.Bytes())) + c.Command.Printf("NNS: Set %s -> %s\n", morphClient.NNSGroupKeyName, hex.EncodeToString(groupKey.Bytes())) totalGasCost += sysFee if err := c.sendCommitteeTx(w.Bytes(), totalGasCost); err != nil { diff --git a/cmd/neofs-adm/internal/modules/morph/initialize_nns.go b/cmd/neofs-adm/internal/modules/morph/initialize_nns.go index 624a4bbfb..1a72801f5 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize_nns.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize_nns.go @@ -20,6 +20,7 @@ import ( "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" + morphClient "github.com/nspcc-dev/neofs-node/pkg/morph/client" ) const defaultNameServiceDomainPrice = 10_0000_0000 @@ -74,7 +75,7 @@ func (c *initializeContext) setNNS() error { if err != nil { return err } - c.Command.Printf("NNS: Set %s -> %s\n", groupKeyDomain, hex.EncodeToString(groupKey.Bytes())) + c.Command.Printf("NNS: Set %s -> %s\n", morphClient.NNSGroupKeyName, hex.EncodeToString(groupKey.Bytes())) return c.awaitTx() } @@ -89,13 +90,13 @@ func (c *initializeContext) updateNNSGroup(nnsHash util.Uint160, pub *keys.Publi } func (c *initializeContext) emitUpdateNNSGroupScript(bw *io.BufBinWriter, nnsHash util.Uint160, pub *keys.PublicKey) (int64, error) { - isAvail, err := c.Client.NNSIsAvailable(nnsHash, groupKeyDomain) + isAvail, err := c.Client.NNSIsAvailable(nnsHash, morphClient.NNSGroupKeyName) if err != nil { return 0, err } if !isAvail { - currentPub, err := nnsResolveKey(c.Client, nnsHash, groupKeyDomain) + currentPub, err := nnsResolveKey(c.Client, nnsHash, morphClient.NNSGroupKeyName) if err != nil { return 0, err } @@ -108,7 +109,7 @@ func (c *initializeContext) emitUpdateNNSGroupScript(bw *io.BufBinWriter, nnsHas sysFee := int64(native.GASFactor) if isAvail { emit.AppCall(bw.BinWriter, nnsHash, "register", callflag.All, - groupKeyDomain, c.CommitteeAcc.Contract.ScriptHash(), + morphClient.NNSGroupKeyName, c.CommitteeAcc.Contract.ScriptHash(), "ops@nspcc.ru", int64(3600), int64(600), int64(604800), int64(3600)) emit.Opcodes(bw.BinWriter, opcode.ASSERT) sysFee += defaultRegisterSysfee diff --git a/pkg/morph/client/client.go b/pkg/morph/client/client.go index b60ffe79e..b6c5e4973 100644 --- a/pkg/morph/client/client.go +++ b/pkg/morph/client/client.go @@ -42,7 +42,8 @@ type Client struct { } type cache struct { - nnsHash util.Uint160 + nnsHash util.Uint160 + groupKey *keys.PublicKey } type singleClient struct { diff --git a/pkg/morph/client/nns.go b/pkg/morph/client/nns.go index 4ae3d303a..9a6017ce2 100644 --- a/pkg/morph/client/nns.go +++ b/pkg/morph/client/nns.go @@ -6,6 +6,7 @@ import ( "strconv" nns "github.com/nspcc-dev/neo-go/examples/nft-nd-nns" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/rpc/client" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" @@ -32,6 +33,8 @@ const ( NNSReputationContractName = "reputation.neofs" // NNSSubnetworkContractName is a name of the subnet contract in NNS. NNSSubnetworkContractName = "subnet.neofs" + // NNSGroupKeyName is a name for the NeoFS group key record in NNS. + NNSGroupKeyName = "group.neofs" ) var ( @@ -58,29 +61,47 @@ func (c *Client) NNSContractAddress(name string) (sh util.Uint160, err error) { }) } - if c.nnsHash.Equals(util.Uint160{}) { - cs, err := c.client.GetContractStateByID(nnsContractID) - if err != nil { - return sh, fmt.Errorf("NNS contract state: %w", err) - } - c.nnsHash = cs.Hash + nnsHash, err := c.NNSHash() + if err != nil { + return util.Uint160{}, err } - sh, err = nnsResolve(c.client, c.nnsHash, name) + sh, err = nnsResolve(c.client, nnsHash, name) if err != nil { return sh, fmt.Errorf("NNS.resolve: %w", err) } return sh, nil } -func nnsResolve(c *client.Client, nnsHash util.Uint160, domain string) (util.Uint160, error) { +// NNSHash returns NNS contract hash. +func (c *Client) NNSHash() (util.Uint160, error) { + if c.multiClient != nil { + var sh util.Uint160 + return sh, c.multiClient.iterateClients(func(c *Client) error { + var err error + sh, err = c.NNSHash() + return err + }) + } + + if c.nnsHash.Equals(util.Uint160{}) { + cs, err := c.client.GetContractStateByID(nnsContractID) + if err != nil { + return util.Uint160{}, fmt.Errorf("NNS contract state: %w", err) + } + c.nnsHash = cs.Hash + } + return c.nnsHash, nil +} + +func nnsResolveItem(c *client.Client, nnsHash util.Uint160, domain string) (stackitem.Item, error) { found, err := exists(c, nnsHash, domain) if err != nil { - return util.Uint160{}, fmt.Errorf("could not check presence in NNS contract for %s: %w", domain, err) + return nil, fmt.Errorf("could not check presence in NNS contract for %s: %w", domain, err) } if !found { - return util.Uint160{}, ErrNNSRecordNotFound + return nil, ErrNNSRecordNotFound } result, err := c.InvokeFunction(nnsHash, "resolve", []smartcontract.Parameter{ @@ -94,19 +115,26 @@ func nnsResolve(c *client.Client, nnsHash util.Uint160, domain string) (util.Uin }, }, nil) if err != nil { - return util.Uint160{}, err + return nil, err } if result.State != vm.HaltState.String() { - return util.Uint160{}, fmt.Errorf("invocation failed: %s", result.FaultException) + return nil, fmt.Errorf("invocation failed: %s", result.FaultException) } if len(result.Stack) == 0 { - return util.Uint160{}, errEmptyResultStack + return nil, errEmptyResultStack + } + return result.Stack[0], nil +} + +func nnsResolve(c *client.Client, nnsHash util.Uint160, domain string) (util.Uint160, error) { + res, err := nnsResolveItem(c, nnsHash, domain) + if err != nil { + return util.Uint160{}, err } // Parse 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. - res := result.Stack[0] if arr, ok := res.Value().([]stackitem.Item); ok { if len(arr) == 0 { return util.Uint160{}, errors.New("NNS record is missing") @@ -146,3 +174,33 @@ func exists(c *client.Client, nnsHash util.Uint160, domain string) (bool, error) // and, therefore, exists return !available, nil } + +// ContractGroupKey returns public key designating NeoFS contract group. +func (c *Client) ContractGroupKey() (*keys.PublicKey, error) { + if c.groupKey != nil { + return c.groupKey, nil + } + + nnsHash, err := c.NNSHash() + if err != nil { + return nil, err + } + + item, err := nnsResolveItem(c.client, nnsHash, NNSGroupKeyName) + if err != nil { + return nil, err + } + + bs, err := item.TryBytes() + if err != nil { + return nil, err + } + + pub, err := keys.NewPublicKeyFromString(string(bs)) + if err != nil { + return nil, err + } + + c.groupKey = pub + return pub, nil +}