diff --git a/cmd/neofs-adm/internal/modules/morph/dump.go b/cmd/neofs-adm/internal/modules/morph/dump.go index 6ca27a781..986dbea33 100644 --- a/cmd/neofs-adm/internal/modules/morph/dump.go +++ b/cmd/neofs-adm/internal/modules/morph/dump.go @@ -2,14 +2,21 @@ package morph import ( "bytes" + "encoding/binary" + "encoding/hex" "errors" "fmt" "text/tabwriter" nns "github.com/nspcc-dev/neo-go/examples/nft-nd-nns" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" "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/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -94,3 +101,86 @@ func dumpContractHashes(cmd *cobra.Command, _ []string) error { return nil } + +func dumpNetworkConfig(cmd *cobra.Command, _ []string) error { + wCtx, err := newInitializeContext(cmd, viper.GetViper()) + if err != nil { + return fmt.Errorf("can't to initialize context: %w", err) + } + + cs, err := wCtx.Client.GetContractStateByID(1) + if err != nil { + return fmt.Errorf("can't get NNS contract info: %w", err) + } + + res, err := wCtx.Client.InvokeFunction(cs.Hash, "resolve", []smartcontract.Parameter{ + {Type: smartcontract.StringType, Value: netmapContract + ".neofs"}, + {Type: smartcontract.IntegerType, Value: int64(nns.TXT)}, + }, nil) + if err != nil { + return fmt.Errorf("can't get netmap contract hash: %w", err) + } + if len(res.Stack) == 0 { + return errors.New("empty response from NNS") + } + + var nmHash util.Uint160 + bs, err := res.Stack[0].TryBytes() + if err == nil { + nmHash, err = util.Uint160DecodeStringLE(string(bs)) + } + if err != nil { + return fmt.Errorf("invalid response from NNS contract: %w", err) + } + + res, err = wCtx.Client.InvokeFunction(nmHash, "listConfig", []smartcontract.Parameter{}, []transaction.Signer{{ + Account: wCtx.CommitteeAcc.Contract.ScriptHash(), + Scopes: transaction.Global, + }}) + if err != nil || res.State != vm.HaltState.String() || len(res.Stack) == 0 { + return errors.New("can't fetch list of network config keys from the netmap contract") + } + + arr, ok := res.Stack[0].Value().([]stackitem.Item) + if !ok { + return errors.New("invalid ListConfig response from netmap contract") + } + + buf := bytes.NewBuffer(nil) + tw := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0) + + for _, param := range arr { + tuple, ok := param.Value().([]stackitem.Item) + if !ok || len(tuple) != 2 { + return errors.New("invalid ListConfig response from netmap contract") + } + + k, err := tuple[0].TryBytes() + if err != nil { + return errors.New("invalid config key from netmap contract") + } + + v, err := tuple[1].TryBytes() + if err != nil { + return errors.New("invalid config value from netmap contract") + } + + switch string(k) { + case "AuditFee", "BasicIncomeRate", "ContainerFee", "EigenTrustIterations", + "EpochDuration", "InnerRingCandidateFee", "MaxObjectSize", "WithdrawFee": + nbuf := make([]byte, 8) + copy(nbuf[:], v) + n := binary.LittleEndian.Uint64(nbuf) + _, _ = tw.Write([]byte(fmt.Sprintf("%s:\t%d (int)\n", k, n))) + case "EigenTrustAlpha": + _, _ = tw.Write([]byte(fmt.Sprintf("%s:\t%s (str)\n", k, v))) + default: + _, _ = tw.Write([]byte(fmt.Sprintf("%s:\t%s (hex)\n", k, hex.EncodeToString(v)))) + } + } + + _ = tw.Flush() + cmd.Print(buf.String()) + + return nil +} diff --git a/cmd/neofs-adm/internal/modules/morph/root.go b/cmd/neofs-adm/internal/modules/morph/root.go index d91fc7ac6..669b98dc8 100644 --- a/cmd/neofs-adm/internal/modules/morph/root.go +++ b/cmd/neofs-adm/internal/modules/morph/root.go @@ -17,6 +17,16 @@ const ( maxObjectSizeCLIFlag = "max-object-size" epochDurationInitFlag = "network.epoch_duration" epochDurationCLIFlag = "epoch-duration" + incomeRateInitFlag = "network.basic_income_rate" + incomeRateCLIFlag = "basic-income-rate" + auditFeeInitFlag = "network.fee.audit" + auditFeeCLIFlag = "audit-fee" + containerFeeInitFlag = "network.fee.container" + containerFeeCLIFlag = "container-fee" + candidateFeeInitFlag = "network.fee.candidate" + candidateFeeCLIFlag = "candidate-fee" + withdrawFeeInitFlag = "network.fee.withdraw" + withdrawFeeCLIFlag = "withdraw-fee" ) var ( @@ -44,6 +54,11 @@ var ( _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) _ = viper.BindPFlag(epochDurationInitFlag, cmd.Flags().Lookup(epochDurationCLIFlag)) _ = viper.BindPFlag(maxObjectSizeInitFlag, cmd.Flags().Lookup(maxObjectSizeCLIFlag)) + _ = viper.BindPFlag(incomeRateInitFlag, cmd.Flags().Lookup(incomeRateCLIFlag)) + _ = viper.BindPFlag(auditFeeInitFlag, cmd.Flags().Lookup(auditFeeCLIFlag)) + _ = viper.BindPFlag(candidateFeeInitFlag, cmd.Flags().Lookup(candidateFeeCLIFlag)) + _ = viper.BindPFlag(containerFeeInitFlag, cmd.Flags().Lookup(containerFeeCLIFlag)) + _ = viper.BindPFlag(withdrawFeeInitFlag, cmd.Flags().Lookup(withdrawFeeCLIFlag)) }, RunE: initializeSideChainCmd, } @@ -77,6 +92,15 @@ var ( }, RunE: dumpContractHashes, } + + dumpNetworkConfigCmd = &cobra.Command{ + Use: "dump-config", + Short: "Dump NeoFS network config.", + PreRun: func(cmd *cobra.Command, _ []string) { + _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) + }, + RunE: dumpNetworkConfig, + } ) func init() { @@ -103,4 +127,7 @@ func init() { RootCmd.AddCommand(dumpContractHashesCmd) dumpContractHashesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint") + + RootCmd.AddCommand(dumpNetworkConfigCmd) + dumpNetworkConfigCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint") }