diff --git a/cmd/frostfs-adm/internal/modules/morph/frostfsid/frostfsid.go b/cmd/frostfs-adm/internal/modules/morph/frostfsid/frostfsid.go index 44292091..091d6634 100644 --- a/cmd/frostfs-adm/internal/modules/morph/frostfsid/frostfsid.go +++ b/cmd/frostfs-adm/internal/modules/morph/frostfsid/frostfsid.go @@ -3,24 +3,32 @@ package frostfsid import ( "errors" "fmt" + "math/big" "sort" frostfsidclient "git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client" + frostfsidrpclient "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/frostfsid" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper" commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common" + "github.com/google/uuid" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/management" "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/stackitem" "github.com/spf13/cobra" "github.com/spf13/viper" ) +const iteratorBatchSize = 1 + const ( namespaceFlag = "namespace" subjectNameFlag = "subject-name" @@ -250,12 +258,15 @@ func frostfsidCreateNamespace(cmd *cobra.Command, _ []string) { } func frostfsidListNamespaces(cmd *cobra.Command, _ []string) { - ffsid, err := newFrostfsIDClient(cmd) - commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err) - - namespaces, err := ffsid.roCli.ListNamespaces() - commonCmd.ExitOnErr(cmd, "list namespaces: %w", err) + inv, _, hash := initInvoker(cmd) + reader := frostfsidrpclient.NewReader(inv, hash) + sessionID, it, err := reader.ListNamespaces() + commonCmd.ExitOnErr(cmd, "can't get namespace: %w", err) + items, err := readIterator(inv, &it, iteratorBatchSize, sessionID) + commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err) + namespaces, err := frostfsidclient.ParseNamespaces(items) + commonCmd.ExitOnErr(cmd, "can't parse namespace: %w", err) sort.Slice(namespaces, func(i, j int) bool { return namespaces[i].Name < namespaces[j].Name }) for _, namespace := range namespaces { @@ -296,14 +307,15 @@ func frostfsidDeleteSubject(cmd *cobra.Command, _ []string) { } func frostfsidListSubjects(cmd *cobra.Command, _ []string) { - ns := getFrostfsIDNamespace(cmd) includeNames, _ := cmd.Flags().GetBool(includeNamesFlag) + ns := getFrostfsIDNamespace(cmd) + inv, _, hash := initInvoker(cmd) + reader := frostfsidrpclient.NewReader(inv, hash) + sessionID, it, err := reader.ListNamespaceSubjects(ns) + commonCmd.ExitOnErr(cmd, "can't get namespace: %w", err) - ffsid, err := newFrostfsIDClient(cmd) - commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err) - - subAddresses, err := ffsid.roCli.ListNamespaceSubjects(ns) - commonCmd.ExitOnErr(cmd, "list subjects: %w", err) + subAddresses, err := frostfsidclient.UnwrapArrayOfUint160(readIterator(inv, &it, iteratorBatchSize, sessionID)) + commonCmd.ExitOnErr(cmd, "can't unwrap: %w", err) sort.Slice(subAddresses, func(i, j int) bool { return subAddresses[i].Less(subAddresses[j]) }) @@ -313,8 +325,14 @@ func frostfsidListSubjects(cmd *cobra.Command, _ []string) { continue } - subj, err := ffsid.roCli.GetSubject(addr) - commonCmd.ExitOnErr(cmd, "get subject: %w", err) + sessionID, it, err := reader.ListSubjects() + commonCmd.ExitOnErr(cmd, "can't get subject: %w", err) + + items, err := readIterator(inv, &it, iteratorBatchSize, sessionID) + commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err) + + subj, err := frostfsidclient.ParseSubject(items) + commonCmd.ExitOnErr(cmd, "can't parse subject: %w", err) cmd.Printf("%s (%s)\n", address.Uint160ToString(addr), subj.Name) } @@ -349,13 +367,17 @@ func frostfsidDeleteGroup(cmd *cobra.Command, _ []string) { } func frostfsidListGroups(cmd *cobra.Command, _ []string) { + inv, _, hash := initInvoker(cmd) ns := getFrostfsIDNamespace(cmd) - ffsid, err := newFrostfsIDClient(cmd) - commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err) + reader := frostfsidrpclient.NewReader(inv, hash) + sessionID, it, err := reader.ListGroups(ns) + commonCmd.ExitOnErr(cmd, "can't get namespace: %w", err) - groups, err := ffsid.roCli.ListGroups(ns) - commonCmd.ExitOnErr(cmd, "list groups: %w", err) + items, err := readIterator(inv, &it, iteratorBatchSize, sessionID) + commonCmd.ExitOnErr(cmd, "can't list groups: %w", err) + groups, err := frostfsidclient.ParseGroups(items) + commonCmd.ExitOnErr(cmd, "can't parse groups: %w", err) sort.Slice(groups, func(i, j int) bool { return groups[i].Name < groups[j].Name }) @@ -394,12 +416,19 @@ func frostfsidListGroupSubjects(cmd *cobra.Command, _ []string) { ns := getFrostfsIDNamespace(cmd) groupID := getFrostfsIDGroupID(cmd) includeNames, _ := cmd.Flags().GetBool(includeNamesFlag) + inv, cs, hash := initInvoker(cmd) + _, err := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.FrostfsIDContract)) + commonCmd.ExitOnErr(cmd, "can't get netmap contract hash: %w", err) - ffsid, err := newFrostfsIDClient(cmd) - commonCmd.ExitOnErr(cmd, "init contract client: %w", err) + reader := frostfsidrpclient.NewReader(inv, hash) + sessionID, it, err := reader.ListGroupSubjects(ns, big.NewInt(groupID)) + commonCmd.ExitOnErr(cmd, "can't list groups: %w", err) - subjects, err := ffsid.roCli.ListGroupSubjects(ns, groupID) - commonCmd.ExitOnErr(cmd, "list group subjects: %w", err) + items, err := readIterator(inv, &it, iteratorBatchSize, sessionID) + commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err) + + subjects, err := frostfsidclient.UnwrapArrayOfUint160(items, err) + commonCmd.ExitOnErr(cmd, "can't unwrap: %w", err) sort.Slice(subjects, func(i, j int) bool { return subjects[i].Less(subjects[j]) }) @@ -409,9 +438,10 @@ func frostfsidListGroupSubjects(cmd *cobra.Command, _ []string) { continue } - subj, err := ffsid.roCli.GetSubject(subjAddr) - commonCmd.ExitOnErr(cmd, "get subject: %w", err) - + items, err := reader.GetSubject(subjAddr) + commonCmd.ExitOnErr(cmd, "can't get subject: %w", err) + subj, err := frostfsidclient.ParseSubject(items) + commonCmd.ExitOnErr(cmd, "can't parse subject: %w", err) cmd.Printf("%s (%s)\n", address.Uint160ToString(subjAddr), subj.Name) } } @@ -474,3 +504,35 @@ func (f *frostfsidClient) sendWaitRes() (*state.AppExecResult, error) { f.wCtx.Command.Println("Waiting for transactions to persist...") return f.roCli.Wait(f.wCtx.SentTxs[0].Hash, f.wCtx.SentTxs[0].Vub, nil) } + +func readIterator(inv *invoker.Invoker, iter *result.Iterator, batchSize int, sessionID uuid.UUID) ([]stackitem.Item, error) { + var shouldStop bool + res := make([]stackitem.Item, 0) + for !shouldStop { + items, err := inv.TraverseIterator(sessionID, iter, batchSize) + if err != nil { + return nil, err + } + + res = append(res, items...) + shouldStop = len(items) < batchSize + } + + return res, nil +} + +func initInvoker(cmd *cobra.Command) (*invoker.Invoker, *state.Contract, util.Uint160) { + c, err := helper.GetN3Client(viper.GetViper()) + commonCmd.ExitOnErr(cmd, "can't create N3 client: %w", err) + + inv := invoker.New(c, nil) + r := management.NewReader(inv) + + cs, err := r.GetContractByID(1) + commonCmd.ExitOnErr(cmd, "can't get NNS contract info: %w", err) + + nmHash, err := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.FrostfsIDContract)) + commonCmd.ExitOnErr(cmd, "can't get netmap contract hash: %w", err) + + return inv, cs, nmHash +}