[#11] Trim the old functionality

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2020-08-21 14:32:03 +03:00 committed by Alex Vanin
parent 783ec72d56
commit a87fdab324
235 changed files with 39 additions and 36211 deletions

View file

View file

@ -1,24 +0,0 @@
package basic
const (
// OpGetRangeHash is an index of GetRangeHash operation in basic ACL bitmask order.
OpGetRangeHash uint8 = iota
// OpGetRange is an index of GetRange operation in basic ACL bitmask order.
OpGetRange
// OpSearch is an index of Search operation in basic ACL bitmask order.
OpSearch
// OpDelete is an index of Delete operation in basic ACL bitmask order.
OpDelete
// OpPut is an index of Put operation in basic ACL bitmask order.
OpPut
// OpHead is an index of Head operation in basic ACL bitmask order.
OpHead
// OpGet is an index of Get operation in basic ACL bitmask order.
OpGet
)

View file

@ -1,159 +0,0 @@
package basic
// ACL represents a container's
// basic permission bits.
type ACL uint32
const (
reservedBitNumber = 2 // first left bits are reserved
stickyBitPos = reservedBitNumber // X-bit after reserved bits
finalBitPos = stickyBitPos + 1 // F-bit after X-bit
)
const (
opOffset = finalBitPos + 1 // offset of operation bits
bitsPerOp = 4 // number of bits per operation
opNumber = 7 // number of operation bit sections
)
const (
bitUser uint8 = iota
bitSystem
bitOthers
bitBearer
)
const leftACLBitPos = opOffset + bitsPerOp*opNumber - 1
// returns true if n-th left bit is set (starting at 0).
func isLeftBitSet(value ACL, n uint8) bool {
bitMask := ACL(1 << (leftACLBitPos - n))
return bitMask != 0 && value&bitMask == bitMask
}
// sets n-th left bit (starting at 0).
func setLeftBit(value *ACL, n uint8) {
*value |= ACL(1 << (leftACLBitPos - n))
}
// resets n-th left bit (starting at 0).
func resetLeftBit(value *ACL, n uint8) {
*value &= ^ACL(1 << (leftACLBitPos - n))
}
// Reserved returns true if n-th reserved option is enabled in basic ACL.
func (a ACL) Reserved(n uint8) bool {
return n < reservedBitNumber && isLeftBitSet(a, n)
}
// SetReserved enables the n-th reserved option in basic ACL.
func (a *ACL) SetReserved(bit uint8) {
if bit < reservedBitNumber {
setLeftBit(a, bit)
}
}
// ResetReserved disables the n-th reserved option in basic ACL.
func (a *ACL) ResetReserved(bit uint8) {
if bit < reservedBitNumber {
resetLeftBit(a, bit)
}
}
// Final returns true if final option is enabled in basic ACL.
func (a ACL) Final() bool {
return isLeftBitSet(a, finalBitPos)
}
// SetFinal enables final option in basic ACL.
func (a *ACL) SetFinal() {
setLeftBit(a, finalBitPos)
}
// ResetFinal disables final option in basic ACL.
func (a *ACL) ResetFinal() {
resetLeftBit(a, finalBitPos)
}
// Sticky returns true if sticky option is enabled in basic ACL.
func (a ACL) Sticky() bool {
return isLeftBitSet(a, stickyBitPos)
}
// SetSticky enables the sticky option in basic ACL.
func (a *ACL) SetSticky() {
setLeftBit(a, stickyBitPos)
}
// ResetSticky disables the sticky option in basic ACL.
func (a *ACL) ResetSticky() {
resetLeftBit(a, stickyBitPos)
}
// UserAllowed returns true if user allowed the n-th operation in basic ACL.
func (a ACL) UserAllowed(n uint8) bool {
return isLeftBitSet(a, opOffset+n*bitsPerOp+bitUser)
}
// AllowUser allows user the n-th operation in basic ACL.
func (a *ACL) AllowUser(n uint8) {
setLeftBit(a, opOffset+n*bitsPerOp+bitUser)
}
// ForbidUser forbids user the n-th operation in basic ACL.
func (a *ACL) ForbidUser(n uint8) {
resetLeftBit(a, opOffset+n*bitsPerOp+bitUser)
}
// SystemAllowed returns true if System group allowed the n-th operation is set in basic ACL.
func (a ACL) SystemAllowed(n uint8) bool {
if n != OpDelete && n != OpGetRange {
return true
}
return isLeftBitSet(a, opOffset+n*bitsPerOp+bitSystem)
}
// AllowSystem allows System group the n-th operation in basic ACL.
func (a *ACL) AllowSystem(op uint8) {
setLeftBit(a, opOffset+op*bitsPerOp+bitSystem)
}
// ForbidSystem forbids System group the n-th operation in basic ACL.
func (a *ACL) ForbidSystem(op uint8) {
resetLeftBit(a, opOffset+op*bitsPerOp+bitSystem)
}
// OthersAllowed returns true if Others group allowed the n-th operation is set in basic ACL.
func (a ACL) OthersAllowed(op uint8) bool {
return isLeftBitSet(a, opOffset+op*bitsPerOp+bitOthers)
}
// AllowOthers allows Others group the n-th operation in basic ACL.
func (a *ACL) AllowOthers(op uint8) {
setLeftBit(a, opOffset+op*bitsPerOp+bitOthers)
}
// ForbidOthers forbids Others group the n-th operation in basic ACL.
func (a *ACL) ForbidOthers(op uint8) {
resetLeftBit(a, opOffset+op*bitsPerOp+bitOthers)
}
// BearerAllowed returns true if Bearer token usage is allowed for n-th operation in basic ACL.
func (a ACL) BearerAllowed(op uint8) bool {
return isLeftBitSet(a, opOffset+op*bitsPerOp+bitBearer)
}
// AllowBearer allows Bearer token usage for n-th operation in basic ACL.
func (a *ACL) AllowBearer(op uint8) {
setLeftBit(a, opOffset+op*bitsPerOp+bitBearer)
}
// ForbidBearer forbids Bearer token usage for n-th operation in basic ACL.
func (a *ACL) ForbidBearer(op uint8) {
resetLeftBit(a, opOffset+op*bitsPerOp+bitBearer)
}

View file

@ -1,189 +0,0 @@
package basic
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestACLValues(t *testing.T) {
t.Run("private", func(t *testing.T) {
acl := FromUint32(0x1C8C8CCC)
require.False(t, acl.Reserved(0))
require.False(t, acl.Reserved(1))
require.False(t, acl.Sticky())
require.True(t, acl.Final())
require.True(t, acl.UserAllowed(OpGetRangeHash))
require.True(t, acl.SystemAllowed(OpGetRangeHash))
require.False(t, acl.OthersAllowed(OpGetRangeHash))
require.False(t, acl.BearerAllowed(OpGetRangeHash))
require.True(t, acl.UserAllowed(OpGetRange))
require.False(t, acl.SystemAllowed(OpGetRange))
require.False(t, acl.OthersAllowed(OpGetRange))
require.False(t, acl.BearerAllowed(OpGetRange))
require.True(t, acl.UserAllowed(OpSearch))
require.True(t, acl.SystemAllowed(OpSearch))
require.False(t, acl.OthersAllowed(OpSearch))
require.False(t, acl.BearerAllowed(OpSearch))
require.True(t, acl.UserAllowed(OpDelete))
require.False(t, acl.SystemAllowed(OpDelete))
require.False(t, acl.OthersAllowed(OpDelete))
require.False(t, acl.BearerAllowed(OpDelete))
require.True(t, acl.UserAllowed(OpPut))
require.True(t, acl.SystemAllowed(OpPut))
require.False(t, acl.OthersAllowed(OpPut))
require.False(t, acl.BearerAllowed(OpPut))
require.True(t, acl.UserAllowed(OpHead))
require.True(t, acl.SystemAllowed(OpHead))
require.False(t, acl.OthersAllowed(OpHead))
require.False(t, acl.BearerAllowed(OpHead))
require.True(t, acl.UserAllowed(OpGet))
require.True(t, acl.SystemAllowed(OpGet))
require.False(t, acl.OthersAllowed(OpGet))
require.False(t, acl.BearerAllowed(OpGet))
})
t.Run("public with X-bit", func(t *testing.T) {
acl := FromUint32(0x3FFFFFFF)
require.False(t, acl.Reserved(0))
require.False(t, acl.Reserved(1))
require.True(t, acl.Sticky())
require.True(t, acl.Final())
require.True(t, acl.UserAllowed(OpGetRangeHash))
require.True(t, acl.SystemAllowed(OpGetRangeHash))
require.True(t, acl.OthersAllowed(OpGetRangeHash))
require.True(t, acl.BearerAllowed(OpGetRangeHash))
require.True(t, acl.UserAllowed(OpGetRange))
require.True(t, acl.SystemAllowed(OpGetRange))
require.True(t, acl.OthersAllowed(OpGetRange))
require.True(t, acl.BearerAllowed(OpGetRange))
require.True(t, acl.UserAllowed(OpSearch))
require.True(t, acl.SystemAllowed(OpSearch))
require.True(t, acl.OthersAllowed(OpSearch))
require.True(t, acl.BearerAllowed(OpSearch))
require.True(t, acl.UserAllowed(OpDelete))
require.True(t, acl.SystemAllowed(OpDelete))
require.True(t, acl.OthersAllowed(OpDelete))
require.True(t, acl.BearerAllowed(OpDelete))
require.True(t, acl.UserAllowed(OpPut))
require.True(t, acl.SystemAllowed(OpPut))
require.True(t, acl.OthersAllowed(OpPut))
require.True(t, acl.BearerAllowed(OpPut))
require.True(t, acl.UserAllowed(OpHead))
require.True(t, acl.SystemAllowed(OpHead))
require.True(t, acl.OthersAllowed(OpHead))
require.True(t, acl.BearerAllowed(OpHead))
require.True(t, acl.UserAllowed(OpGet))
require.True(t, acl.SystemAllowed(OpGet))
require.True(t, acl.OthersAllowed(OpGet))
require.True(t, acl.BearerAllowed(OpGet))
})
t.Run("read only", func(t *testing.T) {
acl := FromUint32(0x1FFFCCFF)
require.False(t, acl.Reserved(0))
require.False(t, acl.Reserved(1))
require.False(t, acl.Sticky())
require.True(t, acl.Final())
require.True(t, acl.UserAllowed(OpGetRangeHash))
require.True(t, acl.SystemAllowed(OpGetRangeHash))
require.True(t, acl.OthersAllowed(OpGetRangeHash))
require.True(t, acl.BearerAllowed(OpGetRangeHash))
require.True(t, acl.UserAllowed(OpGetRange))
require.True(t, acl.SystemAllowed(OpGetRange))
require.True(t, acl.OthersAllowed(OpGetRange))
require.True(t, acl.BearerAllowed(OpGetRange))
require.True(t, acl.UserAllowed(OpSearch))
require.True(t, acl.SystemAllowed(OpSearch))
require.True(t, acl.OthersAllowed(OpSearch))
require.True(t, acl.BearerAllowed(OpSearch))
require.True(t, acl.UserAllowed(OpDelete))
require.True(t, acl.SystemAllowed(OpDelete))
require.False(t, acl.OthersAllowed(OpDelete))
require.False(t, acl.BearerAllowed(OpDelete))
require.True(t, acl.UserAllowed(OpPut))
require.True(t, acl.SystemAllowed(OpPut))
require.False(t, acl.OthersAllowed(OpPut))
require.False(t, acl.BearerAllowed(OpPut))
require.True(t, acl.UserAllowed(OpHead))
require.True(t, acl.SystemAllowed(OpHead))
require.True(t, acl.OthersAllowed(OpHead))
require.True(t, acl.BearerAllowed(OpHead))
require.True(t, acl.UserAllowed(OpGet))
require.True(t, acl.SystemAllowed(OpGet))
require.True(t, acl.OthersAllowed(OpGet))
require.True(t, acl.BearerAllowed(OpGet))
})
}
func TestACLMethods(t *testing.T) {
acl := new(ACL)
for i := uint8(0); i < reservedBitNumber; i++ {
acl.SetReserved(i)
require.True(t, acl.Reserved(i))
acl.ResetReserved(i)
require.False(t, acl.Reserved(i))
}
acl.SetSticky()
require.True(t, acl.Sticky())
acl.ResetSticky()
require.False(t, acl.Sticky())
acl.SetFinal()
require.True(t, acl.Final())
acl.ResetFinal()
require.False(t, acl.Final())
for i := OpGetRangeHash; i <= OpGet; i++ {
acl.AllowUser(i)
require.True(t, acl.UserAllowed(i))
acl.ForbidUser(i)
require.False(t, acl.UserAllowed(i))
acl.AllowOthers(i)
require.True(t, acl.OthersAllowed(i))
acl.ForbidOthers(i)
require.False(t, acl.OthersAllowed(i))
acl.AllowBearer(i)
require.True(t, acl.BearerAllowed(i))
acl.ForbidBearer(i)
require.False(t, acl.BearerAllowed(i))
acl.AllowSystem(i)
require.True(t, acl.SystemAllowed(i))
acl.ForbidSystem(i)
if i == OpDelete || i == OpGetRange {
require.False(t, acl.SystemAllowed(i))
} else {
require.True(t, acl.SystemAllowed(i))
}
}
}

View file

@ -1,64 +0,0 @@
package basic
import (
"encoding/binary"
"io"
)
// Size is a size of ACL
// in a binary form.
const Size = 4
// FromUint32 converts builtin
// uint32 value to ACL.
//
// Try to avoid direct cast for
// better portability.
func FromUint32(v uint32) ACL {
return ACL(v)
}
// ToUint32 converts ACL value
// to builtin uint32.
//
// Try to avoid direct cast for
// better portability.
func ToUint32(v ACL) uint32 {
return uint32(v)
}
// Equal reports whether e and e2 are the same ACL.
//
// Function defines the relation of equality
// between two ACL. Try to avoid comparison through
// "==" operator for better portability.
func Equal(a, b ACL) bool {
return ToUint32(a) == ToUint32(b)
}
// Marshal encodes ACL into a
// binary form and returns the result.
//
// Result slice has Size length.
func Marshal(a ACL) []byte {
d := make([]byte, Size)
binary.BigEndian.PutUint32(d, ToUint32(a))
return d
}
// UnmarshalBinary unmarshals ACL from
// a binary representation.
//
// If buffer size is insufficient,
// io.ErrUnexpectedEOF is returned.
func (a *ACL) UnmarshalBinary(data []byte) error {
if len(data) < Size {
return io.ErrUnexpectedEOF
}
*a = FromUint32(binary.BigEndian.Uint32(data))
return nil
}

View file

@ -1,36 +0,0 @@
package basic
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestEqual(t *testing.T) {
require.True(t,
Equal(
FromUint32(1),
FromUint32(1),
),
)
require.False(t,
Equal(
FromUint32(1),
FromUint32(2),
),
)
}
func TestMarshal(t *testing.T) {
a := FromUint32(1)
a2 := new(ACL)
require.NoError(t,
a2.UnmarshalBinary(
Marshal(a),
),
)
require.True(t, Equal(a, *a2))
}

View file

@ -1,53 +0,0 @@
package storage
import (
"errors"
eacl "github.com/nspcc-dev/neofs-api-go/acl/extended"
"github.com/nspcc-dev/neofs-node/pkg/core/container"
)
// CID represents the container identifier.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container.ID.
type CID = container.ID
// Table represents extended ACL rule table.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container/eacl/extended.Table.
type Table = eacl.Table
// Storage is the interface that wraps
// basic methods of extended ACL table storage.
type Storage interface {
// GetEACL reads the table from the storage by identifier.
// It returns any error encountered.
//
// GetEACL must return exactly one non-nil value.
// GetEACL must return ErrNotFound if the table is not in storage.
//
// Implementations must not retain or modify the table
// (even temporarily).
GetEACL(CID) (Table, error)
// PutEACL saves the table to the underlying storage.
// It returns any error encountered that caused the saving to interrupt.
//
// PutEACL must return extended.ErrNilTable on nil table.
//
// Implementations must not retain or modify the table (even temporarily).
//
// Table rewriting behavior is dictated by implementation.
PutEACL(CID, Table, []byte) error
}
// ErrNotFound is the error returned when eACL table
// was not found in storage.
var ErrNotFound = errors.New("container not found")
// ErrNilStorage is the error returned by functions that
// expect a non-nil eACL table storage implementation,
// but received nil.
var ErrNilStorage = errors.New("eACL storage is nil")

View file

@ -1,48 +0,0 @@
package test
import (
"sync"
"github.com/nspcc-dev/neofs-node/pkg/core/container"
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended"
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage"
)
type testStorage struct {
*sync.RWMutex
items map[container.ID]storage.Table
}
func (s *testStorage) GetEACL(cid storage.CID) (storage.Table, error) {
s.RLock()
table, ok := s.items[cid]
s.RUnlock()
if !ok {
return nil, storage.ErrNotFound
}
return table, nil
}
func (s *testStorage) PutEACL(cid storage.CID, table storage.Table, _ []byte) error {
if table == nil {
return extended.ErrNilTable
}
s.Lock()
s.items[cid] = table
s.Unlock()
return nil
}
// New creates new eACL table storage
// that stores table in go-builtin map.
func New() storage.Storage {
return &testStorage{
RWMutex: new(sync.RWMutex),
items: make(map[container.ID]storage.Table),
}
}

View file

@ -1,102 +0,0 @@
package extended
import (
"errors"
eacl "github.com/nspcc-dev/neofs-api-go/acl/extended"
"github.com/nspcc-dev/neofs-node/pkg/core/container"
)
// FIXME: do not duplicate constants
// OperationType represents the enumeration
// of different destination types of request.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-api-go/eacl.OperationType.
// FIXME: operation type should be defined in core lib.
type OperationType = eacl.OperationType
// Group represents the enumeration
// of different authorization groups.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-api-go/acl/extended.Group.
// FIXME: target should be defined in core lib.
type Group = eacl.Group
// HeaderType represents the enumeration
// of different types of header.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-api-go/eacl.HeaderType.
// FIXME: header type enum should be defined in core lib.
type HeaderType = eacl.HeaderType
// CID represents the container identifier.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container.ID.
type CID = container.ID
// Header is an interface that wraps
// methods of string key-value header.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-api-go/eacl.Header.
// FIXME: header should be defined in core lib.
type Header = eacl.Header
// Table represents extended ACL rule table.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-api-go/eacl.ExtendedACLTable.
// FIXME: eacl table should be defined in core package.
// type Table = eacl.ExtendedACLTable
// TypedHeaderSource is the interface that wraps
// method for selecting typed headers by type.
type TypedHeaderSource interface {
// HeadersOfType returns the list of key-value headers
// of particular type.
//
// It returns any problem encountered through the boolean
// false value.
HeadersOfType(HeaderType) ([]Header, bool)
}
// RequestInfo is an interface that wraps
// request with authority methods.
type RequestInfo interface {
TypedHeaderSource
// CID returns container identifier from request context.
CID() CID
// Key returns the binary representation of
// author's public key.
//
// Any problem encountered can be reflected
// through an empty slice.
//
// Binary key format is dictated by implementation.
Key() []byte
// OperationType returns the type of request destination.
//
// Any problem encountered can be reflected
// through OpTypeUnknown value. Caller should handle
// OpTypeUnknown value according to its internal logic.
OperationType() OperationType
// Group returns the authority group type.
//
// Any problem encountered can be reflected
// through GroupUnknown value. Caller should handle
// TargetUnknown value according to its internal logic.
Group() Group
}
// ErrNilTable is the error returned by functions that
// expect a non-nil eACL table, but received nil.
var ErrNilTable = errors.New("eACL table is nil")

View file

@ -1,82 +0,0 @@
package container
import (
"errors"
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic"
"github.com/nspcc-dev/netmap"
)
// BasicACL represents the basic
// ACL rules.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container/basic.ACL.
type BasicACL = basic.ACL
// PlacementRule represents placement
// rules of the container.
//
// It is a type alias of
// github.com/nspcc-dev/netmap.PlacementRule.
// FIXME: container placement rules should be defined in core lib.
type PlacementRule = netmap.PlacementRule
// Container represents NeoFS container.
type Container struct {
basicACL BasicACL // basic ACL mask
ownerID OwnerID // the identifier of container's owner
salt []byte // unique container bytes
placementRule PlacementRule // placement rules
}
// ErrNilContainer is the error returned by functions that
// expect a non-nil container pointer, but received nil.
var ErrNilContainer = errors.New("container is nil")
// OwnerID returns an ID of the container's owner.
func (c *Container) OwnerID() OwnerID {
return c.ownerID
}
// SetOwnerID sets the ID of the container's owner.
func (c *Container) SetOwnerID(v OwnerID) {
c.ownerID = v
}
// Salt returns the container salt.
//
// Slice is returned by reference without copying.
func (c *Container) Salt() []byte {
return c.salt
}
// SetSalt sets the container salt.
//
// Slice is assigned by reference without copying.
func (c *Container) SetSalt(v []byte) {
c.salt = v
}
// BasicACL returns the mask of basic container permissions.
func (c *Container) BasicACL() BasicACL {
return c.basicACL
}
// SetBasicACL sets the mask of basic container permissions.
func (c *Container) SetBasicACL(v BasicACL) {
c.basicACL = v
}
// PlacementRule returns placement rule of the container.
func (c *Container) PlacementRule() PlacementRule {
return c.placementRule
}
// SetPlacementRule sets placement rule of the container.
func (c *Container) SetPlacementRule(v PlacementRule) {
c.placementRule = v
}

View file

@ -1,30 +0,0 @@
package container
import (
"testing"
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic"
"github.com/stretchr/testify/require"
)
func TestContainerMethods(t *testing.T) {
c := new(Container)
acl := basic.FromUint32(1)
c.SetBasicACL(acl)
require.True(t, basic.Equal(acl, c.BasicACL()))
ownerID := OwnerID{1, 2, 3}
c.SetOwnerID(ownerID)
require.Equal(t, ownerID, c.OwnerID())
salt := []byte{4, 5, 6}
c.SetSalt(salt)
require.Equal(t, salt, c.Salt())
rule := PlacementRule{
ReplFactor: 1,
}
c.SetPlacementRule(rule)
require.Equal(t, rule, c.PlacementRule())
}

View file

@ -1,49 +0,0 @@
package container
import (
"crypto/sha256"
"github.com/nspcc-dev/neofs-api-go/refs"
)
// ID represents the
// container identifier.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-api-go/refs.CID.
// FIXME: container id should be defined in core package.
type ID = refs.CID
// OwnerID represents the
// container owner identifier.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-api-go/refs.OwnerID.
// FIXME: owner ID should be defined in core lib.
type OwnerID = refs.OwnerID
// OwnerIDSize is a size of OwnerID
// in a binary form.
const OwnerIDSize = refs.OwnerIDSize
// CalculateID calculates container identifier
// as SHA256 checksum of the binary form.
//
// If container is nil, ErrNilContainer is returned.
func CalculateID(cnr *Container) (*ID, error) {
if cnr == nil {
return nil, ErrNilContainer
}
data, err := cnr.MarshalBinary()
if err != nil {
return nil, err
}
res := new(ID)
sh := sha256.Sum256(data)
copy(res[:], sh[:])
return res, nil
}

View file

@ -1,38 +0,0 @@
package container
import (
"crypto/sha256"
"testing"
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
)
func TestCalculateID(t *testing.T) {
_, err := CalculateID(nil)
require.True(t, errors.Is(err, ErrNilContainer))
cnr := new(Container)
cnr.SetBasicACL(basic.FromUint32(1))
cnr.SetOwnerID(OwnerID{1, 2, 3})
cnr.SetSalt([]byte{4, 5, 6})
id1, err := CalculateID(cnr)
require.NoError(t, err)
data, err := cnr.MarshalBinary()
require.NoError(t, err)
sh := sha256.Sum256(data)
require.Equal(t, id1.Bytes(), sh[:])
// change the container
cnr.SetSalt(append(cnr.Salt(), 1))
id2, err := CalculateID(cnr)
require.NoError(t, err)
require.NotEqual(t, id1, id2)
}

View file

@ -1,75 +0,0 @@
package container
import (
"encoding/binary"
"io"
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic"
)
const (
saltLenSize = 2
fixedSize = 0 +
basic.Size +
OwnerIDSize +
saltLenSize
)
// MarshalBinary encodes the container into a binary form
// and returns the result.
func (c *Container) MarshalBinary() ([]byte, error) {
data := make([]byte, binaryContainerSize(c))
off := copy(data, basic.Marshal(c.basicACL))
off += copy(data[off:], c.ownerID.Bytes())
binary.BigEndian.PutUint16(data[off:], uint16(len(c.salt)))
off += saltLenSize
off += copy(data[off:], c.salt)
if _, err := c.placementRule.MarshalTo(data[off:]); err != nil {
return nil, err
}
return data, nil
}
// UnmarshalBinary unmarshals container from a binary
// representation.
//
// If buffer size is insufficient, io.ErrUnexpectedEOF is returned.
func (c *Container) UnmarshalBinary(data []byte) error {
if len(data) < binaryContainerSize(c) {
return io.ErrUnexpectedEOF
}
if err := c.basicACL.UnmarshalBinary(data); err != nil {
return err
}
off := basic.Size
off += copy(c.ownerID[:], data[off:])
saltLen := binary.BigEndian.Uint16(data[off:])
off += saltLenSize
c.salt = make([]byte, saltLen)
off += copy(c.salt, data[off:])
if err := c.placementRule.Unmarshal(data[off:]); err != nil {
return err
}
return nil
}
// returns the length of the container in binary form.
func binaryContainerSize(cnr *Container) int {
return fixedSize +
len(cnr.salt) +
cnr.placementRule.Size()
}

View file

@ -1,26 +0,0 @@
package container
import (
"testing"
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic"
"github.com/stretchr/testify/require"
)
func TestContainerMarshal(t *testing.T) {
srcCnr := new(Container)
srcCnr.SetBasicACL(basic.FromUint32(1))
srcCnr.SetOwnerID(OwnerID{1, 2, 3})
srcCnr.SetSalt([]byte{4, 5, 6})
srcCnr.SetPlacementRule(PlacementRule{
ReplFactor: 3,
})
data, err := srcCnr.MarshalBinary()
require.NoError(t, err)
dstCnr := new(Container)
require.NoError(t, dstCnr.UnmarshalBinary(data))
require.Equal(t, srcCnr, dstCnr)
}

View file

@ -1,79 +0,0 @@
package storage
import (
"errors"
"github.com/nspcc-dev/neofs-node/pkg/core/container"
)
// Container represents the NeoFS container.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container.Container.
type Container = container.Container
// OwnerID represents the container
// owner identifier.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container.OwnerID.
type OwnerID = container.OwnerID
// CID represents the container identifier.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container.ID.
type CID = container.ID
// Storage is an interface that wraps
// basic container storage methods.
type Storage interface {
// Put saves pointed container to the underlying storage.
// It returns calculated container identifier and any error
// encountered that caused the saving to interrupt.
//
// Put must return container.ErrNilContainer on nil-pointer.
//
// Implementations must not modify the container through the pointer (even temporarily).
// Implementations must not retain the container pointer.
//
// Container rewriting behavior is dictated by implementation.
Put(*Container) (*CID, error)
// Get reads the container from the storage by identifier.
// It returns the pointer to requested container and any error encountered.
//
// Get must return exactly one non-nil value.
// Get must return ErrNotFound if the container is not in storage.
//
// Implementations must not retain the container pointer and modify
// the container through it.
Get(CID) (*Container, error)
// Delete removes the container from the storage.
// It returns any error encountered that caused the deletion to interrupt.
//
// Delete must return nil if container was successfully deleted.
//
// Behavior when deleting a nonexistent container is dictated by implementation.
Delete(CID) error
// List returns a list of container identifiers belonging to the specified owner.
// It returns any error encountered that caused the listing to interrupt.
//
// List must return the identifiers of all stored containers if owner pointer is nil.
// List must return the empty list and no error in the absence of containers in storage.
//
// Result slice can be either empty slice or nil, so empty list should be checked
// by comparing with zero length (not nil).
//
// Callers should carefully handle the incomplete list in case of interrupt error.
List(*OwnerID) ([]CID, error)
}
// ErrNotFound is the error returned when container was not found in storage.
var ErrNotFound = errors.New("container not found")
// ErrNilStorage is the error returned by functions that
// expect a non-nil container storage implementation, but received nil.
var ErrNilStorage = errors.New("container storage is nil")

View file

@ -1,127 +0,0 @@
package test
import (
"sync"
"testing"
"github.com/nspcc-dev/neofs-node/pkg/core/container"
"github.com/nspcc-dev/neofs-node/pkg/core/container/storage"
"github.com/stretchr/testify/require"
)
type testStorage struct {
*sync.RWMutex
items map[container.ID]*container.Container
}
func (s *testStorage) Put(cnr *storage.Container) (*storage.CID, error) {
if cnr == nil {
return nil, container.ErrNilContainer
}
cid, err := container.CalculateID(cnr)
if err != nil {
return nil, err
}
s.Lock()
s.items[*cid] = cnr
s.Unlock()
return cid, nil
}
func (s *testStorage) Get(cid storage.CID) (*storage.Container, error) {
s.RLock()
cnr, ok := s.items[cid]
s.RUnlock()
if !ok {
return nil, storage.ErrNotFound
}
return cnr, nil
}
func (s *testStorage) Delete(cid storage.CID) error {
s.Lock()
delete(s.items, cid)
s.Unlock()
return nil
}
func (s *testStorage) List(ownerID *storage.OwnerID) ([]storage.CID, error) {
s.RLock()
defer s.RUnlock()
res := make([]storage.CID, 0)
for cid, cnr := range s.items {
if ownerID == nil || ownerID.Equal(cnr.OwnerID()) {
res = append(res, cid)
}
}
return res, nil
}
// New creates new container storage
// that stores containers in go-builtin map.
func New() storage.Storage {
return &testStorage{
RWMutex: new(sync.RWMutex),
items: make(map[container.ID]*container.Container),
}
}
// Storage conducts testing of container
// storage for interface specification.
//
// Storage must be empty.
func Storage(t *testing.T, s storage.Storage) {
list, err := s.List(nil)
require.NoError(t, err)
require.Empty(t, list)
cnr1 := new(container.Container)
cnr1.SetOwnerID(container.OwnerID{1, 2, 3})
id1, err := s.Put(cnr1)
require.NoError(t, err)
res, err := s.Get(*id1)
require.NoError(t, err)
require.Equal(t, cnr1, res)
cnr2 := new(container.Container)
owner1 := cnr1.OwnerID()
owner1[0]++
cnr2.SetOwnerID(owner1)
id2, err := s.Put(cnr2)
require.NoError(t, err)
res, err = s.Get(*id2)
require.NoError(t, err)
require.Equal(t, cnr2, res)
list, err = s.List(nil)
require.NoError(t, err)
require.Len(t, list, 2)
require.Contains(t, list, *id1)
require.Contains(t, list, *id2)
owner1 = cnr1.OwnerID()
list, err = s.List(&owner1)
require.NoError(t, err)
require.Len(t, list, 1)
require.Equal(t, *id1, list[0])
owner2 := cnr2.OwnerID()
list, err = s.List(&owner2)
require.NoError(t, err)
require.Len(t, list, 1)
require.Equal(t, *id2, list[0])
}

View file

@ -1,11 +0,0 @@
package test
import (
"testing"
)
func TestNewStorage(t *testing.T) {
s := New()
Storage(t, s)
}

0
pkg/core/netmap/.gitkeep Normal file
View file

View file

@ -1,37 +0,0 @@
package epoch
import (
"encoding/binary"
"io"
)
// Size is a size of Epoch
// in a binary form.
const Size = 8
// Marshal encodes Epoch into a
// binary form and returns the result.
//
// Result slice has Size length.
func Marshal(e Epoch) []byte {
d := make([]byte, Size)
binary.BigEndian.PutUint64(d, ToUint64(e))
return d
}
// UnmarshalBinary unmarshals Epoch from
// a binary representation.
//
// If buffer size is insufficient,
// io.ErrUnexpectedEOF is returned.
func (e *Epoch) UnmarshalBinary(data []byte) error {
if len(data) < Size {
return io.ErrUnexpectedEOF
}
*e = FromUint64(binary.BigEndian.Uint64(data))
return nil
}

View file

@ -1,20 +0,0 @@
package epoch
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestEpochMarshal(t *testing.T) {
e := FromUint64(1)
e2 := new(Epoch)
require.NoError(t,
e2.UnmarshalBinary(
Marshal(e),
),
)
require.True(t, EQ(e, *e2))
}

View file

@ -1,12 +0,0 @@
package epoch
// Sum returns the result of
// summing up two Epoch.
//
// Function defines a binary
// operation of summing two Epoch.
// Try to avoid using operator
// "+" for better portability.
func Sum(a, b Epoch) Epoch {
return FromUint64(ToUint64(a) + ToUint64(b))
}

View file

@ -1,28 +0,0 @@
package epoch
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestEpochMath(t *testing.T) {
items := []struct {
mathFn func(Epoch, Epoch) Epoch
a, b, c uint64
}{
{
mathFn: Sum, a: 1, b: 2, c: 3},
}
for _, item := range items {
require.Equal(t,
item.mathFn(
FromUint64(item.a),
FromUint64(item.b),
),
FromUint64(item.c),
)
}
}

View file

@ -1,55 +0,0 @@
package epoch
// EQ reports whether e and e2 are the same Epoch.
//
// Function defines the relation of equality
// between two Epoch. Try to avoid comparison through
// "==" operator for better portability.
func EQ(e1, e2 Epoch) bool {
return ToUint64(e1) == ToUint64(e2)
}
// NE reports whether e1 and e2 are the different Epoch.
//
// Method defines the relation of inequality
// between two Epoch. Try to avoid comparison through
// "!=" operator for better portability.
func NE(e1, e2 Epoch) bool {
return ToUint64(e1) != ToUint64(e2)
}
// LT reports whether e1 is less Epoch than e2.
//
// Method defines the "less than" relation
// between two Epoch. Try to avoid comparison through
// "<" operator for better portability.
func LT(e1, e2 Epoch) bool {
return ToUint64(e1) < ToUint64(e2)
}
// GT reports whether e1 is greater Epoch than e2.
//
// Method defines the "greater than" relation
// between two Epoch. Try to avoid comparison through
// ">" operator for better portability.
func GT(e1, e2 Epoch) bool {
return ToUint64(e1) > ToUint64(e2)
}
// LE reports whether e1 is less or equal Epoch than e2.
//
// Method defines the "less or equal" relation
// between two Epoch. Try to avoid comparison through
// "<=" operator for better portability.
func LE(e1, e2 Epoch) bool {
return ToUint64(e1) <= ToUint64(e2)
}
// GE reports whether e1 is greater or equal Epoch than e2.
//
// Method defines the "greater or equal" relation
// between two Epoch. Try to avoid comparison through
// ">=" operator for better portability.
func GE(e1, e2 Epoch) bool {
return ToUint64(e1) >= ToUint64(e2)
}

View file

@ -1,40 +0,0 @@
package epoch
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestEpochRelations(t *testing.T) {
items := []struct {
relFn func(Epoch, Epoch) bool
base, ok, fail uint64
}{
{relFn: EQ, base: 1, ok: 1, fail: 2},
{relFn: NE, base: 1, ok: 2, fail: 1},
{relFn: LT, base: 1, ok: 2, fail: 0},
{relFn: GT, base: 1, ok: 0, fail: 2},
{relFn: LE, base: 1, ok: 1, fail: 0},
{relFn: LE, base: 1, ok: 2, fail: 0},
{relFn: GE, base: 1, ok: 0, fail: 2},
{relFn: GE, base: 1, ok: 1, fail: 2},
}
for _, item := range items {
require.True(t,
item.relFn(
FromUint64(item.base),
FromUint64(item.ok),
),
)
require.False(t,
item.relFn(
FromUint64(item.base),
FromUint64(item.fail),
),
)
}
}

View file

@ -1,23 +0,0 @@
package epoch
// Epoch represents the
// number of NeoFS epoch.
type Epoch uint64
// FromUint64 converts builtin
// uint64 value to Epoch.
//
// Try to avoid direct cast for
// better portability.
func FromUint64(e uint64) Epoch {
return Epoch(e)
}
// ToUint64 converts Epoch value
// to builtin uint64.
//
// Try to avoid direct cast for
// better portability.
func ToUint64(e Epoch) uint64 {
return uint64(e)
}

View file

@ -1,119 +0,0 @@
package netmap
import (
"bytes"
"sync"
"github.com/nspcc-dev/neofs-node/pkg/core/netmap/node"
"github.com/nspcc-dev/netmap"
)
// Info represent node information.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/netmap/node.Info.
type Info = node.Info
// Bucket represents NeoFS network map as a graph.
//
// If is a type alias of
// github.com/nspcc-dev/netmap.Bucket.
type Bucket = netmap.Bucket
// NetMap represents NeoFS network map
// with concurrent access support.
type NetMap struct {
mtx *sync.RWMutex
root *Bucket
items []Info
}
// New creates and initializes a new NetMap.
//
// Using the NetMap that has been created with new(NetMap)
// expression (or just declaring a NetMap variable) is unsafe
// and can lead to panic.
func New() *NetMap {
return &NetMap{
mtx: new(sync.RWMutex),
root: new(Bucket),
}
}
// Root returns the root bucket of the network map.
//
// Changing the result is unsafe and
// affects the network map.
func (n NetMap) Root() *Bucket {
n.mtx.RLock()
defer n.mtx.RUnlock()
return n.root
}
// SetRoot sets the root bucket of the network map.
//
// Subsequent changing the source bucket
// is unsafe and affects the network map.
func (n *NetMap) SetRoot(v *Bucket) {
n.mtx.Lock()
n.root = v
n.mtx.Unlock()
}
// Nodes returns node list of the network map.
//
// Changing the result is unsafe and
// affects the network map.
func (n NetMap) Nodes() []Info {
n.mtx.RLock()
defer n.mtx.RUnlock()
return n.items
}
// SetNodes sets node list of the network map.
//
// Subsequent changing the source slice
// is unsafe and affects the network map.
func (n *NetMap) SetNodes(v []Info) {
n.mtx.Lock()
n.items = v
n.mtx.Unlock()
}
// AddNode adds node information to the network map
//
// If node with provided information is already presented
// in network map, nothing happens,
func (n *NetMap) AddNode(nodeInfo Info) error {
n.mtx.Lock()
defer n.mtx.Unlock()
num := -1
// looking for existed node info item
for i := range n.items {
if bytes.Equal(
n.items[i].PublicKey(),
nodeInfo.PublicKey(),
) {
num = i
break
}
}
// add node if it does not exist
if num < 0 {
n.items = append(n.items, nodeInfo)
num = len(n.items) - 1
}
return n.root.AddStrawNode(netmap.Node{
N: uint32(num),
C: n.items[num].Capacity(),
P: n.items[num].Price(),
}, nodeInfo.Options()...)
}

View file

@ -1,39 +0,0 @@
package netmap
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestNetMap_Nodes(t *testing.T) {
nm := New()
info1 := Info{}
info1.SetPublicKey([]byte{1, 2, 3})
info2 := Info{}
info2.SetPublicKey([]byte{4, 5, 6})
nodes := []Info{
info1,
info2,
}
nm.SetNodes(nodes)
require.Equal(t, nodes, nm.Nodes())
}
func TestNetMap_Root(t *testing.T) {
nm := New()
bucket := &Bucket{
Key: "key",
Value: "value",
}
nm.SetRoot(bucket)
require.Equal(t, bucket, nm.Root())
}

View file

@ -1,154 +0,0 @@
package node
import (
"errors"
)
// Info represents the information
// about NeoFS storage node.
type Info struct {
address string // net address
key []byte // public key
opts []string // options
status Status // status bits
}
// ErrNilInfo is returned by functions that expect
// a non-nil Info pointer, but received nil.
var ErrNilInfo = errors.New("node info is nil")
// Address returns node network address.
//
// Address format is dictated by
// application architecture.
func (i Info) Address() string {
return i.address
}
// SetAddress sets node network address.
func (i *Info) SetAddress(v string) {
i.address = v
}
// Status returns the node status.
func (i Info) Status() Status {
return i.status
}
// SetStatus sets the node status.
func (i *Info) SetStatus(v Status) {
i.status = v
}
// PublicKey returns node public key in
// a binary format.
//
// Changing the result is unsafe and
// affects the node info. In order to
// prevent state mutations, use
// CopyPublicKey.
//
// Key format is dictated by
// application architecture.
func (i Info) PublicKey() []byte {
return i.key
}
// CopyPublicKey returns the copy of
// node public key.
//
// Changing the result is safe and
// does not affect the node info.
func CopyPublicKey(i Info) []byte {
res := make([]byte, len(i.key))
copy(res, i.key)
return res
}
// SetPublicKey sets node public key
// in a binary format.
//
// Subsequent changing the source slice
// is unsafe and affects node info.
// In order to prevent state mutations,
// use SetPublicKeyCopy.
func (i *Info) SetPublicKey(v []byte) {
i.key = v
}
// SetPublicKeyCopy copies public key and
// sets the copy as node public key.
//
// Subsequent changing the source slice
// is safe and does not affect node info.
//
// Returns ErrNilInfo on nil node info.
func SetPublicKeyCopy(i *Info, key []byte) error {
if i == nil {
return ErrNilInfo
}
i.key = make([]byte, len(key))
copy(i.key, key)
return nil
}
// Options returns node option list.
//
// Changing the result is unsafe and
// affects the node info. In order to
// prevent state mutations, use
// CopyOptions.
//
// Option format is dictated by
// application architecture.
func (i Info) Options() []string {
return i.opts
}
// CopyOptions returns the copy of
// node options list.
//
// Changing the result is safe and
// does not affect the node info.
func CopyOptions(i Info) []string {
res := make([]string, len(i.opts))
copy(res, i.opts)
return res
}
// SetOptions sets node option list.
//
// Subsequent changing the source slice
// is unsafe and affects node info.
// In order to prevent state mutations,
// use SetOptionsCopy.
func (i *Info) SetOptions(v []string) {
i.opts = v
}
// SetOptionsCopy copies option list and sets
// the copy as node options list.
//
// Subsequent changing the source slice
// is safe and does not affect node info.
//
// SetOptionsCopy does nothing if Info is nil.
func SetOptionsCopy(i *Info, opts []string) {
if i == nil {
return
}
i.opts = make([]string, len(opts))
copy(i.opts, opts)
}

View file

@ -1,133 +0,0 @@
package node
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestInfo_Address(t *testing.T) {
i := new(Info)
addr := "address"
i.SetAddress(addr)
require.Equal(t, addr, i.Address())
}
func TestInfo_Status(t *testing.T) {
i := new(Info)
st := StatusFromUint64(1)
i.SetStatus(st)
require.Equal(t, st, i.Status())
}
func TestInfo_PublicKey(t *testing.T) {
i := new(Info)
key := []byte{1, 2, 3}
i.SetPublicKey(key)
require.Equal(t, key, i.PublicKey())
}
func TestCopyPublicKey(t *testing.T) {
i := Info{}
// set initial node key
initKey := []byte{1, 2, 3}
i.SetPublicKey(initKey)
// get node key copy
keyCopy := CopyPublicKey(i)
// change the copy
keyCopy[0]++
// check that node key has not changed
require.Equal(t, initKey, i.PublicKey())
}
func TestSetPublicKeyCopy(t *testing.T) {
require.EqualError(t,
SetPublicKeyCopy(nil, nil),
ErrNilInfo.Error(),
)
i := new(Info)
// create source key
srcKey := []byte{1, 2, 3}
// copy and set node key
require.NoError(t, SetPublicKeyCopy(i, srcKey))
// get node key
nodeKey := i.PublicKey()
// change the source key
srcKey[0]++
// check that node key has not changed
require.Equal(t, nodeKey, i.PublicKey())
}
func TestInfo_Options(t *testing.T) {
i := new(Info)
opts := []string{
"opt1",
"opt2",
}
i.SetOptions(opts)
require.Equal(t, opts, i.Options())
}
func TestCopyOptions(t *testing.T) {
i := Info{}
// set initial node options
initOpts := []string{
"opt1",
"opt2",
}
i.SetOptions(initOpts)
// get node options copy
optsCopy := CopyOptions(i)
// change the copy
optsCopy[0] = "some other opt"
// check that node options have not changed
require.Equal(t, initOpts, i.Options())
}
func TestSetOptionsCopy(t *testing.T) {
require.NotPanics(t, func() {
SetOptionsCopy(nil, nil)
})
i := new(Info)
// create source options
srcOpts := []string{
"opt1",
"opt2",
}
// copy and set node options
SetOptionsCopy(i, srcOpts)
// get node options
nodeOpts := i.Options()
// change the source options
srcOpts[0] = "some other opt"
// check that node options have not changed
require.Equal(t, nodeOpts, i.Options())
}

View file

@ -1,46 +0,0 @@
package node
import (
"strconv"
"strings"
"github.com/nspcc-dev/neofs-api-go/object"
)
const optionPrice = "/Price:"
const optionCapacity = "/Capacity:"
// Price parses node options and returns the price in 1e-8*GAS/Megabyte per month.
//
// User sets the price in GAS/Terabyte per month.
func (i Info) Price() uint64 {
for j := range i.opts {
if strings.HasPrefix(i.opts[j], optionPrice) {
n, err := strconv.ParseFloat(i.opts[j][len(optionPrice):], 64)
if err != nil {
return 0
}
return uint64(n*1e8) / uint64(object.UnitsMB) // UnitsMB == megabytes in 1 terabyte
}
}
return 0
}
// Capacity parses node options and returns the capacity .
func (i Info) Capacity() uint64 {
for j := range i.opts {
if strings.HasPrefix(i.opts[j], optionCapacity) {
n, err := strconv.ParseUint(i.opts[j][len(optionCapacity):], 10, 64)
if err != nil {
return 0
}
return n
}
}
return 0
}

View file

@ -1,24 +0,0 @@
package node
import (
"testing"
"github.com/nspcc-dev/neofs-api-go/object"
"github.com/stretchr/testify/require"
)
func TestInfo_Price(t *testing.T) {
var info Info
// too small value
info.opts = []string{"/Price:0.01048575"}
require.Equal(t, uint64(0), info.Price())
// min value
info.opts = []string{"/Price:0.01048576"}
require.Equal(t, uint64(1), info.Price())
// big value
info.opts = []string{"/Price:1000000000.666"}
require.Equal(t, uint64(1000000000.666*1e8/object.UnitsMB), info.Price())
}

View file

@ -1,63 +0,0 @@
package node
// Status represents a node
// status bits.
type Status uint64
const leftBitPos = 64
const (
bitFullStorage = 1
)
// returns true if n-th left bit is set (starting at 0).
func isLeftBitSet(value Status, n uint8) bool {
bitMask := Status(1 << (leftBitPos - n))
return bitMask != 0 && value&bitMask == bitMask
}
// sets n-th left bit (starting at 0).
func setLeftBit(value *Status, n uint8) {
*value |= Status(1 << (leftBitPos - n))
}
// resets n-th left bit (starting at 0).
func resetLeftBit(value *Status, n uint8) {
*value &= ^Status(1 << (leftBitPos - n))
}
// Full returns true if node is in Full status.
//
// Full status marks node has enough space
// for storing users objects.
func (n Status) Full() bool {
return isLeftBitSet(n, bitFullStorage)
}
// SetFull sets Full status of node.
func (n *Status) SetFull() {
setLeftBit(n, bitFullStorage)
}
// ResetFull resets Full status of node.
func (n *Status) ResetFull() {
resetLeftBit(n, bitFullStorage)
}
// StatusFromUint64 converts builtin
// uint64 value to Status.
//
// Try to avoid direct cast for
// better portability.
func StatusFromUint64(v uint64) Status {
return Status(v)
}
// StatusToUint64 converts Status value
// to builtin uint64.
//
// Try to avoid direct cast for
// better portability.
func StatusToUint64(s Status) uint64 {
return uint64(s)
}

View file

@ -1,17 +0,0 @@
package node
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestStatus_Full(t *testing.T) {
st := new(Status)
st.SetFull()
require.True(t, st.Full())
st.ResetFull()
require.False(t, st.Full())
}

0
pkg/core/object/.gitkeep Normal file
View file

View file

@ -1,94 +0,0 @@
package object
// ExtendedHeaderType represents the enumeration
// of extended header types of the NeoFS object.
type ExtendedHeaderType uint32
// ExtendedHeader represents the extended
// header of NeoFS object.
type ExtendedHeader struct {
typ ExtendedHeaderType
val interface{}
}
// Type returns the extended header type.
func (h ExtendedHeader) Type() ExtendedHeaderType {
return h.typ
}
// SetType sets the extended header type.
func (h *ExtendedHeader) SetType(v ExtendedHeaderType) {
h.typ = v
}
// Value returns the extended header value.
//
// In the case of a reference type, the value is
// returned by reference, so value mutations affect
// header state. Therefore, callers must first copy
// the value before changing manipulations.
func (h ExtendedHeader) Value() interface{} {
return h.val
}
// SetValue sets the extended header value.
//
// Caller must take into account that each type of
// header usually has a limited set of expected
// value types.
//
// In the case of a reference type, the value is set
// by reference, so source value mutations affect
// header state. Therefore, callers must first copy
// the source value before changing manipulations.
func (h *ExtendedHeader) SetValue(v interface{}) {
h.val = v
}
// TypeFromUint32 converts builtin
// uint32 value to Epoch.
//
// Try to avoid direct cast for
// better portability.
func TypeFromUint32(v uint32) ExtendedHeaderType {
return ExtendedHeaderType(v)
}
// TypeToUint32 converts Epoch value
// to builtin uint32.
//
// Try to avoid direct cast for
// better portability.
func TypeToUint32(v ExtendedHeaderType) uint32 {
return uint32(v)
}
// TypesEQ reports whether t1 and t2 are the same ExtendedHeaderType.
//
// Function defines the relation of equality
// between two ExtendedHeaderType. Try to avoid comparison through
// "==" operator for better portability.
func TypesEQ(t1, t2 ExtendedHeaderType) bool {
return TypeToUint32(t1) == TypeToUint32(t2)
}
// TypesLT reports whether t1 ExtendedHeaderType
// is less than t2.
//
// Function defines the "less than" relation
// between two ExtendedHeaderType. Try to avoid
// comparison through "<" operator for better portability.
func TypesLT(t1, t2 ExtendedHeaderType) bool {
return TypeToUint32(t1) < TypeToUint32(t2)
}
// TypesGT reports whether t1 ExtendedHeaderType
// is greater than t2.
//
// Function defines the "greater than" relation
// between two ExtendedHeaderType. Try to avoid
// comparison through ">" operator for better portability.
func TypesGT(t1, t2 ExtendedHeaderType) bool {
return TypeToUint32(t1) > TypeToUint32(t2)
}

View file

@ -1,25 +0,0 @@
package object
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestExtendedHeader_Type(t *testing.T) {
h := new(ExtendedHeader)
ht := TypeFromUint32(3)
h.SetType(ht)
require.True(t, TypesEQ(ht, h.Type()))
}
func TestExtendedHeader_Value(t *testing.T) {
h := new(ExtendedHeader)
val := 100
h.SetValue(val)
require.Equal(t, val, h.Value())
}

View file

@ -1,73 +0,0 @@
package object
import (
"errors"
)
// Header represents NeoFS object header.
type Header struct {
// SystemHeader is an obligatory part of any object header.
// It is used to set the identity and basic parameters of
// the object.
//
// Header must inherit all the methods of SystemHeader,
// so the SystemHeader is embedded in Header.
SystemHeader
extendedHeaders []ExtendedHeader // extended headers
}
// ErrNilHeader is returned by functions that expect
// a non-nil Header pointer, but received nil.
var ErrNilHeader = errors.New("object header is nil")
// ExtendedHeaders returns the extended headers of header.
//
// Changing the result is unsafe and affects the header.
// In order to prevent state mutations, use CopyExtendedHeaders.
func (h *Header) ExtendedHeaders() []ExtendedHeader {
return h.extendedHeaders
}
// CopyExtendedHeaders returns the copy of extended headers.
//
// Changing the result is safe and does not affect the header.
//
// Returns nil if header is nil.
func CopyExtendedHeaders(h *Header) []ExtendedHeader {
if h == nil {
return nil
}
res := make([]ExtendedHeader, len(h.extendedHeaders))
copy(res, h.extendedHeaders)
return res
}
// SetExtendedHeaders sets the extended headers of the header.
//
// Subsequent changing the source slice is unsafe and affects
// the header. In order to prevent state mutations, use
// SetExtendedHeadersCopy.
func (h *Header) SetExtendedHeaders(v []ExtendedHeader) {
h.extendedHeaders = v
}
// SetExtendedHeadersCopy copies extended headers and sets the copy
// as the object extended headers.
//
// Subsequent changing the source slice is safe and does not affect
// the header.
//
// SetExtendedHeadersCopy does nothing if Header is nil.
func SetExtendedHeadersCopy(h *Header, hs []ExtendedHeader) {
if h == nil {
return
}
h.extendedHeaders = make([]ExtendedHeader, len(hs))
copy(h.extendedHeaders, hs)
}

View file

@ -1,98 +0,0 @@
package object
import (
"testing"
"github.com/stretchr/testify/require"
)
func testHeaders(num uint32) []ExtendedHeader {
res := make([]ExtendedHeader, num)
for i := uint32(0); i < num; i++ {
res[i].SetType(TypeFromUint32(i))
res[i].SetValue(i)
}
return res
}
func TestObject_ExtendedHeaders(t *testing.T) {
h := new(Header)
hs := testHeaders(2)
h.SetExtendedHeaders(hs)
require.Equal(t, hs, h.ExtendedHeaders())
}
func TestCopyExtendedHeaders(t *testing.T) {
require.Nil(t, CopyExtendedHeaders(nil))
h := new(Header)
// set initial headers
initHs := testHeaders(2)
h.SetExtendedHeaders(initHs)
// get extended headers copy
hsCopy := CopyExtendedHeaders(h)
// change the copy
hsCopy[0] = hsCopy[1]
// check that extended headers have not changed
require.Equal(t, initHs, h.ExtendedHeaders())
}
func TestSetExtendedHeadersCopy(t *testing.T) {
require.NotPanics(t, func() {
SetExtendedHeadersCopy(nil, nil)
})
h := new(Header)
// create source headers
srcHs := testHeaders(2)
// copy and set headers
SetExtendedHeadersCopy(h, srcHs)
// get extended headers
objHs := h.ExtendedHeaders()
// change the source headers
srcHs[0] = srcHs[1]
// check that headeres have not changed
require.Equal(t, objHs, h.ExtendedHeaders())
}
func TestHeaderRelations(t *testing.T) {
items := []struct {
relFn func(ExtendedHeaderType, ExtendedHeaderType) bool
base, ok, fail uint32
}{
{relFn: TypesEQ, base: 1, ok: 1, fail: 2},
{relFn: TypesLT, base: 1, ok: 2, fail: 0},
{relFn: TypesGT, base: 1, ok: 0, fail: 2},
}
for _, item := range items {
require.True(t,
item.relFn(
TypeFromUint32(item.base),
TypeFromUint32(item.ok),
),
)
require.False(t,
item.relFn(
TypeFromUint32(item.base),
TypeFromUint32(item.fail),
),
)
}
}

View file

@ -1,63 +0,0 @@
package headers
import (
"github.com/nspcc-dev/neofs-node/pkg/core/object"
)
// Header represents object extended header.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/object.ExtendedHeader.
type Header = object.ExtendedHeader
// Type represents extended header type.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/object.ExtendedHeaderType.
type Type = object.ExtendedHeaderType
const (
// this is the only place where this cast is appropriate,
// use object.TypeFromUint32 instead.
lowerUndefined = Type(iota) // lower unsupported Type value
// TypeLink is the type of object reference header.
TypeLink
// TypeUser is the of user key-value string header.
TypeUser
// TypeTransform is the type of transformation mark header.
TypeTransform
// TypeTombstone is the type of tombstone mark header.
TypeTombstone
// TypeSessionToken is the type of session token header.
TypeSessionToken
// TypeHomomorphicHash is the type of homomorphic hash header.
TypeHomomorphicHash
// TypePayloadChecksum is the type of payload checksum header.
TypePayloadChecksum
// TypeIntegrity is the type of integrity header.
TypeIntegrity
// TypeStorageGroup is the type of storage group header.
TypeStorageGroup
// TypePublicKey is the type of public key header.
TypePublicKey
upperUndefined // upper unsupported Type value
)
// SupportedType returns true if Type is
// the known type of extended header. Each
// supported type has named constant.
func SupportedType(t Type) bool {
return object.TypesGT(t, lowerUndefined) &&
object.TypesLT(t, upperUndefined)
}

View file

@ -1,34 +0,0 @@
package headers
import (
"testing"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/stretchr/testify/require"
)
func TestSupportedType(t *testing.T) {
for _, typ := range []Type{
TypeLink,
TypeUser,
TypeTransform,
TypeTombstone,
TypeSessionToken,
TypeHomomorphicHash,
TypePayloadChecksum,
TypeIntegrity,
TypeStorageGroup,
TypePublicKey,
} {
require.True(t, SupportedType(typ))
}
for _, typ := range []Type{
lowerUndefined,
upperUndefined,
object.TypeFromUint32(object.TypeToUint32(lowerUndefined) - 1),
object.TypeFromUint32(object.TypeToUint32(upperUndefined) + 1),
} {
require.False(t, SupportedType(typ))
}
}

View file

@ -1,45 +0,0 @@
package headers
// UserHeader is a value of object extended header
// that carries user string key-value pairs.
//
// All user headers must be type of TypeUser.
// All user header must have UserHeader pointer value.
type UserHeader struct {
key, val string
}
// NewUserHeader creates, initialized and returns
// the user extended header.
func NewUserHeader(key, val string) *Header {
res := new(Header)
res.SetType(TypeUser)
res.SetValue(&UserHeader{
key: key,
val: val,
})
return res
}
// Key returns the user header key.
func (u UserHeader) Key() string {
return u.key
}
// SetKey sets the user header key.
func (u *UserHeader) SetKey(key string) {
u.key = key
}
// Value returns the user header value.
func (u UserHeader) Value() string {
return u.val
}
// SetValue sets the user header value.
func (u *UserHeader) SetValue(val string) {
u.val = val
}

View file

@ -1,45 +0,0 @@
package headers
import (
"testing"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/stretchr/testify/require"
)
func TestUserHeader_Key(t *testing.T) {
h := new(UserHeader)
key := "random key"
h.SetKey(key)
require.Equal(t, key, h.Key())
}
func TestUserHeader_Value(t *testing.T) {
h := new(UserHeader)
val := "random value"
h.SetValue(val)
require.Equal(t, val, h.Value())
}
func TestNewUserHeader(t *testing.T) {
key := "user key"
val := "user val"
h := NewUserHeader(key, val)
require.True(t,
object.TypesEQ(
TypeUser,
h.Type(),
),
)
uh := h.Value().(*UserHeader)
require.Equal(t, key, uh.Key())
require.Equal(t, val, uh.Value())
}

View file

@ -1,61 +0,0 @@
package object
import (
"github.com/nspcc-dev/neofs-api-go/refs"
)
// ID represents the object identifier.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-api-go/refs.ObjectID.
// FIXME: object ID should be defined in core package.
type ID = refs.ObjectID
// Address represents NeoFS Object address.
// Acts as a reference to the object.
type Address struct {
cid CID
id ID
}
// CID return the identifier of the container
// that the object belongs to.
func (a Address) CID() CID {
return a.cid
}
// SetCID sets the identifier of the container
// that the object belongs to.
func (a *Address) SetCID(v CID) {
a.cid = v
}
// ID returns the unique identifier of the
// object in container.
func (a Address) ID() ID {
return a.id
}
// SetID sets the unique identifier of the
// object in container.
func (a *Address) SetID(v ID) {
a.id = v
}
// AddressFromObject returns an address based
// on the object's header.
//
// Returns nil on nil object.
func AddressFromObject(o *Object) *Address {
if o == nil {
return nil
}
a := new(Address)
a.SetCID(o.CID())
a.SetID(o.ID())
return a
}

View file

@ -1,42 +0,0 @@
package object
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestAddress_CID(t *testing.T) {
a := new(Address)
cid := CID{1, 2, 3}
a.SetCID(cid)
require.Equal(t, cid, a.CID())
}
func TestAddress_ID(t *testing.T) {
a := new(Address)
id := ID{1, 2, 3}
a.SetID(id)
require.Equal(t, id, a.ID())
}
func TestAddressFromObject(t *testing.T) {
require.Nil(t, AddressFromObject(nil))
o := new(Object)
cid := CID{4, 5, 6}
o.SetCID(cid)
id := ID{1, 2, 3}
o.SetID(id)
a := AddressFromObject(o)
require.Equal(t, cid, a.CID())
require.Equal(t, id, a.ID())
}

View file

@ -1,76 +0,0 @@
package object
import (
"errors"
)
// Object represents NeoFS Object.
type Object struct {
// Header is an obligatory part of any object.
// It is used to carry any additional information
// besides payload.
//
// Object must inherit all the methods of Header,
// so the Header is embedded in Object.
Header
payload []byte // payload bytes
}
// ErrNilObject is returned by functions that expect
// a non-nil Object pointer, but received nil.
var ErrNilObject = errors.New("object is nil")
// Payload returns payload bytes of the object.
//
// Changing the result is unsafe and affects
// the object. In order to prevent state
// mutations, use CopyPayload.
func (o *Object) Payload() []byte {
return o.payload
}
// CopyPayload returns the copy of
// object payload.
//
// Changing the result is safe and
// does not affect the object.
//
// CopyPayload returns nil if object is nil.
func CopyPayload(o *Object) []byte {
if o == nil {
return nil
}
res := make([]byte, len(o.payload))
copy(res, o.payload)
return res
}
// SetPayload sets objecyt payload bytes.
//
// Subsequent changing the source slice
// is unsafe and affects the object.
// In order to prevent state mutations,
// use SetPayloadCopy.
func (o *Object) SetPayload(v []byte) {
o.payload = v
}
// SetPayloadCopy copies slice bytes and sets
// the copy as object payload.
//
// Subsequent changing the source slice
// is safe and does not affect the object.
//
// SetPayloadCopy does nothing if object is nil.
func SetPayloadCopy(o *Object, payload []byte) {
if o == nil {
return
}
o.payload = make([]byte, len(payload))
copy(o.payload, payload)
}

View file

@ -1,58 +0,0 @@
package object
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestObject_Payload(t *testing.T) {
o := new(Object)
payload := []byte{1, 2, 3}
o.SetPayload(payload)
require.Equal(t, payload, o.Payload())
}
func TestCopyPayload(t *testing.T) {
require.Nil(t, CopyPayload(nil))
o := new(Object)
// set initial node key
initPayload := []byte{1, 2, 3}
o.SetPayload(initPayload)
// get payload copy
pCopy := CopyPayload(o)
// change the copy
pCopy[0]++
// check that payload has not changed
require.Equal(t, initPayload, o.Payload())
}
func TestSetPayloadCopy(t *testing.T) {
require.NotPanics(t, func() {
SetExtendedHeadersCopy(nil, nil)
})
o := new(Object)
// create source payload
srcPayload := []byte{1, 2, 3}
// copy and set payload
SetPayloadCopy(o, srcPayload)
// get payload
objPayload := o.Payload()
// change the source payload
srcPayload[0]++
// check that payload has not changed
require.Equal(t, objPayload, o.Payload())
}

View file

@ -1,61 +0,0 @@
package storage
import (
"errors"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
)
// Object represents the NeoFS Object.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/object.Object.
type Object = object.Object
// Address represents the address of
// NeoFS Object.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/object.Address.
type Address = object.Address
// Storage is an interface that wraps
// basic object storage methods.
type Storage interface {
// Put saves pointed object to the underlying storage.
// It returns object address for reference and any error
// encountered that caused the saving to interrupt.
//
// Put must return object.ErrNilObject on nil-pointer.
//
// Implementations must not modify the object through the pointer (even temporarily).
// Implementations must not retain the object pointer.
//
// Object rewriting behavior is dictated by implementation.
Put(*Object) (*Address, error)
// Get reads the object from the storage by address.
// It returns the pointer to requested object and any error encountered.
//
// Get must return exactly one non-nil value.
// Get must return ErrNotFound if the object is not in storage.
//
// Implementations must not retain the object pointer and modify
// the object through it.
Get(Address) (*Object, error)
// Delete removes the object from the storage.
// It returns any error encountered that caused the deletion to interrupt.
//
// Delete must return nil if object was successfully deleted.
//
// Behavior when deleting a nonexistent object is dictated by implementation.
Delete(Address) error
}
// ErrNotFound is the error returned when object was not found in storage.
var ErrNotFound = errors.New("object not found")
// ErrNilStorage is the error returned by functions that
// expect a non-nil object storage implementation, but received nil.
var ErrNilStorage = errors.New("object storage is nil")

View file

@ -1,88 +0,0 @@
package test
import (
"sync"
"testing"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/core/object/storage"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
)
type testStorage struct {
*sync.RWMutex
items map[storage.Address]*storage.Object
}
func (s *testStorage) Put(o *storage.Object) (*storage.Address, error) {
if o == nil {
return nil, object.ErrNilObject
}
a := object.AddressFromObject(o)
s.Lock()
s.items[*a] = o
s.Unlock()
return a, nil
}
func (s *testStorage) Get(a storage.Address) (*storage.Object, error) {
s.RLock()
o, ok := s.items[a]
s.RUnlock()
if !ok {
return nil, storage.ErrNotFound
}
return o, nil
}
func (s *testStorage) Delete(a storage.Address) error {
s.Lock()
delete(s.items, a)
s.Unlock()
return nil
}
// New creates new container storage
// that stores containers in go-builtin map.
func New() storage.Storage {
return &testStorage{
RWMutex: new(sync.RWMutex),
items: make(map[storage.Address]*storage.Object),
}
}
// Storage conducts testing of object
// storage for interface specification.
//
// Storage must be empty.
func Storage(t *testing.T, s storage.Storage) {
_, err := s.Put(nil)
require.True(t, errors.Is(err, object.ErrNilObject))
a := new(object.Address)
_, err = s.Get(*a)
require.True(t, errors.Is(err, storage.ErrNotFound))
o := new(object.Object)
o.SetID(object.ID{1, 2, 3})
a, err = s.Put(o)
require.NoError(t, err)
o2, err := s.Get(*a)
require.NoError(t, err)
require.Equal(t, o, o2)
require.NoError(t, s.Delete(*a))
_, err = s.Get(*a)
require.True(t, errors.Is(err, storage.ErrNotFound))
}

View file

@ -1,11 +0,0 @@
package test
import (
"testing"
)
func TestNewStorage(t *testing.T) {
s := New()
Storage(t, s)
}

View file

@ -1,107 +0,0 @@
package object
import (
"github.com/nspcc-dev/neofs-node/pkg/core/container"
"github.com/nspcc-dev/neofs-node/pkg/core/netmap/epoch"
)
// CID represents the container identifier.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container.ID.
type CID = container.ID
// OwnerID represents the container
// owner identifier.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container.OwnerID.
type OwnerID = container.OwnerID
// Epoch represents the NeoFS epoch number.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/netmap/epoch.Epoch.
type Epoch = epoch.Epoch
// SystemHeader represents the
// system header of NeoFS Object.
type SystemHeader struct {
version uint64 // object version
payloadLen uint64 // length of the payload bytes
id ID // object ID
cid CID // container ID
ownerID OwnerID // object owner ID
creatEpoch Epoch // creation epoch number
}
// Version returns the object version number.
func (s *SystemHeader) Version() uint64 {
return s.version
}
// SetVersion sets the object version number.
func (s *SystemHeader) SetVersion(v uint64) {
s.version = v
}
// PayloadLength returns the length of the
// object payload bytes.
func (s *SystemHeader) PayloadLength() uint64 {
return s.payloadLen
}
// SetPayloadLength sets the length of the object
// payload bytes.
func (s *SystemHeader) SetPayloadLength(v uint64) {
s.payloadLen = v
}
// ID returns the object identifier.
func (s *SystemHeader) ID() ID {
return s.id
}
// SetID sets the object identifier.
func (s *SystemHeader) SetID(v ID) {
s.id = v
}
// CID returns the container identifier
// to which the object belongs.
func (s *SystemHeader) CID() CID {
return s.cid
}
// SetCID sets the container identifier
// to which the object belongs.
func (s *SystemHeader) SetCID(v CID) {
s.cid = v
}
// OwnerID returns the object owner identifier.
func (s *SystemHeader) OwnerID() OwnerID {
return s.ownerID
}
// SetOwnerID sets the object owner identifier.
func (s *SystemHeader) SetOwnerID(v OwnerID) {
s.ownerID = v
}
// CreationEpoch returns the epoch number
// in which the object was created.
func (s *SystemHeader) CreationEpoch() Epoch {
return s.creatEpoch
}
// SetCreationEpoch sets the epoch number
// in which the object was created.
func (s *SystemHeader) SetCreationEpoch(v Epoch) {
s.creatEpoch = v
}

View file

@ -1,62 +0,0 @@
package object
import (
"testing"
"github.com/nspcc-dev/neofs-node/pkg/core/netmap/epoch"
"github.com/stretchr/testify/require"
)
func TestSystemHeader_Version(t *testing.T) {
h := new(SystemHeader)
v := uint64(7)
h.SetVersion(v)
require.Equal(t, v, h.Version())
}
func TestSystemHeader_PayloadLength(t *testing.T) {
h := new(SystemHeader)
ln := uint64(3)
h.SetPayloadLength(ln)
require.Equal(t, ln, h.PayloadLength())
}
func TestSystemHeader_ID(t *testing.T) {
h := new(SystemHeader)
id := ID{1, 2, 3}
h.SetID(id)
require.Equal(t, id, h.ID())
}
func TestSystemHeader_CID(t *testing.T) {
h := new(SystemHeader)
cid := CID{1, 2, 3}
h.SetCID(cid)
require.Equal(t, cid, h.CID())
}
func TestSystemHeader_OwnerID(t *testing.T) {
h := new(SystemHeader)
ownerID := OwnerID{1, 2, 3}
h.SetOwnerID(ownerID)
require.Equal(t, ownerID, h.OwnerID())
}
func TestSystemHeader_CreationEpoch(t *testing.T) {
h := new(SystemHeader)
ep := epoch.FromUint64(1)
h.SetCreationEpoch(ep)
require.True(t, epoch.EQ(ep, h.CreationEpoch()))
}

View file

@ -1,35 +1,11 @@
package wrapper
import (
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neofs-node/pkg/core/container"
"github.com/nspcc-dev/neofs-node/pkg/morph/client/balance"
"github.com/pkg/errors"
)
// OwnerID represents the container owner identifier.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container.OwnerID.
type OwnerID = container.OwnerID
// FIXME: correct the definition.
type OwnerID struct{}
// BalanceOf receives the amount of funds in the client's account
// through the Balance contract call, and returns it.
func (w *Wrapper) BalanceOf(ownerID OwnerID) (int64, error) {
// convert Neo wallet address to Uint160
u160, err := address.StringToUint160(ownerID.String())
if err != nil {
return 0, errors.Wrap(err, "could not convert wallet address to Uint160")
}
// prepare invocation arguments
args := balance.GetBalanceOfArgs{}
args.SetWallet(u160.BytesBE())
values, err := w.client.BalanceOf(args)
if err != nil {
return 0, errors.Wrap(err, "could not invoke smart contract")
}
return values.Amount(), nil
panic("implement me")
}

View file

@ -1,24 +1,12 @@
package wrapper
import (
"github.com/nspcc-dev/neofs-api-go/refs"
"github.com/nspcc-dev/neofs-node/pkg/core/container"
"github.com/nspcc-dev/neofs-node/pkg/core/container/storage"
contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/container"
"github.com/pkg/errors"
)
// OwnerID represents the container owner identifier.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container/storage.OwnerID.
type OwnerID = storage.OwnerID
// FIXME: correct the definition.
type OwnerID struct{}
// Container represents the NeoFS Container structure.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container/storage.Container.
type Container = storage.Container
// FIXME: correct the definition.
type Container struct{}
// Put saves passed container structure in NeoFS system
// through Container contract call.
@ -26,33 +14,7 @@ type Container = storage.Container
// Returns calculated container identifier and any error
// encountered that caused the saving to interrupt.
func (w *Wrapper) Put(cnr *Container) (*CID, error) {
// calculate container identifier
//
// Note: cid is used as return value only, but the calculation is performed
// primarily in order to catch potential error before contract client call.
cid, err := container.CalculateID(cnr)
if err != nil {
return nil, errors.Wrap(err, "could not calculate container identifier")
}
// marshal the container
cnrBytes, err := cnr.MarshalBinary()
if err != nil {
return nil, errors.Wrap(err, "could not marshal the container")
}
// prepare invocation arguments
args := contract.PutArgs{}
args.SetOwnerID(cnr.OwnerID().Bytes())
args.SetContainer(cnrBytes)
args.SetSignature(nil) // TODO: set signature from request when will appear.
// invoke smart contract call
if err := w.client.Put(args); err != nil {
return nil, errors.Wrap(err, "could not invoke smart contract")
}
return cid, nil
panic("implement me")
}
// Get reads the container from NeoFS system by identifier
@ -61,29 +23,7 @@ func (w *Wrapper) Put(cnr *Container) (*CID, error) {
// If an empty slice is returned for the requested identifier,
// storage.ErrNotFound error is returned.
func (w *Wrapper) Get(cid CID) (*Container, error) {
// prepare invocation arguments
args := contract.GetArgs{}
args.SetCID(cid.Bytes())
// invoke smart contract call
values, err := w.client.Get(args)
if err != nil {
return nil, errors.Wrap(err, "could not invoke smart contract")
}
cnrBytes := values.Container()
if len(cnrBytes) == 0 {
return nil, storage.ErrNotFound
}
cnr := new(Container)
// unmarshal the container
if err := cnr.UnmarshalBinary(cnrBytes); err != nil {
return nil, errors.Wrap(err, "could not unmarshal container")
}
return cnr, nil
panic("implement me")
}
// Delete removes the container from NeoFS system
@ -92,19 +32,7 @@ func (w *Wrapper) Get(cid CID) (*Container, error) {
// Returns any error encountered that caused
// the removal to interrupt.
func (w *Wrapper) Delete(cid CID) error {
// prepare invocation arguments
args := contract.DeleteArgs{}
args.SetCID(cid.Bytes())
args.SetOwnerID(nil) // TODO: add owner ID when will appear.
args.SetSignature(nil) // TODO: add CID signature when will appear.
// invoke smart contract call
//
// Note: errors.Wrap return nil on nil error arg.
return errors.Wrap(
w.client.Delete(args),
"could not invoke smart contract",
)
panic("implement me")
}
// List returns a list of container identifiers belonging
@ -114,35 +42,5 @@ func (w *Wrapper) Delete(cid CID) error {
// Returns the identifiers of all NeoFS containers if pointer
// to owner identifier is nil.
func (w *Wrapper) List(ownerID *OwnerID) ([]CID, error) {
// prepare invocation arguments
args := contract.ListArgs{}
// Note: by default owner identifier slice is nil,
// so client won't attach invocation arguments.
// This behavior matches the nil argument of current method.
// If argument is not nil, we must specify owner identifier.
if ownerID != nil {
args.SetOwnerID(ownerID.Bytes())
}
// invoke smart contract call
values, err := w.client.List(args)
if err != nil {
return nil, errors.Wrap(err, "could not invoke smart contract")
}
binCIDList := values.CIDList()
cidList := make([]CID, 0, len(binCIDList))
// unmarshal all container identifiers
for i := range binCIDList {
cid, err := refs.CIDFromBytes(binCIDList[i])
if err != nil {
return nil, errors.Wrapf(err, "could not decode container ID #%d", i)
}
cidList = append(cidList, cid)
}
return cidList, nil
panic("implement me")
}

View file

@ -1,33 +1,13 @@
package wrapper
import (
eacl "github.com/nspcc-dev/neofs-api-go/acl/extended"
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage"
contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/container"
"github.com/pkg/errors"
)
// Table represents extended ACL rule table.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage.Table.
type Table = storage.Table
// FIXME: correct the definition.
type Table struct{}
// GetEACL reads the extended ACL table from NeoFS system
// through Container contract call.
func (w *Wrapper) GetEACL(cid CID) (Table, error) {
// prepare invocation arguments
args := contract.EACLArgs{}
args.SetCID(cid.Bytes())
// invoke smart contract call
values, err := w.client.EACL(args)
if err != nil {
return nil, errors.Wrap(err, "could not invoke smart contract")
}
// unmarshal and return eACL table
return eacl.UnmarshalTable(values.EACL())
panic("implement me")
}
// PutEACL saves the extended ACL table in NeoFS system
@ -35,17 +15,5 @@ func (w *Wrapper) GetEACL(cid CID) (Table, error) {
//
// Returns any error encountered that caused the saving to interrupt.
func (w *Wrapper) PutEACL(cid CID, table Table, sig []byte) error {
// prepare invocation arguments
args := contract.SetEACLArgs{}
args.SetEACL(eacl.MarshalTable(table))
args.SetCID(cid.Bytes())
args.SetSignature(sig)
// invoke smart contract call
//
// Note: errors.Wrap return nil on nil error arg.
return errors.Wrap(
w.client.SetEACL(args),
"could not invoke smart contract",
)
panic("implement me")
}

View file

@ -1,7 +1,6 @@
package wrapper
import (
"github.com/nspcc-dev/neofs-node/pkg/core/container/storage"
"github.com/nspcc-dev/neofs-node/pkg/morph/client/container"
)
@ -12,10 +11,8 @@ import (
type Client = container.Client
// CID represents the container identifier.
//
// CID is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container/storage.CID.
type CID = storage.CID
// FIXME: correct the definition.
type CID struct{}
// Wrapper is a wrapper over container contract
// client which implements container storage and

View file

@ -1,37 +1,10 @@
package wrapper
import (
"github.com/nspcc-dev/neofs-node/pkg/core/netmap"
contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap"
"github.com/pkg/errors"
)
// NodeInfo groups information about NeoFS storage node.
type NodeInfo struct{}
// AddPeer registers peer in NeoFS network through
// Netmap contract call.
func (w *Wrapper) AddPeer(nodeInfo netmap.Info) error {
// prepare invocation arguments
args := contract.AddPeerArgs{}
info := contract.PeerInfo{}
info.SetPublicKey(nodeInfo.PublicKey())
info.SetAddress([]byte(nodeInfo.Address()))
opts := nodeInfo.Options()
binOpts := make([][]byte, 0, len(opts))
for i := range opts {
binOpts = append(binOpts, []byte(opts[i]))
}
info.SetOptions(binOpts)
args.SetInfo(info)
// invoke smart contract call
//
// Note: errors.Wrap returns nil on nil error arg.
return errors.Wrap(
w.client.AddPeer(args),
"could not invoke smart contract",
)
func (w *Wrapper) AddPeer(nodeInfo NodeInfo) error {
panic("implement me")
}

View file

@ -1,60 +1,12 @@
package wrapper
import (
"github.com/nspcc-dev/neofs-node/pkg/core/netmap"
contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap"
"github.com/pkg/errors"
)
// NetMap represents the NeoFS network map.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/NetMap.
type NetMap = netmap.NetMap
// Info represents node information.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/netmap.Info.
type Info = netmap.Info
// FIXME: correct the definition.
type NetMap struct{}
// GetNetMap receives information list about storage nodes
// through the Netmap contract call, composes network map
// from them and returns it.
func (w *Wrapper) GetNetMap() (*NetMap, error) {
// prepare invocation arguments
args := contract.GetNetMapArgs{}
// invoke smart contract call
values, err := w.client.NetMap(args)
if err != nil {
return nil, errors.Wrap(err, "could not invoke smart contract")
}
// parse response and fill the network map
nm := netmap.New()
peerList := values.Peers()
for i := range peerList {
info := Info{}
info.SetPublicKey(peerList[i].PublicKey())
info.SetAddress(string(peerList[i].Address()))
binOpts := peerList[i].Options()
opts := make([]string, 0, len(binOpts))
for j := range binOpts {
opts = append(opts, string(binOpts[j]))
}
info.SetOptions(opts)
if err := nm.AddNode(info); err != nil {
return nil, errors.Wrapf(err, "could not add node #%d to network map", i)
}
}
return nm, nil
panic("implement me")
}

View file

@ -1,23 +1,11 @@
package wrapper
import (
"github.com/nspcc-dev/neofs-node/pkg/core/netmap/epoch"
contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap"
"github.com/pkg/errors"
)
// Epoch represents the NeoFS epoch.
// FIXME: correct the definition.
type Epoch struct{}
// NewEpoch updates NeoFS epoch number through
// Netmap contract call.
func (w *Wrapper) NewEpoch(e epoch.Epoch) error {
// prepare invocation arguments
args := contract.NewEpochArgs{}
args.SetEpochNumber(int64(epoch.ToUint64(e)))
// invoke smart contract call
//
// Note: errors.Wrap returns nil on nil error arg.
return errors.Wrap(
w.client.NewEpoch(args),
"could not invoke smart contract",
)
func (w *Wrapper) NewEpoch(e Epoch) error {
panic("implement me")
}

View file

View file

@ -1,60 +0,0 @@
package bootstrap
import (
"context"
"github.com/nspcc-dev/neofs-node/pkg/core/netmap"
"github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper"
)
// ContractClient represents the Netmap
// contract client.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper.Wrapper.
type ContractClient = *wrapper.Wrapper
// NodeInfo represents the
// information about storage node.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/netmap.Info.
type NodeInfo = netmap.Info
// Registerer represents the tool that
// registers storage node in NeoFS system.
//
// Working Registerer must be created via constructor New.
// Using the Registerer that has been created with new(Registerer)
// expression (or just declaring a Registerer variable) is unsafe
// and can lead to panic.
type Registerer struct {
client ContractClient
info NodeInfo
}
// New creates, initializes and returns the Registerer instance.
//
// If passed contract client is nil, wrapper.ErrNilWrapper is returned.
func New(client ContractClient, info NodeInfo) (*Registerer, error) {
if client == nil {
return nil, wrapper.ErrNilWrapper
}
return &Registerer{
client: client,
info: info,
}, nil
}
// Bootstrap registers storage node in NeoFS system
// through Netmap contract client.
//
// If contract client returns error, panic arises without retry.
func (r *Registerer) Bootstrap(context.Context) {
// register peer in NeoFS network
if err := r.client.AddPeer(r.info); err != nil {
panic(err)
}
}

View file

View file

@ -1,45 +0,0 @@
package peers
import (
"github.com/prometheus/client_golang/prometheus"
"google.golang.org/grpc/connectivity"
)
const stateLabel = "state"
var grpcConnections = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Help: "gRPC connections",
Name: "grpc_connections",
Namespace: "neofs",
},
[]string{stateLabel},
)
var conStates = []connectivity.State{
connectivity.Idle,
connectivity.Connecting,
connectivity.Ready,
connectivity.TransientFailure,
connectivity.Shutdown,
}
func updateMetrics(items map[connectivity.State]float64) {
for _, state := range conStates {
grpcConnections.With(prometheus.Labels{
stateLabel: state.String(),
}).Set(items[state])
}
}
func init() {
prometheus.MustRegister(
grpcConnections,
)
for _, state := range conStates {
grpcConnections.With(prometheus.Labels{
stateLabel: state.String(),
}).Set(0)
}
}

View file

@ -1,234 +0,0 @@
package peers
import (
"context"
"net"
"sync"
"time"
"github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr-net"
"github.com/nspcc-dev/neofs-node/pkg/network"
"github.com/pkg/errors"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/status"
)
type (
// Interface is an interface of network connections controller.
Interface interface {
Job(context.Context)
GRPCConnector
}
// GRPCConnector is an interface of gRPC virtual connector.
GRPCConnector interface {
GRPCConnection(ctx context.Context, maddr multiaddr.Multiaddr) (*grpc.ClientConn, error)
}
// Params groups the parameters of Interface.
Params struct {
Logger *zap.Logger
ConnectionTTL time.Duration
ConnectionIDLE time.Duration
MetricsTimeout time.Duration
KeepAliveTTL time.Duration
KeepAlivePingTTL time.Duration
}
connItem struct {
sync.RWMutex
conn *grpc.ClientConn
used time.Time
}
iface struct {
log *zap.Logger
tick time.Duration
idle time.Duration
keepAlive time.Duration
pingTTL time.Duration
metricsTimeout time.Duration
grpc struct {
// globalMutex used by garbage collector and other high
globalMutex *sync.RWMutex
// bookMutex resolves concurrent access to the new connection
bookMutex *sync.RWMutex
// connBook contains connection info
// it's mutex resolves concurrent access to existed connection
connBook map[string]*connItem
}
}
)
const (
defaultCloseTimer = 30 * time.Second
defaultConIdleTTL = 30 * time.Second
defaultKeepAliveTTL = 5 * time.Second
defaultMetricsTimeout = 5 * time.Second
defaultKeepAlivePingTTL = 50 * time.Millisecond
)
var errNilMultiaddr = errors.New("empty multi-address")
func (s *iface) removeGRPCConnection(addr string) error {
if gCon, ok := s.grpc.connBook[addr]; ok && gCon.conn != nil {
if err := gCon.conn.Close(); err != nil {
state, ok := status.FromError(err)
if !ok {
return err
}
s.log.Debug("error state",
zap.String("address", addr),
zap.Any("code", state.Code()),
zap.String("state", state.Message()),
zap.Any("details", state.Details()))
}
}
delete(s.grpc.connBook, addr)
return nil
}
func isGRPCClosed(con *grpc.ClientConn) bool {
switch con.GetState() {
case connectivity.Idle, connectivity.Connecting, connectivity.Ready:
return false
default:
// connectivity.TransientFailure, connectivity.Shutdown
return true
}
}
func convertAddress(maddr multiaddr.Multiaddr) (string, error) {
if maddr == nil {
return "", errNilMultiaddr
}
addr, err := manet.ToNetAddr(maddr)
if err != nil {
return "", errors.Wrapf(err, "could not convert address `%s`", maddr)
}
return addr.String(), nil
}
// GRPCConnection creates gRPC connection over peers connection.
func (s *iface) GRPCConnection(ctx context.Context, maddr multiaddr.Multiaddr) (*grpc.ClientConn, error) {
addr, err := convertAddress(maddr)
if err != nil {
return nil, errors.Wrapf(err, "could not convert `%v`", maddr)
}
// Get global mutex on read.
// All high level function e.g. peers garbage collector
// or shutdown must use globalMutex.Lock instead
s.grpc.globalMutex.RLock()
// Get connection item from connection book or create a new one.
// Concurrent map access resolved by bookMutex.
s.grpc.bookMutex.Lock()
item, ok := s.grpc.connBook[addr]
if !ok {
item = new(connItem)
s.grpc.connBook[addr] = item
}
s.grpc.bookMutex.Unlock()
// Now lock connection item.
// This denies concurrent access to the same address,
// but allows concurrent access to a different addresses.
item.Lock()
if item.conn != nil && !isGRPCClosed(item.conn) {
item.used = time.Now()
item.Unlock()
s.grpc.globalMutex.RUnlock()
return item.conn, nil
}
// Если вышеописанные строки переместить внутрь WithDialer,
// мы получим сломанный коннекшн, но ошибка не будет возвращена,
// поэтому мы сначала проверяем коннекшн и лишь потом возвращаем
// *gRPC.ClientConn
//
// Это будет работать с `grpc.WithBlock()`, см. ниже
conn, err := grpc.DialContext(ctx, maddr.String(),
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: s.pingTTL,
Timeout: s.keepAlive,
PermitWithoutStream: true,
}),
// TODO: we must provide grpc.WithInsecure() or set credentials
grpc.WithInsecure(),
grpc.WithBlock(),
grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
return network.Dial(ctx, maddr)
}),
)
if err == nil {
item.conn = conn
item.used = time.Now()
}
item.Unlock()
s.grpc.globalMutex.RUnlock()
return conn, err
}
// New create iface instance and check arguments.
func New(p Params) (Interface, error) {
if p.ConnectionTTL <= 0 {
p.ConnectionTTL = defaultCloseTimer
}
if p.ConnectionIDLE <= 0 {
p.ConnectionIDLE = defaultConIdleTTL
}
if p.KeepAliveTTL <= 0 {
p.KeepAliveTTL = defaultKeepAliveTTL
}
if p.KeepAlivePingTTL <= 0 {
p.KeepAlivePingTTL = defaultKeepAlivePingTTL
}
if p.MetricsTimeout <= 0 {
p.MetricsTimeout = defaultMetricsTimeout
}
return &iface{
tick: p.ConnectionTTL,
idle: p.ConnectionIDLE,
keepAlive: p.KeepAliveTTL,
pingTTL: p.KeepAlivePingTTL,
metricsTimeout: p.MetricsTimeout,
log: p.Logger,
grpc: struct {
globalMutex *sync.RWMutex
bookMutex *sync.RWMutex
connBook map[string]*connItem
}{
globalMutex: new(sync.RWMutex),
bookMutex: new(sync.RWMutex),
connBook: make(map[string]*connItem),
},
}, nil
}

View file

@ -1,211 +0,0 @@
package peers
import (
"context"
"encoding"
"encoding/json"
"net"
"strings"
"sync"
"testing"
"github.com/multiformats/go-multiaddr"
"github.com/nspcc-dev/neofs-node/pkg/network"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
)
type (
fakeAddress struct {
json.Marshaler
json.Unmarshaler
encoding.TextMarshaler
encoding.TextUnmarshaler
encoding.BinaryMarshaler
encoding.BinaryUnmarshaler
}
// service is used to implement GreaterServer.
service struct{}
)
// Hello is simple handler
func (*service) Hello(ctx context.Context, req *HelloRequest) (*HelloResponse, error) {
return &HelloResponse{
Message: "Hello " + req.Name,
}, nil
}
var _ multiaddr.Multiaddr = (*fakeAddress)(nil)
func (fakeAddress) Equal(multiaddr.Multiaddr) bool {
return false
}
func (fakeAddress) Bytes() []byte {
return nil
}
func (fakeAddress) String() string {
return "fake"
}
func (fakeAddress) Protocols() []multiaddr.Protocol {
return []multiaddr.Protocol{{Name: "fake"}}
}
func (fakeAddress) Encapsulate(multiaddr.Multiaddr) multiaddr.Multiaddr {
panic("implement me")
}
func (fakeAddress) Decapsulate(multiaddr.Multiaddr) multiaddr.Multiaddr {
panic("implement me")
}
func (fakeAddress) ValueForProtocol(code int) (string, error) {
return "", nil
}
const testCount = 10
func newTestAddress(t *testing.T) multiaddr.Multiaddr {
lis, err := net.Listen("tcp", "0.0.0.0:0") // nolint:gosec
require.NoError(t, err)
require.NoError(t, lis.Close())
l, ok := lis.(*net.TCPListener)
require.True(t, ok)
_, port, err := net.SplitHostPort(l.Addr().String())
require.NoError(t, err)
items := []string{
"ip4",
"127.0.0.1",
"tcp",
port,
}
maddr, err := multiaddr.NewMultiaddr("/" + strings.Join(items, "/"))
require.NoError(t, err)
return maddr
}
func TestInterface(t *testing.T) {
t.Run("gRPC connection test", func(t *testing.T) {
var (
err error
s Interface
h = &service{}
g = grpc.NewServer()
a1 = newTestAddress(t)
_ = h
done = make(chan struct{})
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
s, err = New(Params{})
require.NoError(t, err)
RegisterGreeterServer(g, h) // register service
l, err := network.Listen(a1)
require.NoError(t, err)
defer l.Close() // nolint:golint
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
close(done)
_ = g.Serve(l)
wg.Done()
}()
<-done // wait for server is start listening connections:
// Fail connection
con, err := s.GRPCConnection(ctx, &fakeAddress{})
require.Nil(t, con)
require.Error(t, err)
con, err = s.GRPCConnection(ctx, a1)
require.NoError(t, err)
cli := NewGreeterClient(con)
resp, err := cli.Hello(ctx, &HelloRequest{
Name: "Interface test",
})
require.NoError(t, err)
require.NotNil(t, resp)
require.Equal(t, "Hello Interface test", resp.Message)
g.GracefulStop()
wg.Wait()
})
t.Run("test grpc connections", func(t *testing.T) {
var (
ifaces = make([]Interface, 0, testCount)
addresses = make([]multiaddr.Multiaddr, 0, testCount)
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for i := 0; i < testCount; i++ {
addresses = append(addresses, newTestAddress(t))
s, err := New(Params{})
require.NoError(t, err)
lis, err := network.Listen(addresses[i])
require.NoError(t, err)
svc := &service{}
srv := grpc.NewServer()
RegisterGreeterServer(srv, svc)
ifaces = append(ifaces, s)
go func() {
require.NoError(t, srv.Serve(lis))
}()
}
const reqName = "test"
wg := new(sync.WaitGroup)
for i := 0; i < testCount; i++ {
for j := 0; j < testCount; j++ {
wg.Add(1)
go func(i, j int) {
defer wg.Done()
con, err := ifaces[i].GRPCConnection(ctx, addresses[j])
require.NoError(t, err)
cli := NewGreeterClient(con)
resp, err := cli.Hello(ctx, &HelloRequest{Name: reqName})
require.NoError(t, err)
require.Equal(t, "Hello "+reqName, resp.Message)
require.NoError(t, con.Close())
}(i, j)
}
}
wg.Wait()
})
}

View file

@ -1,601 +0,0 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: pkg/network/peers/peers_test.proto
package peers
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Request message example
type HelloRequest struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HelloRequest) Reset() { *m = HelloRequest{} }
func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
func (*HelloRequest) ProtoMessage() {}
func (*HelloRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_34a082b16b4e437d, []int{0}
}
func (m *HelloRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *HelloRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_HelloRequest.Merge(m, src)
}
func (m *HelloRequest) XXX_Size() int {
return m.Size()
}
func (m *HelloRequest) XXX_DiscardUnknown() {
xxx_messageInfo_HelloRequest.DiscardUnknown(m)
}
var xxx_messageInfo_HelloRequest proto.InternalMessageInfo
func (m *HelloRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
type HelloResponse struct {
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HelloResponse) Reset() { *m = HelloResponse{} }
func (m *HelloResponse) String() string { return proto.CompactTextString(m) }
func (*HelloResponse) ProtoMessage() {}
func (*HelloResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_34a082b16b4e437d, []int{1}
}
func (m *HelloResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *HelloResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_HelloResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *HelloResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_HelloResponse.Merge(m, src)
}
func (m *HelloResponse) XXX_Size() int {
return m.Size()
}
func (m *HelloResponse) XXX_DiscardUnknown() {
xxx_messageInfo_HelloResponse.DiscardUnknown(m)
}
var xxx_messageInfo_HelloResponse proto.InternalMessageInfo
func (m *HelloResponse) GetMessage() string {
if m != nil {
return m.Message
}
return ""
}
func init() {
proto.RegisterType((*HelloRequest)(nil), "peers.HelloRequest")
proto.RegisterType((*HelloResponse)(nil), "peers.HelloResponse")
}
func init() { proto.RegisterFile("pkg/network/peers/peers_test.proto", fileDescriptor_34a082b16b4e437d) }
var fileDescriptor_34a082b16b4e437d = []byte{
// 210 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2a, 0xc8, 0x4e, 0xd7,
0xcf, 0x4b, 0x2d, 0x29, 0xcf, 0x2f, 0xca, 0xd6, 0x2f, 0x48, 0x4d, 0x2d, 0x2a, 0x86, 0x90, 0xf1,
0x25, 0xa9, 0xc5, 0x25, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xac, 0x60, 0x11, 0x25, 0x25,
0x2e, 0x1e, 0x8f, 0xd4, 0x9c, 0x9c, 0xfc, 0xa0, 0xd4, 0xc2, 0xd2, 0xd4, 0xe2, 0x12, 0x21, 0x21,
0x2e, 0x96, 0xbc, 0xc4, 0xdc, 0x54, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x30, 0x5b, 0x49,
0x93, 0x8b, 0x17, 0xaa, 0xa6, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x55, 0x48, 0x82, 0x8b, 0x3d, 0x37,
0xb5, 0xb8, 0x38, 0x31, 0x1d, 0xa6, 0x0e, 0xc6, 0x35, 0xb2, 0xe5, 0x62, 0x77, 0x2f, 0x4a, 0x4d,
0x2d, 0x49, 0x2d, 0x12, 0x32, 0xe2, 0x62, 0x05, 0xeb, 0x12, 0x12, 0xd6, 0x03, 0x5b, 0xa5, 0x87,
0x6c, 0x8f, 0x94, 0x08, 0xaa, 0x20, 0xc4, 0x60, 0x27, 0xe7, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c,
0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc6, 0x63, 0x39, 0x86, 0x28, 0xc3, 0xf4, 0xcc, 0x92,
0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0xbc, 0xe2, 0x82, 0xe4, 0x64, 0xdd, 0x94, 0xd4,
0x32, 0xfd, 0xbc, 0xd4, 0xfc, 0xb4, 0x62, 0xdd, 0xbc, 0xfc, 0x94, 0x54, 0x7d, 0x0c, 0xaf, 0x26,
0xb1, 0x81, 0x3d, 0x68, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x70, 0xa7, 0x10, 0xcc, 0x06, 0x01,
0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// GreeterClient is the client API for Greeter service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type GreeterClient interface {
Hello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error)
}
type greeterClient struct {
cc *grpc.ClientConn
}
func NewGreeterClient(cc *grpc.ClientConn) GreeterClient {
return &greeterClient{cc}
}
func (c *greeterClient) Hello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) {
out := new(HelloResponse)
err := c.cc.Invoke(ctx, "/peers.Greeter/Hello", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// GreeterServer is the server API for Greeter service.
type GreeterServer interface {
Hello(context.Context, *HelloRequest) (*HelloResponse, error)
}
// UnimplementedGreeterServer can be embedded to have forward compatible implementations.
type UnimplementedGreeterServer struct {
}
func (*UnimplementedGreeterServer) Hello(ctx context.Context, req *HelloRequest) (*HelloResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Hello not implemented")
}
func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
s.RegisterService(&_Greeter_serviceDesc, srv)
}
func _Greeter_Hello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HelloRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GreeterServer).Hello(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/peers.Greeter/Hello",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GreeterServer).Hello(ctx, req.(*HelloRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Greeter_serviceDesc = grpc.ServiceDesc{
ServiceName: "peers.Greeter",
HandlerType: (*GreeterServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Hello",
Handler: _Greeter_Hello_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "pkg/network/peers/peers_test.proto",
}
func (m *HelloRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *HelloRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *HelloRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintPeersTest(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *HelloResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *HelloResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *HelloResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Message) > 0 {
i -= len(m.Message)
copy(dAtA[i:], m.Message)
i = encodeVarintPeersTest(dAtA, i, uint64(len(m.Message)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintPeersTest(dAtA []byte, offset int, v uint64) int {
offset -= sovPeersTest(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *HelloRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovPeersTest(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *HelloResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Message)
if l > 0 {
n += 1 + l + sovPeersTest(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovPeersTest(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozPeersTest(x uint64) (n int) {
return sovPeersTest(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *HelloRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPeersTest
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: HelloRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: HelloRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPeersTest
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthPeersTest
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthPeersTest
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipPeersTest(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthPeersTest
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthPeersTest
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *HelloResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPeersTest
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: HelloResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: HelloResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPeersTest
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthPeersTest
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthPeersTest
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Message = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipPeersTest(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthPeersTest
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthPeersTest
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipPeersTest(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowPeersTest
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowPeersTest
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowPeersTest
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthPeersTest
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupPeersTest
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthPeersTest
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthPeersTest = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowPeersTest = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupPeersTest = fmt.Errorf("proto: unexpected end of group")
)

View file

@ -1,18 +0,0 @@
syntax = "proto3";
option go_package = "github.com/nspcc-dev/neofs-node/pkg/network/peers";
package peers;
// The Greater service definition.
service Greeter {
rpc Hello(HelloRequest) returns (HelloResponse);
}
// Request message example
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}

View file

@ -1,238 +0,0 @@
package peers
import (
"crypto/ecdsa"
"crypto/elliptic"
"github.com/multiformats/go-multiaddr"
crypto "github.com/nspcc-dev/neofs-crypto"
netmap "github.com/nspcc-dev/neofs-node/pkg/core/netmap"
"github.com/pkg/errors"
"go.uber.org/zap"
)
type (
// Store is an interface to storage of all p2p connections
Store interface {
SelfIDReceiver
PublicKeyStore
AddressIDReceiver
AddPeer(multiaddr.Multiaddr, *ecdsa.PublicKey, *ecdsa.PrivateKey) (ID, error)
DeletePeer(ID)
PeerNetAddressStore
GetPrivateKey(ID) (*ecdsa.PrivateKey, error)
Update(*netmap.NetMap) error
Sign([]byte) ([]byte, error)
Verify(id ID, data, sign []byte) error
Check(min int) error
}
// PublicKeyStore is an interface of the storage of peer's public keys.
PublicKeyStore interface {
GetPublicKey(ID) (*ecdsa.PublicKey, error)
}
// SelfIDReceiver is an interface of local peer ID value with read access.
SelfIDReceiver interface {
SelfID() ID
}
// AddressIDReceiver is an interface of Multiaddr to ID converter.
AddressIDReceiver interface {
AddressID(multiaddr.Multiaddr) (ID, error)
}
// PeerNetAddressStore is an interface of ID to Multiaddr converter.
PeerNetAddressStore interface {
GetAddr(ID) (multiaddr.Multiaddr, error)
}
// StoreParams for creating new Store.
StoreParams struct {
Addr multiaddr.Multiaddr
Key *ecdsa.PrivateKey
Storage Storage
StoreCap int
Logger *zap.Logger
}
store struct {
self ID
addr multiaddr.Multiaddr
storage Storage
log *zap.Logger
key *ecdsa.PrivateKey
}
)
const defaultMinimalSignaturesCount = 3
var errPeerNotFound = errors.New("peer not found")
func (p *store) AddressID(addr multiaddr.Multiaddr) (ID, error) {
if p.addr.Equal(addr) {
return p.self, nil
}
res := p.storage.Filter(maddrFilter(addr))
if len(res) == 0 {
return "", errPeerNotFound
}
return res[0], nil
}
func maddrFilter(addr multiaddr.Multiaddr) PeerFilter {
return func(p Peer) bool { return addr.Equal(p.Address()) }
}
// SelfID return ID of current Node.
func (p *store) SelfID() ID {
return p.self
}
// AddPeer to store..
// Try to get PeerID from PublicKey, or return error
// Store Address and PublicKey for that PeerID.
func (p *store) AddPeer(addr multiaddr.Multiaddr, pub *ecdsa.PublicKey, key *ecdsa.PrivateKey) (ID, error) {
item := NewPeer(addr, pub, key)
if err := p.storage.Set(item.ID(), item); err != nil {
return "", err
}
return item.ID(), nil
}
// DeletePeer from store.
func (p *store) DeletePeer(id ID) {
if err := p.storage.Rem(id); err != nil {
p.log.Error("could not delete peer",
zap.Stringer("id", id),
zap.Error(err))
}
}
// Update update Store by new network map.
func (p *store) Update(nm *netmap.NetMap) error {
if err := p.storage.Update(nm); err != nil {
return err
}
// we must provide our PrivateKey, after updating
if peer, err := p.storage.Get(p.self); err != nil {
peer = NewPeer(p.addr, &p.key.PublicKey, p.key)
return p.storage.Set(p.self, peer)
} else if err := peer.SetPrivateKey(p.key); err != nil {
return errors.Wrapf(err, "could not update private key (%s)", p.self.String())
} else if err := p.storage.Set(p.self, peer); err != nil {
return errors.Wrapf(err, "could not save peer(%s)", p.self.String())
}
return nil
}
// GetAddr by PeerID.
func (p *store) GetAddr(id ID) (multiaddr.Multiaddr, error) {
n, err := p.storage.Get(id)
if err != nil {
return nil, err
}
return n.Address(), nil
}
// GetPublicKey by PeerID.
func (p *store) GetPublicKey(id ID) (*ecdsa.PublicKey, error) {
n, err := p.storage.Get(id)
if err != nil {
return nil, err
}
return n.PublicKey(), nil
}
// GetPrivateKey by PeerID.
func (p *store) GetPrivateKey(id ID) (*ecdsa.PrivateKey, error) {
n, err := p.storage.Get(id)
if err != nil {
return nil, err
}
return n.PrivateKey()
}
// Sign signs a data using the private key. If the data is longer than
// the bit-length of the private key's curve order, the hash will be
// truncated to that length. It returns the signature as slice bytes.
// The security of the private key depends on the entropy of rand.
func (p *store) Sign(data []byte) ([]byte, error) {
return crypto.Sign(p.key, data)
}
// Verify verifies the signature in r, s of hash using the public key, pub. Its
// return value records whether the signature is valid.
// If store doesn't contains public key for ID,
// returns error about that
// TODO we must provide same method, but for IR list, to check,
// that we have valid signatures of needed IR members
func (p *store) Verify(id ID, data, sign []byte) error {
if pub, err := p.GetPublicKey(id); err != nil {
return errors.Wrap(err, "could not get PublicKey")
} else if err := crypto.Verify(pub, data, sign); err != nil {
return errors.Wrapf(err, "could not verify signature: sign(`%x`) & data(`%x`)", sign, data)
}
return nil
}
// Neighbours peers that which are distributed by hrw(id).
func (p *store) Neighbours(seed int64, count int) ([]ID, error) {
return p.storage.List(p.self, seed, count)
}
// Check validate signatures count
// TODO replace with settings or something else.
// We can fetch min-count from settings, or
// use another method for validate this..
func (p *store) Check(min int) error {
if min <= defaultMinimalSignaturesCount {
return errors.Errorf("invalid count of valid signatures: minimum %d, actual %d",
defaultMinimalSignaturesCount,
min,
)
}
return nil
}
// NewStore creates new store by params.
func NewStore(p StoreParams) (Store, error) {
var storage Storage
if p.Key == nil || p.Key.Curve != elliptic.P256() {
return nil, crypto.ErrEmptyPrivateKey
}
if p.Addr == nil {
return nil, errNilMultiaddr
}
if storage = p.Storage; storage == nil {
storage = NewSimpleStorage(p.StoreCap, p.Logger)
}
id := IDFromPublicKey(&p.Key.PublicKey)
peer := NewPeer(p.Addr, &p.Key.PublicKey, p.Key)
if err := storage.Set(id, peer); err != nil {
return nil, err
}
return &store{
self: id,
storage: storage,
key: p.Key,
addr: p.Addr,
log: p.Logger,
}, nil
}

View file

@ -1,251 +0,0 @@
package peers
import (
"strconv"
"testing"
"github.com/multiformats/go-multiaddr"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/nspcc-dev/neofs-node/pkg/core/netmap"
loggertest "github.com/nspcc-dev/neofs-node/pkg/util/logger/test"
"github.com/nspcc-dev/neofs-node/pkg/util/test"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
type testSign struct {
ID ID
Sign []byte
}
const debug = false
func createNetworkMap(t *testing.T) *netmap.NetMap {
var (
Region = []string{"America", "Europe", "Asia"}
Country = map[string][]string{
"America": {"USA", "Canada", "Brazil"},
"Europe": {"France", "Germany", "Sweden"},
"Asia": {"Russia", "China", "Korea", "Japan"},
}
City = map[string][]string{
"USA": {"Washington", "New-York", "Seattle", "Chicago", "Detroit"},
"Canada": {"Toronto", "Ottawa", "Quebec", "Winnipeg"},
"Brazil": {"Rio-de-Janeiro", "San-Paulo", "Salvador"},
"France": {"Paris", "Lion", "Nice", "Marseille"},
"Germany": {"Berlin", "Munich", "Dortmund", "Hamburg", "Cologne"},
"Sweden": {"Stockholm", "Malmo", "Uppsala"},
"Russia": {"Moscow", "Saint-Petersburg", "Ekaterinburg", "Novosibirsk"},
"China": {"Beijing", "Shanghai", "Shenzhen", "Guangzhou"},
"Korea": {"Seoul", "Busan"},
"Japan": {"Tokyo", "Kyoto", "Yokohama", "Osaka"},
}
nm = netmap.New()
port int64 = 4000
i = 0
)
for _, r := range Region {
for _, co := range Country[r] {
for _, ci := range City[co] {
addr := "/ip4/127.0.0.1/tcp/" + strconv.FormatInt(port, 10)
port++
option := "/Region:" + r + "/Country:" + co + "/City:" + ci
pk := crypto.MarshalPublicKey(&test.DecodeKey(i).PublicKey)
i++
info := netmap.Info{}
info.SetAddress(addr)
info.SetPublicKey(pk)
info.SetOptions([]string{option})
require.NoError(t, nm.AddNode(info))
}
}
}
return nm
}
func testMultiAddress(t *testing.T) multiaddr.Multiaddr {
addr, err := multiaddr.NewMultiaddr("/ip4/0.0.0.0/tcp/0")
require.NoError(t, err)
return addr
}
func TestPeerstore(t *testing.T) {
var (
l = loggertest.NewLogger(false)
key = test.DecodeKey(1)
)
t.Run("it should creates new store", func(t *testing.T) {
ps, err := NewStore(StoreParams{
Key: key,
Logger: l,
Addr: testMultiAddress(t),
})
require.NoError(t, err)
require.NotNil(t, ps)
maddr, err := multiaddr.NewMultiaddr("/ip4/127.0.0.1/tcp/4000")
require.NoError(t, err)
expect := crypto.MarshalPublicKey(&key.PublicKey)
id, err := ps.AddPeer(maddr, &key.PublicKey, key)
require.NoError(t, err)
pub, err := ps.GetPublicKey(id)
require.NoError(t, err)
actual := crypto.MarshalPublicKey(pub)
require.Equal(t, expect, actual)
addr1, err := ps.GetAddr(id)
require.NoError(t, err)
require.True(t, maddr.Equal(addr1))
ps.DeletePeer(id)
addr1, err = ps.GetAddr(id)
require.Nil(t, addr1)
require.Error(t, err)
})
t.Run("it should creates new store based on netmap", func(t *testing.T) {
var nm = createNetworkMap(t)
ps, err := NewStore(StoreParams{
Key: key,
Logger: l,
Addr: testMultiAddress(t),
})
require.NoError(t, err)
require.NotNil(t, ps)
err = ps.Update(nm)
require.NoError(t, err)
expect := nm.Nodes()[0].PublicKey()
id := IDFromBinary(expect)
addr, err := ps.GetAddr(id)
require.NoError(t, err)
require.Equal(t, nm.Nodes()[0].Address(), addr.String())
pub, err := ps.GetPublicKey(id)
require.NoError(t, err)
actual := crypto.MarshalPublicKey(pub)
require.Equal(t, expect, actual)
})
t.Run("multiple store's", func(t *testing.T) {
var (
count = 10
items = make([]Store, 0, count)
data = []byte("Hello world")
peers = make([]Peer, 0, count)
signs = make([]*testSign, 0, count)
)
for i := 0; i < count; i++ {
key := test.DecodeKey(i)
addr, err := multiaddr.NewMultiaddr("/ip4/0.0.0.0/tcp/0")
require.NoError(t, err)
peers = append(peers, NewLocalPeer(addr, key))
}
for i := 0; i < count; i++ {
key, err := peers[i].PrivateKey()
require.NoError(t, err)
store, err := NewStore(StoreParams{
Addr: peers[i].Address(),
Key: key,
Logger: zap.L(),
})
require.NoError(t, err)
items = append(items, store)
hash, err := store.Sign(data)
require.NoError(t, err)
sign := &testSign{
ID: peers[i].ID(),
Sign: hash,
}
signs = append(signs, sign)
l.Info("add peer",
zap.Stringer("id", peers[i].ID()))
}
for i := 0; i < count; i++ {
signature, err := items[i].Sign(data)
require.NoError(t, err)
// check the newly generated signature
err = items[i].Verify(peers[i].ID(), data, signature)
require.NoError(t, err)
for j := 0; j < count; j++ {
// check previously generated signature
addr, pub := peers[j].Address(), peers[j].PublicKey()
key, err := peers[j].PrivateKey()
require.NoError(t, err)
_, err = items[i].AddPeer(addr, pub, key)
require.NoError(t, err)
err = items[i].Verify(signs[j].ID, data, signs[j].Sign)
require.NoError(t, err)
}
}
})
t.Run("Get self address", func(t *testing.T) {
addr := testMultiAddress(t)
ps, err := NewStore(StoreParams{
Key: key,
Logger: l,
Addr: addr,
})
require.NoError(t, err)
require.NotNil(t, ps)
selfAddr, err := ps.GetAddr(ps.SelfID())
require.NoError(t, err)
require.Equal(t, selfAddr, addr)
})
t.Run("Get ID for multi address", func(t *testing.T) {
addr := testMultiAddress(t)
ps, err := NewStore(StoreParams{
Key: key,
Logger: l,
Addr: addr,
})
require.NoError(t, err)
require.NotNil(t, ps)
maddr, err := multiaddr.NewMultiaddr("/ip4/127.0.0.1/tcp/4000")
require.NoError(t, err)
id, err := ps.AddPeer(maddr, &key.PublicKey, key)
require.NoError(t, err)
res, err := ps.AddressID(maddr)
require.NoError(t, err)
require.True(t, id.Equal(res))
maddr2, err := multiaddr.NewMultiaddr("/ip4/127.0.0.1/tcp/4001")
require.NoError(t, err)
res, err = ps.AddressID(maddr2)
require.EqualError(t, err, errPeerNotFound.Error())
})
}

View file

@ -1,297 +0,0 @@
package peers
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha256"
"sync"
"github.com/multiformats/go-multiaddr"
"github.com/multiformats/go-multihash"
"github.com/nspcc-dev/hrw"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/nspcc-dev/neofs-node/pkg/core/netmap"
"github.com/pkg/errors"
"github.com/spaolacci/murmur3"
"go.uber.org/zap"
)
type (
// Peer is value, that stores in Store storage
Peer interface {
ID() ID
Address() multiaddr.Multiaddr
PublicKey() *ecdsa.PublicKey
PrivateKey() (*ecdsa.PrivateKey, error)
SetPrivateKey(*ecdsa.PrivateKey) error
// TODO implement marshal/unmarshal binary.
// Not sure that this method need for now,
// that's why let's leave it on future
// encoding.BinaryMarshaler
// encoding.BinaryUnmarshaler
}
peer struct {
id ID
pub *ecdsa.PublicKey
key *ecdsa.PrivateKey
addr multiaddr.Multiaddr
}
// ID is a type of peer identification
ID string
storage struct {
log *zap.Logger
mu *sync.RWMutex
items map[ID]Peer
}
// PeerFilter is a Peer filtering function.
PeerFilter func(Peer) bool
// Storage is storage interface for Store
Storage interface {
Get(ID) (Peer, error)
Set(ID, Peer) error
Has(ID) bool
Rem(ID) error
List(ID, int64, int) ([]ID, error)
Filter(PeerFilter) []ID
Update(*netmap.NetMap) error
}
)
const defaultStoreCapacity = 100
var (
errUnknownPeer = errors.New("unknown peer")
errBadPublicKey = errors.New("bad public key")
)
var errNilNetMap = errors.New("netmap is nil")
// Hash method used in HRW-library.
func (i ID) Hash() uint64 {
return murmur3.Sum64(i.Bytes())
}
// NewLocalPeer creates new peer instance.
func NewLocalPeer(addr multiaddr.Multiaddr, key *ecdsa.PrivateKey) Peer {
pub := &key.PublicKey
return &peer{
id: IDFromPublicKey(pub),
pub: pub,
key: key,
addr: addr,
}
}
// NewPeer creates new peer instance.
func NewPeer(addr multiaddr.Multiaddr, pub *ecdsa.PublicKey, key *ecdsa.PrivateKey) Peer {
return &peer{
id: IDFromPublicKey(pub),
pub: pub,
key: key,
addr: addr,
}
}
func (p *peer) SetPrivateKey(key *ecdsa.PrivateKey) error {
if key == nil || key.Curve != elliptic.P256() {
return crypto.ErrEmptyPrivateKey
}
p.key = key
return nil
}
// ID of peer.
func (p peer) ID() ID {
return p.id
}
// Address of peer.
func (p peer) Address() multiaddr.Multiaddr {
return p.addr
}
// PublicKey returns copy of peer public key.
func (p peer) PublicKey() *ecdsa.PublicKey {
return p.pub
}
func (p peer) PrivateKey() (*ecdsa.PrivateKey, error) {
if p.key == nil {
return nil, crypto.ErrEmptyPrivateKey
}
return p.key, nil
}
// String returns string representation of PeerID.
func (i ID) String() string {
return string(i)
}
// -- -- //
// Bytes returns bytes representation of PeerID.
func (i ID) Bytes() []byte {
return []byte(i)
}
// Equal checks that both id's are identical.
func (i ID) Equal(id ID) bool {
return i == id
}
// IDFromPublicKey returns peer ID for host with given public key.
func IDFromPublicKey(pk *ecdsa.PublicKey) ID {
if pk == nil {
return ""
}
return IDFromBinary(crypto.MarshalPublicKey(pk))
}
// IDFromBinary returns peer ID for host with given slice of byte.
func IDFromBinary(b []byte) ID {
bytes := sha256.Sum256(b)
hash, _ := multihash.Encode(bytes[:], multihash.IDENTITY)
ident := multihash.Multihash(hash)
return ID(ident.B58String())
}
// NewSimpleStorage is implementation over map.
func NewSimpleStorage(capacity int, l *zap.Logger) Storage {
if capacity <= 0 {
capacity = defaultStoreCapacity
}
return &storage{
log: l,
mu: new(sync.RWMutex),
items: make(map[ID]Peer, capacity),
}
}
// List peers that which are distributed by hrw(seed).
func (s *storage) List(id ID, seed int64, count int) ([]ID, error) {
s.mu.RLock()
items := make([]ID, 0, len(s.items))
for key := range s.items {
// ignore ourselves
if id.Equal(key) {
continue
}
items = append(items, key)
}
s.mu.RUnlock()
// distribute keys by hrw(seed)
hrw.SortSliceByValue(items,
uint64(seed))
return items[:count], nil
}
// Get peer by ID.
func (s *storage) Get(id ID) (Peer, error) {
s.mu.RLock()
p, ok := s.items[id]
s.mu.RUnlock()
if ok {
return p, nil
}
return nil, errors.Wrapf(errUnknownPeer, "peer(%s)", id)
}
// Set peer by id.
func (s *storage) Set(id ID, p Peer) error {
s.mu.Lock()
s.items[id] = p
s.mu.Unlock()
return nil
}
// Has checks peer exists by id.
func (s *storage) Has(id ID) bool {
s.mu.RLock()
_, ok := s.items[id]
s.mu.RUnlock()
return ok
}
// Rem peer by id.
func (s *storage) Rem(id ID) error {
s.mu.Lock()
delete(s.items, id)
s.mu.Unlock()
return nil
}
// Update storage by network map.
func (s *storage) Update(nm *netmap.NetMap) error {
s.mu.Lock()
defer s.mu.Unlock()
list := nm.Nodes()
if len(list) == 0 {
return errNilNetMap
}
items := make(map[ID]Peer, len(s.items))
for i := range list {
addr, err := multiaddr.NewMultiaddr(list[i].Address())
if err != nil {
return errors.Wrapf(err, "address=`%s`", list[i].Address())
}
pubKey := list[i].PublicKey()
pk := crypto.UnmarshalPublicKey(pubKey)
if pk == nil && pubKey != nil {
return errors.Wrapf(errBadPublicKey, "pubkey=`%x`", pubKey)
}
id := IDFromPublicKey(pk)
if pv, ok := s.items[id]; ok {
if pv.Address() != nil && pv.Address().Equal(addr) {
items[id] = pv
continue
}
}
items[id] = NewPeer(addr, pk, nil)
}
s.items = items
return nil
}
func (s *storage) Filter(filter PeerFilter) (res []ID) {
s.mu.RLock()
defer s.mu.RUnlock()
for id, peer := range s.items {
if filter(peer) {
res = append(res, id)
}
}
return
}

View file

@ -1,67 +0,0 @@
package peers
import (
"context"
"time"
"go.uber.org/zap"
"google.golang.org/grpc/connectivity"
)
func (s *iface) Job(ctx context.Context) {
var (
tick = time.NewTimer(s.tick)
metrics = time.NewTimer(s.metricsTimeout)
)
loop:
for {
select {
case <-ctx.Done():
break loop
case <-metrics.C:
var items = make(map[connectivity.State]float64)
s.grpc.globalMutex.Lock()
for _, item := range s.grpc.connBook {
if item.conn != nil {
items[item.conn.GetState()]++
}
}
s.grpc.globalMutex.Unlock()
updateMetrics(items)
metrics.Reset(s.metricsTimeout)
case <-tick.C:
var count int
s.grpc.globalMutex.Lock()
for addr, item := range s.grpc.connBook {
if item.conn == nil || isGRPCClosed(item.conn) || time.Since(item.used) > s.idle {
if err := s.removeGRPCConnection(addr); err != nil {
s.log.Error("could not close connection",
zap.String("address", addr),
zap.String("target", item.conn.Target()),
zap.Stringer("idle", time.Since(item.used)),
zap.Error(err))
continue
}
count++
} else {
s.log.Debug("ignore connection",
zap.String("address", addr),
zap.Stringer("idle", time.Since(item.used)))
}
}
s.grpc.globalMutex.Unlock()
s.log.Debug("cleanup connections done",
zap.Int("closed", count))
tick.Reset(s.tick)
}
}
tick.Stop()
}

View file

@ -1,94 +0,0 @@
package accounting
import (
"context"
"github.com/nspcc-dev/neofs-api-go/accounting"
"github.com/nspcc-dev/neofs-api-go/decimal"
"github.com/nspcc-dev/neofs-node/pkg/morph/client/balance/wrapper"
"github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc"
libgrpc "github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type (
// Service is an interface of the server of Accounting service.
Service interface {
grpc.Service
accounting.AccountingServer
}
// ContractClient represents the client of Balance contract.
//
// It is a type alias of the pointer to
// github.com/nspcc-dev/neofs-node/pkg/morph/client/balance/wrapper.Wrapper.
ContractClient = *wrapper.Wrapper
// Params groups the parameters of Accounting service server's constructor.
Params struct {
ContractClient ContractClient
}
accService struct {
contractClient ContractClient
}
)
var requestVerifyFunc = libgrpc.VerifyRequestWithSignatures
// New is an Accounting service server's constructor.
//
// If Balance contract client is nil,
// wrapper.ErrNilWrapper is returned.
func New(p Params) (Service, error) {
if p.ContractClient == nil {
return nil, wrapper.ErrNilWrapper
}
return &accService{
contractClient: p.ContractClient,
}, nil
}
func (accService) Name() string { return "AccountingService" }
func (s accService) Register(g *grpc.Server) { accounting.RegisterAccountingServer(g, s) }
func (s accService) Balance(ctx context.Context, req *accounting.BalanceRequest) (*accounting.BalanceResponse, error) {
// verify request structure
if err := requestVerifyFunc(req); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
// get the amount of funds in client's account
fundsAmount, err := s.contractClient.BalanceOf(req.GetOwnerID())
if err != nil {
return nil, status.Error(codes.Aborted, err.Error())
}
// get decimals precision of currency transactions
// TODO: Reconsider the approach of getting decimals.
//
// Decimals value does not seem to be frequently changing.
// In this case service can work in static decimals mode and
// the value can be received once to facilitate call flow.
//
// In a true dynamic value installation it is advisable to get
// a balance with decimals through a single call. Variations:
// - add decimal value stack parameter of balanceOf method;
// - create a new method entitled smth like balanceWithDecimals.
decimals, err := s.contractClient.Decimals()
if err != nil {
return nil, status.Error(codes.Aborted, err.Error())
}
res := new(accounting.BalanceResponse)
res.Balance = decimal.NewWithPrecision(
fundsAmount,
decimals,
)
return res, nil
}

View file

@ -1,3 +0,0 @@
package accounting
// TODO: write unit tests

View file

@ -1,70 +0,0 @@
package container
import (
"context"
eacl "github.com/nspcc-dev/neofs-api-go/acl/extended"
"github.com/nspcc-dev/neofs-api-go/container"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func (s cnrService) SetExtendedACL(ctx context.Context, req *container.SetExtendedACLRequest) (*container.SetExtendedACLResponse, error) {
// check healthiness
if err := s.healthy.Healthy(); err != nil {
return nil, status.Error(codes.Unavailable, err.Error())
}
// verify request structure
if err := requestVerifyFunc(req); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
// unmarshal eACL table
table, err := eacl.UnmarshalTable(req.GetEACL())
if err != nil {
return nil, status.Error(
codes.InvalidArgument,
errors.Wrap(err, "could not decode eACL table").Error(),
)
}
// store eACL table
if err := s.aclStore.PutEACL(req.GetID(), table, req.GetSignature()); err != nil {
return nil, status.Error(
codes.Aborted,
errors.Wrap(err, "could not save eACL in storage").Error(),
)
}
return new(container.SetExtendedACLResponse), nil
}
func (s cnrService) GetExtendedACL(ctx context.Context, req *container.GetExtendedACLRequest) (*container.GetExtendedACLResponse, error) {
// check healthiness
if err := s.healthy.Healthy(); err != nil {
return nil, status.Error(codes.Unavailable, err.Error())
}
// verify request structure
if err := requestVerifyFunc(req); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
// receive binary eACL
table, err := s.aclStore.GetEACL(req.GetID())
if err != nil {
return nil, status.Error(
codes.NotFound,
errors.Wrap(err, "could not get eACL from storage").Error(),
)
}
// fill the response
res := new(container.GetExtendedACLResponse)
res.SetEACL(eacl.MarshalTable(table))
res.SetSignature(nil) // TODO: set signature when will appear.
return res, nil
}

View file

@ -1,214 +0,0 @@
package container
import (
"context"
"errors"
"testing"
"github.com/nspcc-dev/neofs-api-go/acl"
eacl "github.com/nspcc-dev/neofs-api-go/acl/extended"
"github.com/nspcc-dev/neofs-api-go/container"
"github.com/nspcc-dev/neofs-api-go/service"
"github.com/nspcc-dev/neofs-node/pkg/util/test"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Entity for mocking interfaces.
// Implementation of any interface intercepts arguments via f (if not nil).
// If err is not nil, it returns as it is. Otherwise, casted to needed type res returns w/o error.
type testEACLEntity struct {
// Set of interfaces which entity must implement, but some methods from those does not call.
// Argument interceptor. Used for ascertain of correct parameter passage between components.
f func(...interface{})
// Mocked result of any interface.
res interface{}
// Mocked error of any interface.
err error
}
var requestSignFunc = service.SignRequestData
func (s *testEACLEntity) GetEACL(cid CID) (Table, error) {
if s.f != nil {
s.f(cid)
}
if s.err != nil {
return nil, s.err
}
return s.res.(Table), nil
}
func (s *testEACLEntity) PutEACL(cid CID, table Table, sig []byte) error {
if s.f != nil {
s.f(cid, table, sig)
}
return s.err
}
func TestCnrService_SetExtendedACL(t *testing.T) {
ctx := context.TODO()
t.Run("unhealthy", func(t *testing.T) {
s := cnrService{
healthy: &testCommonEntity{
err: errors.New("some error"),
},
}
_, err := s.SetExtendedACL(ctx, new(container.SetExtendedACLRequest))
require.Error(t, err)
})
t.Run("invalid request structure", func(t *testing.T) {
s := cnrService{
healthy: new(testCommonEntity),
}
// create unsigned request
req := new(container.SetExtendedACLRequest)
require.Error(t, requestVerifyFunc(req))
_, err := s.SetExtendedACL(ctx, req)
require.Error(t, err)
st, ok := status.FromError(err)
require.True(t, ok)
require.Equal(t, codes.InvalidArgument, st.Code())
})
t.Run("EACL storage failure", func(t *testing.T) {
record := new(acl.EACLRecord)
record.SetAction(acl.EACLRecord_Allow)
table := eacl.WrapTable(nil)
table.SetRecords([]eacl.Record{eacl.WrapRecord(record)})
req := new(container.SetExtendedACLRequest)
req.SetID(CID{1, 2, 3})
req.SetEACL(eacl.MarshalTable(table))
req.SetSignature([]byte{4, 5, 6})
require.NoError(t, requestSignFunc(test.DecodeKey(0), req))
s := cnrService{
healthy: new(testCommonEntity),
aclStore: &testEACLEntity{
f: func(items ...interface{}) {
require.Equal(t, req.GetID(), items[0])
require.Equal(t, req.GetSignature(), items[2])
},
err: errors.New("storage error"),
},
}
_, err := s.SetExtendedACL(ctx, req)
require.Error(t, err)
st, ok := status.FromError(err)
require.True(t, ok)
require.Equal(t, codes.Aborted, st.Code())
})
t.Run("correct result", func(t *testing.T) {
req := new(container.SetExtendedACLRequest)
require.NoError(t, requestSignFunc(test.DecodeKey(0), req))
s := cnrService{
healthy: new(testCommonEntity),
aclStore: new(testEACLEntity),
}
res, err := s.SetExtendedACL(ctx, req)
require.NoError(t, err)
require.NotNil(t, res)
})
}
func TestCnrService_GetExtendedACL(t *testing.T) {
ctx := context.TODO()
t.Run("unhealthy", func(t *testing.T) {
s := cnrService{
healthy: &testCommonEntity{
err: errors.New("some error"),
},
}
_, err := s.GetExtendedACL(ctx, new(container.GetExtendedACLRequest))
require.Error(t, err)
})
t.Run("invalid request structure", func(t *testing.T) {
s := cnrService{
healthy: new(testCommonEntity),
}
// create unsigned request
req := new(container.GetExtendedACLRequest)
require.Error(t, requestVerifyFunc(req))
_, err := s.GetExtendedACL(ctx, req)
require.Error(t, err)
st, ok := status.FromError(err)
require.True(t, ok)
require.Equal(t, codes.InvalidArgument, st.Code())
})
t.Run("EACL storage failure", func(t *testing.T) {
req := new(container.GetExtendedACLRequest)
req.SetID(CID{1, 2, 3})
require.NoError(t, requestSignFunc(test.DecodeKey(0), req))
s := cnrService{
healthy: new(testCommonEntity),
aclStore: &testEACLEntity{
f: func(items ...interface{}) {
require.Equal(t, req.GetID(), items[0])
},
err: errors.New("storage error"),
},
}
_, err := s.GetExtendedACL(ctx, req)
require.Error(t, err)
st, ok := status.FromError(err)
require.True(t, ok)
require.Equal(t, codes.NotFound, st.Code())
})
t.Run("correct result", func(t *testing.T) {
req := new(container.GetExtendedACLRequest)
req.SetID(CID{1, 2, 3})
require.NoError(t, requestSignFunc(test.DecodeKey(0), req))
table := eacl.WrapTable(nil)
record := new(acl.EACLRecord)
record.SetAction(acl.EACLRecord_Allow)
table.SetRecords([]eacl.Record{eacl.WrapRecord(record)})
s := cnrService{
healthy: new(testCommonEntity),
aclStore: &testEACLEntity{
res: table,
},
}
res, err := s.GetExtendedACL(ctx, req)
require.NoError(t, err)
require.Equal(t, eacl.MarshalTable(table), res.GetEACL())
require.Empty(t, res.GetSignature())
})
}

View file

@ -1,30 +0,0 @@
package container
import (
eacl "github.com/nspcc-dev/neofs-api-go/acl/extended"
"github.com/nspcc-dev/neofs-node/pkg/core/container"
)
// CID represents the container identifier.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container.ID.
type CID = container.ID
// OwnerID represents the container owner identifier..
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container.OwnerID.
type OwnerID = container.OwnerID
// Container represents the NeoFS Container structure.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-node/pkg/core/container.Container.
type Container = container.Container
// Table represents the eACL table.
//
// It is a type alias of
// github.com/nspcc-dev/neofs-api-go/acl/extended.ExtendedACLTable.
type Table = eacl.Table

View file

@ -1,19 +0,0 @@
package container
// Entity for mocking interfaces.
// Implementation of any interface intercepts arguments via f (if not nil).
// If err is not nil, it returns as it is. Otherwise, casted to needed type res returns w/o error.
type testCommonEntity struct {
// Set of interfaces which entity must implement, but some methods from those does not call.
// Argument interceptor. Used for ascertain of correct parameter passage between components.
f func(...interface{})
// Mocked result of any interface.
res interface{}
// Mocked error of any interface.
err error
}
func (s testCommonEntity) Healthy() error {
return s.err
}

View file

@ -1,32 +0,0 @@
package container
import (
"context"
"github.com/nspcc-dev/neofs-api-go/container"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func (s cnrService) Delete(ctx context.Context, req *container.DeleteRequest) (*container.DeleteResponse, error) {
// check healthiness
if err := s.healthy.Healthy(); err != nil {
return nil, errors.Wrap(err, "try again later")
}
// verify request structure
if err := requestVerifyFunc(req); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
// remove container from storage
if err := s.cnrStore.Delete(req.GetCID()); err != nil {
return nil, status.Error(
codes.Aborted,
errors.Wrap(err, "could not remove container from storage").Error(),
)
}
return new(container.DeleteResponse), nil
}

View file

@ -1,58 +0,0 @@
package container
import (
"context"
"errors"
"testing"
"github.com/nspcc-dev/neofs-api-go/container"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Entity for mocking interfaces.
// Implementation of any interface intercepts arguments via f (if not nil).
// If err is not nil, it returns as it is. Otherwise, casted to needed type res returns w/o error.
type testDeleteEntity struct {
// Set of interfaces which entity must implement, but some methods from those does not call.
// Argument interceptor. Used for ascertain of correct parameter passage between components.
f func(...interface{})
// Mocked result of any interface.
res interface{}
// Mocked error of any interface.
err error
}
func TestCnrService_Delete(t *testing.T) {
ctx := context.TODO()
t.Run("unhealthy", func(t *testing.T) {
s := cnrService{
healthy: &testCommonEntity{
err: errors.New("some error"),
},
}
_, err := s.Delete(ctx, new(container.DeleteRequest))
require.Error(t, err)
})
t.Run("invalid request structure", func(t *testing.T) {
s := cnrService{
healthy: new(testCommonEntity),
}
// create unsigned request
req := new(container.DeleteRequest)
require.Error(t, requestVerifyFunc(req))
_, err := s.Delete(ctx, req)
require.Error(t, err)
st, ok := status.FromError(err)
require.True(t, ok)
require.Equal(t, codes.InvalidArgument, st.Code())
})
}

View file

@ -1,56 +0,0 @@
package container
import (
"context"
"github.com/google/uuid"
"github.com/nspcc-dev/neofs-api-go/container"
"github.com/nspcc-dev/neofs-api-go/refs"
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func (s cnrService) Get(ctx context.Context, req *container.GetRequest) (*container.GetResponse, error) {
// check healthiness
if err := s.healthy.Healthy(); err != nil {
return nil, status.Error(codes.Unavailable, err.Error())
}
// verify request structure
if err := requestVerifyFunc(req); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
// get container from storage
cnr, err := s.cnrStore.Get(req.GetCID())
if err != nil {
return nil, status.Error(
codes.NotFound,
errors.Wrap(err, "could not get container from storage").Error(),
)
}
// fill the response
res := new(container.GetResponse)
// FIXME: salt should be []byte in the message
salt, err := uuid.FromBytes(cnr.Salt())
if err != nil {
return nil, status.Error(
codes.Aborted,
errors.Wrap(err, "could not decode salt").Error(),
)
}
// FIXME: message field should be the same type or []byte.
res.Container = new(container.Container)
res.Container.Salt = refs.UUID(salt)
res.Container = new(container.Container)
res.Container.OwnerID = cnr.OwnerID()
res.Container.Rules = cnr.PlacementRule()
res.Container.BasicACL = basic.ToUint32(cnr.BasicACL())
return res, nil
}

View file

@ -1,58 +0,0 @@
package container
import (
"context"
"errors"
"testing"
"github.com/nspcc-dev/neofs-api-go/container"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Entity for mocking interfaces.
// Implementation of any interface intercepts arguments via f (if not nil).
// If err is not nil, it returns as it is. Otherwise, casted to needed type res returns w/o error.
type testGetEntity struct {
// Set of interfaces which entity must implement, but some methods from those does not call.
// Argument interceptor. Used for ascertain of correct parameter passage between components.
f func(...interface{})
// Mocked result of any interface.
res interface{}
// Mocked error of any interface.
err error
}
func TestCnrService_Get(t *testing.T) {
ctx := context.TODO()
t.Run("unhealthy", func(t *testing.T) {
s := cnrService{
healthy: &testCommonEntity{
err: errors.New("some error"),
},
}
_, err := s.Get(ctx, new(container.GetRequest))
require.Error(t, err)
})
t.Run("invalid request structure", func(t *testing.T) {
s := cnrService{
healthy: new(testCommonEntity),
}
// create unsigned request
req := new(container.GetRequest)
require.Error(t, requestVerifyFunc(req))
_, err := s.Get(ctx, req)
require.Error(t, err)
st, ok := status.FromError(err)
require.True(t, ok)
require.Equal(t, codes.InvalidArgument, st.Code())
})
}

View file

@ -1,38 +0,0 @@
package container
import (
"context"
"github.com/nspcc-dev/neofs-api-go/container"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func (s cnrService) List(ctx context.Context, req *container.ListRequest) (*container.ListResponse, error) {
// check healthiness
if err := s.healthy.Healthy(); err != nil {
return nil, errors.Wrap(err, "try again later")
}
// verify request structure
if err := requestVerifyFunc(req); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
// list container identifiers from storage
ownerID := req.GetOwnerID()
cidList, err := s.cnrStore.List(&ownerID)
if err != nil {
return nil, status.Error(
codes.NotFound,
errors.Wrap(err, "could not list the containers in storage").Error(),
)
}
// fill the response
res := new(container.ListResponse)
res.CID = cidList
return res, nil
}

View file

@ -1,58 +0,0 @@
package container
import (
"context"
"errors"
"testing"
"github.com/nspcc-dev/neofs-api-go/container"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Entity for mocking interfaces.
// Implementation of any interface intercepts arguments via f (if not nil).
// If err is not nil, it returns as it is. Otherwise, casted to needed type res returns w/o error.
type testListEntity struct {
// Set of interfaces which entity must implement, but some methods from those does not call.
// Argument interceptor. Used for ascertain of correct parameter passage between components.
f func(...interface{})
// Mocked result of any interface.
res interface{}
// Mocked error of any interface.
err error
}
func TestCnrService_List(t *testing.T) {
ctx := context.TODO()
t.Run("unhealthy", func(t *testing.T) {
s := cnrService{
healthy: &testCommonEntity{
err: errors.New("some error"),
},
}
_, err := s.List(ctx, new(container.ListRequest))
require.Error(t, err)
})
t.Run("invalid request structure", func(t *testing.T) {
s := cnrService{
healthy: new(testCommonEntity),
}
// create unsigned request
req := new(container.ListRequest)
require.Error(t, requestVerifyFunc(req))
_, err := s.List(ctx, req)
require.Error(t, err)
st, ok := status.FromError(err)
require.True(t, ok)
require.Equal(t, codes.InvalidArgument, st.Code())
})
}

View file

@ -1,57 +0,0 @@
package container
import (
"context"
"github.com/nspcc-dev/neofs-api-go/container"
"github.com/nspcc-dev/neofs-api-go/refs"
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// TODO verify MessageID.
func (s cnrService) Put(ctx context.Context, req *container.PutRequest) (*container.PutResponse, error) {
// check healthiness
if err := s.healthy.Healthy(); err != nil {
return nil, errors.Wrap(err, "try again later")
}
// verify request structure
if err := requestVerifyFunc(req); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
// create container structure
// FIXME: message field should be the same type or []byte.
cnr := new(Container)
cnr.SetOwnerID(req.GetOwnerID())
cnr.SetPlacementRule(req.GetRules())
cnr.SetBasicACL(basic.FromUint32(req.GetBasicACL()))
uid, err := refs.NewUUID()
if err != nil {
return nil, status.Error(
codes.Aborted,
errors.Wrap(err, "could not generate the salt").Error(),
)
}
cnr.SetSalt(uid.Bytes())
// save container in storage
cid, err := s.cnrStore.Put(cnr)
if err != nil {
return nil, status.Error(
codes.Aborted,
errors.Wrap(err, "could not save the container instorage").Error(),
)
}
// fill the response
res := new(container.PutResponse)
res.CID = *cid
return res, nil
}

View file

@ -1,58 +0,0 @@
package container
import (
"context"
"errors"
"testing"
"github.com/nspcc-dev/neofs-api-go/container"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Entity for mocking interfaces.
// Implementation of any interface intercepts arguments via f (if not nil).
// If err is not nil, it returns as it is. Otherwise, casted to needed type res returns w/o error.
type testPutEntity struct {
// Set of interfaces which entity must implement, but some methods from those does not call.
// Argument interceptor. Used for ascertain of correct parameter passage between components.
f func(...interface{})
// Mocked result of any interface.
res interface{}
// Mocked error of any interface.
err error
}
func TestCnrService_Put(t *testing.T) {
ctx := context.TODO()
t.Run("unhealthy", func(t *testing.T) {
s := cnrService{
healthy: &testCommonEntity{
err: errors.New("some error"),
},
}
_, err := s.Put(ctx, new(container.PutRequest))
require.Error(t, err)
})
t.Run("invalid request structure", func(t *testing.T) {
s := cnrService{
healthy: new(testCommonEntity),
}
// create unsigned request
req := new(container.PutRequest)
require.Error(t, requestVerifyFunc(req))
_, err := s.Put(ctx, req)
require.Error(t, err)
st, ok := status.FromError(err)
require.True(t, ok)
require.Equal(t, codes.InvalidArgument, st.Code())
})
}

View file

@ -1,78 +0,0 @@
package container
import (
"errors"
"github.com/nspcc-dev/neofs-api-go/container"
eacl "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage"
"github.com/nspcc-dev/neofs-node/pkg/core/container/storage"
"github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc"
libgrpc "github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc"
"go.uber.org/zap"
)
type (
// Service is an interface of the server of Container service.
Service interface {
grpc.Service
container.ServiceServer
}
// HealthChecker is an interface of node healthiness checking tool.
HealthChecker interface {
Healthy() error
}
// Params groups the parameters of Container service server's constructor.
Params struct {
Logger *zap.Logger
Healthy HealthChecker
Store storage.Storage
ExtendedACLStore eacl.Storage
}
cnrService struct {
log *zap.Logger
healthy HealthChecker
cnrStore storage.Storage
aclStore eacl.Storage
}
)
var (
errEmptyLogger = errors.New("empty log component")
errEmptyHealthChecker = errors.New("empty healthy component")
)
var requestVerifyFunc = libgrpc.VerifyRequestWithSignatures
// New is an Container service server's constructor.
func New(p Params) (Service, error) {
switch {
case p.Logger == nil:
return nil, errEmptyLogger
case p.Store == nil:
return nil, storage.ErrNilStorage
case p.Healthy == nil:
return nil, errEmptyHealthChecker
case p.ExtendedACLStore == nil:
return nil, eacl.ErrNilStorage
}
return &cnrService{
log: p.Logger,
healthy: p.Healthy,
cnrStore: p.Store,
aclStore: p.ExtendedACLStore,
}, nil
}
func (cnrService) Name() string { return "ContainerService" }
func (s cnrService) Register(g *grpc.Server) { container.RegisterServiceServer(g, s) }

View file

View file

@ -1,13 +0,0 @@
package grpc
import (
"google.golang.org/grpc"
)
type Server = grpc.Server
// Service interface
type Service interface {
Name() string
Register(*Server)
}

View file

@ -1,23 +0,0 @@
package grpc
import (
"errors"
"github.com/nspcc-dev/neofs-api-go/service"
)
// ErrMissingKeySignPairs is returned by functions that expect
// a non-empty SignKeyPair slice, but received empty.
var ErrMissingKeySignPairs = errors.New("missing key-signature pairs")
// VerifyRequestWithSignatures checks if request has signatures and all of them are valid.
//
// Returns ErrMissingKeySignPairs if request does not have signatures.
// Otherwise, behaves like service.VerifyRequestData.
func VerifyRequestWithSignatures(req service.RequestVerifyData) error {
if len(req.GetSignKeyPairs()) == 0 {
return ErrMissingKeySignPairs
}
return service.VerifyRequestData(req)
}

View file

@ -1,60 +0,0 @@
package metrics
import (
"context"
"errors"
"github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc"
"github.com/nspcc-dev/neofs-node/pkg/services/metrics"
"go.uber.org/zap"
)
type (
// Service is an interface of the server of Metrics service.
Service interface {
MetricsServer
grpc.Service
}
// Params groups the parameters of Metrics service server's constructor.
Params struct {
Logger *zap.Logger
Collector metrics.Collector
}
serviceMetrics struct {
log *zap.Logger
col metrics.Collector
}
)
var (
errEmptyLogger = errors.New("empty logger")
errEmptyCollector = errors.New("empty metrics collector")
)
// New is a Metrics service server's constructor.
func New(p Params) (Service, error) {
switch {
case p.Logger == nil:
return nil, errEmptyLogger
case p.Collector == nil:
return nil, errEmptyCollector
}
return &serviceMetrics{
log: p.Logger,
col: p.Collector,
}, nil
}
func (s *serviceMetrics) ResetSpaceCounter(_ context.Context, _ *ResetSpaceRequest) (*ResetSpaceResponse, error) {
s.col.UpdateSpaceUsage()
return &ResetSpaceResponse{}, nil
}
func (s *serviceMetrics) Name() string { return "metrics" }
func (s *serviceMetrics) Register(srv *grpc.Server) {
RegisterMetricsServer(srv, s)
}

View file

@ -1,499 +0,0 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: pkg/network/transport/metrics/grpc/service.proto
package metrics
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type ResetSpaceRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ResetSpaceRequest) Reset() { *m = ResetSpaceRequest{} }
func (m *ResetSpaceRequest) String() string { return proto.CompactTextString(m) }
func (*ResetSpaceRequest) ProtoMessage() {}
func (*ResetSpaceRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_cdef708412a96383, []int{0}
}
func (m *ResetSpaceRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *ResetSpaceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_ResetSpaceRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *ResetSpaceRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ResetSpaceRequest.Merge(m, src)
}
func (m *ResetSpaceRequest) XXX_Size() int {
return m.Size()
}
func (m *ResetSpaceRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ResetSpaceRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ResetSpaceRequest proto.InternalMessageInfo
type ResetSpaceResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ResetSpaceResponse) Reset() { *m = ResetSpaceResponse{} }
func (m *ResetSpaceResponse) String() string { return proto.CompactTextString(m) }
func (*ResetSpaceResponse) ProtoMessage() {}
func (*ResetSpaceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_cdef708412a96383, []int{1}
}
func (m *ResetSpaceResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *ResetSpaceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_ResetSpaceResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *ResetSpaceResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ResetSpaceResponse.Merge(m, src)
}
func (m *ResetSpaceResponse) XXX_Size() int {
return m.Size()
}
func (m *ResetSpaceResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ResetSpaceResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ResetSpaceResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*ResetSpaceRequest)(nil), "metrics.ResetSpaceRequest")
proto.RegisterType((*ResetSpaceResponse)(nil), "metrics.ResetSpaceResponse")
}
func init() {
proto.RegisterFile("pkg/network/transport/metrics/grpc/service.proto", fileDescriptor_cdef708412a96383)
}
var fileDescriptor_cdef708412a96383 = []byte{
// 207 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x8f, 0xb1, 0x4a, 0xc4, 0x40,
0x10, 0x86, 0x4d, 0xe3, 0xc1, 0x76, 0xae, 0x56, 0x27, 0x6c, 0x71, 0xfd, 0xed, 0x88, 0xbe, 0xc1,
0xd9, 0x6a, 0x73, 0x0a, 0x82, 0x5d, 0xb2, 0x19, 0x63, 0x08, 0xd9, 0x59, 0x67, 0x26, 0xf1, 0x55,
0x7c, 0x24, 0x4b, 0x1f, 0x41, 0xe2, 0x8b, 0x08, 0x49, 0x8a, 0xc0, 0xa5, 0x9d, 0x9f, 0xef, 0x1b,
0x3e, 0x73, 0x93, 0x9a, 0x0a, 0x22, 0xea, 0x27, 0x71, 0x03, 0xca, 0x79, 0x94, 0x44, 0xac, 0xd0,
0xa2, 0x72, 0x1d, 0x04, 0x2a, 0x4e, 0x01, 0x04, 0xb9, 0xaf, 0x03, 0xfa, 0xc4, 0xa4, 0x64, 0x37,
0xf3, 0xb6, 0xbb, 0x34, 0x17, 0x47, 0x14, 0xd4, 0xa7, 0x94, 0x07, 0x3c, 0xe2, 0x47, 0x87, 0xa2,
0xbb, 0x2b, 0x63, 0x97, 0x47, 0x49, 0x14, 0x05, 0x6f, 0x5f, 0xcc, 0xe6, 0x71, 0xa2, 0xec, 0xc3,
0x92, 0xba, 0xa7, 0x2e, 0x2a, 0xb2, 0xdd, 0xfa, 0x59, 0xea, 0x4f, 0x8c, 0xdb, 0xeb, 0xd5, 0x6d,
0x12, 0x1f, 0x9e, 0xbf, 0x07, 0x97, 0xfd, 0x0c, 0x2e, 0xfb, 0x1d, 0x5c, 0xf6, 0xf5, 0xe7, 0xce,
0x5e, 0x0f, 0x55, 0xad, 0xef, 0x5d, 0xe1, 0x03, 0xb5, 0x10, 0x25, 0x85, 0xb0, 0x2f, 0xb1, 0x87,
0x88, 0xf4, 0x26, 0xfb, 0x48, 0x25, 0xc2, 0x7a, 0xf0, 0x18, 0x3a, 0x3f, 0x2a, 0xce, 0xc7, 0xd2,
0xbb, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1d, 0x26, 0xa6, 0xbe, 0x1d, 0x01, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// MetricsClient is the client API for Metrics service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type MetricsClient interface {
ResetSpaceCounter(ctx context.Context, in *ResetSpaceRequest, opts ...grpc.CallOption) (*ResetSpaceResponse, error)
}
type metricsClient struct {
cc *grpc.ClientConn
}
func NewMetricsClient(cc *grpc.ClientConn) MetricsClient {
return &metricsClient{cc}
}
func (c *metricsClient) ResetSpaceCounter(ctx context.Context, in *ResetSpaceRequest, opts ...grpc.CallOption) (*ResetSpaceResponse, error) {
out := new(ResetSpaceResponse)
err := c.cc.Invoke(ctx, "/metrics.Metrics/ResetSpaceCounter", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// MetricsServer is the server API for Metrics service.
type MetricsServer interface {
ResetSpaceCounter(context.Context, *ResetSpaceRequest) (*ResetSpaceResponse, error)
}
// UnimplementedMetricsServer can be embedded to have forward compatible implementations.
type UnimplementedMetricsServer struct {
}
func (*UnimplementedMetricsServer) ResetSpaceCounter(ctx context.Context, req *ResetSpaceRequest) (*ResetSpaceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ResetSpaceCounter not implemented")
}
func RegisterMetricsServer(s *grpc.Server, srv MetricsServer) {
s.RegisterService(&_Metrics_serviceDesc, srv)
}
func _Metrics_ResetSpaceCounter_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ResetSpaceRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MetricsServer).ResetSpaceCounter(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/metrics.Metrics/ResetSpaceCounter",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MetricsServer).ResetSpaceCounter(ctx, req.(*ResetSpaceRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Metrics_serviceDesc = grpc.ServiceDesc{
ServiceName: "metrics.Metrics",
HandlerType: (*MetricsServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ResetSpaceCounter",
Handler: _Metrics_ResetSpaceCounter_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "pkg/network/transport/metrics/grpc/service.proto",
}
func (m *ResetSpaceRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *ResetSpaceRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ResetSpaceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func (m *ResetSpaceResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *ResetSpaceResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ResetSpaceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func encodeVarintService(dAtA []byte, offset int, v uint64) int {
offset -= sovService(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *ResetSpaceRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *ResetSpaceResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovService(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozService(x uint64) (n int) {
return sovService(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *ResetSpaceRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowService
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: ResetSpaceRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: ResetSpaceRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipService(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthService
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthService
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *ResetSpaceResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowService
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: ResetSpaceResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: ResetSpaceResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipService(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthService
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthService
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipService(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowService
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowService
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowService
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthService
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupService
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthService
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthService = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowService = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupService = fmt.Errorf("proto: unexpected end of group")
)

View file

@ -1,10 +0,0 @@
syntax = "proto3";
package metrics;
option go_package = "github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc/metrics";
service Metrics {
rpc ResetSpaceCounter(ResetSpaceRequest) returns (ResetSpaceResponse);
}
message ResetSpaceRequest {}
message ResetSpaceResponse {}

View file

@ -1,742 +0,0 @@
package object
import (
"bytes"
"context"
"crypto/ecdsa"
"fmt"
"strconv"
"github.com/multiformats/go-multiaddr"
eacl "github.com/nspcc-dev/neofs-api-go/acl/extended"
"github.com/nspcc-dev/neofs-api-go/object"
"github.com/nspcc-dev/neofs-api-go/refs"
"github.com/nspcc-dev/neofs-api-go/service"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/nspcc-dev/neofs-node/pkg/core/container"
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended"
eaclstorage "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage"
"github.com/nspcc-dev/neofs-node/pkg/core/container/storage"
"github.com/nspcc-dev/neofs-node/pkg/core/netmap"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore"
eaclcheck "github.com/nspcc-dev/neofs-node/pkg/network/transport/object/grpc/eacl"
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport"
"github.com/pkg/errors"
"go.uber.org/zap"
)
type (
// RequestTargeter is an interface of request's ACL group calculator.
RequestTargeter interface {
Target(context.Context, serviceRequest) requestTarget
}
// aclPreProcessor is an implementation of requestPreProcessor interface.
aclPreProcessor struct {
log *zap.Logger
aclInfoReceiver aclInfoReceiver
reqActionCalc requestActionCalculator
localStore localstore.Localstore
extACLSource eaclstorage.Storage
bearerVerifier bearerTokenVerifier
}
// duplicates NetmapClient method, used for testing.
irKeysReceiver interface {
InnerRingKeys() ([][]byte, error)
}
containerNodesLister interface {
ContainerNodes(ctx context.Context, cid CID) ([]multiaddr.Multiaddr, error)
ContainerNodesInfo(ctx context.Context, cid CID, prev int) ([]netmap.Info, error)
}
targetFinder struct {
log *zap.Logger
irKeysRecv irKeysReceiver
cnrLister containerNodesLister
cnrStorage storage.Storage
}
)
type objectHeadersSource interface {
getHeaders() (*Object, bool)
}
type requestActionCalculator interface {
calculateRequestAction(context.Context, requestActionParams) eacl.Action
}
type aclInfoReceiver struct {
cnrStorage storage.Storage
targetFinder RequestTargeter
}
type aclInfo struct {
rule container.BasicACL
checkExtended bool
checkBearer bool
targetInfo requestTarget
}
type reqActionCalc struct {
storage eaclstorage.Storage
log *zap.Logger
}
type serviceRequestInfo struct {
group eacl.Group
req serviceRequest
objHdrSrc objectHeadersSource
}
type requestObjHdrSrc struct {
req serviceRequest
ls localstore.Localstore
}
type eaclFromBearer struct {
eaclstorage.Storage
bearer service.BearerToken
}
type requestTarget struct {
group eacl.Group
ir bool
}
var _ requestPreProcessor = (*aclPreProcessor)(nil)
var errMissingSignatures = errors.New("empty signature list")
func (p *aclPreProcessor) preProcess(ctx context.Context, req serviceRequest) error {
if req == nil {
panic(pmEmptyServiceRequest)
}
// fetch ACL info
aclInfo, err := p.aclInfoReceiver.getACLInfo(ctx, req)
if err != nil {
p.log.Warn("can't get acl of the container", zap.Stringer("cid", req.CID()))
return errAccessDenied
}
// check basic ACL permissions
var checkFn func(uint8) bool
switch aclInfo.targetInfo.group {
case eacl.GroupUser:
checkFn = aclInfo.rule.UserAllowed
case eacl.GroupSystem:
checkFn = aclInfo.rule.SystemAllowed
case eacl.GroupOthers:
checkFn = aclInfo.rule.OthersAllowed
default:
panic(fmt.Sprintf("unknown request group (aclPreProcessor): %d", aclInfo.targetInfo.group))
}
if requestType := req.Type(); !checkFn(requestACLSection(requestType)) ||
aclInfo.targetInfo.ir && !allowedInnerRingRequest(requestType) {
return errAccessDenied
}
if aclInfo.targetInfo.group != eacl.GroupSystem &&
aclInfo.rule.Sticky() &&
!checkObjectRequestOwnerMatch(req) {
return errAccessDenied
}
if !aclInfo.checkBearer && !aclInfo.checkExtended {
return nil
}
actionParams := requestActionParams{
eaclSrc: p.extACLSource,
request: req,
objHdrSrc: &requestObjHdrSrc{
req: req,
ls: p.localStore,
},
group: aclInfo.targetInfo.group,
}
if aclInfo.checkBearer {
bearer := req.GetBearerToken()
if err := p.bearerVerifier.verifyBearerToken(ctx, req.CID(), bearer); err != nil {
p.log.Warn("bearer token verification failure",
zap.String("error", err.Error()),
)
return errAccessDenied
}
actionParams.eaclSrc = eaclFromBearer{
bearer: bearer,
}
}
if p.reqActionCalc.calculateRequestAction(ctx, actionParams) != eacl.ActionAllow {
return errAccessDenied
}
return nil
}
func (t *targetFinder) Target(ctx context.Context, req serviceRequest) requestTarget {
res := requestTarget{
group: eacl.GroupUnknown,
}
ownerID, ownerKey, err := requestOwner(req)
if err != nil {
t.log.Warn("could not get request owner",
zap.String("error", err.Error()),
)
return res
} else if ownerKey == nil {
t.log.Warn("signature with nil public key detected")
return res
}
// if request from container owner then return GroupUser
isOwner, err := isContainerOwner(t.cnrStorage, req.CID(), ownerID)
if err != nil {
t.log.Warn("can't check container owner", zap.String("err", err.Error()))
return res
} else if isOwner {
res.group = eacl.GroupUser
return res
}
ownerKeyBytes := crypto.MarshalPublicKey(ownerKey)
// if request from inner ring then return GroupSystem
irKeyList, err := t.irKeysRecv.InnerRingKeys()
if err != nil {
t.log.Warn("could not verify the key belongs to the IR node", zap.String("err", err.Error()))
return res
}
for i := range irKeyList {
if bytes.Equal(irKeyList[i], ownerKeyBytes) {
res.group = eacl.GroupSystem
res.ir = true
return res
}
}
// if request from current container node then return GroupSystem
cnr, err := t.cnrLister.ContainerNodesInfo(ctx, req.CID(), 0)
if err != nil {
t.log.Warn("can't get current container list", zap.String("err", err.Error()))
return res
}
for i := range cnr {
if bytes.Equal(cnr[i].PublicKey(), ownerKeyBytes) {
res.group = eacl.GroupSystem
return res
}
}
// if request from previous container node then return GroupSystem
cnr, err = t.cnrLister.ContainerNodesInfo(ctx, req.CID(), 1)
if err != nil {
t.log.Warn("can't get previous container list", zap.String("err", err.Error()))
return res
}
for i := range cnr {
if bytes.Equal(cnr[i].PublicKey(), ownerKeyBytes) {
res.group = eacl.GroupSystem
return res
}
}
res.group = eacl.GroupOthers
// if none of the above return GroupOthers
return res
}
func checkObjectRequestOwnerMatch(req serviceRequest) bool {
rt := req.Type()
// ignore all request types except Put and Delete
if rt != object.RequestPut && rt != object.RequestDelete {
return true
}
// get request owner
reqOwner, _, err := requestOwner(req)
if err != nil {
return false
}
var payloadOwner OwnerID
// get owner from request payload
if rt == object.RequestPut {
obj := req.(transport.PutInfo).GetHead()
if obj == nil {
return false
}
payloadOwner = obj.GetSystemHeader().OwnerID
} else {
payloadOwner = req.(*object.DeleteRequest).OwnerID
}
return reqOwner.Equal(payloadOwner)
}
// FIXME: this solution only works with healthy key-to-owner conversion.
func requestOwner(req serviceRequest) (OwnerID, *ecdsa.PublicKey, error) {
// if session token exists => return its owner
if token := req.GetSessionToken(); token != nil {
return token.GetOwnerID(), crypto.UnmarshalPublicKey(token.GetOwnerKey()), nil
}
signKeys := req.GetSignKeyPairs()
if len(signKeys) == 0 {
return OwnerID{}, nil, errMissingSignatures
}
firstKey := signKeys[0].GetPublicKey()
if firstKey == nil {
return OwnerID{}, nil, crypto.ErrEmptyPublicKey
}
owner, err := refs.NewOwnerID(firstKey)
return owner, firstKey, err
}
// HeadersOfType returns request or object headers.
func (s serviceRequestInfo) HeadersOfType(typ eacl.HeaderType) ([]eacl.Header, bool) {
switch typ {
default:
return nil, true
case eacl.HdrTypeRequest:
return TypedHeaderSourceFromExtendedHeaders(s.req).HeadersOfType(typ)
case eacl.HdrTypeObjSys, eacl.HdrTypeObjUsr:
obj, ok := s.objHdrSrc.getHeaders()
if !ok {
return nil, false
}
return TypedHeaderSourceFromObject(obj).HeadersOfType(typ)
}
}
// Key returns a binary representation of sender public key.
func (s serviceRequestInfo) Key() []byte {
_, key, err := requestOwner(s.req)
if err != nil {
return nil
}
return crypto.MarshalPublicKey(key)
}
// TypeOf returns true of object request type corresponds to passed OperationType.
func (s serviceRequestInfo) OperationType() eacl.OperationType {
switch t := s.req.Type(); t {
case object.RequestGet:
return eacl.OpTypeGet
case object.RequestPut:
return eacl.OpTypePut
case object.RequestHead:
return eacl.OpTypeHead
case object.RequestSearch:
return eacl.OpTypeSearch
case object.RequestDelete:
return eacl.OpTypeDelete
case object.RequestRange:
return eacl.OpTypeRange
case object.RequestRangeHash:
return eacl.OpTypeRangeHash
default:
panic(fmt.Sprintf("unknown request type (serviceRequestInfo): %d", t))
}
}
// Group returns the access group of the request.
func (s serviceRequestInfo) Group() eacl.Group {
return s.group
}
// CID returns the container identifier of request.
func (s serviceRequestInfo) CID() CID {
return s.req.CID()
}
func (s requestObjHdrSrc) getHeaders() (*Object, bool) {
switch s.req.Type() {
case object.RequestSearch:
// object header filters is not supported in Search request now
return nil, true
case object.RequestPut:
// for Put we get object headers from request
return s.req.(transport.PutInfo).GetHead(), true
default:
tReq := &transportRequest{
serviceRequest: s.req,
}
// for other requests we get object headers from local storage
m, err := s.ls.Meta(tReq.GetAddress())
if err == nil {
return m.GetObject(), true
}
return nil, false
}
}
type requestActionParams struct {
eaclSrc eaclstorage.Storage
request serviceRequest
objHdrSrc objectHeadersSource
group eacl.Group
}
func (s reqActionCalc) calculateRequestAction(ctx context.Context, p requestActionParams) eacl.Action {
// build eACL validator
validator, err := eaclcheck.NewValidator(p.eaclSrc, s.log)
if err != nil {
s.log.Warn("could not build eacl acl validator",
zap.String("error", err.Error()),
)
return eacl.ActionUnknown
}
// create RequestInfo instance
reqInfo := &serviceRequestInfo{
group: p.group,
req: p.request,
objHdrSrc: p.objHdrSrc,
}
// calculate ACL action
return validator.CalculateAction(reqInfo)
}
func (s aclInfoReceiver) getACLInfo(ctx context.Context, req serviceRequest) (*aclInfo, error) {
cnr, err := s.cnrStorage.Get(req.CID())
if err != nil {
return nil, err
}
rule := cnr.BasicACL()
isBearer := rule.BearerAllowed(requestACLSection(req.Type()))
// fetch group from the request
t := s.targetFinder.Target(ctx, req)
return &aclInfo{
rule: rule,
checkExtended: !rule.Final(),
targetInfo: t,
checkBearer: t.group != eacl.GroupSystem && isBearer && req.GetBearerToken() != nil,
}, nil
}
func (s eaclFromBearer) GetEACL(cid CID) (eaclstorage.Table, error) {
return eacl.UnmarshalTable(s.bearer.GetACLRules())
}
// returns true if request of type argument is allowed for IR needs (audit).
func allowedInnerRingRequest(t object.RequestType) (res bool) {
switch t {
case
object.RequestSearch,
object.RequestHead,
object.RequestRangeHash:
res = true
}
return
}
// returns the index number of request section bits.
func requestACLSection(t object.RequestType) uint8 {
switch t {
case object.RequestRangeHash:
return 0
case object.RequestRange:
return 1
case object.RequestSearch:
return 2
case object.RequestDelete:
return 3
case object.RequestPut:
return 4
case object.RequestHead:
return 5
case object.RequestGet:
return 6
default:
panic(fmt.Sprintf("unknown request type (requestACLSection): %d", t))
}
}
type objectHeaderSource struct {
obj *Object
}
type typedHeader struct {
n string
v string
t eacl.HeaderType
}
type extendedHeadersWrapper struct {
hdrSrc service.ExtendedHeadersSource
}
type typedExtendedHeader struct {
hdr service.ExtendedHeader
}
func newTypedObjSysHdr(name, value string) eacl.TypedHeader {
return &typedHeader{
n: name,
v: value,
t: eacl.HdrTypeObjSys,
}
}
// Name is a name field getter.
func (s typedHeader) Name() string {
return s.n
}
// Value is a value field getter.
func (s typedHeader) Value() string {
return s.v
}
// HeaderType is a type field getter.
func (s typedHeader) HeaderType() eacl.HeaderType {
return s.t
}
// TypedHeaderSourceFromObject wraps passed object and returns TypedHeaderSource interface.
func TypedHeaderSourceFromObject(obj *object.Object) extended.TypedHeaderSource {
return &objectHeaderSource{
obj: obj,
}
}
// HeaderOfType gathers object headers of passed type and returns Header list.
//
// If value of some header can not be calculated (e.g. nil eacl header), it does not appear in list.
//
// Always returns true.
func (s objectHeaderSource) HeadersOfType(typ eacl.HeaderType) ([]eacl.Header, bool) {
if s.obj == nil {
return nil, true
}
var res []eacl.Header
switch typ {
case eacl.HdrTypeObjUsr:
objHeaders := s.obj.GetHeaders()
res = make([]eacl.Header, 0, len(objHeaders)) // 7 system header fields
for i := range objHeaders {
if h := newTypedObjectExtendedHeader(objHeaders[i]); h != nil {
res = append(res, h)
}
}
case eacl.HdrTypeObjSys:
res = make([]eacl.Header, 0, 7)
sysHdr := s.obj.GetSystemHeader()
created := sysHdr.GetCreatedAt()
res = append(res,
// ID
newTypedObjSysHdr(
eacl.HdrObjSysNameID,
sysHdr.ID.String(),
),
// CID
newTypedObjSysHdr(
eacl.HdrObjSysNameCID,
sysHdr.CID.String(),
),
// OwnerID
newTypedObjSysHdr(
eacl.HdrObjSysNameOwnerID,
sysHdr.OwnerID.String(),
),
// Version
newTypedObjSysHdr(
eacl.HdrObjSysNameVersion,
strconv.FormatUint(sysHdr.GetVersion(), 10),
),
// PayloadLength
newTypedObjSysHdr(
eacl.HdrObjSysNamePayloadLength,
strconv.FormatUint(sysHdr.GetPayloadLength(), 10),
),
// CreatedAt.UnitTime
newTypedObjSysHdr(
eacl.HdrObjSysNameCreatedUnix,
strconv.FormatUint(uint64(created.GetUnixTime()), 10),
),
// CreatedAt.Epoch
newTypedObjSysHdr(
eacl.HdrObjSysNameCreatedEpoch,
strconv.FormatUint(created.GetEpoch(), 10),
),
)
}
return res, true
}
func newTypedObjectExtendedHeader(h object.Header) eacl.TypedHeader {
val := h.GetValue()
if val == nil {
return nil
}
res := new(typedHeader)
res.t = eacl.HdrTypeObjSys
switch hdr := val.(type) {
case *object.Header_UserHeader:
if hdr.UserHeader == nil {
return nil
}
res.t = eacl.HdrTypeObjUsr
res.n = hdr.UserHeader.GetKey()
res.v = hdr.UserHeader.GetValue()
case *object.Header_Link:
if hdr.Link == nil {
return nil
}
switch hdr.Link.GetType() {
case object.Link_Previous:
res.n = eacl.HdrObjSysLinkPrev
case object.Link_Next:
res.n = eacl.HdrObjSysLinkNext
case object.Link_Child:
res.n = eacl.HdrObjSysLinkChild
case object.Link_Parent:
res.n = eacl.HdrObjSysLinkPar
case object.Link_StorageGroup:
res.n = eacl.HdrObjSysLinkSG
default:
return nil
}
res.v = hdr.Link.ID.String()
default:
return nil
}
return res
}
// TypedHeaderSourceFromExtendedHeaders wraps passed ExtendedHeadersSource and returns TypedHeaderSource interface.
func TypedHeaderSourceFromExtendedHeaders(hdrSrc service.ExtendedHeadersSource) extended.TypedHeaderSource {
return &extendedHeadersWrapper{
hdrSrc: hdrSrc,
}
}
// Name returns the result of Key method.
func (s typedExtendedHeader) Name() string {
return s.hdr.Key()
}
// Value returns the result of Value method.
func (s typedExtendedHeader) Value() string {
return s.hdr.Value()
}
// HeaderType always returns HdrTypeRequest.
func (s typedExtendedHeader) HeaderType() eacl.HeaderType {
return eacl.HdrTypeRequest
}
// TypedHeaders gathers eacl request headers and returns TypedHeader list.
//
// Nil headers are ignored.
//
// Always returns true.
func (s extendedHeadersWrapper) HeadersOfType(typ eacl.HeaderType) ([]eacl.Header, bool) {
if s.hdrSrc == nil {
return nil, true
}
var res []eacl.Header
if typ == eacl.HdrTypeRequest {
hs := s.hdrSrc.ExtendedHeaders()
res = make([]eacl.Header, 0, len(hs))
for i := range hs {
if hs[i] == nil {
continue
}
res = append(res, &typedExtendedHeader{
hdr: hs[i],
})
}
}
return res, true
}
func isContainerOwner(storage storage.Storage, cid CID, ownerID OwnerID) (bool, error) {
cnr, err := storage.Get(cid)
if err != nil {
return false, err
}
return cnr.OwnerID().Equal(ownerID), nil
}

View file

@ -1,575 +0,0 @@
package object
import (
"context"
"crypto/ecdsa"
"errors"
"testing"
eacl "github.com/nspcc-dev/neofs-api-go/acl/extended"
"github.com/nspcc-dev/neofs-api-go/container"
"github.com/nspcc-dev/neofs-api-go/object"
"github.com/nspcc-dev/neofs-api-go/refs"
"github.com/nspcc-dev/neofs-api-go/service"
crypto "github.com/nspcc-dev/neofs-crypto"
libcnr "github.com/nspcc-dev/neofs-node/pkg/core/container"
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic"
"github.com/nspcc-dev/neofs-node/pkg/core/container/storage"
"github.com/nspcc-dev/neofs-node/pkg/core/netmap"
testlogger "github.com/nspcc-dev/neofs-node/pkg/util/logger/test"
"github.com/nspcc-dev/neofs-node/pkg/util/test"
"github.com/stretchr/testify/require"
)
type (
testACLEntity struct {
// Set of interfaces which testCommonEntity must implement, but some methods from those does not call.
serviceRequest
RequestTargeter
eacl.Table
storage.Storage
containerNodesLister
// Argument interceptor. Used for ascertain of correct parameter passage between components.
f func(...interface{})
// Mocked result of any interface.
res interface{}
// Mocked error of any interface.
err error
}
)
type testBasicChecker struct {
actionErr error
action bool
sticky bool
extended bool
bearer bool
}
func (t *testACLEntity) Get(cid storage.CID) (*storage.Container, error) {
if t.err != nil {
return nil, t.err
}
return t.res.(*storage.Container), nil
}
func (t *testACLEntity) calculateRequestAction(context.Context, requestActionParams) eacl.Action {
return t.res.(eacl.Action)
}
func (t *testACLEntity) GetBasicACL(context.Context, CID) (libcnr.BasicACL, error) {
if t.err != nil {
return 0, t.err
}
return t.res.(libcnr.BasicACL), nil
}
func (t *testACLEntity) Target(context.Context, serviceRequest) requestTarget {
return t.res.(requestTarget)
}
func (t *testACLEntity) CID() CID { return CID{} }
func (t *testACLEntity) Type() object.RequestType { return t.res.(object.RequestType) }
func (t *testACLEntity) GetBearerToken() service.BearerToken { return nil }
func (t *testACLEntity) GetOwner() (*ecdsa.PublicKey, error) {
if t.err != nil {
return nil, t.err
}
return t.res.(*ecdsa.PublicKey), nil
}
func (t testACLEntity) InnerRingKeys() ([][]byte, error) {
if t.err != nil {
return nil, t.err
}
return t.res.([][]byte), nil
}
func (t *testACLEntity) ContainerNodesInfo(ctx context.Context, cid CID, prev int) ([]netmap.Info, error) {
if t.err != nil {
return nil, t.err
}
return t.res.([][]netmap.Info)[prev], nil
}
func (t testACLEntity) GetSignKeyPairs() []service.SignKeyPair {
if t.res == nil {
return nil
}
return t.res.([]service.SignKeyPair)
}
func TestPreprocessor(t *testing.T) {
ctx := context.TODO()
t.Run("empty request", func(t *testing.T) {
require.PanicsWithValue(t, pmEmptyServiceRequest, func() {
_ = new(aclPreProcessor).preProcess(ctx, nil)
})
})
t.Run("everything is okay", func(t *testing.T) {
var rule basic.ACL
rule.SetFinal()
rule.AllowOthers(requestACLSection(object.RequestGet))
cnr := new(storage.Container)
cnr.SetBasicACL(rule)
reqTarget := requestTarget{
group: eacl.GroupOthers,
}
preprocessor := aclPreProcessor{
log: testlogger.NewLogger(false),
aclInfoReceiver: aclInfoReceiver{
cnrStorage: &testACLEntity{
res: cnr,
},
targetFinder: &testACLEntity{res: reqTarget},
},
}
require.NoError(t, preprocessor.preProcess(ctx, &testACLEntity{res: object.RequestGet}))
reqTarget.group = eacl.GroupSystem
preprocessor.aclInfoReceiver.targetFinder = &testACLEntity{res: reqTarget}
require.NoError(t, preprocessor.preProcess(ctx, &testACLEntity{res: object.RequestGet}))
reqTarget.group = eacl.GroupUser
preprocessor.aclInfoReceiver.targetFinder = &testACLEntity{res: reqTarget}
require.Error(t, preprocessor.preProcess(ctx, &testACLEntity{res: object.RequestGet}))
})
t.Run("can't fetch container", func(t *testing.T) {
preprocessor := aclPreProcessor{
log: testlogger.NewLogger(false),
aclInfoReceiver: aclInfoReceiver{
cnrStorage: &testACLEntity{err: container.ErrNotFound},
targetFinder: &testACLEntity{res: requestTarget{
group: eacl.GroupOthers,
}},
},
}
require.Error(t, preprocessor.preProcess(ctx, &testACLEntity{res: object.RequestGet}))
})
t.Run("sticky bit", func(t *testing.T) {
var rule basic.ACL
rule.SetSticky()
rule.SetFinal()
for i := uint8(0); i < 7; i++ {
rule.AllowUser(i)
}
cnr := new(storage.Container)
cnr.SetBasicACL(rule)
s := &aclPreProcessor{
log: testlogger.NewLogger(false),
aclInfoReceiver: aclInfoReceiver{
cnrStorage: &testACLEntity{
res: cnr,
},
targetFinder: &testACLEntity{
res: requestTarget{
group: eacl.GroupUser,
},
},
},
}
ownerKey := &test.DecodeKey(0).PublicKey
ownerID, err := refs.NewOwnerID(ownerKey)
require.NoError(t, err)
okItems := []func() []serviceRequest{
// Read requests
func() []serviceRequest {
return []serviceRequest{
new(object.GetRequest),
new(object.HeadRequest),
new(object.SearchRequest),
new(GetRangeRequest),
new(object.GetRangeHashRequest),
}
},
// PutRequest / DeleteRequest (w/o token)
func() []serviceRequest {
req := object.MakePutRequestHeader(&Object{
SystemHeader: SystemHeader{
OwnerID: ownerID,
},
})
req.AddSignKey(nil, ownerKey)
putReq := &putRequest{
PutRequest: req,
}
delReq := new(object.DeleteRequest)
delReq.OwnerID = ownerID
delReq.AddSignKey(nil, ownerKey)
return []serviceRequest{putReq, delReq}
},
// PutRequest / DeleteRequest (w/ token)
func() []serviceRequest {
token := new(service.Token)
token.SetOwnerID(ownerID)
token.SetOwnerKey(crypto.MarshalPublicKey(ownerKey))
req := object.MakePutRequestHeader(&Object{
SystemHeader: SystemHeader{
OwnerID: ownerID,
},
})
req.SetToken(token)
putReq := &putRequest{
PutRequest: req,
}
delReq := new(object.DeleteRequest)
delReq.OwnerID = ownerID
delReq.SetToken(token)
return []serviceRequest{putReq, delReq}
},
}
failItems := []func() []serviceRequest{
// PutRequest / DeleteRequest (w/o token and wrong owner)
func() []serviceRequest {
otherOwner := ownerID
otherOwner[0]++
req := object.MakePutRequestHeader(&Object{
SystemHeader: SystemHeader{
OwnerID: otherOwner,
},
})
req.AddSignKey(nil, ownerKey)
putReq := &putRequest{
PutRequest: req,
}
delReq := new(object.DeleteRequest)
delReq.OwnerID = otherOwner
delReq.AddSignKey(nil, ownerKey)
return []serviceRequest{putReq, delReq}
},
// PutRequest / DeleteRequest (w/ token w/ wrong owner)
func() []serviceRequest {
otherOwner := ownerID
otherOwner[0]++
token := new(service.Token)
token.SetOwnerID(ownerID)
token.SetOwnerKey(crypto.MarshalPublicKey(ownerKey))
req := object.MakePutRequestHeader(&Object{
SystemHeader: SystemHeader{
OwnerID: otherOwner,
},
})
req.SetToken(token)
putReq := &putRequest{
PutRequest: req,
}
delReq := new(object.DeleteRequest)
delReq.OwnerID = otherOwner
delReq.SetToken(token)
return []serviceRequest{putReq, delReq}
},
}
for _, ok := range okItems {
for _, req := range ok() {
require.NoError(t, s.preProcess(ctx, req))
}
}
for _, fail := range failItems {
for _, req := range fail() {
require.Error(t, s.preProcess(ctx, req))
}
}
})
t.Run("eacl ACL", func(t *testing.T) {
target := requestTarget{
group: eacl.GroupOthers,
}
req := &testACLEntity{
res: object.RequestGet,
}
actCalc := new(testACLEntity)
var rule basic.ACL
rule.AllowOthers(requestACLSection(object.RequestGet))
cnr := new(storage.Container)
cnr.SetBasicACL(rule)
s := &aclPreProcessor{
log: testlogger.NewLogger(false),
aclInfoReceiver: aclInfoReceiver{
cnrStorage: &testACLEntity{
res: cnr,
},
targetFinder: &testACLEntity{
res: target,
},
},
reqActionCalc: actCalc,
}
// force to return non-ActionAllow
actCalc.res = eacl.ActionAllow + 1
require.EqualError(t, s.preProcess(ctx, req), errAccessDenied.Error())
// force to return ActionAllow
actCalc.res = eacl.ActionAllow
require.NoError(t, s.preProcess(ctx, req))
})
t.Run("inner ring group", func(t *testing.T) {
reqTarget := requestTarget{
group: eacl.GroupSystem,
ir: true,
}
cnr := new(storage.Container)
cnr.SetBasicACL(basic.FromUint32(^uint32(0)))
preprocessor := aclPreProcessor{
log: testlogger.NewLogger(false),
aclInfoReceiver: aclInfoReceiver{
cnrStorage: &testACLEntity{res: cnr},
targetFinder: &testACLEntity{res: reqTarget},
},
}
for _, rt := range []object.RequestType{
object.RequestSearch,
object.RequestHead,
object.RequestRangeHash,
} {
require.NoError(t,
preprocessor.preProcess(ctx, &testACLEntity{
res: rt,
}),
)
}
for _, rt := range []object.RequestType{
object.RequestRange,
object.RequestPut,
object.RequestDelete,
object.RequestGet,
} {
require.EqualError(t,
preprocessor.preProcess(ctx, &testACLEntity{
res: rt,
}),
errAccessDenied.Error(),
)
}
})
}
func TestTargetFinder(t *testing.T) {
ctx := context.TODO()
irKey := test.DecodeKey(2)
containerKey := test.DecodeKey(3)
prevContainerKey := test.DecodeKey(4)
var infoList1 []netmap.Info
info := netmap.Info{}
info.SetPublicKey(crypto.MarshalPublicKey(&containerKey.PublicKey))
infoList1 = append(infoList1, info)
var infoList2 []netmap.Info
info.SetPublicKey(crypto.MarshalPublicKey(&prevContainerKey.PublicKey))
infoList2 = append(infoList2, info)
finder := &targetFinder{
log: testlogger.NewLogger(false),
irKeysRecv: &testACLEntity{
res: [][]byte{crypto.MarshalPublicKey(&irKey.PublicKey)},
},
cnrLister: &testACLEntity{res: [][]netmap.Info{
infoList1,
infoList2,
}},
}
t.Run("trusted node", func(t *testing.T) {
pk := &test.DecodeKey(0).PublicKey
ownerKey := &test.DecodeKey(1).PublicKey
owner, err := refs.NewOwnerID(ownerKey)
require.NoError(t, err)
token := new(service.Token)
token.SetSessionKey(crypto.MarshalPublicKey(pk))
token.SetOwnerKey(crypto.MarshalPublicKey(ownerKey))
token.SetOwnerID(owner)
req := new(object.SearchRequest)
req.ContainerID = CID{1, 2, 3}
req.SetToken(token)
req.AddSignKey(nil, pk)
cnr := new(storage.Container)
cnr.SetOwnerID(owner)
finder.cnrStorage = &testACLEntity{
res: cnr,
}
require.Equal(t,
requestTarget{
group: eacl.GroupUser,
},
finder.Target(ctx, req),
)
})
t.Run("container owner", func(t *testing.T) {
key := &test.DecodeKey(0).PublicKey
owner, err := refs.NewOwnerID(key)
require.NoError(t, err)
cnr := new(storage.Container)
cnr.SetOwnerID(owner)
finder.cnrStorage = &testACLEntity{res: cnr}
req := new(object.SearchRequest)
req.AddSignKey(nil, key)
require.Equal(t,
requestTarget{
group: eacl.GroupUser,
},
finder.Target(ctx, req),
)
})
t.Run("system owner", func(t *testing.T) {
finder.cnrStorage = &testACLEntity{res: new(storage.Container)}
req := new(object.SearchRequest)
req.AddSignKey(nil, &irKey.PublicKey)
require.Equal(t,
requestTarget{
group: eacl.GroupSystem,
ir: true,
},
finder.Target(ctx, req),
)
req = new(object.SearchRequest)
req.AddSignKey(nil, &containerKey.PublicKey)
require.Equal(t,
requestTarget{
group: eacl.GroupSystem,
},
finder.Target(ctx, req),
)
req = new(object.SearchRequest)
req.AddSignKey(nil, &prevContainerKey.PublicKey)
require.Equal(t,
requestTarget{
group: eacl.GroupSystem,
},
finder.Target(ctx, req),
)
})
t.Run("other owner", func(t *testing.T) {
finder.cnrStorage = &testACLEntity{res: new(storage.Container)}
req := new(object.SearchRequest)
req.AddSignKey(nil, &test.DecodeKey(0).PublicKey)
require.Equal(t,
requestTarget{
group: eacl.GroupOthers,
},
finder.Target(ctx, req),
)
})
t.Run("can't fetch request owner", func(t *testing.T) {
req := new(object.SearchRequest)
require.Equal(t,
requestTarget{
group: eacl.GroupUnknown,
},
finder.Target(ctx, req),
)
})
t.Run("can't fetch container", func(t *testing.T) {
finder.cnrStorage = &testACLEntity{err: container.ErrNotFound}
req := new(object.SearchRequest)
req.AddSignKey(nil, &test.DecodeKey(0).PublicKey)
require.Equal(t,
requestTarget{
group: eacl.GroupUnknown,
},
finder.Target(ctx, req),
)
})
t.Run("can't fetch ir list", func(t *testing.T) {
finder.cnrStorage = &testACLEntity{res: new(storage.Container)}
finder.irKeysRecv = &testACLEntity{err: errors.New("blockchain is busy")}
req := new(object.SearchRequest)
req.AddSignKey(nil, &test.DecodeKey(0).PublicKey)
require.Equal(t,
requestTarget{
group: eacl.GroupUnknown,
},
finder.Target(ctx, req),
)
})
t.Run("can't fetch container list", func(t *testing.T) {
finder.cnrStorage = &testACLEntity{res: new(storage.Container)}
finder.cnrLister = &testACLEntity{err: container.ErrNotFound}
req := new(object.SearchRequest)
req.AddSignKey(nil, &test.DecodeKey(0).PublicKey)
require.Equal(t,
requestTarget{
group: eacl.GroupUnknown,
},
finder.Target(ctx, req),
)
})
}

View file

@ -1,72 +0,0 @@
package object
import (
"context"
"github.com/nspcc-dev/neofs-api-go/service"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/nspcc-dev/neofs-node/pkg/core/container/storage"
"github.com/pkg/errors"
)
type bearerTokenVerifier interface {
verifyBearerToken(context.Context, CID, service.BearerToken) error
}
type complexBearerVerifier struct {
items []bearerTokenVerifier
}
type bearerActualityVerifier struct {
epochRecv EpochReceiver
}
type bearerOwnershipVerifier struct {
cnrStorage storage.Storage
}
type bearerSignatureVerifier struct{}
var errWrongBearerOwner = errors.New("bearer author is not a container owner")
func (s complexBearerVerifier) verifyBearerToken(ctx context.Context, cid CID, token service.BearerToken) error {
for i := range s.items {
if err := s.items[i].verifyBearerToken(ctx, cid, token); err != nil {
return err
}
}
return nil
}
func (s bearerActualityVerifier) verifyBearerToken(_ context.Context, _ CID, token service.BearerToken) error {
local := s.epochRecv.Epoch()
validUntil := token.ExpirationEpoch()
if local > validUntil {
return errors.Errorf("bearer token is expired (local %d, valid until %d)",
local,
validUntil,
)
}
return nil
}
func (s bearerOwnershipVerifier) verifyBearerToken(ctx context.Context, cid CID, token service.BearerToken) error {
isOwner, err := isContainerOwner(s.cnrStorage, cid, token.GetOwnerID())
if err != nil {
return err
} else if !isOwner {
return errWrongBearerOwner
}
return nil
}
func (s bearerSignatureVerifier) verifyBearerToken(_ context.Context, _ CID, token service.BearerToken) error {
return service.VerifySignatureWithKey(
crypto.UnmarshalPublicKey(token.GetOwnerKey()),
service.NewVerifiedBearerToken(token),
)
}

View file

@ -1,19 +0,0 @@
package object
func (s *objectService) RelativeAvailableCap() float64 {
diff := float64(s.ls.Size()) / float64(s.storageCap)
if 1-diff < 0 {
return 0
}
return 1 - diff
}
func (s *objectService) AbsoluteAvailableCap() uint64 {
localSize := uint64(s.ls.Size())
if localSize > s.storageCap {
return 0
}
return s.storageCap - localSize
}

Some files were not shown because too many files have changed in this diff Show more