From 6396c8ee56e4f6e7015c00c3f5c3765f3c0490d9 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Fri, 23 Oct 2026 18:22:40 +0300 Subject: [PATCH 1/2] go.mod: update SDK to its latest version It now has container RPC signed by container owner (or by an authorized key) and allows IRs checking original requests by its signatures, not by request fields. Refs https://github.com/nspcc-dev/neofs-api/pull/446, https://github.com/nspcc-dev/neofs-sdk-go/pull/898. Signed-off-by: Pavel Karpy --- CHANGELOG.md | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebfd2f838a..4ea198b4e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ Changelog for NeoFS Node - Support for `Created` and `Removed` events of Container contract in SN (#4242) ### Updated -- `github.com/nspcc-dev/neofs-sdk-go` module to `v1.0.0-rc.23.0.20261021092246-abe302e8498b` (#4194, #4219, #4228, #4224, #4222, #4240) +- `github.com/nspcc-dev/neofs-sdk-go` module to `v1.0.0-rc.23.0.20261021092246-e754fb0cb9bf` (#4194, #4219, #4228, #4224, #4222, #4240, #4245) - `github.com/nspcc-dev/neofs-contract` module to `v0.28.0` (#4239, #4246, #4250) ### Updating from v0.57.0 diff --git a/go.mod b/go.mod index f7827df329..62325820f4 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/nspcc-dev/neo-go v0.123.0 github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea github.com/nspcc-dev/neofs-contract v0.28.0 - github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.23.0.20261021092246-abe302e8498b + github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.23.0.20261026124529-e754fb0cb9bf github.com/nspcc-dev/tzhash v1.8.4 github.com/panjf2000/ants/v2 v2.12.1 github.com/prometheus/client_golang v1.24.1 diff --git a/go.sum b/go.sum index 9ba6126b47..02abdd3e55 100644 --- a/go.sum +++ b/go.sum @@ -173,8 +173,8 @@ github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea h1:mK 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.28.0 h1:Ug/vxeM26yJdFG0ix8DGne4M1OHFnGSx48fvEQFNMvA= github.com/nspcc-dev/neofs-contract v0.28.0/go.mod h1:yolNeVnmpZoCJsEMuRlmenpJHlzw1dQqGc3HuVIYNpg= -github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.23.0.20261021092246-abe302e8498b h1:PGE+MSQID0gl+mUaiL1chxtd9O7klkXBhgLVGszwjyM= -github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.23.0.20261021092246-abe302e8498b/go.mod h1:8Nz0EKfdWZVIyq7bEtus0A0j0SCewKX9IfOAeZW5G+A= +github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.23.0.20261026124529-e754fb0cb9bf h1:1WFtroG/7OrBUMaaRZBwo+Q9ouel5/f4ig1wmWoBoMk= +github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.23.0.20261026124529-e754fb0cb9bf/go.mod h1:8Nz0EKfdWZVIyq7bEtus0A0j0SCewKX9IfOAeZW5G+A= github.com/nspcc-dev/rfc6979 v0.2.4 h1:NBgsdCjhLpEPJZqmC9rciMZDcSY297po2smeaRjw57k= github.com/nspcc-dev/rfc6979 v0.2.4/go.mod h1:86ylDw6Kss+P6v4QAJqo1Sp3mC0/Zr9G97xSjQ9TuFg= github.com/nspcc-dev/tzhash v1.8.4 h1:lvuPGWsqEo9dVEvo/kdNLKv/Cy0yxRs9z5hJp8VcBuo= From 1f53dc23b8303e63a8537b43adb23c75abbaeb17 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Thu, 22 Oct 2026 20:34:21 +0300 Subject: [PATCH 2/2] sn,ir: provide/check container API requests via contracts Implementation of https://github.com/nspcc-dev/neofs-contract/pull/637. Signed-off-by: Pavel Karpy --- CHANGELOG.md | 1 + cmd/neofs-node/container.go | 9 +- .../processors/container/process_container.go | 215 ++++++++++++++++-- .../processors/container/process_eacl.go | 28 ++- pkg/morph/client/container/delete.go | 20 +- pkg/morph/client/container/eacl_set.go | 20 +- pkg/morph/client/container/put.go | 28 ++- pkg/morph/contracts/methods.go | 6 + pkg/morph/event/container/notary_requests.go | 159 +++++++++---- pkg/services/container/server.go | 52 +++-- pkg/services/container/server_test.go | 12 +- .../container/service_internal_test.go | 6 +- 12 files changed, 453 insertions(+), 103 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ea198b4e4..ab5125ac26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Changelog for NeoFS Node - Unnecessary local shard relocation of EC parts by policer (#4236) - GC stalling on expired objects protected by LOCKs stored only on other shards (#4237) - Unchecked known payload length of unfinished objects in PUT (#4233) +- IR validates original container API requests via contract calls (#4245) ### Changed - SN now requires requests to have API version >= 2.22 (#4212) diff --git a/cmd/neofs-node/container.go b/cmd/neofs-node/container.go index 686db4fe42..c20a1de403 100644 --- a/cmd/neofs-node/container.go +++ b/cmd/neofs-node/container.go @@ -532,12 +532,13 @@ func (x *containersInChain) List(id user.ID) ([]cid.ID, error) { return x.cnrLst.List(&id) } -func (x *containersInChain) Put(ctx context.Context, cnr container.Container, pub, sig []byte, st []byte, eACL *eacl.Table, eaclPub, eaclSig []byte) (cid.ID, error) { +func (x *containersInChain) Put(ctx context.Context, cnr container.Container, pub, sig []byte, st []byte, eACL *eacl.Table, eaclPub, eaclSig []byte, origReq []byte) (cid.ID, error) { var prm cntClient.PutPrm prm.SetContainer(cnr) prm.SetKey(pub) prm.SetSignature(sig) prm.SetToken(st) + prm.SetOriginalRequest(origReq) if eACL != nil { prm.SetEACLTable(*eACL, eaclPub, eaclSig) } @@ -550,12 +551,13 @@ func (x *containersInChain) Put(ctx context.Context, cnr container.Container, pu return id, err } -func (x *containersInChain) Delete(ctx context.Context, id cid.ID, pub, sig []byte, st []byte) error { +func (x *containersInChain) Delete(ctx context.Context, id cid.ID, pub, sig []byte, st []byte, origReq []byte) error { var prm cntClient.DeletePrm prm.SetCID(id[:]) prm.SetSignature(sig) prm.SetKey(pub) prm.SetToken(st) + prm.SetOriginalRequest(origReq) err := x.cCli.Delete(ctx, prm) if errors.Is(err, client.ErrTxAwaitTimeout) { @@ -565,12 +567,13 @@ func (x *containersInChain) Delete(ctx context.Context, id cid.ID, pub, sig []by return err } -func (x *containersInChain) PutEACL(ctx context.Context, eACL eacl.Table, pub, sig []byte, st []byte) error { +func (x *containersInChain) PutEACL(ctx context.Context, eACL eacl.Table, pub, sig []byte, st []byte, origReq []byte) error { var prm cntClient.PutEACLPrm prm.SetTable(eACL.Marshal()) prm.SetKey(pub) prm.SetSignature(sig) prm.SetToken(st) + prm.SetOriginalRequest(origReq) err := x.cCli.PutEACL(ctx, prm) if errors.Is(err, client.ErrTxAwaitTimeout) { diff --git a/pkg/innerring/processors/container/process_container.go b/pkg/innerring/processors/container/process_container.go index 4d2f4d2ddc..3d11debaea 100644 --- a/pkg/innerring/processors/container/process_container.go +++ b/pkg/innerring/processors/container/process_container.go @@ -1,18 +1,24 @@ package container import ( + "bytes" "errors" "fmt" + "slices" "strings" "time" "github.com/nspcc-dev/neo-go/pkg/core/transaction" cntClient "github.com/nspcc-dev/neofs-node/pkg/morph/client/container" containerEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/container" + "github.com/nspcc-dev/neofs-node/pkg/services/util" "github.com/nspcc-dev/neofs-sdk-go/client" "github.com/nspcc-dev/neofs-sdk-go/container" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" "github.com/nspcc-dev/neofs-sdk-go/eacl" + protocontainer "github.com/nspcc-dev/neofs-sdk-go/proto/container" + protoencoding "github.com/nspcc-dev/neofs-sdk-go/proto/encoding" + protosession "github.com/nspcc-dev/neofs-sdk-go/proto/session" "github.com/nspcc-dev/neofs-sdk-go/session" sessionv2 "github.com/nspcc-dev/neofs-sdk-go/session/v2" "github.com/nspcc-dev/neofs-sdk-go/version" @@ -36,7 +42,7 @@ func (cp *Processor) processCreateContainerRequest(req containerEvent.CreateCont cnrBytes := cnr.Marshal() id := cid.NewFromMarshalledContainer(cnrBytes) - err = cp.checkPutContainer(cnr, cnrBytes, req.SessionToken, req.InvocationScript, req.VerificationScript) + apiReq, err := cp.checkPutContainer(cnr, cnrBytes, req) if err != nil { cp.log.Error("container creation request failed check", zap.Stringer("container", id), zap.Error(err)) @@ -57,8 +63,27 @@ func (cp *Processor) processCreateContainerRequest(req containerEvent.CreateCont zap.Stringer("cid", id), zap.Stringer("cidInEACL", table.GetCID())) return } + if apiReq != nil { + // validate that the table that passed via contracts is the same that + // was attached to the request originally + apiEACL := apiReq.GetBody().GetEacl() + if apiEACL == nil { + cp.log.Error("container contract put with additional eACL but original request does not have it", + zap.Stringer("cid", id)) + return + } + + b := make([]byte, apiEACL.MarshaledSize()) + apiEACL.MarshalStable(b) + + if !bytes.Equal(req.EACLTable.EACL, b) { + cp.log.Error("container contract put have different eACL from contract and API request", + zap.Stringer("cid", id)) + return + } + } - err = cp.checkSetEACL(*req.EACLTable, table, id, cnr) + err = cp.checkSetEACL(*req.EACLTable, table, id, cnr, req.OriginalRequest == nil) if err != nil { cp.log.Error("additional eACL with container creation check failed", zap.Stringer("cid", id), zap.Error(err)) @@ -84,21 +109,21 @@ var allowedSystemAttributes = map[string]struct{}{ sysAttrChainMeta: {}, } -func (cp *Processor) checkPutContainer(cnr container.Container, cnrBytes, sessionToken, invocScript, verifScript []byte) error { +func (cp *Processor) checkPutContainer(cnr container.Container, cnrBytes []byte, req containerEvent.CreateContainerV2Request) (*protocontainer.PutRequest, error) { if ver := cnr.Version(); ver.Compare(minContainerAPIVersion) < 0 { - return fmt.Errorf("API version is %s while %s is minimum supported", ver, minContainerAPIVersion) + return nil, fmt.Errorf("API version is %s while %s is minimum supported", ver, minContainerAPIVersion) } var metaEnabled bool 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) + return nil, fmt.Errorf("system attribute %s is not allowed", k) } if k == sysAttrChainMeta { if !cp.metaEnabled { - return errors.New("chain meta data attribute is not allowed") + return nil, errors.New("chain meta data attribute is not allowed") } metaEnabled = true } @@ -107,33 +132,51 @@ func (cp *Processor) checkPutContainer(cnr container.Container, cnrBytes, sessio ecRules := cnr.PlacementPolicy().ECRules() if !cp.allowEC && len(ecRules) > 0 { - return errors.New("EC rules are not supported yet") + return nil, errors.New("EC rules are not supported yet") } if len(ecRules) > 0 && cnr.PlacementPolicy().NumberOfReplicas() > 0 { - return errors.New("REP+EC rules are not supported yet") + return nil, errors.New("REP+EC rules are not supported yet") } if metaEnabled && cnr.PlacementPolicy().Initial() != nil { - return errors.New("initial placement policies with consistent metadata are not supported yet") + return nil, errors.New("initial placement policies with consistent metadata are not supported yet") + } + + if req.OriginalRequest != nil { + var apiReq protocontainer.PutRequest + err := protoencoding.UnmarshalMessage(req.OriginalRequest, &apiReq) + if err != nil { + return nil, fmt.Errorf("could not unmarshal original request: %w", err) + } + + var ( + body = apiReq.GetBody() + mCnr = body.GetContainer() + ) + if mCnr == nil || !bytes.Equal(cnrBytes, protoencoding.MarshalMessage(mCnr)) { + return nil, fmt.Errorf("container in the original request does not match the contract one") + } + + return &apiReq, cp.checkOriginalRequest(cnr, cid.ID{}, session.VerbContainerPut, sessionv2.VerbContainerPut, body, apiReq.GetMetaHeader(), apiReq.GetVerifyHeader()) } err := cp.verifySignature(signatureVerificationData{ ownerContainer: cnr.Owner(), verb: session.VerbContainerPut, verbV2: sessionv2.VerbContainerPut, - binTokenSession: sessionToken, - verifScript: verifScript, - invocScript: invocScript, + binTokenSession: req.SessionToken, + verifScript: req.VerificationScript, + invocScript: req.InvocationScript, signedData: cnrBytes, }) if err != nil { - return fmt.Errorf("auth container creation: %w", err) + return nil, fmt.Errorf("auth container creation: %w", err) } if err = cnr.PlacementPolicy().Verify(); err != nil { - return fmt.Errorf("invalid storage policy: %w", err) + return nil, fmt.Errorf("invalid storage policy: %w", err) } - return nil + return nil, nil } func (cp *Processor) approvePutContainer(mainTx transaction.Transaction, cnr container.Container, id cid.ID) { @@ -247,6 +290,20 @@ func (cp *Processor) checkDeleteContainer(req containerEvent.RemoveContainerRequ return fmt.Errorf("could not receive the container: %w", err) } + if req.OriginalRequest != nil { + var origReq protocontainer.DeleteRequest + err := protoencoding.UnmarshalMessage(req.OriginalRequest, &origReq) + if err != nil { + return fmt.Errorf("could not unmarshal original request: %w", err) + } + var body = origReq.GetBody() + if !bytes.Equal(req.ID, body.GetContainerId().GetValue()) { + return fmt.Errorf("container ID in the original request does not match the contract one") + } + + return cp.checkOriginalRequest(cnr, idCnr, session.VerbContainerDelete, sessionv2.VerbContainerDelete, body, origReq.GetMetaHeader(), origReq.GetVerifyHeader()) + } + err = cp.verifySignature(signatureVerificationData{ ownerContainer: cnr.Owner(), verb: session.VerbContainerDelete, @@ -265,6 +322,134 @@ func (cp *Processor) checkDeleteContainer(req containerEvent.RemoveContainerRequ return nil } +func (cp *Processor) checkOriginalRequest(cnr container.Container, cnrID cid.ID, verbV1 session.ContainerVerb, verbV2 sessionv2.Verb, reqBody protoencoding.Message, reqMetaHeader *protosession.RequestMetaHeader, reqVerifHeader *protosession.RequestVerificationHeader) error { + if err := util.VerifyRequestAPIVersion(reqMetaHeader.GetVersion()); err != nil { + return fmt.Errorf("invalid request API version: %w", err) + } + if err := util.VerifyRequestValidityTime(cp.chainTime.Now(), reqMetaHeader); err != nil { + return fmt.Errorf("invalid request validity time: %w", err) + } + + var sTok []byte + switch { + case reqMetaHeader.GetSessionToken() != nil: + t := reqMetaHeader.GetSessionToken() + sTok = make([]byte, t.MarshaledSize()) + t.MarshalStable(sTok) + case reqMetaHeader.GetSessionTokenV2() != nil: + t := reqMetaHeader.GetSessionTokenV2() + sTok = make([]byte, t.MarshaledSize()) + t.MarshalStable(sTok) + default: + } + + var ( + signedData []byte + sig interface { + GetKey() []byte + GetSign() []byte + } + verifData = signatureVerificationData{ + ownerContainer: cnr.Owner(), + verb: verbV1, + verbV2: verbV2, + idContainerSet: !cnrID.IsZero(), + idContainer: cnrID, + binTokenSession: sTok, + } + ) + + // API 2.28- special field request check: this is the only thing that + // signed by owner (of with session issued by the owner) in practice + // see https://github.com/nspcc-dev/neofs-api/pull/446, + // https://github.com/nspcc-dev/neofs-sdk-go/pull/898 + if specialFieldSigned(reqMetaHeader) { + switch b := reqBody.(type) { + case *protocontainer.PutRequest_Body: + signedData = cnr.Marshal() + sig = b.GetSignature() + case *protocontainer.DeleteRequest_Body: + signedData = b.GetContainerId().GetValue() + sig = b.GetSignature() + case *protocontainer.SetExtendedACLRequest_Body: + t := b.GetEacl() + signedData = make([]byte, t.MarshaledSize()) + b.GetEacl().MarshalStable(signedData) + + sig = b.GetSignature() + default: + panic(fmt.Sprintf("unexpected body in container service check: %v", b)) + } + } else { + signedData, _ = protoencoding.EncodeRequest(nil, reqBody, reqMetaHeader) + sig = reqVerifHeader.GetRequestSignature() + } + + verifData.verifScript = sig.GetKey() + verifData.invocScript = sig.GetSign() + verifData.signedData = signedData + + err := cp.verifySignature(verifData) + if err != nil { + return fmt.Errorf("auth error: %w", err) + } + + if !multipleReqSignatures(reqMetaHeader) { + return nil + } + + // API 2.25- requests. + + var ( + bodyLen = reqBody.MarshaledSize() + mHeaderLen = reqMetaHeader.MarshaledSize() + needBuff = max(bodyLen, mHeaderLen) + ) + signedData = slices.Grow(verifData.signedData[:0], needBuff) + + // body + + signedData = signedData[:bodyLen] + reqBody.MarshalStable(signedData) + + sig = reqVerifHeader.GetBodySignature() + + verifData.signedData = signedData + verifData.verifScript = sig.GetKey() + verifData.invocScript = sig.GetSign() + + err = cp.verifySignature(verifData) + if err != nil { + return fmt.Errorf("body signature auth error: %w", err) + } + + // meta header + + signedData = signedData[:mHeaderLen] + reqMetaHeader.MarshalStable(signedData) + + sig = reqVerifHeader.GetMetaSignature() + + verifData.signedData = signedData + verifData.verifScript = sig.GetKey() + verifData.invocScript = sig.GetSign() + + err = cp.verifySignature(verifData) + if err != nil { + return fmt.Errorf("meta header auth error: %w", err) + } + + return nil +} + +func multipleReqSignatures(m *protosession.RequestMetaHeader) bool { + return m == nil || m.Version == nil || m.Version.Major < 2 || (m.Version.Major == 2 && m.Version.Minor < 26) +} + +func specialFieldSigned(m *protosession.RequestMetaHeader) bool { + return m == nil || m.Version == nil || m.Version.Major < 2 || (m.Version.Major == 2 && m.Version.Minor < 29) +} + func (cp *Processor) approveDeleteContainer(e containerEvent.RemoveContainerRequest) { err := cp.cnrClient.Morph().NotarySignAndInvokeTX(&e.MainTransaction, false) diff --git a/pkg/innerring/processors/container/process_eacl.go b/pkg/innerring/processors/container/process_eacl.go index 73fb37663d..a3cabbaf2d 100644 --- a/pkg/innerring/processors/container/process_eacl.go +++ b/pkg/innerring/processors/container/process_eacl.go @@ -1,6 +1,7 @@ package container import ( + "bytes" "errors" "fmt" "math/big" @@ -12,6 +13,8 @@ import ( "github.com/nspcc-dev/neofs-sdk-go/container" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" "github.com/nspcc-dev/neofs-sdk-go/eacl" + protocontainer "github.com/nspcc-dev/neofs-sdk-go/proto/container" + protoencoding "github.com/nspcc-dev/neofs-sdk-go/proto/encoding" "github.com/nspcc-dev/neofs-sdk-go/session" sessionv2 "github.com/nspcc-dev/neofs-sdk-go/session/v2" "go.uber.org/zap" @@ -55,7 +58,7 @@ func (cp *Processor) processPutEACLRequest(req cnrevent.PutContainerEACLRequest) return } - err = cp.checkSetEACL(req, table, idCnr, cnr) + err = cp.checkSetEACL(req, table, idCnr, cnr, true) if err != nil { // defer return @@ -64,7 +67,7 @@ func (cp *Processor) processPutEACLRequest(req cnrevent.PutContainerEACLRequest) cp.approveSetEACL(req) } -func (cp *Processor) checkSetEACL(req cnrevent.PutContainerEACLRequest, table eacl.Table, cID cid.ID, cnr container.Container) error { +func (cp *Processor) checkSetEACL(req cnrevent.PutContainerEACLRequest, table eacl.Table, cID cid.ID, cnr container.Container, checkSig bool) error { err := validateEACL(table) if err != nil { return fmt.Errorf("table validation: %w", err) @@ -75,6 +78,27 @@ func (cp *Processor) checkSetEACL(req cnrevent.PutContainerEACLRequest, table ea return errors.New("ACL extension disabled by container basic ACL") } + if !checkSig { + return nil + } + + if len(req.OriginalRequest) != 0 { + var apiReq protocontainer.SetExtendedACLRequest + err := protoencoding.UnmarshalMessage(req.OriginalRequest, &apiReq) + if err != nil { + return fmt.Errorf("could not unmarshal original request: %w", err) + } + var ( + body = apiReq.GetBody() + mEACL = body.GetEacl() + ) + if mEACL == nil || !bytes.Equal(req.EACL, protoencoding.MarshalMessage(mEACL)) { + return fmt.Errorf("container ID in the original request does not match the contract one") + } + + return cp.checkOriginalRequest(cnr, cID, session.VerbContainerSetEACL, sessionv2.VerbContainerSetEACL, apiReq.GetBody(), apiReq.GetMetaHeader(), apiReq.GetVerifyHeader()) + } + err = cp.verifySignature(signatureVerificationData{ ownerContainer: cnr.Owner(), verb: session.VerbContainerSetEACL, diff --git a/pkg/morph/client/container/delete.go b/pkg/morph/client/container/delete.go index b0c048f0df..e6dc363298 100644 --- a/pkg/morph/client/container/delete.go +++ b/pkg/morph/client/container/delete.go @@ -16,6 +16,8 @@ type DeletePrm struct { signature []byte key []byte token []byte + + origReq []byte } // SetCID sets container ID. @@ -38,6 +40,11 @@ func (d *DeletePrm) SetToken(token []byte) { d.token = token } +// SetOriginalRequest sets original API request that triggered contract call. +func (d *DeletePrm) SetOriginalRequest(req []byte) { + d.origReq = req +} + // Delete calls Container contract to delete container with parameterized // credentials. If transaction is accepted for processing, Delete waits for it // to be successfully executed. Waiting is performed within ctx, @@ -48,13 +55,18 @@ func (d *DeletePrm) SetToken(token []byte) { // // Returns [apistatus.ContainerLocked] if container is locked. func (c *Client) Delete(ctx context.Context, p DeletePrm) error { - if len(p.signature) == 0 { + if len(p.signature) == 0 && len(p.origReq) == 0 { return errNilArgument } - err := c.client.CallWithAlphabetWitness(ctx, fschaincontracts.RemoveContainerMethod, []any{ - p.cnr, p.signature, p.key, p.token, - }) + var args []any + if p.origReq == nil { + args = []any{p.cnr, p.signature, p.key, p.token} + } else { + args = []any{p.cnr, p.origReq} + } + + err := c.client.CallWithAlphabetWitness(ctx, fschaincontracts.RemoveContainerMethod, args) if err != nil { if e := err.Error(); strings.Contains(e, containerrpc.ErrorLocked) { return apistatus.NewContainerLocked(e) diff --git a/pkg/morph/client/container/eacl_set.go b/pkg/morph/client/container/eacl_set.go index 93bf27f38a..e6f5a518dc 100644 --- a/pkg/morph/client/container/eacl_set.go +++ b/pkg/morph/client/container/eacl_set.go @@ -13,6 +13,8 @@ type PutEACLPrm struct { key []byte sig []byte token []byte + + origReq []byte } // SetTable sets table. @@ -35,6 +37,11 @@ func (p *PutEACLPrm) SetToken(token []byte) { p.token = token } +// SetOriginalRequest sets original API request that triggered contract call. +func (p *PutEACLPrm) SetOriginalRequest(req []byte) { + p.origReq = req +} + // PutEACL calls Container contract to set container's extended ACL with // parameterized credentials. If transaction is accepted for processing, PutEACL // waits for it to be successfully executed. Waiting is done within ctx, @@ -42,13 +49,18 @@ func (p *PutEACLPrm) SetToken(token []byte) { // // Returns any error encountered that caused the saving to interrupt. func (c *Client) PutEACL(ctx context.Context, p PutEACLPrm) error { - if len(p.sig) == 0 || len(p.key) == 0 { + if (len(p.sig) == 0 || len(p.key) == 0) && len(p.origReq) == 0 { return errNilArgument } - err := c.client.CallWithAlphabetWitness(ctx, fschaincontracts.PutContainerEACLMethod, []any{ - p.table, p.sig, p.key, p.token, - }) + var args []any + if p.origReq == nil { + args = []any{p.table, p.sig, p.key, p.token} + } else { + args = []any{p.table, p.origReq} + } + + err := c.client.CallWithAlphabetWitness(ctx, fschaincontracts.PutContainerEACLMethod, args) if err != nil { return fmt.Errorf("could not invoke method (%s): %w", fschaincontracts.PutContainerEACLMethod, err) } diff --git a/pkg/morph/client/container/put.go b/pkg/morph/client/container/put.go index 85e4306191..9dd0b3b24c 100644 --- a/pkg/morph/client/container/put.go +++ b/pkg/morph/client/container/put.go @@ -21,6 +21,8 @@ type PutPrm struct { eACLTable *eacl.Table eaclKey []byte eaclSig []byte + + origReq []byte } // SetContainer sets container. @@ -51,6 +53,11 @@ func (p *PutPrm) SetEACLTable(eaclTable eacl.Table, key, sig []byte) { p.eaclSig = sig } +// SetOriginalRequest sets original API request that triggered contract call. +func (p *PutPrm) SetOriginalRequest(req []byte) { + p.origReq = req +} + // Put calls Container contract to create container with parameterized // credentials. If transaction is accepted for processing, Put waits for it to // be successfully executed. Waiting is performed within ctx, @@ -59,7 +66,7 @@ func (p *PutPrm) SetEACLTable(eaclTable eacl.Table, key, sig []byte) { // Returns calculated container identifier and any error // encountered that caused the saving to interrupt. func (c *Client) Put(ctx context.Context, p PutPrm) (cid.ID, error) { - if len(p.sig) == 0 || len(p.key) == 0 { + if (len(p.sig) == 0 || len(p.key) == 0) && len(p.origReq) == 0 { return cid.ID{}, errNilArgument } @@ -67,9 +74,7 @@ func (c *Client) Put(ctx context.Context, p PutPrm) (cid.ID, error) { return c.putContainerWithEacl(ctx, p) } - err := c.client.CallWithAlphabetWitness(ctx, fschaincontracts.CreateContainerV2Method, []any{ - containerToStackItem(p.cnr), p.sig, p.key, p.token, - }) + err := c.client.CallWithAlphabetWitness(ctx, fschaincontracts.CreateContainerV2Method, putCnrArgs(p)) if err != nil { return cid.ID{}, fmt.Errorf("could not invoke method (%s): %w", fschaincontracts.CreateContainerV2Method, err) } @@ -84,7 +89,7 @@ func (c *Client) putContainerWithEacl(ctx context.Context, p PutPrm) (cid.ID, er rawTable = p.eACLTable.Marshal() ) - b.InvokeMethod(addr, fschaincontracts.CreateContainerV2Method, containerToStackItem(p.cnr), p.sig, p.key, p.token) + b.InvokeMethod(addr, fschaincontracts.CreateContainerV2Method, putCnrArgs(p)...) b.InvokeMethod(addr, fschaincontracts.PutContainerEACLMethod, rawTable, p.eaclSig, p.eaclKey, p.token) script, err := b.Script() if err != nil { @@ -98,3 +103,16 @@ func (c *Client) putContainerWithEacl(ctx context.Context, p PutPrm) (cid.ID, er return cid.NewFromMarshalledContainer(p.cnr.Marshal()), nil } + +func putCnrArgs(p PutPrm) []any { + var ( + args []any + cnrSI = containerToStackItem(p.cnr) + ) + if p.origReq == nil { + args = []any{cnrSI, p.sig, p.key, p.token} + } else { + args = []any{cnrSI, p.origReq} + } + return args +} diff --git a/pkg/morph/contracts/methods.go b/pkg/morph/contracts/methods.go index 0dbe4a8400..87bb80921e 100644 --- a/pkg/morph/contracts/methods.go +++ b/pkg/morph/contracts/methods.go @@ -26,6 +26,8 @@ type CreateContainerParams struct { DomainName string DomainZone string EnableObjectMetadata bool + + OriginalRequest []byte } // RemoveContainerParams are parameters of [RemoveContainerMethod]. @@ -34,6 +36,8 @@ type RemoveContainerParams struct { InvocationScript []byte VerificationScript []byte SessionToken []byte + + OriginalRequest []byte } // PutContainerEACLParams are parameters of [PutContainerEACLMethod]. @@ -42,4 +46,6 @@ type PutContainerEACLParams struct { InvocationScript []byte VerificationScript []byte SessionToken []byte + + OriginalRequest []byte } diff --git a/pkg/morph/event/container/notary_requests.go b/pkg/morph/event/container/notary_requests.go index 068ed6b1e6..d0a988167d 100644 --- a/pkg/morph/event/container/notary_requests.go +++ b/pkg/morph/event/container/notary_requests.go @@ -25,14 +25,16 @@ type CreateContainerV2Request struct { // EACLTable is set if container eACL was set in the same // transaction container was created. It is nil otherwise. EACLTable *PutContainerEACLRequest + + OriginalRequest []byte } // RestoreCreateContainerV2Request restores [CreateContainerV2Request] from the // notary one. func RestoreCreateContainerV2Request(contractCalls []event.NotaryEvent) (event.Event, error) { - const argNum = 4 var ( res CreateContainerV2Request + args []scparser.PushedItem withOptionalEacl bool err error ) @@ -43,26 +45,46 @@ func RestoreCreateContainerV2Request(contractCalls []event.NotaryEvent) (event.E default: return nil, fmt.Errorf("unexpected number of contract calls: %d", l) } - cnrCall := contractCalls[0] - args, err := event.GetArgs(cnrCall, argNum) - if err != nil { - return nil, err + + const ( + argNumNoOrigReq = 4 + argNumWithOrigReq = 2 + ) + + switch argNum := len(cnrCall.Params()); argNum { + case argNumNoOrigReq: + args, err = event.GetArgs(cnrCall, argNumNoOrigReq) + if err != nil { + return nil, err + } + + if res.InvocationScript, err = event.GetValueFromArg(args, 1, cnrCall.Type().String(), scparser.GetBytesFromInstr); err != nil { + return nil, err + } + if res.VerificationScript, err = event.GetValueFromArg(args, 2, cnrCall.Type().String(), scparser.GetBytesFromInstr); err != nil { + return nil, err + } + if res.SessionToken, err = event.GetValueFromArg(args, 3, cnrCall.Type().String(), scparser.GetBytesFromInstr); err != nil { + return nil, err + } + case argNumWithOrigReq: + args, err = event.GetArgs(cnrCall, argNumWithOrigReq) + if err != nil { + return nil, err + } + + if res.OriginalRequest, err = event.GetValueFromArg(args, 1, cnrCall.Type().String(), scparser.GetBytesFromInstr); err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("wrong parameter count for %s call: expected %d or %d, has %d", cnrCall.Type(), argNumNoOrigReq, argNumWithOrigReq, argNum) } + res.MainTransaction = *cnrCall.Raw().MainTransaction if res.Container, err = containerInfoFromPushedItem(args[0]); err != nil { return nil, event.WrapInvalidArgError(0, "Container", err) } - if res.InvocationScript, err = event.GetValueFromArg(args, 1, cnrCall.Type().String(), scparser.GetBytesFromInstr); err != nil { - return nil, err - } - if res.VerificationScript, err = event.GetValueFromArg(args, 2, cnrCall.Type().String(), scparser.GetBytesFromInstr); err != nil { - return nil, err - } - if res.SessionToken, err = event.GetValueFromArg(args, 3, cnrCall.Type().String(), scparser.GetBytesFromInstr); err != nil { - return nil, err - } - res.MainTransaction = *cnrCall.Raw().MainTransaction if withOptionalEacl { ev, err := RestorePutContainerEACLRequest(contractCalls[1]) @@ -177,27 +199,48 @@ type RemoveContainerRequest struct { // RestoreRemoveContainerRequest restores [RemoveContainerRequest] from the // notary one. func RestoreRemoveContainerRequest(notaryReq event.NotaryEvent) (event.Event, error) { - const argNum = 4 - args, err := event.GetArgs(notaryReq, argNum) - if err != nil { - return nil, err - } + const ( + argNumNoOrigReq = 4 + argNumWithOrigReq = 2 + ) + var ( + res RemoveContainerRequest + args []scparser.PushedItem + err error + ) + switch argNum := len(notaryReq.Params()); argNum { + case argNumNoOrigReq: + args, err = event.GetArgs(notaryReq, argNumNoOrigReq) + if err != nil { + return nil, err + } - var res RemoveContainerRequest + if res.InvocationScript, err = event.GetValueFromArg(args, 1, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { + return nil, err + } + if res.VerificationScript, err = event.GetValueFromArg(args, 2, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { + return nil, err + } + if res.SessionToken, err = event.GetValueFromArg(args, 3, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { + return nil, err + } + case argNumWithOrigReq: + args, err = event.GetArgs(notaryReq, argNumWithOrigReq) + if err != nil { + return nil, err + } - if res.ID, err = event.GetValueFromArg(args, 0, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { - return nil, err - } - if res.InvocationScript, err = event.GetValueFromArg(args, 1, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { - return nil, err - } - if res.VerificationScript, err = event.GetValueFromArg(args, 2, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { - return nil, err + if res.OriginalRequest, err = event.GetValueFromArg(args, 1, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("wrong parameter count for %s call: expected %d or %d, has %d", notaryReq.Type(), argNumNoOrigReq, argNumWithOrigReq, argNum) } - if res.SessionToken, err = event.GetValueFromArg(args, 3, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { + + res.MainTransaction = *notaryReq.Raw().MainTransaction + if res.ID, err = event.GetValueFromArg(args, 0, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { return nil, err } - res.MainTransaction = *notaryReq.Raw().MainTransaction return res, nil } @@ -213,27 +256,49 @@ type PutContainerEACLRequest struct { // RestorePutContainerEACLRequest restores [PutContainerEACLRequest] from the // notary one. func RestorePutContainerEACLRequest(notaryReq event.NotaryEvent) (event.Event, error) { - const argNum = 4 - args, err := event.GetArgs(notaryReq, argNum) - if err != nil { - return nil, err - } + const ( + argNumNoOrigReq = 4 + argNumWithOrigReq = 2 + ) - var res PutContainerEACLRequest + var ( + res PutContainerEACLRequest + args []scparser.PushedItem + err error + ) + switch argNum := len(notaryReq.Params()); argNum { + case argNumNoOrigReq: + args, err = event.GetArgs(notaryReq, argNumNoOrigReq) + if err != nil { + return nil, err + } - if res.EACL, err = event.GetValueFromArg(args, 0, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { - return nil, err - } - if res.InvocationScript, err = event.GetValueFromArg(args, 1, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { - return nil, err - } - if res.VerificationScript, err = event.GetValueFromArg(args, 2, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { - return nil, err + if res.InvocationScript, err = event.GetValueFromArg(args, 1, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { + return nil, err + } + if res.VerificationScript, err = event.GetValueFromArg(args, 2, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { + return nil, err + } + if res.SessionToken, err = event.GetValueFromArg(args, 3, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { + return nil, err + } + case argNumWithOrigReq: + args, err = event.GetArgs(notaryReq, argNumWithOrigReq) + if err != nil { + return nil, err + } + + if res.OriginalRequest, err = event.GetValueFromArg(args, 1, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("wrong parameter count for %s call: expected %d or %d, has %d", notaryReq.Type(), argNumNoOrigReq, argNumWithOrigReq, argNum) } - if res.SessionToken, err = event.GetValueFromArg(args, 3, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { + + res.MainTransaction = *notaryReq.Raw().MainTransaction + if res.EACL, err = event.GetValueFromArg(args, 0, notaryReq.Type().String(), scparser.GetBytesFromInstr); err != nil { return nil, err } - res.MainTransaction = *notaryReq.Raw().MainTransaction return res, nil } diff --git a/pkg/services/container/server.go b/pkg/services/container/server.go index 1e7f4eb27d..8787116780 100644 --- a/pkg/services/container/server.go +++ b/pkg/services/container/server.go @@ -27,6 +27,7 @@ import ( neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" "github.com/nspcc-dev/neofs-sdk-go/eacl" protocontainer "github.com/nspcc-dev/neofs-sdk-go/proto/container" + protoencoding "github.com/nspcc-dev/neofs-sdk-go/proto/encoding" protonetmap "github.com/nspcc-dev/neofs-sdk-go/proto/netmap" iprotobuf "github.com/nspcc-dev/neofs-sdk-go/proto/protobuf" "github.com/nspcc-dev/neofs-sdk-go/proto/refs" @@ -58,7 +59,7 @@ type Contract interface { // transaction is accepted for processing, Put waits for it to be successfully // executed. Waiting is performed within ctx, // [apistatus.ErrContainerAwaitTimeout] is returned on when it is done. - Put(ctx context.Context, _ container.Container, pub, sig []byte, sessionToken []byte, eACL *eacl.Table, eaclPub, eaclSig []byte) (cid.ID, error) + Put(ctx context.Context, _ container.Container, pub, sig []byte, sessionToken []byte, eACL *eacl.Table, eaclPub, eaclSig []byte, origRequest []byte) (cid.ID, error) // Get returns container by its ID. Returns [apistatus.ErrContainerNotFound] // error if container is missing. Get(cid.ID) (container.Container, error) @@ -70,7 +71,7 @@ type Contract interface { // credentials. If transaction is accepted for processing, PutEACL waits for it // to be successfully executed. Waiting is performed within ctx, // [apistatus.ErrContainerAwaitTimeout] is when it is done. - PutEACL(ctx context.Context, _ eacl.Table, pub, sig []byte, sessionToken []byte) error + PutEACL(ctx context.Context, _ eacl.Table, pub, sig []byte, sessionToken []byte, origRequest []byte) error // GetEACL returns eACL of the container by its ID. Returns // [apistatus.ErrEACLNotFound] error if eACL is missing. GetEACL(cid.ID) (eacl.Table, error) @@ -78,7 +79,7 @@ type Contract interface { // transaction is accepted for processing, Delete waits for it to be // successfully executed. Waiting is performed within ctx, // [apistatus.ErrContainerAwaitTimeout] is returned when it is done. - Delete(ctx context.Context, _ cid.ID, pub, sig []byte, sessionToken []byte) error + Delete(ctx context.Context, _ cid.ID, pub, sig []byte, sessionToken []byte, origRequest []byte) error // SetAttribute sends transaction setting container attribute with provided // credentials. If transaction is accepted for processing, SetAttribute waits // for it to be successfully executed. Waiting is performed within ctx, @@ -467,13 +468,14 @@ func (s *Server) Put(ctx context.Context, req *protocontainer.PutRequest) (*prot return s.makeFailedPutResponse(fmt.Errorf("invalid container: %w", err), req) } - stV2, tokenBytes, err := s.getVerifiedSessionTokenV2FromMetaHeader(req.GetMetaHeader(), sessionv2.VerbContainerPut, cid.ID{}) + var reqMetaHeader = req.GetMetaHeader() + stV2, tokenBytes, err := s.getVerifiedSessionTokenV2FromMetaHeader(reqMetaHeader, sessionv2.VerbContainerPut, cid.ID{}) if err != nil { return s.makeFailedPutResponse(fmt.Errorf("verify session token v2: %w", err), req) } if stV2 == nil { - st, b, err := s.getVerifiedSessionTokenFromMetaHeader(req.GetMetaHeader(), session.VerbContainerPut, cid.ID{}) + st, b, err := s.getVerifiedSessionTokenFromMetaHeader(reqMetaHeader, session.VerbContainerPut, cid.ID{}) if err != nil { return s.makeFailedPutResponse(fmt.Errorf("verify session token: %w", err), req) } @@ -500,7 +502,7 @@ func (s *Server) Put(ctx context.Context, req *protocontainer.PutRequest) (*prot return s.makeFailedPutResponse(fmt.Errorf("eACL table's container ID does not correspond to container"), req) } - _, _, err = s.getVerifiedSessionTokenV2FromMetaHeader(req.GetMetaHeader(), sessionv2.VerbContainerSetEACL, cid.ID{}) + _, _, err = s.getVerifiedSessionTokenV2FromMetaHeader(reqMetaHeader, sessionv2.VerbContainerSetEACL, cid.ID{}) if err != nil { return s.makeFailedPutResponse(fmt.Errorf("verify session token v2: %w", err), req) } @@ -513,7 +515,7 @@ func (s *Server) Put(ctx context.Context, req *protocontainer.PutRequest) (*prot ctx, cancel := context.WithTimeout(ctx, defaultTxAwaitTimeout) defer cancel() - id, err := s.contract.Put(ctx, cnr, mSig.Key, mSig.Sign, tokenBytes, eACL, eaclPub, eaclSig) + id, err := s.contract.Put(ctx, cnr, mSig.Key, mSig.Sign, tokenBytes, eACL, eaclPub, eaclSig, marshalRequest(reqBody, reqMetaHeader, req.GetVerifyHeader())) if err != nil && !errors.Is(err, apistatus.ErrContainerAwaitTimeout) { return s.makeFailedPutResponse(err, req) } @@ -560,13 +562,14 @@ func (s *Server) Delete(ctx context.Context, req *protocontainer.DeleteRequest) return s.makeDeleteResponse(fmt.Errorf("invalid ID: %w", err), req) } - stV2, tokenBytes, err := s.getVerifiedSessionTokenV2FromMetaHeader(req.GetMetaHeader(), sessionv2.VerbContainerDelete, id) + var reqMetaHeader = req.GetMetaHeader() + stV2, tokenBytes, err := s.getVerifiedSessionTokenV2FromMetaHeader(reqMetaHeader, sessionv2.VerbContainerDelete, id) if err != nil { return s.makeDeleteResponse(fmt.Errorf("verify session token v2: %w", err), req) } if stV2 == nil { - st, b, err := s.getVerifiedSessionTokenFromMetaHeader(req.GetMetaHeader(), session.VerbContainerDelete, id) + st, b, err := s.getVerifiedSessionTokenFromMetaHeader(reqMetaHeader, session.VerbContainerDelete, id) if err != nil { return s.makeDeleteResponse(fmt.Errorf("verify session token: %w", err), req) } @@ -578,7 +581,7 @@ func (s *Server) Delete(ctx context.Context, req *protocontainer.DeleteRequest) ctx, cancel := context.WithTimeout(ctx, defaultTxAwaitTimeout) defer cancel() - err = s.contract.Delete(ctx, id, mSig.Key, mSig.Sign, tokenBytes) + err = s.contract.Delete(ctx, id, mSig.Key, mSig.Sign, tokenBytes, marshalRequest(reqBody, reqMetaHeader, req.GetVerifyHeader())) return s.makeDeleteResponse(err, req) } @@ -727,17 +730,18 @@ func (s *Server) SetExtendedACL(ctx context.Context, req *protocontainer.SetExte return s.makeSetEACLResponse(errors.New("missing container ID in eACL table"), req) } - if err := s.checkContainerRevision(req.GetMetaHeader(), reqBody.GetContainerRevision(), cnrID); err != nil { + var reqMetaHeader = req.GetMetaHeader() + if err := s.checkContainerRevision(reqMetaHeader, reqBody.GetContainerRevision(), cnrID); err != nil { return s.makeSetEACLResponse(err, req) } - stV2, tokenBytes, err := s.getVerifiedSessionTokenV2FromMetaHeader(req.GetMetaHeader(), sessionv2.VerbContainerSetEACL, cnrID) + stV2, tokenBytes, err := s.getVerifiedSessionTokenV2FromMetaHeader(reqMetaHeader, sessionv2.VerbContainerSetEACL, cnrID) if err != nil { return s.makeSetEACLResponse(fmt.Errorf("verify session token v2: %w", err), req) } if stV2 == nil { - st, b, err := s.getVerifiedSessionTokenFromMetaHeader(req.GetMetaHeader(), session.VerbContainerSetEACL, cnrID) + st, b, err := s.getVerifiedSessionTokenFromMetaHeader(reqMetaHeader, session.VerbContainerSetEACL, cnrID) if err != nil { return s.makeSetEACLResponse(fmt.Errorf("verify session token: %w", err), req) } @@ -749,7 +753,7 @@ func (s *Server) SetExtendedACL(ctx context.Context, req *protocontainer.SetExte ctx, cancel := context.WithTimeout(ctx, defaultTxAwaitTimeout) defer cancel() - err = s.contract.PutEACL(ctx, eACL, mSig.Key, mSig.Sign, tokenBytes) + err = s.contract.PutEACL(ctx, eACL, mSig.Key, mSig.Sign, tokenBytes, marshalRequest(reqBody, reqMetaHeader, req.GetVerifyHeader())) return s.makeSetEACLResponse(err, req) } @@ -976,3 +980,23 @@ func (s *Server) RemoveAttribute(ctx context.Context, req *protocontainer.Remove return s.makeRemoveAttributeResponse(err) } + +func marshalRequest[B protoencoding.Message](reqBody B, reqMetaHeader *protosession.RequestMetaHeader, vHeader *protosession.RequestVerificationHeader) []byte { + var ( + bLen = reqBody.MarshaledSize() + mLen = reqMetaHeader.MarshaledSize() + vLen = vHeader.MarshaledSize() + ) + + ln := protoencoding.CalculateRequestBodyFieldLength(bLen) + ln += protoencoding.CalculateRequestMetaHeaderFieldLength(mLen) + ln += protoencoding.CalculateRequestVerificationHeaderFieldLength(vLen) + + buf := make([]byte, ln) + + off := protoencoding.WriteRequestBodyMessage(buf, reqBody) + off += protoencoding.WriteRequestMetaHeaderMessage(buf[off:], reqMetaHeader) + protoencoding.WriteRequestVerificationHeaderMessage(buf[off:], vHeader) + + return buf +} diff --git a/pkg/services/container/server_test.go b/pkg/services/container/server_test.go index 46345db314..d8a97b821e 100644 --- a/pkg/services/container/server_test.go +++ b/pkg/services/container/server_test.go @@ -52,7 +52,7 @@ func (unimplementedFSChain) HasUserInNNS(string, neoutil.Uint160) (bool, error) type unimplementedContainerContract struct{} -func (unimplementedContainerContract) Put(context.Context, container.Container, []byte, []byte, []byte, *eacl.Table, []byte, []byte) (cid.ID, error) { +func (unimplementedContainerContract) Put(context.Context, container.Container, []byte, []byte, []byte, *eacl.Table, []byte, []byte, []byte) (cid.ID, error) { panic("implement me") } @@ -64,7 +64,7 @@ func (unimplementedContainerContract) List(user.ID) ([]cid.ID, error) { panic("unimplemented") } -func (unimplementedContainerContract) PutEACL(context.Context, eacl.Table, []byte, []byte, []byte) error { +func (unimplementedContainerContract) PutEACL(context.Context, eacl.Table, []byte, []byte, []byte, []byte) error { panic("unimplemented") } @@ -72,7 +72,7 @@ func (unimplementedContainerContract) GetEACL(cid.ID) (eacl.Table, error) { panic("unimplemented") } -func (unimplementedContainerContract) Delete(context.Context, cid.ID, []byte, []byte, []byte) error { +func (unimplementedContainerContract) Delete(context.Context, cid.ID, []byte, []byte, []byte, []byte) error { panic("unimplemented") } @@ -113,7 +113,7 @@ type testFSChain struct { nnsUsers map[string]neoutil.Uint160 } -func (testFSChain) Put(context.Context, container.Container, []byte, []byte, []byte, *eacl.Table, []byte, []byte) (cid.ID, error) { +func (testFSChain) Put(context.Context, container.Container, []byte, []byte, []byte, *eacl.Table, []byte, []byte, []byte) (cid.ID, error) { return cid.ID{}, errors.New("unimplemented") } @@ -132,7 +132,7 @@ func (testFSChain) List(user.ID) ([]cid.ID, error) { return nil, errors.New("unimplemented") } -func (testFSChain) PutEACL(context.Context, eacl.Table, []byte, []byte, []byte) error { +func (testFSChain) PutEACL(context.Context, eacl.Table, []byte, []byte, []byte, []byte) error { return nil } @@ -140,7 +140,7 @@ func (testFSChain) GetEACL(cid.ID) (eacl.Table, error) { return eacl.Table{}, errors.New("unimplemented") } -func (testFSChain) Delete(context.Context, cid.ID, []byte, []byte, []byte) error { +func (testFSChain) Delete(context.Context, cid.ID, []byte, []byte, []byte, []byte) error { return nil } diff --git a/pkg/services/container/service_internal_test.go b/pkg/services/container/service_internal_test.go index a1b2300231..b7afde35ff 100644 --- a/pkg/services/container/service_internal_test.go +++ b/pkg/services/container/service_internal_test.go @@ -35,7 +35,7 @@ type mockContainerContract struct { cnr container.Container } -func (mockContainerContract) Put(context.Context, container.Container, []byte, []byte, []byte, *eacl.Table, []byte, []byte) (cid.ID, error) { +func (mockContainerContract) Put(context.Context, container.Container, []byte, []byte, []byte, *eacl.Table, []byte, []byte, []byte) (cid.ID, error) { panic("unimplemented") } @@ -47,7 +47,7 @@ func (mockContainerContract) List(user.ID) ([]cid.ID, error) { panic("unimplemented") } -func (mockContainerContract) PutEACL(context.Context, eacl.Table, []byte, []byte, []byte) error { +func (mockContainerContract) PutEACL(context.Context, eacl.Table, []byte, []byte, []byte, []byte) error { panic("unimplemented") } @@ -55,7 +55,7 @@ func (mockContainerContract) GetEACL(cid.ID) (eacl.Table, error) { panic("unimplemented") } -func (mockContainerContract) Delete(context.Context, cid.ID, []byte, []byte, []byte) error { +func (mockContainerContract) Delete(context.Context, cid.ID, []byte, []byte, []byte, []byte) error { panic("unimplemented") }