diff --git a/CHANGELOG.md b/CHANGELOG.md index ebfd2f838a..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) @@ -41,7 +42,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/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/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= 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") }