Skip to content

Commit

Permalink
ir: maintain container placement in container contract
Browse files Browse the repository at this point in the history
After nspcc-dev/neofs-contract#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 <carpawell@nspcc.ru>
  • Loading branch information
carpawell committed Dec 11, 2024
1 parent e987e2e commit 0f32605
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 5 deletions.
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
16 changes: 14 additions & 2 deletions pkg/innerring/processors/netmap/cleanup_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package netmap

import (
"bytes"
"slices"
"sync"

"github.com/nspcc-dev/neofs-sdk-go/netmap"
Expand All @@ -13,6 +14,8 @@ type (
enabled bool
threshold uint64
lastAccess map[string]epochStampWithNodeInfo

prev netmap.NetMap
}

epochStamp struct {
Expand All @@ -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()

Expand All @@ -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.
Expand Down
80 changes: 79 additions & 1 deletion pkg/innerring/processors/netmap/process_epoch.go
Original file line number Diff line number Diff line change
@@ -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"
)

Expand Down Expand Up @@ -65,14 +69,88 @@ 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))
np.handleAlphabetSync(governance.NewSyncEvent(ev.TxHash()))
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() {
Expand Down

0 comments on commit 0f32605

Please sign in to comment.