Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Changelog for NeoFS Node
## [Unreleased]

### Added
- SN now supports new `getInfo` and `createV2` methods of the Container contract (#3670)
- IR now supports container creation requests submitted via new `createV2` contract method (#3670)
- IR structures containers in the contract iteratively (#3670)

### Fixed

Expand All @@ -12,6 +15,7 @@ Changelog for NeoFS Node
### Removed

### Updated
- `github.com/nspcc-dev/neofs-contract` module to `v0.25.2-0.20251124180339-40ec608b4893` (#3670)

### Updating from v0.50.1

Expand Down
15 changes: 2 additions & 13 deletions cmd/neofs-node/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,26 +471,15 @@ func (x *containersInChain) List(id user.ID) ([]cid.ID, error) {
}

func (x *containersInChain) Put(cnr containerSDK.Container, pub, sig []byte, st *session.Container) (cid.ID, error) {
data := cnr.Marshal()
d := cnr.ReadDomain()

var prm cntClient.PutPrm
prm.SetContainer(data)
prm.SetName(d.Name())
prm.SetZone(d.Zone())
prm.SetContainer(cnr)
prm.SetKey(pub)
prm.SetSignature(sig)
if st != nil {
prm.SetToken(st.Marshal())
}
if v := cnr.Attribute("__NEOFS__METAINFO_CONSISTENCY"); v == "optimistic" || v == "strict" {
prm.EnableMeta()
}
if err := x.cCli.Put(prm); err != nil {
return cid.ID{}, err
}

return cid.NewFromMarshalledContainer(data), nil
return x.cCli.Put(prm)
}

func (x *containersInChain) Delete(id cid.ID, pub, sig []byte, st *session.Container) error {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ require (
github.com/nspcc-dev/locode-db v0.8.1
github.com/nspcc-dev/neo-go v0.114.0
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea
github.com/nspcc-dev/neofs-contract v0.25.2-0.20251124164423-4fffaa262a8a
github.com/nspcc-dev/neofs-contract v0.25.2-0.20251124180339-40ec608b4893
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.16.0.20251118154818-9480da80f9ad
github.com/nspcc-dev/tzhash v1.8.3
github.com/panjf2000/ants/v2 v2.11.3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20251112080609-3c8e29c66609 h1:9j
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20251112080609-3c8e29c66609/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg=
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.25.2-0.20251124164423-4fffaa262a8a h1:BNuNJsp7vLDourjsPio/aEfuEaKzrpjiPaaq0iHsFgY=
github.com/nspcc-dev/neofs-contract v0.25.2-0.20251124164423-4fffaa262a8a/go.mod h1:uqTaIPQCIouOyflW4aRQAyl4w88GhxYosJ74wvS4AEQ=
github.com/nspcc-dev/neofs-contract v0.25.2-0.20251124180339-40ec608b4893 h1:sh25Y5GMLL9ixlcJdbktHkFUY3nyK9DeLY5EBaEQDwQ=
github.com/nspcc-dev/neofs-contract v0.25.2-0.20251124180339-40ec608b4893/go.mod h1:CYX51uP2pNBCK7Q0ygD1LNsoFSHbB2F5luaBrluFkUo=
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.16.0.20251118154818-9480da80f9ad h1:hWJBAWUqGHqvtInPljzfGP2TIDdMA+jYMX5/DCCyJjA=
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.16.0.20251118154818-9480da80f9ad/go.mod h1:MbgvTuiYY3uG+iMqIcvojmNHfZcxGNz4U+EKNBKPHzE=
github.com/nspcc-dev/rfc6979 v0.2.4 h1:NBgsdCjhLpEPJZqmC9rciMZDcSY297po2smeaRjw57k=
Expand Down
13 changes: 10 additions & 3 deletions pkg/innerring/innerring.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ type (
withoutMainNet bool

// runtime processors
netmapProcessor *netmap.Processor
netmapProcessor *netmap.Processor
containerProcessor *container.Processor

workers []func(context.Context)

Expand Down Expand Up @@ -229,6 +230,12 @@ func (s *Server) Start(ctx context.Context, intError chan<- error) (err error) {
go s.fsChainListener.ListenWithError(ctx, fsChainErr) // listen for neo:fs events
go s.mainnetListener.ListenWithError(ctx, mainnnetErr) // listen for neo:mainnet events

go func() {
if err := s.containerProcessor.AddContainerStructs(ctx); err != nil {
fsChainErr <- fmt.Errorf("structurize containers in the contract: %w", err)
}
}()

s.startWorkers(ctx)

return nil
Expand Down Expand Up @@ -820,7 +827,7 @@ func New(ctx context.Context, log *zap.Logger, cfg *config.Config, errChan chan<
}

// container processor
containerProcessor, err := container.New(&container.Params{
server.containerProcessor, err = container.New(&container.Params{
Log: log,
PoolSize: cfg.Workers.Container,
AlphabetState: server,
Expand All @@ -833,7 +840,7 @@ func New(ctx context.Context, log *zap.Logger, cfg *config.Config, errChan chan<
return nil, err
}

err = bindFSChainProcessor(containerProcessor, server)
err = bindFSChainProcessor(server.containerProcessor, server)
if err != nil {
return nil, err
}
Expand Down
11 changes: 10 additions & 1 deletion pkg/innerring/processors/container/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ import (
"go.uber.org/zap"
)

func (cp *Processor) handleCreationRequest(ev event.Event) {
err := cp.pool.Submit(func() { cp.processCreateContainerRequest(ev.(containerEvent.CreateContainerV2Request)) })
if err != nil {
// there system can be moved into controlled degradation stage
cp.log.Warn("container processor worker pool drained",
zap.Int("capacity", cp.pool.Cap()))
}
}

func (cp *Processor) handlePut(ev event.Event) {
req, ok := ev.(containerEvent.CreateContainerRequest)
if !ok {
Expand All @@ -36,7 +45,7 @@ func (cp *Processor) handlePut(ev event.Event) {

// send an event to the worker pool

err := cp.pool.Submit(func() { cp.processContainerPut(req) })
err := cp.pool.Submit(func() { cp.processContainerPut(req, id) })
if err != nil {
// there system can be moved into controlled degradation stage
cp.log.Warn("container processor worker pool drained",
Expand Down
122 changes: 66 additions & 56 deletions pkg/innerring/processors/container/process_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (
"fmt"
"strings"

"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
fschaincontracts "github.com/nspcc-dev/neofs-node/pkg/morph/contracts"
"github.com/nspcc-dev/neofs-node/pkg/morph/event"
containerEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/container"
containerSDK "github.com/nspcc-dev/neofs-sdk-go/container"
Expand All @@ -14,6 +16,31 @@ import (
"go.uber.org/zap"
)

func (cp *Processor) processCreateContainerRequest(req containerEvent.CreateContainerV2Request) {
if !cp.alphabetState.IsAlphabet() {
cp.log.Info("non alphabet mode, ignore container creation request")
return
}

cnr, err := fschaincontracts.ContainerFromStruct(req.Container)
if err != nil {
cp.log.Error("invalid container struct in creation request", zap.Error(err))
return
}

cnrBytes := cnr.Marshal()
id := cid.NewFromMarshalledContainer(cnrBytes)

err = cp.checkPutContainer(cnr, cnrBytes, req.SessionToken, req.InvocationScript, req.VerificationScript, "", "")
if err != nil {
cp.log.Error("container creation request failed check",
zap.Stringer("container", id), zap.Error(err))
return
}

cp.approvePutContainer(req.MainTransaction, cnr, id)
}

// putEvent is a common interface of Put and PutNamed event.
type putEvent interface {
event.Event
Expand All @@ -24,28 +51,23 @@ type putEvent interface {
NotaryRequest() *payload.P2PNotaryRequest
}

type putContainerContext struct {
e containerEvent.CreateContainerRequest

// must be filled when verifying raw data from e
cID cid.ID
cnr containerSDK.Container
d containerSDK.Domain
}

// Process a new container from the user by checking the container sanity
// and sending approve tx back to the FS chain.
func (cp *Processor) processContainerPut(req containerEvent.CreateContainerRequest) {
func (cp *Processor) processContainerPut(req containerEvent.CreateContainerRequest, id cid.ID) {
if !cp.alphabetState.IsAlphabet() {
cp.log.Info("non alphabet mode, ignore container put")
return
}

ctx := &putContainerContext{
e: req,
var cnr containerSDK.Container
if err := cnr.Unmarshal(req.Container); err != nil {
cp.log.Error("put container check failed",
zap.Error(fmt.Errorf("invalid binary container: %w", err)),
)
return
}

err := cp.checkPutContainer(ctx)
err := cp.checkPutContainer(cnr, req.Container, req.SessionToken, req.InvocationScript, req.VerificationScript, req.DomainName, req.DomainZone)
if err != nil {
cp.log.Error("put container check failed",
zap.Error(err),
Expand All @@ -54,7 +76,7 @@ func (cp *Processor) processContainerPut(req containerEvent.CreateContainerReque
return
}

cp.approvePutContainer(ctx)
cp.approvePutContainer(req.MainTransaction, cnr, id)
}

const (
Expand All @@ -69,16 +91,8 @@ var allowedSystemAttributes = map[string]struct{}{
sysAttrChainMeta: {},
}

func (cp *Processor) checkPutContainer(ctx *putContainerContext) error {
binCnr := ctx.e.Container
ctx.cID = cid.NewFromMarshalledContainer(binCnr)

err := ctx.cnr.Unmarshal(binCnr)
if err != nil {
return fmt.Errorf("invalid binary container: %w", err)
}

for k := range ctx.cnr.Attributes() {
func (cp *Processor) checkPutContainer(cnr containerSDK.Container, cnrBytes, sessionToken, invocScript, verifScript []byte, domainName, domainZone string) error {
for k := range cnr.Attributes() {
if strings.HasPrefix(k, sysAttrPrefix) {
if _, ok := allowedSystemAttributes[k]; !ok {
return fmt.Errorf("system attribute %s is not allowed", k)
Expand All @@ -90,54 +104,53 @@ func (cp *Processor) checkPutContainer(ctx *putContainerContext) error {
}
}

ecRules := ctx.cnr.PlacementPolicy().ECRules()
ecRules := cnr.PlacementPolicy().ECRules()
if !cp.allowEC && len(ecRules) > 0 {
return errors.New("EC rules are not supported yet")
}
if len(ecRules) > 0 && ctx.cnr.PlacementPolicy().NumberOfReplicas() > 0 {
if len(ecRules) > 0 && cnr.PlacementPolicy().NumberOfReplicas() > 0 {
return errors.New("REP+EC rules are not supported yet")
}

err = cp.verifySignature(signatureVerificationData{
ownerContainer: ctx.cnr.Owner(),
err := cp.verifySignature(signatureVerificationData{
ownerContainer: cnr.Owner(),
verb: session.VerbContainerPut,
binTokenSession: ctx.e.SessionToken,
verifScript: ctx.e.VerificationScript,
invocScript: ctx.e.InvocationScript,
signedData: binCnr,
binTokenSession: sessionToken,
verifScript: verifScript,
invocScript: invocScript,
signedData: cnrBytes,
})
if err != nil {
return fmt.Errorf("auth container creation: %w", err)
}

if err = ctx.cnr.PlacementPolicy().Verify(); err != nil {
if err = cnr.PlacementPolicy().Verify(); err != nil {
return fmt.Errorf("invalid storage policy: %w", err)
}

// check homomorphic hashing setting
err = checkHomomorphicHashing(cp.netState, ctx.cnr)
err = checkHomomorphicHashing(cp.netState, cnr)
if err != nil {
return fmt.Errorf("incorrect homomorphic hashing setting: %w", err)
}

// check native name and zone
err = checkNNS(ctx, ctx.cnr)
if err != nil {
return fmt.Errorf("NNS: %w", err)
if domainZone != "" { // if PutNamed event => check if values in-container domain name and zone correspond to args
err = checkNNS(cnr, domainName, domainZone)
if err != nil {
return fmt.Errorf("NNS: %w", err)
}
}

return nil
}

func (cp *Processor) approvePutContainer(ctx *putContainerContext) {
l := cp.log.With(zap.Stringer("cID", ctx.cID))
func (cp *Processor) approvePutContainer(mainTx transaction.Transaction, cnr containerSDK.Container, id cid.ID) {
l := cp.log.With(zap.Stringer("cID", id))
l.Debug("approving new container...")

e := ctx.e

var err error

err = cp.cnrClient.Morph().NotarySignAndInvokeTX(&e.MainTransaction, true)
err = cp.cnrClient.Morph().NotarySignAndInvokeTX(&mainTx, true)

if err != nil {
l.Error("could not approve put container",
Expand All @@ -152,8 +165,8 @@ func (cp *Processor) approvePutContainer(ctx *putContainerContext) {
return
}

policy := ctx.cnr.PlacementPolicy()
vectors, err := nm.ContainerNodes(policy, ctx.cID)
policy := cnr.PlacementPolicy()
vectors, err := nm.ContainerNodes(policy, id)
if err != nil {
l.Error("could not build placement for Container contract update", zap.Error(err))
return
Expand All @@ -168,7 +181,7 @@ func (cp *Processor) approvePutContainer(ctx *putContainerContext) {
replicas[i] = 1 // each EC part is stored in a single copy
}

err = cp.cnrClient.UpdateContainerPlacement(ctx.cID, vectors, replicas)
err = cp.cnrClient.UpdateContainerPlacement(id, vectors, replicas)
if err != nil {
l.Error("could not update Container contract", zap.Error(err))
return
Expand Down Expand Up @@ -238,19 +251,16 @@ func (cp *Processor) approveDeleteContainer(e containerEvent.RemoveContainerRequ
}
}

func checkNNS(ctx *putContainerContext, cnr containerSDK.Container) error {
func checkNNS(cnr containerSDK.Container, name, zone string) error {
// fetch domain info
ctx.d = cnr.ReadDomain()
d := cnr.ReadDomain()

// if PutNamed event => check if values in container correspond to args
if ctx.e.DomainName != "" {
if ctx.e.DomainName != ctx.d.Name() {
return fmt.Errorf("names differ %s/%s", ctx.e.DomainName, ctx.d.Name())
}
if name != d.Name() {
return fmt.Errorf("names differ %s/%s", name, d.Name())
}

if ctx.e.DomainZone != ctx.d.Zone() {
return fmt.Errorf("zones differ %s/%s", ctx.e.DomainZone, ctx.d.Zone())
}
if zone != d.Zone() {
return fmt.Errorf("zones differ %s/%s", zone, d.Zone())
}

return nil
Expand Down
Loading
Loading