[#1513] Upgrade NeoFS SDK Go with changed netmap package

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2022-06-09 02:18:26 +03:00 committed by LeL
parent 24b4c1ecf4
commit 21d2f8f861
70 changed files with 876 additions and 990 deletions

View file

@ -244,8 +244,8 @@ type NodeInfoRes struct {
}
// NodeInfo returns information about the node from netmap.
func (x NodeInfoRes) NodeInfo() *netmap.NodeInfo {
return x.cliRes.NodeInfo()
func (x NodeInfoRes) NodeInfo() netmap.NodeInfo {
return *x.cliRes.NodeInfo()
}
// LatestVersion returns the latest NeoFS API version in use.

View file

@ -16,7 +16,6 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/acl"
"github.com/nspcc-dev/neofs-sdk-go/container"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/policy"
"github.com/nspcc-dev/neofs-sdk-go/session"
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
"github.com/nspcc-dev/neofs-sdk-go/user"
@ -69,12 +68,14 @@ It will be stored in sidechain when inner ring will accepts it.`,
placementPolicy, err := parseContainerPolicy(containerPolicy)
common.ExitOnErr(cmd, "", err)
var subnetID subnetid.ID
if containerSubnet != "" {
var subnetID subnetid.ID
err = subnetID.DecodeString(containerSubnet)
common.ExitOnErr(cmd, "could not parse subnetID: %w", err)
err = subnetID.DecodeString(containerSubnet)
common.ExitOnErr(cmd, "could not parse subnetID: %w", err)
placementPolicy.SetSubnetID(&subnetID)
placementPolicy.RestrictSubnet(subnetID)
}
attributes, err := parseAttributes(containerAttributes)
common.ExitOnErr(cmd, "", err)
@ -177,16 +178,17 @@ func parseContainerPolicy(policyString string) (*netmap.PlacementPolicy, error)
policyString = string(data)
}
result, err := policy.Parse(policyString)
var result netmap.PlacementPolicy
err = result.DecodeString(policyString)
if err == nil {
common.PrintVerbose("Parsed QL encoded policy")
return result, nil
return &result, nil
}
result = netmap.NewPlacementPolicy()
if err = result.UnmarshalJSON([]byte(policyString)); err == nil {
common.PrintVerbose("Parsed JSON encoded policy")
return result, nil
return &result, nil
}
return nil, errors.New("can't parse placement policy")

View file

@ -4,7 +4,6 @@ import (
"bytes"
"encoding/json"
"os"
"strings"
internalclient "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/client"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/common"
@ -12,7 +11,6 @@ import (
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key"
"github.com/nspcc-dev/neofs-sdk-go/acl"
"github.com/nspcc-dev/neofs-sdk-go/container"
"github.com/nspcc-dev/neofs-sdk-go/policy"
"github.com/spf13/cobra"
)
@ -85,6 +83,13 @@ func initContainerInfoCmd() {
flags.BoolVar(&containerJSON, "json", false, "print or dump container in JSON format")
}
type stringWriter cobra.Command
func (x *stringWriter) WriteString(s string) (n int, err error) {
(*cobra.Command)(x).Print(s)
return len(s), nil
}
func prettyPrintContainer(cmd *cobra.Command, cnr *container.Container, jsonEncoding bool) {
if cnr == nil {
return
@ -137,8 +142,14 @@ func prettyPrintContainer(cmd *cobra.Command, cnr *container.Container, jsonEnco
cmd.Println("invalid nonce:", err)
}
cmd.Println("placement policy:")
cmd.Println(strings.Join(policy.Encode(cnr.PlacementPolicy()), "\n"))
pp := cnr.PlacementPolicy()
if pp == nil {
cmd.Println("missing placement policy")
} else {
cmd.Println("placement policy:")
common.ExitOnErr(cmd, "write policy: %w", pp.WriteStringTo((*stringWriter)(cmd)))
cmd.Println()
}
}
func prettyPrintBasicACL(cmd *cobra.Command, basicACL acl.BasicACL) {

View file

@ -9,13 +9,9 @@ import (
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/common"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key"
nmClient "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/spf13/cobra"
)
type netCfgWriter cobra.Command
var netInfoCmd = &cobra.Command{
Use: "netinfo",
Short: "Get information about NeoFS network",
@ -39,21 +35,23 @@ var netInfoCmd = &cobra.Command{
cmd.Printf("Time per block: %s\n", time.Duration(netInfo.MsPerBlock())*time.Millisecond)
netCfg := netInfo.NetworkConfig()
const format = " %s: %v\n"
cmd.Println("NeoFS network configuration")
cmd.Println("NeoFS network configuration (system)")
cmd.Printf(format, "Audit fee", netInfo.AuditFee())
cmd.Printf(format, "Storage price", netInfo.StoragePrice())
cmd.Printf(format, "Container fee", netInfo.ContainerFee())
cmd.Printf(format, "EigenTrust alpha", netInfo.EigenTrustAlpha())
cmd.Printf(format, "Number of EigenTrust iterations", netInfo.NumberOfEigenTrustIterations())
cmd.Printf(format, "Epoch duration", netInfo.EpochDuration())
cmd.Printf(format, "Inner Ring candidate fee", netInfo.IRCandidateFee())
cmd.Printf(format, "Maximum object size", netInfo.MaxObjectSize())
cmd.Printf(format, "Withdrawal fee", netInfo.WithdrawalFee())
err = nmClient.WriteConfig((*netCfgWriter)(cmd), func(f func(key []byte, val []byte) error) error {
var err error
netCfg.IterateParameters(func(prm *netmap.NetworkParameter) bool {
err = f(prm.Key(), prm.Value())
return err != nil
})
return err
cmd.Println("NeoFS network configuration (other)")
netInfo.IterateRawNetworkParameters(func(name string, value []byte) {
cmd.Printf(format, name, hex.EncodeToString(value))
})
common.ExitOnErr(cmd, "read config: %w", err)
},
}
@ -61,57 +59,3 @@ func initNetInfoCmd() {
commonflags.Init(netInfoCmd)
commonflags.InitAPI(netInfoCmd)
}
func (x *netCfgWriter) print(name string, v interface{}, unknown bool) {
var sUnknown string
if unknown {
sUnknown = " (unknown)"
}
(*cobra.Command)(x).Printf(" %s%s: %v\n", name, sUnknown, v)
}
func (x *netCfgWriter) UnknownParameter(k string, v []byte) {
x.print(k, hex.EncodeToString(v), true)
}
func (x *netCfgWriter) MaxObjectSize(v uint64) {
x.print("Maximum object size", v, false)
}
func (x *netCfgWriter) BasicIncomeRate(v uint64) {
x.print("Basic income rate", v, false)
}
func (x *netCfgWriter) AuditFee(v uint64) {
x.print("Audit fee", v, false)
}
func (x *netCfgWriter) EpochDuration(v uint64) {
x.print("Epoch duration", v, false)
}
func (x *netCfgWriter) ContainerFee(v uint64) {
x.print("Container fee", v, false)
}
func (x *netCfgWriter) ContainerAliasFee(v uint64) {
x.print("Container alias fee", v, false)
}
func (x *netCfgWriter) EigenTrustIterations(v uint64) {
x.print("Number EigenTrust of iterations", v, false)
}
func (x *netCfgWriter) EigenTrustAlpha(v float64) {
x.print("EigenTrust α", v, false)
}
func (x *netCfgWriter) InnerRingCandidateFee(v uint64) {
x.print("Inner Ring candidate fee", v, false)
}
func (x *netCfgWriter) WithdrawFee(v uint64) {
x.print("Withdraw fee", v, false)
}

View file

@ -37,7 +37,7 @@ func initNodeInfoCmd() {
nodeInfoCmd.Flags().Bool(nodeInfoJSONFlag, false, "print node info in JSON format")
}
func prettyPrintNodeInfo(cmd *cobra.Command, i *netmap.NodeInfo) {
func prettyPrintNodeInfo(cmd *cobra.Command, i netmap.NodeInfo) {
isJSON, _ := cmd.Flags().GetBool(nodeInfoJSONFlag)
if isJSON {
common.PrettyPrintJSON(cmd, i, "node info")
@ -45,12 +45,24 @@ func prettyPrintNodeInfo(cmd *cobra.Command, i *netmap.NodeInfo) {
}
cmd.Println("key:", hex.EncodeToString(i.PublicKey()))
cmd.Println("state:", i.State())
netmap.IterateAllAddresses(i, func(s string) {
var stateWord string
switch {
default:
stateWord = "<undefined>"
case i.IsOnline():
stateWord = "online"
case i.IsOffline():
stateWord = "offline"
}
cmd.Println("state:", stateWord)
netmap.IterateNetworkEndpoints(i, func(s string) {
cmd.Println("address:", s)
})
for _, attribute := range i.Attributes() {
cmd.Printf("attribute: %s=%s\n", attribute.Key(), attribute.Value())
}
i.IterateAttributes(func(key, value string) {
cmd.Printf("attribute: %s=%s\n", key, value)
})
}

View file

@ -1,69 +1,14 @@
package main
import (
"fmt"
"strconv"
"github.com/nspcc-dev/neofs-node/cmd/neofs-node/config"
nodeconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/node"
"github.com/nspcc-dev/neofs-node/pkg/util/attributes"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
)
const (
// list of default values for well-known attributes
defaultCapacity = 0
defaultPrice = 0
)
func parseAttributes(c *config.Config) []netmap.NodeAttribute {
if nodeconfig.Relay(c) {
return nil
func parseAttributes(c *cfg) {
if nodeconfig.Relay(c.appCfg) {
return
}
stringAttributes := nodeconfig.Attributes(c)
attrs, err := attributes.ParseV2Attributes(stringAttributes, nil)
if err != nil {
fatalOnErr(err)
}
return addWellKnownAttributes(attrs)
}
type wellKnownNodeAttrDesc struct {
explicit bool
defaultVal string
}
func listWellKnownAttrDesc() map[string]wellKnownNodeAttrDesc {
return map[string]wellKnownNodeAttrDesc{
netmap.AttrPrice: {defaultVal: strconv.FormatUint(defaultPrice, 10)},
netmap.AttrCapacity: {defaultVal: strconv.FormatUint(defaultCapacity, 10)},
netmap.AttrUNLOCODE: {explicit: true},
}
}
func addWellKnownAttributes(attrs []netmap.NodeAttribute) []netmap.NodeAttribute {
mWellKnown := listWellKnownAttrDesc()
// check how user defined well-known attributes
for i := range attrs {
delete(mWellKnown, attrs[i].Key())
}
for key, desc := range mWellKnown {
// check if required attribute is set
if desc.explicit {
fatalOnErr(fmt.Errorf("missing explicit value of required node attribute %s", key))
}
// set default value of the attribute
index := len(attrs)
attrs = append(attrs, netmap.NodeAttribute{})
attrs[index].SetKey(key)
attrs[index].SetValue(desc.defaultVal)
}
return attrs
fatalOnErr(attributes.ReadNodeAttributes(&c.cfgNodeInfo.localInfo, nodeconfig.Attributes(c.appCfg)))
}

View file

@ -213,21 +213,21 @@ func newCachedNetmapStorage(s netmap.State, v netmap.Source) netmap.Source {
}
}
func (s *lruNetmapSource) GetNetMap(diff uint64) (*netmapSDK.Netmap, error) {
func (s *lruNetmapSource) GetNetMap(diff uint64) (*netmapSDK.NetMap, error) {
return s.getNetMapByEpoch(s.netState.CurrentEpoch() - diff)
}
func (s *lruNetmapSource) GetNetMapByEpoch(epoch uint64) (*netmapSDK.Netmap, error) {
func (s *lruNetmapSource) GetNetMapByEpoch(epoch uint64) (*netmapSDK.NetMap, error) {
return s.getNetMapByEpoch(epoch)
}
func (s *lruNetmapSource) getNetMapByEpoch(epoch uint64) (*netmapSDK.Netmap, error) {
func (s *lruNetmapSource) getNetMapByEpoch(epoch uint64) (*netmapSDK.NetMap, error) {
val, err := s.cache.get(epoch)
if err != nil {
return nil, err
}
return val.(*netmapSDK.Netmap), nil
return val.(*netmapSDK.NetMap), nil
}
func (s *lruNetmapSource) Epoch() (uint64, error) {

View file

@ -480,15 +480,21 @@ func initObjectPool(cfg *config.Config) (pool cfgObjectRoutines) {
}
func (c *cfg) LocalNodeInfo() (*netmapV2.NodeInfo, error) {
ni := c.cfgNetmap.state.getNodeInfo()
if ni != nil {
return ni.ToV2(), nil
var res netmapV2.NodeInfo
ni, ok := c.cfgNetmap.state.getNodeInfo()
if ok {
ni.WriteToV2(&res)
} else {
c.cfgNodeInfo.localInfo.WriteToV2(&res)
}
return c.cfgNodeInfo.localInfo.ToV2(), nil
return &res, nil
}
// handleLocalNodeInfo rewrites local node info from netmap
// handleLocalNodeInfo rewrites local node info from the NeoFS network map.
// Called with nil when storage node is outside the NeoFS network map
// (before entering the network and after leaving it).
func (c *cfg) handleLocalNodeInfo(ni *netmap.NodeInfo) {
c.cfgNetmap.state.setNodeInfo(ni)
}
@ -496,10 +502,10 @@ func (c *cfg) handleLocalNodeInfo(ni *netmap.NodeInfo) {
// bootstrap sets local node's netmap status to "online".
func (c *cfg) bootstrap() error {
ni := c.cfgNodeInfo.localInfo
ni.SetState(netmap.NodeStateOnline)
ni.SetOnline()
prm := nmClient.AddPeerPrm{}
prm.SetNodeInfo(&ni)
prm.SetNodeInfo(ni)
return c.cfgNetmap.wrapper.AddPeer(prm)
}

View file

@ -329,7 +329,7 @@ type loadPlacementBuilder struct {
cnrSrc containerCore.Source
}
func (l *loadPlacementBuilder) BuildPlacement(epoch uint64, cnr cid.ID) ([]netmap.Nodes, error) {
func (l *loadPlacementBuilder) BuildPlacement(epoch uint64, cnr cid.ID) ([][]netmap.NodeInfo, error) {
cnrNodes, nm, err := l.buildPlacement(epoch, cnr)
if err != nil {
return nil, err
@ -341,7 +341,7 @@ func (l *loadPlacementBuilder) BuildPlacement(epoch uint64, cnr cid.ID) ([]netma
pivotPrefix + strconv.FormatUint(epoch, 10),
)
placement, err := nm.GetPlacementVectors(cnrNodes, pivot)
placement, err := nm.PlacementVectors(cnrNodes, pivot)
if err != nil {
return nil, fmt.Errorf("could not build placement vectors: %w", err)
}
@ -349,12 +349,17 @@ func (l *loadPlacementBuilder) BuildPlacement(epoch uint64, cnr cid.ID) ([]netma
return placement, nil
}
func (l *loadPlacementBuilder) buildPlacement(epoch uint64, idCnr cid.ID) (netmap.ContainerNodes, *netmap.Netmap, error) {
func (l *loadPlacementBuilder) buildPlacement(epoch uint64, idCnr cid.ID) ([][]netmap.NodeInfo, *netmap.NetMap, error) {
cnr, err := l.cnrSrc.Get(idCnr)
if err != nil {
return nil, nil, err
}
policy := cnr.PlacementPolicy()
if policy == nil {
return nil, nil, errors.New("missing placement policy in container")
}
nm, err := l.nmSrc.GetNetMapByEpoch(epoch)
if err != nil {
return nil, nil, fmt.Errorf("could not get network map: %w", err)
@ -363,7 +368,7 @@ func (l *loadPlacementBuilder) buildPlacement(epoch uint64, idCnr cid.ID) (netma
binCnr := make([]byte, sha256.Size)
idCnr.Encode(binCnr)
cnrNodes, err := nm.GetContainerNodes(cnr.PlacementPolicy(), binCnr)
cnrNodes, err := nm.ContainerNodes(*policy, binCnr)
if err != nil {
return nil, nil, fmt.Errorf("could not build container nodes: %w", err)
}
@ -512,9 +517,9 @@ func (l *loadPlacementBuilder) isNodeFromContainerKey(epoch uint64, cnr cid.ID,
return false, err
}
for _, vector := range cnrNodes.Replicas() {
for _, node := range vector {
if bytes.Equal(node.PublicKey(), key) {
for i := range cnrNodes {
for j := range cnrNodes[i] {
if bytes.Equal(cnrNodes[i][j].PublicKey(), key) {
return true, nil
}
}

View file

@ -5,9 +5,7 @@ import (
"errors"
"fmt"
netmapV2 "github.com/nspcc-dev/neofs-api-go/v2/netmap"
netmapGRPC "github.com/nspcc-dev/neofs-api-go/v2/netmap/grpc"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
nodeconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/node"
"github.com/nspcc-dev/neofs-node/pkg/core/netmap"
"github.com/nspcc-dev/neofs-node/pkg/metrics"
@ -20,6 +18,7 @@ import (
netmapService "github.com/nspcc-dev/neofs-node/pkg/services/netmap"
netmapSDK "github.com/nspcc-dev/neofs-sdk-go/netmap"
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
"github.com/nspcc-dev/neofs-sdk-go/version"
"go.uber.org/atomic"
"go.uber.org/zap"
)
@ -53,16 +52,18 @@ func (s *networkState) setCurrentEpoch(v uint64) {
}
func (s *networkState) setNodeInfo(ni *netmapSDK.NodeInfo) {
s.nodeInfo.Store(ni)
ctrlNetSt := control.NetmapStatus_STATUS_UNDEFINED
var ctrlNetSt control.NetmapStatus
if ni != nil {
s.nodeInfo.Store(*ni)
switch ni.State() {
default:
ctrlNetSt = control.NetmapStatus_STATUS_UNDEFINED
case netmapSDK.NodeStateOnline:
ctrlNetSt = control.NetmapStatus_ONLINE
case netmapSDK.NodeStateOffline:
switch {
case ni.IsOnline():
ctrlNetSt = control.NetmapStatus_ONLINE
case ni.IsOffline():
ctrlNetSt = control.NetmapStatus_OFFLINE
}
} else {
ctrlNetSt = control.NetmapStatus_OFFLINE
}
@ -73,27 +74,48 @@ func (s *networkState) controlNetmapStatus() control.NetmapStatus {
return s.controlNetStatus.Load().(control.NetmapStatus)
}
func (s *networkState) getNodeInfo() *netmapSDK.NodeInfo {
return s.nodeInfo.Load().(*netmapSDK.NodeInfo)
func (s *networkState) getNodeInfo() (res netmapSDK.NodeInfo, ok bool) {
v := s.nodeInfo.Load()
if v != nil {
res, ok = v.(netmapSDK.NodeInfo)
if !ok {
panic(fmt.Sprintf("unexpected value in atomic node info state: %T", v))
}
}
return
}
func nodeKeyFromNetmap(c *cfg) []byte {
return c.cfgNetmap.state.getNodeInfo().PublicKey()
ni, ok := c.cfgNetmap.state.getNodeInfo()
if ok {
return ni.PublicKey()
}
return nil
}
func (c *cfg) iterateNetworkAddresses(f func(string) bool) {
c.cfgNetmap.state.getNodeInfo().IterateAddresses(f)
ni, ok := c.cfgNetmap.state.getNodeInfo()
if ok {
ni.IterateNetworkEndpoints(f)
}
}
func (c *cfg) addressNum() int {
return c.cfgNetmap.state.getNodeInfo().NumberOfAddresses()
ni, ok := c.cfgNetmap.state.getNodeInfo()
if ok {
return ni.NumberOfNetworkEndpoints()
}
return 0
}
func initNetmapService(c *cfg) {
network.WriteToNodeInfo(c.localAddr, &c.cfgNodeInfo.localInfo)
c.cfgNodeInfo.localInfo.SetPublicKey(c.key.PublicKey().Bytes())
c.cfgNodeInfo.localInfo.SetAttributes(parseAttributes(c.appCfg)...)
c.cfgNodeInfo.localInfo.SetState(netmapSDK.NodeStateOffline)
parseAttributes(c)
c.cfgNodeInfo.localInfo.SetOffline()
readSubnetCfg(c)
@ -111,10 +133,10 @@ func initNetmapService(c *cfg) {
c,
c.apiVersion,
&netInfo{
netState: c.cfgNetmap.state,
magic: c.cfgMorph.client,
netCfg: c.cfgNetmap.wrapper.IterateConfigParameters,
msPerBlockRdr: c.cfgMorph.client.MsPerBlock,
netState: c.cfgNetmap.state,
magic: c.cfgMorph.client,
morphClientNetMap: c.cfgNetmap.wrapper,
msPerBlockRdr: c.cfgMorph.client.MsPerBlock,
},
),
c.respSvc,
@ -236,14 +258,25 @@ func initNetmapState(c *cfg) {
ni, err := c.netmapLocalNodeState(epoch)
fatalOnErrDetails("could not init network state", err)
stateWord := "undefined"
if ni != nil {
switch {
case ni.IsOnline():
stateWord = "online"
case ni.IsOffline():
stateWord = "offline"
}
}
c.log.Info("initial network state",
zap.Uint64("epoch", epoch),
zap.Stringer("state", ni.State()),
zap.String("state", stateWord),
)
c.cfgNetmap.state.setCurrentEpoch(epoch)
c.cfgNetmap.startEpoch = epoch
c.cfgNetmap.state.setNodeInfo(ni)
c.handleLocalNodeInfo(ni)
}
func (c *cfg) netmapLocalNodeState(epoch uint64) (*netmapSDK.NodeInfo, error) {
@ -253,17 +286,14 @@ func (c *cfg) netmapLocalNodeState(epoch uint64) (*netmapSDK.NodeInfo, error) {
return nil, err
}
return c.localNodeInfoFromNetmap(nm), nil
}
func (c *cfg) localNodeInfoFromNetmap(nm *netmapSDK.Netmap) *netmapSDK.NodeInfo {
for _, n := range nm.Nodes {
if bytes.Equal(n.PublicKey(), c.key.PublicKey().Bytes()) {
return n.NodeInfo
nmNodes := nm.Nodes()
for i := range nmNodes {
if bytes.Equal(nmNodes[i].PublicKey(), c.key.PublicKey().Bytes()) {
return &nmNodes[i], nil
}
}
return nil
return nil, nil
}
// addNewEpochNotificationHandler adds handler that will be executed synchronously
@ -309,18 +339,11 @@ func (c *cfg) SetNetmapStatus(st control.NetmapStatus) error {
return c.bootstrap()
}
var apiState netmapSDK.NodeState
if st == control.NetmapStatus_OFFLINE {
apiState = netmapSDK.NodeStateOffline
}
c.cfgNetmap.reBoostrapTurnedOff.Store(true)
prm := nmClient.UpdatePeerPrm{}
prm.SetKey(c.key.PublicKey().Bytes())
prm.SetState(apiState)
return c.cfgNetmap.wrapper.UpdatePeerState(prm)
}
@ -332,50 +355,49 @@ type netInfo struct {
MagicNumber() (uint64, error)
}
netCfg func(func(key, value []byte) error) error
morphClientNetMap *nmClient.Client
msPerBlockRdr func() (int64, error)
}
func (n *netInfo) Dump(ver *refs.Version) (*netmapV2.NetworkInfo, error) {
func (n *netInfo) Dump(ver version.Version) (*netmapSDK.NetworkInfo, error) {
magic, err := n.magic.MagicNumber()
if err != nil {
return nil, err
}
ni := new(netmapV2.NetworkInfo)
var ni netmapSDK.NetworkInfo
ni.SetCurrentEpoch(n.netState.CurrentEpoch())
ni.SetMagicNumber(magic)
if mjr := ver.GetMajor(); mjr > 2 || mjr == 2 && ver.GetMinor() > 9 {
netInfoMorph, err := n.morphClientNetMap.ReadNetworkConfiguration()
if err != nil {
return nil, fmt.Errorf("read network configuration using netmap contract client: %w", err)
}
if mjr := ver.Major(); mjr > 2 || mjr == 2 && ver.Minor() > 9 {
msPerBlock, err := n.msPerBlockRdr()
if err != nil {
return nil, fmt.Errorf("ms per block: %w", err)
}
var (
ps []netmapV2.NetworkParameter
netCfg netmapV2.NetworkConfig
)
if err := n.netCfg(func(key, value []byte) error {
var p netmapV2.NetworkParameter
p.SetKey(key)
p.SetValue(value)
ps = append(ps, p)
return nil
}); err != nil {
return nil, fmt.Errorf("network config: %w", err)
}
netCfg.SetParameters(ps...)
ni.SetNetworkConfig(&netCfg)
ni.SetMsPerBlock(msPerBlock)
ni.SetMaxObjectSize(netInfoMorph.MaxObjectSize)
ni.SetStoragePrice(netInfoMorph.StoragePrice)
ni.SetAuditFee(netInfoMorph.AuditFee)
ni.SetEpochDuration(netInfoMorph.EpochDuration)
ni.SetContainerFee(netInfoMorph.ContainerFee)
ni.SetNamedContainerFee(netInfoMorph.ContainerAliasFee)
ni.SetNumberOfEigenTrustIterations(netInfoMorph.EigenTrustIterations)
ni.SetEigenTrustAlpha(netInfoMorph.EigenTrustAlpha)
ni.SetIRCandidateFee(netInfoMorph.IRCandidateFee)
ni.SetWithdrawalFee(netInfoMorph.WithdrawalFee)
for i := range netInfoMorph.Raw {
ni.SetRawNetworkParameter(netInfoMorph.Raw[i].Name, netInfoMorph.Raw[i].Value)
}
}
return ni, nil
return &ni, nil
}

View file

@ -530,10 +530,12 @@ func (c *reputationClientConstructor) Get(info coreclient.NodeInfo) (coreclient.
if err == nil {
key := info.PublicKey()
for i := range nm.Nodes {
if bytes.Equal(nm.Nodes[i].PublicKey(), key) {
nmNodes := nm.Nodes()
for i := range nmNodes {
if bytes.Equal(nmNodes[i].PublicKey(), key) {
prm := truststorage.UpdatePrm{}
prm.SetPeer(reputation.PeerIDFromBytes(nm.Nodes[i].PublicKey()))
prm.SetPeer(reputation.PeerIDFromBytes(nmNodes[i].PublicKey()))
return &reputationClient{
MultiAddressClient: cl.(coreclient.MultiAddressClient),

View file

@ -26,7 +26,7 @@ func (i InitialTrustSource) InitialTrust(reputation.PeerID) (reputation.TrustVal
return reputation.TrustZero, fmt.Errorf("failed to get NetMap: %w", err)
}
nodeCount := reputation.TrustValueFromFloat64(float64(len(nm.Nodes)))
nodeCount := reputation.TrustValueFromFloat64(float64(len(nm.Nodes())))
if nodeCount == 0 {
return reputation.TrustZero, ErrEmptyNetMap
}

View file

@ -66,14 +66,15 @@ func (it *TrustIterator) Iterate(h reputation.TrustHandler) error {
// find out if local node is presented in netmap
localIndex := -1
for i := range nm.Nodes {
if bytes.Equal(nm.Nodes[i].PublicKey(), it.storage.LocalKey) {
nmNodes := nm.Nodes()
for i := range nmNodes {
if bytes.Equal(nmNodes[i].PublicKey(), it.storage.LocalKey) {
localIndex = i
break
}
}
ln := len(nm.Nodes)
ln := len(nmNodes)
if localIndex >= 0 && ln > 0 {
ln--
}
@ -81,13 +82,13 @@ func (it *TrustIterator) Iterate(h reputation.TrustHandler) error {
// calculate Pj http://ilpubs.stanford.edu:8090/562/1/2002-56.pdf Chapter 4.5.
p := reputation.TrustOne.Div(reputation.TrustValueFromInt(ln))
for i := range nm.Nodes {
for i := range nmNodes {
if i == localIndex {
continue
}
trust := reputation.Trust{}
trust.SetPeer(reputation.PeerIDFromBytes(nm.Nodes[i].PublicKey()))
trust.SetPeer(reputation.PeerIDFromBytes(nmNodes[i].PublicKey()))
trust.SetValue(p)
trust.SetTrustingPeer(reputation.PeerIDFromBytes(it.storage.LocalKey))

2
go.mod
View file

@ -19,7 +19,7 @@ require (
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220601120906-3bec6657f5c5 // indirect
github.com/nspcc-dev/neofs-api-go/v2 v2.12.2
github.com/nspcc-dev/neofs-contract v0.15.1
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.4.0.20220615085207-eb3b99081235
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.4.0.20220616082321-e986f4780721
github.com/nspcc-dev/tzhash v1.5.2
github.com/panjf2000/ants/v2 v2.4.0
github.com/paulmach/orb v0.2.2

BIN
go.sum

Binary file not shown.

View file

@ -6,7 +6,7 @@ import (
"github.com/google/uuid"
"github.com/nspcc-dev/neofs-node/pkg/util/test"
"github.com/nspcc-dev/neofs-sdk-go/container"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/nspcc-dev/neofs-sdk-go/version"
"github.com/stretchr/testify/require"
@ -17,8 +17,8 @@ func TestCheckFormat(t *testing.T) {
require.Error(t, CheckFormat(c))
policy := netmap.NewPlacementPolicy()
c.SetPlacementPolicy(policy)
policy := netmaptest.PlacementPolicy()
c.SetPlacementPolicy(&policy)
require.Error(t, CheckFormat(c))

31
pkg/core/netmap/nodes.go Normal file
View file

@ -0,0 +1,31 @@
package netmap
import "github.com/nspcc-dev/neofs-sdk-go/netmap"
// Node is a named type of netmap.NodeInfo which provides interface needed
// in the current repository. Node is expected to be used everywhere instead
// of direct usage of netmap.NodeInfo, so it represents a type mediator.
type Node netmap.NodeInfo
// PublicKey returns public key bound to the storage node.
//
// Return value MUST NOT be mutated, make a copy first.
func (x Node) PublicKey() []byte {
return (netmap.NodeInfo)(x).PublicKey()
}
// IterateAddresses iterates over all announced network addresses
// and passes them into f. Handler MUST NOT be nil.
func (x Node) IterateAddresses(f func(string) bool) {
(netmap.NodeInfo)(x).IterateNetworkEndpoints(f)
}
// NumberOfAddresses returns number of announced network addresses.
func (x Node) NumberOfAddresses() int {
return (netmap.NodeInfo)(x).NumberOfNetworkEndpoints()
}
// Nodes is a named type of []netmap.NodeInfo which provides interface needed
// in the current repository. Nodes is expected to be used everywhere instead
// of direct usage of []netmap.NodeInfo, so it represents a type mediator.
type Nodes []netmap.NodeInfo

View file

@ -16,7 +16,7 @@ type Source interface {
//
// Implementations must not retain the network map pointer and modify
// the network map through it.
GetNetMap(diff uint64) (*netmap.Netmap, error)
GetNetMap(diff uint64) (*netmap.NetMap, error)
// GetNetMapByEpoch reads network map by the epoch number from the storage.
// It returns the pointer to the requested network map and any error encountered.
@ -25,7 +25,7 @@ type Source interface {
//
// Implementations must not retain the network map pointer and modify
// the network map through it.
GetNetMapByEpoch(epoch uint64) (*netmap.Netmap, error)
GetNetMapByEpoch(epoch uint64) (*netmap.NetMap, error)
// Epoch reads the current epoch from the storage.
// It returns thw number of the current epoch and any error encountered.
@ -35,11 +35,11 @@ type Source interface {
}
// GetLatestNetworkMap requests and returns the latest network map from the storage.
func GetLatestNetworkMap(src Source) (*netmap.Netmap, error) {
func GetLatestNetworkMap(src Source) (*netmap.NetMap, error) {
return src.GetNetMap(0)
}
// GetPreviousNetworkMap requests and returns previous from the latest network map from the storage.
func GetPreviousNetworkMap(src Source) (*netmap.Netmap, error) {
func GetPreviousNetworkMap(src Source) (*netmap.NetMap, error) {
return src.GetNetMap(1)
}

View file

@ -48,7 +48,9 @@ func (ap *Processor) processEmit() {
return
}
ln := len(networkMap.Nodes)
nmNodes := networkMap.Nodes()
ln := len(nmNodes)
if ln == 0 {
ap.log.Debug("empty network map, do not emit gas")
@ -57,8 +59,8 @@ func (ap *Processor) processEmit() {
gasPerNode := fixedn.Fixed8(ap.storageEmission / uint64(ln))
for i := range networkMap.Nodes {
keyBytes := networkMap.Nodes[i].PublicKey()
for i := range nmNodes {
keyBytes := nmNodes[i].PublicKey()
key, err := keys.NewPublicKeyFromBytes(keyBytes, elliptic.P256())
if err != nil {

View file

@ -6,11 +6,12 @@ import (
"encoding/hex"
clientcore "github.com/nspcc-dev/neofs-node/pkg/core/client"
netmapcore "github.com/nspcc-dev/neofs-node/pkg/core/netmap"
cntClient "github.com/nspcc-dev/neofs-node/pkg/morph/client/container"
"github.com/nspcc-dev/neofs-node/pkg/services/audit"
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement"
"github.com/nspcc-dev/neofs-node/pkg/util/rand"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"go.uber.org/zap"
)
@ -59,10 +60,19 @@ func (ap *Processor) processStartAudit(epoch uint64) {
continue
}
policy := cnr.PlacementPolicy()
if policy == nil {
log.Error("missing placement policy in container, ignore",
zap.Stringer("cid", containers[i]),
)
continue
}
containers[i].Encode(pivot)
// find all container nodes for current epoch
nodes, err := nm.GetContainerNodes(cnr.PlacementPolicy(), pivot)
nodes, err := nm.ContainerNodes(*policy, pivot)
if err != nil {
log.Info("can't build placement for container, ignore",
zap.Stringer("cid", containers[i]),
@ -71,7 +81,7 @@ func (ap *Processor) processStartAudit(epoch uint64) {
continue
}
n := nodes.Flatten()
n := placement.FlattenNodes(nodes)
// shuffle nodes to ask a random one
rand.Shuffle(len(n), func(i, j int) {
@ -110,7 +120,7 @@ func (ap *Processor) processStartAudit(epoch uint64) {
}
}
func (ap *Processor) findStorageGroups(cnr cid.ID, shuffled netmap.Nodes) []oid.ID {
func (ap *Processor) findStorageGroups(cnr cid.ID, shuffled netmapcore.Nodes) []oid.ID {
var sg []oid.ID
ln := len(shuffled)
@ -130,7 +140,7 @@ func (ap *Processor) findStorageGroups(cnr cid.ID, shuffled netmap.Nodes) []oid.
zap.Int("total_tries", ln),
)
err := clientcore.NodeInfoFromRawNetmapElement(&info, shuffled[i])
err := clientcore.NodeInfoFromRawNetmapElement(&info, netmapcore.Node(shuffled[i]))
if err != nil {
log.Warn("parse client node info", zap.String("error", err.Error()))

View file

@ -240,10 +240,15 @@ func checkSubnet(subCli *morphsubnet.Client, cnr *containerSDK.Container) error
return errors.New("missing owner")
}
policy := cnr.PlacementPolicy()
if policy == nil {
return errors.New("missing placement policy")
}
prm := morphsubnet.UserAllowedPrm{}
subID := cnr.PlacementPolicy().SubnetID()
if subID == nil || subnetid.IsZero(*subID) {
subID := policy.Subnet()
if subnetid.IsZero(subID) {
return nil
}
@ -256,7 +261,7 @@ func checkSubnet(subCli *morphsubnet.Client, cnr *containerSDK.Container) error
}
if !res.Allowed() {
return fmt.Errorf("user is not allowed to create containers in %s subnetwork", subID)
return fmt.Errorf("user is not allowed to create containers in %v subnetwork", subID)
}
return nil

View file

@ -3,7 +3,6 @@ package netmap
import (
"bytes"
"encoding/hex"
"fmt"
"sync"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
@ -39,20 +38,19 @@ func newCleanupTable(enabled bool, threshold uint64) cleanupTable {
}
// Update cleanup table based on on-chain information about netmap.
func (c *cleanupTable) update(snapshot *netmap.Netmap, now uint64) {
func (c *cleanupTable) update(snapshot netmap.NetMap, now uint64) {
c.Lock()
defer c.Unlock()
nmNodes := snapshot.Nodes()
// replacing map is less memory efficient but faster
newMap := make(map[string]epochStampWithNodeInfo, len(snapshot.Nodes))
newMap := make(map[string]epochStampWithNodeInfo, len(nmNodes))
for i := range snapshot.Nodes {
binNodeInfo, err := snapshot.Nodes[i].Marshal()
if err != nil {
panic(fmt.Errorf("could not marshal node info: %w", err)) // seems better than ignore
}
for i := range nmNodes {
binNodeInfo := nmNodes[i].Marshal()
keyString := hex.EncodeToString(snapshot.Nodes[i].PublicKey())
keyString := hex.EncodeToString(nmNodes[i].PublicKey())
access, ok := c.lastAccess[keyString]
if ok {

View file

@ -22,14 +22,13 @@ func TestCleanupTable(t *testing.T) {
newNodeInfo(genKey(t).PublicKey()),
}
networkMap, err := netmap.NewNetmap(netmap.NodesFromInfo(infos))
require.NoError(t, err)
var networkMap netmap.NetMap
networkMap.SetNodes(infos)
mapInfos := make(map[string][]byte)
for i := range infos {
binNodeInfo, err := infos[i].Marshal()
require.NoError(t, err)
binNodeInfo := infos[i].Marshal()
mapInfos[hex.EncodeToString(infos[i].PublicKey())] = binNodeInfo
}

View file

@ -30,24 +30,12 @@ var errMissingRequiredAttr = errors.New("missing required attribute in DB record
//
// UN-LOCODE attribute remains untouched.
func (v *Validator) VerifyAndUpdate(n *netmap.NodeInfo) error {
mAttr := uniqueAttributes(n.Attributes())
// check if the derived attributes are presented
for attrKey := range v.mAttr {
if _, ok := mAttr[attrKey]; ok {
return fmt.Errorf("attribute derived from %s is presented: %s",
netmap.AttrUNLOCODE,
attrKey,
)
}
}
attrLocode, ok := mAttr[netmap.AttrUNLOCODE]
if !ok {
attrLocode := n.LOCODE()
if attrLocode == "" {
return nil
}
lc, err := locode.FromString(attrLocode.Value())
lc, err := locode.FromString(attrLocode)
if err != nil {
return fmt.Errorf("invalid locode value: %w", err)
}
@ -57,41 +45,48 @@ func (v *Validator) VerifyAndUpdate(n *netmap.NodeInfo) error {
return fmt.Errorf("could not get locode record from DB: %w", err)
}
for attrKey, attrDesc := range v.mAttr {
attrVal := attrDesc.converter(record)
if attrVal == "" {
if !attrDesc.optional {
return errMissingRequiredAttr
}
continue
}
var a netmap.NodeAttribute
a.SetKey(attrKey)
a.SetValue(attrVal)
mAttr[attrKey] = a
countryCode := record.CountryCode()
if countryCode == nil {
return errMissingRequiredAttr
}
as := n.Attributes()
as = as[:0]
for _, attr := range mAttr {
as = append(as, attr)
strCountryCode := countryCode.String()
if strCountryCode == "" {
return errMissingRequiredAttr
}
n.SetAttributes(as...)
countryName := record.CountryName()
if countryName == "" {
return errMissingRequiredAttr
}
locationName := record.LocationName()
if locationName == "" {
return errMissingRequiredAttr
}
continent := record.Continent()
if continent == nil {
return errMissingRequiredAttr
}
continentName := continent.String()
if continentName == "" {
return errMissingRequiredAttr
}
n.SetCountryCode(strCountryCode)
n.SetCountryName(countryName)
n.SetLocationName(locationName)
n.SetContinentName(continentName)
if subDivCode := record.SubDivCode(); subDivCode != "" {
n.SetSubdivisionCode(subDivCode)
}
if subDivName := record.SubDivName(); subDivName != "" {
n.SetSubdivisionName(subDivName)
}
return nil
}
func uniqueAttributes(as []netmap.NodeAttribute) map[string]netmap.NodeAttribute {
mAttr := make(map[string]netmap.NodeAttribute, len(as))
for _, attr := range as {
mAttr[attr.Key()] = attr
}
return mAttr
}

View file

@ -34,40 +34,21 @@ func (x db) Get(lc *locodestd.LOCODE) (locode.Record, error) {
return r, nil
}
func addAttrKV(n *netmap.NodeInfo, key, val string) {
var a netmap.NodeAttribute
a.SetKey(key)
a.SetValue(val)
n.SetAttributes(append(n.Attributes(), a)...)
}
func addLocodeAttrValue(n *netmap.NodeInfo, val string) {
addAttrKV(n, netmap.AttrUNLOCODE, val)
n.SetLOCODE(val)
}
func addLocodeAttr(n *netmap.NodeInfo, lc locodestd.LOCODE) {
addLocodeAttrValue(n, fmt.Sprintf("%s %s", lc[0], lc[1]))
n.SetLOCODE(fmt.Sprintf("%s %s", lc[0], lc[1]))
}
func nodeInfoWithSomeAttrs() *netmap.NodeInfo {
n := netmap.NewNodeInfo()
var n netmap.NodeInfo
addAttrKV(n, "key1", "val1")
addAttrKV(n, "key2", "val2")
n.SetAttribute("key1", "val1")
n.SetAttribute("key2", "val2")
return n
}
func containsAttr(n *netmap.NodeInfo, key, val string) bool {
for _, a := range n.Attributes() {
if a.Key() == key && a.Value() == val {
return true
}
}
return false
return &n
}
func TestValidator_VerifyAndUpdate(t *testing.T) {
@ -108,41 +89,11 @@ func TestValidator_VerifyAndUpdate(t *testing.T) {
validator := locode.New(p)
t.Run("w/ derived attributes", func(t *testing.T) {
fn := func(withLocode bool) {
for _, derivedAttr := range []string{
netmap.AttrCountryCode,
netmap.AttrCountry,
netmap.AttrLocation,
netmap.AttrSubDivCode,
netmap.AttrSubDiv,
netmap.AttrContinent,
} {
n := nodeInfoWithSomeAttrs()
addAttrKV(n, derivedAttr, "some value")
if withLocode {
addLocodeAttr(n, r.LOCODE)
}
err := validator.VerifyAndUpdate(n)
require.Error(t, err)
}
}
fn(true)
fn(false)
})
t.Run("w/o locode", func(t *testing.T) {
n := nodeInfoWithSomeAttrs()
attrs := n.Attributes()
err := validator.VerifyAndUpdate(n)
require.NoError(t, err)
require.Equal(t, attrs, n.Attributes())
})
t.Run("w/ locode", func(t *testing.T) {
@ -168,22 +119,14 @@ func TestValidator_VerifyAndUpdate(t *testing.T) {
addLocodeAttr(n, r.LOCODE)
attrs := n.Attributes()
err := validator.VerifyAndUpdate(n)
require.NoError(t, err)
outAttrs := n.Attributes()
for _, a := range attrs {
require.Contains(t, outAttrs, a)
}
require.True(t, containsAttr(n, netmap.AttrCountryCode, rec.CountryCode().String()))
require.True(t, containsAttr(n, netmap.AttrCountry, rec.CountryName()))
require.True(t, containsAttr(n, netmap.AttrLocation, rec.LocationName()))
require.True(t, containsAttr(n, netmap.AttrSubDivCode, rec.SubDivCode()))
require.True(t, containsAttr(n, netmap.AttrSubDiv, rec.SubDivName()))
require.True(t, containsAttr(n, netmap.AttrContinent, rec.Continent().String()))
require.Equal(t, rec.CountryCode().String(), n.Attribute("CountryCode"))
require.Equal(t, rec.CountryName(), n.Attribute("Country"))
require.Equal(t, rec.LocationName(), n.Attribute("Location"))
require.Equal(t, rec.SubDivCode(), n.Attribute("SubDivCode"))
require.Equal(t, rec.SubDivName(), n.Attribute("SubDiv"))
require.Equal(t, rec.Continent().String(), n.Attribute("Continent"))
})
}

View file

@ -1,30 +0,0 @@
package locode
type attrDescriptor struct {
optional bool
converter func(Record) string
}
func countryCodeValue(r Record) (val string) {
return r.CountryCode().String()
}
func countryValue(r Record) string {
return r.CountryName()
}
func locationValue(r Record) string {
return r.LocationName()
}
func subDivCodeValue(r Record) string {
return r.SubDivCode()
}
func subDivValue(r Record) string {
return r.SubDivName()
}
func continentValue(r Record) string {
return r.Continent().String()
}

View file

@ -1,9 +1,5 @@
package locode
import (
"github.com/nspcc-dev/neofs-sdk-go/netmap"
)
// Prm groups the required parameters of the Validator's constructor.
//
// All values must comply with the requirements imposed on them.
@ -26,8 +22,6 @@ type Prm struct {
// the Validator is immediately ready to work through API.
type Validator struct {
db DB
mAttr map[string]attrDescriptor
}
// New creates a new instance of the Validator.
@ -39,16 +33,5 @@ type Validator struct {
func New(prm Prm) *Validator {
return &Validator{
db: prm.DB,
mAttr: map[string]attrDescriptor{
netmap.AttrCountryCode: {converter: countryCodeValue},
netmap.AttrCountry: {converter: countryValue},
netmap.AttrLocation: {converter: locationValue},
netmap.AttrSubDivCode: {converter: subDivCodeValue, optional: true},
netmap.AttrSubDiv: {converter: subDivValue, optional: true},
netmap.AttrContinent: {converter: continentValue},
},
}
}

View file

@ -9,7 +9,7 @@ import (
// VerifyAndUpdate calls network.VerifyAddress.
func (v *Validator) VerifyAndUpdate(n *netmap.NodeInfo) error {
err := network.VerifyMultiAddress(n)
err := network.VerifyMultiAddress(*n)
if err != nil {
return fmt.Errorf("could not verify multiaddress: %w", err)
}

View file

@ -2,8 +2,8 @@ package netmap
import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
v2netmap "github.com/nspcc-dev/neofs-api-go/v2/netmap"
netmapclient "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"go.uber.org/zap"
)
@ -34,7 +34,6 @@ func (np *Processor) processNetmapCleanupTick(ev netmapCleanupTick) {
prm := netmapclient.UpdatePeerPrm{}
prm.SetKey(key.Bytes())
prm.SetState(netmap.NodeStateOffline)
prm.SetHash(ev.TxHash())
err = np.netmapClient.UpdatePeerState(prm)
@ -45,7 +44,7 @@ func (np *Processor) processNetmapCleanupTick(ev netmapCleanupTick) {
uint32(ev.epoch),
nil,
methodUpdateStateNotary,
int64(netmap.NodeStateOffline.ToV2()), key.Bytes(),
int64(v2netmap.Offline), key.Bytes(),
)
}
if err != nil {

View file

@ -60,7 +60,7 @@ func (np *Processor) processNewEpoch(ev netmapEvent.NewEpoch) {
}
}
np.netmapSnapshot.update(networkMap, epoch)
np.netmapSnapshot.update(*networkMap, epoch)
np.handleCleanupTick(netmapCleanupTick{epoch: epoch, txHash: ev.TxHash()})
np.handleNewAudit(audit.NewAuditStartEvent(epoch))
np.handleAuditSettlements(settlement.NewAuditEvent(epoch))

View file

@ -3,8 +3,6 @@ package netmap
import (
"bytes"
"encoding/hex"
"sort"
"strings"
netmapclient "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap"
netmapEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/netmap"
@ -36,7 +34,7 @@ func (np *Processor) processAddPeer(ev netmapEvent.AddPeer) {
}
// unmarshal node info
nodeInfo := netmap.NewNodeInfo()
var nodeInfo netmap.NodeInfo
if err := nodeInfo.Unmarshal(ev.Node()); err != nil {
// it will be nice to have tx id at event structure to log it
np.log.Warn("can't parse network map candidate")
@ -44,7 +42,7 @@ func (np *Processor) processAddPeer(ev netmapEvent.AddPeer) {
}
// validate and update node info
err := np.nodeValidator.VerifyAndUpdate(nodeInfo)
err := np.nodeValidator.VerifyAndUpdate(&nodeInfo)
if err != nil {
np.log.Warn("could not verify and update information about network map candidate",
zap.String("error", err.Error()),
@ -54,28 +52,10 @@ func (np *Processor) processAddPeer(ev netmapEvent.AddPeer) {
}
// sort attributes to make it consistent
a := nodeInfo.Attributes()
sort.Slice(a, func(i, j int) bool {
switch strings.Compare(a[i].Key(), a[j].Key()) {
case -1:
return true
case 1:
return false
default:
return a[i].Value() < a[j].Value()
}
})
nodeInfo.SetAttributes(a...)
nodeInfo.SortAttributes()
// marshal updated node info structure
nodeInfoBinary, err := nodeInfo.Marshal()
if err != nil {
np.log.Warn("could not marshal updated network map candidate",
zap.String("error", err.Error()),
)
return
}
nodeInfoBinary := nodeInfo.Marshal()
keyString := hex.EncodeToString(nodeInfo.PublicKey())
@ -121,15 +101,6 @@ func (np *Processor) processUpdatePeer(ev netmapEvent.UpdatePeer) {
return
}
// better use unified enum from neofs-api-go/v2/netmap package
if ev.Status() != netmap.NodeStateOffline {
np.log.Warn("node proposes unknown state",
zap.String("key", hex.EncodeToString(ev.PublicKey().Bytes())),
zap.Stringer("status", ev.Status()),
)
return
}
// flag node to remove from local view, so it can be re-bootstrapped
// again before new epoch will tick
np.netmapSnapshot.flag(hex.EncodeToString(ev.PublicKey().Bytes()))
@ -141,7 +112,9 @@ func (np *Processor) processUpdatePeer(ev netmapEvent.UpdatePeer) {
} else {
prm := netmapclient.UpdatePeerPrm{}
prm.SetState(ev.Status())
if ev.Online() {
prm.SetOnline()
}
prm.SetKey(ev.PublicKey().Bytes())
err = np.netmapClient.UpdatePeerState(prm)
@ -181,12 +154,14 @@ func (np *Processor) processRemoveSubnetNode(ev subnetEvent.RemoveNode) {
return
}
for _, node := range candidates.Nodes {
if !bytes.Equal(node.NodeInfo.PublicKey(), ev.Node()) {
candidateNodes := candidates.Nodes()
for i := range candidateNodes {
if !bytes.Equal(candidateNodes[i].PublicKey(), ev.Node()) {
continue
}
err = node.IterateSubnets(func(subNetID subnetid.ID) error {
err = candidateNodes[i].IterateSubnets(func(subNetID subnetid.ID) error {
if subNetID.Equals(subnetToRemoveFrom) {
return netmap.ErrRemoveSubnet
}
@ -199,7 +174,6 @@ func (np *Processor) processRemoveSubnetNode(ev subnetEvent.RemoveNode) {
prm := netmapclient.UpdatePeerPrm{}
prm.SetKey(ev.Node())
prm.SetState(netmap.NodeStateOffline)
prm.SetHash(ev.TxHash())
err = np.netmapClient.UpdatePeerState(prm)
@ -209,7 +183,7 @@ func (np *Processor) processRemoveSubnetNode(ev subnetEvent.RemoveNode) {
}
} else {
prm := netmapclient.AddPeerPrm{}
prm.SetNodeInfo(node.NodeInfo)
prm.SetNodeInfo(candidateNodes[i])
prm.SetHash(ev.TxHash())
err = np.netmapClient.AddPeer(prm)

View file

@ -7,6 +7,7 @@ import (
"time"
clientcore "github.com/nspcc-dev/neofs-node/pkg/core/client"
netmapcore "github.com/nspcc-dev/neofs-node/pkg/core/netmap"
neofsapiclient "github.com/nspcc-dev/neofs-node/pkg/innerring/internal/client"
auditproc "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/audit"
"github.com/nspcc-dev/neofs-node/pkg/network/cache"
@ -69,7 +70,7 @@ func (c *ClientCache) GetSG(task *audit.Task, id oid.ID) (*storagegroup.StorageG
return c.getSG(task.AuditContext(), sgAddress, task.NetworkMap(), task.ContainerNodes())
}
func (c *ClientCache) getSG(ctx context.Context, addr oid.Address, nm *netmap.Netmap, cn netmap.ContainerNodes) (*storagegroup.StorageGroup, error) {
func (c *ClientCache) getSG(ctx context.Context, addr oid.Address, nm *netmap.NetMap, cn [][]netmap.NodeInfo) (*storagegroup.StorageGroup, error) {
obj := addr.Object()
nodes, err := placement.BuildObjectPlacement(nm, cn, &obj)
@ -80,7 +81,7 @@ func (c *ClientCache) getSG(ctx context.Context, addr oid.Address, nm *netmap.Ne
var info clientcore.NodeInfo
for _, node := range placement.FlattenNodes(nodes) {
err := clientcore.NodeInfoFromRawNetmapElement(&info, node)
err := clientcore.NodeInfoFromRawNetmapElement(&info, netmapcore.Node(node))
if err != nil {
return nil, fmt.Errorf("parse client node info: %w", err)
}
@ -124,14 +125,14 @@ func (c *ClientCache) getSG(ctx context.Context, addr oid.Address, nm *netmap.Ne
}
// GetHeader requests node from the container under audit to return object header by id.
func (c *ClientCache) GetHeader(task *audit.Task, node *netmap.Node, id oid.ID, relay bool) (*object.Object, error) {
func (c *ClientCache) GetHeader(task *audit.Task, node netmap.NodeInfo, id oid.ID, relay bool) (*object.Object, error) {
var objAddress oid.Address
objAddress.SetContainer(task.ContainerID())
objAddress.SetObject(id)
var info clientcore.NodeInfo
err := clientcore.NodeInfoFromRawNetmapElement(&info, node)
err := clientcore.NodeInfoFromRawNetmapElement(&info, netmapcore.Node(node))
if err != nil {
return nil, fmt.Errorf("parse client node info: %w", err)
}
@ -162,14 +163,14 @@ func (c *ClientCache) GetHeader(task *audit.Task, node *netmap.Node, id oid.ID,
// GetRangeHash requests node from the container under audit to return Tillich-Zemor hash of the
// payload range of the object with specified identifier.
func (c *ClientCache) GetRangeHash(task *audit.Task, node *netmap.Node, id oid.ID, rng *object.Range) ([]byte, error) {
func (c *ClientCache) GetRangeHash(task *audit.Task, node netmap.NodeInfo, id oid.ID, rng *object.Range) ([]byte, error) {
var objAddress oid.Address
objAddress.SetContainer(task.ContainerID())
objAddress.SetObject(id)
var info clientcore.NodeInfo
err := clientcore.NodeInfoFromRawNetmapElement(&info, node)
err := clientcore.NodeInfoFromRawNetmapElement(&info, netmapcore.Node(node))
if err != nil {
return nil, fmt.Errorf("parse client node info: %w", err)
}

View file

@ -6,6 +6,7 @@ import (
"crypto/elliptic"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"math/big"
@ -18,6 +19,7 @@ import (
auditClient "github.com/nspcc-dev/neofs-node/pkg/morph/client/audit"
balanceClient "github.com/nspcc-dev/neofs-node/pkg/morph/client/balance"
containerClient "github.com/nspcc-dev/neofs-node/pkg/morph/client/container"
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement"
"github.com/nspcc-dev/neofs-node/pkg/util/logger"
auditAPI "github.com/nspcc-dev/neofs-sdk-go/audit"
containerAPI "github.com/nspcc-dev/neofs-sdk-go/container"
@ -75,7 +77,7 @@ type auditSettlementCalculator audit.Calculator
type containerWrapper containerAPI.Container
type nodeInfoWrapper struct {
ni *netmapAPI.Node
ni netmapAPI.NodeInfo
}
type sgWrapper storagegroup.StorageGroup
@ -89,7 +91,7 @@ func (n nodeInfoWrapper) PublicKey() []byte {
}
func (n nodeInfoWrapper) Price() *big.Int {
return big.NewInt(int64(n.ni.Price))
return big.NewInt(int64(n.ni.Price()))
}
func (c *containerWrapper) Owner() user.ID {
@ -125,9 +127,9 @@ func (s settlementDeps) ContainerInfo(cid cid.ID) (common.ContainerInfo, error)
return (*containerWrapper)(cnr), nil
}
func (s settlementDeps) buildContainer(e uint64, cid cid.ID) (netmapAPI.ContainerNodes, *netmapAPI.Netmap, error) {
func (s settlementDeps) buildContainer(e uint64, cid cid.ID) ([][]netmapAPI.NodeInfo, *netmapAPI.NetMap, error) {
var (
nm *netmapAPI.Netmap
nm *netmapAPI.NetMap
err error
)
@ -146,11 +148,16 @@ func (s settlementDeps) buildContainer(e uint64, cid cid.ID) (netmapAPI.Containe
return nil, nil, fmt.Errorf("could not get container from sidechain: %w", err)
}
policy := cnr.PlacementPolicy()
if policy == nil {
return nil, nil, errors.New("missing placement policy in container")
}
binCnr := make([]byte, sha256.Size)
cid.Encode(binCnr)
cn, err := nm.GetContainerNodes(
cnr.PlacementPolicy(),
cn, err := nm.ContainerNodes(
*policy,
binCnr, // may be replace pivot calculation to neofs-api-go
)
if err != nil {
@ -166,12 +173,12 @@ func (s settlementDeps) ContainerNodes(e uint64, cid cid.ID) ([]common.NodeInfo,
return nil, err
}
ns := cn.Flatten()
ns := placement.FlattenNodes(cn)
res := make([]common.NodeInfo, 0, len(ns))
for i := range ns {
res = append(res, &nodeInfoWrapper{
ni: &ns[i],
ni: ns[i],
})
}

View file

@ -301,19 +301,21 @@ func (s *Server) handleSubnetRemoval(e event.Event) {
return
}
for _, c := range candidates.Nodes {
s.processCandidate(delEv.TxHash(), removedID, c)
candidateNodes := candidates.Nodes()
for i := range candidateNodes {
s.processCandidate(delEv.TxHash(), removedID, candidateNodes[i])
}
}
func (s *Server) processCandidate(txHash neogoutil.Uint256, removedID subnetid.ID, c netmap.Node) {
func (s *Server) processCandidate(txHash neogoutil.Uint256, removedID subnetid.ID, c netmap.NodeInfo) {
removeSubnet := false
log := s.log.With(
zap.String("public_key", hex.EncodeToString(c.NodeInfo.PublicKey())),
zap.String("public_key", hex.EncodeToString(c.PublicKey())),
zap.String("removed_subnet", removedID.String()),
)
err := c.NodeInfo.IterateSubnets(func(id subnetid.ID) error {
err := c.IterateSubnets(func(id subnetid.ID) error {
if removedID.Equals(id) {
removeSubnet = true
return netmap.ErrRemoveSubnet
@ -326,8 +328,7 @@ func (s *Server) processCandidate(txHash neogoutil.Uint256, removedID subnetid.I
log.Debug("removing node from netmap candidates")
var updateStatePrm netmapclient.UpdatePeerPrm
updateStatePrm.SetState(netmap.NodeStateOffline)
updateStatePrm.SetKey(c.NodeInfo.PublicKey())
updateStatePrm.SetKey(c.PublicKey())
updateStatePrm.SetHash(txHash)
err = s.netmapClient.UpdatePeerState(updateStatePrm)
@ -346,7 +347,7 @@ func (s *Server) processCandidate(txHash neogoutil.Uint256, removedID subnetid.I
log.Debug("removing subnet from the node")
var addPeerPrm netmapclient.AddPeerPrm
addPeerPrm.SetNodeInfo(c.NodeInfo)
addPeerPrm.SetNodeInfo(c)
addPeerPrm.SetHash(txHash)
err = s.netmapClient.AddPeer(addPeerPrm)

View file

@ -9,13 +9,13 @@ import (
// AddPeerPrm groups parameters of AddPeer operation.
type AddPeerPrm struct {
nodeInfo *netmap.NodeInfo
nodeInfo netmap.NodeInfo
client.InvokePrmOptional
}
// SetNodeInfo sets new peer NodeInfo.
func (a *AddPeerPrm) SetNodeInfo(nodeInfo *netmap.NodeInfo) {
func (a *AddPeerPrm) SetNodeInfo(nodeInfo netmap.NodeInfo) {
a.nodeInfo = nodeInfo
}
@ -31,18 +31,9 @@ func (c *Client) AddPeer(p AddPeerPrm) error {
method += "IR"
}
if p.nodeInfo == nil {
return fmt.Errorf("nil node info (%s)", method)
}
rawNodeInfo, err := p.nodeInfo.Marshal()
if err != nil {
return fmt.Errorf("can't marshal node info (%s): %w", method, err)
}
prm := client.InvokePrm{}
prm.SetMethod(method)
prm.SetArgs(rawNodeInfo)
prm.SetArgs(p.nodeInfo.Marshal())
prm.InvokePrmOptional = p.InvokePrmOptional
if err := c.client.Invoke(prm); err != nil {

View file

@ -185,85 +185,112 @@ func (c *Client) SetConfig(p SetConfigPrm) error {
return c.client.Invoke(prm)
}
// IterateConfigParameters iterates over configuration parameters stored in Netmap contract and passes them to f.
//
// Returns f's errors directly.
func (c *Client) IterateConfigParameters(f func(key, value []byte) error) error {
// RawNetworkParameter is a NeoFS network parameter which is transmitted but
// not interpreted by the NeoFS API protocol.
type RawNetworkParameter struct {
// Name of the parameter.
Name string
// Raw parameter value.
Value []byte
}
// NetworkConfiguration represents NeoFS network configuration stored
// in the NeoFS Sidechain.
type NetworkConfiguration struct {
MaxObjectSize uint64
StoragePrice uint64
AuditFee uint64
EpochDuration uint64
ContainerFee uint64
ContainerAliasFee uint64
EigenTrustIterations uint64
EigenTrustAlpha float64
IRCandidateFee uint64
WithdrawalFee uint64
Raw []RawNetworkParameter
}
// ReadNetworkConfiguration reads NetworkConfiguration from the NeoFS Sidechain.
func (c *Client) ReadNetworkConfiguration() (*NetworkConfiguration, error) {
prm := client.TestInvokePrm{}
prm.SetMethod(configListMethod)
items, err := c.client.TestInvoke(prm)
if err != nil {
return fmt.Errorf("could not perform test invocation (%s): %w",
return nil, fmt.Errorf("could not perform test invocation (%s): %w",
configListMethod, err)
}
if ln := len(items); ln != 1 {
return fmt.Errorf("unexpected stack item count (%s): %d", configListMethod, ln)
return nil, fmt.Errorf("unexpected stack item count (%s): %d", configListMethod, ln)
}
arr, err := client.ArrayFromStackItem(items[0])
if err != nil {
return fmt.Errorf("record list (%s): %w", configListMethod, err)
return nil, fmt.Errorf("record list (%s): %w", configListMethod, err)
}
return iterateRecords(arr, func(key, value []byte) error {
return f(key, value)
})
}
m := make(map[string]struct{}, len(arr))
var res NetworkConfiguration
res.Raw = make([]RawNetworkParameter, 0, len(arr))
// 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)
ContainerAliasFee(uint64)
EigenTrustIterations(uint64)
EigenTrustAlpha(float64)
InnerRingCandidateFee(uint64)
WithdrawFee(uint64)
}
err = iterateRecords(arr, func(name string, value []byte) error {
_, ok := m[name]
if ok {
return fmt.Errorf("duplicated config name %s", name)
}
// 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 {
m[name] = struct{}{}
switch name {
default:
dst.UnknownParameter(k, val)
res.Raw = append(res.Raw, RawNetworkParameter{
Name: name,
Value: value,
})
case maxObjectSizeConfig:
dst.MaxObjectSize(bytesToUint64(val))
res.MaxObjectSize = bytesToUint64(value)
case basicIncomeRateConfig:
dst.BasicIncomeRate(bytesToUint64(val))
res.StoragePrice = bytesToUint64(value)
case auditFeeConfig:
dst.AuditFee(bytesToUint64(val))
res.AuditFee = bytesToUint64(value)
case epochDurationConfig:
dst.EpochDuration(bytesToUint64(val))
res.EpochDuration = bytesToUint64(value)
case containerFeeConfig:
dst.ContainerFee(bytesToUint64(val))
res.ContainerFee = bytesToUint64(value)
case containerAliasFeeConfig:
dst.ContainerAliasFee(bytesToUint64(val))
res.ContainerAliasFee = bytesToUint64(value)
case etIterationsConfig:
dst.EigenTrustIterations(bytesToUint64(val))
res.EigenTrustIterations = bytesToUint64(value)
case etAlphaConfig:
v, err := strconv.ParseFloat(string(val), 64)
res.EigenTrustAlpha, err = strconv.ParseFloat(string(value), 64)
if err != nil {
return fmt.Errorf("prm %s: %v", etAlphaConfig, err)
return fmt.Errorf("invalid prm %s: %v", etAlphaConfig, err)
}
dst.EigenTrustAlpha(v)
case irCandidateFeeConfig:
dst.InnerRingCandidateFee(bytesToUint64(val))
res.IRCandidateFee = bytesToUint64(value)
case withdrawFeeConfig:
dst.WithdrawFee(bytesToUint64(val))
res.WithdrawalFee = bytesToUint64(value)
}
return nil
})
if err != nil {
return nil, err
}
return &res, nil
}
func bytesToUint64(val []byte) uint64 {
@ -307,7 +334,7 @@ func StringAssert(item stackitem.Item) (interface{}, error) {
// iterateRecords iterates over all config records and passes them to f.
//
// Returns f's errors directly.
func iterateRecords(arr []stackitem.Item, f func(key, value []byte) error) error {
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 {
@ -328,7 +355,7 @@ func iterateRecords(arr []stackitem.Item, f func(key, value []byte) error) error
return fmt.Errorf("record value: %w", err)
}
if err := f(k, v); err != nil {
if err := f(string(k), v); err != nil {
return err
}
}

View file

@ -8,20 +8,6 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/netmap"
)
// State is an enumeration of various states of the NeoFS node.
type State int64
const (
// Undefined is unknown state.
Undefined State = iota
// Online is network unavailable state.
Online
// Offline is an active state in the network.
Offline
)
const (
nodeInfoFixedPrmNumber = 1
@ -31,7 +17,7 @@ const (
// GetNetMapByEpoch receives information list about storage nodes
// through the Netmap contract call, composes network map
// from them and returns it. Returns snapshot of the specified epoch number.
func (c *Client) GetNetMapByEpoch(epoch uint64) (*netmap.Netmap, error) {
func (c *Client) GetNetMapByEpoch(epoch uint64) (*netmap.NetMap, error) {
invokePrm := client.TestInvokePrm{}
invokePrm.SetMethod(epochSnapshotMethod)
invokePrm.SetArgs(epoch)
@ -48,7 +34,7 @@ func (c *Client) GetNetMapByEpoch(epoch uint64) (*netmap.Netmap, error) {
// GetCandidates receives information list about candidates
// for the next epoch network map through the Netmap contract
// call, composes network map from them and returns it.
func (c *Client) GetCandidates() (*netmap.Netmap, error) {
func (c *Client) GetCandidates() (*netmap.NetMap, error) {
invokePrm := client.TestInvokePrm{}
invokePrm.SetMethod(netMapCandidatesMethod)
@ -62,7 +48,10 @@ func (c *Client) GetCandidates() (*netmap.Netmap, error) {
return nil, fmt.Errorf("could not parse contract response: %w", err)
}
return netmap.NewNetmap(netmap.NodesFromInfo(candVals))
var nm netmap.NetMap
nm.SetNodes(candVals)
return &nm, nil
}
// NetMap performs the test invoke of get network map
@ -128,11 +117,9 @@ func stackItemToNodeInfo(prm stackitem.Item, res *netmap.NodeInfo) error {
switch state {
case 1:
res.SetState(netmap.NodeStateOnline)
res.SetOnline()
case 2:
res.SetState(netmap.NodeStateOffline)
default:
res.SetState(0)
res.SetOffline()
}
return nil

View file

@ -17,23 +17,29 @@ func Test_stackItemsToNodeInfos(t *testing.T) {
pub := make([]byte, 33)
rand.Read(pub)
expected[i].SetState(netmap.NodeState(i % 3))
switch i % 3 {
case 1:
expected[i].SetOffline()
case 2:
expected[i].SetOnline()
}
expected[i].SetPublicKey(pub)
var attr netmap.NodeAttribute
attr.SetKey("key")
attr.SetValue(strconv.Itoa(i))
expected[i].SetAttributes(attr)
expected[i].SetAttribute("key", strconv.Itoa(i))
}
items := make([]stackitem.Item, 4)
for i := range items {
data, err := expected[i].Marshal()
require.NoError(t, err)
data := expected[i].Marshal()
state := int64(expected[i].State())
if state != 0 { // In contract online=1, offline=2, in API it is the other way.
state = 3 - state
var state int64
switch {
case expected[i].IsOnline():
state = 1
case expected[i].IsOffline():
state = 2
}
items[i] = stackitem.NewStruct([]stackitem.Item{

View file

@ -12,17 +12,17 @@ import (
// through the Netmap contract call, composes network map
// from them and returns it. With diff == 0 returns current
// network map, else return snapshot of previous network map.
func (c *Client) GetNetMap(diff uint64) (*netmap.Netmap, error) {
func (c *Client) GetNetMap(diff uint64) (*netmap.NetMap, error) {
return c.getNetMap(diff)
}
// Snapshot returns current netmap node infos.
// Consider using pkg/morph/client/netmap for this.
func (c *Client) Snapshot() (*netmap.Netmap, error) {
func (c *Client) Snapshot() (*netmap.NetMap, error) {
return c.getNetMap(0)
}
func (c *Client) getNetMap(diff uint64) (*netmap.Netmap, error) {
func (c *Client) getNetMap(diff uint64) (*netmap.NetMap, error) {
prm := client.TestInvokePrm{}
prm.SetMethod(snapshotMethod)
prm.SetArgs(diff)
@ -35,7 +35,7 @@ func (c *Client) getNetMap(diff uint64) (*netmap.Netmap, error) {
return unmarshalNetmap(res, snapshotMethod)
}
func unmarshalNetmap(items []stackitem.Item, method string) (*netmap.Netmap, error) {
func unmarshalNetmap(items []stackitem.Item, method string) (*netmap.NetMap, error) {
rawPeers, err := peersFromStackItems(items, method)
if err != nil {
return nil, err
@ -48,5 +48,8 @@ func unmarshalNetmap(items []stackitem.Item, method string) (*netmap.Netmap, err
}
}
return netmap.NewNetmap(netmap.NodesFromInfo(result))
var nm netmap.NetMap
nm.SetNodes(result)
return &nm, nil
}

View file

@ -4,13 +4,13 @@ import (
"fmt"
"github.com/nspcc-dev/neofs-node/pkg/morph/client"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
)
// UpdatePeerPrm groups parameters of UpdatePeerState operation.
type UpdatePeerPrm struct {
key []byte
state netmap.NodeState
key []byte
online bool
client.InvokePrmOptional
}
@ -21,8 +21,8 @@ func (u *UpdatePeerPrm) SetKey(key []byte) {
}
// SetState sets node state.
func (u *UpdatePeerPrm) SetState(state netmap.NodeState) {
u.state = state
func (u *UpdatePeerPrm) SetOnline() {
u.online = true
}
// UpdatePeerState changes peer status through Netmap contract call.
@ -36,9 +36,14 @@ func (c *Client) UpdatePeerState(p UpdatePeerPrm) error {
method += "IR"
}
state := 2
if p.online {
state = 1
}
prm := client.InvokePrm{}
prm.SetMethod(method)
prm.SetArgs(int64(p.state.ToV2()), p.key)
prm.SetArgs(int64(state), p.key)
prm.InvokePrmOptional = p.InvokePrmOptional
if err := c.client.Invoke(prm); err != nil {

View file

@ -10,12 +10,12 @@ import (
v2netmap "github.com/nspcc-dev/neofs-api-go/v2/netmap"
"github.com/nspcc-dev/neofs-node/pkg/morph/client"
"github.com/nspcc-dev/neofs-node/pkg/morph/event"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
)
type UpdatePeer struct {
publicKey *keys.PublicKey
status netmap.NodeState
online bool
// For notary notifications only.
// Contains raw transactions of notary request.
@ -25,8 +25,8 @@ type UpdatePeer struct {
// MorphEvent implements Neo:Morph Event interface.
func (UpdatePeer) MorphEvent() {}
func (s UpdatePeer) Status() netmap.NodeState {
return s.status
func (s UpdatePeer) Online() bool {
return s.online
}
func (s UpdatePeer) PublicKey() *keys.PublicKey {
@ -73,7 +73,13 @@ func ParseUpdatePeer(e *subscriptions.NotificationEvent) (event.Event, error) {
return nil, fmt.Errorf("could not get node status: %w", err)
}
ev.status = netmap.NodeStateFromV2(v2netmap.NodeState(st))
switch v2netmap.NodeState(st) {
default:
return nil, fmt.Errorf("unsupported node state %d", st)
case v2netmap.Offline:
case v2netmap.Online:
ev.online = true
}
return ev, nil
}

View file

@ -7,9 +7,8 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
netmapv2 "github.com/nspcc-dev/neofs-api-go/v2/netmap"
v2netmap "github.com/nspcc-dev/neofs-api-go/v2/netmap"
"github.com/nspcc-dev/neofs-node/pkg/morph/event"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
)
var errNilPubKey = errors.New("could not parse public key: public key is nil")
@ -27,10 +26,6 @@ func (s *UpdatePeer) setPublicKey(v []byte) (err error) {
return
}
func (s *UpdatePeer) setStatus(v uint32) {
s.status = netmap.NodeStateFromV2(netmapv2.NodeState(v))
}
const (
// UpdateStateNotaryEvent is method name for netmap state updating
// operations in `Netmap` contract. Is used as identificator for
@ -66,7 +61,13 @@ func ParseUpdatePeerNotary(ne event.NotaryEvent) (event.Event, error) {
return nil, err
}
ev.setStatus(uint32(state))
switch v2netmap.NodeState(state) {
default:
return nil, fmt.Errorf("unsupported node state %d", err)
case v2netmap.Offline:
case v2netmap.Online:
ev.online = true
}
fieldNum++
case fieldNum == expectedItemNumUpdatePeer:

View file

@ -7,7 +7,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neofs-node/pkg/morph/event"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/stretchr/testify/require"
)
@ -15,10 +14,7 @@ func TestParseUpdatePeer(t *testing.T) {
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
var (
publicKey = priv.PublicKey()
state = netmap.NodeStateOffline
)
publicKey := priv.PublicKey()
t.Run("wrong number of parameters", func(t *testing.T) {
prms := []stackitem.Item{
@ -48,14 +44,14 @@ func TestParseUpdatePeer(t *testing.T) {
t.Run("correct behavior", func(t *testing.T) {
ev, err := ParseUpdatePeer(createNotifyEventFromItems([]stackitem.Item{
stackitem.NewBigInteger(new(big.Int).SetInt64(int64(state.ToV2()))),
stackitem.NewBigInteger(new(big.Int).SetInt64(1)),
stackitem.NewByteArray(publicKey.Bytes()),
}))
require.NoError(t, err)
require.Equal(t, UpdatePeer{
publicKey: publicKey,
status: state,
online: true,
}, ev)
})
}

View file

@ -132,10 +132,11 @@ func WriteToNodeInfo(g AddressGroup, ni *netmap.NodeInfo) {
addrs := make([]string, 0, num)
iterateAllAddresses(g, func(addr Address) {
ni.SetNetworkEndpoints()
addrs = append(addrs, addr.String())
})
ni.SetAddresses(addrs...)
ni.SetNetworkEndpoints(addrs...)
}
// Intersects checks if two AddressGroup have

View file

@ -30,6 +30,18 @@ var (
errUnsupportedPresentationProtocol = errors.New("unsupported presentation protocol in multiaddress")
)
// NodeEndpointsIterator is a wrapper over netmap.NodeInfo which implements
// MultiAddressIterator.
type NodeEndpointsIterator netmap.NodeInfo
func (x NodeEndpointsIterator) IterateAddresses(f func(string) bool) {
(netmap.NodeInfo)(x).IterateNetworkEndpoints(f)
}
func (x NodeEndpointsIterator) NumberOfAddresses() int {
return (netmap.NodeInfo)(x).NumberOfNetworkEndpoints()
}
// VerifyMultiAddress validates multiaddress of n.
//
// If n's address contains more than 3 protocols
@ -45,8 +57,8 @@ var (
// 2. tcp
// 3. tls(optional, may be absent)
//
func VerifyMultiAddress(ni *netmap.NodeInfo) error {
return iterateParsedAddresses(ni, checkProtocols)
func VerifyMultiAddress(ni netmap.NodeInfo) error {
return iterateParsedAddresses(NodeEndpointsIterator(ni), checkProtocols)
}
func checkProtocols(a Address) error {

View file

@ -55,10 +55,7 @@ func TestVerifyMultiAddress_Order(t *testing.T) {
}
}
func constructNodeInfo(address string) *netmap.NodeInfo {
ni := new(netmap.NodeInfo)
ni.SetAddresses(address)
func constructNodeInfo(address string) (ni netmap.NodeInfo) {
ni.SetNetworkEndpoints(address)
return ni
}

View file

@ -29,7 +29,7 @@ type Context struct {
sgMembersCache map[int][]oid.ID
placementMtx sync.Mutex
placementCache map[string][]netmap.Nodes
placementCache map[string][][]netmap.NodeInfo
porRequests, porRetries atomic.Uint32
@ -51,11 +51,11 @@ type Context struct {
type pairMemberInfo struct {
failedPDP, passedPDP bool // at least one
node *netmap.Node
node netmap.NodeInfo
}
type gamePair struct {
n1, n2 *netmap.Node
n1, n2 netmap.NodeInfo
id oid.ID
@ -88,11 +88,11 @@ type ContainerCommunicator interface {
GetSG(*audit.Task, oid.ID) (*storagegroup.StorageGroup, error)
// Must return object header from the container node.
GetHeader(*audit.Task, *netmap.Node, oid.ID, bool) (*object.Object, error)
GetHeader(*audit.Task, netmap.NodeInfo, oid.ID, bool) (*object.Object, error)
// Must return homomorphic Tillich-Zemor hash of payload range of the
// object stored in container node.
GetRangeHash(*audit.Task, *netmap.Node, oid.ID, *object.Range) ([]byte, error)
GetRangeHash(*audit.Task, netmap.NodeInfo, oid.ID, *object.Range) ([]byte, error)
}
// NewContext creates, initializes and returns Context.
@ -160,9 +160,12 @@ func (c *Context) init() {
c.sgMembersCache = make(map[int][]oid.ID)
c.placementCache = make(map[string][]netmap.Nodes)
c.placementCache = make(map[string][][]netmap.NodeInfo)
c.cnrNodesNum = len(c.task.ContainerNodes().Flatten())
cnrVectors := c.task.ContainerNodes()
for i := range cnrVectors {
c.cnrNodesNum += len(cnrVectors[i])
}
c.pairedNodes = make(map[uint64]*pairMemberInfo)
@ -200,7 +203,7 @@ func (c *Context) writeReport() {
}
}
func (c *Context) buildPlacement(id oid.ID) ([]netmap.Nodes, error) {
func (c *Context) buildPlacement(id oid.ID) ([][]netmap.NodeInfo, error) {
c.placementMtx.Lock()
defer c.placementMtx.Unlock()

View file

@ -108,7 +108,7 @@ func (c *Context) splitPayload(id oid.ID) []uint64 {
}
func (c *Context) collectHashes(p *gamePair) {
fn := func(n *netmap.Node, rngs []*object.Range) [][]byte {
fn := func(n netmap.NodeInfo, rngs []*object.Range) [][]byte {
// Here we randomize the order a bit: the hypothesis is that this
// makes it harder for an unscrupulous node to come up with a
// reliable cheating strategy.
@ -176,7 +176,7 @@ func (c *Context) analyzeHashes(p *gamePair) {
c.passNodesPDP(p.n1, p.n2)
}
func (c *Context) failNodesPDP(ns ...*netmap.Node) {
func (c *Context) failNodesPDP(ns ...netmap.NodeInfo) {
c.pairedMtx.Lock()
for i := range ns {
@ -186,7 +186,7 @@ func (c *Context) failNodesPDP(ns ...*netmap.Node) {
c.pairedMtx.Unlock()
}
func (c *Context) passNodesPDP(ns ...*netmap.Node) {
func (c *Context) passNodesPDP(ns ...netmap.NodeInfo) {
c.pairedMtx.Lock()
for i := range ns {
@ -200,18 +200,18 @@ func (c *Context) writePairsResult() {
var failCount, okCount int
c.iteratePairedNodes(
func(*netmap.Node) { failCount++ },
func(*netmap.Node) { okCount++ },
func(netmap.NodeInfo) { failCount++ },
func(netmap.NodeInfo) { okCount++ },
)
failedNodes := make([][]byte, 0, failCount)
passedNodes := make([][]byte, 0, okCount)
c.iteratePairedNodes(
func(n *netmap.Node) {
func(n netmap.NodeInfo) {
failedNodes = append(failedNodes, n.PublicKey())
},
func(n *netmap.Node) {
func(n netmap.NodeInfo) {
passedNodes = append(passedNodes, n.PublicKey())
},
)
@ -219,7 +219,7 @@ func (c *Context) writePairsResult() {
c.report.SetPDPResults(passedNodes, failedNodes)
}
func (c *Context) iteratePairedNodes(onFail, onPass func(*netmap.Node)) {
func (c *Context) iteratePairedNodes(onFail, onPass func(netmap.NodeInfo)) {
for _, pairedNode := range c.pairedNodes {
if pairedNode.failedPDP {
onFail(pairedNode.node)

View file

@ -23,12 +23,12 @@ func (c *Context) executePoP() {
}
func (c *Context) buildCoverage() {
replicas := c.task.ContainerStructure().PlacementPolicy().Replicas()
policy := c.task.ContainerStructure().PlacementPolicy()
// select random member from another storage group
// and process all placement vectors
c.iterateSGMembersPlacementRand(func(id oid.ID, ind int, nodes netmap.Nodes) bool {
c.processObjectPlacement(id, nodes, replicas[ind].Count())
c.iterateSGMembersPlacementRand(func(id oid.ID, ind int, nodes []netmap.NodeInfo) bool {
c.processObjectPlacement(id, nodes, policy.ReplicaNumberByIndex(ind))
return c.containerCovered()
})
}
@ -38,7 +38,7 @@ func (c *Context) containerCovered() bool {
return c.cnrNodesNum <= len(c.pairedNodes)
}
func (c *Context) processObjectPlacement(id oid.ID, nodes netmap.Nodes, replicas uint32) {
func (c *Context) processObjectPlacement(id oid.ID, nodes []netmap.NodeInfo, replicas uint32) {
var (
ok uint32
optimal bool
@ -50,7 +50,7 @@ func (c *Context) processObjectPlacement(id oid.ID, nodes netmap.Nodes, replicas
for i := 0; ok < replicas && i < len(nodes); i++ {
// try to get object header from node
hdr, err := c.cnrCom.GetHeader(c.task, &nodes[i], id, false)
hdr, err := c.cnrCom.GetHeader(c.task, nodes[i], id, false)
if err != nil {
c.log.Debug("could not get object header from candidate",
zap.Stringer("id", id),
@ -95,14 +95,14 @@ func (c *Context) processObjectPlacement(id oid.ID, nodes netmap.Nodes, replicas
if unpairedCandidate1 >= 0 {
if unpairedCandidate2 >= 0 {
c.composePair(id, &nodes[unpairedCandidate1], &nodes[unpairedCandidate2])
c.composePair(id, nodes[unpairedCandidate1], nodes[unpairedCandidate2])
} else if pairedCandidate >= 0 {
c.composePair(id, &nodes[unpairedCandidate1], &nodes[pairedCandidate])
c.composePair(id, nodes[unpairedCandidate1], nodes[pairedCandidate])
}
}
}
func (c *Context) composePair(id oid.ID, n1, n2 *netmap.Node) {
func (c *Context) composePair(id oid.ID, n1, n2 netmap.NodeInfo) {
c.pairs = append(c.pairs, gamePair{
n1: n1,
n2: n2,
@ -117,7 +117,7 @@ func (c *Context) composePair(id oid.ID, n1, n2 *netmap.Node) {
}
}
func (c *Context) iterateSGMembersPlacementRand(f func(oid.ID, int, netmap.Nodes) bool) {
func (c *Context) iterateSGMembersPlacementRand(f func(oid.ID, int, []netmap.NodeInfo) bool) {
// iterate over storage groups members for all storage groups (one by one)
// with randomly shuffled members
c.iterateSGMembersRand(func(id oid.ID) bool {

View file

@ -78,7 +78,7 @@ func (c *Context) checkStorageGroupPoR(ind int, sg oid.ID) {
accRetries++
}
hdr, err := c.cnrCom.GetHeader(c.task, &flat[j], members[i], true)
hdr, err := c.cnrCom.GetHeader(c.task, flat[j], members[i], true)
if err != nil {
c.log.Debug("can't head object",
zap.String("remote_node", hex.EncodeToString(flat[j].PublicKey())),

View file

@ -19,9 +19,9 @@ type Task struct {
cnr *container.Container
nm *netmap.Netmap
nm *netmap.NetMap
cnrNodes netmap.ContainerNodes
cnrNodes [][]netmap.NodeInfo
sgList []oid.ID
}
@ -83,7 +83,7 @@ func (t *Task) ContainerStructure() *container.Container {
}
// WithContainerNodes sets nodes in the container under audit.
func (t *Task) WithContainerNodes(cnrNodes netmap.ContainerNodes) *Task {
func (t *Task) WithContainerNodes(cnrNodes [][]netmap.NodeInfo) *Task {
if t != nil {
t.cnrNodes = cnrNodes
}
@ -92,12 +92,12 @@ func (t *Task) WithContainerNodes(cnrNodes netmap.ContainerNodes) *Task {
}
// NetworkMap returns network map of audit epoch.
func (t *Task) NetworkMap() *netmap.Netmap {
func (t *Task) NetworkMap() *netmap.NetMap {
return t.nm
}
// WithNetworkMap sets network map of audit epoch.
func (t *Task) WithNetworkMap(nm *netmap.Netmap) *Task {
func (t *Task) WithNetworkMap(nm *netmap.NetMap) *Task {
if t != nil {
t.nm = nm
}
@ -106,7 +106,7 @@ func (t *Task) WithNetworkMap(nm *netmap.Netmap) *Task {
}
// ContainerNodes returns nodes in the container under audit.
func (t *Task) ContainerNodes() netmap.ContainerNodes {
func (t *Task) ContainerNodes() [][]netmap.NodeInfo {
return t.cnrNodes
}

View file

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
netmapcore "github.com/nspcc-dev/neofs-node/pkg/core/netmap"
loadroute "github.com/nspcc-dev/neofs-node/pkg/services/container/announcement/load/route"
"github.com/nspcc-dev/neofs-sdk-go/container"
)
@ -37,14 +38,12 @@ func (b *Builder) NextStage(a container.UsedSpaceAnnouncement, passed []loadrout
continue
}
target := placement[i][0]
if len(passed) == 1 && bytes.Equal(passed[0].PublicKey(), target.PublicKey()) {
if len(passed) == 1 && bytes.Equal(passed[0].PublicKey(), placement[i][0].PublicKey()) {
// add nil element so the announcement will be saved in local memory
res = append(res, nil)
} else {
// add element with remote node to send announcement to
res = append(res, target)
res = append(res, netmapcore.Node(placement[i][0]))
}
}

View file

@ -10,5 +10,5 @@ type PlacementBuilder interface {
// BuildPlacement must compose and sort (according to a specific algorithm)
// storage nodes from the container by its identifier using network map
// of particular epoch.
BuildPlacement(epoch uint64, cnr cid.ID) ([]netmap.Nodes, error)
BuildPlacement(epoch uint64, cnr cid.ID) ([][]netmap.NodeInfo, error)
}

View file

@ -29,7 +29,7 @@ func (s *Server) NetmapSnapshot(ctx context.Context, req *control.NetmapSnapshot
nm := new(control.Netmap)
nm.SetEpoch(epoch)
nm.SetNodes(nodesFromAPI(apiNetMap.Nodes))
nm.SetNodes(nodesFromAPI(apiNetMap.Nodes()))
// create and fill response
resp := new(control.NetmapSnapshotResponse)
@ -47,20 +47,28 @@ func (s *Server) NetmapSnapshot(ctx context.Context, req *control.NetmapSnapshot
return resp, nil
}
func nodesFromAPI(apiNodes netmapAPI.Nodes) []*control.NodeInfo {
func nodesFromAPI(apiNodes []netmapAPI.NodeInfo) []*control.NodeInfo {
nodes := make([]*control.NodeInfo, 0, len(apiNodes))
for _, apiNode := range apiNodes {
for i := range apiNodes {
node := new(control.NodeInfo)
node.SetPublicKey(apiNode.PublicKey())
node.SetPublicKey(apiNodes[i].PublicKey())
addrs := make([]string, 0, apiNode.NumberOfAddresses())
netmapAPI.IterateAllAddresses(apiNode.NodeInfo, func(s string) {
addrs := make([]string, 0, apiNodes[i].NumberOfNetworkEndpoints())
netmapAPI.IterateNetworkEndpoints(apiNodes[i], func(s string) {
addrs = append(addrs, s)
})
node.SetAddresses(addrs)
node.SetAttributes(attributesFromAPI(apiNode.Attributes()))
node.SetState(stateFromAPI(apiNode.State()))
node.SetAttributes(attributesFromAPI(apiNodes[i]))
switch {
default:
node.SetState(control.NetmapStatus_STATUS_UNDEFINED)
case apiNodes[i].IsOnline():
node.SetState(control.NetmapStatus_ONLINE)
case apiNodes[i].IsOffline():
node.SetState(control.NetmapStatus_OFFLINE)
}
nodes = append(nodes, node)
}
@ -68,36 +76,16 @@ func nodesFromAPI(apiNodes netmapAPI.Nodes) []*control.NodeInfo {
return nodes
}
func stateFromAPI(s netmapAPI.NodeState) control.NetmapStatus {
switch s {
default:
return control.NetmapStatus_STATUS_UNDEFINED
case netmapAPI.NodeStateOffline:
return control.NetmapStatus_OFFLINE
case netmapAPI.NodeStateOnline:
return control.NetmapStatus_ONLINE
}
}
func attributesFromAPI(apiNode netmapAPI.NodeInfo) []*control.NodeInfo_Attribute {
attrs := make([]*control.NodeInfo_Attribute, 0, apiNode.NumberOfAttributes())
func attributesFromAPI(apiAttrs []netmapAPI.NodeAttribute) []*control.NodeInfo_Attribute {
attrs := make([]*control.NodeInfo_Attribute, 0, len(apiAttrs))
for _, apiAttr := range apiAttrs {
apiNode.IterateAttributes(func(key, value string) {
a := new(control.NodeInfo_Attribute)
a.SetKey(apiAttr.Key())
a.SetValue(apiAttr.Value())
apiParents := apiAttr.ParentKeys()
parents := make([]string, 0, len(apiParents))
for i := range apiParents {
parents = append(parents, apiParents[i])
}
a.SetParents(parents)
a.SetKey(key)
a.SetValue(value)
attrs = append(attrs, a)
}
})
return attrs
}

View file

@ -7,6 +7,7 @@ import (
"github.com/nspcc-dev/neofs-api-go/v2/netmap"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-node/pkg/core/version"
netmapSDK "github.com/nspcc-dev/neofs-sdk-go/netmap"
versionsdk "github.com/nspcc-dev/neofs-sdk-go/version"
)
@ -32,7 +33,7 @@ type NetworkInfo interface {
// Must return recent network information in NeoFS API v2 NetworkInfo structure.
//
// If protocol version is <=2.9, MillisecondsPerBlock and network config should be unset.
Dump(*refs.Version) (*netmap.NetworkInfo, error)
Dump(versionsdk.Version) (*netmapSDK.NetworkInfo, error)
}
func NewExecutionService(s NodeState, v versionsdk.Version, netInfo NetworkInfo) Server {
@ -93,13 +94,24 @@ func (s *executorSvc) LocalNodeInfo(
func (s *executorSvc) NetworkInfo(
_ context.Context,
req *netmap.NetworkInfoRequest) (*netmap.NetworkInfoResponse, error) {
ni, err := s.netInfo.Dump(req.GetMetaHeader().GetVersion())
verV2 := req.GetMetaHeader().GetVersion()
if verV2 == nil {
return nil, errors.New("missing protocol version in meta header")
}
var ver versionsdk.Version
ver.ReadFromV2(*verV2)
ni, err := s.netInfo.Dump(ver)
if err != nil {
return nil, err
}
var niV2 netmap.NetworkInfo
ni.WriteToV2(&niV2)
body := new(netmap.NetworkInfoResponseBody)
body.SetNetworkInfo(ni)
body.SetNetworkInfo(&niV2)
resp := new(netmap.NetworkInfoResponse)
resp.SetBody(body)

View file

@ -132,18 +132,24 @@ func (c senderClassifier) isContainerKey(
}
func lookupKeyInContainer(
nm *netmap.Netmap,
nm *netmap.NetMap,
owner, idCnr []byte,
cnr *container.Container) (bool, error) {
cnrNodes, err := nm.GetContainerNodes(cnr.PlacementPolicy(), idCnr)
policy := cnr.PlacementPolicy()
if policy == nil {
return false, errors.New("missing placement policy in container")
}
cnrVectors, err := nm.ContainerNodes(*policy, idCnr)
if err != nil {
return false, err
}
flatCnrNodes := cnrNodes.Flatten() // we need single array to iterate on
for i := range flatCnrNodes {
if bytes.Equal(flatCnrNodes[i].PublicKey(), owner) {
return true, nil
for i := range cnrVectors {
for j := range cnrVectors[i] {
if bytes.Equal(cnrVectors[i][j].PublicKey(), owner) {
return true, nil
}
}
}

View file

@ -9,6 +9,7 @@ import (
"testing"
"github.com/nspcc-dev/neofs-node/pkg/core/client"
netmapcore "github.com/nspcc-dev/neofs-node/pkg/core/netmap"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/network"
"github.com/nspcc-dev/neofs-node/pkg/services/object/util"
@ -18,6 +19,7 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/container"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
@ -38,7 +40,7 @@ type testTraverserGenerator struct {
}
type testPlacementBuilder struct {
vectors map[string][]netmap.Nodes
vectors map[string][][]netmap.NodeInfo
}
type testClientCache struct {
@ -81,7 +83,7 @@ func (g *testTraverserGenerator) GenerateTraverser(cnr cid.ID, obj *oid.ID, e ui
return placement.NewTraverser(opts...)
}
func (p *testPlacementBuilder) BuildPlacement(cnr cid.ID, obj *oid.ID, _ *netmap.PlacementPolicy) ([]netmap.Nodes, error) {
func (p *testPlacementBuilder) BuildPlacement(cnr cid.ID, obj *oid.ID, _ netmap.PlacementPolicy) ([][]netmap.NodeInfo, error) {
var addr oid.Address
addr.SetContainer(cnr)
@ -392,8 +394,8 @@ func TestGetLocalOnly(t *testing.T) {
})
}
func testNodeMatrix(t testing.TB, dim []int) ([]netmap.Nodes, [][]string) {
mNodes := make([]netmap.Nodes, len(dim))
func testNodeMatrix(t testing.TB, dim []int) ([][]netmap.NodeInfo, [][]string) {
mNodes := make([][]netmap.NodeInfo, len(dim))
mAddr := make([][]string, len(dim))
for i := range dim {
@ -406,20 +408,20 @@ func testNodeMatrix(t testing.TB, dim []int) ([]netmap.Nodes, [][]string) {
strconv.Itoa(60000+j),
)
ni := netmap.NewNodeInfo()
ni.SetAddresses(a)
var ni netmap.NodeInfo
ni.SetNetworkEndpoints(a)
var na network.AddressGroup
err := na.FromIterator(ni)
err := na.FromIterator(netmapcore.Node(ni))
require.NoError(t, err)
as[j] = network.StringifyGroup(na)
ns[j] = *ni
ns[j] = ni
}
mNodes[i] = netmap.NodesFromInfo(ns)
mNodes[i] = ns
mAddr[i] = as
}
@ -464,7 +466,8 @@ func generateChain(ln int, cnr cid.ID) ([]*objectSDK.Object, []oid.ID, []byte) {
func TestGetRemoteSmall(t *testing.T) {
ctx := context.Background()
cnr := container.New(container.WithPolicy(new(netmap.PlacementPolicy)))
pp := netmaptest.PlacementPolicy()
cnr := container.New(container.WithPolicy(&pp))
idCnr := container.CalculateID(cnr)
newSvc := func(b *testPlacementBuilder, c *testClientCache) *Service {
@ -527,7 +530,7 @@ func TestGetRemoteSmall(t *testing.T) {
ns, as := testNodeMatrix(t, []int{2})
builder := &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
vectors: map[string][][]netmap.NodeInfo{
addr.EncodeToString(): ns,
},
}
@ -590,7 +593,7 @@ func TestGetRemoteSmall(t *testing.T) {
ns, as := testNodeMatrix(t, []int{2})
builder := &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
vectors: map[string][][]netmap.NodeInfo{
addr.EncodeToString(): ns,
},
}
@ -634,7 +637,7 @@ func TestGetRemoteSmall(t *testing.T) {
ns, as := testNodeMatrix(t, []int{2})
builder := &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
vectors: map[string][][]netmap.NodeInfo{
addr.EncodeToString(): ns,
},
}
@ -708,7 +711,7 @@ func TestGetRemoteSmall(t *testing.T) {
c2.addResult(splitAddr, nil, apistatus.ObjectNotFound{})
builder := &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
vectors: map[string][][]netmap.NodeInfo{
addr.EncodeToString(): ns,
splitAddr.EncodeToString(): ns,
},
@ -781,7 +784,7 @@ func TestGetRemoteSmall(t *testing.T) {
c2.addResult(child2Addr, nil, apistatus.ObjectNotFound{})
builder := &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
vectors: map[string][][]netmap.NodeInfo{
addr.EncodeToString(): ns,
linkAddr.EncodeToString(): ns,
child1Addr.EncodeToString(): ns,
@ -858,7 +861,7 @@ func TestGetRemoteSmall(t *testing.T) {
c2.addResult(child2Addr, children[1], nil)
builder := &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
vectors: map[string][][]netmap.NodeInfo{
addr.EncodeToString(): ns,
linkAddr.EncodeToString(): ns,
child1Addr.EncodeToString(): ns,
@ -924,7 +927,7 @@ func TestGetRemoteSmall(t *testing.T) {
c2.addResult(splitAddr, nil, apistatus.ObjectNotFound{})
builder := &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
vectors: map[string][][]netmap.NodeInfo{
addr.EncodeToString(): ns,
splitAddr.EncodeToString(): ns,
},
@ -988,7 +991,7 @@ func TestGetRemoteSmall(t *testing.T) {
c2.addResult(rightAddr, rightObj, nil)
builder := &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
vectors: map[string][][]netmap.NodeInfo{
addr.EncodeToString(): ns,
rightAddr.EncodeToString(): ns,
preRightAddr.EncodeToString(): ns,
@ -1058,7 +1061,7 @@ func TestGetRemoteSmall(t *testing.T) {
}
builder := &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{},
vectors: map[string][][]netmap.NodeInfo{},
}
builder.vectors[addr.EncodeToString()] = ns
@ -1116,7 +1119,8 @@ func TestGetRemoteSmall(t *testing.T) {
func TestGetFromPastEpoch(t *testing.T) {
ctx := context.Background()
cnr := container.New(container.WithPolicy(new(netmap.PlacementPolicy)))
pp := netmaptest.PlacementPolicy()
cnr := container.New(container.WithPolicy(&pp))
idCnr := container.CalculateID(cnr)
addr := oidtest.Address()
@ -1153,12 +1157,12 @@ func TestGetFromPastEpoch(t *testing.T) {
c: cnr,
b: map[uint64]placement.Builder{
curEpoch: &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
vectors: map[string][][]netmap.NodeInfo{
addr.EncodeToString(): ns[:1],
},
},
curEpoch - 1: &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
vectors: map[string][][]netmap.NodeInfo{
addr.EncodeToString(): ns[1:],
},
},

View file

@ -6,6 +6,7 @@ import (
"fmt"
clientcore "github.com/nspcc-dev/neofs-node/pkg/core/client"
netmapCore "github.com/nspcc-dev/neofs-node/pkg/core/netmap"
internalclient "github.com/nspcc-dev/neofs-node/pkg/services/object/internal/client"
"github.com/nspcc-dev/neofs-node/pkg/services/object/util"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
@ -29,7 +30,7 @@ type RemoteHeader struct {
type RemoteHeadPrm struct {
commonHeadPrm *Prm
node *netmap.NodeInfo
node netmap.NodeInfo
}
const remoteOpTTL = 1
@ -45,7 +46,7 @@ func NewRemoteHeader(keyStorage *util.KeyStorage, cache ClientConstructor) *Remo
}
// WithNodeInfo sets information about the remote node.
func (p *RemoteHeadPrm) WithNodeInfo(v *netmap.NodeInfo) *RemoteHeadPrm {
func (p *RemoteHeadPrm) WithNodeInfo(v netmap.NodeInfo) *RemoteHeadPrm {
if p != nil {
p.node = v
}
@ -71,7 +72,7 @@ func (h *RemoteHeader) Head(ctx context.Context, prm *RemoteHeadPrm) (*object.Ob
var info clientcore.NodeInfo
err = clientcore.NodeInfoFromRawNetmapElement(&info, prm.node)
err = clientcore.NodeInfoFromRawNetmapElement(&info, netmapCore.Node(prm.node))
if err != nil {
return nil, fmt.Errorf("parse client node info: %w", err)
}

View file

@ -5,6 +5,7 @@ import (
"fmt"
clientcore "github.com/nspcc-dev/neofs-node/pkg/core/client"
netmapCore "github.com/nspcc-dev/neofs-node/pkg/core/netmap"
internalclient "github.com/nspcc-dev/neofs-node/pkg/services/object/internal/client"
"github.com/nspcc-dev/neofs-node/pkg/services/object/util"
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer"
@ -36,7 +37,7 @@ type RemoteSender struct {
// RemotePutPrm groups remote put operation parameters.
type RemotePutPrm struct {
node *netmap.NodeInfo
node netmap.NodeInfo
obj *object.Object
}
@ -95,7 +96,7 @@ func NewRemoteSender(keyStorage *util.KeyStorage, cons ClientConstructor) *Remot
}
// WithNodeInfo sets information about the remote node.
func (p *RemotePutPrm) WithNodeInfo(v *netmap.NodeInfo) *RemotePutPrm {
func (p *RemotePutPrm) WithNodeInfo(v netmap.NodeInfo) *RemotePutPrm {
if p != nil {
p.node = v
}
@ -120,7 +121,7 @@ func (s *RemoteSender) PutObject(ctx context.Context, p *RemotePutPrm) error {
clientConstructor: s.clientConstructor,
}
err := clientcore.NodeInfoFromRawNetmapElement(&t.nodeInfo, p.node)
err := clientcore.NodeInfoFromRawNetmapElement(&t.nodeInfo, netmapCore.Node(p.node))
if err != nil {
return fmt.Errorf("parse client node info: %w", err)
}

View file

@ -10,6 +10,7 @@ import (
"testing"
clientcore "github.com/nspcc-dev/neofs-node/pkg/core/client"
netmapcore "github.com/nspcc-dev/neofs-node/pkg/core/netmap"
"github.com/nspcc-dev/neofs-node/pkg/network"
"github.com/nspcc-dev/neofs-node/pkg/services/object/util"
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement"
@ -37,7 +38,7 @@ type testTraverserGenerator struct {
}
type testPlacementBuilder struct {
vectors map[string][]netmap.Nodes
vectors map[string][][]netmap.NodeInfo
}
type testClientCache struct {
@ -73,7 +74,7 @@ func (g *testTraverserGenerator) generateTraverser(_ cid.ID, epoch uint64) (*pla
)
}
func (p *testPlacementBuilder) BuildPlacement(cnr cid.ID, obj *oid.ID, _ *netmap.PlacementPolicy) ([]netmap.Nodes, error) {
func (p *testPlacementBuilder) BuildPlacement(cnr cid.ID, obj *oid.ID, _ netmap.PlacementPolicy) ([][]netmap.NodeInfo, error) {
var addr oid.Address
addr.SetContainer(cnr)
@ -86,7 +87,7 @@ func (p *testPlacementBuilder) BuildPlacement(cnr cid.ID, obj *oid.ID, _ *netmap
return nil, errors.New("vectors for address not found")
}
res := make([]netmap.Nodes, len(vs))
res := make([][]netmap.NodeInfo, len(vs))
copy(res, vs)
return res, nil
@ -193,8 +194,8 @@ func TestGetLocalOnly(t *testing.T) {
})
}
func testNodeMatrix(t testing.TB, dim []int) ([]netmap.Nodes, [][]string) {
mNodes := make([]netmap.Nodes, len(dim))
func testNodeMatrix(t testing.TB, dim []int) ([][]netmap.NodeInfo, [][]string) {
mNodes := make([][]netmap.NodeInfo, len(dim))
mAddr := make([][]string, len(dim))
for i := range dim {
@ -207,20 +208,20 @@ func testNodeMatrix(t testing.TB, dim []int) ([]netmap.Nodes, [][]string) {
strconv.Itoa(60000+j),
)
ni := netmap.NewNodeInfo()
ni.SetAddresses(a)
var ni netmap.NodeInfo
ni.SetNetworkEndpoints(a)
var na network.AddressGroup
err := na.FromIterator(ni)
err := na.FromIterator(netmapcore.Node(ni))
require.NoError(t, err)
as[j] = network.StringifyGroup(na)
ns[j] = *ni
ns[j] = ni
}
mNodes[i] = netmap.NodesFromInfo(ns)
mNodes[i] = ns
mAddr[i] = as
}
@ -232,15 +233,15 @@ func TestGetRemoteSmall(t *testing.T) {
placementDim := []int{2}
rs := make([]netmap.Replica, len(placementDim))
rs := make([]netmap.ReplicaDescriptor, len(placementDim))
for i := range placementDim {
rs[i].SetCount(uint32(placementDim[i]))
rs[i].SetNumberOfObjects(uint32(placementDim[i]))
}
pp := netmap.NewPlacementPolicy()
pp.SetReplicas(rs...)
var pp netmap.PlacementPolicy
pp.AddReplicas(rs...)
cnr := container.New(container.WithPolicy(pp))
cnr := container.New(container.WithPolicy(&pp))
id := container.CalculateID(cnr)
newSvc := func(b *testPlacementBuilder, c *testClientCache) *Service {
@ -278,7 +279,7 @@ func TestGetRemoteSmall(t *testing.T) {
ns, as := testNodeMatrix(t, placementDim)
builder := &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
vectors: map[string][][]netmap.NodeInfo{
addr.EncodeToString(): ns,
},
}
@ -317,16 +318,16 @@ func TestGetFromPastEpoch(t *testing.T) {
placementDim := []int{2, 2}
rs := make([]netmap.Replica, len(placementDim))
rs := make([]netmap.ReplicaDescriptor, len(placementDim))
for i := range placementDim {
rs[i].SetCount(uint32(placementDim[i]))
rs[i].SetNumberOfObjects(uint32(placementDim[i]))
}
pp := netmap.NewPlacementPolicy()
pp.SetReplicas(rs...)
var pp netmap.PlacementPolicy
pp.AddReplicas(rs...)
cnr := container.New(container.WithPolicy(pp))
cnr := container.New(container.WithPolicy(&pp))
idCnr := container.CalculateID(cnr)
var addr oid.Address
@ -360,12 +361,12 @@ func TestGetFromPastEpoch(t *testing.T) {
c: cnr,
b: map[uint64]placement.Builder{
curEpoch: &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
vectors: map[string][][]netmap.NodeInfo{
addr.EncodeToString(): ns[:1],
},
},
curEpoch - 1: &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
vectors: map[string][][]netmap.NodeInfo{
addr.EncodeToString(): ns[1:],
},
},

View file

@ -43,7 +43,7 @@ func NewLocalPlacement(b placement.Builder, s netmap.AnnouncedKeys) placement.Bu
}
}
func (p *localPlacement) BuildPlacement(cnr cid.ID, obj *oid.ID, policy *netmapSDK.PlacementPolicy) ([]netmapSDK.Nodes, error) {
func (p *localPlacement) BuildPlacement(cnr cid.ID, obj *oid.ID, policy netmapSDK.PlacementPolicy) ([][]netmapSDK.NodeInfo, error) {
vs, err := p.builder.BuildPlacement(cnr, obj, policy)
if err != nil {
return nil, fmt.Errorf("(%T) could not build object placement: %w", p, err)
@ -53,13 +53,13 @@ func (p *localPlacement) BuildPlacement(cnr cid.ID, obj *oid.ID, policy *netmapS
for j := range vs[i] {
var addr network.AddressGroup
err := addr.FromIterator(vs[i][j])
err := addr.FromIterator(network.NodeEndpointsIterator(vs[i][j]))
if err != nil {
continue
}
if p.netmapKeys.IsLocalKey(vs[i][j].PublicKey()) {
return []netmapSDK.Nodes{{vs[i][j]}}, nil
return [][]netmapSDK.NodeInfo{{vs[i][j]}}, nil
}
}
}
@ -76,7 +76,7 @@ func NewRemotePlacementBuilder(b placement.Builder, s netmap.AnnouncedKeys) plac
}
}
func (p *remotePlacement) BuildPlacement(cnr cid.ID, obj *oid.ID, policy *netmapSDK.PlacementPolicy) ([]netmapSDK.Nodes, error) {
func (p *remotePlacement) BuildPlacement(cnr cid.ID, obj *oid.ID, policy netmapSDK.PlacementPolicy) ([][]netmapSDK.NodeInfo, error) {
vs, err := p.builder.BuildPlacement(cnr, obj, policy)
if err != nil {
return nil, fmt.Errorf("(%T) could not build object placement: %w", p, err)
@ -86,7 +86,7 @@ func (p *remotePlacement) BuildPlacement(cnr cid.ID, obj *oid.ID, policy *netmap
for j := 0; j < len(vs[i]); j++ {
var addr network.AddressGroup
err := addr.FromIterator(vs[i][j])
err := addr.FromIterator(network.NodeEndpointsIterator(vs[i][j]))
if err != nil {
continue
}

View file

@ -16,7 +16,7 @@ type netMapBuilder struct {
nmSrc netmap.Source
// mtx protects lastNm and containerCache fields.
mtx sync.Mutex
lastNm *netmapSDK.Netmap
lastNm *netmapSDK.NetMap
// containerCache caches container nodes by ID. It is used to skip `GetContainerNodes` invocation if
// neither netmap nor container has changed.
containerCache simplelru.LRUCache
@ -25,13 +25,13 @@ type netMapBuilder struct {
type netMapSrc struct {
netmap.Source
nm *netmapSDK.Netmap
nm *netmapSDK.NetMap
}
// defaultContainerCacheSize is the default size for the container cache.
const defaultContainerCacheSize = 10
func NewNetworkMapBuilder(nm *netmapSDK.Netmap) Builder {
func NewNetworkMapBuilder(nm *netmapSDK.NetMap) Builder {
cache, _ := simplelru.NewLRU(defaultContainerCacheSize, nil) // no error
return &netMapBuilder{
nmSrc: &netMapSrc{nm: nm},
@ -47,11 +47,11 @@ func NewNetworkMapSourceBuilder(nmSrc netmap.Source) Builder {
}
}
func (s *netMapSrc) GetNetMap(diff uint64) (*netmapSDK.Netmap, error) {
func (s *netMapSrc) GetNetMap(diff uint64) (*netmapSDK.NetMap, error) {
return s.nm, nil
}
func (b *netMapBuilder) BuildPlacement(cnr cid.ID, obj *oid.ID, p *netmapSDK.PlacementPolicy) ([]netmapSDK.Nodes, error) {
func (b *netMapBuilder) BuildPlacement(cnr cid.ID, obj *oid.ID, p netmapSDK.PlacementPolicy) ([][]netmapSDK.NodeInfo, error) {
nm, err := netmap.GetLatestNetworkMap(b.nmSrc)
if err != nil {
return nil, fmt.Errorf("could not get network map: %w", err)
@ -65,7 +65,7 @@ func (b *netMapBuilder) BuildPlacement(cnr cid.ID, obj *oid.ID, p *netmapSDK.Pla
raw, ok := b.containerCache.Get(string(binCnr))
b.mtx.Unlock()
if ok {
cn := raw.(netmapSDK.ContainerNodes)
cn := raw.([][]netmapSDK.NodeInfo)
return BuildObjectPlacement(nm, cn, obj)
}
} else {
@ -73,7 +73,7 @@ func (b *netMapBuilder) BuildPlacement(cnr cid.ID, obj *oid.ID, p *netmapSDK.Pla
b.mtx.Unlock()
}
cn, err := nm.GetContainerNodes(p, binCnr)
cn, err := nm.ContainerNodes(p, binCnr)
if err != nil {
return nil, fmt.Errorf("could not get container nodes: %w", err)
}
@ -85,15 +85,15 @@ func (b *netMapBuilder) BuildPlacement(cnr cid.ID, obj *oid.ID, p *netmapSDK.Pla
return BuildObjectPlacement(nm, cn, obj)
}
func BuildObjectPlacement(nm *netmapSDK.Netmap, cnrNodes netmapSDK.ContainerNodes, id *oid.ID) ([]netmapSDK.Nodes, error) {
func BuildObjectPlacement(nm *netmapSDK.NetMap, cnrNodes [][]netmapSDK.NodeInfo, id *oid.ID) ([][]netmapSDK.NodeInfo, error) {
if id == nil {
return cnrNodes.Replicas(), nil
return cnrNodes, nil
}
binObj := make([]byte, sha256.Size)
id.Encode(binObj)
on, err := nm.GetPlacementVectors(cnrNodes, binObj)
on, err := nm.PlacementVectors(cnrNodes, binObj)
if err != nil {
return nil, fmt.Errorf("could not get placement vectors for object: %w", err)
}
@ -102,8 +102,15 @@ func BuildObjectPlacement(nm *netmapSDK.Netmap, cnrNodes netmapSDK.ContainerNode
}
// FlattenNodes appends each row to the flat list.
func FlattenNodes(ns []netmapSDK.Nodes) netmapSDK.Nodes {
result := make(netmapSDK.Nodes, 0, len(ns))
func FlattenNodes(ns [][]netmapSDK.NodeInfo) []netmapSDK.NodeInfo {
var sz int
for i := range ns {
sz += len(ns[i])
}
result := make([]netmapSDK.NodeInfo, 0, sz)
for i := range ns {
result = append(result, ns[i]...)
}

View file

@ -20,7 +20,7 @@ type Builder interface {
//
// Must return all container nodes if object identifier
// is nil.
BuildPlacement(cid.ID, *oid.ID, *netmap.PlacementPolicy) ([]netmap.Nodes, error)
BuildPlacement(cid.ID, *oid.ID, netmap.PlacementPolicy) ([][]netmap.NodeInfo, error)
}
// Option represents placement traverser option.
@ -31,7 +31,7 @@ type Option func(*cfg)
type Traverser struct {
mtx *sync.RWMutex
vectors []netmap.Nodes
vectors [][]netmap.NodeInfo
rem []int
}
@ -78,7 +78,7 @@ func NewTraverser(opts ...Option) (*Traverser, error) {
return nil, fmt.Errorf("%s: %w", invalidOptsMsg, errNilPolicy)
}
ns, err := cfg.builder.BuildPlacement(cfg.cnr, cfg.obj, cfg.policy)
ns, err := cfg.builder.BuildPlacement(cfg.cnr, cfg.obj, *cfg.policy)
if err != nil {
return nil, fmt.Errorf("could not build placement: %w", err)
}
@ -88,12 +88,12 @@ func NewTraverser(opts ...Option) (*Traverser, error) {
ns = flatNodes(ns)
rem = []int{int(*cfg.flatSuccess)}
} else {
rs := cfg.policy.Replicas()
rem = make([]int, 0, len(rs))
replNum := cfg.policy.NumberOfReplicas()
rem = make([]int, 0, replNum)
for i := range rs {
for i := 0; i < replNum; i++ {
if cfg.trackCopies {
rem = append(rem, int(rs[i].Count()))
rem = append(rem, int(cfg.policy.ReplicaNumberByIndex(i)))
} else {
rem = append(rem, -1)
}
@ -107,18 +107,18 @@ func NewTraverser(opts ...Option) (*Traverser, error) {
}, nil
}
func flatNodes(ns []netmap.Nodes) []netmap.Nodes {
func flatNodes(ns [][]netmap.NodeInfo) [][]netmap.NodeInfo {
sz := 0
for i := range ns {
sz += len(ns[i])
}
flat := make(netmap.Nodes, 0, sz)
flat := make([]netmap.NodeInfo, 0, sz)
for i := range ns {
flat = append(flat, ns[i]...)
}
return []netmap.Nodes{flat}
return [][]netmap.NodeInfo{flat}
}
// Node is a descriptor of storage node with information required for intra-container communication.
@ -161,7 +161,7 @@ func (t *Traverser) Next() []Node {
nodes := make([]Node, count)
for i := 0; i < count; i++ {
err := nodes[i].addresses.FromIterator(t.vectors[0][i])
err := nodes[i].addresses.FromIterator(network.NodeEndpointsIterator(t.vectors[0][i]))
if err != nil {
return nil
}

View file

@ -4,6 +4,7 @@ import (
"strconv"
"testing"
netmapcore "github.com/nspcc-dev/neofs-node/pkg/core/netmap"
"github.com/nspcc-dev/neofs-node/pkg/network"
"github.com/nspcc-dev/neofs-sdk-go/container"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
@ -13,24 +14,24 @@ import (
)
type testBuilder struct {
vectors []netmap.Nodes
vectors [][]netmap.NodeInfo
}
func (b testBuilder) BuildPlacement(cid.ID, *oid.ID, *netmap.PlacementPolicy) ([]netmap.Nodes, error) {
func (b testBuilder) BuildPlacement(cid.ID, *oid.ID, netmap.PlacementPolicy) ([][]netmap.NodeInfo, error) {
return b.vectors, nil
}
func testNode(v uint32) (n netmap.NodeInfo) {
n.SetAddresses("/ip4/0.0.0.0/tcp/" + strconv.Itoa(int(v)))
n.SetNetworkEndpoints("/ip4/0.0.0.0/tcp/" + strconv.Itoa(int(v)))
return n
}
func copyVectors(v []netmap.Nodes) []netmap.Nodes {
vc := make([]netmap.Nodes, 0, len(v))
func copyVectors(v [][]netmap.NodeInfo) [][]netmap.NodeInfo {
vc := make([][]netmap.NodeInfo, 0, len(v))
for i := range v {
ns := make(netmap.Nodes, len(v[i]))
ns := make([]netmap.NodeInfo, len(v[i]))
copy(ns, v[i])
vc = append(vc, ns)
@ -39,9 +40,9 @@ func copyVectors(v []netmap.Nodes) []netmap.Nodes {
return vc
}
func testPlacement(t *testing.T, ss, rs []int) ([]netmap.Nodes, *container.Container) {
nodes := make([]netmap.Nodes, 0, len(rs))
replicas := make([]netmap.Replica, 0, len(rs))
func testPlacement(t *testing.T, ss, rs []int) ([][]netmap.NodeInfo, *container.Container) {
nodes := make([][]netmap.NodeInfo, 0, len(rs))
replicas := make([]netmap.ReplicaDescriptor, 0, len(rs))
num := uint32(0)
for i := range ss {
@ -52,24 +53,24 @@ func testPlacement(t *testing.T, ss, rs []int) ([]netmap.Nodes, *container.Conta
num++
}
nodes = append(nodes, netmap.NodesFromInfo(ns))
nodes = append(nodes, ns)
var s netmap.Replica
s.SetCount(uint32(rs[i]))
var rd netmap.ReplicaDescriptor
rd.SetNumberOfObjects(uint32(rs[i]))
replicas = append(replicas, s)
replicas = append(replicas, rd)
}
policy := new(netmap.PlacementPolicy)
policy.SetReplicas(replicas...)
policy.AddReplicas(replicas...)
return nodes, container.New(container.WithPolicy(policy))
}
func assertSameAddress(t *testing.T, ni *netmap.NodeInfo, addr network.AddressGroup) {
func assertSameAddress(t *testing.T, ni netmap.NodeInfo, addr network.AddressGroup) {
var netAddr network.AddressGroup
err := netAddr.FromIterator(ni)
err := netAddr.FromIterator(netmapcore.Node(ni))
require.NoError(t, err)
require.True(t, netAddr.Intersects(addr))
}
@ -96,7 +97,7 @@ func TestTraverserObjectScenarios(t *testing.T) {
require.Len(t, addrs, len(nodes[i]))
for j, n := range nodes[i] {
assertSameAddress(t, n.NodeInfo, addrs[j].Addresses())
assertSameAddress(t, n, addrs[j].Addresses())
}
}
@ -127,7 +128,7 @@ func TestTraverserObjectScenarios(t *testing.T) {
var n network.AddressGroup
err = n.FromIterator(nodes[1][0])
err = n.FromIterator(netmapcore.Node(nodes[1][0]))
require.NoError(t, err)
require.Equal(t, []Node{{addresses: n}}, tr.Next())
@ -153,7 +154,7 @@ func TestTraverserObjectScenarios(t *testing.T) {
require.Len(t, addrs, replicas[curVector])
for j := range addrs {
assertSameAddress(t, nodes[curVector][i+j].NodeInfo, addrs[j].Addresses())
assertSameAddress(t, nodes[curVector][i+j], addrs[j].Addresses())
}
}
@ -185,7 +186,7 @@ func TestTraverserObjectScenarios(t *testing.T) {
tr, err := NewTraverser(
ForContainer(cnr),
UseBuilder(&testBuilder{
vectors: []netmap.Nodes{{nodes[1][1]}}, // single node (local)
vectors: [][]netmap.NodeInfo{{nodes[1][1]}}, // single node (local)
}),
SuccessAfter(1),
)

View file

@ -45,9 +45,17 @@ func (p *Policer) processObject(ctx context.Context, addr oid.Address) {
}
policy := cnr.PlacementPolicy()
if policy == nil {
p.log.Error("missing placement policy in container",
zap.Stringer("cid", idCnr),
)
return
}
obj := addr.Object()
nn, err := p.placementBuilder.BuildPlacement(idCnr, &obj, policy)
nn, err := p.placementBuilder.BuildPlacement(idCnr, &obj, *policy)
if err != nil {
p.log.Error("could not build placement vector for object",
zap.String("error", err.Error()),
@ -56,7 +64,6 @@ func (p *Policer) processObject(ctx context.Context, addr oid.Address) {
return
}
replicas := policy.Replicas()
c := &processPlacementContext{
Context: ctx,
}
@ -76,7 +83,7 @@ func (p *Policer) processObject(ctx context.Context, addr oid.Address) {
default:
}
p.processNodes(c, addr, nn[i], replicas[i].Count(), checkedNodes)
p.processNodes(c, addr, nn[i], policy.ReplicaNumberByIndex(i), checkedNodes)
}
if !c.needLocalCopy {
@ -95,7 +102,7 @@ type processPlacementContext struct {
}
func (p *Policer) processNodes(ctx *processPlacementContext, addr oid.Address,
nodes netmap.Nodes, shortage uint32, checkedNodes nodeCache) {
nodes []netmap.NodeInfo, shortage uint32, checkedNodes nodeCache) {
prm := new(headsvc.RemoteHeadPrm).WithObjectAddress(addr)
for i := 0; shortage > 0 && i < len(nodes); i++ {
@ -110,7 +117,8 @@ func (p *Policer) processNodes(ctx *processPlacementContext, addr oid.Address,
shortage--
} else {
if hasReplica, checked := checkedNodes[nodes[i].ID]; checked {
nodeID := nodes[i].Hash()
if hasReplica, checked := checkedNodes[nodeID]; checked {
if hasReplica {
// node already contains replica, no need to replicate
nodes = append(nodes[:i], nodes[i+1:]...)
@ -123,7 +131,7 @@ func (p *Policer) processNodes(ctx *processPlacementContext, addr oid.Address,
callCtx, cancel := context.WithTimeout(ctx, p.headTimeout)
_, err := p.remoteHeader.Head(callCtx, prm.WithNodeInfo(nodes[i].NodeInfo))
_, err := p.remoteHeader.Head(callCtx, prm.WithNodeInfo(nodes[i]))
cancel()
@ -133,7 +141,7 @@ func (p *Policer) processNodes(ctx *processPlacementContext, addr oid.Address,
}
if client.IsErrObjectNotFound(err) {
checkedNodes[nodes[i].ID] = false
checkedNodes[nodeID] = false
continue
}
@ -144,7 +152,7 @@ func (p *Policer) processNodes(ctx *processPlacementContext, addr oid.Address,
)
} else {
shortage--
checkedNodes[nodes[i].ID] = true
checkedNodes[nodeID] = true
}
}

View file

@ -52,7 +52,7 @@ func (p *Replicator) HandleTask(ctx context.Context, task *Task, res TaskResult)
callCtx, cancel := context.WithTimeout(ctx, p.putTimeout)
err = p.remoteSender.PutObject(callCtx, prm.WithNodeInfo(task.nodes[i].NodeInfo))
err = p.remoteSender.PutObject(callCtx, prm.WithNodeInfo(task.nodes[i]))
cancel()
@ -65,7 +65,7 @@ func (p *Replicator) HandleTask(ctx context.Context, task *Task, res TaskResult)
task.quantity--
res.SubmitSuccessfulReplication(task.nodes[i].ID)
res.SubmitSuccessfulReplication(task.nodes[i].Hash())
}
}
}

View file

@ -11,7 +11,7 @@ type Task struct {
addr oid.Address
nodes netmap.Nodes
nodes []netmap.NodeInfo
}
// WithCopiesNumber sets number of copies to replicate.
@ -33,7 +33,7 @@ func (t *Task) WithObjectAddress(v oid.Address) *Task {
}
// WithNodes sets a list of potential object holders.
func (t *Task) WithNodes(v netmap.Nodes) *Task {
func (t *Task) WithNodes(v []netmap.NodeInfo) *Task {
if t != nil {
t.nodes = v
}

View file

@ -56,6 +56,21 @@ func NewManagerBuilder(prm ManagersPrm, opts ...MngOption) ManagerBuilder {
}
}
// implements Server on apiNetmap.NodeInfo
type nodeServer apiNetmap.NodeInfo
func (x nodeServer) PublicKey() []byte {
return (apiNetmap.NodeInfo)(x).PublicKey()
}
func (x nodeServer) IterateAddresses(f func(string) bool) {
(apiNetmap.NodeInfo)(x).IterateNetworkEndpoints(f)
}
func (x nodeServer) NumberOfAddresses() int {
return (apiNetmap.NodeInfo)(x).NumberOfNetworkEndpoints()
}
// BuildManagers sorts nodes in NetMap with HRW algorithms and
// takes the next node after the current one as the only manager.
func (mb *managerBuilder) BuildManagers(epoch uint64, p reputation.PeerID) ([]ServerInfo, error) {
@ -69,10 +84,12 @@ func (mb *managerBuilder) BuildManagers(epoch uint64, p reputation.PeerID) ([]Se
return nil, err
}
// make a copy to keep order consistency of the origin netmap after sorting
nodes := make([]apiNetmap.Node, len(nm.Nodes))
nmNodes := nm.Nodes()
copy(nodes, nm.Nodes)
// make a copy to keep order consistency of the origin netmap after sorting
nodes := make([]apiNetmap.NodeInfo, len(nmNodes))
copy(nodes, nmNodes)
hrw.SortSliceByValue(nodes, epoch)
@ -84,7 +101,7 @@ func (mb *managerBuilder) BuildManagers(epoch uint64, p reputation.PeerID) ([]Se
managerIndex = 0
}
return []ServerInfo{nodes[managerIndex]}, nil
return []ServerInfo{nodeServer(nodes[managerIndex])}, nil
}
}

View file

@ -8,104 +8,63 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/netmap"
)
const (
pairSeparator = "/"
keyValueSeparator = ":"
)
const keyValueSeparator = ":"
var (
errEmptyChain = errors.New("empty attribute chain")
errNonUniqueBucket = errors.New("attributes must contain unique keys")
errUnexpectedKey = errors.New("attributes contain unexpected key")
)
// ParseV2Attributes parses strings like "K1:V1/K2:V2/K3:V3" into netmap
// attributes. Supports escaped symbols "\:", "\/" and "\\".
func ParseV2Attributes(attrs []string, excl []string) ([]netmap.NodeAttribute, error) {
restricted := make(map[string]struct{}, len(excl))
for i := range excl {
restricted[excl[i]] = struct{}{}
}
cache := make(map[string]*netmap.NodeAttribute, len(attrs))
result := make([]netmap.NodeAttribute, 0, len(attrs))
// ReadNodeAttributes parses node attributes from list of string in "Key:Value" format
// and writes them into netmap.NodeInfo instance. Supports escaped symbols
// "\:", "\/" and "\\".
func ReadNodeAttributes(dst *netmap.NodeInfo, attrs []string) error {
cache := make(map[string]struct{}, len(attrs))
for i := range attrs {
line := strings.Trim(attrs[i], pairSeparator)
line = replaceEscaping(line, false) // replaced escaped symbols with non-printable symbols
chain := strings.Split(line, pairSeparator)
if len(chain) == 0 {
return nil, errEmptyChain
line := replaceEscaping(attrs[i], false) // replaced escaped symbols with non-printable symbols
words := strings.Split(line, keyValueSeparator)
if len(words) != 2 {
return errors.New("missing attribute key and/or value")
}
var parentKey string // backtrack parents in next pairs
for j := range chain {
pair := strings.Split(chain[j], keyValueSeparator)
if len(pair) != 2 {
return nil, fmt.Errorf("incorrect attribute pair %s", chain[j])
}
key := pair[0]
value := pair[1]
attribute, present := cache[key]
if present && attribute.Value() != value {
return nil, errNonUniqueBucket
}
if _, ok := restricted[key]; ok {
return nil, errUnexpectedKey
}
if !present {
result = append(result, netmap.NodeAttribute{})
attribute = &result[len(result)-1]
cache[key] = attribute
// replace non-printable symbols with escaped symbols without escape character
key = replaceEscaping(key, true)
value = replaceEscaping(value, true)
attribute.SetKey(key)
attribute.SetValue(value)
}
if parentKey != "" {
parentKeys := attribute.ParentKeys()
if !hasString(parentKeys, parentKey) {
attribute.SetParentKeys(append(parentKeys, parentKey)...)
}
}
parentKey = key
_, ok := cache[words[0]]
if ok {
return fmt.Errorf("duplicated keys %s", words[0])
}
cache[words[0]] = struct{}{}
// replace non-printable symbols with escaped symbols without escape character
words[0] = replaceEscaping(words[0], true)
words[1] = replaceEscaping(words[1], true)
fmt.Println(words[0], words[1])
if words[0] == "" {
return errors.New("empty key")
} else if words[1] == "" {
return errors.New("empty value")
}
dst.SetAttribute(words[0], words[1])
}
return result, nil
return nil
}
func replaceEscaping(target string, rollback bool) (s string) {
const escChar = `\`
var (
oldPairSep = escChar + pairSeparator
oldKVSep = escChar + keyValueSeparator
oldEsc = escChar + escChar
newPairSep = string(uint8(1))
newKVSep = string(uint8(2))
newEsc = string(uint8(3))
oldKVSep = escChar + keyValueSeparator
oldEsc = escChar + escChar
newKVSep = string(uint8(2))
newEsc = string(uint8(3))
)
if rollback {
oldPairSep, oldKVSep, oldEsc = newPairSep, newKVSep, newEsc
newPairSep = pairSeparator
oldKVSep, oldEsc = newKVSep, newEsc
newKVSep = keyValueSeparator
newEsc = escChar
}
s = strings.ReplaceAll(target, oldEsc, newEsc)
s = strings.ReplaceAll(s, oldPairSep, newPairSep)
s = strings.ReplaceAll(s, oldKVSep, newKVSep)
return

View file

@ -4,117 +4,96 @@ import (
"testing"
"github.com/nspcc-dev/neofs-node/pkg/util/attributes"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/stretchr/testify/require"
)
func TestParseV2Attributes(t *testing.T) {
t.Run("empty", func(t *testing.T) {
attrs, err := attributes.ParseV2Attributes(nil, nil)
require.NoError(t, err)
require.Len(t, attrs, 0)
func testAttributeMap(t *testing.T, mSrc, mExp map[string]string) {
var node netmap.NodeInfo
s := make([]string, 0, len(mSrc))
for k, v := range mSrc {
s = append(s, k+":"+v)
}
err := attributes.ReadNodeAttributes(&node, s)
require.NoError(t, err)
if mExp == nil {
mExp = mSrc
}
node.IterateAttributes(func(key, value string) {
v, ok := mExp[key]
require.True(t, ok)
require.Equal(t, value, v)
delete(mExp, key)
})
t.Run("non unique bucket keys", func(t *testing.T) {
good := []string{
"StorageType:HDD/RPM:7200",
"StorageType:HDD/SMR:True",
}
_, err := attributes.ParseV2Attributes(good, nil)
require.NoError(t, err)
require.Empty(t, mExp)
}
bad := append(good, "StorageType:SSD/Cell:QLC")
_, err = attributes.ParseV2Attributes(bad, nil)
func TestParseV2Attributes(t *testing.T) {
t.Run("empty", func(t *testing.T) {
var node netmap.NodeInfo
err := attributes.ReadNodeAttributes(&node, nil)
require.NoError(t, err)
require.Zero(t, node.NumberOfAttributes())
})
t.Run("empty key and/or value", func(t *testing.T) {
var node netmap.NodeInfo
err := attributes.ReadNodeAttributes(&node, []string{
":HDD",
})
require.Error(t, err)
err = attributes.ReadNodeAttributes(&node, []string{
"StorageType:",
})
require.Error(t, err)
err = attributes.ReadNodeAttributes(&node, []string{
":",
})
require.Error(t, err)
})
t.Run("non-unique keys", func(t *testing.T) {
var node netmap.NodeInfo
err := attributes.ReadNodeAttributes(&node, []string{
"StorageType:HDD",
"StorageType:HDD",
})
require.Error(t, err)
})
t.Run("malformed", func(t *testing.T) {
_, err := attributes.ParseV2Attributes([]string{"..."}, nil)
var node netmap.NodeInfo
err := attributes.ReadNodeAttributes(&node, []string{"..."})
require.Error(t, err)
_, err = attributes.ParseV2Attributes([]string{"a:b", ""}, nil)
err = attributes.ReadNodeAttributes(&node, []string{"a:b", ""})
require.Error(t, err)
_, err = attributes.ParseV2Attributes([]string{"//"}, nil)
require.Error(t, err)
})
t.Run("unexpected", func(t *testing.T) {
unexpectedBucket := []string{
"Location:Europe/City:Moscow",
"Price:100",
}
_, err := attributes.ParseV2Attributes(unexpectedBucket, []string{"Price"})
err = attributes.ReadNodeAttributes(&node, []string{"//"})
require.Error(t, err)
})
t.Run("correct", func(t *testing.T) {
from := []string{
"/Location:Europe/Country:Sweden/City:Stockholm",
"/StorageType:HDD/RPM:7200",
}
attrs, err := attributes.ParseV2Attributes(from, nil)
require.NoError(t, err)
require.Len(t, attrs, 5)
testAttributeMap(t, map[string]string{
"Location": "Europe",
"StorageType": "HDD",
}, nil)
})
t.Run("escape characters", func(t *testing.T) {
from := []string{
`/K\:ey1:V\/alue\\/Ke\/y2:Va\:lue`,
}
attrs, err := attributes.ParseV2Attributes(from, nil)
require.NoError(t, err)
require.Equal(t, `K:ey1`, attrs[0].Key())
require.Equal(t, `V/alue\`, attrs[0].Value())
require.Equal(t, `Ke/y2`, attrs[1].Key())
require.Equal(t, `Va:lue`, attrs[1].Value())
})
t.Run("same attributes", func(t *testing.T) {
from := []string{"/a:b", "/a:b"}
attrs, err := attributes.ParseV2Attributes(from, nil)
require.NoError(t, err)
require.Len(t, attrs, 1)
t.Run("with escape characters", func(t *testing.T) {
from = []string{`/a\::b\/`, `/a\::b\/`}
attrs, err := attributes.ParseV2Attributes(from, nil)
require.NoError(t, err)
require.Len(t, attrs, 1)
testAttributeMap(t, map[string]string{
`K\:ey1`: `V\/alue`,
`Ke\/y2`: `Va\:lue`,
}, map[string]string{
`K:ey1`: `V\/alue`,
`Ke\/y2`: `Va:lue`,
})
})
t.Run("multiple parents", func(t *testing.T) {
from := []string{
"/parent1:x/child:x",
"/parent2:x/child:x",
"/parent2:x/child:x/subchild:x",
}
attrs, err := attributes.ParseV2Attributes(from, nil)
require.NoError(t, err)
var flag bool
for _, attr := range attrs {
if attr.Key() == "child" {
flag = true
require.Equal(t, []string{"parent1", "parent2"}, attr.ParentKeys())
}
}
require.True(t, flag)
})
t.Run("consistent order in chain", func(t *testing.T) {
from := []string{"/a:1/b:2/c:3"}
for i := 0; i < 10000; i++ {
attrs, err := attributes.ParseV2Attributes(from, nil)
require.NoError(t, err)
require.Equal(t, "a", attrs[0].Key())
require.Equal(t, "1", attrs[0].Value())
require.Equal(t, "b", attrs[1].Key())
require.Equal(t, "2", attrs[1].Value())
require.Equal(t, "c", attrs[2].Key())
require.Equal(t, "3", attrs[2].Value())
}
})
}