Skip to content

Commit

Permalink
feat/exchange object meta's signatures (#2928)
Browse files Browse the repository at this point in the history
  • Loading branch information
roman-khimov authored Sep 12, 2024
2 parents cfc1f43 + d7b63cb commit 4a1bc79
Show file tree
Hide file tree
Showing 18 changed files with 384 additions and 52 deletions.
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 @@ type netInfo struct {
netState netmap.State

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

morphClientNetMap *nmClient.Client
Expand All @@ -404,7 +404,7 @@ func (n *netInfo) Dump(ver version.Version) (*netmapSDK.NetworkInfo, error) {

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

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 @@ 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"
Expand Down Expand Up @@ -256,7 +257,11 @@ func initObjectService(c *cfg) {
searchsvcV2.WithKeyStorage(keyStorage),
)

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

sPut := putsvc.NewService(&transport{clients: putConstructor}, c,
putsvc.WithNetworkMagic(mNumber),
putsvc.WithKeyStorage(keyStorage),
putsvc.WithClientConstructor(putConstructor),
putsvc.WithMaxSizeSource(newCachedMaxObjectSizeSource(c)),
Expand Down Expand Up @@ -350,7 +355,7 @@ func initObjectService(c *cfg) {
firstSvc = objectService.NewMetricCollector(signSvc, c.metricsCollector)
}

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

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 @@ 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"
"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 @@ 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 {
return fmt.Errorf("API transport (service=%s,op=%s): %w", m.Service, m.Name, err)
}
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) }

Expand All @@ -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
}
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
//
// 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))
}

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 @@ func (c *Client) roleList(r noderoles.Role) (keys.PublicKeys, error) {

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

// 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 @@ 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.
Expand Down Expand Up @@ -178,7 +180,18 @@ func (s *Server) Replicate(_ context.Context, req *objectGRPC.ReplicateRequest)
}}, nil
}

return new(objectGRPC.ReplicateResponse), nil
resp := new(objectGRPC.ReplicateResponse)
if req.GetSignObject() {
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
}
}

return resp, nil
}

func objectFromMessage(gMsg *objectGRPC.Object) (*object.Object, error) {
Expand All @@ -190,3 +203,42 @@ 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.EncodeReplicationMetaInfo(o.GetContainerID(), o.GetID(), o.PayloadSize(), deleted, locked,
o.CreationEpoch(), s.mNumber)

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
}
Loading

0 comments on commit 4a1bc79

Please sign in to comment.