From 7582689de4e993658c09124318a01086abdc26ee Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 20 Sep 2021 19:09:32 +0300 Subject: [PATCH] [#833] morph/netmap: Support ListConfig contract method Implement `Client.ListConfig` method which calls `listConfig` method of Netmap contract. Implement `Wrapper.IterateConfigParameters` method which uses previous one. Implement `wrapper.WriteConfig` helper function which allows to interpret parameters by names. Signed-off-by: Leonard Lyubich --- pkg/morph/client/netmap/client.go | 20 ++++++- pkg/morph/client/netmap/config.go | 69 +++++++++++++++++++++++ pkg/morph/client/netmap/wrapper/config.go | 68 ++++++++++++++++++++++ 3 files changed, 156 insertions(+), 1 deletion(-) diff --git a/pkg/morph/client/netmap/client.go b/pkg/morph/client/netmap/client.go index 73a38c5a4..a72314508 100644 --- a/pkg/morph/client/netmap/client.go +++ b/pkg/morph/client/netmap/client.go @@ -43,7 +43,8 @@ type cfg struct { lastEpochBlockMethod, // get last epoch number method name updateInnerRing, // update innerring method name setConfigMethod, // set config method name - configMethod string // get config value method name + configMethod, // get config value method name + configListMethod string // list config method name } const ( @@ -61,6 +62,8 @@ const ( defaultUpdateStateMethod = "updateState" // default update state method name defaultEpochSnapshotMethod = "snapshotByEpoch" // default get network map snapshot by epoch method name + + defaultConfigListMethod = "listConfig" // default config listing method name ) func defaultConfig() *cfg { @@ -78,6 +81,7 @@ func defaultConfig() *cfg { updateStateMethod: defaultUpdateStateMethod, updateInnerRing: defaultUpdateInnerRingMethod, epochSnapshotMethod: defaultEpochSnapshotMethod, + configListMethod: defaultConfigListMethod, } } @@ -214,3 +218,17 @@ func WithEpochSnapshotMethod(n string) Option { } } } + +// WithConfigListMethod returns a client constructor option that +// specifies the config listing method name. +// +// Ignores empty value. +// +// If option not provided, "listConfig" is used. +func WithConfigListMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.configListMethod = n + } + } +} diff --git a/pkg/morph/client/netmap/config.go b/pkg/morph/client/netmap/config.go index d418f69d8..e6962adfa 100644 --- a/pkg/morph/client/netmap/config.go +++ b/pkg/morph/client/netmap/config.go @@ -68,3 +68,72 @@ func IntegerAssert(item stackitem.Item) (interface{}, error) { func StringAssert(item stackitem.Item) (interface{}, error) { return client.StringFromStackItem(item) } + +// ListConfigArgs groups the arguments +// of config listing test invoke call. +type ListConfigArgs struct { +} + +// ConfigValues groups the stack parameters +// returned by config listing test invoke. +type ListConfigValues struct { + rs []stackitem.Item +} + +// IterateRecords iterates over all config records and passes them to f. +// +// Returns f's errors directly. +func (x ListConfigValues) IterateRecords(f func(key, value []byte) error) error { + for i := range x.rs { + fields, err := client.ArrayFromStackItem(x.rs[i]) + if err != nil { + return fmt.Errorf("record fields: %w", err) + } + + if ln := len(fields); ln != 2 { + return fmt.Errorf("unexpected record fields number: %d", ln) + } + + k, err := client.BytesFromStackItem(fields[0]) + if err != nil { + return fmt.Errorf("record key: %w", err) + } + + v, err := client.BytesFromStackItem(fields[1]) + if err != nil { + return fmt.Errorf("record value: %w", err) + } + + if err := f(k, v); err != nil { + return err + } + } + + return nil +} + +// ListConfig performs the test invoke of config listing method of NeoFS Netmap contract. +func (c *Client) ListConfig(args ListConfigArgs) (*ListConfigValues, error) { + items, err := c.client.TestInvoke( + c.configListMethod, + ) + if err != nil { + return nil, fmt.Errorf("could not perform test invocation (%s): %w", + c.configListMethod, err) + } + + if ln := len(items); ln != 1 { + return nil, fmt.Errorf("unexpected stack item count (%s): %d", + c.configListMethod, ln) + } + + arr, err := client.ArrayFromStackItem(items[0]) + if err != nil { + return nil, fmt.Errorf("record list (%s): %w", + c.configListMethod, err) + } + + return &ListConfigValues{ + rs: arr, + }, nil +} diff --git a/pkg/morph/client/netmap/wrapper/config.go b/pkg/morph/client/netmap/wrapper/config.go index 77d1a2902..6e31b97e9 100644 --- a/pkg/morph/client/netmap/wrapper/config.go +++ b/pkg/morph/client/netmap/wrapper/config.go @@ -4,6 +4,7 @@ import ( "fmt" "strconv" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap" ) @@ -159,3 +160,70 @@ func (w *Wrapper) readStringConfig(key string) (string, error) { func (w *Wrapper) SetConfig(id, key []byte, value interface{}) error { return w.client.SetConfig(id, key, value) } + +// IterateConfigParameters iterates over configuration parameters stored in Netmap contract and passes them to f. +// +// Returns f's errors directly. +func (w *Wrapper) IterateConfigParameters(f func(key, value []byte) error) error { + var args netmap.ListConfigArgs + + v, err := w.client.ListConfig(args) + if err != nil { + return fmt.Errorf("client error: %w", err) + } + + return v.IterateRecords(func(key, value []byte) error { + return f(key, value) + }) +} + +// ConfigWriter is an interface of NeoFS network config writer. +type ConfigWriter interface { + UnknownParameter(string, []byte) + MaxObjectSize(uint64) + BasicIncomeRate(uint64) + AuditFee(uint64) + EpochDuration(uint64) + ContainerFee(uint64) + EigenTrustIterations(uint64) + EigenTrustAlpha(float64) + InnerRingCandidateFee(uint64) + WithdrawFee(uint64) +} + +// WriteConfig writes NeoFS network configuration received via iterator. +// +// Returns iterator's errors directly. +func WriteConfig(dst ConfigWriter, iterator func(func(key, val []byte) error) error) error { + return iterator(func(key, val []byte) error { + switch k := string(key); k { + default: + dst.UnknownParameter(k, val) + case maxObjectSizeConfig: + dst.MaxObjectSize(bigint.FromBytes(val).Uint64()) + case basicIncomeRateConfig: + dst.BasicIncomeRate(bigint.FromBytes(val).Uint64()) + case auditFeeConfig: + dst.AuditFee(bigint.FromBytes(val).Uint64()) + case epochDurationConfig: + dst.EpochDuration(bigint.FromBytes(val).Uint64()) + case containerFeeConfig: + dst.ContainerFee(bigint.FromBytes(val).Uint64()) + case etIterationsConfig: + dst.EigenTrustIterations(bigint.FromBytes(val).Uint64()) + case etAlphaConfig: + v, err := strconv.ParseFloat(string(val), 64) + if err != nil { + return fmt.Errorf("prm %s: %v", etAlphaConfig, err) + } + + dst.EigenTrustAlpha(v) + case irCandidateFeeConfig: + dst.InnerRingCandidateFee(bigint.FromBytes(val).Uint64()) + case withdrawFeeConfig: + dst.WithdrawFee(bigint.FromBytes(val).Uint64()) + } + + return nil + }) +}