From 60134081630d3fe4ac4bf8bed3f2a81307aedb6b Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 11 Jun 2025 20:14:32 +0300 Subject: [PATCH 1/6] sn: Support unsigned responses by API server Follow https://github.com/nspcc-dev/neofs-api/pull/374. Refs #3396. Signed-off-by: Leonard Lyubich --- CHANGELOG.md | 1 + cmd/neofs-node/reputation.go | 24 ++--- pkg/services/accounting/server.go | 20 ++-- pkg/services/container/server.go | 122 +++++++++++------------ pkg/services/netmap/server.go | 42 ++++---- pkg/services/object/server.go | 160 ++++++++++++++++-------------- pkg/services/session/server.go | 22 ++-- pkg/services/util/sign.go | 23 +++++ pkg/services/util/sign_test.go | 49 +++++++++ 9 files changed, 271 insertions(+), 192 deletions(-) create mode 100644 pkg/services/util/sign_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 354b7c2cb5..1a8e400fe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Changelog for NeoFS Node - `owner mismatches signature` for stored objects (#3836) ### Changed +- SN returns unsigned responses to requests with API >= `v2.22` (#3785) ### Removed diff --git a/cmd/neofs-node/reputation.go b/cmd/neofs-node/reputation.go index 6fdb1dc707..6391db9e51 100644 --- a/cmd/neofs-node/reputation.go +++ b/cmd/neofs-node/reputation.go @@ -263,17 +263,17 @@ func (s *reputationServer) makeResponseMetaHeader(st *protostatus.Status) *proto } } -func (s *reputationServer) makeLocalResponse(err error) (*protoreputation.AnnounceLocalTrustResponse, error) { +func (s *reputationServer) makeLocalResponse(err error, req *protoreputation.AnnounceLocalTrustRequest) (*protoreputation.AnnounceLocalTrustResponse, error) { resp := &protoreputation.AnnounceLocalTrustResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), } - resp.VerifyHeader = util.SignResponse(&s.key.PrivateKey, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(&s.key.PrivateKey, resp, req) return resp, nil } func (s *reputationServer) AnnounceLocalTrust(ctx context.Context, req *protoreputation.AnnounceLocalTrustRequest) (*protoreputation.AnnounceLocalTrustResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeLocalResponse(err) + return s.makeLocalResponse(err, req) } passedRoute := reverseRoute(req.GetVerifyHeader()) @@ -288,30 +288,30 @@ func (s *reputationServer) AnnounceLocalTrust(ctx context.Context, req *protorep w, err := s.localRouter.InitWriter(reputationrouter.NewRouteContext(eCtx, passedRoute)) if err != nil { - return s.makeLocalResponse(fmt.Errorf("could not initialize local trust writer: %w", err)) + return s.makeLocalResponse(fmt.Errorf("could not initialize local trust writer: %w", err), req) } for _, trust := range body.GetTrusts() { err = s.processLocalTrust(body.GetEpoch(), apiToLocalTrust(trust, passedRoute[0].PublicKey()), passedRoute, w) if err != nil { - return s.makeLocalResponse(fmt.Errorf("could not write one of local trusts: %w", err)) + return s.makeLocalResponse(fmt.Errorf("could not write one of local trusts: %w", err), req) } } - return s.makeLocalResponse(util.StatusOKErr) + return s.makeLocalResponse(util.StatusOKErr, req) } -func (s *reputationServer) makeIntermediateResponse(err error) (*protoreputation.AnnounceIntermediateResultResponse, error) { +func (s *reputationServer) makeIntermediateResponse(err error, req *protoreputation.AnnounceIntermediateResultRequest) (*protoreputation.AnnounceIntermediateResultResponse, error) { resp := &protoreputation.AnnounceIntermediateResultResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), } - resp.VerifyHeader = util.SignResponse(&s.key.PrivateKey, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(&s.key.PrivateKey, resp, req) return resp, nil } func (s *reputationServer) AnnounceIntermediateResult(ctx context.Context, req *protoreputation.AnnounceIntermediateResultRequest) (*protoreputation.AnnounceIntermediateResultResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeIntermediateResponse(err) + return s.makeIntermediateResponse(err, req) } passedRoute := reverseRoute(req.GetVerifyHeader()) @@ -323,7 +323,7 @@ func (s *reputationServer) AnnounceIntermediateResult(ctx context.Context, req * w, err := s.intermediateRouter.InitWriter(reputationrouter.NewRouteContext(eiCtx, passedRoute)) if err != nil { - return s.makeIntermediateResponse(fmt.Errorf("could not initialize trust writer: %w", err)) + return s.makeIntermediateResponse(fmt.Errorf("could not initialize trust writer: %w", err), req) } v2Trust := body.GetTrust() @@ -332,10 +332,10 @@ func (s *reputationServer) AnnounceIntermediateResult(ctx context.Context, req * err = w.Write(trust) if err != nil { - return s.makeIntermediateResponse(fmt.Errorf("could not write trust: %w", err)) + return s.makeIntermediateResponse(fmt.Errorf("could not write trust: %w", err), req) } - return s.makeIntermediateResponse(util.StatusOKErr) + return s.makeIntermediateResponse(util.StatusOKErr, req) } func (s *reputationServer) processLocalTrust(epoch uint64, t reputation.Trust, diff --git a/pkg/services/accounting/server.go b/pkg/services/accounting/server.go index 895ca47d94..24fb4558d7 100644 --- a/pkg/services/accounting/server.go +++ b/pkg/services/accounting/server.go @@ -46,7 +46,7 @@ func New(s *ecdsa.PrivateKey, net netmap.State, c BalanceContract) protoaccounti } } -func (s *server) makeBalanceResponse(body *protoaccounting.BalanceResponse_Body, st *protostatus.Status) (*protoaccounting.BalanceResponse, error) { +func (s *server) makeBalanceResponse(body *protoaccounting.BalanceResponse_Body, st *protostatus.Status, req *protoaccounting.BalanceRequest) (*protoaccounting.BalanceResponse, error) { resp := &protoaccounting.BalanceResponse{ Body: body, MetaHeader: &protosession.ResponseMetaHeader{ @@ -55,41 +55,41 @@ func (s *server) makeBalanceResponse(body *protoaccounting.BalanceResponse_Body, Status: st, }, } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *server) makeFailedBalanceResponse(err error) (*protoaccounting.BalanceResponse, error) { - return s.makeBalanceResponse(nil, util.ToStatus(err)) +func (s *server) makeFailedBalanceResponse(err error, req *protoaccounting.BalanceRequest) (*protoaccounting.BalanceResponse, error) { + return s.makeBalanceResponse(nil, util.ToStatus(err), req) } // Balance gets current balance of the requested user using underlying // [BalanceContract] and returns result in the response. func (s *server) Balance(_ context.Context, req *protoaccounting.BalanceRequest) (*protoaccounting.BalanceResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeFailedBalanceResponse(err) + return s.makeFailedBalanceResponse(err, nil) } mUsr := req.GetBody().GetOwnerId() if mUsr == nil { - return s.makeFailedBalanceResponse(errors.New("missing account")) + return s.makeFailedBalanceResponse(errors.New("missing account"), req) } var id user.ID if err := id.FromProtoMessage(mUsr); err != nil { - return s.makeFailedBalanceResponse(fmt.Errorf("invalid account: %w", err)) + return s.makeFailedBalanceResponse(fmt.Errorf("invalid account: %w", err), req) } bal, err := s.contract.BalanceOf(id) if err != nil { - return s.makeFailedBalanceResponse(err) + return s.makeFailedBalanceResponse(err, req) } ds, err := s.contract.Decimals() if err != nil { - return s.makeFailedBalanceResponse(err) + return s.makeFailedBalanceResponse(err, req) } body := &protoaccounting.BalanceResponse_Body{ Balance: &protoaccounting.Decimal{Value: bal.Int64(), Precision: ds}, } - return s.makeBalanceResponse(body, util.StatusOK) + return s.makeBalanceResponse(body, util.StatusOK, req) } diff --git a/pkg/services/container/server.go b/pkg/services/container/server.go index 68331825ef..189af21a6f 100644 --- a/pkg/services/container/server.go +++ b/pkg/services/container/server.go @@ -376,17 +376,17 @@ func (s *Server) verifySessionTokenV2AgainstRequest(token sessionv2.Token, reqVe return nil } -func (s *Server) makePutResponse(body *protocontainer.PutResponse_Body, err error) (*protocontainer.PutResponse, error) { +func (s *Server) makePutResponse(body *protocontainer.PutResponse_Body, err error, req *protocontainer.PutRequest) (*protocontainer.PutResponse, error) { resp := &protocontainer.PutResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *Server) makeFailedPutResponse(err error) (*protocontainer.PutResponse, error) { - return s.makePutResponse(nil, err) +func (s *Server) makeFailedPutResponse(err error, req *protocontainer.PutRequest) (*protocontainer.PutResponse, error) { + return s.makePutResponse(nil, err, req) } const ( @@ -442,40 +442,40 @@ func verifyStoragePolicy(policy *protonetmap.PlacementPolicy) error { // to check request status in the response. func (s *Server) Put(ctx context.Context, req *protocontainer.PutRequest) (*protocontainer.PutResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeFailedPutResponse(err) + return s.makeFailedPutResponse(err, req) } reqBody := req.GetBody() mSig := reqBody.GetSignature() if mSig == nil { - return s.makeFailedPutResponse(errors.New("missing container signature")) + return s.makeFailedPutResponse(errors.New("missing container signature"), req) } mCnr := reqBody.GetContainer() if mCnr == nil { - return s.makeFailedPutResponse(errors.New("missing container")) + return s.makeFailedPutResponse(errors.New("missing container"), req) } if mCnr.PlacementPolicy == nil { - return s.makeFailedPutResponse(errors.New("missing storage policy")) + return s.makeFailedPutResponse(errors.New("missing storage policy"), req) } if err := verifyStoragePolicy(mCnr.PlacementPolicy); err != nil { - return s.makeFailedPutResponse(fmt.Errorf("invalid storage policy: %w", err)) + return s.makeFailedPutResponse(fmt.Errorf("invalid storage policy: %w", err), req) } var cnr container.Container if err := cnr.FromProtoMessage(mCnr); err != nil { - return s.makeFailedPutResponse(fmt.Errorf("invalid container: %w", err)) + return s.makeFailedPutResponse(fmt.Errorf("invalid container: %w", err), req) } stV2, tokenBytes, err := s.getVerifiedSessionTokenV2FromMetaHeader(req.GetMetaHeader(), sessionv2.VerbContainerPut, cid.ID{}) if err != nil { - return s.makeFailedPutResponse(fmt.Errorf("verify session token v2: %w", err)) + 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{}) if err != nil { - return s.makeFailedPutResponse(fmt.Errorf("verify session token: %w", err)) + return s.makeFailedPutResponse(fmt.Errorf("verify session token: %w", err), req) } if st != nil { tokenBytes = b @@ -487,20 +487,20 @@ func (s *Server) Put(ctx context.Context, req *protocontainer.PutRequest) (*prot id, err := s.contract.Put(ctx, cnr, mSig.Key, mSig.Sign, tokenBytes) if err != nil && !errors.Is(err, apistatus.ErrContainerAwaitTimeout) { - return s.makeFailedPutResponse(err) + return s.makeFailedPutResponse(err, req) } respBody := &protocontainer.PutResponse_Body{ ContainerId: id.ProtoMessage(), } - return s.makePutResponse(respBody, err) + return s.makePutResponse(respBody, err, req) } -func (s *Server) makeDeleteResponse(err error) (*protocontainer.DeleteResponse, error) { +func (s *Server) makeDeleteResponse(err error, req *protocontainer.DeleteRequest) (*protocontainer.DeleteResponse, error) { resp := &protocontainer.DeleteResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } @@ -508,33 +508,33 @@ func (s *Server) makeDeleteResponse(err error) (*protocontainer.DeleteResponse, // further processing. If session token is attached, it's verified. func (s *Server) Delete(ctx context.Context, req *protocontainer.DeleteRequest) (*protocontainer.DeleteResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeDeleteResponse(err) + return s.makeDeleteResponse(err, req) } reqBody := req.GetBody() mSig := reqBody.GetSignature() if mSig == nil { - return s.makeDeleteResponse(errors.New("missing ID signature")) + return s.makeDeleteResponse(errors.New("missing ID signature"), req) } mID := reqBody.GetContainerId() if mID == nil { - return s.makeDeleteResponse(errors.New("missing ID")) + return s.makeDeleteResponse(errors.New("missing ID"), req) } var id cid.ID if err := id.FromProtoMessage(mID); err != nil { - return s.makeDeleteResponse(fmt.Errorf("invalid ID: %w", err)) + return s.makeDeleteResponse(fmt.Errorf("invalid ID: %w", err), req) } stV2, tokenBytes, err := s.getVerifiedSessionTokenV2FromMetaHeader(req.GetMetaHeader(), sessionv2.VerbContainerDelete, id) if err != nil { - return s.makeDeleteResponse(fmt.Errorf("verify session token v2: %w", err)) + 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) if err != nil { - return s.makeDeleteResponse(fmt.Errorf("verify session token: %w", err)) + return s.makeDeleteResponse(fmt.Errorf("verify session token: %w", err), req) } if st != nil { tokenBytes = b @@ -546,89 +546,89 @@ func (s *Server) Delete(ctx context.Context, req *protocontainer.DeleteRequest) err = s.contract.Delete(ctx, id, mSig.Key, mSig.Sign, tokenBytes) - return s.makeDeleteResponse(err) + return s.makeDeleteResponse(err, req) } -func (s *Server) makeGetResponse(body *protocontainer.GetResponse_Body, st *protostatus.Status) (*protocontainer.GetResponse, error) { +func (s *Server) makeGetResponse(body *protocontainer.GetResponse_Body, st *protostatus.Status, req *protocontainer.GetRequest) (*protocontainer.GetResponse, error) { resp := &protocontainer.GetResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(st), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *Server) makeFailedGetResponse(err error) (*protocontainer.GetResponse, error) { - return s.makeGetResponse(nil, util.ToStatus(err)) +func (s *Server) makeFailedGetResponse(err error, req *protocontainer.GetRequest) (*protocontainer.GetResponse, error) { + return s.makeGetResponse(nil, util.ToStatus(err), req) } // Get requests container from the underlying [Contract] and returns it in the // response. func (s *Server) Get(_ context.Context, req *protocontainer.GetRequest) (*protocontainer.GetResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeFailedGetResponse(err) + return s.makeFailedGetResponse(err, req) } mID := req.GetBody().GetContainerId() if mID == nil { - return s.makeFailedGetResponse(errors.New("missing ID")) + return s.makeFailedGetResponse(errors.New("missing ID"), req) } var id cid.ID if err := id.FromProtoMessage(mID); err != nil { - return s.makeFailedGetResponse(fmt.Errorf("invalid ID: %w", err)) + return s.makeFailedGetResponse(fmt.Errorf("invalid ID: %w", err), req) } cnr, err := s.contract.Get(id) if err != nil { - return s.makeFailedGetResponse(err) + return s.makeFailedGetResponse(err, req) } body := &protocontainer.GetResponse_Body{ Container: cnr.ProtoMessage(), } - return s.makeGetResponse(body, nil) + return s.makeGetResponse(body, nil, req) } -func (s *Server) makeListResponse(body *protocontainer.ListResponse_Body, st *protostatus.Status) (*protocontainer.ListResponse, error) { +func (s *Server) makeListResponse(body *protocontainer.ListResponse_Body, st *protostatus.Status, req *protocontainer.ListRequest) (*protocontainer.ListResponse, error) { resp := &protocontainer.ListResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(st), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *Server) makeFailedListResponse(err error) (*protocontainer.ListResponse, error) { - return s.makeListResponse(nil, util.ToStatus(err)) +func (s *Server) makeFailedListResponse(err error, req *protocontainer.ListRequest) (*protocontainer.ListResponse, error) { + return s.makeListResponse(nil, util.ToStatus(err), req) } // List lists user containers from the underlying [Contract] and returns their // IDs in the response. func (s *Server) List(_ context.Context, req *protocontainer.ListRequest) (*protocontainer.ListResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeFailedListResponse(err) + return s.makeFailedListResponse(err, req) } mID := req.GetBody().GetOwnerId() if mID == nil { - return s.makeFailedListResponse(errors.New("missing user")) + return s.makeFailedListResponse(errors.New("missing user"), req) } var id user.ID if len(mID.Value) != user.IDSize || !islices.AllZeros(mID.Value) { if err := id.FromProtoMessage(mID); err != nil { - return s.makeFailedListResponse(fmt.Errorf("invalid user: %w", err)) + return s.makeFailedListResponse(fmt.Errorf("invalid user: %w", err), req) } } cs, err := s.contract.List(id) if err != nil { - return s.makeFailedListResponse(err) + return s.makeFailedListResponse(err, req) } if len(cs) == 0 { - return s.makeListResponse(nil, util.StatusOK) + return s.makeListResponse(nil, util.StatusOK, req) } body := &protocontainer.ListResponse_Body{ @@ -637,14 +637,14 @@ func (s *Server) List(_ context.Context, req *protocontainer.ListRequest) (*prot for i := range cs { body.ContainerIds[i] = cs[i].ProtoMessage() } - return s.makeListResponse(body, util.StatusOK) + return s.makeListResponse(body, util.StatusOK, req) } -func (s *Server) makeSetEACLResponse(err error) (*protocontainer.SetExtendedACLResponse, error) { +func (s *Server) makeSetEACLResponse(err error, req *protocontainer.SetExtendedACLRequest) (*protocontainer.SetExtendedACLResponse, error) { resp := &protocontainer.SetExtendedACLResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } @@ -652,38 +652,38 @@ func (s *Server) makeSetEACLResponse(err error) (*protocontainer.SetExtendedACLR // for further processing. If session token is attached, it's verified. func (s *Server) SetExtendedACL(ctx context.Context, req *protocontainer.SetExtendedACLRequest) (*protocontainer.SetExtendedACLResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeSetEACLResponse(err) + return s.makeSetEACLResponse(err, req) } reqBody := req.GetBody() mSig := reqBody.GetSignature() if mSig == nil { - return s.makeSetEACLResponse(errors.New("missing eACL signature")) + return s.makeSetEACLResponse(errors.New("missing eACL signature"), req) } mEACL := reqBody.GetEacl() if mEACL == nil { - return s.makeSetEACLResponse(errors.New("missing eACL")) + return s.makeSetEACLResponse(errors.New("missing eACL"), req) } var eACL eacl.Table if err := eACL.FromProtoMessage(mEACL); err != nil { - return s.makeSetEACLResponse(fmt.Errorf("invalid eACL: %w", err)) + return s.makeSetEACLResponse(fmt.Errorf("invalid eACL: %w", err), req) } cnrID := eACL.GetCID() if cnrID.IsZero() { - return s.makeSetEACLResponse(errors.New("missing container ID in eACL table")) + return s.makeSetEACLResponse(errors.New("missing container ID in eACL table"), req) } stV2, tokenBytes, err := s.getVerifiedSessionTokenV2FromMetaHeader(req.GetMetaHeader(), sessionv2.VerbContainerSetEACL, cnrID) if err != nil { - return s.makeSetEACLResponse(fmt.Errorf("verify session token v2: %w", err)) + 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) if err != nil { - return s.makeSetEACLResponse(fmt.Errorf("verify session token: %w", err)) + return s.makeSetEACLResponse(fmt.Errorf("verify session token: %w", err), req) } if st != nil { tokenBytes = b @@ -695,48 +695,48 @@ func (s *Server) SetExtendedACL(ctx context.Context, req *protocontainer.SetExte err = s.contract.PutEACL(ctx, eACL, mSig.Key, mSig.Sign, tokenBytes) - return s.makeSetEACLResponse(err) + return s.makeSetEACLResponse(err, req) } -func (s *Server) makeGetEACLResponse(body *protocontainer.GetExtendedACLResponse_Body, st *protostatus.Status) (*protocontainer.GetExtendedACLResponse, error) { +func (s *Server) makeGetEACLResponse(body *protocontainer.GetExtendedACLResponse_Body, st *protostatus.Status, req *protocontainer.GetExtendedACLRequest) (*protocontainer.GetExtendedACLResponse, error) { resp := &protocontainer.GetExtendedACLResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(st), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *Server) makeFailedGetEACLResponse(err error) (*protocontainer.GetExtendedACLResponse, error) { - return s.makeGetEACLResponse(nil, util.ToStatus(err)) +func (s *Server) makeFailedGetEACLResponse(err error, req *protocontainer.GetExtendedACLRequest) (*protocontainer.GetExtendedACLResponse, error) { + return s.makeGetEACLResponse(nil, util.ToStatus(err), req) } // GetExtendedACL read eACL of the requested container from the underlying // [Contract] and returns the result in the response. func (s *Server) GetExtendedACL(_ context.Context, req *protocontainer.GetExtendedACLRequest) (*protocontainer.GetExtendedACLResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeFailedGetEACLResponse(err) + return s.makeFailedGetEACLResponse(err, req) } mID := req.GetBody().GetContainerId() if mID == nil { - return s.makeFailedGetEACLResponse(errors.New("missing ID")) + return s.makeFailedGetEACLResponse(errors.New("missing ID"), req) } var id cid.ID if err := id.FromProtoMessage(mID); err != nil { - return s.makeFailedGetEACLResponse(fmt.Errorf("invalid ID: %w", err)) + return s.makeFailedGetEACLResponse(fmt.Errorf("invalid ID: %w", err), req) } eACL, err := s.contract.GetEACL(id) if err != nil { - return s.makeFailedGetEACLResponse(err) + return s.makeFailedGetEACLResponse(err, req) } body := &protocontainer.GetExtendedACLResponse_Body{ Eacl: eACL.ProtoMessage(), } - return s.makeGetEACLResponse(body, util.StatusOK) + return s.makeGetEACLResponse(body, util.StatusOK, req) } func (s *Server) makeSetAttributeResponse(err error) (*protocontainer.SetAttributeResponse, error) { diff --git a/pkg/services/netmap/server.go b/pkg/services/netmap/server.go index 84184359cb..707eb0e9e2 100644 --- a/pkg/services/netmap/server.go +++ b/pkg/services/netmap/server.go @@ -57,95 +57,95 @@ func (s *server) makeResponseMetaHeader(st *protostatus.Status) *protosession.Re } } -func (s *server) makeNodeInfoResponse(body *protonetmap.LocalNodeInfoResponse_Body, st *protostatus.Status) (*protonetmap.LocalNodeInfoResponse, error) { +func (s *server) makeNodeInfoResponse(body *protonetmap.LocalNodeInfoResponse_Body, st *protostatus.Status, req *protonetmap.LocalNodeInfoRequest) (*protonetmap.LocalNodeInfoResponse, error) { resp := &protonetmap.LocalNodeInfoResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(st), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *server) makeStatusNodeInfoResponse(err error) (*protonetmap.LocalNodeInfoResponse, error) { - return s.makeNodeInfoResponse(nil, util.ToStatus(err)) +func (s *server) makeStatusNodeInfoResponse(err error, req *protonetmap.LocalNodeInfoRequest) (*protonetmap.LocalNodeInfoResponse, error) { + return s.makeNodeInfoResponse(nil, util.ToStatus(err), req) } // LocalNodeInfo returns current state of the local node from the underlying // [NodeState]. func (s server) LocalNodeInfo(_ context.Context, req *protonetmap.LocalNodeInfoRequest) (*protonetmap.LocalNodeInfoResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeStatusNodeInfoResponse(err) + return s.makeStatusNodeInfoResponse(err, req) } n, err := s.contract.LocalNodeInfo() if err != nil { - return s.makeStatusNodeInfoResponse(err) + return s.makeStatusNodeInfoResponse(err, req) } body := &protonetmap.LocalNodeInfoResponse_Body{ Version: currentProtoVersion(), NodeInfo: n.ProtoMessage(), } - return s.makeNodeInfoResponse(body, util.StatusOK) + return s.makeNodeInfoResponse(body, util.StatusOK, req) } -func (s *server) makeNetInfoResponse(body *protonetmap.NetworkInfoResponse_Body, st *protostatus.Status) (*protonetmap.NetworkInfoResponse, error) { +func (s *server) makeNetInfoResponse(body *protonetmap.NetworkInfoResponse_Body, st *protostatus.Status, req *protonetmap.NetworkInfoRequest) (*protonetmap.NetworkInfoResponse, error) { resp := &protonetmap.NetworkInfoResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(st), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *server) makeStatusNetInfoResponse(err error) (*protonetmap.NetworkInfoResponse, error) { - return s.makeNetInfoResponse(nil, util.ToStatus(err)) +func (s *server) makeStatusNetInfoResponse(err error, req *protonetmap.NetworkInfoRequest) (*protonetmap.NetworkInfoResponse, error) { + return s.makeNetInfoResponse(nil, util.ToStatus(err), req) } // NetworkInfo returns current network configuration from the underlying // [Contract]. func (s *server) NetworkInfo(_ context.Context, req *protonetmap.NetworkInfoRequest) (*protonetmap.NetworkInfoResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeStatusNetInfoResponse(err) + return s.makeStatusNetInfoResponse(err, req) } n, err := s.contract.GetNetworkInfo() if err != nil { - return s.makeStatusNetInfoResponse(err) + return s.makeStatusNetInfoResponse(err, req) } body := &protonetmap.NetworkInfoResponse_Body{ NetworkInfo: n.ProtoMessage(), } - return s.makeNetInfoResponse(body, util.StatusOK) + return s.makeNetInfoResponse(body, util.StatusOK, req) } -func (s *server) makeNetmapResponse(body *protonetmap.NetmapSnapshotResponse_Body, st *protostatus.Status) (*protonetmap.NetmapSnapshotResponse, error) { +func (s *server) makeNetmapResponse(body *protonetmap.NetmapSnapshotResponse_Body, st *protostatus.Status, req *protonetmap.NetmapSnapshotRequest) (*protonetmap.NetmapSnapshotResponse, error) { resp := &protonetmap.NetmapSnapshotResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(st), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *server) makeStatusNetmapResponse(err error) (*protonetmap.NetmapSnapshotResponse, error) { - return s.makeNetmapResponse(nil, util.ToStatus(err)) +func (s *server) makeStatusNetmapResponse(err error, req *protonetmap.NetmapSnapshotRequest) (*protonetmap.NetmapSnapshotResponse, error) { + return s.makeNetmapResponse(nil, util.ToStatus(err), req) } // NetmapSnapshot returns current network map from the underlying [Contract]. func (s *server) NetmapSnapshot(_ context.Context, req *protonetmap.NetmapSnapshotRequest) (*protonetmap.NetmapSnapshotResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeStatusNetmapResponse(err) + return s.makeStatusNetmapResponse(err, req) } n, err := s.contract.GetNetworkMap() if err != nil { - return s.makeStatusNetmapResponse(err) + return s.makeStatusNetmapResponse(err, req) } body := &protonetmap.NetmapSnapshotResponse_Body{ Netmap: n.ProtoMessage(), } - return s.makeNetmapResponse(body, util.StatusOK) + return s.makeNetmapResponse(body, util.StatusOK, req) } diff --git a/pkg/services/object/server.go b/pkg/services/object/server.go index 8bfed63ef9..5af96c13a2 100644 --- a/pkg/services/object/server.go +++ b/pkg/services/object/server.go @@ -237,7 +237,7 @@ func (s *Server) makeResponseMetaHeader(st *protostatus.Status) *protosession.Re } } -func (s *Server) sendPutResponse(stream protoobject.ObjectService_PutServer, resp *protoobject.PutResponse, err error) error { +func (s *Server) sendPutResponse(stream protoobject.ObjectService_PutServer, resp *protoobject.PutResponse, err error, req *protoobject.PutRequest) error { if resp == nil { resp = new(protoobject.PutResponse) } @@ -245,12 +245,12 @@ func (s *Server) sendPutResponse(stream protoobject.ObjectService_PutServer, res resp.MetaHeader = s.makeResponseMetaHeader(util.ToStatus(err)) } - resp.VerifyHeader = util.SignResponse(&s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(&s.signer, resp, req) return stream.SendAndClose(resp) } -func (s *Server) sendStatusPutResponse(stream protoobject.ObjectService_PutServer, err error) error { - return s.sendPutResponse(stream, nil, err) +func (s *Server) sendStatusPutResponse(stream protoobject.ObjectService_PutServer, err error, req *protoobject.PutRequest) error { + return s.sendPutResponse(stream, nil, err, req) } type putStream struct { @@ -429,7 +429,7 @@ func (s *Server) Put(gStream protoobject.ObjectService_PutServer) error { return err } - var req *protoobject.PutRequest + var req, reqFirst *protoobject.PutRequest var resp *protoobject.PutResponse ps := newIntermediatePutStream(s.signer, stream, gStream.Context()) @@ -437,23 +437,27 @@ func (s *Server) Put(gStream protoobject.ObjectService_PutServer) error { if req, err = gStream.Recv(); err != nil { if errors.Is(err, io.EOF) { resp, err = ps.close() - err = s.sendPutResponse(gStream, resp, err) + err = s.sendPutResponse(gStream, resp, err, reqFirst) return err } return err } + if reqFirst == nil { + reqFirst = req + } + if c := req.GetBody().GetChunk(); c != nil { s.metrics.AddPutPayload(len(c)) } if err = icrypto.VerifyRequestSignaturesN3(req, s.fsChain); err != nil { - err = s.sendStatusPutResponse(gStream, err) // assign for defer + err = s.sendStatusPutResponse(gStream, err, reqFirst) // assign for defer return err } if s.fsChain.LocalNodeUnderMaintenance() { - return s.sendStatusPutResponse(gStream, apistatus.ErrNodeUnderMaintenance) + return s.sendStatusPutResponse(gStream, apistatus.ErrNodeUnderMaintenance, reqFirst) } if req.Body == nil { @@ -470,37 +474,37 @@ func (s *Server) Put(gStream protoobject.ObjectService_PutServer) error { bad.SetMessage(err.Error()) err = bad // defer } - return s.sendStatusPutResponse(gStream, err) + return s.sendStatusPutResponse(gStream, err, reqFirst) } } else { if !s.aclChecker.CheckBasicACL(reqInfo) || !s.aclChecker.StickyBitCheck(reqInfo, objOwner) { err = basicACLErr(reqInfo) // needed for defer - return s.sendStatusPutResponse(gStream, err) + return s.sendStatusPutResponse(gStream, err, reqFirst) } err = s.aclChecker.CheckEACL(req, reqInfo) if err != nil && !errors.Is(err, aclsvc.ErrNotMatched) { // Not matched -> follow basic ACL. err = eACLErr(reqInfo, err) // needed for defer - return s.sendStatusPutResponse(gStream, err) + return s.sendStatusPutResponse(gStream, err, reqFirst) } } if err = ps.forwardRequest(req); err != nil { - err = s.sendStatusPutResponse(gStream, err) // assign for defer + err = s.sendStatusPutResponse(gStream, err, reqFirst) // assign for defer return err } } } -func (s *Server) signDeleteResponse(resp *protoobject.DeleteResponse, err error) *protoobject.DeleteResponse { +func (s *Server) signDeleteResponse(resp *protoobject.DeleteResponse, err error, req *protoobject.DeleteRequest) *protoobject.DeleteResponse { if err != nil { resp.MetaHeader = s.makeResponseMetaHeader(util.ToStatus(err)) } - resp.VerifyHeader = util.SignResponse(&s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(&s.signer, resp, req) return resp } -func (s *Server) makeStatusDeleteResponse(err error) *protoobject.DeleteResponse { - return s.signDeleteResponse(new(protoobject.DeleteResponse), err) +func (s *Server) makeStatusDeleteResponse(err error, req *protoobject.DeleteRequest) *protoobject.DeleteResponse { + return s.signDeleteResponse(new(protoobject.DeleteResponse), err, req) } type deleteResponseBody protoobject.DeleteResponse_Body @@ -517,11 +521,11 @@ func (s *Server) Delete(ctx context.Context, req *protoobject.DeleteRequest) (*p defer func() { s.pushOpExecResult(stat.MethodObjectDelete, err, t) }() if err = icrypto.VerifyRequestSignaturesN3(req, s.fsChain); err != nil { - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } if s.fsChain.LocalNodeUnderMaintenance() { - return s.makeStatusDeleteResponse(apistatus.ErrNodeUnderMaintenance), nil + return s.makeStatusDeleteResponse(apistatus.ErrNodeUnderMaintenance, req), nil } reqInfo, err := s.reqInfoProc.DeleteRequestToInfo(req) @@ -531,16 +535,16 @@ func (s *Server) Delete(ctx context.Context, req *protoobject.DeleteRequest) (*p bad.SetMessage(err.Error()) err = bad // defer } - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } if !s.aclChecker.CheckBasicACL(reqInfo) { err = basicACLErr(reqInfo) // needed for defer - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } err = s.aclChecker.CheckEACL(req, reqInfo) if err != nil && !errors.Is(err, aclsvc.ErrNotMatched) { // Not matched -> follow basic ACL. err = eACLErr(reqInfo, err) // needed for defer - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } ma := req.GetBody().GetAddress() @@ -548,7 +552,7 @@ func (s *Server) Delete(ctx context.Context, req *protoobject.DeleteRequest) (*p var bad = new(apistatus.BadRequest) bad.SetMessage("malformed request: missing object address") err = bad // defer - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } var addr oid.Address err = addr.FromProtoMessage(ma) @@ -556,7 +560,7 @@ func (s *Server) Delete(ctx context.Context, req *protoobject.DeleteRequest) (*p var bad = new(apistatus.BadRequest) bad.SetMessage(fmt.Sprintf("invalid object address: %s", err.Error())) err = bad // defer - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } cp, err := objutil.CommonPrmFromRequest(req) @@ -564,7 +568,7 @@ func (s *Server) Delete(ctx context.Context, req *protoobject.DeleteRequest) (*p var bad = new(apistatus.BadRequest) bad.SetMessage(fmt.Sprintf("invalid object address: %s", err.Error())) err = bad // defer - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } var rb protoobject.DeleteResponse_Body @@ -575,10 +579,10 @@ func (s *Server) Delete(ctx context.Context, req *protoobject.DeleteRequest) (*p p.WithTombstoneAddressTarget((*deleteResponseBody)(&rb)) err = s.handlers.Delete(ctx, p) if err != nil && !errors.Is(err, apistatus.ErrIncomplete) { - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } - return s.signDeleteResponse(&protoobject.DeleteResponse{Body: &rb}, err), nil + return s.signDeleteResponse(&protoobject.DeleteResponse{Body: &rb}, err, req), nil } func (s *Server) signHeadResponse(resp *protoobject.HeadResponse, sign bool) *protoobject.HeadResponse { @@ -804,15 +808,15 @@ func getHeaderFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *pr return obj, nil } -func (s *Server) signHashResponse(resp *protoobject.GetRangeHashResponse) *protoobject.GetRangeHashResponse { - resp.VerifyHeader = util.SignResponse(&s.signer, resp) +func (s *Server) signHashResponse(resp *protoobject.GetRangeHashResponse, req *protoobject.GetRangeHashRequest) *protoobject.GetRangeHashResponse { + resp.VerifyHeader = util.SignResponseIfNeeded(&s.signer, resp, req) return resp } -func (s *Server) makeStatusHashResponse(err error) *protoobject.GetRangeHashResponse { +func (s *Server) makeStatusHashResponse(err error, req *protoobject.GetRangeHashRequest) *protoobject.GetRangeHashResponse { return s.signHashResponse(&protoobject.GetRangeHashResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), - }) + }, req) } // GetRangeHash converts gRPC GetRangeHashRequest message and passes it to internal Object service. @@ -823,11 +827,11 @@ func (s *Server) GetRangeHash(ctx context.Context, req *protoobject.GetRangeHash ) defer func() { s.pushOpExecResult(stat.MethodObjectHash, err, t) }() if err = icrypto.VerifyRequestSignaturesN3(req, s.fsChain); err != nil { - return s.makeStatusHashResponse(err), nil + return s.makeStatusHashResponse(err, req), nil } if s.fsChain.LocalNodeUnderMaintenance() { - return s.makeStatusHashResponse(apistatus.ErrNodeUnderMaintenance), nil + return s.makeStatusHashResponse(apistatus.ErrNodeUnderMaintenance, req), nil } reqInfo, err := s.reqInfoProc.HashRequestToInfo(req) @@ -837,16 +841,16 @@ func (s *Server) GetRangeHash(ctx context.Context, req *protoobject.GetRangeHash bad.SetMessage(err.Error()) err = bad // defer } - return s.makeStatusHashResponse(err), nil + return s.makeStatusHashResponse(err, req), nil } if !s.aclChecker.CheckBasicACL(reqInfo) { err = basicACLErr(reqInfo) // needed for defer - return s.makeStatusHashResponse(err), nil + return s.makeStatusHashResponse(err, req), nil } err = s.aclChecker.CheckEACL(req, reqInfo) if err != nil && !errors.Is(err, aclsvc.ErrNotMatched) { // Not matched -> follow basic ACL. err = eACLErr(reqInfo, err) // needed for defer - return s.makeStatusHashResponse(err), nil + return s.makeStatusHashResponse(err, req), nil } p, err := convertHashPrm(s.signer, s.storage, req) @@ -856,18 +860,18 @@ func (s *Server) GetRangeHash(ctx context.Context, req *protoobject.GetRangeHash bad.SetMessage(err.Error()) err = bad // defer } - return s.makeStatusHashResponse(err), nil + return s.makeStatusHashResponse(err, req), nil } res, err := s.handlers.GetRangeHash(ctx, p) if err != nil { - return s.makeStatusHashResponse(err), nil + return s.makeStatusHashResponse(err, req), nil } return s.signHashResponse(&protoobject.GetRangeHashResponse{ Body: &protoobject.GetRangeHashResponse_Body{ Type: req.Body.Type, HashList: res.Hashes(), - }}), nil + }}, req), nil } // converts original request into parameters accepted by the internal handler. @@ -1303,12 +1307,12 @@ func (x *getProxyContext) continueWithConn(ctx context.Context, conn *grpc.Clien } } -func (s *Server) sendRangeResponse(stream protoobject.ObjectService_GetRangeServer, resp *protoobject.GetRangeResponse) error { - resp.VerifyHeader = util.SignResponse(&s.signer, resp) +func (s *Server) sendRangeResponse(stream protoobject.ObjectService_GetRangeServer, resp *protoobject.GetRangeResponse, req *protoobject.GetRangeRequest) error { + resp.VerifyHeader = util.SignResponseIfNeeded(&s.signer, resp, req) return stream.Send(resp) } -func (s *Server) sendStatusRangeResponse(stream protoobject.ObjectService_GetRangeServer, err error) error { +func (s *Server) sendStatusRangeResponse(stream protoobject.ObjectService_GetRangeServer, err error, req *protoobject.GetRangeRequest) error { var splitErr *object.SplitInfoError if errors.As(err, &splitErr) { return s.sendRangeResponse(stream, &protoobject.GetRangeResponse{ @@ -1317,16 +1321,17 @@ func (s *Server) sendStatusRangeResponse(stream protoobject.ObjectService_GetRan SplitInfo: splitErr.SplitInfo().ProtoMessage(), }, }, - }) + }, req) } return s.sendRangeResponse(stream, &protoobject.GetRangeResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), - }) + }, req) } type rangeStream struct { base protoobject.ObjectService_GetRangeServer srv *Server + req *protoobject.GetRangeRequest } func (s *rangeStream) WriteChunk(chunk []byte) error { @@ -1338,7 +1343,7 @@ func (s *rangeStream) WriteChunk(chunk []byte) error { }, }, } - if err := s.srv.sendRangeResponse(s.base, newResp); err != nil { + if err := s.srv.sendRangeResponse(s.base, newResp, s.req); err != nil { return err } } @@ -1352,11 +1357,11 @@ func (s *Server) GetRange(req *protoobject.GetRangeRequest, gStream protoobject. ) defer func() { s.pushOpExecResult(stat.MethodObjectRange, err, t) }() if err = icrypto.VerifyRequestSignaturesN3(req, s.fsChain); err != nil { - return s.sendStatusRangeResponse(gStream, err) + return s.sendStatusRangeResponse(gStream, err, req) } if s.fsChain.LocalNodeUnderMaintenance() { - return s.sendStatusRangeResponse(gStream, apistatus.ErrNodeUnderMaintenance) + return s.sendStatusRangeResponse(gStream, apistatus.ErrNodeUnderMaintenance, req) } reqInfo, err := s.reqInfoProc.RangeRequestToInfo(req) @@ -1366,21 +1371,22 @@ func (s *Server) GetRange(req *protoobject.GetRangeRequest, gStream protoobject. bad.SetMessage(err.Error()) err = bad // defer } - return s.sendStatusRangeResponse(gStream, err) + return s.sendStatusRangeResponse(gStream, err, req) } if !s.aclChecker.CheckBasicACL(reqInfo) { err = basicACLErr(reqInfo) // needed for defer - return s.sendStatusRangeResponse(gStream, err) + return s.sendStatusRangeResponse(gStream, err, req) } err = s.aclChecker.CheckEACL(req, reqInfo) if err != nil && !errors.Is(err, aclsvc.ErrNotMatched) { // Not matched -> follow basic ACL. err = eACLErr(reqInfo, err) // needed for defer - return s.sendStatusRangeResponse(gStream, err) + return s.sendStatusRangeResponse(gStream, err, req) } p, err := convertRangePrm(s.signer, req, &rangeStream{ base: gStream, srv: s, + req: req, }) if err != nil { if !errors.Is(err, apistatus.Error) { @@ -1388,11 +1394,11 @@ func (s *Server) GetRange(req *protoobject.GetRangeRequest, gStream protoobject. bad.SetMessage(err.Error()) err = bad // defer } - return s.sendStatusRangeResponse(gStream, err) + return s.sendStatusRangeResponse(gStream, err, req) } err = s.handlers.GetRange(gStream.Context(), p) if err != nil { - return s.sendStatusRangeResponse(gStream, err) + return s.sendStatusRangeResponse(gStream, err, req) } return nil } @@ -1527,20 +1533,21 @@ func continueRangeFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, nod } } -func (s *Server) sendSearchResponse(stream protoobject.ObjectService_SearchServer, resp *protoobject.SearchResponse) error { - resp.VerifyHeader = util.SignResponse(&s.signer, resp) +func (s *Server) sendSearchResponse(stream protoobject.ObjectService_SearchServer, resp *protoobject.SearchResponse, req *protoobject.SearchRequest) error { + resp.VerifyHeader = util.SignResponseIfNeeded(&s.signer, resp, req) return stream.Send(resp) } -func (s *Server) sendStatusSearchResponse(stream protoobject.ObjectService_SearchServer, err error) error { +func (s *Server) sendStatusSearchResponse(stream protoobject.ObjectService_SearchServer, err error, req *protoobject.SearchRequest) error { return s.sendSearchResponse(stream, &protoobject.SearchResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), - }) + }, req) } type searchStream struct { base protoobject.ObjectService_SearchServer srv *Server + req *protoobject.SearchRequest } func (s *searchStream) WriteIDs(ids []oid.ID) error { @@ -1556,7 +1563,7 @@ func (s *searchStream) WriteIDs(ids []oid.ID) error { for i := range cut { r.Body.IdList[i] = ids[i].ProtoMessage() } - if err := s.srv.sendSearchResponse(s.base, r); err != nil { + if err := s.srv.sendSearchResponse(s.base, r, s.req); err != nil { return err } @@ -1572,11 +1579,11 @@ func (s *Server) Search(req *protoobject.SearchRequest, gStream protoobject.Obje ) defer func() { s.pushOpExecResult(stat.MethodObjectSearch, err, t) }() if err = icrypto.VerifyRequestSignaturesN3(req, s.fsChain); err != nil { - return s.sendStatusSearchResponse(gStream, err) + return s.sendStatusSearchResponse(gStream, err, req) } if s.fsChain.LocalNodeUnderMaintenance() { - return s.sendStatusSearchResponse(gStream, apistatus.ErrNodeUnderMaintenance) + return s.sendStatusSearchResponse(gStream, apistatus.ErrNodeUnderMaintenance, req) } reqInfo, err := s.reqInfoProc.SearchRequestToInfo(req) @@ -1586,21 +1593,22 @@ func (s *Server) Search(req *protoobject.SearchRequest, gStream protoobject.Obje bad.SetMessage(err.Error()) err = bad // defer } - return s.sendStatusSearchResponse(gStream, err) + return s.sendStatusSearchResponse(gStream, err, req) } if !s.aclChecker.CheckBasicACL(reqInfo) { err = basicACLErr(reqInfo) // needed for defer - return s.sendStatusSearchResponse(gStream, err) + return s.sendStatusSearchResponse(gStream, err, req) } err = s.aclChecker.CheckEACL(req, reqInfo) if err != nil && !errors.Is(err, aclsvc.ErrNotMatched) { // Not matched -> follow basic ACL. err = eACLErr(reqInfo, err) - return s.sendStatusSearchResponse(gStream, err) + return s.sendStatusSearchResponse(gStream, err, req) } p, err := convertSearchPrm(gStream.Context(), s.signer, req, &searchStream{ base: gStream, srv: s, + req: req, }) if err != nil { if !errors.Is(err, apistatus.Error) { @@ -1608,11 +1616,11 @@ func (s *Server) Search(req *protoobject.SearchRequest, gStream protoobject.Obje bad.SetMessage(err.Error()) err = bad // defer } - return s.sendStatusSearchResponse(gStream, err) + return s.sendStatusSearchResponse(gStream, err, req) } err = s.handlers.Search(gStream.Context(), p) if err != nil { - return s.sendStatusSearchResponse(gStream, err) + return s.sendStatusSearchResponse(gStream, err, req) } return nil } @@ -1893,7 +1901,7 @@ func (s *Server) Replicate(_ context.Context, req *protoobject.ReplicateRequest) return resp, nil } -func (s *Server) signSearchResponse(body *protoobject.SearchV2Response_Body, err error) *protoobject.SearchV2Response { +func (s *Server) signSearchResponse(body *protoobject.SearchV2Response_Body, err error, req *protoobject.SearchV2Request) *protoobject.SearchV2Response { var resp = new(protoobject.SearchV2Response) if err != nil { @@ -1902,7 +1910,7 @@ func (s *Server) signSearchResponse(body *protoobject.SearchV2Response_Body, err if err == nil || errors.Is(err, apistatus.ErrIncomplete) { resp.Body = body } - resp.VerifyHeader = util.SignResponse(&s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(&s.signer, resp, req) return resp } @@ -1913,11 +1921,11 @@ func (s *Server) SearchV2(ctx context.Context, req *protoobject.SearchV2Request) ) defer s.pushOpExecResult(stat.MethodObjectSearchV2, err, t) if err = icrypto.VerifyRequestSignaturesN3(req, s.fsChain); err != nil { - return s.signSearchResponse(nil, err), nil + return s.signSearchResponse(nil, err, req), nil } if s.fsChain.LocalNodeUnderMaintenance() { - return s.signSearchResponse(nil, apistatus.ErrNodeUnderMaintenance), nil + return s.signSearchResponse(nil, apistatus.ErrNodeUnderMaintenance, req), nil } reqInfo, err := s.reqInfoProc.SearchV2RequestToInfo(req) @@ -1927,19 +1935,21 @@ func (s *Server) SearchV2(ctx context.Context, req *protoobject.SearchV2Request) bad.SetMessage(err.Error()) err = bad // defer } - return s.signSearchResponse(nil, err), nil + return s.signSearchResponse(nil, err, req), nil } if !s.aclChecker.CheckBasicACL(reqInfo) { err = basicACLErr(reqInfo) // needed for defer - return s.signSearchResponse(nil, err), nil + return s.signSearchResponse(nil, err, req), nil } err = s.aclChecker.CheckEACL(req, reqInfo) if err != nil && !errors.Is(err, aclsvc.ErrNotMatched) { // Not matched -> follow basic ACL. err = eACLErr(reqInfo, err) - return s.signSearchResponse(nil, err), nil + return s.signSearchResponse(nil, err, req), nil } - return s.signSearchResponse(s.processSearchRequest(ctx, req)), nil + respBody, err := s.processSearchRequest(ctx, req) + + return s.signSearchResponse(respBody, err, req), nil } func verifySearchFilter(f *protoobject.SearchFilter) error { @@ -2358,12 +2368,8 @@ func chunkToSend(global, local int, chunk []byte) []byte { return chunk[global-local:] } -func needSignGetResponse(req interface { - GetMetaHeader() *protosession.RequestMetaHeader -}) bool { - ver := req.GetMetaHeader().GetVersion() - mjr := ver.GetMajor() // NPE-safe - return mjr < 2 || mjr == 2 && ver.GetMinor() <= 17 +func needSignGetResponse(req util.Request) bool { + return util.VersionLE(req, 2, 17) } func checkHeaderAgainstID(hdr *protoobject.Header, id oid.ID) error { diff --git a/pkg/services/session/server.go b/pkg/services/session/server.go index fd70a52ee7..14c0cc6645 100644 --- a/pkg/services/session/server.go +++ b/pkg/services/session/server.go @@ -46,7 +46,7 @@ func New(s *ecdsa.PrivateKey, net netmap.State, ks KeyStorage) protosession.Sess } } -func (s *server) makeCreateResponse(body *protosession.CreateResponse_Body, st *protostatus.Status) (*protosession.CreateResponse, error) { +func (s *server) makeCreateResponse(body *protosession.CreateResponse_Body, st *protostatus.Status, req *protosession.CreateRequest) (*protosession.CreateResponse, error) { resp := &protosession.CreateResponse{ Body: body, MetaHeader: &protosession.ResponseMetaHeader{ @@ -55,50 +55,50 @@ func (s *server) makeCreateResponse(body *protosession.CreateResponse_Body, st * Status: st, }, } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *server) makeFailedCreateResponse(err error) (*protosession.CreateResponse, error) { - return s.makeCreateResponse(nil, util.ToStatus(err)) +func (s *server) makeFailedCreateResponse(err error, req *protosession.CreateRequest) (*protosession.CreateResponse, error) { + return s.makeCreateResponse(nil, util.ToStatus(err), req) } // Create generates new private session key and saves it in the underlying // [KeyStorage]. func (s *server) Create(_ context.Context, req *protosession.CreateRequest) (*protosession.CreateResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeFailedCreateResponse(err) + return s.makeFailedCreateResponse(err, req) } reqBody := req.GetBody() mUsr := reqBody.GetOwnerId() if mUsr == nil { - return s.makeFailedCreateResponse(errors.New("missing account")) + return s.makeFailedCreateResponse(errors.New("missing account"), req) } var usr user.ID if err := usr.FromProtoMessage(mUsr); err != nil { - return s.makeFailedCreateResponse(fmt.Errorf("invalid account: %w", err)) + return s.makeFailedCreateResponse(fmt.Errorf("invalid account: %w", err), req) } key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { - return s.makeFailedCreateResponse(fmt.Errorf("generate private key: %w", err)) + return s.makeFailedCreateResponse(fmt.Errorf("generate private key: %w", err), req) } uid := uuid.New() if err := s.keys.Store(*key, usr, uid[:], reqBody.Expiration); err != nil { - return s.makeFailedCreateResponse(fmt.Errorf("store private key locally: %w", err)) + return s.makeFailedCreateResponse(fmt.Errorf("store private key locally: %w", err), req) } // also store the key using account as key ID keyUser := user.NewFromECDSAPublicKey(key.PublicKey) if err := s.keys.Store(*key, usr, keyUser[:], reqBody.Expiration); err != nil { - return s.makeFailedCreateResponse(fmt.Errorf("store private key with public key locally: %w", err)) + return s.makeFailedCreateResponse(fmt.Errorf("store private key with public key locally: %w", err), req) } body := &protosession.CreateResponse_Body{ Id: uid[:], SessionKey: neofscrypto.PublicKeyBytes((*neofsecdsa.PublicKey)(&key.PublicKey)), } - return s.makeCreateResponse(body, util.StatusOK) + return s.makeCreateResponse(body, util.StatusOK, req) } diff --git a/pkg/services/util/sign.go b/pkg/services/util/sign.go index 501a043f7a..48968e27be 100644 --- a/pkg/services/util/sign.go +++ b/pkg/services/util/sign.go @@ -11,6 +11,29 @@ import ( protostatus "github.com/nspcc-dev/neofs-sdk-go/proto/status" ) +// Request is a common interface of API request messages. +type Request interface { + GetMetaHeader() *protosession.RequestMetaHeader +} + +// VersionLE checks whether proto version in request meta header is less or +// equal than the specified one. +func VersionLE(req Request, mjr, mnr uint32) bool { + ver := req.GetMetaHeader().GetVersion() + gotMjr := ver.GetMajor() // NPE-safe + return gotMjr < mjr || gotMjr == mjr && ver.GetMinor() <= mnr +} + +// SignResponseIfNeeded checks whether response for the req should be signed. If +// so, calculated verification header is returned. Otherwise, nil returns. +func SignResponseIfNeeded[R sdkcrypto.ProtoMessage](signer *ecdsa.PrivateKey, r sdkcrypto.SignedResponse[R], req Request) *protosession.ResponseVerificationHeader { + if VersionLE(req, 2, 21) { + return SignResponse(signer, r) + } + + return nil +} + func SignResponse[R sdkcrypto.ProtoMessage](signer *ecdsa.PrivateKey, r sdkcrypto.SignedResponse[R]) *protosession.ResponseVerificationHeader { verHeader, err := sdkcrypto.SignResponseWithBuffer(sdkecdsa.Signer(*signer), r, nil) if err != nil { diff --git a/pkg/services/util/sign_test.go b/pkg/services/util/sign_test.go new file mode 100644 index 0000000000..6f22d7e06d --- /dev/null +++ b/pkg/services/util/sign_test.go @@ -0,0 +1,49 @@ +package util_test + +import ( + "testing" + + "github.com/nspcc-dev/neofs-node/pkg/services/util" + neofscryptotest "github.com/nspcc-dev/neofs-sdk-go/crypto/test" + protoobject "github.com/nspcc-dev/neofs-sdk-go/proto/object" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" + protosession "github.com/nspcc-dev/neofs-sdk-go/proto/session" + "github.com/stretchr/testify/require" +) + +func TestVersionLE(t *testing.T) { + req := new(protoobject.GetRequest) // any + + require.True(t, util.VersionLE(req, 2, 0)) + require.True(t, util.VersionLE(req, 0, 1)) + + req.MetaHeader = &protosession.RequestMetaHeader{ + Version: &refs.Version{Major: 2, Minor: 21}, + } + + require.True(t, util.VersionLE(req, 2, 21)) + require.True(t, util.VersionLE(req, 2, 22)) + + require.False(t, util.VersionLE(req, 2, 20)) + require.False(t, util.VersionLE(req, 1, 22)) +} + +func TestSignResponseIfNeeded(t *testing.T) { + req := new(protoobject.GetRequest) + resp := new(protoobject.GetResponse) + key := neofscryptotest.Signer().ECDSAPrivateKey + + require.NotNil(t, util.SignResponseIfNeeded(&key, resp, req)) + + req.MetaHeader = &protosession.RequestMetaHeader{ + Version: &refs.Version{Major: 2, Minor: 21}, + } + require.NotNil(t, util.SignResponseIfNeeded(&key, resp, req)) + + req.MetaHeader.Version.Minor = 22 + require.Nil(t, util.SignResponseIfNeeded(&key, resp, req)) + + req.MetaHeader.Version.Major = 3 + req.MetaHeader.Version.Minor = 0 + require.Nil(t, util.SignResponseIfNeeded(&key, resp, req)) +} From eb7a12c152342838aa1bf46e6a969d256dcc7cdb Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 26 Jan 2026 15:38:37 +0300 Subject: [PATCH 2/6] sn/object: Ignore verification header of responses to forwarded requests Refs #3396. Signed-off-by: Leonard Lyubich --- pkg/services/object/server.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/pkg/services/object/server.go b/pkg/services/object/server.go index 5af96c13a2..c98bd44783 100644 --- a/pkg/services/object/server.go +++ b/pkg/services/object/server.go @@ -305,9 +305,6 @@ func putToRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []byte, if err := internal.VerifyResponseKeyV2(nodePub, resp); err != nil { return err } - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return fmt.Errorf("response verification failed: %w", err) - } if err := checkStatus(resp.GetMetaHeader().GetStatus()); err != nil { return fmt.Errorf("remote node response: %w", err) } @@ -982,9 +979,6 @@ func getHashesFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub if err := internal.VerifyResponseKeyV2(nodePub, resp); err != nil { return nil, err } - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return nil, fmt.Errorf("response verification failed: %w", err) - } if err := checkStatus(resp.GetMetaHeader().GetStatus()); err != nil { return nil, err } @@ -1497,9 +1491,6 @@ func continueRangeFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, nod if err = internal.VerifyResponseKeyV2(nodePub, resp); err != nil { return err } - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return fmt.Errorf("response verification failed: %w", err) - } if err := checkStatus(resp.GetMetaHeader().GetStatus()); err != nil { return err } @@ -1710,9 +1701,6 @@ func searchOnRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []by if err := internal.VerifyResponseKeyV2(nodePub, resp); err != nil { return nil, err } - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return nil, fmt.Errorf("could not verify %T: %w", resp, err) - } if err := checkStatus(resp.GetMetaHeader().GetStatus()); err != nil { return nil, fmt.Errorf("remote node response: %w", err) } @@ -2233,9 +2221,6 @@ func searchOnRemoteAddress(ctx context.Context, conn *grpc.ClientConn, nodePub [ if !bytes.Equal(resp.GetVerifyHeader().GetBodySignature().GetKey(), nodePub) { return nil, false, client.ErrWrongPublicKey } - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return nil, false, fmt.Errorf("response verification failed: %w", err) - } if err := apistatus.ToError(resp.GetMetaHeader().GetStatus()); err != nil { return nil, false, err } From 6a39e2d2a04bd457f05f0645f942887f4de9fed9 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 26 Jan 2026 15:40:27 +0300 Subject: [PATCH 3/6] cli: Ignore verification headers in `request` commands Refs #3396. Signed-off-by: Leonard Lyubich --- cmd/neofs-cli/modules/request/container.go | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/cmd/neofs-cli/modules/request/container.go b/cmd/neofs-cli/modules/request/container.go index cb0703ee7b..2b87f18547 100644 --- a/cmd/neofs-cli/modules/request/container.go +++ b/cmd/neofs-cli/modules/request/container.go @@ -161,13 +161,7 @@ func createContainer(cmd *cobra.Command, args []string) error { return fmt.Errorf("transport failure: %w", err) } - cmd.Println("Response received. Checking signatures...") - - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return fmt.Errorf("failed to verify response signatures: %w", err) - } - - cmd.Println("Signatures are valid. Checking status...") + cmd.Println("Response received. Checking status...") if err := apistatus.ToError(resp.GetMetaHeader().GetStatus()); err != nil { return fmt.Errorf("status failure: %w", err) @@ -210,13 +204,7 @@ func createContainer(cmd *cobra.Command, args []string) error { return fmt.Errorf("transport failure: %w", err) } - cmd.Println("Response received. Checking signatures...") - - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return fmt.Errorf("failed to verify response signatures: %w", err) - } - - cmd.Println("Signatures are valid. Checking status...") + cmd.Println("Response received. Checking status...") if err := apistatus.ToError(resp.GetMetaHeader().GetStatus()); err == nil { cmd.Println("Status OK. Operation succeeded.") From e8a39dc11ac13e013c2daec5c84f1d5e5f213e6d Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 26 Jan 2026 15:44:05 +0300 Subject: [PATCH 4/6] sn,cli,ir: Ignore response verification headers in API client from SDK Refs #3396. Signed-off-by: Leonard Lyubich --- CHANGELOG.md | 1 + cmd/neofs-node/config.go | 3 ++- cmd/neofs-node/netmap.go | 4 +-- cmd/neofs-node/object.go | 10 ++++---- cmd/neofs-node/reputation/common/remote.go | 7 +++--- cmd/neofs-node/transport.go | 2 +- go.mod | 2 +- go.sum | 4 +-- pkg/network/cache/clients.go | 25 +++++++++++-------- pkg/services/object/get/exec.go | 2 +- pkg/services/object/get/get.go | 4 +-- pkg/services/object/get/get_test.go | 2 +- pkg/services/object/get/service.go | 4 +-- pkg/services/object/get/util.go | 18 ++++++------- pkg/services/object/head/remote.go | 6 ++--- pkg/services/object/put/remote.go | 4 +-- pkg/services/object/put/service.go | 2 +- pkg/services/object/put/service_test.go | 2 +- pkg/services/object/put/streamer.go | 2 +- pkg/services/object/search/container.go | 2 +- pkg/services/object/search/search_test.go | 2 +- pkg/services/object/search/service.go | 4 +-- pkg/services/object/search/util.go | 4 +-- pkg/services/object/server.go | 2 +- pkg/services/object/server_test.go | 2 +- .../reputation/common/router/calls.go | 2 +- pkg/services/reputation/common/router/deps.go | 4 ++- 27 files changed, 67 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a8e400fe2..3bb10b0343 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Changelog for NeoFS Node ### Removed ### Updated +- `github.com/nspcc-dev/neofs-sdk-go` module to `v1.0.0-rc.17.0.20260211130529-740a11a64a87` (#3785) ### Updating from v0.51.1 diff --git a/cmd/neofs-node/config.go b/cmd/neofs-node/config.go index 62539e8db0..8fb4c7d7f4 100644 --- a/cmd/neofs-node/config.go +++ b/cmd/neofs-node/config.go @@ -44,6 +44,7 @@ import ( "github.com/nspcc-dev/neofs-node/pkg/util" "github.com/nspcc-dev/neofs-node/pkg/util/state" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" "github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/nspcc-dev/neofs-sdk-go/user" "github.com/nspcc-dev/neofs-sdk-go/version" @@ -413,7 +414,7 @@ func initCfg(appCfg *config.Config) *cfg { pingTimeout := appCfg.APIClient.PingTimeout newClientCache := func(scope string) *cache.Clients { return cache.NewClients(c.log.With(zap.String("scope", scope)), &buffers, streamTimeout, - minConnTimeout, pingInterval, pingTimeout) + minConnTimeout, pingInterval, pingTimeout, neofsecdsa.Signer(key.PrivateKey)) } c.shared = shared{ basics: basicSharedConfig, diff --git a/cmd/neofs-node/netmap.go b/cmd/neofs-node/netmap.go index 19ce67f253..bfb143412b 100644 --- a/cmd/neofs-node/netmap.go +++ b/cmd/neofs-node/netmap.go @@ -259,11 +259,11 @@ func initNetmapService(c *cfg) { local := slices.IndexFunc(nodes, func(node netmapSDK.NodeInfo) bool { return c.IsLocalKey(node.PublicKey()) }) var wg sync.WaitGroup l.Info("syncing SN connection caches with the new network map...") - for _, c := range []*cache.Clients{c.clientCache, c.putClientCache, c.bgClientCache} { + for _, cl := range []*cache.Clients{c.clientCache, c.putClientCache, c.bgClientCache} { wg.Add(1) go func() { defer wg.Done() - c.SyncWithNewNetmap(nodes, local) + cl.SyncWithNewNetmap(c.ctx, nodes, local) }() } wg.Wait() diff --git a/cmd/neofs-node/object.go b/cmd/neofs-node/object.go index cc80737bb2..046a0df6bf 100644 --- a/cmd/neofs-node/object.go +++ b/cmd/neofs-node/object.go @@ -143,8 +143,8 @@ func (fn *innerRingFetcherWithNotary) InnerRingKeys() ([][]byte, error) { type coreClientConstructor reputationClientConstructor -func (x *coreClientConstructor) Get(info coreclient.NodeInfo) (coreclient.MultiAddressClient, error) { - c, err := (*reputationClientConstructor)(x).Get(info) +func (x *coreClientConstructor) Get(ctx context.Context, info coreclient.NodeInfo) (coreclient.MultiAddressClient, error) { + c, err := (*reputationClientConstructor)(x).Get(ctx, info) if err != nil { return nil, err } @@ -340,7 +340,7 @@ type reputationClientConstructor struct { trustStorage *truststorage.Storage basicConstructor interface { - Get(coreclient.NodeInfo) (coreclient.MultiAddressClient, error) + Get(context.Context, coreclient.NodeInfo) (coreclient.MultiAddressClient, error) } } @@ -421,8 +421,8 @@ func (c *reputationClient) ObjectSearchInit(ctx context.Context, containerID cid return res, err } -func (c *reputationClientConstructor) Get(info coreclient.NodeInfo) (coreclient.Client, error) { - cl, err := c.basicConstructor.Get(info) +func (c *reputationClientConstructor) Get(ctx context.Context, info coreclient.NodeInfo) (coreclient.Client, error) { + cl, err := c.basicConstructor.Get(ctx, info) if err != nil { return nil, err } diff --git a/cmd/neofs-node/reputation/common/remote.go b/cmd/neofs-node/reputation/common/remote.go index 876cce5b19..392638716e 100644 --- a/cmd/neofs-node/reputation/common/remote.go +++ b/cmd/neofs-node/reputation/common/remote.go @@ -1,6 +1,7 @@ package common import ( + "context" "fmt" "github.com/nspcc-dev/neofs-node/pkg/core/client" @@ -12,7 +13,7 @@ import ( ) type clientCache interface { - Get(client.NodeInfo) (client.MultiAddressClient, error) + Get(context.Context, client.NodeInfo) (client.MultiAddressClient, error) } // clientKeyRemoteProvider must provide a remote writer and take into account @@ -71,7 +72,7 @@ func NewRemoteTrustProvider(prm RemoteProviderPrm) reputationrouter.RemoteWriter } } -func (rtp *remoteTrustProvider) InitRemote(srv reputationcommon.ServerInfo) (reputationcommon.WriterProvider, error) { +func (rtp *remoteTrustProvider) InitRemote(ctx context.Context, srv reputationcommon.ServerInfo) (reputationcommon.WriterProvider, error) { rtp.log.Debug("initializing remote writer provider") if srv == nil { @@ -92,7 +93,7 @@ func (rtp *remoteTrustProvider) InitRemote(srv reputationcommon.ServerInfo) (rep return nil, fmt.Errorf("parse client node info: %w", err) } - c, err := rtp.clientCache.Get(info) + c, err := rtp.clientCache.Get(ctx, info) if err != nil { return nil, fmt.Errorf("could not initialize API client: %w", err) } diff --git a/cmd/neofs-node/transport.go b/cmd/neofs-node/transport.go index 582f26b1d3..d96fcecd2e 100644 --- a/cmd/neofs-node/transport.go +++ b/cmd/neofs-node/transport.go @@ -20,7 +20,7 @@ type transport struct { // SendReplicationRequestToNode connects to described node and sends prepared // replication request message to it. func (x *transport) SendReplicationRequestToNode(ctx context.Context, req []byte, node coreclient.NodeInfo) ([]byte, error) { - c, err := x.clients.Get(node) + c, err := x.clients.Get(ctx, node) if err != nil { return nil, fmt.Errorf("connect to remote node: %w", err) } diff --git a/go.mod b/go.mod index 4e8cbd9f61..f420a5492f 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/nspcc-dev/neo-go v0.117.0 github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea github.com/nspcc-dev/neofs-contract v0.26.1 - github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.17 + github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.17.0.20260211130529-740a11a64a87 github.com/nspcc-dev/tzhash v1.8.3 github.com/panjf2000/ants/v2 v2.11.3 github.com/prometheus/client_golang v1.23.2 diff --git a/go.sum b/go.sum index 5d4d0b379a..ea8f3ab954 100644 --- a/go.sum +++ b/go.sum @@ -199,8 +199,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.26.1 h1:7Ii7Q4L3au408LOsIWKiSgfnT1g8G9jo3W7381d41T8= github.com/nspcc-dev/neofs-contract v0.26.1/go.mod h1:pevVF9OWdEN5bweKxOu6ryZv9muCEtS1ppzYM4RfBIo= -github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.17 h1:MahpltbItODvLsGIUsDuW9fz1MXmAi0c8dZNsK8Azqc= -github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.17/go.mod h1:y2vNz9DVTqBkR7ctYb6taLnabWTtG7xtCHlGofEpKOM= +github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.17.0.20260211130529-740a11a64a87 h1:RlOzZGS7925Dz5F6OckibMSLv/awqhla2D8PeO9nIHI= +github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.17.0.20260211130529-740a11a64a87/go.mod h1:y2vNz9DVTqBkR7ctYb6taLnabWTtG7xtCHlGofEpKOM= 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.3 h1:EWJMOL/ppdqNBvkKjHECljusopcsNu4i4kH8KctTv10= diff --git a/pkg/network/cache/clients.go b/pkg/network/cache/clients.go index 1ff9eab7e5..7248ce241a 100644 --- a/pkg/network/cache/clients.go +++ b/pkg/network/cache/clients.go @@ -46,11 +46,13 @@ type Clients struct { mtx sync.RWMutex conns map[string]*connections // keys are public key bytes + + signer neofscrypto.Signer } // NewClients constructs Clients initializing connection to any endpoint with // given parameters. -func NewClients(l *zap.Logger, signBufPool *sync.Pool, streamTimeout, minConnTimeout, pingInterval, pingTimeout time.Duration) *Clients { +func NewClients(l *zap.Logger, signBufPool *sync.Pool, streamTimeout, minConnTimeout, pingInterval, pingTimeout time.Duration, signer neofscrypto.Signer) *Clients { return &Clients{ log: l, streamMsgTimeout: streamTimeout, @@ -59,6 +61,7 @@ func NewClients(l *zap.Logger, signBufPool *sync.Pool, streamTimeout, minConnTim pingInterval: pingInterval, pingTimeout: pingTimeout, conns: make(map[string]*connections), + signer: signer, } } @@ -76,7 +79,7 @@ func snCacheKey(pub []byte) string { return string(pub) } // Get initializes connections to network addresses of described SN and returns // interface to access them. All opened connections are cached and kept alive // until [Clients.CloseAll]. -func (x *Clients) Get(info clientcore.NodeInfo) (clientcore.MultiAddressClient, error) { +func (x *Clients) Get(ctx context.Context, info clientcore.NodeInfo) (clientcore.MultiAddressClient, error) { pub := info.PublicKey() cacheKey := snCacheKey(pub) @@ -93,7 +96,7 @@ func (x *Clients) Get(info clientcore.NodeInfo) (clientcore.MultiAddressClient, return c, nil } - c, err := x.initConnections(pub, info.AddressGroup()) + c, err := x.initConnections(ctx, pub, info.AddressGroup()) if err != nil { return nil, fmt.Errorf("init connections: %w", err) } @@ -102,7 +105,7 @@ func (x *Clients) Get(info clientcore.NodeInfo) (clientcore.MultiAddressClient, } // SyncWithNewNetmap synchronizes x with the passed new network map. -func (x *Clients) SyncWithNewNetmap(sns []netmap.NodeInfo, local int) { +func (x *Clients) SyncWithNewNetmap(ctx context.Context, sns []netmap.NodeInfo, local int) { x.mtx.Lock() defer x.mtx.Unlock() @@ -110,7 +113,7 @@ func (x *Clients) SyncWithNewNetmap(sns []netmap.NodeInfo, local int) { if i == local { continue } - if err := x.syncWithNetmapSN(sns[i]); err != nil { + if err := x.syncWithNetmapSN(ctx, sns[i]); err != nil { x.log.Warn("failed to sync connection cache with SN from the new network map, skip", zap.String("pub", hex.EncodeToString(sns[i].PublicKey())), zap.Error(err)) } @@ -128,7 +131,7 @@ func (x *Clients) SyncWithNewNetmap(sns []netmap.NodeInfo, local int) { }) } -func (x *Clients) syncWithNetmapSN(sn netmap.NodeInfo) error { +func (x *Clients) syncWithNetmapSN(ctx context.Context, sn netmap.NodeInfo) error { pub := sn.PublicKey() conns, ok := x.conns[snCacheKey(pub)] if !ok { @@ -168,7 +171,7 @@ func (x *Clients) syncWithNetmapSN(sn netmap.NodeInfo) error { continue } x.log.Info("initializing connection to new SN address in the new network map...", zap.String("address", ma)) - c, err := x.initConnection(pub, as[i].URIAddr()) + c, err := x.initConnection(ctx, pub, as[i].URIAddr()) if err != nil { x.log.Info("failed to init connection to new SN address in the new network map", zap.String("address", ma), zap.Error(err)) @@ -181,13 +184,13 @@ func (x *Clients) syncWithNetmapSN(sn netmap.NodeInfo) error { return nil } -func (x *Clients) initConnections(pub []byte, as network.AddressGroup) (*connections, error) { +func (x *Clients) initConnections(ctx context.Context, pub []byte, as network.AddressGroup) (*connections, error) { m := make(map[string]*client.Client, len(as)) l := x.log.With(zap.String("public key", hex.EncodeToString(pub))) for i := range as { cacheKey := as[i].String() l.Info("initializing connection to the SN...", zap.String("address", cacheKey)) - c, err := x.initConnection(pub, as[i].URIAddr()) + c, err := x.initConnection(ctx, pub, as[i].URIAddr()) if err != nil { // TODO: if at least one address is OK, SN can be operational for cl := range maps.Values(m) { @@ -206,7 +209,7 @@ func (x *Clients) initConnections(pub []byte, as network.AddressGroup) (*connect }, nil } -func (x *Clients) initConnection(pub []byte, uri string) (*client.Client, error) { +func (x *Clients) initConnection(ctx context.Context, pub []byte, uri string) (*client.Client, error) { target, withTLS, err := uriutil.Parse(uri) if err != nil { return nil, fmt.Errorf("parse URI: %w", err) @@ -232,7 +235,7 @@ func (x *Clients) initConnection(pub []byte, uri string) (*client.Client, error) if err != nil { // should never happen return nil, fmt.Errorf("init gRPC client conn: %w", err) } - res, err := client.NewGRPC(grpcConn, x.signBufPool, x.streamMsgTimeout, func(respPub []byte) error { + res, err := client.NewGRPC(ctx, pub, grpcConn, x.signBufPool, x.streamMsgTimeout, func(respPub []byte) error { if !bytes.Equal(respPub, pub) { return clientcore.ErrWrongPublicKey } diff --git a/pkg/services/object/get/exec.go b/pkg/services/object/get/exec.go index 3ae66c7f94..dfc65d2d25 100644 --- a/pkg/services/object/get/exec.go +++ b/pkg/services/object/get/exec.go @@ -297,7 +297,7 @@ func (exec *execCtx) headChild(id oid.ID) (*object.Object, bool) { } func (exec *execCtx) remoteClient(info clientcore.NodeInfo) (getClient, bool) { - c, err := exec.svc.clientCache.get(info) + c, err := exec.svc.clientCache.get(exec.context(), info) if err == nil { return c, true diff --git a/pkg/services/object/get/get.go b/pkg/services/object/get/get.go index 82113cdbe9..f93342fc01 100644 --- a/pkg/services/object/get/get.go +++ b/pkg/services/object/get/get.go @@ -64,7 +64,7 @@ func (s *Service) proxyGetRequest(ctx context.Context, sortedNodeLists [][]netma req string, headWriter internal.HeaderWriter) error { for i := range sortedNodeLists { for j := range sortedNodeLists[i] { - conn, node, err := s.conns.(*clientCacheWrapper)._connect(sortedNodeLists[i][j]) + conn, node, err := s.conns.(*clientCacheWrapper)._connect(ctx, sortedNodeLists[i][j]) if err != nil { s.log.Debug("get conn to remote node", zap.Stringer("addresses", node.AddressGroup()), zap.Error(err)) @@ -197,7 +197,7 @@ func (s *Service) GetRangeHash(ctx context.Context, prm RangeHashPrm) (*RangeHas func (s *Service) proxyHashRequest(ctx context.Context, sortedNodeLists [][]netmap.NodeInfo, proxyFn RangeRequestForwarder) ([][]byte, error) { for i := range sortedNodeLists { for j := range sortedNodeLists[i] { - conn, node, err := s.conns.(*clientCacheWrapper)._connect(sortedNodeLists[i][j]) + conn, node, err := s.conns.(*clientCacheWrapper)._connect(ctx, sortedNodeLists[i][j]) if err != nil { s.log.Debug("get conn to remote node", zap.Stringer("addresses", node.AddressGroup()), zap.Error(err)) diff --git a/pkg/services/object/get/get_test.go b/pkg/services/object/get/get_test.go index 9d7373857d..c79106ebb3 100644 --- a/pkg/services/object/get/get_test.go +++ b/pkg/services/object/get/get_test.go @@ -78,7 +78,7 @@ func (g *testNeoFS) GetNodesForObject(addr oid.Address) ([][]netmap.NodeInfo, [] return nodeLists, primaryNums, nil, nil } -func (c *testClientCache) get(info client.NodeInfo) (getClient, error) { +func (c *testClientCache) get(_ context.Context, info client.NodeInfo) (getClient, error) { v, ok := c.clients[info.AddressGroup().String()] if !ok { return nil, errors.New("could not construct client") diff --git a/pkg/services/object/get/service.go b/pkg/services/object/get/service.go index 0b23aefeaf..6d62586162 100644 --- a/pkg/services/object/get/service.go +++ b/pkg/services/object/get/service.go @@ -90,7 +90,7 @@ type cfg struct { } clientCache interface { - get(client.NodeInfo) (getClient, error) + get(context.Context, client.NodeInfo) (getClient, error) } // TODO: merge with clientCache // TODO: this differs with https://pkg.go.dev/github.com/nspcc-dev/neofs-sdk-go/client#Client @@ -152,7 +152,7 @@ func WithLocalStorageEngine(e *engine.StorageEngine) Option { } type ClientConstructor interface { - Get(client.NodeInfo) (client.MultiAddressClient, error) + Get(context.Context, client.NodeInfo) (client.MultiAddressClient, error) } // WithClientConstructor returns option to set constructor of remote node clients. diff --git a/pkg/services/object/get/util.go b/pkg/services/object/get/util.go index 6a6d41da9b..d709b8c7f4 100644 --- a/pkg/services/object/get/util.go +++ b/pkg/services/object/get/util.go @@ -165,8 +165,8 @@ func (s *SimpleObjectWriter) Object() *object.Object { return s.obj } -func (c *clientCacheWrapper) get(info coreclient.NodeInfo) (getClient, error) { - clt, err := c.cache.Get(info) +func (c *clientCacheWrapper) get(ctx context.Context, info coreclient.NodeInfo) (getClient, error) { + clt, err := c.cache.Get(ctx, info) if err != nil { return nil, err } @@ -345,7 +345,7 @@ func (w *directChildWriter) WriteHeader(obj *object.Object) error { func (c *clientCacheWrapper) InitGetObjectStream(ctx context.Context, node netmap.NodeInfo, pk ecdsa.PrivateKey, cnr cid.ID, id oid.ID, sTok *session.Object, local, verifyID bool, xs []string) (object.Object, io.ReadCloser, error) { - conn, err := c.connect(node) + conn, err := c.connect(ctx, node) if err != nil { return object.Object{}, nil, err } @@ -375,7 +375,7 @@ func (c *clientCacheWrapper) InitGetObjectStream(ctx context.Context, node netma func (c *clientCacheWrapper) Head(ctx context.Context, node netmap.NodeInfo, pk ecdsa.PrivateKey, cnr cid.ID, id oid.ID, sTok *session.Object) (object.Object, error) { - conn, err := c.connect(node) + conn, err := c.connect(ctx, node) if err != nil { return object.Object{}, err } @@ -396,7 +396,7 @@ func (c *clientCacheWrapper) Head(ctx context.Context, node netmap.NodeInfo, pk func (c *clientCacheWrapper) InitGetObjectRangeStream(ctx context.Context, node netmap.NodeInfo, pk ecdsa.PrivateKey, cnr cid.ID, id oid.ID, off, ln uint64, sTok *session.Object, xs []string) (io.ReadCloser, error) { - conn, err := c.connect(node) + conn, err := c.connect(ctx, node) if err != nil { return nil, err } @@ -416,12 +416,12 @@ func (c *clientCacheWrapper) InitGetObjectRangeStream(ctx context.Context, node return rc, nil } -func (c *clientCacheWrapper) connect(node netmap.NodeInfo) (coreclient.MultiAddressClient, error) { - conn, _, err := c._connect(node) +func (c *clientCacheWrapper) connect(ctx context.Context, node netmap.NodeInfo) (coreclient.MultiAddressClient, error) { + conn, _, err := c._connect(ctx, node) return conn, err } -func (c *clientCacheWrapper) _connect(node netmap.NodeInfo) (coreclient.MultiAddressClient, coreclient.NodeInfo, error) { +func (c *clientCacheWrapper) _connect(ctx context.Context, node netmap.NodeInfo) (coreclient.MultiAddressClient, coreclient.NodeInfo, error) { // TODO: code is copied from pkg/services/object/get/container.go:63. Worth sharing? // TODO: we may waste resources doing this per request. Make once on network map change instead. var ag network.AddressGroup @@ -433,7 +433,7 @@ func (c *clientCacheWrapper) _connect(node netmap.NodeInfo) (coreclient.MultiAdd ni.SetAddressGroup(ag) ni.SetPublicKey(node.PublicKey()) - conn, err := c.cache.Get(ni) + conn, err := c.cache.Get(ctx, ni) if err != nil { return nil, coreclient.NodeInfo{}, fmt.Errorf("get conn: %w", err) } diff --git a/pkg/services/object/head/remote.go b/pkg/services/object/head/remote.go index f42a8b5381..05b0697e66 100644 --- a/pkg/services/object/head/remote.go +++ b/pkg/services/object/head/remote.go @@ -19,7 +19,7 @@ import ( ) type ClientConstructor interface { - Get(clientcore.NodeInfo) (clientcore.Client, error) + Get(context.Context, clientcore.NodeInfo) (clientcore.Client, error) } // RemoteHeader represents utility for getting @@ -93,7 +93,7 @@ func (h *RemoteHeader) Head(ctx context.Context, prm *RemoteHeadPrm) (*object.Ob return nil, fmt.Errorf("parse client node info: %w", err) } - c, err := h.clientCache.Get(info) + c, err := h.clientCache.Get(ctx, info) if err != nil { return nil, fmt.Errorf("(%T) could not create SDK client %s: %w", h, info.AddressGroup(), err) } @@ -127,7 +127,7 @@ func (h *RemoteHeader) GetRange(ctx context.Context, node netmap.NodeInfo, cnr c return nil, fmt.Errorf("parse client node info: %w", err) } - conn, err := h.clientCache.Get(info) + conn, err := h.clientCache.Get(ctx, info) if err != nil { return nil, fmt.Errorf("get conn: %w", err) } diff --git a/pkg/services/object/put/remote.go b/pkg/services/object/put/remote.go index df3d1c6d67..4c1cbb886b 100644 --- a/pkg/services/object/put/remote.go +++ b/pkg/services/object/put/remote.go @@ -59,7 +59,7 @@ func putObjectToNode(ctx context.Context, nodeInfo clientcore.NodeInfo, obj *obj opts.WithinSession(*tok) } - c, err := clientConstructor.Get(nodeInfo) + c, err := clientConstructor.Get(ctx, nodeInfo) if err != nil { return fmt.Errorf("could not create SDK client %s: %w", nodeInfo, err) } @@ -150,7 +150,7 @@ func (s *RemoteSender) ReplicateObjectToNode(ctx context.Context, id oid.ID, src return fmt.Errorf("fetch local node's private key: %w", err) } - c, err := s.clientConstructor.Get(nodeInfoForCons) + c, err := s.clientConstructor.Get(ctx, nodeInfoForCons) if err != nil { return fmt.Errorf("init NeoFS API client of the remote node: %w", err) } diff --git a/pkg/services/object/put/service.go b/pkg/services/object/put/service.go index cc1377454b..acb109ec45 100644 --- a/pkg/services/object/put/service.go +++ b/pkg/services/object/put/service.go @@ -62,7 +62,7 @@ type Transport interface { } type ClientConstructor interface { - Get(client.NodeInfo) (client.MultiAddressClient, error) + Get(context.Context, client.NodeInfo) (client.MultiAddressClient, error) } // ContainerNodes provides access to storage nodes matching storage policy of diff --git a/pkg/services/object/put/service_test.go b/pkg/services/object/put/service_test.go index 0b1f99468d..67a94d8865 100644 --- a/pkg/services/object/put/service_test.go +++ b/pkg/services/object/put/service_test.go @@ -970,7 +970,7 @@ func (x nodeServices) lookupNode(node clientcore.NodeInfo) (*Service, error) { return x[ind], nil } -func (x nodeServices) Get(node clientcore.NodeInfo) (clientcore.MultiAddressClient, error) { +func (x nodeServices) Get(_ context.Context, node clientcore.NodeInfo) (clientcore.MultiAddressClient, error) { svc, err := x.lookupNode(node) if err != nil { return nil, err diff --git a/pkg/services/object/put/streamer.go b/pkg/services/object/put/streamer.go index e7f91c1ae1..e482ac2c0c 100644 --- a/pkg/services/object/put/streamer.go +++ b/pkg/services/object/put/streamer.go @@ -251,7 +251,7 @@ func (p *Streamer) newCommonTarget(prm *PutInitPrm) internal.Target { var relay func(nodeDesc) error if p.relay != nil { relay = func(node nodeDesc) error { - c, err := p.clientConstructor.Get(node.info) + c, err := p.clientConstructor.Get(p.ctx, node.info) if err != nil { return fmt.Errorf("could not create SDK client %s: %w", node.info.AddressGroup(), err) } diff --git a/pkg/services/object/search/container.go b/pkg/services/object/search/container.go index abc2c13526..237a672dbe 100644 --- a/pkg/services/object/search/container.go +++ b/pkg/services/object/search/container.go @@ -63,7 +63,7 @@ func (exec *execCtx) executeOnContainer(ectx context.Context) { lg.Debug("processing node...") - c, err := exec.svc.clientConstructor.get(info) + c, err := exec.svc.clientConstructor.get(ctx, info) if err != nil { mtx.Lock() exec.status = statusUndefined diff --git a/pkg/services/object/search/search_test.go b/pkg/services/object/search/search_test.go index 6a5be55fe3..a50b5ca5a0 100644 --- a/pkg/services/object/search/search_test.go +++ b/pkg/services/object/search/search_test.go @@ -70,7 +70,7 @@ func (g *testContainers) ForEachRemoteContainerNode(cnr cid.ID, f func(info netm return nil } -func (c *testClientCache) get(info clientcore.NodeInfo) (searchClient, error) { +func (c *testClientCache) get(_ context.Context, info clientcore.NodeInfo) (searchClient, error) { v, ok := c.clients[info.AddressGroup().String()] if !ok { return nil, errors.New("could not construct client") diff --git a/pkg/services/object/search/service.go b/pkg/services/object/search/service.go index 15edab15b9..8bb0dcf83d 100644 --- a/pkg/services/object/search/service.go +++ b/pkg/services/object/search/service.go @@ -42,7 +42,7 @@ type Containers interface { } type ClientConstructor interface { - Get(client.NodeInfo) (client.MultiAddressClient, error) + Get(context.Context, client.NodeInfo) (client.MultiAddressClient, error) } type cfg struct { @@ -53,7 +53,7 @@ type cfg struct { } clientConstructor interface { - get(client.NodeInfo) (searchClient, error) + get(context.Context, client.NodeInfo) (searchClient, error) } keyStore *util.KeyStorage diff --git a/pkg/services/object/search/util.go b/pkg/services/object/search/util.go index 96a8959cf9..0ac11918e9 100644 --- a/pkg/services/object/search/util.go +++ b/pkg/services/object/search/util.go @@ -61,8 +61,8 @@ func (w *uniqueIDWriter) WriteIDs(list []oid.ID) error { return w.writer.WriteIDs(list) } -func (c *clientConstructorWrapper) get(info client.NodeInfo) (searchClient, error) { - clt, err := c.constructor.Get(info) +func (c *clientConstructorWrapper) get(ctx context.Context, info client.NodeInfo) (searchClient, error) { + clt, err := c.constructor.Get(ctx, info) if err != nil { return nil, err } diff --git a/pkg/services/object/server.go b/pkg/services/object/server.go index c98bd44783..02397b297b 100644 --- a/pkg/services/object/server.go +++ b/pkg/services/object/server.go @@ -2197,7 +2197,7 @@ func (s *Server) searchOnRemoteNode(ctx context.Context, node sdknetmap.NodeInfo info.SetAddressGroup(endpoints) nodePub := node.PublicKey() info.SetPublicKey(nodePub) - c, err := s.nodeClients.Get(info) + c, err := s.nodeClients.Get(ctx, info) if err != nil { return nil, false, fmt.Errorf("get node client: %w", err) } diff --git a/pkg/services/object/server_test.go b/pkg/services/object/server_test.go index 516a807a05..c5ca504f2d 100644 --- a/pkg/services/object/server_test.go +++ b/pkg/services/object/server_test.go @@ -155,7 +155,7 @@ func (noCallTestReqInfoExtractor) SearchV2RequestToInfo(*protoobject.SearchV2Req type noCallClients struct{} -func (noCallClients) Get(clientcore.NodeInfo) (clientcore.MultiAddressClient, error) { +func (noCallClients) Get(context.Context, clientcore.NodeInfo) (clientcore.MultiAddressClient, error) { panic("must not be called") } diff --git a/pkg/services/reputation/common/router/calls.go b/pkg/services/reputation/common/router/calls.go index c8447e489c..ab6f665d67 100644 --- a/pkg/services/reputation/common/router/calls.go +++ b/pkg/services/reputation/common/router/calls.go @@ -89,7 +89,7 @@ func (w *trustWriter) Write(t reputation.Trust) error { remoteWriter, ok := w.mServers[key] if !ok { - provider, err := w.router.remoteProvider.InitRemote(remoteInfo) + provider, err := w.router.remoteProvider.InitRemote(w.routeCtx, remoteInfo) if err != nil { w.router.log.Debug("could not initialize writer provider", zap.Error(err), diff --git a/pkg/services/reputation/common/router/deps.go b/pkg/services/reputation/common/router/deps.go index 5d1f1290a9..66b95325b8 100644 --- a/pkg/services/reputation/common/router/deps.go +++ b/pkg/services/reputation/common/router/deps.go @@ -1,6 +1,8 @@ package router import ( + "context" + "github.com/nspcc-dev/neofs-node/pkg/services/reputation" "github.com/nspcc-dev/neofs-node/pkg/services/reputation/common" ) @@ -24,5 +26,5 @@ type RemoteWriterProvider interface { // corresponding to info. // // Nil info matches the end of the route. - InitRemote(info common.ServerInfo) (common.WriterProvider, error) + InitRemote(ctx context.Context, info common.ServerInfo) (common.WriterProvider, error) } From 4dea9e257a9793b4862993580556bd9dfb19f399 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 11 Feb 2026 20:20:11 +0300 Subject: [PATCH 5/6] sn/object: Rework remote SN public key check Request info on connection init, assert key and do this no more. Closes #3396. Signed-off-by: Leonard Lyubich --- pkg/network/cache/clients.go | 23 ++++++++++++++++------- pkg/services/object/internal/key.go | 19 ------------------- pkg/services/object/server.go | 16 ---------------- 3 files changed, 16 insertions(+), 42 deletions(-) delete mode 100644 pkg/services/object/internal/key.go diff --git a/pkg/network/cache/clients.go b/pkg/network/cache/clients.go index 7248ce241a..e9fd8a4929 100644 --- a/pkg/network/cache/clients.go +++ b/pkg/network/cache/clients.go @@ -235,17 +235,26 @@ func (x *Clients) initConnection(ctx context.Context, pub []byte, uri string) (* if err != nil { // should never happen return nil, fmt.Errorf("init gRPC client conn: %w", err) } - res, err := client.NewGRPC(ctx, pub, grpcConn, x.signBufPool, x.streamMsgTimeout, func(respPub []byte) error { - if !bytes.Equal(respPub, pub) { - return clientcore.ErrWrongPublicKey - } - return nil - }) + res, err := client.NewGRPC(ctx, pub, grpcConn, x.signBufPool, x.streamMsgTimeout, nil) if err != nil { _ = grpcConn.Close() return res, fmt.Errorf("init NeoFS API client from gRPC client conn: %w", err) } - grpcConn.Connect() + + ctx, cancel := context.WithTimeout(ctx, x.streamMsgTimeout) + defer cancel() + + resp, err := res.EndpointInfo(ctx, client.PrmEndpointInfo{}) + if err != nil { + _ = grpcConn.Close() + return nil, fmt.Errorf("get node info to check public key: %w", err) + } + + if got := resp.NodeInfo().PublicKey(); !bytes.Equal(got, pub) { + _ = grpcConn.Close() + return nil, clientcore.ErrWrongPublicKey + } + return res, nil } diff --git a/pkg/services/object/internal/key.go b/pkg/services/object/internal/key.go deleted file mode 100644 index ce86b6cfb7..0000000000 --- a/pkg/services/object/internal/key.go +++ /dev/null @@ -1,19 +0,0 @@ -package internal - -import ( - "bytes" - - "github.com/nspcc-dev/neofs-node/pkg/core/client" - protosession "github.com/nspcc-dev/neofs-sdk-go/proto/session" -) - -// VerifyResponseKeyV2 checks if response is signed with expected key. Returns client.ErrWrongPublicKey if not. -func VerifyResponseKeyV2(expectedKey []byte, resp interface { - GetVerifyHeader() *protosession.ResponseVerificationHeader -}) error { - if !bytes.Equal(resp.GetVerifyHeader().GetBodySignature().GetKey(), expectedKey) { - return client.ErrWrongPublicKey - } - - return nil -} diff --git a/pkg/services/object/server.go b/pkg/services/object/server.go index 02397b297b..78376978df 100644 --- a/pkg/services/object/server.go +++ b/pkg/services/object/server.go @@ -26,7 +26,6 @@ import ( aclsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/acl/v2" deletesvc "github.com/nspcc-dev/neofs-node/pkg/services/object/delete" getsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/get" - "github.com/nspcc-dev/neofs-node/pkg/services/object/internal" putsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/put" searchsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/search" objutil "github.com/nspcc-dev/neofs-node/pkg/services/object/util" @@ -302,9 +301,6 @@ func putToRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []byte, return fmt.Errorf("closing the stream failed: %w", err) } - if err := internal.VerifyResponseKeyV2(nodePub, resp); err != nil { - return err - } if err := checkStatus(resp.GetMetaHeader().GetStatus()); err != nil { return fmt.Errorf("remote node response: %w", err) } @@ -976,9 +972,6 @@ func getHashesFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub return nil, fmt.Errorf("GetRangeHash rpc failure: %w", err) } - if err := internal.VerifyResponseKeyV2(nodePub, resp); err != nil { - return nil, err - } if err := checkStatus(resp.GetMetaHeader().GetStatus()); err != nil { return nil, err } @@ -1488,9 +1481,6 @@ func continueRangeFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, nod return fmt.Errorf("reading the response failed: %w", err) } - if err = internal.VerifyResponseKeyV2(nodePub, resp); err != nil { - return err - } if err := checkStatus(resp.GetMetaHeader().GetStatus()); err != nil { return err } @@ -1698,9 +1688,6 @@ func searchOnRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []by return nil, fmt.Errorf("reading the response failed: %w", err) } - if err := internal.VerifyResponseKeyV2(nodePub, resp); err != nil { - return nil, err - } if err := checkStatus(resp.GetMetaHeader().GetStatus()); err != nil { return nil, fmt.Errorf("remote node response: %w", err) } @@ -2218,9 +2205,6 @@ func searchOnRemoteAddress(ctx context.Context, conn *grpc.ClientConn, nodePub [ return nil, false, fmt.Errorf("send request over gRPC: %w", err) } - if !bytes.Equal(resp.GetVerifyHeader().GetBodySignature().GetKey(), nodePub) { - return nil, false, client.ErrWrongPublicKey - } if err := apistatus.ToError(resp.GetMetaHeader().GetStatus()); err != nil { return nil, false, err } From ab56be7301dfaa675ebea4033e8ca92781917885 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 11 Feb 2026 20:32:49 +0300 Subject: [PATCH 6/6] sn/object: Refactor forwarding code Drop node info parameters since public key is no longer accessed. Signed-off-by: Leonard Lyubich --- pkg/services/object/get/get.go | 4 +-- pkg/services/object/get/get_test.go | 2 +- pkg/services/object/get/prm.go | 4 +-- pkg/services/object/get/remote.go | 2 +- pkg/services/object/get/service.go | 2 +- pkg/services/object/get/util.go | 6 ++-- pkg/services/object/put/prm.go | 4 +-- pkg/services/object/put/streamer.go | 4 +-- pkg/services/object/search/container.go | 2 +- pkg/services/object/search/prm.go | 2 +- pkg/services/object/search/search_test.go | 2 +- pkg/services/object/search/service.go | 2 +- pkg/services/object/search/util.go | 4 +-- pkg/services/object/server.go | 41 ++++++++++------------- 14 files changed, 37 insertions(+), 44 deletions(-) diff --git a/pkg/services/object/get/get.go b/pkg/services/object/get/get.go index f93342fc01..965294fa7b 100644 --- a/pkg/services/object/get/get.go +++ b/pkg/services/object/get/get.go @@ -71,7 +71,7 @@ func (s *Service) proxyGetRequest(ctx context.Context, sortedNodeLists [][]netma continue } - hdr, err := proxyFn(ctx, node, conn) + hdr, err := proxyFn(ctx, conn) if err == nil { if headWriter != nil { return headWriter.WriteHeader(hdr) @@ -204,7 +204,7 @@ func (s *Service) proxyHashRequest(ctx context.Context, sortedNodeLists [][]netm continue } - hashes, err := proxyFn(ctx, node, conn) + hashes, err := proxyFn(ctx, conn) if err == nil { return hashes, nil } diff --git a/pkg/services/object/get/get_test.go b/pkg/services/object/get/get_test.go index c79106ebb3..bb501b4ff4 100644 --- a/pkg/services/object/get/get_test.go +++ b/pkg/services/object/get/get_test.go @@ -96,7 +96,7 @@ func newTestClient() *testClient { } } -func (c *testClient) getObject(exec *execCtx, _ client.NodeInfo) (*object.Object, io.ReadCloser, error) { +func (c *testClient) getObject(exec *execCtx) (*object.Object, io.ReadCloser, error) { v, ok := c.results[exec.address()] if !ok { var errNotFound apistatus.ObjectNotFound diff --git a/pkg/services/object/get/prm.go b/pkg/services/object/get/prm.go index 50841d41e1..2855fcfea4 100644 --- a/pkg/services/object/get/prm.go +++ b/pkg/services/object/get/prm.go @@ -37,8 +37,8 @@ type RangeHashPrm struct { forwardedRangeHashResponse [][]byte } -type RequestForwarder func(context.Context, coreclient.NodeInfo, coreclient.MultiAddressClient) (*object.Object, error) -type RangeRequestForwarder func(context.Context, coreclient.NodeInfo, coreclient.MultiAddressClient) ([][]byte, error) +type RequestForwarder func(context.Context, coreclient.MultiAddressClient) (*object.Object, error) +type RangeRequestForwarder func(context.Context, coreclient.MultiAddressClient) ([][]byte, error) // HeadPrm groups parameters of Head service call. type HeadPrm struct { diff --git a/pkg/services/object/get/remote.go b/pkg/services/object/get/remote.go index b88615ff44..e80cdd60c3 100644 --- a/pkg/services/object/get/remote.go +++ b/pkg/services/object/get/remote.go @@ -17,7 +17,7 @@ func (exec *execCtx) processNode(info client.NodeInfo) bool { return true } - obj, reader, err := remoteClient.getObject(exec, info) + obj, reader, err := remoteClient.getObject(exec) var errSplitInfo *object.SplitInfoError diff --git a/pkg/services/object/get/service.go b/pkg/services/object/get/service.go index 6d62586162..eb530819ff 100644 --- a/pkg/services/object/get/service.go +++ b/pkg/services/object/get/service.go @@ -60,7 +60,7 @@ type Service struct { type Option func(*cfg) type getClient interface { - getObject(*execCtx, client.NodeInfo) (*object.Object, io.ReadCloser, error) + getObject(*execCtx) (*object.Object, io.ReadCloser, error) } type cfg struct { diff --git a/pkg/services/object/get/util.go b/pkg/services/object/get/util.go index d709b8c7f4..de0d35289f 100644 --- a/pkg/services/object/get/util.go +++ b/pkg/services/object/get/util.go @@ -176,9 +176,9 @@ func (c *clientCacheWrapper) get(ctx context.Context, info coreclient.NodeInfo) }, nil } -func (c *clientWrapper) getObject(exec *execCtx, info coreclient.NodeInfo) (*object.Object, io.ReadCloser, error) { +func (c *clientWrapper) getObject(exec *execCtx) (*object.Object, io.ReadCloser, error) { if exec.isForwardingEnabled() { - obj, err := exec.prm.forwarder(exec.ctx, info, c.client) + obj, err := exec.prm.forwarder(exec.ctx, c.client) return obj, nil, err } @@ -219,7 +219,7 @@ func (c *clientWrapper) getObject(exec *execCtx, info coreclient.NodeInfo) (*obj } if rngH := exec.prmRangeHash; rngH != nil && exec.isRangeHashForwardingEnabled() { - exec.prmRangeHash.forwardedRangeHashResponse, err = exec.prm.rangeForwarder(exec.ctx, info, c.client) + exec.prmRangeHash.forwardedRangeHashResponse, err = exec.prm.rangeForwarder(exec.ctx, c.client) return nil, nil, err } diff --git a/pkg/services/object/put/prm.go b/pkg/services/object/put/prm.go index 58761e3734..ddc5bfe916 100644 --- a/pkg/services/object/put/prm.go +++ b/pkg/services/object/put/prm.go @@ -18,7 +18,7 @@ type PutInitPrm struct { copiesNumber uint32 - relay func(client.NodeInfo, client.MultiAddressClient) error + relay func(client.MultiAddressClient) error containerNodes ContainerNodes ecPart iec.PartInfo @@ -48,7 +48,7 @@ func (p *PutInitPrm) WithObject(v *object.Object) *PutInitPrm { return p } -func (p *PutInitPrm) WithRelay(f func(client.NodeInfo, client.MultiAddressClient) error) *PutInitPrm { +func (p *PutInitPrm) WithRelay(f func(client.MultiAddressClient) error) *PutInitPrm { if p != nil { p.relay = f } diff --git a/pkg/services/object/put/streamer.go b/pkg/services/object/put/streamer.go index e482ac2c0c..6c4fc0a1f1 100644 --- a/pkg/services/object/put/streamer.go +++ b/pkg/services/object/put/streamer.go @@ -25,7 +25,7 @@ type Streamer struct { target internal.Target - relay func(client.NodeInfo, client.MultiAddressClient) error + relay func(client.MultiAddressClient) error maxPayloadSz uint64 // network config @@ -256,7 +256,7 @@ func (p *Streamer) newCommonTarget(prm *PutInitPrm) internal.Target { return fmt.Errorf("could not create SDK client %s: %w", node.info.AddressGroup(), err) } - return p.relay(node.info, c) + return p.relay(c) } } diff --git a/pkg/services/object/search/container.go b/pkg/services/object/search/container.go index 237a672dbe..b935649a8e 100644 --- a/pkg/services/object/search/container.go +++ b/pkg/services/object/search/container.go @@ -74,7 +74,7 @@ func (exec *execCtx) executeOnContainer(ectx context.Context) { return } - ids, err := c.searchObjects(ctx, exec, info) + ids, err := c.searchObjects(ctx, exec) if err != nil { lg.Debug("remote operation failed", zap.Error(err)) diff --git a/pkg/services/object/search/prm.go b/pkg/services/object/search/prm.go index 0baf8c615c..158212efb7 100644 --- a/pkg/services/object/search/prm.go +++ b/pkg/services/object/search/prm.go @@ -29,7 +29,7 @@ type IDListWriter interface { // RequestForwarder is a callback for forwarding of the // original Search requests. -type RequestForwarder func(coreclient.NodeInfo, coreclient.MultiAddressClient) ([]oid.ID, error) +type RequestForwarder func(coreclient.MultiAddressClient) ([]oid.ID, error) // SetCommonParameters sets common parameters of the operation. func (p *Prm) SetCommonParameters(common *util.CommonPrm) { diff --git a/pkg/services/object/search/search_test.go b/pkg/services/object/search/search_test.go index a50b5ca5a0..8b0a581ad4 100644 --- a/pkg/services/object/search/search_test.go +++ b/pkg/services/object/search/search_test.go @@ -88,7 +88,7 @@ func (ts *testStorage) search(exec *execCtx) ([]oid.ID, error) { return v.ids, v.err } -func (ts *testStorage) searchObjects(_ context.Context, exec *execCtx, _ clientcore.NodeInfo) ([]oid.ID, error) { +func (ts *testStorage) searchObjects(_ context.Context, exec *execCtx) ([]oid.ID, error) { v, ok := ts.items[exec.containerID()] if !ok { return nil, nil diff --git a/pkg/services/object/search/service.go b/pkg/services/object/search/service.go index 8bb0dcf83d..a066515b59 100644 --- a/pkg/services/object/search/service.go +++ b/pkg/services/object/search/service.go @@ -26,7 +26,7 @@ type Option func(*cfg) type searchClient interface { // searchObjects searches objects on the specified node. // MUST NOT modify execCtx as it can be accessed concurrently. - searchObjects(context.Context, *execCtx, client.NodeInfo) ([]oid.ID, error) + searchObjects(context.Context, *execCtx) ([]oid.ID, error) } // Containers provides information about NeoFS containers necessary for the diff --git a/pkg/services/object/search/util.go b/pkg/services/object/search/util.go index 0ac11918e9..2e25239b55 100644 --- a/pkg/services/object/search/util.go +++ b/pkg/services/object/search/util.go @@ -72,9 +72,9 @@ func (c *clientConstructorWrapper) get(ctx context.Context, info client.NodeInfo }, nil } -func (c *clientWrapper) searchObjects(ctx context.Context, exec *execCtx, info client.NodeInfo) ([]oid.ID, error) { +func (c *clientWrapper) searchObjects(ctx context.Context, exec *execCtx) ([]oid.ID, error) { if exec.prm.forwarder != nil { - return exec.prm.forwarder(info, c.client) + return exec.prm.forwarder(c.client) } key, err := exec.svc.keyStore.GetKey(nil) diff --git a/pkg/services/object/server.go b/pkg/services/object/server.go index 78376978df..5e6da9b715 100644 --- a/pkg/services/object/server.go +++ b/pkg/services/object/server.go @@ -272,15 +272,13 @@ func newIntermediatePutStream(signer ecdsa.PrivateKey, base *putsvc.Streamer, ct } } -func (x *putStream) sendToRemoteNode(node client.NodeInfo, c client.MultiAddressClient) error { - nodePub := node.PublicKey() +func (x *putStream) sendToRemoteNode(c client.MultiAddressClient) error { return c.ForEachGRPCConn(x.ctx, func(ctx context.Context, conn *grpc.ClientConn) error { - return putToRemoteNode(ctx, conn, nodePub, x.initReq, x.chunkReqs) // TODO: log error + return putToRemoteNode(ctx, conn, x.initReq, x.chunkReqs) // TODO: log error }) } -func putToRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []byte, - initReq *protoobject.PutRequest, chunkReqs []*protoobject.PutRequest) error { +func putToRemoteNode(ctx context.Context, conn *grpc.ClientConn, initReq *protoobject.PutRequest, chunkReqs []*protoobject.PutRequest) error { stream, err := protoobject.NewObjectServiceClient(conn).Put(ctx) if err != nil { return fmt.Errorf("stream opening failed: %w", err) @@ -719,7 +717,7 @@ func convertHeadPrm(signer ecdsa.PrivateKey, req *protoobject.HeadRequest, resp if meta == nil { return getsvc.HeadPrm{}, errors.New("missing meta header") } - p.SetRequestForwarder(func(ctx context.Context, node client.NodeInfo, c client.MultiAddressClient) (*object.Object, error) { + p.SetRequestForwarder(func(ctx context.Context, c client.MultiAddressClient) (*object.Object, error) { var err error onceResign.Do(func() { req.MetaHeader = &protosession.RequestMetaHeader{ @@ -939,7 +937,7 @@ func convertHashPrm(signer ecdsa.PrivateKey, ss sessions, req *protoobject.GetRa if meta == nil { return getsvc.RangeHashPrm{}, errors.New("missing meta header") } - p.SetRangeHashRequestForwarder(func(ctx context.Context, node client.NodeInfo, c client.MultiAddressClient) ([][]byte, error) { + p.SetRangeHashRequestForwarder(func(ctx context.Context, c client.MultiAddressClient) ([][]byte, error) { var err error onceResign.Do(func() { req.MetaHeader = &protosession.RequestMetaHeader{ @@ -954,19 +952,17 @@ func convertHashPrm(signer ecdsa.PrivateKey, ss sessions, req *protoobject.GetRa return nil, err } - nodePub := node.PublicKey() var hs [][]byte return hs, c.ForEachGRPCConn(ctx, func(ctx context.Context, conn *grpc.ClientConn) error { var err error - hs, err = getHashesFromRemoteNode(ctx, conn, nodePub, req) + hs, err = getHashesFromRemoteNode(ctx, conn, req) return err // TODO: log error }) }) return p, nil } -func getHashesFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []byte, - req *protoobject.GetRangeHashRequest) ([][]byte, error) { +func getHashesFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *protoobject.GetRangeHashRequest) ([][]byte, error) { resp, err := protoobject.NewObjectServiceClient(conn).GetRangeHash(ctx, req) if err != nil { return nil, fmt.Errorf("GetRangeHash rpc failure: %w", err) @@ -1151,7 +1147,7 @@ func convertGetPrm(signer ecdsa.PrivateKey, req *protoobject.GetRequest, stream respStream: stream, } - p.SetRequestForwarder(func(ctx context.Context, node client.NodeInfo, c client.MultiAddressClient) (*object.Object, error) { + p.SetRequestForwarder(func(ctx context.Context, c client.MultiAddressClient) (*object.Object, error) { var err error onceResign.Do(func() { req.MetaHeader = &protosession.RequestMetaHeader{ @@ -1438,7 +1434,7 @@ func convertRangePrm(signer ecdsa.PrivateKey, req *protoobject.GetRangeRequest, if meta == nil { return getsvc.RangePrm{}, errors.New("missing meta header") } - p.SetRequestForwarder(func(ctx context.Context, node client.NodeInfo, c client.MultiAddressClient) (*object.Object, error) { + p.SetRequestForwarder(func(ctx context.Context, c client.MultiAddressClient) (*object.Object, error) { var err error onceResign.Do(func() { req.MetaHeader = &protosession.RequestMetaHeader{ @@ -1452,9 +1448,8 @@ func convertRangePrm(signer ecdsa.PrivateKey, req *protoobject.GetRangeRequest, return nil, err } - nodePub := node.PublicKey() return nil, c.ForEachGRPCConn(ctx, func(ctx context.Context, conn *grpc.ClientConn) error { - err := continueRangeFromRemoteNode(ctx, conn, nodePub, req, stream, &respondedPayload) + err := continueRangeFromRemoteNode(ctx, conn, req, stream, &respondedPayload) if errors.Is(err, io.EOF) { return nil } @@ -1464,7 +1459,7 @@ func convertRangePrm(signer ecdsa.PrivateKey, req *protoobject.GetRangeRequest, return p, nil } -func continueRangeFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []byte, req *protoobject.GetRangeRequest, +func continueRangeFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *protoobject.GetRangeRequest, stream *rangeStream, respondedPayload *int) error { rangeStream, err := protoobject.NewObjectServiceClient(conn).GetRange(ctx, req) if err != nil { @@ -1647,7 +1642,7 @@ func convertSearchPrm(ctx context.Context, signer ecdsa.PrivateKey, req *protoob if meta == nil { return searchsvc.Prm{}, errors.New("missing meta header") } - p.SetRequestForwarder(func(node client.NodeInfo, c client.MultiAddressClient) ([]oid.ID, error) { + p.SetRequestForwarder(func(c client.MultiAddressClient) ([]oid.ID, error) { var err error onceResign.Do(func() { req.MetaHeader = &protosession.RequestMetaHeader{ @@ -1661,18 +1656,17 @@ func convertSearchPrm(ctx context.Context, signer ecdsa.PrivateKey, req *protoob return nil, err } - nodePub := node.PublicKey() var res []oid.ID return res, c.ForEachGRPCConn(ctx, func(ctx context.Context, conn *grpc.ClientConn) error { var err error - res, err = searchOnRemoteNode(ctx, conn, nodePub, req) + res, err = searchOnRemoteNode(ctx, conn, req) return err // TODO: log error }) }) return p, nil } -func searchOnRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []byte, req *protoobject.SearchRequest) ([]oid.ID, error) { +func searchOnRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *protoobject.SearchRequest) ([]oid.ID, error) { searchStream, err := protoobject.NewObjectServiceClient(conn).Search(ctx, req) if err != nil { return nil, err @@ -2182,8 +2176,7 @@ func (s *Server) searchOnRemoteNode(ctx context.Context, node sdknetmap.NodeInfo } var info client.NodeInfo info.SetAddressGroup(endpoints) - nodePub := node.PublicKey() - info.SetPublicKey(nodePub) + info.SetPublicKey(node.PublicKey()) c, err := s.nodeClients.Get(ctx, info) if err != nil { return nil, false, fmt.Errorf("get node client: %w", err) @@ -2193,12 +2186,12 @@ func (s *Server) searchOnRemoteNode(ctx context.Context, node sdknetmap.NodeInfo var more bool return items, more, c.ForEachGRPCConn(ctx, func(ctx context.Context, conn *grpc.ClientConn) error { var err error - items, more, err = searchOnRemoteAddress(ctx, conn, nodePub, req) + items, more, err = searchOnRemoteAddress(ctx, conn, req) return err // TODO: log error }) } -func searchOnRemoteAddress(ctx context.Context, conn *grpc.ClientConn, nodePub []byte, +func searchOnRemoteAddress(ctx context.Context, conn *grpc.ClientConn, req *protoobject.SearchV2Request) ([]sdkclient.SearchResultItem, bool, error) { resp, err := protoobject.NewObjectServiceClient(conn).SearchV2(ctx, req) if err != nil {