From b41658db044354a000745ddea7c8fdf5eff882f5 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 27 Jun 2022 17:47:13 +0300 Subject: [PATCH] [#1555] neofs-adm: Allow to remove nodes from the netmap Signed-off-by: Evgenii Stratonikov --- cmd/neofs-adm/internal/modules/morph/epoch.go | 23 ++++--- .../internal/modules/morph/remove_node.go | 60 +++++++++++++++++++ cmd/neofs-adm/internal/modules/morph/root.go | 15 +++++ 3 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 cmd/neofs-adm/internal/modules/morph/remove_node.go diff --git a/cmd/neofs-adm/internal/modules/morph/epoch.go b/cmd/neofs-adm/internal/modules/morph/epoch.go index f29cd4ea..67dd048c 100644 --- a/cmd/neofs-adm/internal/modules/morph/epoch.go +++ b/cmd/neofs-adm/internal/modules/morph/epoch.go @@ -6,6 +6,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/io" "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" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/spf13/cobra" @@ -28,6 +29,19 @@ func forceNewEpochCmd(cmd *cobra.Command, args []string) error { return fmt.Errorf("can't get netmap contract hash: %w", err) } + bw := io.NewBufBinWriter() + if err := emitNewEpochCall(bw, wCtx, nmHash); err != nil { + return err + } + + if err := wCtx.sendCommitteeTx(bw.Bytes(), -1, true); err != nil { + return err + } + + return wCtx.awaitTx() +} + +func emitNewEpochCall(bw *io.BufBinWriter, wCtx *initializeContext, nmHash util.Uint160) error { res, err := invokeFunction(wCtx.Client, nmHash, "epoch", nil, nil) if err != nil || res.State != vm.HaltState.String() || len(res.Stack) == 0 { return errors.New("can't fetch current epoch from the netmap contract") @@ -39,15 +53,10 @@ func forceNewEpochCmd(cmd *cobra.Command, args []string) error { } newEpoch := bi.Int64() + 1 - cmd.Printf("Current epoch: %s, increase to %d.\n", bi, newEpoch) + wCtx.Command.Printf("Current epoch: %s, increase to %d.\n", bi, newEpoch) // In NeoFS this is done via Notary contract. Here, however, we can form the // transaction locally. - bw := io.NewBufBinWriter() emit.AppCall(bw.BinWriter, nmHash, "newEpoch", callflag.All, newEpoch) - if err := wCtx.sendCommitteeTx(bw.Bytes(), -1, true); err != nil { - return err - } - - return wCtx.awaitTx() + return bw.Err } diff --git a/cmd/neofs-adm/internal/modules/morph/remove_node.go b/cmd/neofs-adm/internal/modules/morph/remove_node.go new file mode 100644 index 00000000..c351d9af --- /dev/null +++ b/cmd/neofs-adm/internal/modules/morph/remove_node.go @@ -0,0 +1,60 @@ +package morph + +import ( + "errors" + "fmt" + + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" + "github.com/nspcc-dev/neo-go/pkg/vm/emit" + netmapcontract "github.com/nspcc-dev/neofs-contract/netmap" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func removeNodesCmd(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("at least one node key must be provided") + } + + nodeKeys := make(keys.PublicKeys, len(args)) + for i := range args { + var err error + nodeKeys[i], err = keys.NewPublicKeyFromString(args[i]) + if err != nil { + return fmt.Errorf("can't parse node public key: %w", err) + } + } + + wCtx, err := newInitializeContext(cmd, viper.GetViper()) + if err != nil { + return fmt.Errorf("can't initialize context: %w", err) + } + + cs, err := wCtx.Client.GetContractStateByID(1) + if err != nil { + return fmt.Errorf("can't get NNS contract info: %w", err) + } + + nmHash, err := nnsResolveHash(wCtx.Client, cs.Hash, netmapContract+".neofs") + if err != nil { + return fmt.Errorf("can't get netmap contract hash: %w", err) + } + + bw := io.NewBufBinWriter() + for i := range nodeKeys { + emit.AppCall(bw.BinWriter, nmHash, "updateStateIR", callflag.All, + int64(netmapcontract.OfflineState), nodeKeys[i].Bytes()) + } + + if err := emitNewEpochCall(bw, wCtx, nmHash); err != nil { + return err + } + + if err := wCtx.sendCommitteeTx(bw.Bytes(), -1, true); err != nil { + return err + } + + return wCtx.awaitTx() +} diff --git a/cmd/neofs-adm/internal/modules/morph/root.go b/cmd/neofs-adm/internal/modules/morph/root.go index 1d65390a..46ac04f0 100644 --- a/cmd/neofs-adm/internal/modules/morph/root.go +++ b/cmd/neofs-adm/internal/modules/morph/root.go @@ -111,6 +111,17 @@ var ( RunE: forceNewEpochCmd, } + removeNodes = &cobra.Command{ + Use: "remove-nodes key1 [key2 [...]]", + Short: "Remove storage nodes from the netmap", + Long: `Move nodes to the Offline state in the candidates list and tick an epoch to update the netmap`, + PreRun: func(cmd *cobra.Command, _ []string) { + _ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag)) + _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) + }, + RunE: removeNodesCmd, + } + setPolicy = &cobra.Command{ Use: "set-policy [ExecFeeFactor=] [StoragePrice=] [FeePerByte=]", DisableFlagsInUseLine: true, @@ -216,6 +227,10 @@ func init() { forceNewEpoch.Flags().String(alphabetWalletsFlag, "", "path to alphabet wallets dir") forceNewEpoch.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint") + RootCmd.AddCommand(removeNodes) + removeNodes.Flags().String(alphabetWalletsFlag, "", "path to alphabet wallets dir") + removeNodes.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint") + RootCmd.AddCommand(setPolicy) setPolicy.Flags().String(alphabetWalletsFlag, "", "path to alphabet wallets dir") setPolicy.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")