Skip to content

Commit

Permalink
adm: Add commands to work with verified nodes' domains
Browse files Browse the repository at this point in the history
Add commands to get and set list of the storage nodes allowed to use
domain of the private node group.

Refs #2280.

Signed-off-by: Leonard Lyubich <[email protected]>
  • Loading branch information
cthulhu-rider committed Oct 10, 2023
1 parent 6ab5c7c commit 23f9c70
Show file tree
Hide file tree
Showing 6 changed files with 437 additions and 3 deletions.
2 changes: 1 addition & 1 deletion cmd/neofs-adm/internal/modules/morph/n3client.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ type clientContext struct {
SentTxs []hashVUBPair
}

func getN3Client(v *viper.Viper) (Client, error) {
func getN3Client(v *viper.Viper) (*rpcclient.Client, error) {
// number of opened connections
// by neo-go client per one host
const (
Expand Down
107 changes: 107 additions & 0 deletions cmd/neofs-adm/internal/modules/morph/nns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package morph

import (
"errors"
"fmt"
"strings"

"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
nnsrpc "github.com/nspcc-dev/neofs-contract/rpc/nns"
)

// Various NeoFS NNS errors.
var (
errDomainNotFound = errors.New("domain not found")
errMissingDomainRecords = errors.New("missing domain records")
)

func invalidNNSDomainRecordError(cause error) error {
return fmt.Errorf("invalid domain record: %w", cause)
}

var errBreakIterator = errors.New("break iterator")

// iterates over text records of the specified NeoFS NNS domain and passes them
// into f. Breaks on any f's error and returns it (if f returns
// errBreakIterator, iterateNNSDomainTextRecords returns no error). Returns
// errDomainNotFound if domain is missing in the NNS. Returns
// errMissingDomainRecords if domain exists but has no records.
func iterateNNSDomainTextRecords(inv nnsrpc.Invoker, nnsContractAddr util.Uint160, domain string, f func(string) error) error {
nnsContract := nnsrpc.NewReader(inv, nnsContractAddr)

sessionID, iter, err := nnsContract.GetAllRecords(domain)
if err != nil {
// Track https://github.com/nspcc-dev/neofs-node/issues/2583.
if strings.Contains(err.Error(), "token not found") {
return errDomainNotFound
}

return fmt.Errorf("get all records of the NNS domain: %w", err)
}

defer func() {
_ = inv.TerminateSession(sessionID)
}()

hasRecords := false

for {
items, err := inv.TraverseIterator(sessionID, &iter, 10)
if err != nil {
return fmt.Errorf("NNS domain records' iterator break: %w", err)
}

if len(items) == 0 {
if hasRecords {
return nil
}

return errMissingDomainRecords
}

hasRecords = true

for i := range items {
fields, ok := items[i].Value().([]stackitem.Item)
if !ok {
return invalidNNSDomainRecordError(
fmt.Errorf("unexpected type %s instead of %s", stackitem.StructT, items[i].Type()))
}

if len(fields) < 3 {
return invalidNNSDomainRecordError(
fmt.Errorf("unsupported number of struct fields: expected at least 3, got %d", len(fields)))
}

_, err = fields[0].TryBytes()
if err != nil {
return invalidNNSDomainRecordError(
fmt.Errorf("1st field is not a byte array: got %v", fields[0].Type()))
}

typ, err := fields[1].TryInteger()
if err != nil {
return invalidNNSDomainRecordError(fmt.Errorf("2nd field is not an integer: got %v", fields[1].Type()))
}

if typ.Cmp(nnsrpc.TXT) != 0 {
continue
}

data, err := fields[2].TryBytes()
if err != nil {
return invalidNNSDomainRecordError(
fmt.Errorf("3rd field is not a byte array: got %v", fields[2].Type()))
}

if err = f(string(data)); err != nil {
if errors.Is(err, errBreakIterator) {
return nil
}

return err
}
}
}
}
60 changes: 60 additions & 0 deletions cmd/neofs-adm/internal/modules/morph/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const (
localDumpFlag = "local-dump"
protoConfigPath = "protocol"
walletAddressFlag = "wallet-address"
domainFlag = "domain"
neoAddressesFlag = "neo-addresses"
publicKeysFlag = "public-keys"
)

var (
Expand Down Expand Up @@ -257,6 +260,38 @@ Values for unknown keys are added exactly the way they're provided, no conversio
},
RunE: listNetmapCandidatesNodes,
}

verifiedNodesDomainCmd = &cobra.Command{
Use: "verified-nodes-domain",
Short: "Group of commands to work with verified domains for the storage nodes",
Args: cobra.NoArgs,
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
_ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag))
_ = viper.BindPFlag(domainFlag, cmd.Flags().Lookup(domainFlag))
},
}

verifiedNodesDomainAccessListCmd = &cobra.Command{
Use: "access-list",
Short: "Get access list for the verified nodes' domain",
Long: "List Neo addresses of the storage nodes that have access to use the specified verified domain.",
Args: cobra.NoArgs,
RunE: verifiedNodesDomainAccessList,
}

verifiedNodesDomainSetAccessListCmd = &cobra.Command{
Use: "set-access-list",
Short: "Set access list for the verified nodes' domain",
Long: "Set list of the storage nodes that have access to use the specified verified domain. " +
"The list may be either Neo addresses or HEX-encoded public keys of the nodes.",
Args: cobra.NoArgs,
PreRun: func(cmd *cobra.Command, _ []string) {
_ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag))
_ = viper.BindPFlag(publicKeysFlag, cmd.Flags().Lookup(publicKeysFlag))
_ = viper.BindPFlag(neoAddressesFlag, cmd.Flags().Lookup(neoAddressesFlag))
},
RunE: verifiedNodesDomainSetAccessList,
}
)

func init() {
Expand Down Expand Up @@ -365,4 +400,29 @@ func init() {

RootCmd.AddCommand(netmapCandidatesCmd)
netmapCandidatesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")

cmd := verifiedNodesDomainAccessListCmd
fs := cmd.Flags()
fs.StringP(endpointFlag, "r", "", "NeoFS Sidechain RPC endpoint")
_ = cmd.MarkFlagRequired(endpointFlag)
fs.StringP(domainFlag, "d", "", "Verified domain of the storage nodes. Must be a valid NeoFS NNS domain (e.g. 'nodes.some-org.neofs')")
_ = cmd.MarkFlagRequired(domainFlag)

verifiedNodesDomainCmd.AddCommand(cmd)

cmd = verifiedNodesDomainSetAccessListCmd
fs = cmd.Flags()
fs.String(alphabetWalletsFlag, "", "Path to directory containing Alphabet wallets in files 'az.json', 'buky.json', etc.")
_ = cmd.MarkFlagRequired(alphabetWalletsFlag)
fs.StringP(endpointFlag, "r", "", "NeoFS Sidechain RPC endpoint")
_ = cmd.MarkFlagRequired(endpointFlag)
fs.StringP(domainFlag, "d", "", "Verified domain of the storage nodes. Must be a valid NeoFS NNS domain (e.g. 'nodes.some-org.neofs')")
_ = cmd.MarkFlagRequired(domainFlag)
fs.StringSlice(neoAddressesFlag, nil, "Neo addresses resolved from public keys of the storage nodes")
fs.StringSlice(publicKeysFlag, nil, "HEX-encoded public keys of the storage nodes")
cmd.MarkFlagsMutuallyExclusive(publicKeysFlag, neoAddressesFlag)

verifiedNodesDomainCmd.AddCommand(cmd)

RootCmd.AddCommand(verifiedNodesDomainCmd)
}
Loading

0 comments on commit 23f9c70

Please sign in to comment.