From 0f32605c05f7813beb932877918fb33a2faf102c Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Wed, 11 Dec 2024 00:07:27 +0300 Subject: [PATCH] ir: maintain container placement in container contract After https://github.com/nspcc-dev/neofs-contract/pull/438 Container contract now has API for container-side placement/verification operations. But building placement vectors and updating when needed is still a complex operation that should be done on a Go application side. This commit adds such a responsibility for Alphabet nodes. For simplicity, updating is done every epoch and once for _every_ container if netmap has been changed. Additional optimization can be considered. Signed-off-by: Pavel Karpy --- go.sum | 4 +- .../processors/netmap/cleanup_table.go | 16 +++- .../processors/netmap/process_epoch.go | 80 ++++++++++++++++++- 3 files changed, 95 insertions(+), 5 deletions(-) diff --git a/go.sum b/go.sum index 05b08c27f9..41a11da74b 100644 --- a/go.sum +++ b/go.sum @@ -178,8 +178,8 @@ github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240729160116-d8e3e57f88f2 h1:tv github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240729160116-d8e3e57f88f2/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY= github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea h1:mK0EMGLvunXcFyq7fBURS/CsN4MH+4nlYiqn6pTwWAU= github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea/go.mod h1:YzhD4EZmC9Z/PNyd7ysC7WXgIgURc9uCG1UWDeV027Y= -github.com/nspcc-dev/neofs-contract v0.20.0 h1:ARE/3mSN+P9qi/10NBsf7QyPiYrvnxeEgYUN13vHRlo= -github.com/nspcc-dev/neofs-contract v0.20.0/go.mod h1:YxtKYE/5cMNiqwWcQWzeizbB9jizauLni+p8wXxfhsQ= +github.com/nspcc-dev/neofs-contract v0.20.1-0.20241203222542-f5a31fd10a31 h1:Lsf6FQ2f2OUAG0D8MobP5k2thsAiCto8+RxsLJ8d//w= +github.com/nspcc-dev/neofs-contract v0.20.1-0.20241203222542-f5a31fd10a31/go.mod h1:wRKbLECekUTmJRtVH0PhTtiTnEa5uvCU0VRWOq9PY00= github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12.0.20240809202351-256513c1b29b h1:/7jXQP5pf+M0kRFC1gg5GEdTPkvotpMHxjSXIbMZaGQ= github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12.0.20240809202351-256513c1b29b/go.mod h1:ewV84r1NACvoBfbKQKzRLUun+Xn5+z9JVqsuCVgv9xI= github.com/nspcc-dev/rfc6979 v0.2.3 h1:QNVykGZ3XjFwM/88rGfV3oj4rKNBy+nYI6jM7q19hDI= diff --git a/pkg/innerring/processors/netmap/cleanup_table.go b/pkg/innerring/processors/netmap/cleanup_table.go index 226a7d4471..d3cd023146 100644 --- a/pkg/innerring/processors/netmap/cleanup_table.go +++ b/pkg/innerring/processors/netmap/cleanup_table.go @@ -2,6 +2,7 @@ package netmap import ( "bytes" + "slices" "sync" "github.com/nspcc-dev/neofs-sdk-go/netmap" @@ -13,6 +14,8 @@ type ( enabled bool threshold uint64 lastAccess map[string]epochStampWithNodeInfo + + prev netmap.NetMap } epochStamp struct { @@ -36,8 +39,9 @@ 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) { +// Update cleanup table based on on-chain information about netmap. Returned +// value indicates if the composition of network map memebers has changed. +func (c *cleanupTable) update(snapshot netmap.NetMap, now uint64) bool { c.Lock() defer c.Unlock() @@ -64,6 +68,14 @@ func (c *cleanupTable) update(snapshot netmap.NetMap, now uint64) { } c.lastAccess = newMap + + // order is expected to be the same from epoch to epoch + mapChanged := !slices.EqualFunc(c.prev.Nodes(), nmNodes, func(i1 netmap.NodeInfo, i2 netmap.NodeInfo) bool { + return bytes.Equal(i1.PublicKey(), i2.PublicKey()) + }) + c.prev = snapshot + + return mapChanged } // updates last access time of the netmap node by string public key. diff --git a/pkg/innerring/processors/netmap/process_epoch.go b/pkg/innerring/processors/netmap/process_epoch.go index 01e63de45c..b8ca7ebb06 100644 --- a/pkg/innerring/processors/netmap/process_epoch.go +++ b/pkg/innerring/processors/netmap/process_epoch.go @@ -1,11 +1,15 @@ package netmap import ( + "fmt" + "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/audit" "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/governance" "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/settlement" cntClient "github.com/nspcc-dev/neofs-node/pkg/morph/client/container" netmapEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/netmap" + cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + "github.com/nspcc-dev/neofs-sdk-go/netmap" "go.uber.org/zap" ) @@ -65,7 +69,15 @@ func (np *Processor) processNewEpoch(ev netmapEvent.NewEpoch) { } } - np.netmapSnapshot.update(*networkMap, epoch) + if np.netmapSnapshot.update(*networkMap, epoch) { + l.Debug("updating placements in Container contract...") + err = np.updatePlacementInContract(*networkMap, l) + if err != nil { + l.Error("can't update placements in Container contract", zap.Error(err)) + } else { + l.Debug("updated placements in Container contract") + } + } np.handleCleanupTick(netmapCleanupTick{epoch: epoch, txHash: ev.TxHash()}) np.handleNewAudit(audit.NewAuditStartEvent(epoch)) np.handleAuditSettlements(settlement.NewAuditEvent(epoch)) @@ -73,6 +85,72 @@ func (np *Processor) processNewEpoch(ev netmapEvent.NewEpoch) { np.handleNotaryDeposit(ev) } +func (np *Processor) updatePlacementInContract(nm netmap.NetMap, l *zap.Logger) error { + // TODO: https://github.com/nspcc-dev/neofs-node/issues/3045 + cids, err := np.containerWrp.List(nil) + if err != nil { + return fmt.Errorf("can't get containers list: %w", err) + } + + for _, cID := range cids { + l := l.With(zap.String("cid", cID.String())) + l.Debug("updating container placement in Container contract...") + + cnr, err := np.containerWrp.Get(cID[:]) + if err != nil { + l.Error("can't get container to update its placement in Container contract", zap.Error(err)) + continue + } + + policy := cnr.Value.PlacementPolicy() + + vectors, err := nm.ContainerNodes(policy, cID) + if err != nil { + l.Error("can't build placement vectors for update in Container contract", zap.Error(err)) + continue + } + + err = np.putPlacementVectors(cID, policy, vectors) + if err != nil { + l.Error("can't put placement vectors to Container contract", zap.Error(err)) + continue + } + + l.Debug("updated container placement in Container contract") + } + + return nil +} + +func (np *Processor) putPlacementVectors(cID cid.ID, p netmap.PlacementPolicy, vectors [][]netmap.NodeInfo) error { + replicas := make([]uint32, 0, p.NumberOfReplicas()) + + for i, vector := range vectors { + replicas = append(replicas, p.ReplicaNumberByIndex(i)) + + err := np.containerWrp.AddNextEpochNodes(cID[:], i, pubKeys(vector)) + if err != nil { + return fmt.Errorf("can't add %d placement vector to Container contract: %w", i, err) + } + } + + err := np.containerWrp.CommitContainerListUpdate(cID[:], replicas) + if err != nil { + return fmt.Errorf("can't commit container list to Container contract: %w", err) + } + + return nil +} + +func pubKeys(nodes []netmap.NodeInfo) [][]byte { + res := make([][]byte, 0, len(nodes)) + for _, node := range nodes { + res = append(res, node.PublicKey()) + } + + return res +} + // Process new epoch tick by invoking new epoch method in network map contract. func (np *Processor) processNewEpochTick() { if !np.alphabetState.IsAlphabet() {