diff --git a/cmd/neofs-node/object.go b/cmd/neofs-node/object.go index 701d23b77c..24addfe663 100644 --- a/cmd/neofs-node/object.go +++ b/cmd/neofs-node/object.go @@ -38,6 +38,7 @@ import ( truststorage "github.com/nspcc-dev/neofs-node/pkg/services/reputation/local/storage" "github.com/nspcc-dev/neofs-sdk-go/client" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl" netmapsdk "github.com/nspcc-dev/neofs-sdk-go/netmap" objectSDK "github.com/nspcc-dev/neofs-sdk-go/object" @@ -350,7 +351,7 @@ func initObjectService(c *cfg) { firstSvc = objectService.NewMetricCollector(signSvc, c.metricsCollector) } - server := objectTransportGRPC.New(firstSvc, objNode) + server := objectTransportGRPC.New(firstSvc, objNode, neofsecdsa.SignerRFC6979(c.shared.basics.key.PrivateKey)) for _, srv := range c.cfgGRPC.servers { objectGRPC.RegisterObjectServiceServer(srv, server) diff --git a/cmd/neofs-node/transport.go b/cmd/neofs-node/transport.go index 33a5dc56e4..156be9a54c 100644 --- a/cmd/neofs-node/transport.go +++ b/cmd/neofs-node/transport.go @@ -5,6 +5,7 @@ import ( "fmt" objectGRPC "github.com/nspcc-dev/neofs-api-go/v2/object/grpc" + "github.com/nspcc-dev/neofs-api-go/v2/refs" rawclient "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" "github.com/nspcc-dev/neofs-api-go/v2/rpc/common" "github.com/nspcc-dev/neofs-api-go/v2/rpc/grpc" @@ -12,6 +13,7 @@ import ( "github.com/nspcc-dev/neofs-api-go/v2/status" coreclient "github.com/nspcc-dev/neofs-node/pkg/core/client" apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" + neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" ) type transport struct { @@ -20,17 +22,17 @@ 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) error { +func (x *transport) SendReplicationRequestToNode(ctx context.Context, req []byte, node coreclient.NodeInfo) (*neofscrypto.Signature, error) { c, err := x.clients.Get(node) if err != nil { - return fmt.Errorf("connect to remote node: %w", err) + return nil, fmt.Errorf("connect to remote node: %w", err) } - return c.ExecRaw(func(c *rawclient.Client) error { + var resp replicateResponse + err = c.ExecRaw(func(c *rawclient.Client) error { // this will be changed during NeoFS API Go deprecation. Code most likely be // placed in SDK m := common.CallMethodInfo{Service: "neo.fs.v2.object.ObjectService", Name: "Replicate"} - var resp replicateResponse err = rawclient.SendUnary(c, m, rawclient.BinaryMessage(req), &resp, rawclient.WithContext(ctx), rawclient.AllowBinarySendingOnly()) if err != nil { @@ -38,9 +40,13 @@ func (x *transport) SendReplicationRequestToNode(ctx context.Context, req []byte } return resp.err }) + return resp.sig, err } -type replicateResponse struct{ err error } +type replicateResponse struct { + sig *neofscrypto.Signature + err error +} func (x replicateResponse) ToGRPCMessage() grpc.Message { return new(objectGRPC.ReplicateResponse) } @@ -60,6 +66,26 @@ func (x *replicateResponse) FromGRPCMessage(gm grpc.Message) error { } x.err = apistatus.ErrorFromV2(st) + if x.err != nil { + return nil + } + + sig := m.GetObjectSignature() + if sig == nil { + return nil + } + + sigV2 := new(refs.Signature) + err := sigV2.Unmarshal(sig) + if err != nil { + return fmt.Errorf("decoding signature from proto message: %w", err) + } + + x.sig = new(neofscrypto.Signature) + err = x.sig.ReadFromV2(*sigV2) + if err != nil { + return fmt.Errorf("invalid signature: %w", err) + } return nil } diff --git a/pkg/core/object/replicate.go b/pkg/core/object/replicate.go new file mode 100644 index 0000000000..3db3632f1b --- /dev/null +++ b/pkg/core/object/replicate.go @@ -0,0 +1,68 @@ +package object + +import ( + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" +) + +const ( + validInterval = 10 // in epoches + currentVersion = 6 // it is also a number of fields + + cidKey = "cid" + oidKey = "oid" + sizeKey = "size" + deletedKey = "deleted" + lockedKey = "locked" + validUntilKey = "validuntil" +) + +// MetaInfo uses NEO's map (strict order) serialized format as a raw +// representation of object's meta information. +// +// This (ordered) format is used (keys are strings): +// +// "cid": _raw_ container ID (32 bytes) +// "oid": _raw_ object ID (32 bytes) +// "size": payload size +// "deleted": array of _raw_ object IDs +// "locked": array of _raw_ object IDs +// "validuntil": last valid epoch number for meta information +// +// Last valid epoch is object's creation epoch + 10. +func MetaInfo(cID cid.ID, oID oid.ID, pSize uint64, deleted []oid.ID, locked []oid.ID, createdAt uint64) []byte { + kvs := make([]stackitem.MapElement, 0, currentVersion) + kvs = append(kvs, kv(cidKey, cID[:])) + kvs = append(kvs, kv(oidKey, oID[:])) + kvs = append(kvs, kv(sizeKey, pSize)) + kvs = append(kvs, oidsKV(deletedKey, deleted)) + kvs = append(kvs, oidsKV(lockedKey, locked)) + kvs = append(kvs, kv(validUntilKey, createdAt+validInterval)) + + result, err := stackitem.Serialize(stackitem.NewMapWithValue(kvs)) + if err != nil { + // all the errors in the stackitem relate only cases when it is + // impossible to use serialized values (too many values, unsupported + // types, etc.), unexpected errors at all + panic(err) + } + + return result +} + +func kv(k string, value any) stackitem.MapElement { + return stackitem.MapElement{ + Key: stackitem.Make(k), + Value: stackitem.Make(value), + } +} + +func oidsKV(fieldKey string, oIDs []oid.ID) stackitem.MapElement { + res := make([]stackitem.Item, 0, len(oIDs)) + for _, oID := range oIDs { + res = append(res, stackitem.NewByteArray(oID[:])) + } + + return kv(fieldKey, res) +} diff --git a/pkg/core/object/replicate_test.go b/pkg/core/object/replicate_test.go new file mode 100644 index 0000000000..163e3a0ad1 --- /dev/null +++ b/pkg/core/object/replicate_test.go @@ -0,0 +1,65 @@ +package object + +import ( + "math/big" + "math/rand/v2" + "testing" + + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" + oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" + "github.com/stretchr/testify/require" +) + +func TestMetaInfo(t *testing.T) { + oID := oidtest.ID() + cID := cidtest.ID() + size := rand.Uint64() + deleted := oidtest.IDs(10) + locked := oidtest.IDs(10) + validUntil := rand.Uint64() + + raw := MetaInfo(cID, oID, size, deleted, locked, validUntil) + item, err := stackitem.Deserialize(raw) + require.NoError(t, err) + + require.Equal(t, stackitem.MapT, item.Type()) + mm, ok := item.Value().([]stackitem.MapElement) + require.True(t, ok) + + require.Len(t, mm, currentVersion) + + require.Equal(t, cidKey, string(mm[0].Key.Value().([]byte))) + require.Equal(t, cID[:], mm[0].Value.Value().([]byte)) + + require.Equal(t, oidKey, string(mm[1].Key.Value().([]byte))) + require.Equal(t, oID[:], mm[1].Value.Value().([]byte)) + + require.Equal(t, sizeKey, string(mm[2].Key.Value().([]byte))) + require.Equal(t, size, mm[2].Value.Value().(*big.Int).Uint64()) + + require.Equal(t, deletedKey, string(mm[3].Key.Value().([]byte))) + require.Equal(t, deleted, stackItemToOIDs(t, mm[3].Value)) + + require.Equal(t, lockedKey, string(mm[4].Key.Value().([]byte))) + require.Equal(t, locked, stackItemToOIDs(t, mm[4].Value)) + + require.Equal(t, validUntilKey, string(mm[5].Key.Value().([]byte))) + require.Equal(t, validUntil+validInterval, mm[5].Value.Value().(*big.Int).Uint64()) +} + +func stackItemToOIDs(t *testing.T, value stackitem.Item) []oid.ID { + value, ok := value.(*stackitem.Array) + require.True(t, ok) + + vv := value.Value().([]stackitem.Item) + res := make([]oid.ID, 0, len(vv)) + + for _, v := range vv { + raw := v.Value().([]byte) + res = append(res, oid.ID(raw)) + } + + return res +} diff --git a/pkg/network/transport/object/grpc/replication.go b/pkg/network/transport/object/grpc/replication.go index 0945acf1e0..71d765e93b 100644 --- a/pkg/network/transport/object/grpc/replication.go +++ b/pkg/network/transport/object/grpc/replication.go @@ -11,11 +11,13 @@ import ( refsv2 "github.com/nspcc-dev/neofs-api-go/v2/refs" refs "github.com/nspcc-dev/neofs-api-go/v2/refs/grpc" status "github.com/nspcc-dev/neofs-api-go/v2/status/grpc" + objectcore "github.com/nspcc-dev/neofs-node/pkg/core/object" apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" "github.com/nspcc-dev/neofs-sdk-go/object" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" ) // Replicate serves neo.fs.v2.object.ObjectService/Replicate RPC. @@ -178,7 +180,20 @@ func (s *Server) Replicate(_ context.Context, req *objectGRPC.ReplicateRequest) }}, nil } - return new(objectGRPC.ReplicateResponse), nil + resp := new(objectGRPC.ReplicateResponse) + if req.GetSignObject() { + sigRaw, err := s.metaInfoSignature(*obj) + if err != nil { + return &objectGRPC.ReplicateResponse{Status: &status.Status{ + Code: codeInternal, + Message: fmt.Sprintf("failed to sign object meta information: %v", err), + }}, nil + } + + resp.ObjectSignature = sigRaw + } + + return resp, nil } func objectFromMessage(gMsg *objectGRPC.Object) (*object.Object, error) { @@ -190,3 +205,41 @@ func objectFromMessage(gMsg *objectGRPC.Object) (*object.Object, error) { return object.NewFromV2(&msg), nil } + +func (s *Server) metaInfoSignature(o object.Object) ([]byte, error) { + var deleted []oid.ID + var locked []oid.ID + switch o.Type() { + case object.TypeTombstone: + var t object.Tombstone + err := t.Unmarshal(o.Payload()) + if err != nil { + return nil, fmt.Errorf("reading tombstoned objects: %w", err) + } + + deleted = t.Members() + case object.TypeLock: + var l object.Lock + err := l.Unmarshal(o.Payload()) + if err != nil { + return nil, fmt.Errorf("reading locked objects: %w", err) + } + + locked = make([]oid.ID, l.NumberOfMembers()) + l.ReadMembers(locked) + default: + } + + metaInfo := objectcore.MetaInfo(o.GetContainerID(), o.GetID(), o.PayloadSize(), deleted, locked, o.CreationEpoch()) + + var sig neofscrypto.Signature + err := sig.Calculate(s.signer, metaInfo) + if err != nil { + return nil, fmt.Errorf("signature failure: %w", err) + } + + sigV2 := new(refsv2.Signature) + sig.WriteToV2(sigV2) + + return sigV2.StableMarshal(nil), nil +} diff --git a/pkg/network/transport/object/grpc/replication_test.go b/pkg/network/transport/object/grpc/replication_test.go index e48e34b72f..09048fb23d 100644 --- a/pkg/network/transport/object/grpc/replication_test.go +++ b/pkg/network/transport/object/grpc/replication_test.go @@ -141,6 +141,7 @@ func anyValidRequest(tb testing.TB, signer neofscrypto.Signer, cnr cid.ID, objID Key: neofscrypto.PublicKeyBytes(signer.Public()), Sign: sig, }, + SignObject: false, } switch signer.Scheme() { @@ -160,7 +161,7 @@ func anyValidRequest(tb testing.TB, signer neofscrypto.Signer, cnr cid.ID, objID func TestServer_Replicate(t *testing.T) { var noCallNode noCallTestNode var noCallObjSvc noCallObjectService - noCallSrv := New(noCallObjSvc, &noCallNode) + noCallSrv := New(noCallObjSvc, &noCallNode, neofscryptotest.Signer()) clientSigner := neofscryptotest.Signer() clientPubKey := neofscrypto.PublicKeyBytes(clientSigner.Public()) serverPubKey := neofscrypto.PublicKeyBytes(neofscryptotest.Signer().Public()) @@ -324,7 +325,7 @@ func TestServer_Replicate(t *testing.T) { t.Run("apply storage policy failure", func(t *testing.T) { node := newTestNode(t, serverPubKey, clientPubKey, cnr, req.Object) - srv := New(noCallObjSvc, node) + srv := New(noCallObjSvc, node, neofscryptotest.Signer()) node.cnrErr = errors.New("any error") @@ -336,7 +337,7 @@ func TestServer_Replicate(t *testing.T) { t.Run("client or server mismatches object's storage policy", func(t *testing.T) { node := newTestNode(t, serverPubKey, clientPubKey, cnr, req.Object) - srv := New(noCallObjSvc, node) + srv := New(noCallObjSvc, node, neofscryptotest.Signer()) node.serverOutsideCnr = true node.clientOutsideCnr = true @@ -356,7 +357,7 @@ func TestServer_Replicate(t *testing.T) { t.Run("local storage failure", func(t *testing.T) { node := newTestNode(t, serverPubKey, clientPubKey, cnr, req.Object) - srv := New(noCallObjSvc, node) + srv := New(noCallObjSvc, node, neofscryptotest.Signer()) node.storeErr = errors.New("any error") @@ -368,7 +369,7 @@ func TestServer_Replicate(t *testing.T) { t.Run("OK", func(t *testing.T) { node := newTestNode(t, serverPubKey, clientPubKey, cnr, req.Object) - srv := New(noCallObjSvc, node) + srv := New(noCallObjSvc, node, neofscryptotest.Signer()) resp, err := srv.Replicate(context.Background(), req) require.NoError(t, err) @@ -395,7 +396,7 @@ func BenchmarkServer_Replicate(b *testing.B) { ctx := context.Background() var node nopNode - srv := New(nil, node) + srv := New(nil, node, neofscryptotest.Signer()) for _, tc := range []struct { name string diff --git a/pkg/network/transport/object/grpc/service.go b/pkg/network/transport/object/grpc/service.go index 511d5413fa..2b2babdaf6 100644 --- a/pkg/network/transport/object/grpc/service.go +++ b/pkg/network/transport/object/grpc/service.go @@ -11,6 +11,7 @@ import ( objectSvc "github.com/nspcc-dev/neofs-node/pkg/services/object" "github.com/nspcc-dev/neofs-node/pkg/services/util" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" objectsdk "github.com/nspcc-dev/neofs-sdk-go/object" ) @@ -47,14 +48,16 @@ type Node interface { type Server struct { srv objectSvc.ServiceServer - node Node + node Node + signer neofscrypto.Signer } // New creates, initializes and returns Server instance. -func New(c objectSvc.ServiceServer, node Node) *Server { +func New(c objectSvc.ServiceServer, node Node, signer neofscrypto.Signer) *Server { return &Server{ - srv: c, - node: node, + srv: c, + node: node, + signer: signer, } } diff --git a/pkg/services/object/put/distributed.go b/pkg/services/object/put/distributed.go index cb0b59554f..4f620baef6 100644 --- a/pkg/services/object/put/distributed.go +++ b/pkg/services/object/put/distributed.go @@ -1,6 +1,7 @@ package putsvc import ( + "bytes" "fmt" "math" "slices" @@ -12,6 +13,7 @@ import ( "github.com/nspcc-dev/neofs-node/pkg/network" svcutil "github.com/nspcc-dev/neofs-node/pkg/services/object/util" "github.com/nspcc-dev/neofs-node/pkg/util" + apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" "github.com/nspcc-dev/neofs-sdk-go/netmap" objectSDK "github.com/nspcc-dev/neofs-sdk-go/object" @@ -21,14 +23,15 @@ import ( type preparedObjectTarget interface { WriteObject(*objectSDK.Object, object.ContentMeta, encodedObject) error - Close() (oid.ID, error) + Close() (oid.ID, *neofscrypto.Signature, error) } type distributedTarget struct { placementIterator placementIterator - obj *objectSDK.Object - objMeta object.ContentMeta + obj *objectSDK.Object + objMeta object.ContentMeta + objSharedMeta []byte localNodeInContainer bool localNodeSigner neofscrypto.Signer @@ -76,7 +79,7 @@ func (t *distributedTarget) WriteHeader(hdr *objectSDK.Object) error { if t.placementIterator.localOnly { t.encodedObject, err = encodeObjectWithoutPayload(*hdr, int(payloadLen)) } else { - t.encodedObject, err = encodeReplicateRequestWithoutPayload(t.localNodeSigner, *hdr, int(payloadLen)) + t.encodedObject, err = encodeReplicateRequestWithoutPayload(t.localNodeSigner, *hdr, int(payloadLen), true) } if err != nil { return fmt.Errorf("encode object into binary: %w", err) @@ -127,6 +130,17 @@ func (t *distributedTarget) Close() (oid.ID, error) { } } + var deletedObjs []oid.ID + var lockedObjs []oid.ID + switch t.objMeta.Type() { + case objectSDK.TypeTombstone: + deletedObjs = t.objMeta.Objects() + case objectSDK.TypeLock: + lockedObjs = t.objMeta.Objects() + default: + } + + t.objSharedMeta = object.MetaInfo(t.obj.GetContainerID(), t.obj.GetID(), t.obj.PayloadSize(), deletedObjs, lockedObjs, t.obj.CreationEpoch()) id, _ := t.obj.ID() return id, t.placementIterator.iterateNodesForObject(id, t.sendObject) } @@ -138,11 +152,30 @@ func (t *distributedTarget) sendObject(node nodeDesc) error { target := t.nodeTargetInitializer(node) - if err := target.WriteObject(t.obj, t.objMeta, t.encodedObject); err != nil { + err := target.WriteObject(t.obj, t.objMeta, t.encodedObject) + if err != nil { return fmt.Errorf("could not write header: %w", err) - } else if _, err := target.Close(); err != nil { + } + + _, sig, err := target.Close() + if err != nil { return fmt.Errorf("could not close object stream: %w", err) } + + if !node.local { + if !bytes.Equal(sig.PublicKeyBytes(), node.info.PublicKey()) { + return fmt.Errorf("%w: public key differs", apistatus.ErrSignatureVerification) + } + + fmt.Println("DEBUG: trying to verify signatures") + + if !sig.Verify(t.objSharedMeta) { + return apistatus.ErrSignatureVerification + } + + fmt.Println("DEBUG: verification was OK") + } + return nil } diff --git a/pkg/services/object/put/local.go b/pkg/services/object/put/local.go index 1963983dda..755274e738 100644 --- a/pkg/services/object/put/local.go +++ b/pkg/services/object/put/local.go @@ -8,6 +8,7 @@ import ( objectCore "github.com/nspcc-dev/neofs-node/pkg/core/object" "github.com/nspcc-dev/neofs-sdk-go/checksum" + neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" "github.com/nspcc-dev/neofs-sdk-go/object" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" "github.com/nspcc-dev/tzhash/tz" @@ -47,15 +48,15 @@ func (t *localTarget) WriteObject(obj *object.Object, meta objectCore.ContentMet return nil } -func (t *localTarget) Close() (oid.ID, error) { +func (t *localTarget) Close() (oid.ID, *neofscrypto.Signature, error) { err := putObjectLocally(t.storage, t.obj, t.meta, &t.enc) if err != nil { - return oid.ID{}, err + return oid.ID{}, nil, err } id, _ := t.obj.ID() - return id, nil + return id, nil, nil } func putObjectLocally(storage ObjectStorage, obj *object.Object, meta objectCore.ContentMeta, enc *encodedObject) error { diff --git a/pkg/services/object/put/proto.go b/pkg/services/object/put/proto.go index 75f1174e84..5cc3c4eccf 100644 --- a/pkg/services/object/put/proto.go +++ b/pkg/services/object/put/proto.go @@ -30,6 +30,7 @@ const ( _ = iota fieldNumReplicateObject fieldNumReplicateSignature + fieldSignObjectMeta ) type encodedObject struct { @@ -66,7 +67,7 @@ func encodeObjectWithoutPayload(hdr object.Object, pldLen int) (encodedObject, e return res, nil } -func encodeReplicateRequestWithoutPayload(signer neofscrypto.Signer, hdr object.Object, pldLen int) (encodedObject, error) { +func encodeReplicateRequestWithoutPayload(signer neofscrypto.Signer, hdr object.Object, pldLen int, signObjectMeta bool) (encodedObject, error) { var res encodedObject id, ok := hdr.ID() if !ok { @@ -98,6 +99,7 @@ func encodeReplicateRequestWithoutPayload(signer neofscrypto.Signer, hdr object. return res, fmt.Errorf("replicate request exceeds server limit %d", math.MaxInt) } fullLen += protowire.SizeBytes(objFldLen) + fullLen += protowire.SizeTag(fieldSignObjectMeta) + protowire.SizeVarint(protowire.EncodeBool(signObjectMeta)) res.b = getPayload() if cap(res.b) < fullLen { @@ -105,6 +107,9 @@ func encodeReplicateRequestWithoutPayload(signer neofscrypto.Signer, hdr object. res.b = make([]byte, 0, fullLen) } + // meta signature extension flag + res.b = protowire.AppendTag(res.b, fieldSignObjectMeta, protowire.VarintType) + res.b = protowire.AppendVarint(res.b, protowire.EncodeBool(signObjectMeta)) // signature res.b = protowire.AppendTag(res.b, fieldNumReplicateSignature, protowire.BytesType) res.b = protowire.AppendVarint(res.b, uint64(sigFldLen)) diff --git a/pkg/services/object/put/proto_test.go b/pkg/services/object/put/proto_test.go index b8e55d0058..fe18ea6c7a 100644 --- a/pkg/services/object/put/proto_test.go +++ b/pkg/services/object/put/proto_test.go @@ -29,7 +29,7 @@ func TestUnaryReplicateRequest(t *testing.T) { signer := neofscryptotest.Signer() // prepare request - r, err := encodeReplicateRequestWithoutPayload(signer, hdr, len(payload)) + r, err := encodeReplicateRequestWithoutPayload(signer, hdr, len(payload), true) require.NoError(t, err) require.Equal(t, len(payload), cap(r.b)-r.pldOff) require.Equal(t, len(payload), cap(r.b)-len(r.b)) @@ -55,4 +55,7 @@ func TestUnaryReplicateRequest(t *testing.T) { require.NoError(t, objv2.FromGRPCMessage(req.Object)) obj2 := *object.NewFromV2(&objv2) require.Equal(t, obj, obj2) + + // check meta signature flag + require.True(t, req.GetSignObject()) } diff --git a/pkg/services/object/put/remote.go b/pkg/services/object/put/remote.go index f0f34ab73a..a484822741 100644 --- a/pkg/services/object/put/remote.go +++ b/pkg/services/object/put/remote.go @@ -10,6 +10,7 @@ import ( objectcore "github.com/nspcc-dev/neofs-node/pkg/core/object" internalclient "github.com/nspcc-dev/neofs-node/pkg/services/object/internal/client" "github.com/nspcc-dev/neofs-node/pkg/services/object/util" + neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" 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/object" @@ -53,14 +54,13 @@ func (t *remoteTarget) WriteObject(obj *object.Object, _ objectcore.ContentMeta, return nil } -func (t *remoteTarget) Close() (oid.ID, error) { +func (t *remoteTarget) Close() (oid.ID, *neofscrypto.Signature, error) { if t.enc.hdrOff > 0 { - err := t.transport.SendReplicationRequestToNode(t.ctx, t.enc.b, t.nodeInfo) + sig, err := t.transport.SendReplicationRequestToNode(t.ctx, t.enc.b, t.nodeInfo) if err != nil { - return oid.ID{}, fmt.Errorf("replicate object to remote node (key=%x): %w", t.nodeInfo.PublicKey(), err) + return oid.ID{}, nil, fmt.Errorf("replicate object to remote node (key=%x): %w", t.nodeInfo.PublicKey(), err) } - id, _ := t.obj.ID() - return id, nil + return t.obj.GetID(), sig, nil } var sessionInfo *util.SessionInfo @@ -74,12 +74,12 @@ func (t *remoteTarget) Close() (oid.ID, error) { key, err := t.keyStorage.GetKey(sessionInfo) if err != nil { - return oid.ID{}, fmt.Errorf("(%T) could not receive private key: %w", t, err) + return oid.ID{}, nil, fmt.Errorf("(%T) could not receive private key: %w", t, err) } c, err := t.clientConstructor.Get(t.nodeInfo) if err != nil { - return oid.ID{}, fmt.Errorf("(%T) could not create SDK client %s: %w", t, t.nodeInfo, err) + return oid.ID{}, nil, fmt.Errorf("(%T) could not create SDK client %s: %w", t, t.nodeInfo, err) } var prm internalclient.PutObjectPrm @@ -94,10 +94,10 @@ func (t *remoteTarget) Close() (oid.ID, error) { res, err := internalclient.PutObject(prm) if err != nil { - return oid.ID{}, fmt.Errorf("(%T) could not put object to %s: %w", t, t.nodeInfo.AddressGroup(), err) + return oid.ID{}, nil, fmt.Errorf("(%T) could not put object to %s: %w", t, t.nodeInfo.AddressGroup(), err) } - return res.ID(), nil + return res.ID(), nil, nil } // NewRemoteSender creates, initializes and returns new RemoteSender instance. @@ -139,9 +139,13 @@ func (s *RemoteSender) PutObject(ctx context.Context, p *RemotePutPrm) error { return fmt.Errorf("parse client node info: %w", err) } - if err := t.WriteObject(p.obj, objectcore.ContentMeta{}, encodedObject{}); err != nil { + err = t.WriteObject(p.obj, objectcore.ContentMeta{}, encodedObject{}) + if err != nil { return fmt.Errorf("(%T) could not send object header: %w", s, err) - } else if _, err := t.Close(); err != nil { + } + + _, _, err = t.Close() + if err != nil { return fmt.Errorf("(%T) could not send object: %w", s, err) } diff --git a/pkg/services/object/put/service.go b/pkg/services/object/put/service.go index 04ae1a82ec..5b14ea6377 100644 --- a/pkg/services/object/put/service.go +++ b/pkg/services/object/put/service.go @@ -10,6 +10,7 @@ import ( objutil "github.com/nspcc-dev/neofs-node/pkg/services/object/util" "github.com/nspcc-dev/neofs-node/pkg/util" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" netmapsdk "github.com/nspcc-dev/neofs-sdk-go/netmap" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" "go.uber.org/zap" @@ -35,7 +36,7 @@ type Option func(*cfg) type Transport interface { // SendReplicationRequestToNode sends a prepared replication request message to // the specified remote node. - SendReplicationRequestToNode(ctx context.Context, req []byte, node client.NodeInfo) error + SendReplicationRequestToNode(ctx context.Context, req []byte, node client.NodeInfo) (*neofscrypto.Signature, error) } type ClientConstructor interface {