Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/exchange object meta's signatures #2928

Merged
merged 4 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/neofs-node/netmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@
netState netmap.State

magic interface {
MagicNumber() (uint64, error)
MagicNumber() (uint32, error)
}

morphClientNetMap *nmClient.Client
Expand All @@ -404,7 +404,7 @@

var ni netmapSDK.NetworkInfo
ni.SetCurrentEpoch(n.netState.CurrentEpoch())
ni.SetMagicNumber(magic)
ni.SetMagicNumber(uint64(magic))

Check warning on line 407 in cmd/neofs-node/netmap.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/netmap.go#L407

Added line #L407 was not covered by tests

netInfoMorph, err := n.morphClientNetMap.ReadNetworkConfiguration()
if err != nil {
Expand Down
7 changes: 6 additions & 1 deletion cmd/neofs-node/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
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"
Expand Down Expand Up @@ -256,7 +257,11 @@
searchsvcV2.WithKeyStorage(keyStorage),
)

mNumber, err := c.shared.basics.cli.MagicNumber()
fatalOnErr(err)

Check warning on line 261 in cmd/neofs-node/object.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/object.go#L260-L261

Added lines #L260 - L261 were not covered by tests

sPut := putsvc.NewService(&transport{clients: putConstructor}, c,
putsvc.WithNetworkMagic(mNumber),

Check warning on line 264 in cmd/neofs-node/object.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/object.go#L264

Added line #L264 was not covered by tests
putsvc.WithKeyStorage(keyStorage),
putsvc.WithClientConstructor(putConstructor),
putsvc.WithMaxSizeSource(newCachedMaxObjectSizeSource(c)),
Expand Down Expand Up @@ -350,7 +355,7 @@
firstSvc = objectService.NewMetricCollector(signSvc, c.metricsCollector)
}

server := objectTransportGRPC.New(firstSvc, objNode)
server := objectTransportGRPC.New(firstSvc, mNumber, objNode, neofsecdsa.SignerRFC6979(c.shared.basics.key.PrivateKey))

Check warning on line 358 in cmd/neofs-node/object.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/object.go#L358

Added line #L358 was not covered by tests

for _, srv := range c.cfgGRPC.servers {
objectGRPC.RegisterObjectServiceServer(srv, server)
Expand Down
36 changes: 31 additions & 5 deletions cmd/neofs-node/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
"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"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/message"
"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 {
Expand All @@ -20,27 +22,31 @@

// 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) {

Check warning on line 25 in cmd/neofs-node/transport.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/transport.go#L25

Added line #L25 was not covered by tests
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)

Check warning on line 28 in cmd/neofs-node/transport.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/transport.go#L28

Added line #L28 was not covered by tests
}

return c.ExecRaw(func(c *rawclient.Client) error {
var resp replicateResponse
err = c.ExecRaw(func(c *rawclient.Client) error {

Check warning on line 32 in cmd/neofs-node/transport.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/transport.go#L31-L32

Added lines #L31 - L32 were not covered by tests
// 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 {
return fmt.Errorf("API transport (service=%s,op=%s): %w", m.Service, m.Name, err)
}
return resp.err
})
return resp.sig, err

Check warning on line 43 in cmd/neofs-node/transport.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/transport.go#L43

Added line #L43 was not covered by tests
}

type replicateResponse struct{ err error }
type replicateResponse struct {
sig *neofscrypto.Signature
err error
}

func (x replicateResponse) ToGRPCMessage() grpc.Message { return new(objectGRPC.ReplicateResponse) }

Expand All @@ -60,6 +66,26 @@
}

x.err = apistatus.ErrorFromV2(st)
if x.err != nil {
return nil

Check warning on line 70 in cmd/neofs-node/transport.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/transport.go#L69-L70

Added lines #L69 - L70 were not covered by tests
}

sig := m.GetObjectSignature()
if sig == nil {
return nil

Check warning on line 75 in cmd/neofs-node/transport.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/transport.go#L73-L75

Added lines #L73 - L75 were not covered by tests
}

sigV2 := new(refs.Signature)
err := sigV2.Unmarshal(sig)
if err != nil {
return fmt.Errorf("decoding signature from proto message: %w", err)

Check warning on line 81 in cmd/neofs-node/transport.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/transport.go#L78-L81

Added lines #L78 - L81 were not covered by tests
}

x.sig = new(neofscrypto.Signature)
err = x.sig.ReadFromV2(*sigV2)
if err != nil {
return fmt.Errorf("invalid signature: %w", err)

Check warning on line 87 in cmd/neofs-node/transport.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/transport.go#L84-L87

Added lines #L84 - L87 were not covered by tests
}

return nil
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/nspcc-dev/hrw/v2 v2.0.1
github.com/nspcc-dev/locode-db v0.6.0
github.com/nspcc-dev/neo-go v0.106.3
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea
github.com/nspcc-dev/neofs-contract v0.20.0
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12.0.20240809202351-256513c1b29b
github.com/nspcc-dev/tzhash v1.8.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ github.com/nspcc-dev/neo-go v0.106.3 h1:HEyhgkjQY+HfBzotMJ12xx2VuOUphkngZ4kEkjvX
github.com/nspcc-dev/neo-go v0.106.3/go.mod h1:3vEwJ2ld12N7HRGCaH/l/7EwopplC/+8XdIdPDNmD/M=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240729160116-d8e3e57f88f2 h1:tvPkeqnIeBFhM1b1Iwwi0jJiuoxkY4Xbk8mP3W1YVUY=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240729160116-d8e3e57f88f2/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4 h1:arN0Ypn+jawZpu1BND7TGRn44InAVIqKygndsx0y2no=
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4/go.mod h1:7Tm1NKEoUVVIUlkVwFrPh7GG5+Lmta2m7EGr4oVpBd8=
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea h1:mK0EMGLvunXcFyq7fBURS/CsN4MH+4nlYiqn6pTwWAU=
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.20.0 h1:ARE/3mSN+P9qi/10NBsf7QyPiYrvnxeEgYUN13vHRlo=
github.com/nspcc-dev/neofs-contract v0.20.0/go.mod h1:YxtKYE/5cMNiqwWcQWzeizbB9jizauLni+p8wXxfhsQ=
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12.0.20240809202351-256513c1b29b h1:/7jXQP5pf+M0kRFC1gg5GEdTPkvotpMHxjSXIbMZaGQ=
Expand Down
77 changes: 77 additions & 0 deletions pkg/core/object/replicate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package object

import (
"fmt"

"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 epochs
currentVersion = 7 // it is also a number of fields
)

const (
networkMagicKey = "network"
cidKey = "cid"
oidKey = "oid"
sizeKey = "size"
deletedKey = "deleted"
lockedKey = "locked"
validUntilKey = "validuntil"
)

// EncodeReplicationMetaInfo 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):
//
// "network": network magic
// "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
//
roman-khimov marked this conversation as resolved.
Show resolved Hide resolved
// Last valid epoch is object's creation epoch + 10.
func EncodeReplicationMetaInfo(cID cid.ID, oID oid.ID, pSize uint64,
deleted, locked []oid.ID, createdAt uint64, magicNumber uint32) []byte {
kvs := []stackitem.MapElement{
kv(networkMagicKey, magicNumber),
kv(cidKey, cID[:]),
kv(oidKey, oID[:]),
kv(sizeKey, pSize),
oidsKV(deletedKey, deleted),
oidsKV(lockedKey, locked),
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(fmt.Errorf("unexpected stackitem map serialization failure: %v", err))

Check warning on line 57 in pkg/core/object/replicate.go

View check run for this annotation

Codecov / codecov/patch

pkg/core/object/replicate.go#L57

Added line #L57 was not covered by tests
}

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)
}
69 changes: 69 additions & 0 deletions pkg/core/object/replicate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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) {
network := rand.Uint32()
oID := oidtest.ID()
cID := cidtest.ID()
size := rand.Uint64()
deleted := oidtest.IDs(10)
locked := oidtest.IDs(10)
validUntil := rand.Uint64()

raw := EncodeReplicationMetaInfo(cID, oID, size, deleted, locked, validUntil, network)
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, networkMagicKey, string(mm[0].Key.Value().([]byte)))
require.Equal(t, network, uint32(mm[0].Value.Value().(*big.Int).Uint64()))

require.Equal(t, cidKey, string(mm[1].Key.Value().([]byte)))
require.Equal(t, cID[:], mm[1].Value.Value().([]byte))

require.Equal(t, oidKey, string(mm[2].Key.Value().([]byte)))
require.Equal(t, oID[:], mm[2].Value.Value().([]byte))

require.Equal(t, sizeKey, string(mm[3].Key.Value().([]byte)))
require.Equal(t, size, mm[3].Value.Value().(*big.Int).Uint64())

require.Equal(t, deletedKey, string(mm[4].Key.Value().([]byte)))
require.Equal(t, deleted, stackItemToOIDs(t, mm[4].Value))

require.Equal(t, lockedKey, string(mm[5].Key.Value().([]byte)))
require.Equal(t, locked, stackItemToOIDs(t, mm[5].Value))

require.Equal(t, validUntilKey, string(mm[6].Key.Value().([]byte)))
require.Equal(t, validUntil+validInterval, mm[6].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
}
4 changes: 2 additions & 2 deletions pkg/morph/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,15 +533,15 @@

// MagicNumber returns the magic number of the network
// to which the underlying RPC node client is connected.
func (c *Client) MagicNumber() (uint64, error) {
func (c *Client) MagicNumber() (uint32, error) {

Check warning on line 536 in pkg/morph/client/client.go

View check run for this annotation

Codecov / codecov/patch

pkg/morph/client/client.go#L536

Added line #L536 was not covered by tests
c.switchLock.RLock()
defer c.switchLock.RUnlock()

if c.inactive {
return 0, ErrConnectionLost
}

return uint64(c.rpcActor.GetNetwork()), nil
return uint32(c.rpcActor.GetNetwork()), nil

Check warning on line 544 in pkg/morph/client/client.go

View check run for this annotation

Codecov / codecov/patch

pkg/morph/client/client.go#L544

Added line #L544 was not covered by tests
}

// BlockCount returns block count of the network
Expand Down
54 changes: 53 additions & 1 deletion pkg/network/transport/object/grpc/replication.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
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.
Expand Down Expand Up @@ -178,7 +180,18 @@
}}, nil
}

return new(objectGRPC.ReplicateResponse), nil
resp := new(objectGRPC.ReplicateResponse)
if req.GetSignObject() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do u plan to add tests for these cases? there are few of them now

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added some

resp.ObjectSignature, 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

Check warning on line 190 in pkg/network/transport/object/grpc/replication.go

View check run for this annotation

Codecov / codecov/patch

pkg/network/transport/object/grpc/replication.go#L187-L190

Added lines #L187 - L190 were not covered by tests
}
}

return resp, nil
}

func objectFromMessage(gMsg *objectGRPC.Object) (*object.Object, error) {
Expand All @@ -190,3 +203,42 @@

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)

Check warning on line 215 in pkg/network/transport/object/grpc/replication.go

View check run for this annotation

Codecov / codecov/patch

pkg/network/transport/object/grpc/replication.go#L211-L215

Added lines #L211 - L215 were not covered by tests
}

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)

Check warning on line 223 in pkg/network/transport/object/grpc/replication.go

View check run for this annotation

Codecov / codecov/patch

pkg/network/transport/object/grpc/replication.go#L218-L223

Added lines #L218 - L223 were not covered by tests
}

locked = make([]oid.ID, l.NumberOfMembers())
l.ReadMembers(locked)

Check warning on line 227 in pkg/network/transport/object/grpc/replication.go

View check run for this annotation

Codecov / codecov/patch

pkg/network/transport/object/grpc/replication.go#L226-L227

Added lines #L226 - L227 were not covered by tests
default:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forgot code or redundant?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, why? if we are replicating a lock/tomb object, we add IDs, but if we are adding a regular object, nothing should be done in this switch
or what you suggest here?

}

metaInfo := objectcore.EncodeReplicationMetaInfo(o.GetContainerID(), o.GetID(), o.PayloadSize(), deleted, locked,
o.CreationEpoch(), s.mNumber)

var sig neofscrypto.Signature
err := sig.Calculate(s.signer, metaInfo)
Copy link
Contributor

@cthulhu-rider cthulhu-rider Sep 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this differs from the original protocol definition

do I understand correctly that we decided to limit the amount of information being signed, and this approach will eventually be "backported" into the protocol?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, things have changed with time (or i did not get them from the beginning). we have discussed it with @roman-khimov, a custom message should be provided (see the 2. in the comment mostly)

and this approach will eventually be "backported" into the protocol?

yes, that is right. not release was done since so it should not be a problem

if err != nil {
return nil, fmt.Errorf("signature failure: %w", err)

Check warning on line 237 in pkg/network/transport/object/grpc/replication.go

View check run for this annotation

Codecov / codecov/patch

pkg/network/transport/object/grpc/replication.go#L237

Added line #L237 was not covered by tests
}

sigV2 := new(refsv2.Signature)
sig.WriteToV2(sigV2)

return sigV2.StableMarshal(nil), nil
}
Loading
Loading