Skip to content

Commit d087255

Browse files
committed
adm: Add commands to work with verified nodes' domains
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]>
1 parent 28c8a7c commit d087255

File tree

6 files changed

+442
-3
lines changed

6 files changed

+442
-3
lines changed

cmd/neofs-adm/internal/modules/morph/n3client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ type clientContext struct {
5858
SentTxs []hashVUBPair
5959
}
6060

61-
func getN3Client(v *viper.Viper) (Client, error) {
61+
func getN3Client(v *viper.Viper) (*rpcclient.Client, error) {
6262
// number of opened connections
6363
// by neo-go client per one host
6464
const (
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package morph
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/nspcc-dev/neo-go/pkg/util"
9+
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
10+
nnsrpc "github.com/nspcc-dev/neofs-contract/rpc/nns"
11+
)
12+
13+
// Various NeoFS NNS errors.
14+
var (
15+
errDomainNotFound = errors.New("domain not found")
16+
errMissingDomainRecords = errors.New("missing domain records")
17+
)
18+
19+
func invalidNNSDomainRecordError(cause error) error {
20+
return fmt.Errorf("invalid domain record: %w", cause)
21+
}
22+
23+
var errBreakIterator = errors.New("break iterator")
24+
25+
// iterates over text records of the specified NeoFS NNS domain and passes them
26+
// into f. Breaks on any f's error and returns it (if f returns
27+
// errBreakIterator, iterateNNSDomainTextRecords returns no error). Returns
28+
// errDomainNotFound if domain is missing in the NNS. Returns
29+
// errMissingDomainRecords if domain exists but has no records.
30+
func iterateNNSDomainTextRecords(inv nnsrpc.Invoker, nnsContractAddr util.Uint160, domain string, f func(string) error) error {
31+
nnsContract := nnsrpc.NewReader(inv, nnsContractAddr)
32+
33+
sessionID, iter, err := nnsContract.GetAllRecords(domain)
34+
if err != nil {
35+
// Track https://github.com/nspcc-dev/neofs-node/issues/2583.
36+
if strings.Contains(err.Error(), "token not found") {
37+
return errDomainNotFound
38+
}
39+
40+
return fmt.Errorf("get all records of the NNS domain: %w", err)
41+
}
42+
43+
defer func() {
44+
_ = inv.TerminateSession(sessionID)
45+
}()
46+
47+
hasRecords := false
48+
49+
for {
50+
items, err := inv.TraverseIterator(sessionID, &iter, 10)
51+
if err != nil {
52+
return fmt.Errorf("NNS domain records' iterator break: %w", err)
53+
}
54+
55+
if len(items) == 0 {
56+
if hasRecords {
57+
return nil
58+
}
59+
60+
return errMissingDomainRecords
61+
}
62+
63+
hasRecords = true
64+
65+
for i := range items {
66+
fields, ok := items[i].Value().([]stackitem.Item)
67+
if !ok {
68+
return invalidNNSDomainRecordError(
69+
fmt.Errorf("unexpected type %s instead of %s", stackitem.StructT, items[i].Type()))
70+
}
71+
72+
if len(fields) < 3 {
73+
return invalidNNSDomainRecordError(
74+
fmt.Errorf("unsupported number of struct fields: expected at least 3, got %d", len(fields)))
75+
}
76+
77+
_, err = fields[0].TryBytes()
78+
if err != nil {
79+
return invalidNNSDomainRecordError(
80+
fmt.Errorf("1st field is not a byte array: got %v", fields[0].Type()))
81+
}
82+
83+
typ, err := fields[1].TryInteger()
84+
if err != nil {
85+
return invalidNNSDomainRecordError(fmt.Errorf("2nd field is not an integer: got %v", fields[1].Type()))
86+
}
87+
88+
if typ.Cmp(nnsrpc.TXT) != 0 {
89+
return invalidNNSDomainRecordError(fmt.Errorf("non-TXT record of type %v", typ))
90+
}
91+
92+
data, err := fields[2].TryBytes()
93+
if err != nil {
94+
return invalidNNSDomainRecordError(
95+
fmt.Errorf("3rd field is not a byte array: got %v", fields[2].Type()))
96+
}
97+
98+
if err = f(string(data)); err != nil {
99+
if errors.Is(err, errBreakIterator) {
100+
return nil
101+
}
102+
103+
return err
104+
}
105+
}
106+
}
107+
}

cmd/neofs-adm/internal/modules/morph/root.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ const (
4242
localDumpFlag = "local-dump"
4343
protoConfigPath = "protocol"
4444
walletAddressFlag = "wallet-address"
45+
domainFlag = "domain"
46+
neoAddressesFlag = "neo-addresses"
47+
publicKeysFlag = "public-keys"
4548
)
4649

4750
var (
@@ -257,6 +260,38 @@ Values for unknown keys are added exactly the way they're provided, no conversio
257260
},
258261
RunE: listNetmapCandidatesNodes,
259262
}
263+
264+
verifiedNodesDomainCmd = &cobra.Command{
265+
Use: "verified-nodes-domain",
266+
Short: "Group of commands to work with verified domains for the storage nodes",
267+
Args: cobra.NoArgs,
268+
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
269+
_ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag))
270+
_ = viper.BindPFlag(domainFlag, cmd.Flags().Lookup(domainFlag))
271+
},
272+
}
273+
274+
verifiedNodesDomainAccessListCmd = &cobra.Command{
275+
Use: "access-list",
276+
Short: "Get access list for the verified nodes' domain",
277+
Long: "List Neo addresses of the storage nodes that have access to use the specified verified domain.",
278+
Args: cobra.NoArgs,
279+
RunE: verifiedNodesDomainAccessList,
280+
}
281+
282+
verifiedNodesDomainSetAccessListCmd = &cobra.Command{
283+
Use: "set-access-list",
284+
Short: "Set access list for the verified nodes' domain",
285+
Long: "Set list of the storage nodes that have access to use the specified verified domain. " +
286+
"The list may be either Neo addresses or HEX-encoded public keys of the nodes.",
287+
Args: cobra.NoArgs,
288+
PreRun: func(cmd *cobra.Command, _ []string) {
289+
_ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag))
290+
_ = viper.BindPFlag(publicKeysFlag, cmd.Flags().Lookup(publicKeysFlag))
291+
_ = viper.BindPFlag(neoAddressesFlag, cmd.Flags().Lookup(neoAddressesFlag))
292+
},
293+
RunE: verifiedNodesDomainSetAccessList,
294+
}
260295
)
261296

262297
func init() {
@@ -365,4 +400,29 @@ func init() {
365400

366401
RootCmd.AddCommand(netmapCandidatesCmd)
367402
netmapCandidatesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
403+
404+
cmd := verifiedNodesDomainAccessListCmd
405+
fs := cmd.Flags()
406+
fs.StringP(endpointFlag, "r", "", "NeoFS Sidechain RPC endpoint")
407+
_ = cmd.MarkFlagRequired(endpointFlag)
408+
fs.StringP(domainFlag, "d", "", "Verified domain of the storage nodes. Must be a valid NeoFS NNS domain (e.g. 'nodes.some-org.neofs')")
409+
_ = cmd.MarkFlagRequired(domainFlag)
410+
411+
verifiedNodesDomainCmd.AddCommand(cmd)
412+
413+
cmd = verifiedNodesDomainSetAccessListCmd
414+
fs = cmd.Flags()
415+
fs.String(alphabetWalletsFlag, "", "Path to directory containing Alphabet wallets in files 'az.json', 'buky.json', etc.")
416+
_ = cmd.MarkFlagRequired(alphabetWalletsFlag)
417+
fs.StringP(endpointFlag, "r", "", "NeoFS Sidechain RPC endpoint")
418+
_ = cmd.MarkFlagRequired(endpointFlag)
419+
fs.StringP(domainFlag, "d", "", "Verified domain of the storage nodes. Must be a valid NeoFS NNS domain (e.g. 'nodes.some-org.neofs')")
420+
_ = cmd.MarkFlagRequired(domainFlag)
421+
fs.StringSlice(neoAddressesFlag, nil, "Neo addresses resolved from public keys of the storage nodes")
422+
fs.StringSlice(publicKeysFlag, nil, "HEX-encoded public keys of the storage nodes")
423+
cmd.MarkFlagsMutuallyExclusive(publicKeysFlag, neoAddressesFlag)
424+
425+
verifiedNodesDomainCmd.AddCommand(cmd)
426+
427+
RootCmd.AddCommand(verifiedNodesDomainCmd)
368428
}

0 commit comments

Comments
 (0)