Skip to content
Draft
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Changelog for NeoFS Node
- IR `experimental.allow_ec` config option (#3570)
- SN `pprof.enable_block` and `pprof.enable_mutex` options (#3655)
- `neofs-adm fschain load-report` command (#3649)
- SN now supports new `getInfo` and `createV2` methods of the Container contract (#XXX)
- IR now supports container creation requests submitted via new `createV2` contract method (#XXX)

### Fixed
- Write cache using too much CPU (#3642)
Expand All @@ -28,6 +30,7 @@ Changelog for NeoFS Node
- Pre-0.46.0 write cache format migration (#3647)

### Updated
- `github.com/nspcc-dev/neofs-contract` dependency to `XXX` (#XXX)

### Updating from v0.49.1
Erasure coding is available in experimental mode. To enable it, set
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 @@ -385,26 +385,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.113.0
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea
github.com/nspcc-dev/neofs-contract v0.24.0
github.com/nspcc-dev/neofs-contract v0.24.1-0.20251106142751-e84eb253fb43
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.15.0.20251015122943-b38583ddd311
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-20250923153235-ffb84619d02f h1:EO
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250923153235-ffb84619d02f/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.24.0 h1:lQHtfRc00WEhW9qcnVNbM2sMa4oCBQ5v7vcunJKk9rA=
github.com/nspcc-dev/neofs-contract v0.24.0/go.mod h1:PPxjwRiK6hhXPXduvyojEqLMHNpgPaF+rULPhdFlzDg=
github.com/nspcc-dev/neofs-contract v0.24.1-0.20251106142751-e84eb253fb43 h1:ba1l9ToFA18yc+VUITjItR2Afrl1HRpHByl7RGWLL7w=
github.com/nspcc-dev/neofs-contract v0.24.1-0.20251106142751-e84eb253fb43/go.mod h1:uDF8RHDeOHAJFfaSvAwAQB/s0NcHrH65GudosWHmFqQ=
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.15.0.20251015122943-b38583ddd311 h1:iHjokyLIiOW7zvaNZZPmch0c1tghwkfOniL+JOt+F7M=
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.15.0.20251015122943-b38583ddd311/go.mod h1:Vukuf6qDOQESOWAx5yOjYtVC5wdsQp3hiZrxbJIa2fs=
github.com/nspcc-dev/rfc6979 v0.2.4 h1:NBgsdCjhLpEPJZqmC9rciMZDcSY297po2smeaRjw57k=
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 @@ -10,6 +10,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 @@ -35,7 +44,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
8 changes: 8 additions & 0 deletions pkg/innerring/processors/container/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ func (cp *Processor) ListenerNotaryParsers() []event.NotaryParserInfo {
p.SetParser(containerEvent.RestoreCreateContainerRequest)
pp = append(pp, p)

p.SetRequestType(fschaincontracts.CreateContainerV2Method)
p.SetParser(containerEvent.RestoreCreateContainerV2Request)
pp = append(pp, p)

// container delete
p.SetRequestType(containerEvent.DeleteNotaryEvent)
p.SetParser(containerEvent.ParseDeleteNotary)
Expand Down Expand Up @@ -194,6 +198,10 @@ func (cp *Processor) ListenerNotaryHandlers() []event.NotaryHandlerInfo {
h.SetHandler(cp.handlePut)
hh = append(hh, h)

h.SetRequestType(fschaincontracts.CreateContainerV2Method)
h.SetHandler(cp.handleCreationRequest)
hh = append(hh, h)

// container delete
h.SetRequestType(containerEvent.DeleteNotaryEvent)
h.SetHandler(cp.handleDelete)
Expand Down
1 change: 1 addition & 0 deletions pkg/morph/client/container/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (
deleteMethod = "delete"
getMethod = "get"
getDataMethod = "getContainerData"
getInfoMethod = "getInfo"
listMethod = "containersOf"
eaclMethod = "eACL"
eaclDataMethod = "getEACLData"
Expand Down
Loading
Loading