package morph import ( "fmt" "sort" frostfsidclient "git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client" 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" "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/vm/emit" "github.com/spf13/cobra" "github.com/spf13/viper" ) const ( namespaceFlag = "namespace" subjectNameFlag = "subject-name" subjectKeyFlag = "subject-key" includeNamesFlag = "include-names" groupNameFlag = "group-name" ) var ( frostfsidCmd = &cobra.Command{ Use: "frostfsid", Short: "Section for frostfsid interactions commands", } frostfsidCreateNamespaceCmd = &cobra.Command{ Use: "create-namespace", Short: "Create new namespace in frostfsid contract", PreRun: func(cmd *cobra.Command, _ []string) { _ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag)) _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) }, Run: frostfsidCreateNamespace, } frostfsidListNamespacesCmd = &cobra.Command{ Use: "list-namespaces", Short: "List all namespaces in frostfsid", PreRun: func(cmd *cobra.Command, _ []string) { _ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag)) _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) }, Run: frostfsidListNamespaces, } frostfsidCreateSubjectCmd = &cobra.Command{ Use: "create-subject", Short: "Create subject in frostfsid contract", PreRun: func(cmd *cobra.Command, _ []string) { _ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag)) _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) }, Run: frostfsidCreateSubject, } frostfsidDeleteSubjectCmd = &cobra.Command{ Use: "delete-subject", Short: "Delete subject from frostfsid contract", PreRun: func(cmd *cobra.Command, _ []string) { _ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag)) _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) }, Run: frostfsidDeleteSubject, } frostfsidListSubjectsCmd = &cobra.Command{ Use: "list-subjects", Short: "List subjects in namespace", PreRun: func(cmd *cobra.Command, _ []string) { _ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag)) _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) }, Run: frostfsidListSubjects, } frostfsidCreateGroupCmd = &cobra.Command{ Use: "create-group", Short: "Create group in frostfsid contract", PreRun: func(cmd *cobra.Command, _ []string) { _ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag)) _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) }, Run: frostfsidCreateGroup, } frostfsidDeleteGroupCmd = &cobra.Command{ Use: "delete-group", Short: "Delete group from frostfsid contract", PreRun: func(cmd *cobra.Command, _ []string) { _ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag)) _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) }, Run: frostfsidDeleteGroup, } frostfsidListGroupsCmd = &cobra.Command{ Use: "list-groups", Short: "List groups in namespace", PreRun: func(cmd *cobra.Command, _ []string) { _ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag)) _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) }, Run: frostfsidListGroups, } ) func initFrostfsIDCreateNamespaceCmd() { frostfsidCmd.AddCommand(frostfsidCreateNamespaceCmd) frostfsidCreateNamespaceCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc) frostfsidCreateNamespaceCmd.Flags().String(namespaceFlag, "", "Namespace name to create") } func initFrostfsIDListNamespacesCmd() { frostfsidCmd.AddCommand(frostfsidListNamespacesCmd) frostfsidListNamespacesCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc) } func initFrostfsIDCreateSubjectCmd() { frostfsidCmd.AddCommand(frostfsidCreateSubjectCmd) frostfsidCreateSubjectCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc) frostfsidCreateSubjectCmd.Flags().String(namespaceFlag, "", "Namespace where create subject") frostfsidCreateSubjectCmd.Flags().String(subjectNameFlag, "", "Subject name, must be unique in namespace") frostfsidCreateSubjectCmd.Flags().String(subjectKeyFlag, "", "Subject hex-encoded public key") } func initFrostfsIDDeleteSubjectCmd() { frostfsidCmd.AddCommand(frostfsidDeleteSubjectCmd) frostfsidDeleteSubjectCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc) frostfsidDeleteSubjectCmd.Flags().String(subjectAddressFlag, "", "Subject address") } func initFrostfsIDListSubjectsCmd() { frostfsidCmd.AddCommand(frostfsidListSubjectsCmd) frostfsidListSubjectsCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc) frostfsidListSubjectsCmd.Flags().String(namespaceFlag, "", "Namespace to list subjects") frostfsidListSubjectsCmd.Flags().Bool(includeNamesFlag, false, "Whether include subject name (require additional requests)") } func initFrostfsIDCreateGroupCmd() { frostfsidCmd.AddCommand(frostfsidCreateGroupCmd) frostfsidCreateGroupCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc) frostfsidCreateGroupCmd.Flags().String(namespaceFlag, "", "Namespace where create group") frostfsidCreateGroupCmd.Flags().String(groupNameFlag, "", "Group name, must be unique in namespace") } func initFrostfsIDDeleteGroupCmd() { frostfsidCmd.AddCommand(frostfsidDeleteGroupCmd) frostfsidDeleteGroupCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc) frostfsidDeleteGroupCmd.Flags().String(namespaceFlag, "", "Namespace to delete group") frostfsidDeleteGroupCmd.Flags().Int64(groupIDFlag, 0, "Group id") } func initFrostfsIDListGroupsCmd() { frostfsidCmd.AddCommand(frostfsidListGroupsCmd) frostfsidListGroupsCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc) frostfsidListGroupsCmd.Flags().String(namespaceFlag, "", "Namespace to list groups") } type ffsidMethodArgs struct { name string args []any } func frostfsidCreateNamespace(cmd *cobra.Command, _ []string) { ns, _ := cmd.Flags().GetString(namespaceFlag) err := sendFrostfsIDTx(cmd, ffsidMethodArgs{name: "createNamespace", args: []any{ns}}) commonCmd.ExitOnErr(cmd, "processing error: %w", err) } func frostfsidListNamespaces(cmd *cobra.Command, _ []string) { ffsid, err := frostfsIDClient(cmd) commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err) namespaces, err := ffsid.ListNamespaces() commonCmd.ExitOnErr(cmd, "list namespaces: %w", err) sort.Slice(namespaces, func(i, j int) bool { return namespaces[i].Name < namespaces[j].Name }) for _, namespace := range namespaces { cmd.Printf("%q\n", namespace.Name) } } func frostfsidCreateSubject(cmd *cobra.Command, _ []string) { ns, _ := cmd.Flags().GetString(namespaceFlag) subjName, _ := cmd.Flags().GetString(subjectNameFlag) subjKey := getFrostfsIDSubjectKey(cmd) args := []ffsidMethodArgs{ {name: "createSubject", args: []any{ns, subjKey.Bytes()}}, } if subjName != "" { args = append(args, ffsidMethodArgs{name: "setSubjectName", args: []any{subjKey.GetScriptHash(), subjName}}) } err := sendFrostfsIDTx(cmd, args...) commonCmd.ExitOnErr(cmd, "processing error: %w", err) } func frostfsidDeleteSubject(cmd *cobra.Command, _ []string) { subjectAddress := getFrostfsIDSubjectAddress(cmd) err := sendFrostfsIDTx(cmd, ffsidMethodArgs{name: "deleteSubject", args: []any{subjectAddress}}) commonCmd.ExitOnErr(cmd, "delete subject error: %w", err) } func frostfsidListSubjects(cmd *cobra.Command, _ []string) { ns, _ := cmd.Flags().GetString(namespaceFlag) includeNames, _ := cmd.Flags().GetBool(includeNamesFlag) ffsid, err := frostfsIDClient(cmd) commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err) subAddresses, err := ffsid.ListNamespaceSubjects(ns) commonCmd.ExitOnErr(cmd, "list subjects: %w", err) sort.Slice(subAddresses, func(i, j int) bool { return subAddresses[i].Less(subAddresses[j]) }) for _, addr := range subAddresses { if !includeNames { cmd.Println(address.Uint160ToString(addr)) continue } subj, err := ffsid.GetSubject(addr) commonCmd.ExitOnErr(cmd, "get subject: %w", err) cmd.Printf("%s (%s)\n", address.Uint160ToString(addr), subj.Name) } } func frostfsidCreateGroup(cmd *cobra.Command, _ []string) { ns, _ := cmd.Flags().GetString(namespaceFlag) groupName, _ := cmd.Flags().GetString(groupNameFlag) ffsid, err := frostfsIDClient(cmd) commonCmd.ExitOnErr(cmd, "init contract client: %w", err) groupID, err := ffsid.ParseGroupID(ffsid.Wait(ffsid.CreateGroup(ns, groupName))) commonCmd.ExitOnErr(cmd, "create group: %w", err) cmd.Printf("group '%s' created with id: %d\n", groupName, groupID) } func frostfsidDeleteGroup(cmd *cobra.Command, _ []string) { ns, _ := cmd.Flags().GetString(namespaceFlag) groupID, _ := cmd.Flags().GetInt64(groupIDFlag) ffsid, err := frostfsIDClient(cmd) commonCmd.ExitOnErr(cmd, "init contract client: %w", err) _, err = ffsid.Wait(ffsid.DeleteGroup(ns, groupID)) commonCmd.ExitOnErr(cmd, "delete group error: %w", err) } func frostfsidListGroups(cmd *cobra.Command, _ []string) { ns, _ := cmd.Flags().GetString(namespaceFlag) ffsid, err := frostfsIDClient(cmd) commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err) groups, err := ffsid.ListGroups(ns) commonCmd.ExitOnErr(cmd, "list groups: %w", err) sort.Slice(groups, func(i, j int) bool { return groups[i].Name < groups[j].Name }) for _, group := range groups { cmd.Printf("%q (%d)\n", group.Name, group.ID) } } func sendFrostfsIDTx(cmd *cobra.Command, methodArgs ...ffsidMethodArgs) error { wCtx, err := newInitializeContext(cmd, viper.GetViper()) if err != nil { return fmt.Errorf("can't to initialize context: %w", err) } r := management.NewReader(wCtx.ReadOnlyInvoker) cs, err := r.GetContractByID(1) if err != nil { return fmt.Errorf("can't get NNS contract info: %w", err) } ffsidHash, err := nnsResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, domainOf(frostfsIDContract)) if err != nil { return fmt.Errorf("can't get proxy contract hash: %w", err) } bw := io.NewBufBinWriter() for _, method := range methodArgs { emit.AppCall(bw.BinWriter, ffsidHash, method.name, callflag.All, method.args...) } if err = wCtx.sendConsensusTx(bw.Bytes()); err != nil { return err } return wCtx.awaitTx() } func frostfsIDClient(cmd *cobra.Command) (*frostfsidclient.Client, error) { wCtx, err := newInitializeContext(cmd, viper.GetViper()) if err != nil { return nil, fmt.Errorf("can't to initialize context: %w", err) } r := management.NewReader(wCtx.ReadOnlyInvoker) cs, err := r.GetContractByID(1) if err != nil { return nil, fmt.Errorf("can't get NNS contract info: %w", err) } ffsidHash, err := nnsResolveHash(wCtx.ReadOnlyInvoker, cs.Hash, domainOf(frostfsIDContract)) if err != nil { return nil, fmt.Errorf("can't get proxy contract hash: %w", err) } return frostfsidclient.NewSimple(wCtx.CommitteeAct, ffsidHash), nil }