diff --git a/go.mod b/go.mod index 322f96d80a..bf338a912a 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/multiformats/go-multiaddr v0.4.0 github.com/nspcc-dev/hrw v1.0.9 github.com/nspcc-dev/neo-go v0.97.3 - github.com/nspcc-dev/neofs-api-go v1.29.1-0.20210929152813-0117e90e9adb + github.com/nspcc-dev/neofs-api-go v1.29.1-0.20211014122040-db1ed764733b github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210520210714-9dee13f0d556 github.com/nspcc-dev/tzhash v1.4.0 github.com/panjf2000/ants/v2 v2.4.0 diff --git a/go.sum b/go.sum index 3eab86811d..5859236732 100644 --- a/go.sum +++ b/go.sum @@ -397,8 +397,8 @@ github.com/nspcc-dev/neo-go v0.97.3/go.mod h1:31LelE8G5NZwGmePCykqui6BpPyEklTVbO github.com/nspcc-dev/neofs-api-go v1.24.0/go.mod h1:G7dqincfdjBrAbL5nxVp82emF05fSVEqe59ICsoRDI8= github.com/nspcc-dev/neofs-api-go v1.26.1/go.mod h1:SHuH1Ba3U/h3j+8HHbb3Cns1LfMlEb88guWog9Qi68Y= github.com/nspcc-dev/neofs-api-go v1.27.1/go.mod h1:i0Cwgvcu9A4M4e58pydbXFisUhSxpfljmuWFPIp2btE= -github.com/nspcc-dev/neofs-api-go v1.29.1-0.20210929152813-0117e90e9adb h1:634hGE+KGXakNRkgB83rUoJEB4MWkfsiKCD4LrS3XOc= -github.com/nspcc-dev/neofs-api-go v1.29.1-0.20210929152813-0117e90e9adb/go.mod h1:KC8T91skIg8juvUh7lQabswQ9J6KmnXErpH8qwDitXA= +github.com/nspcc-dev/neofs-api-go v1.29.1-0.20211014122040-db1ed764733b h1:n5tIRk8WMwJJCjpBO6V2sJFqNDnJvYXH7lY5GdkQaAo= +github.com/nspcc-dev/neofs-api-go v1.29.1-0.20211014122040-db1ed764733b/go.mod h1:KC8T91skIg8juvUh7lQabswQ9J6KmnXErpH8qwDitXA= github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM= diff --git a/pkg/innerring/processors/container/handlers.go b/pkg/innerring/processors/container/handlers.go index 3fabdb1578..f23195f275 100644 --- a/pkg/innerring/processors/container/handlers.go +++ b/pkg/innerring/processors/container/handlers.go @@ -10,7 +10,7 @@ import ( ) func (cp *Processor) handlePut(ev event.Event) { - put := ev.(containerEvent.Put) + put := ev.(putEvent) id := sha256.Sum256(put.Container()) cp.log.Info("notification", @@ -19,7 +19,7 @@ func (cp *Processor) handlePut(ev event.Event) { // send event to the worker pool - err := cp.pool.Submit(func() { cp.processContainerPut(&put) }) + err := cp.pool.Submit(func() { cp.processContainerPut(put) }) if err != nil { // there system can be moved into controlled degradation stage cp.log.Warn("container processor worker pool drained", diff --git a/pkg/innerring/processors/container/process_container.go b/pkg/innerring/processors/container/process_container.go index bd6c69dfa6..ad3fe67e96 100644 --- a/pkg/innerring/processors/container/process_container.go +++ b/pkg/innerring/processors/container/process_container.go @@ -7,24 +7,46 @@ import ( "fmt" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/network/payload" containerSDK "github.com/nspcc-dev/neofs-api-go/pkg/container" cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id" "github.com/nspcc-dev/neofs-api-go/pkg/session" "github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-node/pkg/core/container" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" containerEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/container" "go.uber.org/zap" ) +// putEvent is a common interface of Put and PutNamed event. +type putEvent interface { + event.Event + Container() []byte + PublicKey() []byte + Signature() []byte + SessionToken() []byte + NotaryRequest() *payload.P2PNotaryRequest +} + +type putContainerContext struct { + e putEvent + + name, zone string // from container structure +} + // Process new container from the user by checking container sanity // and sending approve tx back to morph. -func (cp *Processor) processContainerPut(put *containerEvent.Put) { +func (cp *Processor) processContainerPut(put putEvent) { if !cp.alphabetState.IsAlphabet() { cp.log.Info("non alphabet mode, ignore container put") return } - err := cp.checkPutContainer(put) + ctx := &putContainerContext{ + e: put, + } + + err := cp.checkPutContainer(ctx) if err != nil { cp.log.Error("put container check failed", zap.String("error", err.Error()), @@ -33,10 +55,12 @@ func (cp *Processor) processContainerPut(put *containerEvent.Put) { return } - cp.approvePutContainer(put) + cp.approvePutContainer(ctx) } -func (cp *Processor) checkPutContainer(e *containerEvent.Put) error { +func (cp *Processor) checkPutContainer(ctx *putContainerContext) error { + e := ctx.e + // verify signature key, err := keys.NewPublicKeyFromBytes(e.PublicKey(), elliptic.P256()) if err != nil { @@ -58,6 +82,12 @@ func (cp *Processor) checkPutContainer(e *containerEvent.Put) error { return fmt.Errorf("invalid binary container: %w", err) } + // check native name and zone + err = checkNNS(ctx, cnr) + if err != nil { + return fmt.Errorf("NNS: %w", err) + } + // perform format check err = container.CheckFormat(cnr) if err != nil { @@ -85,7 +115,9 @@ func (cp *Processor) checkPutContainer(e *containerEvent.Put) error { return cp.checkKeyOwnership(cnr, key) } -func (cp *Processor) approvePutContainer(e *containerEvent.Put) { +func (cp *Processor) approvePutContainer(ctx *putContainerContext) { + e := ctx.e + var err error if nr := e.NotaryRequest(); nr != nil { @@ -93,7 +125,7 @@ func (cp *Processor) approvePutContainer(e *containerEvent.Put) { err = cp.cnrClient.Morph().NotarySignAndInvokeTX(nr.MainTransaction) } else { // put event was received via notification service - err = cp.cnrClient.Put(e.Container(), e.PublicKey(), e.Signature(), e.SessionToken()) + err = cp.cnrClient.Put(e.Container(), e.PublicKey(), e.Signature(), e.SessionToken(), ctx.name, ctx.zone) } if err != nil { cp.log.Error("could not approve put container", @@ -202,3 +234,24 @@ func (cp *Processor) approveDeleteContainer(e *containerEvent.Delete) { ) } } + +func checkNNS(ctx *putContainerContext, cnr *containerSDK.Container) error { + // fetch native name and zone + ctx.name, ctx.zone = containerSDK.GetNativeNameWithZone(cnr) + + // if PutNamed event => check if values in container correspond to args + if named, ok := ctx.e.(interface { + Name() string + Zone() string + }); ok { + if name := named.Name(); name != ctx.name { + return fmt.Errorf("names differ %s/%s", name, ctx.name) + } + + if zone := named.Zone(); zone != ctx.zone { + return fmt.Errorf("zones differ %s/%s", zone, ctx.zone) + } + } + + return nil +} diff --git a/pkg/innerring/processors/container/processor.go b/pkg/innerring/processors/container/processor.go index 67dd02802d..dd3e4ed842 100644 --- a/pkg/innerring/processors/container/processor.go +++ b/pkg/innerring/processors/container/processor.go @@ -161,7 +161,7 @@ func (cp *Processor) ListenerNotaryParsers() []event.NotaryParserInfo { var ( p event.NotaryParserInfo - pp = make([]event.NotaryParserInfo, 0, 3) + pp = make([]event.NotaryParserInfo, 0, 4) ) p.SetMempoolType(mempoolevent.TransactionAdded) @@ -172,6 +172,11 @@ func (cp *Processor) ListenerNotaryParsers() []event.NotaryParserInfo { p.SetParser(containerEvent.ParsePutNotary) pp = append(pp, p) + // container named put + p.SetRequestType(containerEvent.PutNamedNotaryEvent) + p.SetParser(containerEvent.ParsePutNamedNotary) + pp = append(pp, p) + // container delete p.SetRequestType(containerEvent.DeleteNotaryEvent) p.SetParser(containerEvent.ParseDeleteNotary) @@ -190,7 +195,7 @@ func (cp *Processor) ListenerNotaryHandlers() []event.NotaryHandlerInfo { var ( h event.NotaryHandlerInfo - hh = make([]event.NotaryHandlerInfo, 0, 3) + hh = make([]event.NotaryHandlerInfo, 0, 4) ) h.SetScriptHash(cp.cnrClient.ContractAddress()) @@ -201,6 +206,10 @@ func (cp *Processor) ListenerNotaryHandlers() []event.NotaryHandlerInfo { h.SetHandler(cp.handlePut) hh = append(hh, h) + // container named put (same handler) + h.SetRequestType(containerEvent.PutNamedNotaryEvent) + hh = append(hh, h) + // container delete h.SetRequestType(containerEvent.DeleteNotaryEvent) h.SetHandler(cp.handleDelete) diff --git a/pkg/morph/client/container/client.go b/pkg/morph/client/container/client.go index 3c068d6984..7fc5185225 100644 --- a/pkg/morph/client/container/client.go +++ b/pkg/morph/client/container/client.go @@ -29,6 +29,7 @@ type Option func(*cfg) type cfg struct { putMethod, // put container method name for invocation + putNamedMethod, // put named container method name for invocation putSizeMethod, // put container size method name for invocation listSizesMethod, // list container sizes method name for invocation getSizeMethod, // get container size method name for invocation @@ -55,6 +56,8 @@ const ( defaultPutSizeMethod = "putContainerSize" // default "put container size" method name defaultListSizesMethod = "listContainerSizes" // default "list container sizes" method name defaultGetSizeMethod = "getContainerSize" // default "get container size" method name + + defaultPutNamedMethod = "putNamed" // default put named container method name ) func defaultConfig() *cfg { @@ -71,6 +74,8 @@ func defaultConfig() *cfg { putSizeMethod: defaultPutSizeMethod, listSizesMethod: defaultListSizesMethod, getSizeMethod: defaultGetSizeMethod, + + putNamedMethod: defaultPutNamedMethod, } } @@ -270,3 +275,17 @@ func WithGetSizeMethod(n string) Option { } } } + +// WithPutNamedMethod returns a client constructor option that +// specifies the method name of "put named container" operation. +// +// Ignores empty value. +// +// If option not provided, "putNamed" is used. +func WithPutNamedMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.putNamedMethod = n + } + } +} diff --git a/pkg/morph/client/container/put.go b/pkg/morph/client/container/put.go index 5852dbb07f..85cd1828bb 100644 --- a/pkg/morph/client/container/put.go +++ b/pkg/morph/client/container/put.go @@ -14,6 +14,8 @@ type PutArgs struct { publicKey []byte // public key of container owner token []byte // binary session token + + name, zone string // native name and zone } // SetPublicKey sets the public key of container owner @@ -41,19 +43,46 @@ func (p *PutArgs) SetSessionToken(v []byte) { p.token = v } -// Put invokes the call of put container method +// SetNativeNameWithZone sets container native name and its zone. +func (p *PutArgs) SetNativeNameWithZone(name, zone string) { + p.name, p.zone = name, zone +} + +// Put invokes the call of put (named if name is set) container method // of NeoFS Container contract. func (c *Client) Put(args PutArgs) error { - err := c.client.Invoke( - c.putMethod, - args.cnr, - args.sig, - args.publicKey, - args.token, + var ( + err error + method string ) + if args.name != "" { + err = c.client.Invoke( + c.putNamedMethod, + args.cnr, + args.sig, + args.publicKey, + args.token, + args.name, + args.zone, + ) + + method = c.putNamedMethod + } else { + err = c.client.Invoke( + c.putMethod, + args.cnr, + args.sig, + args.publicKey, + args.token, + ) + + method = c.putMethod + } + if err != nil { - return fmt.Errorf("could not invoke method (%s): %w", c.putMethod, err) + return fmt.Errorf("could not invoke method (%s): %w", method, err) } + return nil } diff --git a/pkg/morph/client/container/wrapper/container.go b/pkg/morph/client/container/wrapper/container.go index 47a185c85b..0e21214837 100644 --- a/pkg/morph/client/container/wrapper/container.go +++ b/pkg/morph/client/container/wrapper/container.go @@ -41,7 +41,9 @@ func Put(w *Wrapper, cnr *container.Container) (*cid.ID, error) { sig := cnr.Signature() - err = w.Put(data, sig.Key(), sig.Sign(), binToken) + name, zone := container.GetNativeNameWithZone(cnr) + + err = w.Put(data, sig.Key(), sig.Sign(), binToken, name, zone) if err != nil { return nil, err } @@ -59,7 +61,7 @@ func Put(w *Wrapper, cnr *container.Container) (*cid.ID, error) { // encountered that caused the saving to interrupt. // // If TryNotary is provided, calls notary contract. -func (w *Wrapper) Put(cnr, key, sig, token []byte) error { +func (w *Wrapper) Put(cnr, key, sig, token []byte, name, zone string) error { if len(sig) == 0 || len(key) == 0 { return errNilArgument } @@ -70,6 +72,7 @@ func (w *Wrapper) Put(cnr, key, sig, token []byte) error { args.SetSignature(sig) args.SetPublicKey(key) args.SetSessionToken(token) + args.SetNativeNameWithZone(name, zone) err := w.client.Put(args) if err != nil { diff --git a/pkg/morph/event/container/put.go b/pkg/morph/event/container/put.go index c5af119cbd..9cfdf4557d 100644 --- a/pkg/morph/event/container/put.go +++ b/pkg/morph/event/container/put.go @@ -47,6 +47,23 @@ func (p Put) NotaryRequest() *payload.P2PNotaryRequest { return p.notaryRequest } +// PutNamed represents notification event spawned by PutNamed method from Container contract of NeoFS Morph chain. +type PutNamed struct { + Put + + name, zone string +} + +// Name returns "name" arg of contract call. +func (x PutNamed) Name() string { + return x.name +} + +// Zone returns "zone" arg of contract call. +func (x PutNamed) Zone() string { + return x.zone +} + // ParsePut from notification into container event structure. func ParsePut(params []stackitem.Item) (event.Event, error) { var ( diff --git a/pkg/morph/event/container/put_notary.go b/pkg/morph/event/container/put_notary.go index d95806c7c3..f489537f33 100644 --- a/pkg/morph/event/container/put_notary.go +++ b/pkg/morph/event/container/put_notary.go @@ -1,6 +1,9 @@ package container import ( + "fmt" + + "github.com/nspcc-dev/neo-go/pkg/network/payload" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neofs-node/pkg/morph/event" ) @@ -42,34 +45,79 @@ const ( // in `Container` contract. Is used as identificator for notary // put container requests. PutNotaryEvent = "put" + + // PutNotaryEvent is an ID of notary "put named container" notification. + PutNamedNotaryEvent = "putNamed" ) -// ParsePutNotary from NotaryEvent into container event structure. -func ParsePutNotary(ne event.NotaryEvent) (event.Event, error) { +func parsePutNotary(ev *Put, raw *payload.P2PNotaryRequest, ops []event.Op) error { var ( - ev Put currentOp opcode.Opcode + fieldNum = 0 ) - fieldNum := 0 - - for _, op := range ne.Params() { + for _, op := range ops { currentOp = op.Code() switch { case opcode.PUSHDATA1 <= currentOp && currentOp <= opcode.PUSHDATA4: if fieldNum == expectedItemNumPut { - return nil, event.UnexpectedArgNumErr(PutNotaryEvent) + return event.UnexpectedArgNumErr(PutNotaryEvent) } - putFieldSetters[fieldNum](&ev, op.Param()) + putFieldSetters[fieldNum](ev, op.Param()) fieldNum++ default: - return nil, event.UnexpectedOpcode(PutNotaryEvent, op.Code()) + return event.UnexpectedOpcode(PutNotaryEvent, op.Code()) } } - ev.notaryRequest = ne.Raw() + ev.notaryRequest = raw + + return nil +} + +// ParsePutNotary from NotaryEvent into container event structure. +func ParsePutNotary(ne event.NotaryEvent) (event.Event, error) { + var ev Put + + err := parsePutNotary(&ev, ne.Raw(), ne.Params()) + if err != nil { + return nil, err + } + + return ev, nil +} + +// ParsePutNamedNotary parses PutNamed event structure from generic event.NotaryEvent. +func ParsePutNamedNotary(ne event.NotaryEvent) (event.Event, error) { + ops := ne.Params() + + const putNamedAdditionalArgs = 2 // PutNamed has same args as Put + (name, zone) (2) + + if len(ops) != expectedItemNumPut+putNamedAdditionalArgs { + return nil, event.UnexpectedArgNumErr(PutNamedNotaryEvent) + } + + var ( + ev PutNamed + err error + ) + + ev.zone, err = event.StringFromOpcode(ops[0]) + if err != nil { + return nil, fmt.Errorf("parse arg zone: %w", err) + } + + ev.name, err = event.StringFromOpcode(ops[1]) + if err != nil { + return nil, fmt.Errorf("parse arg name: %w", err) + } + + err = parsePutNotary(&ev.Put, ne.Raw(), ops[putNamedAdditionalArgs:]) + if err != nil { + return nil, err + } return ev, nil } diff --git a/pkg/morph/event/opcodes.go b/pkg/morph/event/opcodes.go index 7548ac974e..3385c2eace 100644 --- a/pkg/morph/event/opcodes.go +++ b/pkg/morph/event/opcodes.go @@ -25,6 +25,8 @@ func (o Op) Param() []byte { return o.param } +// Below are the functions which reverse the results of github.com/nspcc-dev/neo-go/pkg/vm/emit.Array function. + // BytesFromOpcode tries to retrieve bytes from Op. func BytesFromOpcode(op Op) ([]byte, error) { switch code := op.Code(); code { @@ -48,3 +50,10 @@ func IntFromOpcode(op Op) (int64, error) { return 0, fmt.Errorf("unexpected INT opcode %s", code) } } + +// StringFromOpcode tries to retrieve string from Op. +func StringFromOpcode(op Op) (string, error) { + // strings are emitted like bytes + data, err := BytesFromOpcode(op) + return string(data), err +}