From ac357fbd541f26dbc1ff7a2a7cfdbe5bd2afdf2b Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 1 Dec 2023 14:52:17 +0400 Subject: [PATCH] ir: Use default network settings for auto-deploy and drop configuration Network settings configuration did not justify itself because used only when initializing the FS chain. Use default network settings during the FS chain auto-deployment (same ones used by the NeoFS ADM tool). Introduce `fschain_autodeploy` config flag that replaces mode tuning previously based on `network_settings` section presence. Closes #2660. Signed-off-by: Leonard Lyubich --- config/example/ir.yaml | 41 +----- pkg/innerring/config.go | 180 ++++---------------------- pkg/innerring/config_test.go | 242 ++++++----------------------------- pkg/innerring/innerring.go | 10 +- 4 files changed, 72 insertions(+), 401 deletions(-) diff --git a/config/example/ir.yaml b/config/example/ir.yaml index 9891708c2c7..8944094a95b 100644 --- a/config/example/ir.yaml +++ b/config/example/ir.yaml @@ -76,44 +76,9 @@ morph: interval: 30s # Optional time period between pings. Defaults to 30s. Must not be negative timeout: 90s # Optional time period to wait for pong. Defaults to 1m. Must not be negative -network_settings: # NeoFS network settings managed in the Netmap contract - epoch_duration: 240 # Time interval (approximate) between two adjacent NeoFS epochs measured in Sidechain blocks. - # Must be an integer in range [1, 18446744073709551615] - max_object_size: 67108864 # [bytes] Maximum size of physically stored NeoFS objects. Note that this applies - # only to objects located on storage nodes: user objects have no restrictions and, if necessary, are sliced. - # Must be an integer in range [1, 18446744073709551615] - require_homomorphic_hashing: true # Toggles the requirement for homomorphic hashing of object payloads. - # Must be 'true' or 'false' - allow_maintenance_mode: true # Toggles permission to transition storage nodes to maintenance state. - # Must be 'true' or 'false' - eigen_trust: - alpha: 0.1 # Alpha parameter of EigenTrust algorithm used in the Reputation system. - # Must be a floating point number in range [0, 1]. - iterations_number: 4 # Number of EigenTrust algorithm iterations to pass in the Reputation system. - # Must be an integer in range [1, 18446744073709551615] - price: # Price settings. NEOFS means NeoFS Balance contract tokens (usually GASe-12). - storage: 100000000 # [NEOFS] Price for 1GB of data paid every epoch by data owner to storage nodes. - # Must be an integer in range [0, 18446744073709551615] - fee: - ir_candidate: 100 # [GASe-8] Contribution from the new candidate to the Inner Ring. Must be non-negative integer - # Must be an integer in range [0, 18446744073709551615] - withdraw: 100000000 # [GASe-8] Fee paid by the user account to; - # - NeoFS Processing contract (if Notary service is enabled in the NeoFS Mainchain) - # - each Alphabet member (otherwise) - # Must be an integer in range [0, 18446744073709551615] - audit: 10000 # [NEOFS] Fee for data audit paid by storage group owner to the auditor (Inner Ring member). - # Must be an integer in range [0, 18446744073709551615] - new_container: 1000 # [NEOFS] Fee for new container paid by creator to each Alphabet member. - # Must be an integer in range [0, 18446744073709551615] - container_domain: 500 # [NEOFS] Fee for container's NNS domain paid by container creator to each Alphabet member. - # Must be a non-negative integer - custom: # Optional list of custom key-value pairs to be set in the network configuration. Forbidden keys: - # [AuditFee, BasicIncomeRate, ContainerAliasFee, ContainerFee, EigenTrustAlpha, EigenTrustIterations, EpochDuration, - # HomomorphicHashingDisabled, InnerRingCandidateFee, MaintenanceModeAllowed, MaxObjectSize, WithdrawFee] - # Note that this list can be extended in the future, so, to avoid potential collision, it is recommended - # to use the most specific keys. - - my_custom_key1=val1 - - my_custom_key2=val2 +fschain_autodeploy: true # Optional flag to run auto-deployment procedure of the FS chain. By default, + # the chain is expected to be deployed/updated in the background (e.g. via NeoFS ADM tool). + # If set, must be 'true' or 'false'. nns: system_email: usr@domain.io diff --git a/pkg/innerring/config.go b/pkg/innerring/config.go index ea6f1951ab5..e58d6bb08c2 100644 --- a/pkg/innerring/config.go +++ b/pkg/innerring/config.go @@ -20,9 +20,15 @@ import ( ) // checks whether Inner Ring app is configured to initialize underlying NeoFS -// Sidechain or await for a background deployment. -func isAutoDeploymentMode(cfg *viper.Viper) bool { - return cfg.IsSet("network_settings") +// Sidechain or await for a background deployment. Returns an error if +// the configuration format is violated. +func isAutoDeploymentMode(cfg *viper.Viper) (bool, error) { + res, err := parseConfigBool(cfg, "fschain_autodeploy", "flag to auto-deploy the FS chain") + if err != nil && !errors.Is(err, errMissingConfig) { + return false, err + } + + return res, nil } // checks if Inner Ring app is configured to be launched in local consensus @@ -209,139 +215,21 @@ func parseBlockchainConfig(v *viper.Viper, _logger *zap.Logger) (c blockchain.Co return c, nil } -const networkSettingsConfigSection = "network_settings" - -func parseNetworkSettingsConfig(v *viper.Viper) (c netmap.NetworkConfiguration, err error) { - if !v.IsSet(networkSettingsConfigSection) { - return c, fmt.Errorf("missing root section '%s'", networkSettingsConfigSection) - } - - c.EpochDuration, err = parseConfigUint64Range(v, networkSettingsConfigSection+".epoch_duration", "epoch duration", 1, math.MaxUint32) - if err != nil { - return - } - - c.MaxObjectSize, err = parseConfigUint64Range(v, networkSettingsConfigSection+".max_object_size", "max object size", 1, math.MaxUint64) - if err != nil { - return - } - - requireHomoHash, err := parseConfigBool(v, networkSettingsConfigSection+".require_homomorphic_hashing", "is homomorphic hashing required") - if err != nil { - return - } - - c.HomomorphicHashingDisabled = !requireHomoHash - - c.MaintenanceModeAllowed, err = parseConfigBool(v, networkSettingsConfigSection+".allow_maintenance_mode", "is maintenance mode allowed") - if err != nil { - return - } - - const eigenTrustSection = networkSettingsConfigSection + ".eigen_trust" - if !v.IsSet(eigenTrustSection) { - return c, fmt.Errorf("missing EigenTrust section '%s'", eigenTrustSection) - } - - c.EigenTrustAlpha, err = parseConfigFloatRange(v, eigenTrustSection+".alpha", "EigenTrust alpha parameter", 0, 1) - if err != nil { - return - } - - c.EigenTrustIterations, err = parseConfigUint64Range(v, eigenTrustSection+".iterations_number", "number of EigenTrust iterations", 1, math.MaxUint64) - if err != nil { - return - } - - const priceSection = networkSettingsConfigSection + ".price" - if !v.IsSet(priceSection) { - return c, fmt.Errorf("missing price section '%s'", priceSection) - } - - c.StoragePrice, err = parseConfigUint64Max(v, priceSection+".storage", "storage price", math.MaxUint64) - if err != nil { - return - } - - const feeSection = priceSection + ".fee" - if !v.IsSet(feeSection) { - return c, fmt.Errorf("missing fee section '%s'", feeSection) - } - - c.IRCandidateFee, err = parseConfigUint64Max(v, feeSection+".ir_candidate", "Inner Ring candidate fee", math.MaxUint64) - if err != nil { - return - } - - c.WithdrawalFee, err = parseConfigUint64Max(v, feeSection+".withdraw", "withdrawal fee", math.MaxUint64) - if err != nil { - return - } - - c.AuditFee, err = parseConfigUint64Max(v, feeSection+".audit", "data audit fee", math.MaxUint64) - if err != nil { - return - } - - c.ContainerFee, err = parseConfigUint64Max(v, feeSection+".new_container", "container creation fee", math.MaxUint64) - if err != nil { - return - } - - c.ContainerAliasFee, err = parseConfigUint64Max(v, feeSection+".container_domain", "container domain fee", math.MaxUint64) - if err != nil { - return - } - - customSettingsKey := networkSettingsConfigSection + ".custom" - if v.IsSet(customSettingsKey) { - var sss []string - sss, err = parseConfigStrings(v, customSettingsKey, "custom settings") - if err != nil { - return - } - - if len(sss) == 0 { - return c, fmt.Errorf("missing custom settings '%s'", customSettingsKey) - } - - c.Raw = make([]netmap.RawNetworkParameter, len(sss)) - - for i := range sss { - const sep = "=" - ss := strings.Split(sss[i], sep) - if len(ss) != 2 { - return c, fmt.Errorf("invalid %s '%s' (%s-separated key-value): failed to parse element #%d", customSettingsKey, ss[i], sep, i) - } - - switch ss[0] { - default: - for j := 0; j < i; j++ { - if ss[0] == c.Raw[j].Name { - return c, fmt.Errorf("duplicated custom network setting '%s' in '%s'", ss[0], customSettingsKey) - } - } - case "AuditFee", - "BasicIncomeRate", - "ContainerAliasFee", - "ContainerFee", - "EigenTrustAlpha", - "EigenTrustIterations", - "EpochDuration", - "HomomorphicHashingDisabled", - "InnerRingCandidateFee", - "MaintenanceModeAllowed", - "MaxObjectSize", - "WithdrawFee": - return c, fmt.Errorf("invalid %s '%s' (%s-separated key-value): key to element #%d is forbidden", customSettingsKey, ss[i], sep, i) - } - - c.Raw[i].Name = ss[0] - c.Raw[i].Value = []byte(ss[1]) - } - } - - return +// sets NeoFS network settings to be used for the NeoFS Sidechain +// auto-deployment. +func setNetworkSettingsDefaults(netCfg *netmap.NetworkConfiguration) { + netCfg.MaxObjectSize = 64 << 20 // 64MB of object payload + netCfg.EpochDuration = 240 // in NeoFS Sidechain blocks (e.g. ~1h for 15s block interval) + netCfg.StoragePrice = 0 // in GAS per 1GB (NeoFS Balance contract's decimals) + netCfg.AuditFee = 0 // in GAS per audit (NeoFS Balance contract's decimals) + netCfg.ContainerFee = 1000 // in GAS per container (NeoFS Balance contract's decimals) + netCfg.ContainerAliasFee = 500 // in GAS per container (NeoFS Balance contract's decimals) + netCfg.IRCandidateFee = 0 // in GAS per candidate (Fixed8) + netCfg.WithdrawalFee = 0 // in GAS per withdrawal (Fixed8) + netCfg.EigenTrustIterations = 4 + netCfg.EigenTrustAlpha = 0.1 + netCfg.HomomorphicHashingDisabled = false + netCfg.MaintenanceModeAllowed = false } type nnsConfig struct { @@ -530,26 +418,6 @@ func parseConfigBool(v *viper.Viper, key, desc string) (bool, error) { return res, nil } -func parseConfigFloatRange(v *viper.Viper, key, desc string, min, max float64) (float64, error) { - var res float64 - var err error - if !v.IsSet(key) { - err = errMissingConfig - } - if err == nil { - res, err = cast.ToFloat64E(v.Get(key)) - if err == nil { - if res < min || res > max { - err = fmt.Errorf("out of allowable range [%.2f:%.2f]", min, max) - } - } - } - if err != nil { - return res, fmt.Errorf("invalid %s '%s' (boolean): %w", desc, key, err) - } - return res, nil -} - func parseConfigString(v *viper.Viper, key, desc string) (string, error) { var res string var err error diff --git a/pkg/innerring/config_test.go b/pkg/innerring/config_test.go index 7a5968ea59a..aa81ff3a666 100644 --- a/pkg/innerring/config_test.go +++ b/pkg/innerring/config_test.go @@ -10,7 +10,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neofs-node/pkg/innerring/internal/blockchain" - "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap" "github.com/spf13/viper" "github.com/stretchr/testify/require" "go.uber.org/zap" @@ -362,198 +361,6 @@ morph: }) } -// YAML configuration of the NeoFS network settings with all required fields. -const validNetworkSettingsConfigMinimal = ` -network_settings: - epoch_duration: 1 - max_object_size: 2 - require_homomorphic_hashing: true - allow_maintenance_mode: false - eigen_trust: - alpha: 0.1 - iterations_number: 3 - price: - storage: 4 - fee: - ir_candidate: 5 - withdraw: 6 - audit: 7 - new_container: 8 - container_domain: 9 -` - -// YAML configuration the NeoFS network settings with all optional fields. -const validNetworkSettingsConfigOptions = ` - custom: - - my_custom_key1=val1 - - my_custom_key2=val2 -` - -// returns viper.Viper initialized from valid network configuration above. -func newValidNetworkSettingsConfig(tb testing.TB, full bool) *viper.Viper { - if full { - return _newConfigFromYAML(tb, validNetworkSettingsConfigMinimal, validNetworkSettingsConfigOptions) - } - - return _newConfigFromYAML(tb, validNetworkSettingsConfigMinimal, "") -} - -func TestParseNetworkSettingsConfig(t *testing.T) { - fullConfig := true - - t.Run("minimal", func(t *testing.T) { - v := newValidNetworkSettingsConfig(t, !fullConfig) - c, err := parseNetworkSettingsConfig(v) - require.NoError(t, err) - - require.Equal(t, netmap.NetworkConfiguration{ - MaxObjectSize: 2, - StoragePrice: 4, - AuditFee: 7, - EpochDuration: 1, - ContainerFee: 8, - ContainerAliasFee: 9, - EigenTrustIterations: 3, - EigenTrustAlpha: 0.1, - IRCandidateFee: 5, - WithdrawalFee: 6, - HomomorphicHashingDisabled: false, - MaintenanceModeAllowed: false, - }, c) - }) - - t.Run("full", func(t *testing.T) { - v := newValidNetworkSettingsConfig(t, fullConfig) - c, err := parseNetworkSettingsConfig(v) - require.NoError(t, err) - - require.Equal(t, netmap.NetworkConfiguration{ - MaxObjectSize: 2, - StoragePrice: 4, - AuditFee: 7, - EpochDuration: 1, - ContainerFee: 8, - ContainerAliasFee: 9, - EigenTrustIterations: 3, - EigenTrustAlpha: 0.1, - IRCandidateFee: 5, - WithdrawalFee: 6, - HomomorphicHashingDisabled: false, - MaintenanceModeAllowed: false, - Raw: []netmap.RawNetworkParameter{ - {Name: "my_custom_key1", Value: []byte("val1")}, - {Name: "my_custom_key2", Value: []byte("val2")}, - }, - }, c) - }) - - t.Run("incomplete", func(t *testing.T) { - for _, requiredKey := range []string{ - "epoch_duration", - "max_object_size", - "require_homomorphic_hashing", - "allow_maintenance_mode", - "eigen_trust", - "eigen_trust.alpha", - "eigen_trust.iterations_number", - "price.storage", - "price.fee", - "price.fee.ir_candidate", - "price.fee.withdraw", - "price.fee.audit", - "price.fee.new_container", - "price.fee.container_domain", - } { - v := newValidNetworkSettingsConfig(t, !fullConfig) - resetConfig(t, v, "network_settings."+requiredKey) - _, err := parseNetworkSettingsConfig(v) - require.Error(t, err, requiredKey) - } - }) - - t.Run("invalid", func(t *testing.T) { - type kv struct { - key string - val interface{} - } - - kvF := func(k string, v interface{}) kv { - return kv{k, v} - } - - for _, testCase := range [][]kv{ - {kvF("epoch_duration", "not an integer")}, - {kvF("epoch_duration", -1)}, - {kvF("epoch_duration", 0)}, - {kvF("epoch_duration", 0.1)}, - {kvF("max_object_size", "not an integer")}, - {kvF("max_object_size", -1)}, - {kvF("max_object_size", 0)}, - {kvF("max_object_size", 0.1)}, - {kvF("require_homomorphic_hashing", "not a boolean")}, - {kvF("require_homomorphic_hashing", 1)}, - {kvF("require_homomorphic_hashing", "True")}, - {kvF("require_homomorphic_hashing", "False")}, - {kvF("allow_maintenance_mode", "not a boolean")}, - {kvF("allow_maintenance_mode", 1)}, - {kvF("allow_maintenance_mode", "True")}, - {kvF("allow_maintenance_mode", "False")}, - {kvF("eigen_trust.alpha", "not a float")}, - {kvF("eigen_trust.alpha", -0.1)}, - {kvF("eigen_trust.alpha", 1.1)}, - {kvF("eigen_trust.iterations_number", "not an integer")}, - {kvF("eigen_trust.iterations_number", -1)}, - {kvF("eigen_trust.iterations_number", 0)}, - {kvF("eigen_trust.iterations_number", 0.1)}, - {kvF("price.storage", "not an integer")}, - {kvF("price.storage", -1)}, - {kvF("price.storage", 0.1)}, - {kvF("price.fee.ir_candidate", "not an integer")}, - {kvF("price.fee.ir_candidate", -1)}, - {kvF("price.fee.ir_candidate", 0.1)}, - {kvF("price.fee.withdraw", "not an integer")}, - {kvF("price.fee.withdraw", -1)}, - {kvF("price.fee.withdraw", 0.1)}, - {kvF("price.fee.audit", "not an integer")}, - {kvF("price.fee.audit", -1)}, - {kvF("price.fee.audit", 0.1)}, - {kvF("price.fee.new_container", "not an integer")}, - {kvF("price.fee.new_container", -1)}, - {kvF("price.fee.new_container", 0.1)}, - {kvF("price.fee.container_domain", "not an integer")}, - {kvF("price.fee.container_domain", -1)}, - {kvF("price.fee.container_domain", 0.1)}, - {kvF("custom", []string{})}, - {kvF("custom", []string{"without_separator"})}, - {kvF("custom", []string{"with=several=separators"})}, - {kvF("custom", []string{"dup=1", "dup=2"})}, - {kvF("custom", []string{"AuditFee=any"})}, - {kvF("custom", []string{"BasicIncomeRate=any"})}, - {kvF("custom", []string{"ContainerAliasFee=any"})}, - {kvF("custom", []string{"EigenTrustIterations=any"})}, - {kvF("custom", []string{"EpochDuration=any"})}, - {kvF("custom", []string{"HomomorphicHashingDisabled=any"})}, - {kvF("custom", []string{"MaintenanceModeAllowed=any"})}, - {kvF("custom", []string{"MaxObjectSize=any"})}, - {kvF("custom", []string{"WithdrawFee=any"})}, - } { - var reportMsg []string - - v := newValidNetworkSettingsConfig(t, fullConfig) - for _, kvPair := range testCase { - key := kvPair.key - val := kvPair.val - - v.Set("network_settings."+key, val) - reportMsg = append(reportMsg, fmt.Sprintf("%s=%v", key, val)) - } - - _, err := parseNetworkSettingsConfig(v) - require.Error(t, err, strings.Join(reportMsg, ", ")) - } - }) -} - // YAML configuration of the NNS with all required fields. const validNNSConfig = ` nns: @@ -624,32 +431,63 @@ func TestIsAutoDeploymentMode(t *testing.T) { v.SetEnvPrefix("neofs_ir") v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - const envKey = "NEOFS_IR_NETWORK_SETTINGS" + const envKey = "NEOFS_IR_FSCHAIN_AUTODEPLOY" err := os.Unsetenv(envKey) require.NoError(t, err) - require.False(t, isAutoDeploymentMode(v)) + b, err := isAutoDeploymentMode(v) + require.NoError(t, err) + require.False(t, b) - err = os.Setenv(envKey, "any string") + err = os.Setenv(envKey, "true") + require.NoError(t, err) + + b, err = isAutoDeploymentMode(v) + require.NoError(t, err) + require.True(t, b) + + err = os.Setenv(envKey, "not a boolean") + require.NoError(t, err) + + b, err = isAutoDeploymentMode(v) + require.Error(t, err) + + err = os.Setenv(envKey, "false") require.NoError(t, err) - require.True(t, isAutoDeploymentMode(v)) + b, err = isAutoDeploymentMode(v) + require.NoError(t, err) + require.False(t, b) }) t.Run("YAML", func(t *testing.T) { v := viper.New() v.SetConfigType("yaml") err := v.ReadConfig(strings.NewReader(` -network_settings: - any_key: any_val +fschain_autodeploy: true `)) require.NoError(t, err) - require.True(t, isAutoDeploymentMode(v)) + b, err := isAutoDeploymentMode(v) + require.NoError(t, err) + require.True(t, b) + + resetConfig(t, v, "fschain_autodeploy") + + b, err = isAutoDeploymentMode(v) + require.NoError(t, err) + require.False(t, b) - resetConfig(t, v, "network_settings") + v.Set("fschain_autodeploy", "not a boolean") - require.False(t, isAutoDeploymentMode(v)) + b, err = isAutoDeploymentMode(v) + require.Error(t, err) + + v.Set("fschain_autodeploy", "false") + + b, err = isAutoDeploymentMode(v) + require.NoError(t, err) + require.False(t, b) }) } diff --git a/pkg/innerring/innerring.go b/pkg/innerring/innerring.go index 67f4782d681..27864c2c366 100644 --- a/pkg/innerring/innerring.go +++ b/pkg/innerring/innerring.go @@ -445,7 +445,10 @@ func New(ctx context.Context, log *zap.Logger, cfg *viper.Viper, errChan chan<- sidechainOpts[1] = client.WithLogger(log) sidechainOpts[2] = client.WithSingleClient(wsClient) - isAutoDeploy := isAutoDeploymentMode(cfg) + isAutoDeploy, err := isAutoDeploymentMode(cfg) + if err != nil { + return nil, err + } if !isAutoDeploy { sidechainOpts = append(sidechainOpts, client.WithAutoSidechainScope()) @@ -480,10 +483,7 @@ func New(ctx context.Context, log *zap.Logger, cfg *viper.Viper, errChan chan<- return nil, err } - deployPrm.NetmapContract.Config, err = parseNetworkSettingsConfig(cfg) - if err != nil { - return nil, fmt.Errorf("invalid configuration of network settings: %w", err) - } + setNetworkSettingsDefaults(&deployPrm.NetmapContract.Config) err = deploy.Deploy(ctx, deployPrm) if err != nil {