diff --git a/cmd/neofs-cli/internal/client/client.go b/cmd/neofs-cli/internal/client/client.go index 3ae06a03e..28a655fe2 100644 --- a/cmd/neofs-cli/internal/client/client.go +++ b/cmd/neofs-cli/internal/client/client.go @@ -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. diff --git a/cmd/neofs-cli/modules/container/create.go b/cmd/neofs-cli/modules/container/create.go index 4084381c7..bf7336fbe 100644 --- a/cmd/neofs-cli/modules/container/create.go +++ b/cmd/neofs-cli/modules/container/create.go @@ -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") diff --git a/cmd/neofs-cli/modules/container/get.go b/cmd/neofs-cli/modules/container/get.go index 2ec6dcb12..1bdd19221 100644 --- a/cmd/neofs-cli/modules/container/get.go +++ b/cmd/neofs-cli/modules/container/get.go @@ -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) { diff --git a/cmd/neofs-cli/modules/netmap/netinfo.go b/cmd/neofs-cli/modules/netmap/netinfo.go index f02c6800c..9e230aa66 100644 --- a/cmd/neofs-cli/modules/netmap/netinfo.go +++ b/cmd/neofs-cli/modules/netmap/netinfo.go @@ -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) -} diff --git a/cmd/neofs-cli/modules/netmap/nodeinfo.go b/cmd/neofs-cli/modules/netmap/nodeinfo.go index ee397086c..0f44b7c5e 100644 --- a/cmd/neofs-cli/modules/netmap/nodeinfo.go +++ b/cmd/neofs-cli/modules/netmap/nodeinfo.go @@ -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 = "" + 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) + }) } diff --git a/cmd/neofs-node/attributes.go b/cmd/neofs-node/attributes.go index d4f962dbe..cf1b05244 100644 --- a/cmd/neofs-node/attributes.go +++ b/cmd/neofs-node/attributes.go @@ -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))) } diff --git a/cmd/neofs-node/cache.go b/cmd/neofs-node/cache.go index 84952465a..b65f214c6 100644 --- a/cmd/neofs-node/cache.go +++ b/cmd/neofs-node/cache.go @@ -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) { diff --git a/cmd/neofs-node/config.go b/cmd/neofs-node/config.go index 6ce5772e5..dcae1dd3a 100644 --- a/cmd/neofs-node/config.go +++ b/cmd/neofs-node/config.go @@ -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) } diff --git a/cmd/neofs-node/container.go b/cmd/neofs-node/container.go index 4e708089e..0b30af5fc 100644 --- a/cmd/neofs-node/container.go +++ b/cmd/neofs-node/container.go @@ -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 } } diff --git a/cmd/neofs-node/netmap.go b/cmd/neofs-node/netmap.go index f79d7145f..8f5c58646 100644 --- a/cmd/neofs-node/netmap.go +++ b/cmd/neofs-node/netmap.go @@ -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 } diff --git a/cmd/neofs-node/object.go b/cmd/neofs-node/object.go index b5a5f6651..441e5de69 100644 --- a/cmd/neofs-node/object.go +++ b/cmd/neofs-node/object.go @@ -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), diff --git a/cmd/neofs-node/reputation/intermediate/calculator.go b/cmd/neofs-node/reputation/intermediate/calculator.go index 64fb6abcb..51ac3f183 100644 --- a/cmd/neofs-node/reputation/intermediate/calculator.go +++ b/cmd/neofs-node/reputation/intermediate/calculator.go @@ -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 } diff --git a/cmd/neofs-node/reputation/local/storage.go b/cmd/neofs-node/reputation/local/storage.go index f9ef2099d..347e6464d 100644 --- a/cmd/neofs-node/reputation/local/storage.go +++ b/cmd/neofs-node/reputation/local/storage.go @@ -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)) diff --git a/go.mod b/go.mod index e582d4826..1c9484efe 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 9eb96d598..895a5b671 100644 Binary files a/go.sum and b/go.sum differ diff --git a/pkg/core/container/fmt_test.go b/pkg/core/container/fmt_test.go index 41d4d9eb8..65fc3d937 100644 --- a/pkg/core/container/fmt_test.go +++ b/pkg/core/container/fmt_test.go @@ -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)) diff --git a/pkg/core/netmap/nodes.go b/pkg/core/netmap/nodes.go new file mode 100644 index 000000000..3183a2cce --- /dev/null +++ b/pkg/core/netmap/nodes.go @@ -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 diff --git a/pkg/core/netmap/storage.go b/pkg/core/netmap/storage.go index 2ef469edc..8f72ce1fe 100644 --- a/pkg/core/netmap/storage.go +++ b/pkg/core/netmap/storage.go @@ -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) } diff --git a/pkg/innerring/processors/alphabet/process_emit.go b/pkg/innerring/processors/alphabet/process_emit.go index 51ef9f584..a06defb3a 100644 --- a/pkg/innerring/processors/alphabet/process_emit.go +++ b/pkg/innerring/processors/alphabet/process_emit.go @@ -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 { diff --git a/pkg/innerring/processors/audit/process.go b/pkg/innerring/processors/audit/process.go index c542de2df..83c974b79 100644 --- a/pkg/innerring/processors/audit/process.go +++ b/pkg/innerring/processors/audit/process.go @@ -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())) diff --git a/pkg/innerring/processors/container/process_container.go b/pkg/innerring/processors/container/process_container.go index d761f15d3..ff6bb34c6 100644 --- a/pkg/innerring/processors/container/process_container.go +++ b/pkg/innerring/processors/container/process_container.go @@ -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 diff --git a/pkg/innerring/processors/netmap/cleanup_table.go b/pkg/innerring/processors/netmap/cleanup_table.go index 3049f1f67..f7af9977d 100644 --- a/pkg/innerring/processors/netmap/cleanup_table.go +++ b/pkg/innerring/processors/netmap/cleanup_table.go @@ -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 { diff --git a/pkg/innerring/processors/netmap/cleanup_table_test.go b/pkg/innerring/processors/netmap/cleanup_table_test.go index 773d0f2ac..a3f5062e5 100644 --- a/pkg/innerring/processors/netmap/cleanup_table_test.go +++ b/pkg/innerring/processors/netmap/cleanup_table_test.go @@ -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 } diff --git a/pkg/innerring/processors/netmap/nodevalidation/locode/calls.go b/pkg/innerring/processors/netmap/nodevalidation/locode/calls.go index 1d386cc78..831792624 100644 --- a/pkg/innerring/processors/netmap/nodevalidation/locode/calls.go +++ b/pkg/innerring/processors/netmap/nodevalidation/locode/calls.go @@ -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 -} diff --git a/pkg/innerring/processors/netmap/nodevalidation/locode/calls_test.go b/pkg/innerring/processors/netmap/nodevalidation/locode/calls_test.go index 8d4d2fa30..ab17e34ba 100644 --- a/pkg/innerring/processors/netmap/nodevalidation/locode/calls_test.go +++ b/pkg/innerring/processors/netmap/nodevalidation/locode/calls_test.go @@ -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")) }) } diff --git a/pkg/innerring/processors/netmap/nodevalidation/locode/util.go b/pkg/innerring/processors/netmap/nodevalidation/locode/util.go deleted file mode 100644 index efbff4d8a..000000000 --- a/pkg/innerring/processors/netmap/nodevalidation/locode/util.go +++ /dev/null @@ -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() -} diff --git a/pkg/innerring/processors/netmap/nodevalidation/locode/validator.go b/pkg/innerring/processors/netmap/nodevalidation/locode/validator.go index 38e47c81a..639eb1651 100644 --- a/pkg/innerring/processors/netmap/nodevalidation/locode/validator.go +++ b/pkg/innerring/processors/netmap/nodevalidation/locode/validator.go @@ -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}, - }, } } diff --git a/pkg/innerring/processors/netmap/nodevalidation/maddress/calls.go b/pkg/innerring/processors/netmap/nodevalidation/maddress/calls.go index 4067c83dc..fa6afbde7 100644 --- a/pkg/innerring/processors/netmap/nodevalidation/maddress/calls.go +++ b/pkg/innerring/processors/netmap/nodevalidation/maddress/calls.go @@ -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) } diff --git a/pkg/innerring/processors/netmap/process_cleanup.go b/pkg/innerring/processors/netmap/process_cleanup.go index fa29f0f6d..0aff66fc9 100644 --- a/pkg/innerring/processors/netmap/process_cleanup.go +++ b/pkg/innerring/processors/netmap/process_cleanup.go @@ -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 { diff --git a/pkg/innerring/processors/netmap/process_epoch.go b/pkg/innerring/processors/netmap/process_epoch.go index cf7633839..edd152c84 100644 --- a/pkg/innerring/processors/netmap/process_epoch.go +++ b/pkg/innerring/processors/netmap/process_epoch.go @@ -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)) diff --git a/pkg/innerring/processors/netmap/process_peers.go b/pkg/innerring/processors/netmap/process_peers.go index 9edb87d51..c6849661d 100644 --- a/pkg/innerring/processors/netmap/process_peers.go +++ b/pkg/innerring/processors/netmap/process_peers.go @@ -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) diff --git a/pkg/innerring/rpc.go b/pkg/innerring/rpc.go index b23e0e3d1..a612d25bd 100644 --- a/pkg/innerring/rpc.go +++ b/pkg/innerring/rpc.go @@ -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) } diff --git a/pkg/innerring/settlement.go b/pkg/innerring/settlement.go index b7f01af52..dbb6a8503 100644 --- a/pkg/innerring/settlement.go +++ b/pkg/innerring/settlement.go @@ -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], }) } diff --git a/pkg/innerring/subnet.go b/pkg/innerring/subnet.go index f60d736a0..0e49a25fa 100644 --- a/pkg/innerring/subnet.go +++ b/pkg/innerring/subnet.go @@ -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) diff --git a/pkg/morph/client/netmap/add_peer.go b/pkg/morph/client/netmap/add_peer.go index b5736a9df..d42fab2b1 100644 --- a/pkg/morph/client/netmap/add_peer.go +++ b/pkg/morph/client/netmap/add_peer.go @@ -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 { diff --git a/pkg/morph/client/netmap/config.go b/pkg/morph/client/netmap/config.go index 501142635..77cf95b65 100644 --- a/pkg/morph/client/netmap/config.go +++ b/pkg/morph/client/netmap/config.go @@ -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 } } diff --git a/pkg/morph/client/netmap/netmap.go b/pkg/morph/client/netmap/netmap.go index 0af7e22c1..d099f680d 100644 --- a/pkg/morph/client/netmap/netmap.go +++ b/pkg/morph/client/netmap/netmap.go @@ -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 diff --git a/pkg/morph/client/netmap/netmap_test.go b/pkg/morph/client/netmap/netmap_test.go index 3b6a43937..bb38e207a 100644 --- a/pkg/morph/client/netmap/netmap_test.go +++ b/pkg/morph/client/netmap/netmap_test.go @@ -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{ diff --git a/pkg/morph/client/netmap/snapshot.go b/pkg/morph/client/netmap/snapshot.go index 0f6df2868..5eaed9a3e 100644 --- a/pkg/morph/client/netmap/snapshot.go +++ b/pkg/morph/client/netmap/snapshot.go @@ -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 } diff --git a/pkg/morph/client/netmap/update_state.go b/pkg/morph/client/netmap/update_state.go index 9bd83f7ff..e6c8cca71 100644 --- a/pkg/morph/client/netmap/update_state.go +++ b/pkg/morph/client/netmap/update_state.go @@ -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 { diff --git a/pkg/morph/event/netmap/update_peer.go b/pkg/morph/event/netmap/update_peer.go index 40911b5e7..e7fd5fcfd 100644 --- a/pkg/morph/event/netmap/update_peer.go +++ b/pkg/morph/event/netmap/update_peer.go @@ -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 } diff --git a/pkg/morph/event/netmap/update_peer_notary.go b/pkg/morph/event/netmap/update_peer_notary.go index 0738bbfd4..8dc22bfc5 100644 --- a/pkg/morph/event/netmap/update_peer_notary.go +++ b/pkg/morph/event/netmap/update_peer_notary.go @@ -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: diff --git a/pkg/morph/event/netmap/update_peer_test.go b/pkg/morph/event/netmap/update_peer_test.go index 0ee4072f9..aa397be7e 100644 --- a/pkg/morph/event/netmap/update_peer_test.go +++ b/pkg/morph/event/netmap/update_peer_test.go @@ -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) }) } diff --git a/pkg/network/group.go b/pkg/network/group.go index 113fd0c3b..e57c59748 100644 --- a/pkg/network/group.go +++ b/pkg/network/group.go @@ -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 diff --git a/pkg/network/validation.go b/pkg/network/validation.go index 3d2013737..061baf247 100644 --- a/pkg/network/validation.go +++ b/pkg/network/validation.go @@ -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 { diff --git a/pkg/network/validation_test.go b/pkg/network/validation_test.go index b44d10cc6..0c3859668 100644 --- a/pkg/network/validation_test.go +++ b/pkg/network/validation_test.go @@ -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 } diff --git a/pkg/services/audit/auditor/context.go b/pkg/services/audit/auditor/context.go index ef0816f78..5d296c011 100644 --- a/pkg/services/audit/auditor/context.go +++ b/pkg/services/audit/auditor/context.go @@ -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() diff --git a/pkg/services/audit/auditor/pdp.go b/pkg/services/audit/auditor/pdp.go index 22ffb1a07..afec36c24 100644 --- a/pkg/services/audit/auditor/pdp.go +++ b/pkg/services/audit/auditor/pdp.go @@ -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) diff --git a/pkg/services/audit/auditor/pop.go b/pkg/services/audit/auditor/pop.go index c9dbc3725..bc7d36894 100644 --- a/pkg/services/audit/auditor/pop.go +++ b/pkg/services/audit/auditor/pop.go @@ -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 { diff --git a/pkg/services/audit/auditor/por.go b/pkg/services/audit/auditor/por.go index 1a4c183d9..7b9c1a8ca 100644 --- a/pkg/services/audit/auditor/por.go +++ b/pkg/services/audit/auditor/por.go @@ -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())), diff --git a/pkg/services/audit/task.go b/pkg/services/audit/task.go index 93ec3a89a..737a0a12c 100644 --- a/pkg/services/audit/task.go +++ b/pkg/services/audit/task.go @@ -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 } diff --git a/pkg/services/container/announcement/load/route/placement/calls.go b/pkg/services/container/announcement/load/route/placement/calls.go index f33bc3be1..2e81a32aa 100644 --- a/pkg/services/container/announcement/load/route/placement/calls.go +++ b/pkg/services/container/announcement/load/route/placement/calls.go @@ -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])) } } diff --git a/pkg/services/container/announcement/load/route/placement/deps.go b/pkg/services/container/announcement/load/route/placement/deps.go index 53ebc9bd5..0a4527cd9 100644 --- a/pkg/services/container/announcement/load/route/placement/deps.go +++ b/pkg/services/container/announcement/load/route/placement/deps.go @@ -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) } diff --git a/pkg/services/control/server/netmap_snapshot.go b/pkg/services/control/server/netmap_snapshot.go index 29b31a5d6..d9fcfd529 100644 --- a/pkg/services/control/server/netmap_snapshot.go +++ b/pkg/services/control/server/netmap_snapshot.go @@ -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 } diff --git a/pkg/services/netmap/executor.go b/pkg/services/netmap/executor.go index 5a2ebca57..132556bb8 100644 --- a/pkg/services/netmap/executor.go +++ b/pkg/services/netmap/executor.go @@ -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) diff --git a/pkg/services/object/acl/v2/classifier.go b/pkg/services/object/acl/v2/classifier.go index d5ece10ab..13d114f8a 100644 --- a/pkg/services/object/acl/v2/classifier.go +++ b/pkg/services/object/acl/v2/classifier.go @@ -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 + } } } diff --git a/pkg/services/object/get/get_test.go b/pkg/services/object/get/get_test.go index e34b679d0..aad3686ec 100644 --- a/pkg/services/object/get/get_test.go +++ b/pkg/services/object/get/get_test.go @@ -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:], }, }, diff --git a/pkg/services/object/head/remote.go b/pkg/services/object/head/remote.go index 50ed1190b..4854ad658 100644 --- a/pkg/services/object/head/remote.go +++ b/pkg/services/object/head/remote.go @@ -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) } diff --git a/pkg/services/object/put/remote.go b/pkg/services/object/put/remote.go index e76ab3a1e..5a6273f68 100644 --- a/pkg/services/object/put/remote.go +++ b/pkg/services/object/put/remote.go @@ -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) } diff --git a/pkg/services/object/search/search_test.go b/pkg/services/object/search/search_test.go index 808478508..7ce3a4c52 100644 --- a/pkg/services/object/search/search_test.go +++ b/pkg/services/object/search/search_test.go @@ -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:], }, }, diff --git a/pkg/services/object/util/placement.go b/pkg/services/object/util/placement.go index 3e52c398e..742f007e9 100644 --- a/pkg/services/object/util/placement.go +++ b/pkg/services/object/util/placement.go @@ -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 } diff --git a/pkg/services/object_manager/placement/netmap.go b/pkg/services/object_manager/placement/netmap.go index 0c67b7470..2d581adb3 100644 --- a/pkg/services/object_manager/placement/netmap.go +++ b/pkg/services/object_manager/placement/netmap.go @@ -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]...) } diff --git a/pkg/services/object_manager/placement/traverser.go b/pkg/services/object_manager/placement/traverser.go index 3f78bcfa0..7ccf40470 100644 --- a/pkg/services/object_manager/placement/traverser.go +++ b/pkg/services/object_manager/placement/traverser.go @@ -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 } diff --git a/pkg/services/object_manager/placement/traverser_test.go b/pkg/services/object_manager/placement/traverser_test.go index d2e159f89..0dc46c05e 100644 --- a/pkg/services/object_manager/placement/traverser_test.go +++ b/pkg/services/object_manager/placement/traverser_test.go @@ -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), ) diff --git a/pkg/services/policer/check.go b/pkg/services/policer/check.go index cb8e179f9..95a3df2de 100644 --- a/pkg/services/policer/check.go +++ b/pkg/services/policer/check.go @@ -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 } } diff --git a/pkg/services/replicator/process.go b/pkg/services/replicator/process.go index 033f4fdc5..91d8fd3d0 100644 --- a/pkg/services/replicator/process.go +++ b/pkg/services/replicator/process.go @@ -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()) } } } diff --git a/pkg/services/replicator/task.go b/pkg/services/replicator/task.go index d40cde6b1..99d1c5139 100644 --- a/pkg/services/replicator/task.go +++ b/pkg/services/replicator/task.go @@ -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 } diff --git a/pkg/services/reputation/common/managers.go b/pkg/services/reputation/common/managers.go index 64f5f094d..7f45f8c7e 100644 --- a/pkg/services/reputation/common/managers.go +++ b/pkg/services/reputation/common/managers.go @@ -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 } } diff --git a/pkg/util/attributes/parser.go b/pkg/util/attributes/parser.go index 2af03486c..efb5c7985 100644 --- a/pkg/util/attributes/parser.go +++ b/pkg/util/attributes/parser.go @@ -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 diff --git a/pkg/util/attributes/parser_test.go b/pkg/util/attributes/parser_test.go index 87fcef2a0..03db0bdcf 100644 --- a/pkg/util/attributes/parser_test.go +++ b/pkg/util/attributes/parser_test.go @@ -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()) - } - }) }