frostfs-node/pkg/morph/client/netmap/config.go
Evgenii Stratonikov c4f941a5f5
All checks were successful
Vulncheck / Vulncheck (push) Successful in 1m13s
Pre-commit hooks / Pre-commit (push) Successful in 1m48s
Build / Build Components (push) Successful in 2m19s
Tests and linters / Run gofumpt (push) Successful in 2m22s
Tests and linters / Tests (push) Successful in 3m29s
Tests and linters / Staticcheck (push) Successful in 3m33s
Tests and linters / Lint (push) Successful in 4m17s
Tests and linters / gopls check (push) Successful in 4m26s
OCI image / Build container images (push) Successful in 4m49s
Tests and linters / Tests with -race (push) Successful in 5m17s
[#1689] client/netmap: Remove useless error-handling
No functional changes.

Change-Id: I3a53c992c3ce5e8c6db252abb09aa40626142a97
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-05 09:16:33 +03:00

300 lines
8.1 KiB
Go

package netmap
import (
"context"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
const (
MaxObjectSizeConfig = "MaxObjectSize"
MaxECParityCountConfig = "MaxECParityCount"
MaxECDataCountConfig = "MaxECDataCount"
EpochDurationConfig = "EpochDuration"
ContainerFeeConfig = "ContainerFee"
ContainerAliasFeeConfig = "ContainerAliasFee"
IrCandidateFeeConfig = "InnerRingCandidateFee"
WithdrawFeeConfig = "WithdrawFee"
HomomorphicHashingDisabledKey = "HomomorphicHashingDisabled"
MaintenanceModeAllowedConfig = "MaintenanceModeAllowed"
)
// MaxObjectSize receives max object size configuration
// value through the Netmap contract call.
func (c *Client) MaxObjectSize(ctx context.Context) (uint64, error) {
return c.readUInt64Config(ctx, MaxObjectSizeConfig)
}
// EpochDuration returns number of sidechain blocks per one FrostFS epoch.
func (c *Client) EpochDuration(ctx context.Context) (uint64, error) {
return c.readUInt64Config(ctx, EpochDurationConfig)
}
// ContainerFee returns fee paid by container owner to each alphabet node
// for container registration.
func (c *Client) ContainerFee(ctx context.Context) (uint64, error) {
return c.readUInt64Config(ctx, ContainerFeeConfig)
}
// ContainerAliasFee returns additional fee paid by container owner to each
// alphabet node for container nice name registration.
func (c *Client) ContainerAliasFee(ctx context.Context) (uint64, error) {
return c.readUInt64Config(ctx, ContainerAliasFeeConfig)
}
// HomomorphicHashDisabled returns global configuration value of homomorphic hashing
// settings.
//
// Returns (false, nil) if config key is not found in the contract.
func (c *Client) HomomorphicHashDisabled(ctx context.Context) (bool, error) {
return c.readBoolConfig(ctx, HomomorphicHashingDisabledKey)
}
// InnerRingCandidateFee returns global configuration value of fee paid by
// node to be in inner ring candidates list.
func (c *Client) InnerRingCandidateFee(ctx context.Context) (uint64, error) {
return c.readUInt64Config(ctx, IrCandidateFeeConfig)
}
// WithdrawFee returns global configuration value of fee paid by user to
// withdraw assets from FrostFS contract.
func (c *Client) WithdrawFee(ctx context.Context) (uint64, error) {
return c.readUInt64Config(ctx, WithdrawFeeConfig)
}
// MaintenanceModeAllowed reads admission of "maintenance" state from the
// FrostFS network configuration stored in the Sidechain. The admission means
// that storage nodes are allowed to switch their state to "maintenance".
//
// By default, maintenance state is disallowed.
func (c *Client) MaintenanceModeAllowed(ctx context.Context) (bool, error) {
return c.readBoolConfig(ctx, MaintenanceModeAllowedConfig)
}
func (c *Client) readUInt64Config(ctx context.Context, key string) (uint64, error) {
v, err := c.config(ctx, []byte(key))
if err != nil {
return 0, fmt.Errorf("read netconfig value '%s': %w", key, err)
}
bi, err := v.TryInteger()
if err != nil {
return 0, err
}
return bi.Uint64(), nil
}
// reads boolean value by the given key from the FrostFS network configuration
// stored in the Sidechain. Returns false if key is not presented.
func (c *Client) readBoolConfig(ctx context.Context, key string) (bool, error) {
v, err := c.config(ctx, []byte(key))
if err != nil {
return false, fmt.Errorf("read netconfig value '%s': %w", key, err)
}
return v.TryBool()
}
// SetConfigPrm groups parameters of SetConfig operation.
type SetConfigPrm struct {
id []byte
key []byte
value any
client.InvokePrmOptional
}
// SetID sets ID of the config value.
func (s *SetConfigPrm) SetID(id []byte) {
s.id = id
}
// SetKey sets key of the config value.
func (s *SetConfigPrm) SetKey(key []byte) {
s.key = key
}
// SetValue sets value of the config value.
func (s *SetConfigPrm) SetValue(value any) {
s.value = value
}
// SetConfig sets config field.
func (c *Client) SetConfig(ctx context.Context, p SetConfigPrm) error {
prm := client.InvokePrm{}
prm.SetMethod(setConfigMethod)
prm.SetArgs(p.id, p.key, p.value)
prm.InvokePrmOptional = p.InvokePrmOptional
_, err := c.client.Invoke(ctx, prm)
return err
}
// RawNetworkParameter is a FrostFS network parameter which is transmitted but
// not interpreted by the FrostFS API protocol.
type RawNetworkParameter struct {
// Name of the parameter.
Name string
// Raw parameter value.
Value []byte
}
// NetworkConfiguration represents FrostFS network configuration stored
// in the FrostFS Sidechain.
type NetworkConfiguration struct {
MaxObjectSize uint64
EpochDuration uint64
ContainerFee uint64
ContainerAliasFee uint64
IRCandidateFee uint64
WithdrawalFee uint64
HomomorphicHashingDisabled bool
MaintenanceModeAllowed bool
Raw []RawNetworkParameter
}
// ReadNetworkConfiguration reads NetworkConfiguration from the FrostFS Sidechain.
func (c *Client) ReadNetworkConfiguration(ctx context.Context) (NetworkConfiguration, error) {
var res NetworkConfiguration
prm := client.TestInvokePrm{}
prm.SetMethod(configListMethod)
items, err := c.client.TestInvoke(ctx, prm)
if err != nil {
return res, fmt.Errorf("test invoke (%s): %w",
configListMethod, err)
}
if ln := len(items); ln != 1 {
return res, fmt.Errorf("unexpected stack item count (%s): %d", configListMethod, ln)
}
arr, err := client.ArrayFromStackItem(items[0])
if err != nil {
return res, fmt.Errorf("record list (%s): %w", configListMethod, err)
}
m := make(map[string]struct{}, len(arr))
res.Raw = make([]RawNetworkParameter, 0, len(arr))
err = iterateRecords(arr, func(name string, value []byte) error {
_, ok := m[name]
if ok {
return fmt.Errorf("duplicated config name %s", name)
}
m[name] = struct{}{}
switch name {
default:
res.Raw = append(res.Raw, RawNetworkParameter{
Name: name,
Value: value,
})
case MaxObjectSizeConfig:
res.MaxObjectSize = bytesToUint64(value)
case EpochDurationConfig:
res.EpochDuration = bytesToUint64(value)
case ContainerFeeConfig:
res.ContainerFee = bytesToUint64(value)
case ContainerAliasFeeConfig:
res.ContainerAliasFee = bytesToUint64(value)
case IrCandidateFeeConfig:
res.IRCandidateFee = bytesToUint64(value)
case WithdrawFeeConfig:
res.WithdrawalFee = bytesToUint64(value)
case HomomorphicHashingDisabledKey:
res.HomomorphicHashingDisabled = bytesToBool(value)
case MaintenanceModeAllowedConfig:
res.MaintenanceModeAllowed = bytesToBool(value)
}
return nil
})
return res, err
}
func bytesToUint64(val []byte) uint64 {
if len(val) == 0 {
return 0
}
return bigint.FromBytes(val).Uint64()
}
func bytesToBool(val []byte) bool {
for i := range val {
if val[i] != 0 {
return true
}
}
return false
}
// config performs the test invoke of get config value
// method of FrostFS Netmap contract.
//
// Returns ErrConfigNotFound if config key is not found in the contract.
func (c *Client) config(ctx context.Context, key []byte) (stackitem.Item, error) {
prm := client.TestInvokePrm{}
prm.SetMethod(configMethod)
prm.SetArgs(key)
items, err := c.client.TestInvoke(ctx, prm)
if err != nil {
return nil, fmt.Errorf("test invoke (%s): %w",
configMethod, err)
}
if ln := len(items); ln != 1 {
return nil, fmt.Errorf("unexpected stack item count (%s): %d",
configMethod, ln)
}
return items[0], nil
}
// iterateRecords iterates over all config records and passes them to f.
//
// Returns f's errors directly.
func iterateRecords(arr []stackitem.Item, f func(key string, value []byte) error) error {
for i := range arr {
fields, err := client.ArrayFromStackItem(arr[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(string(k), v); err != nil {
return err
}
}
return nil
}