diff --git a/accounting/fixtures/cheque.sh b/accounting/fixtures/cheque.sh deleted file mode 100755 index 581402a..0000000 --- a/accounting/fixtures/cheque.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -CHEQUE=7849b02d01cc7f7734295fa815ea64ec4d2012e45b8781eb891723ba2703c53263e8d6e522dc32203339dcd8eee9c6b7439a00ea56fa00000000611e000000000000060003012d47e76210aec73be39ab3d186e0a40fe8d86bfa3d4fabfda57ba13b88f96a8ebe4360627a1326f13fb9516c0dbc4af90f116e44bd33f4d04a0d1633afa243ad4f2fa9cd933e7631a619b5132cec6983906aba757af5590434124b232a43e302f7528ce97c9a93558efe7d4f62577aabdf771c931f54a71be6ad21e7d9cc177744b4b9781cf0c29adb503f33d2df9f810ebf33a774849848984cf7e2bbebd48ef0cd8592fbf9b6aee1dc74803e31c95a02dbbd5fd9783f9ecbcbf444b5942f830368a6f5829fb2a34fa03d0308ae6b05f433f2904d9a852fed1f5d2eb598ca794770adb1ece9dccd1c7ad98f709cfb890e3bdd5973dcdd838111fae2efa4c3c09ea2133e5d7c6eac6ae24afcce46db7c9f4dc154f123c835adff4e0b7e19bcffda0340750b92789821683283bcb98e32b7e032b94f267b6964613fc31a7ce5813fddad8298f71dfdc4f9b3e353f969727af476d43b12a25727cf6b9c73ae7152266d995bec807068ad2156288c4d946aeb17ebca787d498a1b87b9dae1bcd935763403fef27f744e829131d0ec980829fafa51db1714c2761d9f78762c008c323e9d66db9b5086d355897e52fe065e14f1cc70334248349aa4c7a3e6e3dc8f8693b1511c73dc88e6d6e8b6c6c68de922f351b5b1543917af2f2a3588aebfbd1ff3fac6023e00f03a16e8707ce045eb42ee80d392451541ee510dc18e1c8befbac54d742648b58f379b5337d9b74c5a61afb8ef3db7f3eb0454d6823777b613a3ee22cd6ce47e4fa72170d49267b773cc09c123654e0bcd7278aa2ae1e7c85d049b557a3c -DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) - -echo $CHEQUE | xxd -p -r > $DIR/cheque_data - -exit 0 diff --git a/accounting/fixtures/cheque_data b/accounting/fixtures/cheque_data deleted file mode 100644 index a153096..0000000 Binary files a/accounting/fixtures/cheque_data and /dev/null differ diff --git a/accounting/service.go b/accounting/service.go deleted file mode 100644 index 229707f..0000000 --- a/accounting/service.go +++ /dev/null @@ -1,46 +0,0 @@ -package accounting - -import ( - "github.com/nspcc-dev/neofs-api-go/decimal" - "github.com/nspcc-dev/neofs-api-go/internal" - "github.com/nspcc-dev/neofs-api-go/refs" -) - -type ( - // OwnerID type alias. - OwnerID = refs.OwnerID - - // Decimal type alias. - Decimal = decimal.Decimal - - // Filter is used to filter accounts by criteria. - Filter func(acc *Account) bool -) - -const ( - // ErrEmptyAddress is raised when passed Address is empty. - ErrEmptyAddress = internal.Error("empty address") - - // ErrEmptyLockTarget is raised when passed LockTarget is empty. - ErrEmptyLockTarget = internal.Error("empty lock target") - - // ErrEmptyContainerID is raised when passed CID is empty. - ErrEmptyContainerID = internal.Error("empty container ID") - - // ErrEmptyParentAddress is raised when passed ParentAddress is empty. - ErrEmptyParentAddress = internal.Error("empty parent address") -) - -// SumFunds goes through all accounts and sums up active funds. -func SumFunds(accounts []*Account) (res *decimal.Decimal) { - res = decimal.Zero.Copy() - - for i := range accounts { - if accounts[i] == nil { - continue - } - - res = res.Add(accounts[i].ActiveFunds) - } - return -} diff --git a/accounting/service.pb.go b/accounting/service.pb.go deleted file mode 100644 index 8dd146b..0000000 Binary files a/accounting/service.pb.go and /dev/null differ diff --git a/accounting/service.proto b/accounting/service.proto deleted file mode 100644 index d1540c4..0000000 --- a/accounting/service.proto +++ /dev/null @@ -1,36 +0,0 @@ -syntax = "proto3"; -package accounting; -option go_package = "github.com/nspcc-dev/neofs-api-go/accounting"; -option csharp_namespace = "NeoFS.API.Accounting"; - -import "service/meta.proto"; -import "service/verify.proto"; -import "decimal/decimal.proto"; -import "accounting/types.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -// Accounting is a service that provides access for accounting balance -// information -service Accounting { - // Balance returns current balance status of the NeoFS user - rpc Balance(BalanceRequest) returns (BalanceResponse); -} - -message BalanceRequest { - // OwnerID is a wallet address - bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message BalanceResponse { - // Balance contains current account balance state - decimal.Decimal Balance = 1; - // LockAccounts contains information about locked funds. Locked funds appear - // when user pays for storage or withdraw assets. - repeated Account LockAccounts = 2; -} diff --git a/accounting/sign.go b/accounting/sign.go deleted file mode 100644 index 1eabed4..0000000 --- a/accounting/sign.go +++ /dev/null @@ -1,167 +0,0 @@ -package accounting - -import ( - "encoding/binary" - "io" - - "github.com/nspcc-dev/neofs-api-go/service" -) - -// SignedData returns payload bytes of the request. -func (m BalanceRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m BalanceRequest) SignedDataSize() int { - return m.GetOwnerID().Size() -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m BalanceRequest) ReadSignedData(p []byte) (int, error) { - sz := m.SignedDataSize() - if len(p) < sz { - return 0, io.ErrUnexpectedEOF - } - - copy(p, m.GetOwnerID().Bytes()) - - return sz, nil -} - -// SignedData returns payload bytes of the request. -func (m GetRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m GetRequest) SignedDataSize() int { - return m.GetID().Size() + m.GetOwnerID().Size() -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m GetRequest) ReadSignedData(p []byte) (int, error) { - sz := m.SignedDataSize() - if len(p) < sz { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.GetID().Bytes()) - - copy(p[off:], m.GetOwnerID().Bytes()) - - return sz, nil -} - -// SignedData returns payload bytes of the request. -func (m PutRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m PutRequest) SignedDataSize() (sz int) { - sz += m.GetOwnerID().Size() - - sz += m.GetMessageID().Size() - - sz += 8 - - if amount := m.GetAmount(); amount != nil { - sz += amount.Size() - } - - return -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m PutRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.GetOwnerID().Bytes()) - - off += copy(p[off:], m.GetMessageID().Bytes()) - - binary.BigEndian.PutUint64(p[off:], m.GetHeight()) - off += 8 - - if amount := m.GetAmount(); amount != nil { - n, err := amount.MarshalTo(p[off:]) - off += n - if err != nil { - return off + n, err - } - } - - return off, nil -} - -// SignedData returns payload bytes of the request. -func (m ListRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m ListRequest) SignedDataSize() int { - return m.GetOwnerID().Size() -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m ListRequest) ReadSignedData(p []byte) (int, error) { - sz := m.SignedDataSize() - if len(p) < sz { - return 0, io.ErrUnexpectedEOF - } - - copy(p, m.GetOwnerID().Bytes()) - - return sz, nil -} - -// SignedData returns payload bytes of the request. -func (m DeleteRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m DeleteRequest) SignedDataSize() (sz int) { - sz += m.GetID().Size() - - sz += m.GetOwnerID().Size() - - sz += m.GetMessageID().Size() - - return -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m DeleteRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.GetID().Bytes()) - - off += copy(p[off:], m.GetOwnerID().Bytes()) - - off += copy(p[off:], m.GetMessageID().Bytes()) - - return off, nil -} diff --git a/accounting/sign_test.go b/accounting/sign_test.go deleted file mode 100644 index ebc683b..0000000 --- a/accounting/sign_test.go +++ /dev/null @@ -1,185 +0,0 @@ -package accounting - -import ( - "testing" - - "github.com/nspcc-dev/neofs-api-go/decimal" - "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/stretchr/testify/require" -) - -func TestSignBalanceRequest(t *testing.T) { - sk := test.DecodeKey(0) - - type sigType interface { - service.RequestData - service.SignKeyPairAccumulator - service.SignKeyPairSource - SetToken(*service.Token) - } - - items := []struct { - constructor func() sigType - payloadCorrupt []func(sigType) - }{ - { // BalanceRequest - constructor: func() sigType { - return new(BalanceRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - req := s.(*BalanceRequest) - - owner := req.GetOwnerID() - owner[0]++ - - req.SetOwnerID(owner) - }, - }, - }, - { // GetRequest - constructor: func() sigType { - return new(GetRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - req := s.(*GetRequest) - - id, err := NewChequeID() - require.NoError(t, err) - - req.SetID(id) - }, - func(s sigType) { - req := s.(*GetRequest) - - id := req.GetOwnerID() - id[0]++ - - req.SetOwnerID(id) - }, - }, - }, - { // PutRequest - constructor: func() sigType { - req := new(PutRequest) - - amount := decimal.New(1) - req.SetAmount(amount) - - return req - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - req := s.(*PutRequest) - - owner := req.GetOwnerID() - owner[0]++ - - req.SetOwnerID(owner) - }, - func(s sigType) { - req := s.(*PutRequest) - - mid := req.GetMessageID() - mid[0]++ - - req.SetMessageID(mid) - }, - func(s sigType) { - req := s.(*PutRequest) - - req.SetHeight(req.GetHeight() + 1) - }, - func(s sigType) { - req := s.(*PutRequest) - - amount := req.GetAmount() - if amount == nil { - req.SetAmount(decimal.New(0)) - } else { - req.SetAmount(amount.Add(decimal.New(amount.GetValue()))) - } - }, - }, - }, - { // ListRequest - constructor: func() sigType { - return new(ListRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - req := s.(*ListRequest) - - owner := req.GetOwnerID() - owner[0]++ - - req.SetOwnerID(owner) - }, - }, - }, - { - constructor: func() sigType { - return new(DeleteRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - req := s.(*DeleteRequest) - - id, err := NewChequeID() - require.NoError(t, err) - - req.SetID(id) - }, - func(s sigType) { - req := s.(*DeleteRequest) - - owner := req.GetOwnerID() - owner[0]++ - - req.SetOwnerID(owner) - }, - func(s sigType) { - req := s.(*DeleteRequest) - - mid := req.GetMessageID() - mid[0]++ - - req.SetMessageID(mid) - }, - }, - }, - } - - for _, item := range items { - { // token corruptions - v := item.constructor() - - token := new(service.Token) - v.SetToken(token) - - require.NoError(t, service.SignRequestData(sk, v)) - - require.NoError(t, service.VerifyRequestData(v)) - - token.SetSessionKey(append(token.GetSessionKey(), 1)) - - require.Error(t, service.VerifyRequestData(v)) - } - - { // payload corruptions - for _, corruption := range item.payloadCorrupt { - v := item.constructor() - - require.NoError(t, service.SignRequestData(sk, v)) - - require.NoError(t, service.VerifyRequestData(v)) - - corruption(v) - - require.Error(t, service.VerifyRequestData(v)) - } - } - } -} diff --git a/accounting/types.go b/accounting/types.go deleted file mode 100644 index e16fa99..0000000 --- a/accounting/types.go +++ /dev/null @@ -1,453 +0,0 @@ -package accounting - -import ( - "crypto/ecdsa" - "crypto/rand" - "encoding/binary" - "reflect" - - "github.com/mr-tron/base58" - "github.com/nspcc-dev/neofs-api-go/chain" - "github.com/nspcc-dev/neofs-api-go/decimal" - "github.com/nspcc-dev/neofs-api-go/internal" - "github.com/nspcc-dev/neofs-api-go/refs" - crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/pkg/errors" -) - -type ( - // Cheque structure that describes a user request for withdrawal of funds. - Cheque struct { - ID ChequeID - Owner refs.OwnerID - Amount *decimal.Decimal - Height uint64 - Signatures []ChequeSignature - } - - // BalanceReceiver interface that is used to retrieve user balance by address. - BalanceReceiver interface { - Balance(accountAddress string) (*Account, error) - } - - // ChequeID is identifier of user request for withdrawal of funds. - ChequeID string - - // CID type alias. - CID = refs.CID - - // SGID type alias. - SGID = refs.SGID - - // ChequeSignature contains public key and hash, and is used to verify signatures. - ChequeSignature struct { - Key *ecdsa.PublicKey - Hash []byte - } -) - -const ( - // ErrWrongSignature is raised when wrong signature is passed. - ErrWrongSignature = internal.Error("wrong signature") - - // ErrWrongPublicKey is raised when wrong public key is passed. - ErrWrongPublicKey = internal.Error("wrong public key") - - // ErrWrongChequeData is raised when passed bytes cannot not be parsed as valid Cheque. - ErrWrongChequeData = internal.Error("wrong cheque data") - - // ErrInvalidLength is raised when passed bytes cannot not be parsed as valid ChequeID. - ErrInvalidLength = internal.Error("invalid length") - - u16size = 2 - u64size = 8 - - signaturesOffset = chain.AddressLength + refs.OwnerIDSize + u64size + u64size -) - -// NewChequeID generates valid random ChequeID using crypto/rand.Reader. -func NewChequeID() (ChequeID, error) { - d := make([]byte, chain.AddressLength) - if _, err := rand.Read(d); err != nil { - return "", err - } - - id := base58.Encode(d) - - return ChequeID(id), nil -} - -// String returns string representation of ChequeID. -func (b ChequeID) String() string { return string(b) } - -// Empty returns true, if ChequeID is empty. -func (b ChequeID) Empty() bool { return len(b) == 0 } - -// Valid validates ChequeID. -func (b ChequeID) Valid() bool { - d, err := base58.Decode(string(b)) - return err == nil && len(d) == chain.AddressLength -} - -// Bytes returns bytes representation of ChequeID. -func (b ChequeID) Bytes() []byte { - d, err := base58.Decode(string(b)) - if err != nil { - return make([]byte, chain.AddressLength) - } - return d -} - -// Equal checks that current ChequeID is equal to passed ChequeID. -func (b ChequeID) Equal(b2 ChequeID) bool { - return b.Valid() && b2.Valid() && string(b) == string(b2) -} - -// Unmarshal tries to parse []byte into valid ChequeID. -func (b *ChequeID) Unmarshal(data []byte) error { - *b = ChequeID(base58.Encode(data)) - if !b.Valid() { - return ErrInvalidLength - } - return nil -} - -// Size returns size (chain.AddressLength). -func (b ChequeID) Size() int { - return chain.AddressLength -} - -// MarshalTo tries to marshal ChequeID into passed bytes and returns -// count of copied bytes or error, if bytes len is not enough to contain ChequeID. -func (b ChequeID) MarshalTo(data []byte) (int, error) { - if len(data) < chain.AddressLength { - return 0, ErrInvalidLength - } - return copy(data, b.Bytes()), nil -} - -// Equals checks that m and tx are valid and equal Tx values. -func (m Tx) Equals(tx Tx) bool { - return m.From == tx.From && - m.To == tx.To && - m.Type == tx.Type && - m.Amount == tx.Amount -} - -// Verify validates current Cheque and Signatures that are generated for current Cheque. -func (b Cheque) Verify() error { - data := b.marshalBody() - for i, sign := range b.Signatures { - if err := crypto.VerifyRFC6979(sign.Key, data, sign.Hash); err != nil { - return errors.Wrapf(ErrWrongSignature, "item #%d: %s", i, err.Error()) - } - } - - return nil -} - -// Sign is used to sign current Cheque and stores result inside b.Signatures. -func (b *Cheque) Sign(key *ecdsa.PrivateKey) error { - hash, err := crypto.SignRFC6979(key, b.marshalBody()) - if err != nil { - return err - } - - b.Signatures = append(b.Signatures, ChequeSignature{ - Key: &key.PublicKey, - Hash: hash, - }) - - return nil -} - -func (b *Cheque) marshalBody() []byte { - buf := make([]byte, signaturesOffset) - - var offset int - - offset += copy(buf, b.ID.Bytes()) - offset += copy(buf[offset:], b.Owner.Bytes()) - - binary.LittleEndian.PutUint64(buf[offset:], uint64(b.Amount.Value)) - offset += u64size - - binary.LittleEndian.PutUint64(buf[offset:], b.Height) - - return buf -} - -func (b *Cheque) unmarshalBody(buf []byte) error { - var offset int - - if len(buf) < signaturesOffset { - return ErrWrongChequeData - } - - { // unmarshal UUID - if err := b.ID.Unmarshal(buf[offset : offset+chain.AddressLength]); err != nil { - return err - } - offset += chain.AddressLength - } - - { // unmarshal OwnerID - if err := b.Owner.Unmarshal(buf[offset : offset+refs.OwnerIDSize]); err != nil { - return err - } - offset += refs.OwnerIDSize - } - - { // unmarshal amount - amount := int64(binary.LittleEndian.Uint64(buf[offset:])) - b.Amount = decimal.New(amount) - offset += u64size - } - - { // unmarshal height - b.Height = binary.LittleEndian.Uint64(buf[offset:]) - offset += u64size - } - - return nil -} - -// MarshalBinary is used to marshal Cheque into bytes. -func (b Cheque) MarshalBinary() ([]byte, error) { - var ( - count = len(b.Signatures) - buf = make([]byte, b.Size()) - offset = copy(buf, b.marshalBody()) - ) - - binary.LittleEndian.PutUint16(buf[offset:], uint16(count)) - offset += u16size - - for _, sign := range b.Signatures { - key := crypto.MarshalPublicKey(sign.Key) - offset += copy(buf[offset:], key) - offset += copy(buf[offset:], sign.Hash) - } - - return buf, nil -} - -// Size returns size of Cheque (count of bytes needs to store it). -func (b Cheque) Size() int { - return signaturesOffset + u16size + - len(b.Signatures)*(crypto.PublicKeyCompressedSize+crypto.RFC6979SignatureSize) -} - -// UnmarshalBinary tries to parse []byte into valid Cheque. -func (b *Cheque) UnmarshalBinary(buf []byte) error { - if err := b.unmarshalBody(buf); err != nil { - return err - } - - body := buf[:signaturesOffset] - - count := int64(binary.LittleEndian.Uint16(buf[signaturesOffset:])) - offset := signaturesOffset + u16size - - if ln := count * int64(crypto.PublicKeyCompressedSize+crypto.RFC6979SignatureSize); ln > int64(len(buf[offset:])) { - return ErrWrongChequeData - } - - for i := int64(0); i < count; i++ { - sign := ChequeSignature{ - Key: crypto.UnmarshalPublicKey(buf[offset : offset+crypto.PublicKeyCompressedSize]), - Hash: make([]byte, crypto.RFC6979SignatureSize), - } - - offset += crypto.PublicKeyCompressedSize - if sign.Key == nil { - return errors.Wrapf(ErrWrongPublicKey, "item #%d", i) - } - - offset += copy(sign.Hash, buf[offset:offset+crypto.RFC6979SignatureSize]) - if err := crypto.VerifyRFC6979(sign.Key, body, sign.Hash); err != nil { - return errors.Wrapf(ErrWrongSignature, "item #%d: %s (offset=%d, len=%d)", i, err.Error(), offset, len(sign.Hash)) - } - - b.Signatures = append(b.Signatures, sign) - } - - return nil -} - -// ErrNotEnoughFunds generates error using address and amounts. -func ErrNotEnoughFunds(addr string, needed, residue *decimal.Decimal) error { - return errors.Errorf("not enough funds (requested=%s, residue=%s, addr=%s", needed, residue, addr) -} - -func (m *Account) hasLockAcc(addr string) bool { - for i := range m.LockAccounts { - if m.LockAccounts[i].Address == addr { - return true - } - } - return false -} - -// ValidateLock checks that account can be locked. -func (m *Account) ValidateLock() error { - switch { - case m.Address == "": - return ErrEmptyAddress - case m.ParentAddress == "": - return ErrEmptyParentAddress - case m.LockTarget == nil: - return ErrEmptyLockTarget - } - - switch v := m.LockTarget.Target.(type) { - case *LockTarget_WithdrawTarget: - if v.WithdrawTarget.Cheque != m.Address { - return errors.Errorf("wrong cheque ID: expected %s, has %s", m.Address, v.WithdrawTarget.Cheque) - } - case *LockTarget_ContainerCreateTarget: - switch { - case v.ContainerCreateTarget.CID.Empty(): - return ErrEmptyContainerID - } - } - return nil -} - -// CanLock checks possibility to lock funds. -func (m *Account) CanLock(lockAcc *Account) error { - switch { - case m.ActiveFunds.LT(lockAcc.ActiveFunds): - return ErrNotEnoughFunds(lockAcc.ParentAddress, lockAcc.ActiveFunds, m.ActiveFunds) - case m.hasLockAcc(lockAcc.Address): - return errors.Errorf("could not lock account(%s) funds: duplicating lock(%s)", m.Address, lockAcc.Address) - default: - return nil - } -} - -// LockForWithdraw checks that account contains locked funds by passed ChequeID. -func (m *Account) LockForWithdraw(chequeID string) bool { - switch v := m.LockTarget.Target.(type) { - case *LockTarget_WithdrawTarget: - return v.WithdrawTarget.Cheque == chequeID - } - return false -} - -// LockForContainerCreate checks that account contains locked funds for container creation. -func (m *Account) LockForContainerCreate(cid refs.CID) bool { - switch v := m.LockTarget.Target.(type) { - case *LockTarget_ContainerCreateTarget: - return v.ContainerCreateTarget.CID.Equal(cid) - } - return false -} - -// Equal checks that current Settlement is equal to passed Settlement. -func (m *Settlement) Equal(s *Settlement) bool { - if s == nil || m.Epoch != s.Epoch || len(m.Transactions) != len(s.Transactions) { - return false - } - return len(m.Transactions) == 0 || reflect.DeepEqual(m.Transactions, s.Transactions) -} - -// GetOwnerID is an OwnerID field getter. -func (m BalanceRequest) GetOwnerID() OwnerID { - return m.OwnerID -} - -// SetOwnerID is an OwnerID field setter. -func (m *BalanceRequest) SetOwnerID(owner OwnerID) { - m.OwnerID = owner -} - -// GetID is an ID field getter. -func (m GetRequest) GetID() ChequeID { - return m.ID -} - -// SetID is an ID field setter. -func (m *GetRequest) SetID(id ChequeID) { - m.ID = id -} - -// GetOwnerID is an OwnerID field getter. -func (m GetRequest) GetOwnerID() OwnerID { - return m.OwnerID -} - -// SetOwnerID is an OwnerID field setter. -func (m *GetRequest) SetOwnerID(id OwnerID) { - m.OwnerID = id -} - -// GetOwnerID is an OwnerID field getter. -func (m PutRequest) GetOwnerID() OwnerID { - return m.OwnerID -} - -// SetOwnerID is an OwnerID field setter. -func (m *PutRequest) SetOwnerID(id OwnerID) { - m.OwnerID = id -} - -// GetMessageID is a MessageID field getter. -func (m PutRequest) GetMessageID() MessageID { - return m.MessageID -} - -// SetMessageID is a MessageID field setter. -func (m *PutRequest) SetMessageID(id MessageID) { - m.MessageID = id -} - -// SetAmount is an Amount field setter. -func (m *PutRequest) SetAmount(amount *decimal.Decimal) { - m.Amount = amount -} - -// SetHeight is a Height field setter. -func (m *PutRequest) SetHeight(h uint64) { - m.Height = h -} - -// GetOwnerID is an OwnerID field getter. -func (m ListRequest) GetOwnerID() OwnerID { - return m.OwnerID -} - -// SetOwnerID is an OwnerID field setter. -func (m *ListRequest) SetOwnerID(id OwnerID) { - m.OwnerID = id -} - -// GetID is an ID field getter. -func (m DeleteRequest) GetID() ChequeID { - return m.ID -} - -// SetID is an ID field setter. -func (m *DeleteRequest) SetID(id ChequeID) { - m.ID = id -} - -// GetOwnerID is an OwnerID field getter. -func (m DeleteRequest) GetOwnerID() OwnerID { - return m.OwnerID -} - -// SetOwnerID is an OwnerID field setter. -func (m *DeleteRequest) SetOwnerID(id OwnerID) { - m.OwnerID = id -} - -// GetMessageID is a MessageID field getter. -func (m DeleteRequest) GetMessageID() MessageID { - return m.MessageID -} - -// SetMessageID is a MessageID field setter. -func (m *DeleteRequest) SetMessageID(id MessageID) { - m.MessageID = id -} diff --git a/accounting/types.pb.go b/accounting/types.pb.go deleted file mode 100644 index 576ba1f..0000000 Binary files a/accounting/types.pb.go and /dev/null differ diff --git a/accounting/types.proto b/accounting/types.proto deleted file mode 100644 index 02fb691..0000000 --- a/accounting/types.proto +++ /dev/null @@ -1,125 +0,0 @@ -syntax = "proto3"; -package accounting; -option go_package = "github.com/nspcc-dev/neofs-api-go/accounting"; -option csharp_namespace = "NeoFS.API.Accounting"; - -import "decimal/decimal.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -message Account { - // OwnerID is a wallet address - bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // Address is identifier of accounting record - string Address = 2; - // ParentAddress is identifier of parent accounting record - string ParentAddress = 3; - // ActiveFunds is amount of active (non locked) funds for account - decimal.Decimal ActiveFunds = 4; - // Lifetime is time until account is valid (used for lock accounts) - Lifetime Lifetime = 5 [(gogoproto.nullable) = false]; - // LockTarget is the purpose of lock funds (it might be withdraw or payment for storage) - LockTarget LockTarget = 6; - // LockAccounts contains child accounts with locked funds - repeated Account LockAccounts = 7; -} - -// LockTarget must be one of two options -message LockTarget { - oneof Target { - // WithdrawTarget used when user requested withdraw - WithdrawTarget WithdrawTarget = 1; - // ContainerCreateTarget used when user requested creation of container - ContainerCreateTarget ContainerCreateTarget = 2; - } -} - -message Balances { - // Accounts contains multiple account snapshots - repeated Account Accounts = 1 [(gogoproto.nullable) = false]; -} - -message PayIO { - // BlockID contains id of the NEO block where withdraw or deposit - // call was invoked - uint64 BlockID = 1; - // Transactions contains all transactions that founded in block - // and used for PayIO - repeated Tx Transactions = 2 [(gogoproto.nullable) = false]; -} - -message Lifetime { - // Unit can be Unlimited, based on NeoFS epoch or Neo block - enum Unit { - Unlimited = 0; - NeoFSEpoch = 1; - NeoBlock = 2; - } - - // Unit describes how lifetime is measured in account - Unit unit = 1 [(gogoproto.customname) = "Unit"]; - // Value describes how long lifetime will be valid - int64 Value = 2; -} - -message Tx { - // Type can be withdrawal, payIO or inner - enum Type { - Unknown = 0; - Withdraw = 1; - PayIO = 2; - Inner = 3; - } - - // Type describes target of transaction - Type type = 1 [(gogoproto.customname) = "Type"]; - // From describes sender of funds - string From = 2; - // To describes receiver of funds - string To = 3; - // Amount describes amount of funds - decimal.Decimal Amount = 4; - // PublicKeys contains public key of sender - bytes PublicKeys = 5; -} - -message Settlement { - message Receiver { - // To is the address of funds recipient - string To = 1; - // Amount is the amount of funds that will be sent - decimal.Decimal Amount = 2; - } - - message Container { - // CID is container identifier - bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false]; - // SGIDs is a set of storage groups that successfully passed the audit - repeated bytes SGIDs = 2 [(gogoproto.customtype) = "SGID", (gogoproto.nullable) = false]; - } - - message Tx { - // From is the address of the sender of funds - string From = 1; - // Container that successfully had passed the audit - Container Container = 2 [(gogoproto.nullable) = false]; - // Receivers is a set of addresses of funds recipients - repeated Receiver Receivers = 3 [(gogoproto.nullable) = false]; - } - - // Epoch contains an epoch when settlement was accepted - uint64 Epoch = 1; - // Transactions is a set of transactions - repeated Tx Transactions = 2; -} - -message ContainerCreateTarget { - // CID is container identifier - bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false]; -} - -message WithdrawTarget { - // Cheque is a string representation of cheque id - string Cheque = 1; -} diff --git a/accounting/types_test.go b/accounting/types_test.go deleted file mode 100644 index a440028..0000000 --- a/accounting/types_test.go +++ /dev/null @@ -1,193 +0,0 @@ -package accounting - -import ( - "io/ioutil" - "testing" - - "github.com/mr-tron/base58" - "github.com/nspcc-dev/neofs-api-go/chain" - "github.com/nspcc-dev/neofs-api-go/decimal" - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/stretchr/testify/require" -) - -func TestCheque(t *testing.T) { - t.Run("new/valid", func(t *testing.T) { - id, err := NewChequeID() - require.NoError(t, err) - require.True(t, id.Valid()) - - d := make([]byte, chain.AddressLength+1) - - // expected size + 1 byte - str := base58.Encode(d) - require.False(t, ChequeID(str).Valid()) - - // expected size - 1 byte - str = base58.Encode(d[:len(d)-2]) - require.False(t, ChequeID(str).Valid()) - - // wrong encoding - d = d[:len(d)-1] // normal size - require.False(t, ChequeID(string(d)).Valid()) - }) - - t.Run("marshal/unmarshal", func(t *testing.T) { - b2 := new(Cheque) - - key1 := test.DecodeKey(0) - key2 := test.DecodeKey(1) - - id, err := NewChequeID() - require.NoError(t, err) - - owner, err := refs.NewOwnerID(&key1.PublicKey) - require.NoError(t, err) - - b1 := &Cheque{ - ID: id, - Owner: owner, - Height: 100, - Amount: decimal.NewGAS(100), - } - - require.NoError(t, b1.Sign(key1)) - require.NoError(t, b1.Sign(key2)) - - data, err := b1.MarshalBinary() - require.NoError(t, err) - - require.Len(t, data, b1.Size()) - require.NoError(t, b2.UnmarshalBinary(data)) - require.Equal(t, b1, b2) - - require.NoError(t, b1.Verify()) - require.NoError(t, b2.Verify()) - }) - - t.Run("example from SC", func(t *testing.T) { - pathToCheque := "fixtures/cheque_data" - expect, err := ioutil.ReadFile(pathToCheque) - require.NoError(t, err) - - var cheque Cheque - require.NoError(t, cheque.UnmarshalBinary(expect)) - - actual, err := cheque.MarshalBinary() - require.NoError(t, err) - - require.Equal(t, expect, actual) - - require.NoError(t, cheque.Verify()) - require.Equal(t, cheque.Height, uint64(7777)) - require.Equal(t, cheque.Amount, decimal.NewGAS(42)) - }) -} - -func TestBalanceRequest_SetOwnerID(t *testing.T) { - ownerID := OwnerID{1, 2, 3} - m := new(BalanceRequest) - - m.SetOwnerID(ownerID) - - require.Equal(t, ownerID, m.GetOwnerID()) -} - -func TestGetRequestGettersSetters(t *testing.T) { - t.Run("id", func(t *testing.T) { - id := ChequeID("test id") - m := new(GetRequest) - - m.SetID(id) - - require.Equal(t, id, m.GetID()) - }) - - t.Run("owner", func(t *testing.T) { - id := OwnerID{1, 2, 3} - m := new(GetRequest) - - m.SetOwnerID(id) - - require.Equal(t, id, m.GetOwnerID()) - }) -} - -func TestPutRequestGettersSetters(t *testing.T) { - t.Run("owner", func(t *testing.T) { - id := OwnerID{1, 2, 3} - m := new(PutRequest) - - m.SetOwnerID(id) - - require.Equal(t, id, m.GetOwnerID()) - }) - - t.Run("message ID", func(t *testing.T) { - id, err := refs.NewUUID() - require.NoError(t, err) - - m := new(PutRequest) - m.SetMessageID(id) - - require.Equal(t, id, m.GetMessageID()) - }) - - t.Run("amount", func(t *testing.T) { - amount := decimal.New(1) - m := new(PutRequest) - - m.SetAmount(amount) - - require.Equal(t, amount, m.GetAmount()) - }) - - t.Run("height", func(t *testing.T) { - h := uint64(3) - m := new(PutRequest) - - m.SetHeight(h) - - require.Equal(t, h, m.GetHeight()) - }) -} - -func TestListRequestGettersSetters(t *testing.T) { - ownerID := OwnerID{1, 2, 3} - m := new(ListRequest) - - m.SetOwnerID(ownerID) - - require.Equal(t, ownerID, m.GetOwnerID()) -} - -func TestDeleteRequestGettersSetters(t *testing.T) { - t.Run("id", func(t *testing.T) { - id := ChequeID("test id") - m := new(DeleteRequest) - - m.SetID(id) - - require.Equal(t, id, m.GetID()) - }) - - t.Run("owner", func(t *testing.T) { - id := OwnerID{1, 2, 3} - m := new(DeleteRequest) - - m.SetOwnerID(id) - - require.Equal(t, id, m.GetOwnerID()) - }) - - t.Run("message ID", func(t *testing.T) { - id, err := refs.NewUUID() - require.NoError(t, err) - - m := new(DeleteRequest) - m.SetMessageID(id) - - require.Equal(t, id, m.GetMessageID()) - }) -} diff --git a/accounting/withdraw.go b/accounting/withdraw.go deleted file mode 100644 index 1077395..0000000 --- a/accounting/withdraw.go +++ /dev/null @@ -1,35 +0,0 @@ -package accounting - -import ( - "encoding/binary" - - "github.com/nspcc-dev/neofs-api-go/refs" -) - -type ( - // MessageID type alias. - MessageID = refs.MessageID -) - -// PrepareData prepares bytes representation of PutRequest to satisfy SignedRequest interface. -func (m *PutRequest) PrepareData() ([]byte, error) { - var offset int - // MessageID-len + OwnerID-len + Amount + Height - buf := make([]byte, refs.UUIDSize+refs.OwnerIDSize+binary.MaxVarintLen64+binary.MaxVarintLen64) - offset += copy(buf[offset:], m.MessageID.Bytes()) - offset += copy(buf[offset:], m.OwnerID.Bytes()) - offset += binary.PutVarint(buf[offset:], m.Amount.Value) - binary.PutUvarint(buf[offset:], m.Height) - return buf, nil -} - -// PrepareData prepares bytes representation of DeleteRequest to satisfy SignedRequest interface. -func (m *DeleteRequest) PrepareData() ([]byte, error) { - var offset int - // ID-len + OwnerID-len + MessageID-len - buf := make([]byte, refs.UUIDSize+refs.OwnerIDSize+refs.UUIDSize) - offset += copy(buf[offset:], m.ID.Bytes()) - offset += copy(buf[offset:], m.OwnerID.Bytes()) - copy(buf[offset:], m.MessageID.Bytes()) - return buf, nil -} diff --git a/accounting/withdraw.pb.go b/accounting/withdraw.pb.go deleted file mode 100644 index e0825f5..0000000 Binary files a/accounting/withdraw.pb.go and /dev/null differ diff --git a/accounting/withdraw.proto b/accounting/withdraw.proto deleted file mode 100644 index 080a100..0000000 --- a/accounting/withdraw.proto +++ /dev/null @@ -1,101 +0,0 @@ -syntax = "proto3"; -package accounting; -option go_package = "github.com/nspcc-dev/neofs-api-go/accounting"; -option csharp_namespace = "NeoFS.API.Accounting"; - -import "service/meta.proto"; -import "service/verify.proto"; -import "decimal/decimal.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -// Withdraw is a service that provides withdraw assets operations from the NeoFS -service Withdraw { - // Get returns cheque if it was signed by inner ring nodes - rpc Get(GetRequest) returns (GetResponse); - // Put ask inner ring nodes to sign a cheque for withdraw invoke - rpc Put(PutRequest) returns (PutResponse); - // List shows all user's checks - rpc List(ListRequest) returns (ListResponse); - // Delete allows user to remove unused cheque - rpc Delete(DeleteRequest) returns (DeleteResponse); -} - -message Item { - // ID is a cheque identifier - bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false]; - // OwnerID is a wallet address - bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // Amount of funds - decimal.Decimal Amount = 3; - // Height is the neo blockchain height until the cheque is valid - uint64 Height = 4; - // Payload contains cheque representation in bytes - bytes Payload = 5; -} - -message GetRequest { - // ID is cheque identifier - bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false]; - // OwnerID is a wallet address - bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message GetResponse { - // Withdraw is cheque with meta information - Item Withdraw = 1; -} - -message PutRequest { - // OwnerID is a wallet address - bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // Amount of funds - decimal.Decimal Amount = 2; - // Height is the neo blockchain height until the cheque is valid - uint64 Height = 3; - // MessageID is a nonce for uniq request (UUIDv4) - bytes MessageID = 4 [(gogoproto.customtype) = "MessageID", (gogoproto.nullable) = false]; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} -message PutResponse { - // ID is cheque identifier - bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false]; -} - -message ListRequest { - // OwnerID is a wallet address - bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message ListResponse { - // Items is a set of cheques with meta information - repeated Item Items = 1; -} - -message DeleteRequest { - // ID is cheque identifier - bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false]; - // OwnerID is a wallet address - bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // MessageID is a nonce for uniq request (UUIDv4) - bytes MessageID = 3 [(gogoproto.customtype) = "MessageID", (gogoproto.nullable) = false]; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -// DeleteResponse is empty -message DeleteResponse {} diff --git a/acl/extended/enum.go b/acl/extended/enum.go deleted file mode 100644 index e803476..0000000 --- a/acl/extended/enum.go +++ /dev/null @@ -1,126 +0,0 @@ -package eacl - -const ( - // MatchUnknown is a MatchType value used to mark value as undefined. - // Most of the tools consider MatchUnknown as incalculable. - // Using MatchUnknown in HeaderFilter is unsafe. - MatchUnknown MatchType = iota - - // StringEqual is a MatchType of string equality. - StringEqual - - // StringNotEqual is a MatchType of string inequality. - StringNotEqual -) - -const ( - // ActionUnknown is Action used to mark value as undefined. - // Most of the tools consider ActionUnknown as incalculable. - // Using ActionUnknown in Record is unsafe. - ActionUnknown Action = iota - - // ActionAllow is Action used to mark an applicability of ACL rule. - ActionAllow - - // ActionDeny is Action used to mark an inapplicability of ACL rule. - ActionDeny -) - -const ( - // GroupUnknown is a Group value used to mark value as undefined. - // Most of the tools consider GroupUnknown as incalculable. - // Using GroupUnknown in Target is unsafe. - GroupUnknown Group = iota - - // GroupUser is a Group value for User access group. - GroupUser - - // GroupSystem is a Group value for System access group. - GroupSystem - - // GroupOthers is a Group value for Others access group. - GroupOthers -) - -const ( - // HdrTypeUnknown is a HeaderType value used to mark value as undefined. - // Most of the tools consider HdrTypeUnknown as incalculable. - // Using HdrTypeUnknown in HeaderFilter is unsafe. - HdrTypeUnknown HeaderType = iota - - // HdrTypeRequest is a HeaderType for request header. - HdrTypeRequest - - // HdrTypeObjSys is a HeaderType for system headers of object. - HdrTypeObjSys - - // HdrTypeObjUsr is a HeaderType for user headers of object. - HdrTypeObjUsr -) - -const ( - // OpTypeUnknown is a OperationType value used to mark value as undefined. - // Most of the tools consider OpTypeUnknown as incalculable. - // Using OpTypeUnknown in Record is unsafe. - OpTypeUnknown OperationType = iota - - // OpTypeGet is an OperationType for object.Get RPC - OpTypeGet - - // OpTypePut is an OperationType for object.Put RPC - OpTypePut - - // OpTypeHead is an OperationType for object.Head RPC - OpTypeHead - - // OpTypeSearch is an OperationType for object.Search RPC - OpTypeSearch - - // OpTypeDelete is an OperationType for object.Delete RPC - OpTypeDelete - - // OpTypeRange is an OperationType for object.GetRange RPC - OpTypeRange - - // OpTypeRangeHash is an OperationType for object.GetRangeHash RPC - OpTypeRangeHash -) - -const ( - // HdrObjSysNameID is a name of ID field in system header of object. - HdrObjSysNameID = "ID" - - // HdrObjSysNameCID is a name of CID field in system header of object. - HdrObjSysNameCID = "CID" - - // HdrObjSysNameOwnerID is a name of OwnerID field in system header of object. - HdrObjSysNameOwnerID = "OWNER_ID" - - // HdrObjSysNameVersion is a name of Version field in system header of object. - HdrObjSysNameVersion = "VERSION" - - // HdrObjSysNamePayloadLength is a name of PayloadLength field in system header of object. - HdrObjSysNamePayloadLength = "PAYLOAD_LENGTH" - - // HdrObjSysNameCreatedUnix is a name of CreatedAt.UnitTime field in system header of object. - HdrObjSysNameCreatedUnix = "CREATED_UNIX" - - // HdrObjSysNameCreatedEpoch is a name of CreatedAt.Epoch field in system header of object. - HdrObjSysNameCreatedEpoch = "CREATED_EPOCH" - - // HdrObjSysLinkPrev is a name of previous link header in extended headers of object. - HdrObjSysLinkPrev = "LINK_PREV" - - // HdrObjSysLinkNext is a name of next link header in extended headers of object. - HdrObjSysLinkNext = "LINK_NEXT" - - // HdrObjSysLinkChild is a name of child link header in extended headers of object. - HdrObjSysLinkChild = "LINK_CHILD" - - // HdrObjSysLinkPar is a name of parent link header in extended headers of object. - HdrObjSysLinkPar = "LINK_PAR" - - // HdrObjSysLinkSG is a name of storage group link header in extended headers of object. - HdrObjSysLinkSG = "LINK_SG" -) - diff --git a/acl/extended/marshal.go b/acl/extended/marshal.go deleted file mode 100644 index d1fac07..0000000 --- a/acl/extended/marshal.go +++ /dev/null @@ -1,333 +0,0 @@ -package eacl - -import ( - "encoding/binary" - - "github.com/pkg/errors" -) - -const ( - sliceLenSize = 2 // uint16 for len() - actionSize = 4 // uint32 - opTypeSize = 4 // uint32 - hdrTypeSize = 4 // uint32 - matchTypeSize = 4 // uint32 - targetSize = 4 // uint32 -) - -// MarshalTable encodes Table into a -// binary form and returns the result. -// -// If table is nil, empty slice is returned. -func MarshalTable(table Table) []byte { - if table == nil { - return make([]byte, 0) - } - - // allocate buffer - buf := make([]byte, tableBinSize(table)) - - records := table.Records() - - // write record number - binary.BigEndian.PutUint16(buf, uint16(len(records))) - off := sliceLenSize - - // write all records - for _, record := range records { - // write action - binary.BigEndian.PutUint32(buf[off:], uint32(record.Action())) - off += actionSize - - // write operation type - binary.BigEndian.PutUint32(buf[off:], uint32(record.OperationType())) - off += actionSize - - filters := record.HeaderFilters() - - // write filter number - binary.BigEndian.PutUint16(buf[off:], uint16(len(filters))) - off += sliceLenSize - - // write all filters - for _, filter := range filters { - // write header type - binary.BigEndian.PutUint32(buf[off:], uint32(filter.HeaderType())) - off += hdrTypeSize - - // write match type - binary.BigEndian.PutUint32(buf[off:], uint32(filter.MatchType())) - off += matchTypeSize - - // write header name size - name := []byte(filter.Name()) - binary.BigEndian.PutUint16(buf[off:], uint16(len(name))) - off += sliceLenSize - - // write header name bytes - off += copy(buf[off:], name) - - // write header value size - val := []byte(filter.Value()) - binary.BigEndian.PutUint16(buf[off:], uint16(len(val))) - off += sliceLenSize - - // write header value bytes - off += copy(buf[off:], val) - } - - targets := record.TargetList() - - // write target number - binary.BigEndian.PutUint16(buf[off:], uint16(len(targets))) - off += sliceLenSize - - // write all targets - for _, target := range targets { - // write target group - binary.BigEndian.PutUint32(buf[off:], uint32(target.Group())) - off += targetSize - - keys := target.KeyList() - - // write key number - binary.BigEndian.PutUint16(buf[off:], uint16(len(keys))) - off += sliceLenSize - - // write keys - for i := range keys { - // write key size - binary.BigEndian.PutUint16(buf[off:], uint16(len(keys[i]))) - off += sliceLenSize - - // write key bytes - off += copy(buf[off:], keys[i]) - } - } - } - - return buf -} - -// returns the size of Table in a binary format. -func tableBinSize(table Table) (sz int) { - sz = sliceLenSize // number of records - - records := table.Records() - ln := len(records) - - sz += ln * actionSize // action type of each record - sz += ln * opTypeSize // operation type of each record - - for _, record := range records { - sz += sliceLenSize // number of filters - - filters := record.HeaderFilters() - ln := len(filters) - - sz += ln * hdrTypeSize // header type of each filter - sz += ln * matchTypeSize // match type of each filter - - for _, filter := range filters { - sz += sliceLenSize // header name size - sz += len(filter.Name()) // header name bytes - - sz += sliceLenSize // header value size - sz += len(filter.Value()) // header value bytes - } - - sz += sliceLenSize // number of targets - - targets := record.TargetList() - ln = len(targets) - - sz += ln * targetSize // target group of each target - - for _, target := range targets { - sz += sliceLenSize // number of keys - - for _, key := range target.KeyList() { - sz += sliceLenSize // key size - sz += len(key) // key bytes - } - } - } - - return -} - -// UnmarshalTable unmarshals Table from -// a binary representation. -// -// If data is empty, table w/o records is returned. -func UnmarshalTable(data []byte) (Table, error) { - table := WrapTable(nil) - - if len(data) == 0 { - return table, nil - } - - // decode record number - if len(data) < sliceLenSize { - return nil, errors.New("could not decode record number") - } - - recordNum := binary.BigEndian.Uint16(data) - records := make([]Record, 0, recordNum) - - off := sliceLenSize - - // decode all records one by one - for i := uint16(0); i < recordNum; i++ { - record := WrapRecord(nil) - - // decode action - if len(data[off:]) < actionSize { - return nil, errors.Errorf("could not decode action of record #%d", i) - } - - record.SetAction(Action(binary.BigEndian.Uint32(data[off:]))) - off += actionSize - - // decode operation type - if len(data[off:]) < opTypeSize { - return nil, errors.Errorf("could not decode operation type of record #%d", i) - } - - record.SetOperationType(OperationType(binary.BigEndian.Uint32(data[off:]))) - off += opTypeSize - - // decode filter number - if len(data[off:]) < sliceLenSize { - return nil, errors.Errorf("could not decode filter number of record #%d", i) - } - - filterNum := binary.BigEndian.Uint16(data[off:]) - off += sliceLenSize - filters := make([]HeaderFilter, 0, filterNum) - - // decode filters one by one - for j := uint16(0); j < filterNum; j++ { - filter := WrapFilterInfo(nil) - - // decode header type - if len(data[off:]) < hdrTypeSize { - return nil, errors.Errorf("could not decode header type of filter #%d of record #%d", j, i) - } - - filter.SetHeaderType(HeaderType(binary.BigEndian.Uint32(data[off:])) ) - off += hdrTypeSize - - // decode match type - if len(data[off:]) < matchTypeSize { - return nil, errors.Errorf("could not decode match type of filter #%d of record #%d", j, i) - } - - filter.SetMatchType(MatchType(binary.BigEndian.Uint32(data[off:])) ) - off += matchTypeSize - - // decode header name size - if len(data[off:]) < sliceLenSize { - return nil, errors.Errorf("could not decode header name size of filter #%d of record #%d", j, i) - } - - hdrNameSize := int(binary.BigEndian.Uint16(data[off:])) - off += sliceLenSize - - // decode header name - if len(data[off:]) < hdrNameSize { - return nil, errors.Errorf("could not decode header name of filter #%d of record #%d", j, i) - } - - filter.SetName(string(data[off : off+hdrNameSize])) - - off += hdrNameSize - - // decode header value size - if len(data[off:]) < sliceLenSize { - return nil, errors.Errorf("could not decode header value size of filter #%d of record #%d", j, i) - } - - hdrValSize := int(binary.BigEndian.Uint16(data[off:])) - off += sliceLenSize - - // decode header value - if len(data[off:]) < hdrValSize { - return nil, errors.Errorf("could not decode header value of filter #%d of record #%d", j, i) - } - - filter.SetValue(string(data[off : off+hdrValSize])) - - off += hdrValSize - - filters = append(filters, filter) - } - - record.SetHeaderFilters(filters) - - // decode target number - if len(data[off:]) < sliceLenSize { - return nil, errors.Errorf("could not decode target number of record #%d", i) - } - - targetNum := int(binary.BigEndian.Uint16(data[off:])) - off += sliceLenSize - - targets := make([]Target, 0, targetNum) - - // decode targets one by one - for j := 0; j < targetNum; j++ { - target := WrapTarget(nil) - - // decode target group - if len(data[off:]) < targetSize { - return nil, errors.Errorf("could not decode target group of target #%d of record #%d", j, i) - } - - target.SetGroup( Group(binary.BigEndian.Uint32(data[off:])), ) - off += targetSize - - // decode key number - if len(data[off:]) < sliceLenSize { - return nil, errors.Errorf("could not decode key number of target #%d of record #%d", j, i) - } - - keyNum := int(binary.BigEndian.Uint16(data[off:])) - off += sliceLenSize - keys := make([][]byte, 0, keyNum) - - for k := 0; k < keyNum; k++ { - // decode key size - if len(data[off:]) < sliceLenSize { - return nil, errors.Errorf("could not decode size of key #%d target #%d of record #%d", k, j, i) - } - - keySz := int(binary.BigEndian.Uint16(data[off:])) - off += sliceLenSize - - // decode key - if len(data[off:]) < keySz { - return nil, errors.Errorf("could not decode key #%d target #%d of record #%d", k, j, i) - } - - key := make([]byte, keySz) - - off += copy(key, data[off:off+keySz]) - - keys = append(keys, key) - } - - target.SetKeyList(keys) - - targets = append(targets, target) - } - - record.SetTargetList(targets) - - records = append(records, record) - } - - table.SetRecords(records) - - return table, nil -} diff --git a/acl/extended/types.go b/acl/extended/types.go deleted file mode 100644 index 6b1b74b..0000000 --- a/acl/extended/types.go +++ /dev/null @@ -1,73 +0,0 @@ -package eacl - -// OperationType is an enumeration of operation types for extended ACL. -type OperationType uint32 - -// HeaderType is an enumeration of header types for extended ACL. -type HeaderType uint32 - -// MatchType is an enumeration of match types for extended ACL. -type MatchType uint32 - -// Action is an enumeration of extended ACL actions. -type Action uint32 - -// Group is an enumeration of access groups. -type Group uint32 - -// Header is an interface of string key-value pair, -type Header interface { - // Must return string identifier of header. - Name() string - - // Must return string value of header. - Value() string -} - -// TypedHeader is an interface of Header and HeaderType pair. -type TypedHeader interface { - Header - - // Must return type of filtered header. - HeaderType() HeaderType -} - -// HeaderFilter is an interface of grouped information about filtered header. -type HeaderFilter interface { - // Must return match type of filter. - MatchType() MatchType - - TypedHeader -} - -// Target is an interface of grouped information about extended ACL rule target. -type Target interface { - // Must return ACL target type. - Group() Group - - // Must return public key list of ACL targets. - KeyList() [][]byte -} - -// Record is an interface of record of extended ACL rule table. -type Record interface { - // Must return operation type of extended ACL rule. - OperationType() OperationType - - // Must return list of header filters of extended ACL rule. - HeaderFilters() []HeaderFilter - - // Must return target list of extended ACL rule. - TargetList() []Target - - // Must return action of extended ACL rule. - Action() Action -} - -// Table is an interface of extended ACL table. -type Table interface { - // Must return list of extended ACL rules. - Records() []Record -} - - diff --git a/acl/extended/wrappers.go b/acl/extended/wrappers.go deleted file mode 100644 index 2c8ff2e..0000000 --- a/acl/extended/wrappers.go +++ /dev/null @@ -1,528 +0,0 @@ -package eacl - -import ( - "github.com/nspcc-dev/neofs-api-go/acl" -) - -// FilterWrapper is a wrapper over acl.EACLRecord_FilterInfo pointer. -type FilterWrapper struct { - filter *acl.EACLRecord_FilterInfo -} - -// TargetWrapper is a wrapper over acl.EACLRecord_TargetInfo pointer. -type TargetWrapper struct { - target *acl.EACLRecord_TargetInfo -} - -// RecordWrapper is a wrapper over acl.EACLRecord pointer. -type RecordWrapper struct { - record *acl.EACLRecord -} - -// TableWrapper is a wrapper over acl.EACLTable pointer. -type TableWrapper struct { - table *acl.EACLTable -} - -// WrapFilterInfo wraps EACLRecord_FilterInfo pointer. -// -// If argument is nil, new EACLRecord_FilterInfo is initialized. -func WrapFilterInfo(v *acl.EACLRecord_FilterInfo) FilterWrapper { - if v == nil { - v = new(acl.EACLRecord_FilterInfo) - } - - return FilterWrapper{ - filter: v, - } -} - -// WrapTarget wraps EACLRecord_TargetInfo pointer. -// -// If argument is nil, new EACLRecord_TargetInfo is initialized. -func WrapTarget(v *acl.EACLRecord_TargetInfo) TargetWrapper { - if v == nil { - v = new(acl.EACLRecord_TargetInfo) - } - - return TargetWrapper{ - target: v, - } -} - -// WrapRecord wraps EACLRecord pointer. -// -// If argument is nil, new EACLRecord is initialized. -func WrapRecord(v *acl.EACLRecord) RecordWrapper { - if v == nil { - v = new(acl.EACLRecord) - } - - return RecordWrapper{ - record: v, - } -} - -// WrapTable wraps EACLTable pointer. -// -// If argument is nil, new EACLTable is initialized. -func WrapTable(v *acl.EACLTable) TableWrapper { - if v == nil { - v = new(acl.EACLTable) - } - - return TableWrapper{ - table: v, - } -} - -// MatchType returns the match type of the filter. -// -// If filter is not initialized, 0 returns. -// -// Returns 0 if MatchType is not one of: -// - EACLRecord_FilterInfo_StringEqual; -// - EACLRecord_FilterInfo_StringNotEqual. -func (s FilterWrapper) MatchType() (res MatchType) { - if s.filter != nil { - switch s.filter.GetMatchType() { - case acl.EACLRecord_FilterInfo_StringEqual: - res = StringEqual - case acl.EACLRecord_FilterInfo_StringNotEqual: - res = StringNotEqual - } - } - - return -} - -// SetMatchType sets the match type of the filter. -// -// If filter is not initialized, nothing changes. -// -// MatchType is set to EACLRecord_FilterInfo_MatchUnknown if argument is not one of: -// - StringEqual; -// - StringNotEqual. -func (s FilterWrapper) SetMatchType(v MatchType) { - if s.filter != nil { - switch v { - case StringEqual: - s.filter.SetMatchType(acl.EACLRecord_FilterInfo_StringEqual) - case StringNotEqual: - s.filter.SetMatchType(acl.EACLRecord_FilterInfo_StringNotEqual) - default: - s.filter.SetMatchType(acl.EACLRecord_FilterInfo_MatchUnknown) - } - } -} - -// Name returns the name of filtering header. -// -// If filter is not initialized, empty string returns. -func (s FilterWrapper) Name() string { - if s.filter == nil { - return "" - } - - return s.filter.GetHeaderName() -} - -// SetName sets the name of the filtering header. -// -// If filter is not initialized, nothing changes. -func (s FilterWrapper) SetName(v string) { - if s.filter != nil { - s.filter.SetHeaderName(v) - } -} - -// Value returns the value of filtering header. -// -// If filter is not initialized, empty string returns. -func (s FilterWrapper) Value() string { - if s.filter == nil { - return "" - } - - return s.filter.GetHeaderVal() -} - -// SetValue sets the value of filtering header. -// -// If filter is not initialized, nothing changes. -func (s FilterWrapper) SetValue(v string) { - if s.filter != nil { - s.filter.SetHeaderVal(v) - } -} - -// HeaderType returns the header type of the filter. -// -// If filter is not initialized, HdrTypeUnknown returns. -// -// Returns HdrTypeUnknown if Header is not one of: -// - EACLRecord_FilterInfo_Request; -// - EACLRecord_FilterInfo_ObjectSystem; -// - EACLRecord_FilterInfo_ObjectUser. -func (s FilterWrapper) HeaderType() (res HeaderType) { - res = HdrTypeUnknown - - if s.filter != nil { - switch s.filter.GetHeader() { - case acl.EACLRecord_FilterInfo_Request: - res = HdrTypeRequest - case acl.EACLRecord_FilterInfo_ObjectSystem: - res = HdrTypeObjSys - case acl.EACLRecord_FilterInfo_ObjectUser: - res = HdrTypeObjUsr - } - } - - return -} - -// SetHeaderType sets the header type of the filter. -// -// If filter is not initialized, nothing changes. -// -// Header is set to EACLRecord_FilterInfo_HeaderUnknown if argument is not one of: -// - HdrTypeRequest; -// - HdrTypeObjSys; -// - HdrTypeObjUsr. -func (s FilterWrapper) SetHeaderType(t HeaderType) { - if s.filter != nil { - switch t { - case HdrTypeRequest: - s.filter.SetHeader(acl.EACLRecord_FilterInfo_Request) - case HdrTypeObjSys: - s.filter.SetHeader(acl.EACLRecord_FilterInfo_ObjectSystem) - case HdrTypeObjUsr: - s.filter.SetHeader(acl.EACLRecord_FilterInfo_ObjectUser) - default: - s.filter.SetHeader(acl.EACLRecord_FilterInfo_HeaderUnknown) - } - } -} - -// Group returns the access group of the target. -// -// If target is not initialized, GroupUnknown returns. -// -// Returns GroupUnknown if Target is not one of: -// - Target_User; -// - GroupSystem; -// - GroupOthers. -func (s TargetWrapper) Group() (res Group) { - res = GroupUnknown - - if s.target != nil { - switch s.target.GetTarget() { - case acl.Target_User: - res = GroupUser - case acl.Target_System: - res = GroupSystem - case acl.Target_Others: - res = GroupOthers - } - } - - return -} - -// SetGroup sets the access group of the target. -// -// If target is not initialized, nothing changes. -// -// Target is set to Target_Unknown if argument is not one of: -// - GroupUser; -// - GroupSystem; -// - GroupOthers. -func (s TargetWrapper) SetGroup(g Group) { - if s.target != nil { - switch g { - case GroupUser: - s.target.SetTarget(acl.Target_User) - case GroupSystem: - s.target.SetTarget(acl.Target_System) - case GroupOthers: - s.target.SetTarget(acl.Target_Others) - default: - s.target.SetTarget(acl.Target_Unknown) - } - } -} - -// KeyList returns the key list of the target. -// -// If target is not initialized, nil returns. -func (s TargetWrapper) KeyList() [][]byte { - if s.target == nil { - return nil - } - - return s.target.GetKeyList() -} - -// SetKeyList sets the key list of the target. -// -// If target is not initialized, nothing changes. -func (s TargetWrapper) SetKeyList(v [][]byte) { - if s.target != nil { - s.target.SetKeyList(v) - } -} - -// OperationType returns the operation type of the record. -// -// If record is not initialized, OpTypeUnknown returns. -// -// Returns OpTypeUnknown if Operation is not one of: -// - EACLRecord_HEAD; -// - EACLRecord_PUT; -// - EACLRecord_SEARCH; -// - EACLRecord_GET; -// - EACLRecord_GETRANGE; -// - EACLRecord_GETRANGEHASH; -// - EACLRecord_DELETE. -func (s RecordWrapper) OperationType() (res OperationType) { - res = OpTypeUnknown - - if s.record != nil { - switch s.record.GetOperation() { - case acl.EACLRecord_HEAD: - res = OpTypeHead - case acl.EACLRecord_PUT: - res = OpTypePut - case acl.EACLRecord_SEARCH: - res = OpTypeSearch - case acl.EACLRecord_GET: - res = OpTypeGet - case acl.EACLRecord_GETRANGE: - res = OpTypeRange - case acl.EACLRecord_GETRANGEHASH: - res = OpTypeRangeHash - case acl.EACLRecord_DELETE: - res = OpTypeDelete - } - } - - return -} - -// SetOperationType sets the operation type of the record. -// -// If record is not initialized, nothing changes. -// -// Operation is set to EACLRecord_OPERATION_UNKNOWN if argument is not one of: -// - OpTypeHead; -// - OpTypePut; -// - OpTypeSearch; -// - OpTypeGet; -// - OpTypeRange; -// - OpTypeRangeHash; -// - OpTypeDelete. -func (s RecordWrapper) SetOperationType(v OperationType) { - if s.record != nil { - switch v { - case OpTypeHead: - s.record.SetOperation(acl.EACLRecord_HEAD) - case OpTypePut: - s.record.SetOperation(acl.EACLRecord_PUT) - case OpTypeSearch: - s.record.SetOperation(acl.EACLRecord_SEARCH) - case OpTypeGet: - s.record.SetOperation(acl.EACLRecord_GET) - case OpTypeRange: - s.record.SetOperation(acl.EACLRecord_GETRANGE) - case OpTypeRangeHash: - s.record.SetOperation(acl.EACLRecord_GETRANGEHASH) - case OpTypeDelete: - s.record.SetOperation(acl.EACLRecord_DELETE) - default: - s.record.SetOperation(acl.EACLRecord_OPERATION_UNKNOWN) - } - } -} - -// Action returns the action of the record. -// -// If record is not initialized, ActionUnknown returns. -// -// Returns ActionUnknown if Action is not one of: -// - EACLRecord_Deny; -// - EACLRecord_Allow. -func (s RecordWrapper) Action() (res Action) { - res = ActionUnknown - - if s.record != nil { - switch s.record.GetAction() { - case acl.EACLRecord_Deny: - res = ActionDeny - case acl.EACLRecord_Allow: - res = ActionAllow - } - } - - return -} - -// SetAction sets the action of the record. -// -// If record is not initialized, nothing changes. -// -// Action is set to EACLRecord_ActionUnknown if argument is not one of: -// - ActionDeny; -// - ActionAllow. -func (s RecordWrapper) SetAction(v Action) { - if s.record != nil { - switch v { - case ActionDeny: - s.record.SetAction(acl.EACLRecord_Deny) - case ActionAllow: - s.record.SetAction(acl.EACLRecord_Allow) - default: - s.record.SetAction(acl.EACLRecord_ActionUnknown) - } - } -} - -// HeaderFilters returns the header filter list of the record. -// -// If record is not initialized, nil returns. -func (s RecordWrapper) HeaderFilters() []HeaderFilter { - if s.record == nil { - return nil - } - - filters := s.record.GetFilters() - - res := make([]HeaderFilter, 0, len(filters)) - - for i := range filters { - res = append(res, WrapFilterInfo(filters[i])) - } - - return res -} - -// SetHeaderFilters sets the header filter list of the record. -// -// Ignores nil elements of argument. -// If record is not initialized, nothing changes. -func (s RecordWrapper) SetHeaderFilters(v []HeaderFilter) { - if s.record == nil { - return - } - - filters := make([]*acl.EACLRecord_FilterInfo, 0, len(v)) - - for i := range v { - if v[i] == nil { - continue - } - - w := WrapFilterInfo(nil) - w.SetMatchType(v[i].MatchType()) - w.SetHeaderType(v[i].HeaderType()) - w.SetName(v[i].Name()) - w.SetValue(v[i].Value()) - - filters = append(filters, w.filter) - } - - s.record.SetFilters(filters) -} - -// TargetList returns the target list of the record. -// -// If record is not initialized, nil returns. -func (s RecordWrapper) TargetList() []Target { - if s.record == nil { - return nil - } - - targets := s.record.GetTargets() - - res := make([]Target, 0, len(targets)) - - for i := range targets { - res = append(res, WrapTarget(targets[i])) - } - - return res -} - -// SetTargetList sets the target list of the record. -// -// Ignores nil elements of argument. -// If record is not initialized, nothing changes. -func (s RecordWrapper) SetTargetList(v []Target) { - if s.record == nil { - return - } - - targets := make([]*acl.EACLRecord_TargetInfo, 0, len(v)) - - for i := range v { - if v[i] == nil { - continue - } - - w := WrapTarget(nil) - w.SetGroup(v[i].Group()) - w.SetKeyList(v[i].KeyList()) - - targets = append(targets, w.target) - } - - s.record.SetTargets(targets) -} - -// Records returns the record list of the table. -// -// If table is not initialized, nil returns. -func (s TableWrapper) Records() []Record { - if s.table == nil { - return nil - } - - records := s.table.GetRecords() - - res := make([]Record, 0, len(records)) - - for i := range records { - res = append(res, WrapRecord(records[i])) - } - - return res -} - -// SetRecords sets the record list of the table. -// -// Ignores nil elements of argument. -// If table is not initialized, nothing changes. -func (s TableWrapper) SetRecords(v []Record) { - if s.table == nil { - return - } - - records := make([]*acl.EACLRecord, 0, len(v)) - - for i := range v { - if v[i] == nil { - continue - } - - w := WrapRecord(nil) - w.SetOperationType(v[i].OperationType()) - w.SetAction(v[i].Action()) - w.SetHeaderFilters(v[i].HeaderFilters()) - w.SetTargetList(v[i].TargetList()) - - records = append(records, w.record) - } - - s.table.SetRecords(records) -} - - diff --git a/acl/extended/wrappers_test.go b/acl/extended/wrappers_test.go deleted file mode 100644 index b139e59..0000000 --- a/acl/extended/wrappers_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package eacl - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestEACLFilterWrapper(t *testing.T) { - s := WrapFilterInfo(nil) - - mt := StringEqual - s.SetMatchType(mt) - require.Equal(t, mt, s.MatchType()) - - ht := HdrTypeObjUsr - s.SetHeaderType(ht) - require.Equal(t, ht, s.HeaderType()) - - n := "name" - s.SetName(n) - require.Equal(t, n, s.Name()) - - v := "value" - s.SetValue(v) - require.Equal(t, v, s.Value()) -} - -func TestEACLTargetWrapper(t *testing.T) { - s := WrapTarget(nil) - - group := Group(3) - s.SetGroup(group) - require.Equal(t, group, s.Group()) - - keys := [][]byte{ - {1, 2, 3}, - {4, 5, 6}, - } - s.SetKeyList(keys) - require.Equal(t, keys, s.KeyList()) -} - -func TestEACLRecordWrapper(t *testing.T) { - s := WrapRecord(nil) - - action := ActionAllow - s.SetAction(action) - require.Equal(t, action, s.Action()) - - opType := OperationType(5) - s.SetOperationType(opType) - require.Equal(t, opType, s.OperationType()) - - f1Name := "name1" - f1 := WrapFilterInfo(nil) - f1.SetName(f1Name) - - f2Name := "name2" - f2 := WrapFilterInfo(nil) - f2.SetName(f2Name) - - s.SetHeaderFilters([]HeaderFilter{f1, f2}) - - filters := s.HeaderFilters() - require.Len(t, filters, 2) - require.Equal(t, f1Name, filters[0].Name()) - require.Equal(t, f2Name, filters[1].Name()) - - group1 := Group(1) - t1 := WrapTarget(nil) - t1.SetGroup(group1) - - group2 := Group(2) - t2 := WrapTarget(nil) - t2.SetGroup(group2) - - s.SetTargetList([]Target{t1, t2}) - - targets := s.TargetList() - require.Len(t, targets, 2) - require.Equal(t, group1, targets[0].Group()) - require.Equal(t, group2, targets[1].Group()) -} - -func TestEACLTableWrapper(t *testing.T) { - s := WrapTable(nil) - - action1 := Action(1) - r1 := WrapRecord(nil) - r1.SetAction(action1) - - action2 := Action(2) - r2 := WrapRecord(nil) - r2.SetAction(action2) - - s.SetRecords([]Record{r1, r2}) - - records := s.Records() - require.Len(t, records, 2) - require.Equal(t, action1, records[0].Action()) - require.Equal(t, action2, records[1].Action()) - - s2, err := UnmarshalTable(MarshalTable(s)) - require.NoError(t, err) - - records1 := s.Records() - records2 := s2.Records() - require.Len(t, records1, len(records2)) - - for i := range records1 { - require.Equal(t, records1[i].Action(), records2[i].Action()) - require.Equal(t, records1[i].OperationType(), records2[i].OperationType()) - - targets1 := records1[i].TargetList() - targets2 := records2[i].TargetList() - require.Len(t, targets1, len(targets2)) - - for j := range targets1 { - require.Equal(t, targets1[j].Group(), targets2[j].Group()) - require.Equal(t, targets1[j].KeyList(), targets2[j].KeyList()) - } - - filters1 := records1[i].HeaderFilters() - filters2 := records2[i].HeaderFilters() - require.Len(t, filters1, len(filters2)) - - for j := range filters1 { - require.Equal(t, filters1[j].MatchType(), filters2[j].MatchType()) - require.Equal(t, filters1[j].HeaderType(), filters2[j].HeaderType()) - require.Equal(t, filters1[j].Name(), filters2[j].Name()) - require.Equal(t, filters1[j].Value(), filters2[j].Value()) - require.Equal(t, filters1[j].Value(), filters2[j].Value()) - } - } -} diff --git a/acl/types.go b/acl/types.go deleted file mode 100644 index 4aaf8fb..0000000 --- a/acl/types.go +++ /dev/null @@ -1,57 +0,0 @@ -package acl - - -// SetMatchType is MatchType field setter. -func (m *EACLRecord_FilterInfo) SetMatchType(v EACLRecord_FilterInfo_MatchType) { - m.MatchType = v -} - -// SetHeader is a Header field setter. -func (m *EACLRecord_FilterInfo) SetHeader(v EACLRecord_FilterInfo_Header) { - m.Header = v -} - -// SetHeaderName is a HeaderName field setter. -func (m *EACLRecord_FilterInfo) SetHeaderName(v string) { - m.HeaderName = v -} - -// SetHeaderVal is a HeaderVal field setter. -func (m *EACLRecord_FilterInfo) SetHeaderVal(v string) { - m.HeaderVal = v -} - -// SetTarget is a Target field setter. -func (m *EACLRecord_TargetInfo) SetTarget(v Target) { - m.Target = v -} - -// SetKeyList is a KeyList field setter. -func (m *EACLRecord_TargetInfo) SetKeyList(v [][]byte) { - m.KeyList = v -} - -// SetOperation is an Operation field setter. -func (m *EACLRecord) SetOperation(v EACLRecord_Operation) { - m.Operation = v -} - -// SetAction is an Action field setter. -func (m *EACLRecord) SetAction(v EACLRecord_Action) { - m.Action = v -} - -// SetFilters is a Filters field setter. -func (m *EACLRecord) SetFilters(v []*EACLRecord_FilterInfo) { - m.Filters = v -} - -// SetTargets is a Targets field setter. -func (m *EACLRecord) SetTargets(v []*EACLRecord_TargetInfo) { - m.Targets = v -} - -// SetRecords is a Records field setter. -func (m *EACLTable) SetRecords(v []*EACLRecord) { - m.Records = v -} diff --git a/acl/types.pb.go b/acl/types.pb.go deleted file mode 100644 index 24ecf28..0000000 Binary files a/acl/types.pb.go and /dev/null differ diff --git a/acl/types.proto b/acl/types.proto deleted file mode 100644 index 7f3efc9..0000000 --- a/acl/types.proto +++ /dev/null @@ -1,106 +0,0 @@ -syntax = "proto3"; -package acl; -option go_package = "github.com/nspcc-dev/neofs-api-go/acl"; -option csharp_namespace = "NeoFS.API.Acl"; - -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; -option (gogoproto.stable_marshaler_all) = true; - -// Target of the access control rule in access control list. -enum Target { - // Unknown target, default value. - Unknown = 0; - - // User target rule is applied if sender is the owner of the container. - User = 1; - - // System target rule is applied if sender is the storage node within the - // container or inner ring node. - System = 2; - - // Others target rule is applied if sender is not user or system target. - Others = 3; - - // PubKey target rule is applied if sender has public key provided in - // extended ACL. - PubKey = 4; -} - -// EACLRecord groups information about extended ACL rule. -message EACLRecord { - // Operation is an enumeration of operation types. - enum Operation { - OPERATION_UNKNOWN = 0; - GET = 1; - HEAD = 2; - PUT = 3; - DELETE = 4; - SEARCH = 5; - GETRANGE = 6; - GETRANGEHASH = 7; - } - - // Operation carries type of operation. - Operation operation = 1 [(gogoproto.customname) = "Operation", json_name="Operation"]; - - // Action is an enumeration of EACL actions. - enum Action { - ActionUnknown = 0; - Allow = 1; - Deny = 2; - } - - // Action carries ACL target action. - Action action = 2 [(gogoproto.customname) = "Action", json_name="Action"]; - - // FilterInfo groups information about filter. - message FilterInfo { - // Header is an enumeration of filtering header types. - enum Header { - HeaderUnknown = 0; - Request = 1; - ObjectSystem = 2; - ObjectUser = 3; - } - - // Header carries type of header. - Header header = 1 [(gogoproto.customname) = "Header", json_name="HeaderType"]; - - // MatchType is an enumeration of match types. - enum MatchType { - MatchUnknown = 0; - StringEqual = 1; - StringNotEqual = 2; - } - - // MatchType carries type of match. - MatchType matchType = 2 [(gogoproto.customname) = "MatchType", json_name="MatchType"]; - - // HeaderName carries name of filtering header. - string HeaderName = 3 [json_name="Name"]; - - // HeaderVal carries value of filtering header. - string HeaderVal = 4 [json_name="Value"]; - } - - // Filters carries set of filters. - repeated FilterInfo Filters = 3 [json_name="Filters"]; - - // TargetInfo groups information about extended ACL target. - message TargetInfo { - // Target carries target of ACL rule. - acl.Target Target = 1 [json_name="Role"]; - - // KeyList carries public keys of ACL target. - repeated bytes KeyList = 2 [json_name="Keys"]; - } - - // Targets carries information about extended ACL target list. - repeated TargetInfo Targets = 4 [json_name="Targets"]; -} - -// EACLRecord carries the information about extended ACL rules. -message EACLTable { - // Records carries list of extended ACL rule records. - repeated EACLRecord Records = 1 [json_name="Records"]; -} diff --git a/bootstrap/service.go b/bootstrap/service.go deleted file mode 100644 index 929d9b3..0000000 --- a/bootstrap/service.go +++ /dev/null @@ -1,8 +0,0 @@ -package bootstrap - -import ( - "github.com/nspcc-dev/neofs-api-go/service" -) - -// NodeType type alias. -type NodeType = service.NodeRole diff --git a/bootstrap/service.pb.go b/bootstrap/service.pb.go deleted file mode 100644 index 50e6c67..0000000 Binary files a/bootstrap/service.pb.go and /dev/null differ diff --git a/bootstrap/service.proto b/bootstrap/service.proto deleted file mode 100644 index 6918bef..0000000 --- a/bootstrap/service.proto +++ /dev/null @@ -1,42 +0,0 @@ -syntax = "proto3"; -package bootstrap; -option go_package = "github.com/nspcc-dev/neofs-api-go/bootstrap"; -option csharp_namespace = "NeoFS.API.Bootstrap"; - -import "service/meta.proto"; -import "service/verify.proto"; -import "bootstrap/types.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -// Bootstrap service allows neofs-node to connect to the network. Node should -// perform at least one bootstrap request in the epoch to stay in the network -// for the next epoch. -service Bootstrap { - // Process is method that allows to register node in the network and receive actual netmap - rpc Process(Request) returns (bootstrap.SpreadMap); -} - -message Request { - // Node state - enum State { - // used by default - Unknown = 0; - // used to inform that node online - Online = 1; - // used to inform that node offline - Offline = 2; - } - - // Type is NodeType, can be InnerRingNode (type=1) or StorageNode (type=2) - int32 type = 1 [(gogoproto.customname) = "Type" , (gogoproto.nullable) = false, (gogoproto.customtype) = "NodeType"]; - // Info contains information about node - bootstrap.NodeInfo info = 2 [(gogoproto.nullable) = false]; - // State contains node status - State state = 3; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} diff --git a/bootstrap/sign.go b/bootstrap/sign.go deleted file mode 100644 index 34f7fc2..0000000 --- a/bootstrap/sign.go +++ /dev/null @@ -1,46 +0,0 @@ -package bootstrap - -import ( - "io" - - "github.com/nspcc-dev/neofs-api-go/service" -) - -// SignedData returns payload bytes of the request. -func (m Request) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m Request) SignedDataSize() (sz int) { - sz += m.GetType().Size() - - sz += m.GetState().Size() - - info := m.GetInfo() - sz += info.Size() - - return -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the Request size is insufficient, io.ErrUnexpectedEOF returns. -func (m Request) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.GetType().Bytes()) - - off += copy(p[off:], m.GetState().Bytes()) - - info := m.GetInfo() - // FIXME: implement and use stable functions - n, err := info.MarshalTo(p[off:]) - off += n - - return off, err -} diff --git a/bootstrap/sign_test.go b/bootstrap/sign_test.go deleted file mode 100644 index 3812130..0000000 --- a/bootstrap/sign_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package bootstrap - -import ( - "testing" - - "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/stretchr/testify/require" -) - -func TestRequestSign(t *testing.T) { - sk := test.DecodeKey(0) - - type sigType interface { - service.RequestData - service.SignKeyPairAccumulator - service.SignKeyPairSource - SetToken(*service.Token) - } - - items := []struct { - constructor func() sigType - payloadCorrupt []func(sigType) - }{ - { // Request - constructor: func() sigType { - return new(Request) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - req := s.(*Request) - - req.SetType(req.GetType() + 1) - }, - func(s sigType) { - req := s.(*Request) - - req.SetState(req.GetState() + 1) - }, - func(s sigType) { - req := s.(*Request) - - info := req.GetInfo() - info.Address += "1" - - req.SetInfo(info) - }, - }, - }, - } - - for _, item := range items { - { // token corruptions - v := item.constructor() - - token := new(service.Token) - v.SetToken(token) - - require.NoError(t, service.SignRequestData(sk, v)) - - require.NoError(t, service.VerifyRequestData(v)) - - token.SetSessionKey(append(token.GetSessionKey(), 1)) - - require.Error(t, service.VerifyRequestData(v)) - } - - { // payload corruptions - for _, corruption := range item.payloadCorrupt { - v := item.constructor() - - require.NoError(t, service.SignRequestData(sk, v)) - - require.NoError(t, service.VerifyRequestData(v)) - - corruption(v) - - require.Error(t, service.VerifyRequestData(v)) - } - } - } -} diff --git a/bootstrap/types.go b/bootstrap/types.go deleted file mode 100644 index 7ad3ec2..0000000 --- a/bootstrap/types.go +++ /dev/null @@ -1,137 +0,0 @@ -package bootstrap - -import ( - "bytes" - "encoding/binary" - "encoding/hex" - "strconv" - "strings" - - "github.com/golang/protobuf/proto" - "github.com/nspcc-dev/neofs-api-go/object" -) - -type ( - // NodeStatus is a bitwise status field of the node. - NodeStatus uint64 -) - -const ( - storageFullMask = 0x1 - - optionCapacity = "/Capacity:" - optionPrice = "/Price:" -) - -var ( - _ proto.Message = (*NodeInfo)(nil) - _ proto.Message = (*SpreadMap)(nil) -) - -var requestEndianness = binary.BigEndian - -// Equals checks whether two NodeInfo has same address. -func (m NodeInfo) Equals(n1 NodeInfo) bool { - return m.Address == n1.Address && bytes.Equal(m.PubKey, n1.PubKey) -} - -// Full checks if node has enough space for storing users objects. -func (n NodeStatus) Full() bool { - return n&storageFullMask > 0 -} - -// SetFull changes state of node to indicate if node has enough space for storing users objects. -// If value is true - there's not enough space. -func (n *NodeStatus) SetFull(value bool) { - switch value { - case true: - *n |= NodeStatus(storageFullMask) - case false: - *n &= NodeStatus(^uint64(storageFullMask)) - } -} - -// Price returns price in 1e-8*GAS/Megabyte per month. -// User set price in GAS/Terabyte per month. -func (m NodeInfo) Price() uint64 { - for i := range m.Options { - if strings.HasPrefix(m.Options[i], optionPrice) { - n, err := strconv.ParseFloat(m.Options[i][len(optionPrice):], 64) - if err != nil { - return 0 - } - return uint64(n*1e8) / uint64(object.UnitsMB) // UnitsMB == megabytes in 1 terabyte - } - } - return 0 -} - -// Capacity returns node's capacity as reported by user. -func (m NodeInfo) Capacity() uint64 { - for i := range m.Options { - if strings.HasPrefix(m.Options[i], optionCapacity) { - n, err := strconv.ParseUint(m.Options[i][len(optionCapacity):], 10, 64) - if err != nil { - return 0 - } - return n - } - } - return 0 -} - -// String returns string representation of NodeInfo. -func (m NodeInfo) String() string { - return "(NodeInfo)<" + - "Address:" + m.Address + - ", " + - "PublicKey:" + hex.EncodeToString(m.PubKey) + - ", " + - "Options: [" + strings.Join(m.Options, ",") + "]>" -} - -// String returns string representation of SpreadMap. -func (m SpreadMap) String() string { - result := make([]string, 0, len(m.NetMap)) - for i := range m.NetMap { - result = append(result, m.NetMap[i].String()) - } - return "(SpreadMap)<" + - "Epoch: " + strconv.FormatUint(m.Epoch, 10) + - ", " + - "Netmap: [" + strings.Join(result, ",") + "]>" -} - -// GetType is a Type field getter. -func (m Request) GetType() NodeType { - return m.Type -} - -// SetType is a Type field setter. -func (m *Request) SetType(t NodeType) { - m.Type = t -} - -// SetState is a State field setter. -func (m *Request) SetState(state Request_State) { - m.State = state -} - -// SetInfo is an Info field getter. -func (m *Request) SetInfo(info NodeInfo) { - m.Info = info -} - -// Size returns the size necessary for a binary representation of the state. -func (x Request_State) Size() int { - return 4 -} - -// Bytes returns a binary representation of the state. -func (x Request_State) Bytes() []byte { - data := make([]byte, x.Size()) - - requestEndianness.PutUint32(data, uint32(x)) - - return data -} diff --git a/bootstrap/types.pb.go b/bootstrap/types.pb.go deleted file mode 100644 index fcd8848..0000000 Binary files a/bootstrap/types.pb.go and /dev/null differ diff --git a/bootstrap/types.proto b/bootstrap/types.proto deleted file mode 100644 index 4ef0fff..0000000 --- a/bootstrap/types.proto +++ /dev/null @@ -1,29 +0,0 @@ -syntax = "proto3"; -package bootstrap; -option go_package = "github.com/nspcc-dev/neofs-api-go/bootstrap"; -option csharp_namespace = "NeoFS.API.Bootstrap"; - -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -option (gogoproto.stringer_all) = false; -option (gogoproto.goproto_stringer_all) = false; - -message SpreadMap { - // Epoch is current epoch for netmap - uint64 Epoch = 1; - // NetMap is a set of NodeInfos - repeated NodeInfo NetMap = 2 [(gogoproto.nullable) = false]; -} - -message NodeInfo { - // Address is a node [multi-address](https://github.com/multiformats/multiaddr) - string Address = 1 [(gogoproto.jsontag) = "address"]; - // PubKey is a compressed public key representation in bytes - bytes PubKey = 2 [(gogoproto.jsontag) = "pubkey,omitempty"]; - // Options is set of node optional information, such as storage capacity, node location, price and etc - repeated string Options = 3 [(gogoproto.jsontag) = "options,omitempty"]; - // Status is bitmap status of the node - uint64 Status = 4 [(gogoproto.jsontag) = "status", (gogoproto.nullable) = false, (gogoproto.customtype) = "NodeStatus"]; -} diff --git a/bootstrap/types_test.go b/bootstrap/types_test.go deleted file mode 100644 index 20b1b1a..0000000 --- a/bootstrap/types_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package bootstrap - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestRequestGettersSetters(t *testing.T) { - t.Run("type", func(t *testing.T) { - rt := NodeType(1) - m := new(Request) - - m.SetType(rt) - - require.Equal(t, rt, m.GetType()) - }) - - t.Run("state", func(t *testing.T) { - st := Request_State(1) - m := new(Request) - - m.SetState(st) - - require.Equal(t, st, m.GetState()) - }) - - t.Run("info", func(t *testing.T) { - info := NodeInfo{ - Address: "some address", - } - - m := new(Request) - - m.SetInfo(info) - - require.Equal(t, info, m.GetInfo()) - }) -} diff --git a/chain/address.go b/chain/address.go deleted file mode 100644 index 0a56b8e..0000000 --- a/chain/address.go +++ /dev/null @@ -1,30 +0,0 @@ -package chain - -import ( - "crypto/ecdsa" - - "github.com/nspcc-dev/neo-go/pkg/crypto/keys" -) - -// WalletAddress implements NEO address. -type WalletAddress [AddressLength]byte - -const ( - // AddressLength contains size of address, - // 1 byte of address version + 20 bytes of ScriptHash + 4 bytes of checksum. - AddressLength = 25 -) - -// KeyToAddress returns NEO address composed from public key. -func KeyToAddress(key *ecdsa.PublicKey) string { - if key == nil { - return "" - } - - neoPublicKey := keys.PublicKey{ - X: key.X, - Y: key.Y, - } - - return neoPublicKey.Address() -} diff --git a/chain/address_test.go b/chain/address_test.go deleted file mode 100644 index a998a11..0000000 --- a/chain/address_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package chain - -import ( - "encoding/hex" - "testing" - - crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/stretchr/testify/require" -) - -type addressTestCase struct { - name string - publicKey string - wallet string -} - -func TestKeyToAddress(t *testing.T) { - tests := []addressTestCase{ - { - "nil key", - "", - "", - }, - { - "correct key", - "031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a", - "NgzuJWWGVEwFGsRrgzj8knswEYRJrTe7sm", - }, - } - - for i := range tests { - t.Run(tests[i].name, func(t *testing.T) { - data, err := hex.DecodeString(tests[i].publicKey) - require.NoError(t, err) - - key := crypto.UnmarshalPublicKey(data) - - require.Equal(t, tests[i].wallet, KeyToAddress(key)) - }) - } -} diff --git a/container/service.go b/container/service.go deleted file mode 100644 index 2f36dc1..0000000 --- a/container/service.go +++ /dev/null @@ -1,60 +0,0 @@ -package container - -import ( - "bytes" - "encoding/binary" - - "github.com/nspcc-dev/neofs-api-go/internal" - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/pkg/errors" -) - -type ( - // CID type alias. - CID = refs.CID - // UUID type alias. - UUID = refs.UUID - // OwnerID type alias. - OwnerID = refs.OwnerID - // MessageID type alias. - MessageID = refs.MessageID -) - -const ( - // ErrNotFound is raised when container could not be found. - ErrNotFound = internal.Error("could not find container") -) - -// PrepareData prepares bytes representation of PutRequest to satisfy SignedRequest interface. -func (m *PutRequest) PrepareData() ([]byte, error) { - var ( - err error - buf = new(bytes.Buffer) - capBytes = make([]byte, 8) - aclBytes = make([]byte, 4) - ) - - binary.BigEndian.PutUint64(capBytes, m.Capacity) - binary.BigEndian.PutUint32(capBytes, m.BasicACL) - - if _, err = buf.Write(m.MessageID.Bytes()); err != nil { - return nil, errors.Wrap(err, "could not write message id") - } else if _, err = buf.Write(capBytes); err != nil { - return nil, errors.Wrap(err, "could not write capacity") - } else if _, err = buf.Write(m.OwnerID.Bytes()); err != nil { - return nil, errors.Wrap(err, "could not write pub") - } else if data, err := m.Rules.Marshal(); err != nil { - return nil, errors.Wrap(err, "could not marshal placement") - } else if _, err = buf.Write(data); err != nil { - return nil, errors.Wrap(err, "could not write placement") - } else if _, err = buf.Write(aclBytes); err != nil { - return nil, errors.Wrap(err, "could not write basic acl") - } - - return buf.Bytes(), nil -} - -// PrepareData prepares bytes representation of DeleteRequest to satisfy SignedRequest interface. -func (m *DeleteRequest) PrepareData() ([]byte, error) { - return m.CID.Bytes(), nil -} diff --git a/container/service.pb.go b/container/service.pb.go deleted file mode 100644 index f49f79f..0000000 Binary files a/container/service.pb.go and /dev/null differ diff --git a/container/service.proto b/container/service.proto deleted file mode 100644 index b174052..0000000 --- a/container/service.proto +++ /dev/null @@ -1,146 +0,0 @@ -syntax = "proto3"; -package container; -option go_package = "github.com/nspcc-dev/neofs-api-go/container"; -option csharp_namespace = "NeoFS.API.Container"; - -import "service/meta.proto"; -import "service/verify.proto"; -import "container/types.proto"; -import "github.com/nspcc-dev/netmap/selector.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -// Container service provides API for manipulating with the container. -service Service { - // Put request proposes container to the inner ring nodes. They will - // accept new container if user has enough deposit. All containers - // are accepted by the consensus, therefore it is asynchronous process. - rpc Put(PutRequest) returns (PutResponse); - - // Delete container removes it from the inner ring container storage. It - // also asynchronous process done by consensus. - rpc Delete(DeleteRequest) returns (DeleteResponse); - - // Get container returns container instance - rpc Get(GetRequest) returns (GetResponse); - - // List returns all user's containers - rpc List(ListRequest) returns (ListResponse); - - // SetExtendedACL changes extended ACL rules of the container - rpc SetExtendedACL(SetExtendedACLRequest) returns (SetExtendedACLResponse); - - // GetExtendedACL returns extended ACL rules of the container - rpc GetExtendedACL(GetExtendedACLRequest) returns (GetExtendedACLResponse); -} - -message PutRequest { - // MessageID is a nonce for uniq container id calculation - bytes MessageID = 1 [(gogoproto.customtype) = "MessageID", (gogoproto.nullable) = false]; - - // Capacity defines amount of data that can be stored in the container (doesn't used for now). - uint64 Capacity = 2; - - // OwnerID is a wallet address - bytes OwnerID = 3 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - - // Rules define storage policy for the object inside the container. - netmap.PlacementRule rules = 4 [(gogoproto.nullable) = false]; - - // BasicACL of the container. - uint32 BasicACL = 5; - - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message PutResponse { - // CID (container id) is a SHA256 hash of the container structure - bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false]; -} - -message DeleteRequest { - // CID (container id) is a SHA256 hash of the container structure - bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false]; - - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -// DeleteResponse is empty because delete operation is asynchronous and done -// via consensus in inner ring nodes -message DeleteResponse { } - - -message GetRequest { - // CID (container id) is a SHA256 hash of the container structure - bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false]; - - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message GetResponse { - // Container is a structure that contains placement rules and owner id - container.Container Container = 1; -} - -message ListRequest { - // OwnerID is a wallet address - bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message ListResponse { - // CID (container id) is list of SHA256 hashes of the container structures - repeated bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false]; -} - -message ExtendedACLKey { - // ID (container id) is a SHA256 hash of the container structure - bytes ID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false]; -} - -message ExtendedACLValue { - // EACL carries binary representation of the table of extended ACL rules - bytes EACL = 1; - // Signature carries EACL field signature - bytes Signature = 2; -} - -message SetExtendedACLRequest { - // Key carries key to extended ACL information - ExtendedACLKey Key = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // Value carries extended ACL information - ExtendedACLValue Value = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message SetExtendedACLResponse {} - -message GetExtendedACLRequest { - // Key carries key to extended ACL information - ExtendedACLKey Key = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message GetExtendedACLResponse { - // ACL carries extended ACL information - ExtendedACLValue ACL = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} diff --git a/container/sign.go b/container/sign.go deleted file mode 100644 index f538f2d..0000000 --- a/container/sign.go +++ /dev/null @@ -1,194 +0,0 @@ -package container - -import ( - "encoding/binary" - "io" - - service "github.com/nspcc-dev/neofs-api-go/service" -) - -var requestEndianness = binary.BigEndian - -// SignedData returns payload bytes of the request. -func (m PutRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m PutRequest) SignedDataSize() (sz int) { - sz += m.GetMessageID().Size() - - sz += 8 - - sz += m.GetOwnerID().Size() - - rules := m.GetRules() - sz += rules.Size() - - sz += 4 - - return -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the Request size is insufficient, io.ErrUnexpectedEOF returns. -func (m PutRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.GetMessageID().Bytes()) - - requestEndianness.PutUint64(p[off:], m.GetCapacity()) - off += 8 - - off += copy(p[off:], m.GetOwnerID().Bytes()) - - rules := m.GetRules() - // FIXME: implement and use stable functions - n, err := rules.MarshalTo(p[off:]) - off += n - if err != nil { - return off, err - } - - requestEndianness.PutUint32(p[off:], m.GetBasicACL()) - off += 4 - - return off, nil -} - -// SignedData returns payload bytes of the request. -func (m DeleteRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m DeleteRequest) SignedDataSize() int { - return m.GetCID().Size() -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the Request size is insufficient, io.ErrUnexpectedEOF returns. -func (m DeleteRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.GetCID().Bytes()) - - return off, nil -} - -// SignedData returns payload bytes of the request. -func (m GetRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m GetRequest) SignedDataSize() int { - return m.GetCID().Size() -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the Request size is insufficient, io.ErrUnexpectedEOF returns. -func (m GetRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.GetCID().Bytes()) - - return off, nil -} - -// SignedData returns payload bytes of the request. -func (m ListRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m ListRequest) SignedDataSize() int { - return m.GetOwnerID().Size() -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the Request size is insufficient, io.ErrUnexpectedEOF returns. -func (m ListRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.GetOwnerID().Bytes()) - - return off, nil -} - -// SignedData returns payload bytes of the request. -func (m GetExtendedACLRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m GetExtendedACLRequest) SignedDataSize() int { - return m.GetID().Size() -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the Request size is insufficient, io.ErrUnexpectedEOF returns. -func (m GetExtendedACLRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.GetID().Bytes()) - - return off, nil -} - -// SignedData returns payload bytes of the request. -func (m SetExtendedACLRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m SetExtendedACLRequest) SignedDataSize() int { - return 0 + - m.GetID().Size() + - len(m.GetEACL()) + - len(m.GetSignature()) -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the Request size is insufficient, io.ErrUnexpectedEOF returns. -func (m SetExtendedACLRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.GetID().Bytes()) - - off += copy(p[off:], m.GetEACL()) - - off += copy(p[off:], m.GetSignature()) - - return off, nil -} diff --git a/container/sign_test.go b/container/sign_test.go deleted file mode 100644 index d04a698..0000000 --- a/container/sign_test.go +++ /dev/null @@ -1,187 +0,0 @@ -package container - -import ( - "testing" - - "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/stretchr/testify/require" -) - -func TestRequestSign(t *testing.T) { - sk := test.DecodeKey(0) - - type sigType interface { - service.RequestData - service.SignKeyPairAccumulator - service.SignKeyPairSource - SetToken(*service.Token) - } - - items := []struct { - constructor func() sigType - payloadCorrupt []func(sigType) - }{ - { // PutRequest - constructor: func() sigType { - return new(PutRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - req := s.(*PutRequest) - - id := req.GetMessageID() - id[0]++ - - req.SetMessageID(id) - }, - func(s sigType) { - req := s.(*PutRequest) - - req.SetCapacity(req.GetCapacity() + 1) - }, - func(s sigType) { - req := s.(*PutRequest) - - owner := req.GetOwnerID() - owner[0]++ - - req.SetOwnerID(owner) - }, - func(s sigType) { - req := s.(*PutRequest) - - rules := req.GetRules() - rules.ReplFactor++ - - req.SetRules(rules) - }, - func(s sigType) { - req := s.(*PutRequest) - - req.SetBasicACL(req.GetBasicACL() + 1) - }, - }, - }, - { // DeleteRequest - constructor: func() sigType { - return new(DeleteRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - req := s.(*DeleteRequest) - - cid := req.GetCID() - cid[0]++ - - req.SetCID(cid) - }, - }, - }, - { // GetRequest - constructor: func() sigType { - return new(GetRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - req := s.(*GetRequest) - - cid := req.GetCID() - cid[0]++ - - req.SetCID(cid) - }, - }, - }, - { // ListRequest - constructor: func() sigType { - return new(ListRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - req := s.(*ListRequest) - - owner := req.GetOwnerID() - owner[0]++ - - req.SetOwnerID(owner) - }, - }, - }, - { // GetExtendedACLRequest - constructor: func() sigType { - return new(GetExtendedACLRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - req := s.(*GetExtendedACLRequest) - - id := req.GetID() - id[0]++ - - req.SetID(id) - }, - }, - }, - { // SetExtendedACLRequest - constructor: func() sigType { - return new(SetExtendedACLRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - req := s.(*SetExtendedACLRequest) - - id := req.GetID() - id[0]++ - - req.SetID(id) - }, - func(s sigType) { - req := s.(*SetExtendedACLRequest) - - req.SetEACL( - append(req.GetEACL(), 1), - ) - }, - func(s sigType) { - req := s.(*SetExtendedACLRequest) - - req.SetSignature( - append(req.GetSignature(), 1), - ) - }, - }, - }, - } - - for _, item := range items { - { // token corruptions - v := item.constructor() - - token := new(service.Token) - v.SetToken(token) - - require.NoError(t, service.SignRequestData(sk, v)) - - require.NoError(t, service.VerifyRequestData(v)) - - token.SetSessionKey(append(token.GetSessionKey(), 1)) - - require.Error(t, service.VerifyRequestData(v)) - } - - { // payload corruptions - for _, corruption := range item.payloadCorrupt { - v := item.constructor() - - require.NoError(t, service.SignRequestData(sk, v)) - - require.NoError(t, service.VerifyRequestData(v)) - - corruption(v) - - require.Error(t, service.VerifyRequestData(v)) - } - } - } -} diff --git a/container/types.go b/container/types.go deleted file mode 100644 index be06b53..0000000 --- a/container/types.go +++ /dev/null @@ -1,188 +0,0 @@ -package container - -import ( - "bytes" - - "github.com/gogo/protobuf/proto" - "github.com/google/uuid" - "github.com/nspcc-dev/neofs-api-go/internal" - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/nspcc-dev/netmap" - "github.com/pkg/errors" -) - -var ( - _ internal.Custom = (*Container)(nil) - - emptySalt = (UUID{}).Bytes() - emptyOwner = (OwnerID{}).Bytes() -) - -// New creates new user container based on capacity, OwnerID, ACL and PlacementRules. -func New(cap uint64, owner OwnerID, acl uint32, rules netmap.PlacementRule) (*Container, error) { - if bytes.Equal(owner[:], emptyOwner) { - return nil, refs.ErrEmptyOwner - } else if cap == 0 { - return nil, refs.ErrEmptyCapacity - } - - salt, err := uuid.NewRandom() - if err != nil { - return nil, errors.Wrap(err, "could not create salt") - } - - return &Container{ - OwnerID: owner, - Salt: UUID(salt), - Capacity: cap, - Rules: rules, - BasicACL: acl, - }, nil -} - -// Bytes returns bytes representation of Container. -func (m *Container) Bytes() []byte { - data, err := m.Marshal() - if err != nil { - return nil - } - - return data -} - -// ID returns generated ContainerID based on Container (data). -func (m *Container) ID() (CID, error) { - if m.Empty() { - return CID{}, refs.ErrEmptyContainer - } - data, err := m.Marshal() - if err != nil { - return CID{}, err - } - - return refs.CIDForBytes(data), nil -} - -// Merge used by proto.Clone -func (m *Container) Merge(src proto.Message) { - if tmp, ok := src.(*Container); ok { - *m = *tmp - } -} - -// Empty checks that container is empty. -func (m *Container) Empty() bool { - return m.Capacity == 0 || bytes.Equal(m.Salt.Bytes(), emptySalt) || bytes.Equal(m.OwnerID.Bytes(), emptyOwner) -} - -// -- Test container definition -- // - -// NewTestContainer returns test container. -// WARNING: DON'T USE THIS OUTSIDE TESTS. -func NewTestContainer() (*Container, error) { - key := test.DecodeKey(0) - owner, err := refs.NewOwnerID(&key.PublicKey) - if err != nil { - return nil, err - } - return New(100, owner, 0xFFFFFFFF, netmap.PlacementRule{ - ReplFactor: 2, - SFGroups: []netmap.SFGroup{ - { - Selectors: []netmap.Select{ - {Key: "Country", Count: 1}, - {Key: netmap.NodesBucket, Count: 2}, - }, - Filters: []netmap.Filter{ - {Key: "Country", F: netmap.FilterIn("USA")}, - }, - }, - }, - }) -} - -// GetMessageID is a MessageID field getter. -func (m PutRequest) GetMessageID() MessageID { - return m.MessageID -} - -// SetMessageID is a MessageID field getter. -func (m *PutRequest) SetMessageID(id MessageID) { - m.MessageID = id -} - -// SetCapacity is a Capacity field setter. -func (m *PutRequest) SetCapacity(c uint64) { - m.Capacity = c -} - -// GetOwnerID is an OwnerID field getter. -func (m PutRequest) GetOwnerID() OwnerID { - return m.OwnerID -} - -// SetOwnerID is an OwnerID field setter. -func (m *PutRequest) SetOwnerID(owner OwnerID) { - m.OwnerID = owner -} - -// SetRules is a Rules field setter. -func (m *PutRequest) SetRules(rules netmap.PlacementRule) { - m.Rules = rules -} - -// SetBasicACL is a BasicACL field setter. -func (m *PutRequest) SetBasicACL(acl uint32) { - m.BasicACL = acl -} - -// GetCID is a CID field getter. -func (m DeleteRequest) GetCID() CID { - return m.CID -} - -// SetCID is a CID field setter. -func (m *DeleteRequest) SetCID(cid CID) { - m.CID = cid -} - -// GetCID is a CID field getter. -func (m GetRequest) GetCID() CID { - return m.CID -} - -// SetCID is a CID field setter. -func (m *GetRequest) SetCID(cid CID) { - m.CID = cid -} - -// GetOwnerID is an OwnerID field getter. -func (m ListRequest) GetOwnerID() OwnerID { - return m.OwnerID -} - -// SetOwnerID is an OwnerID field setter. -func (m *ListRequest) SetOwnerID(owner OwnerID) { - m.OwnerID = owner -} - -// GetID is an ID field getter. -func (m ExtendedACLKey) GetID() CID { - return m.ID -} - -// SetID is an ID field setter. -func (m *ExtendedACLKey) SetID(v CID) { - m.ID = v -} - -// SetEACL is an EACL field setter. -func (m *ExtendedACLValue) SetEACL(v []byte) { - m.EACL = v -} - -// SetSignature is a Signature field setter. -func (m *ExtendedACLValue) SetSignature(sig []byte) { - m.Signature = sig -} diff --git a/container/types.pb.go b/container/types.pb.go deleted file mode 100644 index 391656c..0000000 Binary files a/container/types.pb.go and /dev/null differ diff --git a/container/types.proto b/container/types.proto deleted file mode 100644 index dc79bd3..0000000 --- a/container/types.proto +++ /dev/null @@ -1,24 +0,0 @@ -syntax = "proto3"; -package container; -option go_package = "github.com/nspcc-dev/neofs-api-go/container"; -option csharp_namespace = "NeoFS.API.Container"; - -import "github.com/nspcc-dev/netmap/selector.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -// The Container service definition. -message Container { - // OwnerID is a wallet address. - bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // Salt is a nonce for unique container id calculation. - bytes Salt = 2 [(gogoproto.customtype) = "UUID", (gogoproto.nullable) = false]; - // Capacity defines amount of data that can be stored in the container (doesn't used for now). - uint64 Capacity = 3; - // Rules define storage policy for the object inside the container. - netmap.PlacementRule Rules = 4 [(gogoproto.nullable) = false]; - // BasicACL with access control rules for owner, system, others and - // permission bits for bearer token and extended ACL. - uint32 BasicACL = 5; -} diff --git a/container/types_test.go b/container/types_test.go deleted file mode 100644 index 76bbe1c..0000000 --- a/container/types_test.go +++ /dev/null @@ -1,162 +0,0 @@ -package container - -import ( - "testing" - - "github.com/gogo/protobuf/proto" - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/nspcc-dev/netmap" - "github.com/stretchr/testify/require" -) - -func TestCID(t *testing.T) { - t.Run("check that marshal/unmarshal works like expected", func(t *testing.T) { - var ( - c2 Container - cid2 CID - key = test.DecodeKey(0) - ) - - rules := netmap.PlacementRule{ - ReplFactor: 2, - SFGroups: []netmap.SFGroup{ - { - Selectors: []netmap.Select{ - {Key: "Country", Count: 1}, - {Key: netmap.NodesBucket, Count: 2}, - }, - Filters: []netmap.Filter{ - {Key: "Country", F: netmap.FilterIn("USA")}, - }, - }, - }, - } - - owner, err := refs.NewOwnerID(&key.PublicKey) - require.NoError(t, err) - - c1, err := New(10, owner, 0xDEADBEEF, rules) - require.NoError(t, err) - - data, err := proto.Marshal(c1) - require.NoError(t, err) - - require.NoError(t, c2.Unmarshal(data)) - require.Equal(t, c1, &c2) - - cid1, err := c1.ID() - require.NoError(t, err) - - data, err = proto.Marshal(&cid1) - require.NoError(t, err) - require.NoError(t, cid2.Unmarshal(data)) - - require.Equal(t, cid1, cid2) - }) -} - -func TestPutRequestGettersSetters(t *testing.T) { - t.Run("owner", func(t *testing.T) { - owner := OwnerID{1, 2, 3} - m := new(PutRequest) - - m.SetOwnerID(owner) - - require.Equal(t, owner, m.GetOwnerID()) - }) - - t.Run("capacity", func(t *testing.T) { - cp := uint64(3) - m := new(PutRequest) - - m.SetCapacity(cp) - - require.Equal(t, cp, m.GetCapacity()) - }) - - t.Run("message ID", func(t *testing.T) { - id, err := refs.NewUUID() - require.NoError(t, err) - - m := new(PutRequest) - - m.SetMessageID(id) - - require.Equal(t, id, m.GetMessageID()) - }) - - t.Run("rules", func(t *testing.T) { - rules := netmap.PlacementRule{ - ReplFactor: 1, - } - - m := new(PutRequest) - - m.SetRules(rules) - - require.Equal(t, rules, m.GetRules()) - }) - - t.Run("basic ACL", func(t *testing.T) { - bACL := uint32(5) - m := new(PutRequest) - - m.SetBasicACL(bACL) - - require.Equal(t, bACL, m.GetBasicACL()) - }) -} - -func TestDeleteRequestGettersSetters(t *testing.T) { - t.Run("cid", func(t *testing.T) { - cid := CID{1, 2, 3} - m := new(DeleteRequest) - - m.SetCID(cid) - - require.Equal(t, cid, m.GetCID()) - }) -} - -func TestGetRequestGettersSetters(t *testing.T) { - t.Run("cid", func(t *testing.T) { - cid := CID{1, 2, 3} - m := new(GetRequest) - - m.SetCID(cid) - - require.Equal(t, cid, m.GetCID()) - }) -} - -func TestListRequestGettersSetters(t *testing.T) { - t.Run("owner", func(t *testing.T) { - owner := OwnerID{1, 2, 3} - m := new(PutRequest) - - m.SetOwnerID(owner) - - require.Equal(t, owner, m.GetOwnerID()) - }) -} - -func TestExtendedACLKey(t *testing.T) { - s := new(ExtendedACLKey) - - id := CID{1, 2, 3} - s.SetID(id) - require.Equal(t, id, s.GetID()) -} - -func TestExtendedACLValue(t *testing.T) { - s := new(ExtendedACLValue) - - acl := []byte{1, 2, 3} - s.SetEACL(acl) - require.Equal(t, acl, s.GetEACL()) - - sig := []byte{4, 5, 6} - s.SetSignature(sig) - require.Equal(t, sig, s.GetSignature()) -} diff --git a/decimal/decimal.go b/decimal/decimal.go deleted file mode 100644 index 2ee0088..0000000 --- a/decimal/decimal.go +++ /dev/null @@ -1,110 +0,0 @@ -package decimal - -import ( - "math" - "strconv" - "strings" -) - -// GASPrecision contains precision for NEO Gas token. -const GASPrecision = 8 - -// Zero is empty Decimal value. -var Zero = &Decimal{} - -// New returns new Decimal (in satoshi). -func New(v int64) *Decimal { - return NewWithPrecision(v, GASPrecision) -} - -// NewGAS returns new Decimal * 1e8 (in GAS). -func NewGAS(v int64) *Decimal { - v *= int64(math.Pow10(GASPrecision)) - return NewWithPrecision(v, GASPrecision) -} - -// NewWithPrecision returns new Decimal with custom precision. -func NewWithPrecision(v int64, p uint32) *Decimal { - return &Decimal{Value: v, Precision: p} -} - -// ParseFloat return new Decimal parsed from float64 * 1e8 (in GAS). -func ParseFloat(v float64) *Decimal { - return new(Decimal).Parse(v, GASPrecision) -} - -// ParseFloatWithPrecision returns new Decimal parsed from float64 * 1^p. -func ParseFloatWithPrecision(v float64, p int) *Decimal { - return new(Decimal).Parse(v, p) -} - -// Copy returns copy of current Decimal. -func (m *Decimal) Copy() *Decimal { return &Decimal{Value: m.Value, Precision: m.Precision} } - -// Parse returns parsed Decimal from float64 * 1^p. -func (m *Decimal) Parse(v float64, p int) *Decimal { - m.Value = int64(v * math.Pow10(p)) - m.Precision = uint32(p) - return m -} - -// String returns string representation of Decimal. -func (m Decimal) String() string { - buf := new(strings.Builder) - val := m.Value - dec := int64(math.Pow10(int(m.Precision))) - if val < 0 { - buf.WriteRune('-') - val = -val - } - str := strconv.FormatInt(val/dec, 10) - buf.WriteString(str) - val %= dec - if val > 0 { - buf.WriteRune('.') - str = strconv.FormatInt(val, 10) - for i := len(str); i < int(m.Precision); i++ { - buf.WriteRune('0') - } - buf.WriteString(strings.TrimRight(str, "0")) - } - return buf.String() -} - -// Add returns d + m. -func (m Decimal) Add(d *Decimal) *Decimal { - precision := m.Precision - if precision < d.Precision { - precision = d.Precision - } - return &Decimal{ - Value: m.Value + d.Value, - Precision: precision, - } -} - -// Zero checks that Decimal is empty. -func (m Decimal) Zero() bool { return m.Value == 0 } - -// Equal checks that current Decimal is equal to passed Decimal. -func (m Decimal) Equal(v *Decimal) bool { return m.Value == v.Value && m.Precision == v.Precision } - -// GT checks that m > v. -func (m Decimal) GT(v *Decimal) bool { return m.Value > v.Value } - -// GTE checks that m >= v. -func (m Decimal) GTE(v *Decimal) bool { return m.Value >= v.Value } - -// LT checks that m < v. -func (m Decimal) LT(v *Decimal) bool { return m.Value < v.Value } - -// LTE checks that m <= v. -func (m Decimal) LTE(v *Decimal) bool { return m.Value <= v.Value } - -// Neg returns negative representation of current Decimal (m * -1). -func (m Decimal) Neg() *Decimal { - return &Decimal{ - Value: m.Value * -1, - Precision: m.Precision, - } -} diff --git a/decimal/decimal.pb.go b/decimal/decimal.pb.go deleted file mode 100644 index 9108e61..0000000 Binary files a/decimal/decimal.pb.go and /dev/null differ diff --git a/decimal/decimal.proto b/decimal/decimal.proto deleted file mode 100644 index f3d5c00..0000000 --- a/decimal/decimal.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; -package decimal; -option go_package = "github.com/nspcc-dev/neofs-api-go/decimal"; -option csharp_namespace = "NeoFS.API.Decimal"; - -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -// Decimal is a structure used for representation of assets amount -message Decimal { - option (gogoproto.goproto_stringer) = false; - - // Value is value number - int64 Value = 1; - // Precision is precision number - uint32 Precision = 2; -} diff --git a/decimal/decimal_test.go b/decimal/decimal_test.go deleted file mode 100644 index c5ffef7..0000000 --- a/decimal/decimal_test.go +++ /dev/null @@ -1,445 +0,0 @@ -package decimal - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestDecimal_Parse(t *testing.T) { - tests := []struct { - value float64 - name string - expect *Decimal - }{ - {name: "empty", expect: &Decimal{Precision: GASPrecision}}, - - { - value: 100, - name: "100 GAS", - expect: &Decimal{Value: 1e10, Precision: GASPrecision}, - }, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.expect, ParseFloat(tt.value)) - }) - } -} - -func TestDecimal_ParseWithPrecision(t *testing.T) { - type args struct { - v float64 - p int - } - tests := []struct { - args args - name string - expect *Decimal - }{ - {name: "empty", expect: &Decimal{}}, - - { - name: "empty precision", - expect: &Decimal{Value: 0, Precision: 0}, - }, - - { - name: "100 GAS", - args: args{100, GASPrecision}, - expect: &Decimal{Value: 1e10, Precision: GASPrecision}, - }, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.expect, - ParseFloatWithPrecision(tt.args.v, tt.args.p)) - }) - } -} - -func TestNew(t *testing.T) { - tests := []struct { - name string - val int64 - expect *Decimal - }{ - {name: "empty", expect: &Decimal{Value: 0, Precision: GASPrecision}}, - {name: "100 GAS", val: 1e10, expect: &Decimal{Value: 1e10, Precision: GASPrecision}}, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.Equalf(t, tt.expect, New(tt.val), tt.name) - }) - } -} - -func TestNewGAS(t *testing.T) { - tests := []struct { - name string - val int64 - expect *Decimal - }{ - {name: "empty", expect: &Decimal{Value: 0, Precision: GASPrecision}}, - {name: "100 GAS", val: 100, expect: &Decimal{Value: 1e10, Precision: GASPrecision}}, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.Equalf(t, tt.expect, NewGAS(tt.val), tt.name) - }) - } -} -func TestNewWithPrecision(t *testing.T) { - tests := []struct { - name string - val int64 - pre uint32 - expect *Decimal - }{ - {name: "empty", expect: &Decimal{}}, - {name: "100 GAS", val: 1e10, pre: GASPrecision, expect: &Decimal{Value: 1e10, Precision: GASPrecision}}, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.Equalf(t, tt.expect, NewWithPrecision(tt.val, tt.pre), tt.name) - }) - } -} - -func TestDecimal_Neg(t *testing.T) { - tests := []struct { - name string - val int64 - expect *Decimal - }{ - {name: "empty", expect: &Decimal{Value: 0, Precision: GASPrecision}}, - {name: "100 GAS", val: 1e10, expect: &Decimal{Value: -1e10, Precision: GASPrecision}}, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.NotPanicsf(t, func() { - require.Equalf(t, tt.expect, New(tt.val).Neg(), tt.name) - }, tt.name) - }) - } -} - -func TestDecimal_String(t *testing.T) { - tests := []struct { - name string - expect string - value *Decimal - }{ - {name: "empty", expect: "0", value: &Decimal{}}, - {name: "100 GAS", expect: "100", value: &Decimal{Value: 1e10, Precision: GASPrecision}}, - {name: "-100 GAS", expect: "-100", value: &Decimal{Value: -1e10, Precision: GASPrecision}}, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.Equalf(t, tt.expect, tt.value.String(), tt.name) - }) - } -} - -const SomethingElsePrecision = 5 - -func TestDecimal_Add(t *testing.T) { - tests := []struct { - name string - expect *Decimal - values [2]*Decimal - }{ - {name: "empty", expect: &Decimal{}, values: [2]*Decimal{{}, {}}}, - { - name: "5 GAS + 2 GAS", - expect: &Decimal{Value: 7e8, Precision: GASPrecision}, - values: [2]*Decimal{ - {Value: 2e8, Precision: GASPrecision}, - {Value: 5e8, Precision: GASPrecision}, - }, - }, - { - name: "1e2 + 1e3", - expect: &Decimal{Value: 1.1e3, Precision: 3}, - values: [2]*Decimal{ - {Value: 1e2, Precision: 2}, - {Value: 1e3, Precision: 3}, - }, - }, - { - name: "5 GAS + 10 SomethingElse", - expect: &Decimal{Value: 5.01e8, Precision: GASPrecision}, - values: [2]*Decimal{ - {Value: 5e8, Precision: GASPrecision}, - {Value: 1e6, Precision: SomethingElsePrecision}, - }, - }, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.NotPanicsf(t, func() { - { // A + B - one := tt.values[0] - two := tt.values[1] - require.Equalf(t, tt.expect, one.Add(two), tt.name) - t.Log(one.Add(two)) - } - - { // B + A - one := tt.values[0] - two := tt.values[1] - require.Equalf(t, tt.expect, two.Add(one), tt.name) - t.Log(two.Add(one)) - } - }, tt.name) - }) - } -} - -func TestDecimal_Copy(t *testing.T) { - tests := []struct { - name string - expect *Decimal - value *Decimal - }{ - {name: "zero", expect: Zero}, - { - name: "5 GAS", - expect: &Decimal{Value: 5e8, Precision: GASPrecision}, - }, - { - name: "100 GAS", - expect: &Decimal{Value: 1e10, Precision: GASPrecision}, - }, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.NotPanicsf(t, func() { - require.Equal(t, tt.expect, tt.expect.Copy()) - }, tt.name) - }) - } -} - -func TestDecimal_Zero(t *testing.T) { - tests := []struct { - name string - expect bool - value *Decimal - }{ - {name: "zero", expect: true, value: Zero}, - { - name: "5 GAS", - expect: false, - value: &Decimal{Value: 5e8, Precision: GASPrecision}, - }, - { - name: "100 GAS", - expect: false, - value: &Decimal{Value: 1e10, Precision: GASPrecision}, - }, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.NotPanicsf(t, func() { - require.Truef(t, tt.expect == tt.value.Zero(), tt.name) - }, tt.name) - }) - } -} - -func TestDecimal_Equal(t *testing.T) { - tests := []struct { - name string - expect bool - values [2]*Decimal - }{ - {name: "zero == zero", expect: true, values: [2]*Decimal{Zero, Zero}}, - { - name: "5 GAS != 2 GAS", - expect: false, - values: [2]*Decimal{ - {Value: 5e8, Precision: GASPrecision}, - {Value: 2e8, Precision: GASPrecision}, - }, - }, - { - name: "100 GAS == 100 GAS", - expect: true, - values: [2]*Decimal{ - {Value: 1e10, Precision: GASPrecision}, - {Value: 1e10, Precision: GASPrecision}, - }, - }, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.NotPanicsf(t, func() { - require.Truef(t, tt.expect == (tt.values[0].Equal(tt.values[1])), tt.name) - }, tt.name) - }) - } -} - -func TestDecimal_GT(t *testing.T) { - tests := []struct { - name string - expect bool - values [2]*Decimal - }{ - {name: "two zeros", expect: false, values: [2]*Decimal{Zero, Zero}}, - { - name: "5 GAS > 2 GAS", - expect: true, - values: [2]*Decimal{ - {Value: 5e8, Precision: GASPrecision}, - {Value: 2e8, Precision: GASPrecision}, - }, - }, - { - name: "100 GAS !> 100 GAS", - expect: false, - values: [2]*Decimal{ - {Value: 1e10, Precision: GASPrecision}, - {Value: 1e10, Precision: GASPrecision}, - }, - }, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.NotPanicsf(t, func() { - require.Truef(t, tt.expect == (tt.values[0].GT(tt.values[1])), tt.name) - }, tt.name) - }) - } -} - -func TestDecimal_GTE(t *testing.T) { - tests := []struct { - name string - expect bool - values [2]*Decimal - }{ - {name: "two zeros", expect: true, values: [2]*Decimal{Zero, Zero}}, - { - name: "5 GAS >= 2 GAS", - expect: true, - values: [2]*Decimal{ - {Value: 5e8, Precision: GASPrecision}, - {Value: 2e8, Precision: GASPrecision}, - }, - }, - { - name: "1 GAS !>= 100 GAS", - expect: false, - values: [2]*Decimal{ - {Value: 1e8, Precision: GASPrecision}, - {Value: 1e10, Precision: GASPrecision}, - }, - }, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.NotPanicsf(t, func() { - require.Truef(t, tt.expect == (tt.values[0].GTE(tt.values[1])), tt.name) - }, tt.name) - }) - } -} - -func TestDecimal_LT(t *testing.T) { - tests := []struct { - name string - expect bool - values [2]*Decimal - }{ - {name: "two zeros", expect: false, values: [2]*Decimal{Zero, Zero}}, - { - name: "5 GAS !< 2 GAS", - expect: false, - values: [2]*Decimal{ - {Value: 5e8, Precision: GASPrecision}, - {Value: 2e8, Precision: GASPrecision}, - }, - }, - { - name: "1 GAS < 100 GAS", - expect: true, - values: [2]*Decimal{ - {Value: 1e8, Precision: GASPrecision}, - {Value: 1e10, Precision: GASPrecision}, - }, - }, - { - name: "100 GAS !< 100 GAS", - expect: false, - values: [2]*Decimal{ - {Value: 1e10, Precision: GASPrecision}, - {Value: 1e10, Precision: GASPrecision}, - }, - }, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.NotPanicsf(t, func() { - require.Truef(t, tt.expect == (tt.values[0].LT(tt.values[1])), tt.name) - }, tt.name) - }) - } -} - -func TestDecimal_LTE(t *testing.T) { - tests := []struct { - name string - expect bool - values [2]*Decimal - }{ - {name: "two zeros", expect: true, values: [2]*Decimal{Zero, Zero}}, - { - name: "5 GAS <= 2 GAS", - expect: false, - values: [2]*Decimal{ - {Value: 5e8, Precision: GASPrecision}, - {Value: 2e8, Precision: GASPrecision}, - }, - }, - { - name: "1 GAS <= 100 GAS", - expect: true, - values: [2]*Decimal{ - {Value: 1e8, Precision: GASPrecision}, - {Value: 1e10, Precision: GASPrecision}, - }, - }, - { - name: "100 GAS !<= 1 GAS", - expect: false, - values: [2]*Decimal{ - {Value: 1e10, Precision: GASPrecision}, - {Value: 1e8, Precision: GASPrecision}, - }, - }, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - require.NotPanicsf(t, func() { - require.Truef(t, tt.expect == (tt.values[0].LTE(tt.values[1])), tt.name) - }, tt.name) - }) - } -} diff --git a/hash/hash.go b/hash/hash.go deleted file mode 100644 index 4e2fb05..0000000 --- a/hash/hash.go +++ /dev/null @@ -1,106 +0,0 @@ -package hash - -import ( - "bytes" - - "github.com/gogo/protobuf/proto" - "github.com/mr-tron/base58" - "github.com/nspcc-dev/neofs-api-go/internal" - "github.com/nspcc-dev/tzhash/tz" - "github.com/pkg/errors" -) - -// HomomorphicHashSize contains size of HH. -const HomomorphicHashSize = 64 - -// Hash is implementation of HomomorphicHash. -type Hash [HomomorphicHashSize]byte - -// ErrWrongDataSize raised when wrong size of bytes is passed to unmarshal HH. -const ErrWrongDataSize = internal.Error("wrong data size") - -var ( - _ internal.Custom = (*Hash)(nil) - - emptyHH [HomomorphicHashSize]byte -) - -// Size returns size of Hash (HomomorphicHashSize). -func (h Hash) Size() int { return HomomorphicHashSize } - -// Empty checks that Hash is empty. -func (h Hash) Empty() bool { return bytes.Equal(h.Bytes(), emptyHH[:]) } - -// Reset sets current Hash to empty value. -func (h *Hash) Reset() { *h = Hash{} } - -// ProtoMessage method to satisfy proto.Message interface. -func (h Hash) ProtoMessage() {} - -// Bytes represents Hash as bytes. -func (h Hash) Bytes() []byte { - buf := make([]byte, HomomorphicHashSize) - copy(buf, h[:]) - return h[:] -} - -// Marshal returns bytes representation of Hash. -func (h Hash) Marshal() ([]byte, error) { return h.Bytes(), nil } - -// MarshalTo tries to marshal Hash into passed bytes and returns count of copied bytes. -func (h *Hash) MarshalTo(data []byte) (int, error) { return copy(data, h.Bytes()), nil } - -// Unmarshal tries to parse bytes into valid Hash. -func (h *Hash) Unmarshal(data []byte) error { - if ln := len(data); ln != HomomorphicHashSize { - return errors.Wrapf(ErrWrongDataSize, "expect=%d, actual=%d", HomomorphicHashSize, ln) - } - - copy((*h)[:], data) - return nil -} - -// String returns string representation of Hash. -func (h Hash) String() string { return base58.Encode(h[:]) } - -// Equal checks that current Hash is equal to passed Hash. -func (h Hash) Equal(hash Hash) bool { return h == hash } - -// Verify validates if current hash generated from passed data. -func (h Hash) Verify(data []byte) bool { return h.Equal(Sum(data)) } - -// Validate checks if combined hashes are equal to current Hash. -func (h Hash) Validate(hashes []Hash) bool { - hashBytes := make([][]byte, 0, len(hashes)) - for i := range hashes { - hashBytes = append(hashBytes, hashes[i].Bytes()) - } - ok, err := tz.Validate(h.Bytes(), hashBytes) - return err == nil && ok -} - -// Merge used by proto.Clone -func (h *Hash) Merge(src proto.Message) { - if tmp, ok := src.(*Hash); ok { - *h = *tmp - } -} - -// Sum returns Tillich-Zémor checksum of data. -func Sum(data []byte) Hash { return tz.Sum(data) } - -// Concat combines hashes based on homomorphic property. -func Concat(hashes []Hash) (Hash, error) { - var ( - hash Hash - h = make([][]byte, 0, len(hashes)) - ) - for i := range hashes { - h = append(h, hashes[i].Bytes()) - } - cat, err := tz.Concat(h) - if err != nil { - return hash, err - } - return hash, hash.Unmarshal(cat) -} diff --git a/hash/hash_test.go b/hash/hash_test.go deleted file mode 100644 index b3ef703..0000000 --- a/hash/hash_test.go +++ /dev/null @@ -1,166 +0,0 @@ -package hash - -import ( - "bytes" - "crypto/rand" - "testing" - - "github.com/pkg/errors" - "github.com/stretchr/testify/require" -) - -func Test_Sum(t *testing.T) { - var ( - data = []byte("Hello world") - sum = Sum(data) - hash = []byte{0, 0, 0, 0, 1, 79, 16, 173, 134, 90, 176, 77, 114, 165, 253, 114, 0, 0, 0, 0, 0, 148, - 172, 222, 98, 248, 15, 99, 205, 129, 66, 91, 0, 0, 0, 0, 0, 138, 173, 39, 228, 231, 239, 123, - 170, 96, 186, 61, 0, 0, 0, 0, 0, 90, 69, 237, 131, 90, 161, 73, 38, 164, 185, 55} - ) - - require.Equal(t, hash, sum.Bytes()) -} - -func Test_Validate(t *testing.T) { - var ( - data = []byte("Hello world") - hash = Sum(data) - pieces = splitData(data, 2) - ln = len(pieces) - hashes = make([]Hash, 0, ln) - ) - - for i := 0; i < ln; i++ { - hashes = append(hashes, Sum(pieces[i])) - } - - require.True(t, hash.Validate(hashes)) -} - -func Test_Concat(t *testing.T) { - var ( - data = []byte("Hello world") - hash = Sum(data) - pieces = splitData(data, 2) - ln = len(pieces) - hashes = make([]Hash, 0, ln) - ) - - for i := 0; i < ln; i++ { - hashes = append(hashes, Sum(pieces[i])) - } - - res, err := Concat(hashes) - require.NoError(t, err) - require.Equal(t, hash, res) -} - -func Test_HashChunks(t *testing.T) { - var ( - chars = []byte("+") - size = 1400 - data = bytes.Repeat(chars, size) - hash = Sum(data) - count = 150 - ) - - hashes, err := dataHashes(data, count) - require.NoError(t, err) - require.Len(t, hashes, count) - - require.True(t, hash.Validate(hashes)) - - // 100 / 150 = 0 - hashes, err = dataHashes(data[:100], count) - require.Error(t, err) - require.Nil(t, hashes) -} - -func TestXOR(t *testing.T) { - var ( - dl = 10 - data = make([]byte, dl) - ) - - _, err := rand.Read(data) - require.NoError(t, err) - - t.Run("XOR with salt", func(t *testing.T) { - res := SaltXOR(data, nil) - require.Equal(t, res, data) - }) - - t.Run("XOR with empty salt", func(t *testing.T) { - xorWithSalt(t, data, 0) - }) - - t.Run("XOR with salt same data size", func(t *testing.T) { - xorWithSalt(t, data, dl) - }) - - t.Run("XOR with salt shorter than data aliquot", func(t *testing.T) { - xorWithSalt(t, data, dl/2) - }) - - t.Run("XOR with salt shorter than data aliquant", func(t *testing.T) { - xorWithSalt(t, data, dl/3/+1) - }) - - t.Run("XOR with salt longer than data aliquot", func(t *testing.T) { - xorWithSalt(t, data, dl*2) - }) - - t.Run("XOR with salt longer than data aliquant", func(t *testing.T) { - xorWithSalt(t, data, dl*2-1) - }) -} - -func xorWithSalt(t *testing.T, data []byte, saltSize int) { - var ( - direct, reverse []byte - salt = make([]byte, saltSize) - ) - - _, err := rand.Read(salt) - require.NoError(t, err) - - direct = SaltXOR(data, salt) - require.Len(t, direct, len(data)) - - reverse = SaltXOR(direct, salt) - require.Len(t, reverse, len(data)) - - require.Equal(t, reverse, data) -} - -func splitData(buf []byte, lim int) [][]byte { - var piece []byte - pieces := make([][]byte, 0, len(buf)/lim+1) - for len(buf) >= lim { - piece, buf = buf[:lim], buf[lim:] - pieces = append(pieces, piece) - } - if len(buf) > 0 { - pieces = append(pieces, buf) - } - return pieces -} - -func dataHashes(data []byte, count int) ([]Hash, error) { - var ( - ln = len(data) - mis = ln / count - off = (count - 1) * mis - hashes = make([]Hash, 0, count) - ) - if mis == 0 { - return nil, errors.Errorf("could not split %d bytes to %d pieces", ln, count) - } - - pieces := splitData(data[:off], mis) - pieces = append(pieces, data[off:]) - for i := 0; i < count; i++ { - hashes = append(hashes, Sum(pieces[i])) - } - return hashes, nil -} diff --git a/hash/hashesslice.go b/hash/hashesslice.go deleted file mode 100644 index 83bf4c5..0000000 --- a/hash/hashesslice.go +++ /dev/null @@ -1,20 +0,0 @@ -package hash - -import ( - "bytes" -) - -// HashesSlice is a collection that satisfies sort.Interface and can be -// sorted by the routines in sort package. -type HashesSlice []Hash - -// -- HashesSlice -- an inner type to sort Objects -// Len is the number of elements in the collection. -func (hs HashesSlice) Len() int { return len(hs) } - -// Less reports whether the element with -// index i should be sorted before the element with index j. -func (hs HashesSlice) Less(i, j int) bool { return bytes.Compare(hs[i].Bytes(), hs[j].Bytes()) == -1 } - -// Swap swaps the elements with indexes i and j. -func (hs HashesSlice) Swap(i, j int) { hs[i], hs[j] = hs[j], hs[i] } diff --git a/hash/salt.go b/hash/salt.go deleted file mode 100644 index 5b6eeb0..0000000 --- a/hash/salt.go +++ /dev/null @@ -1,17 +0,0 @@ -package hash - -// SaltXOR xors bits of data with salt -// repeating salt if necessary. -func SaltXOR(data, salt []byte) (result []byte) { - result = make([]byte, len(data)) - ls := len(salt) - if ls == 0 { - copy(result, data) - return - } - - for i := range result { - result[i] = data[i] ^ salt[i%ls] - } - return -} diff --git a/internal/error.go b/internal/error.go deleted file mode 100644 index 7df1603..0000000 --- a/internal/error.go +++ /dev/null @@ -1,7 +0,0 @@ -package internal - -// Error is a custom error. -type Error string - -// Error is an implementation of error interface. -func (e Error) Error() string { return string(e) } diff --git a/internal/proto.go b/internal/proto.go deleted file mode 100644 index 9a924b5..0000000 --- a/internal/proto.go +++ /dev/null @@ -1,19 +0,0 @@ -package internal - -import "github.com/gogo/protobuf/proto" - -// Custom contains methods to satisfy proto.Message -// including custom methods to satisfy protobuf for -// non-proto defined types. -type Custom interface { - Size() int - Empty() bool - Bytes() []byte - Marshal() ([]byte, error) - MarshalTo(data []byte) (int, error) - Unmarshal(data []byte) error - proto.Message - - // Should contains for proto.Clone - proto.Merger -} diff --git a/object/doc.go b/object/doc.go deleted file mode 100644 index d81495b..0000000 --- a/object/doc.go +++ /dev/null @@ -1,143 +0,0 @@ -/* -Package object manages main storage structure in the system. All storage -operations are performed with the objects. During lifetime object might be -transformed into another object by cutting its payload or adding meta -information. All transformation may be reversed, therefore source object -will be able to restore. - -Object structure - -Object consists of Payload and Header. Payload is unlimited but storage nodes -may have a policy to store objects with a limited payload. In this case object -with large payload will be transformed into the chain of objects with small -payload. - -Headers are simple key-value fields that divided into two groups: system -headers and extended headers. System headers contain information about -protocol version, object id, payload length in bytes, owner id, container id -and object creation timestamp (both in epochs and unix time). All these fields -must be set up in the correct object. - - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - | System Headers | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - | Version : 1 | - | Payload Length : 21673465 | - | Object ID : 465208e2-ba4f-4f99-ad47-82a59f4192d4 | - | Owner ID : AShvoCbSZ7VfRiPkVb1tEcBLiJrcbts1tt | - | Container ID : FGobtRZA6sBZv2i9k4L7TiTtnuP6E788qa278xfj3Fxj | - | Created At : Epoch#10, 1573033162 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - | Extended Headers | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - | User Header : , | - | Verification Header : , | - | Homomorphic Hash : 0x23d35a56ae... | - | Payload Checksum : 0x1bd34abs75... | - | Integrity Header :
, | - | Transformation : Payload Split | - | Link-parent : cae08935-b4ba-499a-bf6c-98276c1e6c0b | - | Link-next : c3b40fbf-3798-4b61-a189-2992b5fb5070 | - | Payload Checksum : 0x1f387a5c36... | - | Integrity Header :
, | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - | Payload | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - | 0xd1581963a342d231... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - -There are different kinds of extended headers. A correct object must contain -verification header, homomorphic hash header, payload checksum and -integrity header. The order of headers is matter. Let's look through all -these headers. - -Link header points to the connected objects. During object transformation, large -object might be transformed into the chain of smaller objects. One of these -objects drops payload and has several "Child" links. We call this object as -zero-object. Others will have "Parent" link to the zero-object, "Previous" -and "Next" links in the payload chain. - - [ Object ID:1 ] = > transformed - `- [ Zero-Object ID:1 ] - `- Link-child ID:2 - `- Link-child ID:3 - `- Link-child ID:4 - `- Payload [null] - `- [ Object ID:2 ] - `- Link-parent ID:1 - `- Link-next ID:3 - `- Payload [ 0x13ba... ] - `- [ Object ID:3 ] - `- Link-parent ID:1 - `- Link-previous ID:2 - `- Link-next ID:4 - `- Payload [ 0xcd34... ] - `- [ Object ID:4 ] - `- Link-parent ID:1 - `- Link-previous ID:3 - `- Payload [ 0xef86... ] - -Storage groups are also objects. They have "Storage Group" links to all -objects in the group. Links are set by nodes during transformations and, -in general, they should not be set by user manually. - -Redirect headers are not used yet, they will be implemented and described -later. - -User header is a key-value pair of string that can be defined by user. User -can use these headers as search attribute. You can store any meta information -about object there, e.g. object's nicename. - -Transformation header notifies that object was transformed by some pre-defined -way. This header sets up before object is transformed and all headers after -transformation must be located after transformation header. During reverse -transformation, all headers under transformation header will be cut out. - - +-+-+-+-+-+-+-+-+-+- +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+ - | Payload checksum | | Payload checksum | | Payload checksum | - | Integrity header | => | Integrity header | + | Integrity header | - +-+-+-+-+-+-+-+-+-+- | Transformation | | Transformation | - | Large payload | | New Checksum | | New Checksum | - +-+-+-+-+-+-+-+-+-+- | New Integrity | | New Integrity | - +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+ - | Small payload | | Small payload | - +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+ - -For now, we use only one type of transformation: payload split transformation. -This header set up by node automatically. - -Tombstone header notifies that this object was deleted by user. Objects with -tombstone header do not have payload, but they still contain meta information -in the headers. This way we implement two-phase commit for object removal. -Storage nodes will eventually delete all tombstone objects. If you want to -delete object, you must create new object with the same object id, with -tombstone header, correct signatures and without payload. - -Verification header contains session information. To put the object in -the system user must create session. It is required because objects might -be transformed and therefore must be re-signed. To do that node creates -a pair of session public and private keys. Object owner delegates permission to -re-sign objects by signing session public key. This header contains session -public key and owner's signature of this key. You must specify this header -manually. - -Homomorphic hash header contains homomorphic hash of the source object. -Transformations do not affect this header. This header used by data audit and -set by node automatically. - -Payload checksum contains checksum of the actual object payload. All payload -transformation must set new payload checksum headers. This header set by node -automatically. - -Integrity header contains checksum of the header and signature of the -session key. This header must be last in the list of extended headers. -Checksum is calculated by marshaling all above headers, including system -headers. This header set by node automatically. - -Storage group header is presented in storage group objects. It contains -information for data audit: size of validated data, homomorphic has of this -data, storage group expiration time in epochs or unix time. - - -*/ -package object diff --git a/object/extensions.go b/object/extensions.go deleted file mode 100644 index be755c6..0000000 --- a/object/extensions.go +++ /dev/null @@ -1,64 +0,0 @@ -package object - -// todo: all extensions must be transferred to the separate util library - -import "github.com/nspcc-dev/neofs-api-go/storagegroup" - -// IsLinking checks if object has children links to another objects. -// We have to check payload size because zero-object must have zero -// payload and non-zero payload length field in system header. -func (m Object) IsLinking() bool { - for i := range m.Headers { - switch v := m.Headers[i].Value.(type) { - case *Header_Link: - if v.Link.GetType() == Link_Child { - return m.SystemHeader.PayloadLength > 0 && len(m.Payload) == 0 - } - } - } - return false -} - -// Links returns slice of ids of specified link type -func (m *Object) Links(t Link_Type) []ID { - var res []ID - for i := range m.Headers { - switch v := m.Headers[i].Value.(type) { - case *Header_Link: - if v.Link.GetType() == t { - res = append(res, v.Link.ID) - } - } - } - return res -} - -// Tombstone returns tombstone header if it is presented in extended headers. -func (m Object) Tombstone() *Tombstone { - _, h := m.LastHeader(HeaderType(TombstoneHdr)) - if h != nil { - return h.Value.(*Header_Tombstone).Tombstone - } - return nil -} - -// IsTombstone checks if object has tombstone header. -func (m Object) IsTombstone() bool { - n, _ := m.LastHeader(HeaderType(TombstoneHdr)) - return n != -1 -} - -// StorageGroup returns storage group structure if it is presented in extended headers. -func (m Object) StorageGroup() (*storagegroup.StorageGroup, error) { - _, sgHdr := m.LastHeader(HeaderType(StorageGroupHdr)) - if sgHdr == nil { - return nil, ErrHeaderNotFound - } - return sgHdr.Value.(*Header_StorageGroup).StorageGroup, nil -} - -// SetStorageGroup sets storage group header in the object. -// It will replace existing storage group header or add a new one. -func (m *Object) SetStorageGroup(group *storagegroup.StorageGroup) { - m.SetHeader(&Header{Value: &Header_StorageGroup{StorageGroup: group}}) -} diff --git a/object/service.go b/object/service.go deleted file mode 100644 index 0e38d70..0000000 --- a/object/service.go +++ /dev/null @@ -1,196 +0,0 @@ -package object - -import ( - "github.com/nspcc-dev/neofs-api-go/hash" - "github.com/nspcc-dev/neofs-api-go/internal" - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-api-go/session" -) - -type ( - // ID is a type alias of object id. - ID = refs.ObjectID - - // CID is a type alias of container id. - CID = refs.CID - - // SGID is a type alias of storage group id. - SGID = refs.SGID - - // OwnerID is a type alias of owner id. - OwnerID = refs.OwnerID - - // Hash is a type alias of Homomorphic hash. - Hash = hash.Hash - - // Token is a type alias of session token. - Token = session.Token - - // Request defines object rpc requests. - // All object operations must have TTL, Epoch, Type, Container ID and - // permission of usage previous network map. - Request interface { - service.SeizedRequestMetaContainer - - CID() CID - Type() RequestType - AllowPreviousNetMap() bool - } -) - -const ( - // starts enum for amount of bytes. - _ int64 = 1 << (10 * iota) - - // UnitsKB defines amount of bytes in one kilobyte. - UnitsKB - - // UnitsMB defines amount of bytes in one megabyte. - UnitsMB - - // UnitsGB defines amount of bytes in one gigabyte. - UnitsGB - - // UnitsTB defines amount of bytes in one terabyte. - UnitsTB -) - -const ( - // ErrNotFound is raised when object is not found in the system. - ErrNotFound = internal.Error("could not find object") - - // ErrHeaderExpected is raised when first message in protobuf stream does not contain user header. - ErrHeaderExpected = internal.Error("expected header as a first message in stream") - - // KeyStorageGroup is a key for a search object by storage group id. - KeyStorageGroup = "STORAGE_GROUP" - - // KeyNoChildren is a key for searching object that have no children links. - KeyNoChildren = "LEAF" - - // KeyParent is a key for searching object by id of parent object. - KeyParent = "PARENT" - - // KeyHasParent is a key for searching object that have parent link. - KeyHasParent = "HAS_PAR" - - // KeyTombstone is a key for searching object that have tombstone header. - KeyTombstone = "TOMBSTONE" - - // KeyChild is a key for searching object by id of child link. - KeyChild = "CHILD" - - // KeyPrev is a key for searching object by id of previous link. - KeyPrev = "PREV" - - // KeyNext is a key for searching object by id of next link. - KeyNext = "NEXT" - - // KeyID is a key for searching object by object id. - KeyID = "ID" - - // KeyCID is a key for searching object by container id. - KeyCID = "CID" - - // KeyOwnerID is a key for searching object by owner id. - KeyOwnerID = "OWNERID" - - // KeyRootObject is a key for searching object that are zero-object or do - // not have any children. - KeyRootObject = "ROOT_OBJECT" -) - -func checkIsNotFull(v interface{}) bool { - var obj *Object - - switch t := v.(type) { - case *GetResponse: - obj = t.GetObject() - case *PutRequest: - if h := t.GetHeader(); h != nil { - obj = h.Object - } - default: - panic("unknown type") - } - - return obj == nil || obj.SystemHeader.PayloadLength != uint64(len(obj.Payload)) && !obj.IsLinking() -} - -// NotFull checks if protobuf stream provided whole object for get operation. -func (m *GetResponse) NotFull() bool { return checkIsNotFull(m) } - -// NotFull checks if protobuf stream provided whole object for put operation. -func (m *PutRequest) NotFull() bool { return checkIsNotFull(m) } - -// CID returns container id value from object put request. -func (m *PutRequest) CID() (cid CID) { - if header := m.GetHeader(); header == nil { - return - } else if obj := header.GetObject(); obj == nil { - return - } else { - return obj.SystemHeader.CID - } -} - -// CID returns container id value from object get request. -func (m *GetRequest) CID() CID { return m.Address.CID } - -// CID returns container id value from object head request. -func (m *HeadRequest) CID() CID { return m.Address.CID } - -// CID returns container id value from object search request. -func (m *SearchRequest) CID() CID { return m.ContainerID } - -// CID returns container id value from object delete request. -func (m *DeleteRequest) CID() CID { return m.Address.CID } - -// CID returns container id value from object get range request. -func (m *GetRangeRequest) CID() CID { return m.Address.CID } - -// CID returns container id value from object get range hash request. -func (m *GetRangeHashRequest) CID() CID { return m.Address.CID } - -// AllowPreviousNetMap returns permission to use previous network map in object put request. -func (m *PutRequest) AllowPreviousNetMap() bool { return false } - -// AllowPreviousNetMap returns permission to use previous network map in object get request. -func (m *GetRequest) AllowPreviousNetMap() bool { return true } - -// AllowPreviousNetMap returns permission to use previous network map in object head request. -func (m *HeadRequest) AllowPreviousNetMap() bool { return true } - -// AllowPreviousNetMap returns permission to use previous network map in object search request. -func (m *SearchRequest) AllowPreviousNetMap() bool { return true } - -// AllowPreviousNetMap returns permission to use previous network map in object delete request. -func (m *DeleteRequest) AllowPreviousNetMap() bool { return false } - -// AllowPreviousNetMap returns permission to use previous network map in object get range request. -func (m *GetRangeRequest) AllowPreviousNetMap() bool { return false } - -// AllowPreviousNetMap returns permission to use previous network map in object get range hash request. -func (m *GetRangeHashRequest) AllowPreviousNetMap() bool { return false } - -// Type returns type of the object put request. -func (m *PutRequest) Type() RequestType { return RequestPut } - -// Type returns type of the object get request. -func (m *GetRequest) Type() RequestType { return RequestGet } - -// Type returns type of the object head request. -func (m *HeadRequest) Type() RequestType { return RequestHead } - -// Type returns type of the object search request. -func (m *SearchRequest) Type() RequestType { return RequestSearch } - -// Type returns type of the object delete request. -func (m *DeleteRequest) Type() RequestType { return RequestDelete } - -// Type returns type of the object get range request. -func (m *GetRangeRequest) Type() RequestType { return RequestRange } - -// Type returns type of the object get range hash request. -func (m *GetRangeHashRequest) Type() RequestType { return RequestRangeHash } diff --git a/object/service.pb.go b/object/service.pb.go deleted file mode 100644 index f882f5e..0000000 Binary files a/object/service.pb.go and /dev/null differ diff --git a/object/service.proto b/object/service.proto deleted file mode 100644 index 91d0b99..0000000 --- a/object/service.proto +++ /dev/null @@ -1,197 +0,0 @@ -syntax = "proto3"; -package object; -option go_package = "github.com/nspcc-dev/neofs-api-go/object"; -option csharp_namespace = "NeoFS.API.Object"; - -import "refs/types.proto"; -import "object/types.proto"; -import "service/meta.proto"; -import "service/verify.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -// Object service provides API for manipulating with the object. -service Service { - - // Get the object from container. Response uses gRPC stream. First response - // message carry object of requested address. Chunk messages are parts of - // the object's payload if it is needed. All messages except first carry - // chunks. Requested object can be restored by concatenation of object - // message payload and all chunks keeping receiving order. - rpc Get(GetRequest) returns (stream GetResponse); - - // Put the object into container. Request uses gRPC stream. First message - // SHOULD BE type of PutHeader. Container id and Owner id of object SHOULD - // BE set. Session token SHOULD BE obtained before put operation (see - // session package). Chunk messages considered by server as part of object - // payload. All messages except first SHOULD BE chunks. Chunk messages - // SHOULD BE sent in direct order of fragmentation. - rpc Put(stream PutRequest) returns (PutResponse); - - // Delete the object from a container - rpc Delete(DeleteRequest) returns (DeleteResponse); - - // Head returns the object without data payload. Object in the - // response has system header only. If full headers flag is set, extended - // headers are also present. - rpc Head(HeadRequest) returns (HeadResponse); - - // Search objects in container. Version of query language format SHOULD BE - // set to 1. Search query represented in serialized format (see query - // package). - rpc Search(SearchRequest) returns (stream SearchResponse); - - // GetRange of data payload. Range is a pair (offset, length). - // Requested range can be restored by concatenation of all chunks - // keeping receiving order. - rpc GetRange(GetRangeRequest) returns (stream GetRangeResponse); - - // GetRangeHash returns homomorphic hash of object payload range after XOR - // operation. Ranges are set of pairs (offset, length). Hashes order in - // response corresponds to ranges order in request. Homomorphic hash is - // calculated for XORed data. - rpc GetRangeHash(GetRangeHashRequest) returns (GetRangeHashResponse); -} - -message GetRequest { - // Address of object (container id + object id) - refs.Address Address = 1 [(gogoproto.nullable) = false]; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message GetResponse { - oneof R { - // Object header and some payload - Object object = 1; - // Chunk of remaining payload - bytes Chunk = 2; - } - - // ResponseMetaHeader contains meta information based on request processing by server (should be embedded into message) - service.ResponseMetaHeader Meta = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message PutRequest { - message PutHeader { - // Object with at least container id and owner id fields - Object Object = 1; - // Number of the object copies to store within the RPC call (zero is processed according to the placement rules) - uint32 CopiesNumber = 2; - } - - oneof R { - // Header should be the first message in the stream - PutHeader Header = 1; - // Chunk should be a remaining message in stream should be chunks - bytes Chunk = 2; - } - - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message PutResponse { - // Address of object (container id + object id) - refs.Address Address = 1 [(gogoproto.nullable) = false]; - // ResponseMetaHeader contains meta information based on request processing by server (should be embedded into message) - service.ResponseMetaHeader Meta = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} -message DeleteRequest { - // Address of object (container id + object id) - refs.Address Address = 1 [(gogoproto.nullable) = false]; - // OwnerID is a wallet address - bytes OwnerID = 2 [(gogoproto.nullable) = false, (gogoproto.customtype) = "OwnerID"]; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -// DeleteResponse is empty because we cannot guarantee permanent object removal -// in distributed system. -message DeleteResponse { - // ResponseMetaHeader contains meta information based on request processing by server (should be embedded into message) - service.ResponseMetaHeader Meta = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message HeadRequest { - // Address of object (container id + object id) - refs.Address Address = 1 [(gogoproto.nullable) = false, (gogoproto.customtype) = "Address"]; - // FullHeaders can be set true for extended headers in the object - bool FullHeaders = 2; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} -message HeadResponse { - // Object without payload - Object Object = 1; - // ResponseMetaHeader contains meta information based on request processing by server (should be embedded into message) - service.ResponseMetaHeader Meta = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message SearchRequest { - // ContainerID for searching the object - bytes ContainerID = 1 [(gogoproto.nullable) = false, (gogoproto.customtype) = "CID"]; - // Query in the binary serialized format - bytes Query = 2; - // QueryVersion is a version of search query format - uint32 QueryVersion = 3; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message SearchResponse { - // Addresses of found objects - repeated refs.Address Addresses = 1 [(gogoproto.nullable) = false]; - // ResponseMetaHeader contains meta information based on request processing by server (should be embedded into message) - service.ResponseMetaHeader Meta = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message GetRangeRequest { - // Address of object (container id + object id) - refs.Address Address = 1 [(gogoproto.nullable) = false]; - // Range of object's payload to return - Range Range = 2 [(gogoproto.nullable) = false]; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message GetRangeResponse { - // Fragment of object's payload - bytes Fragment = 1; - // ResponseMetaHeader contains meta information based on request processing by server (should be embedded into message) - service.ResponseMetaHeader Meta = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message GetRangeHashRequest { - // Address of object (container id + object id) - refs.Address Address = 1 [(gogoproto.nullable) = false]; - // Ranges of object's payload to calculate homomorphic hash - repeated Range Ranges = 2 [(gogoproto.nullable) = false]; - // Salt is used to XOR object's payload ranges before hashing, it can be nil - bytes Salt = 3; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -message GetRangeHashResponse { - // Hashes is a homomorphic hashes of all ranges - repeated bytes Hashes = 1 [(gogoproto.customtype) = "Hash", (gogoproto.nullable) = false]; - // ResponseMetaHeader contains meta information based on request processing by server (should be embedded into message) - service.ResponseMetaHeader Meta = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - diff --git a/object/service_test.go b/object/service_test.go deleted file mode 100644 index 5b7a358..0000000 --- a/object/service_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package object - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestRequest(t *testing.T) { - cases := []Request{ - &PutRequest{}, - &GetRequest{}, - &HeadRequest{}, - &SearchRequest{}, - &DeleteRequest{}, - &GetRangeRequest{}, - &GetRangeHashRequest{}, - MakePutRequestHeader(nil), - MakePutRequestHeader(&Object{}), - } - - types := []RequestType{ - RequestPut, - RequestGet, - RequestHead, - RequestSearch, - RequestDelete, - RequestRange, - RequestRangeHash, - RequestPut, - RequestPut, - } - - for i := range cases { - v := cases[i] - - t.Run(fmt.Sprintf("%T_%d", v, i), func(t *testing.T) { - require.NotPanics(t, func() { v.CID() }) - require.Equal(t, types[i], v.Type()) - }) - } -} diff --git a/object/sg.go b/object/sg.go deleted file mode 100644 index a75b950..0000000 --- a/object/sg.go +++ /dev/null @@ -1,43 +0,0 @@ -package object - -import ( - "sort" - - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/storagegroup" -) - -// Here are defined getter functions for objects that contain storage group -// information. - -var _ storagegroup.Provider = (*Object)(nil) - -// Group returns slice of object ids that are part of a storage group. -func (m *Object) Group() []refs.ObjectID { - sgLinks := m.Links(Link_StorageGroup) - sort.Sort(storagegroup.IDList(sgLinks)) - return sgLinks -} - -// Zones returns validation zones of storage group. -func (m *Object) Zones() []storagegroup.ZoneInfo { - sgInfo, err := m.StorageGroup() - if err != nil { - return nil - } - return []storagegroup.ZoneInfo{ - { - Hash: sgInfo.ValidationHash, - Size: sgInfo.ValidationDataSize, - }, - } -} - -// IDInfo returns meta information about storage group. -func (m *Object) IDInfo() *storagegroup.IdentificationInfo { - return &storagegroup.IdentificationInfo{ - SGID: m.SystemHeader.ID, - CID: m.SystemHeader.CID, - OwnerID: m.SystemHeader.OwnerID, - } -} diff --git a/object/sg_test.go b/object/sg_test.go deleted file mode 100644 index 716e916..0000000 --- a/object/sg_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package object - -import ( - "math/rand" - "sort" - "testing" - - "github.com/nspcc-dev/neofs-api-go/hash" - "github.com/nspcc-dev/neofs-api-go/storagegroup" - "github.com/stretchr/testify/require" -) - -func TestObject_StorageGroup(t *testing.T) { - t.Run("group method", func(t *testing.T) { - var linkCount byte = 100 - - obj := &Object{Headers: make([]Header, 0, linkCount)} - require.Empty(t, obj.Group()) - - idList := make([]ID, linkCount) - for i := byte(0); i < linkCount; i++ { - idList[i] = ID{i} - obj.Headers = append(obj.Headers, Header{ - Value: &Header_Link{Link: &Link{ - Type: Link_StorageGroup, - ID: idList[i], - }}, - }) - } - - rand.Shuffle(len(obj.Headers), func(i, j int) { obj.Headers[i], obj.Headers[j] = obj.Headers[j], obj.Headers[i] }) - sort.Sort(storagegroup.IDList(idList)) - require.Equal(t, idList, obj.Group()) - }) - t.Run("identification method", func(t *testing.T) { - oid, cid, owner := ID{1}, CID{2}, OwnerID{3} - obj := &Object{ - SystemHeader: SystemHeader{ - ID: oid, - OwnerID: owner, - CID: cid, - }, - } - - idInfo := obj.IDInfo() - require.Equal(t, oid, idInfo.SGID) - require.Equal(t, cid, idInfo.CID) - require.Equal(t, owner, idInfo.OwnerID) - }) - t.Run("zones method", func(t *testing.T) { - sgSize := uint64(100) - - d := make([]byte, sgSize) - _, err := rand.Read(d) - require.NoError(t, err) - sgHash := hash.Sum(d) - - obj := &Object{ - Headers: []Header{ - { - Value: &Header_StorageGroup{ - StorageGroup: &storagegroup.StorageGroup{ - ValidationDataSize: sgSize, - ValidationHash: sgHash, - }, - }, - }, - }, - } - - var ( - sumSize uint64 - zones = obj.Zones() - hashes = make([]Hash, len(zones)) - ) - - for i := range zones { - sumSize += zones[i].Size - hashes[i] = zones[i].Hash - } - - sumHash, err := hash.Concat(hashes) - require.NoError(t, err) - - require.Equal(t, sgSize, sumSize) - require.Equal(t, sgHash, sumHash) - }) -} diff --git a/object/sign.go b/object/sign.go deleted file mode 100644 index 3cdf94f..0000000 --- a/object/sign.go +++ /dev/null @@ -1,272 +0,0 @@ -package object - -import ( - "crypto/ecdsa" - "encoding/binary" - "io" - - "github.com/nspcc-dev/neofs-api-go/service" -) - -// SignedData returns payload bytes of the request. -// -// If payload is nil, ErrHeaderNotFound returns. -func (m PutRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m PutRequest) ReadSignedData(p []byte) (int, error) { - r := m.GetR() - if r == nil { - return 0, ErrHeaderNotFound - } - - return r.MarshalTo(p) -} - -// SignedDataSize returns the size of payload of the Put request. -// -// If payload is nil, -1 returns. -func (m PutRequest) SignedDataSize() int { - r := m.GetR() - if r == nil { - return -1 - } - - return r.Size() -} - -// SignedData returns payload bytes of the request. -func (m GetRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m GetRequest) ReadSignedData(p []byte) (int, error) { - addr := m.GetAddress() - - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], addr.CID.Bytes()) - - off += copy(p[off:], addr.ObjectID.Bytes()) - - return off, nil -} - -// SignedDataSize returns payload size of the request. -func (m GetRequest) SignedDataSize() int { - return addressSize(m.GetAddress()) -} - -// SignedData returns payload bytes of the request. -func (m HeadRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m HeadRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - if m.GetFullHeaders() { - p[0] = 1 - } else { - p[0] = 0 - } - - off := 1 - - off += copy(p[off:], m.Address.CID.Bytes()) - - off += copy(p[off:], m.Address.ObjectID.Bytes()) - - return off, nil -} - -// SignedDataSize returns payload size of the request. -func (m HeadRequest) SignedDataSize() int { - return addressSize(m.Address) + 1 -} - -// SignedData returns payload bytes of the request. -func (m DeleteRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m DeleteRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.OwnerID.Bytes()) - - off += copy(p[off:], addressBytes(m.Address)) - - return off, nil -} - -// SignedDataSize returns payload size of the request. -func (m DeleteRequest) SignedDataSize() int { - return m.OwnerID.Size() + addressSize(m.Address) -} - -// SignedData returns payload bytes of the request. -func (m GetRangeRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m GetRangeRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - n, err := (&m.Range).MarshalTo(p) - if err != nil { - return 0, err - } - - n += copy(p[n:], addressBytes(m.GetAddress())) - - return n, nil -} - -// SignedDataSize returns payload size of the request. -func (m GetRangeRequest) SignedDataSize() int { - return (&m.Range).Size() + addressSize(m.GetAddress()) -} - -// SignedData returns payload bytes of the request. -func (m GetRangeHashRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m GetRangeHashRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], addressBytes(m.GetAddress())) - - off += copy(p[off:], rangeSetBytes(m.GetRanges())) - - off += copy(p[off:], m.GetSalt()) - - return off, nil -} - -// SignedDataSize returns payload size of the request. -func (m GetRangeHashRequest) SignedDataSize() int { - var sz int - - sz += addressSize(m.GetAddress()) - - sz += rangeSetSize(m.GetRanges()) - - sz += len(m.GetSalt()) - - return sz -} - -// SignedData returns payload bytes of the request. -func (m SearchRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m SearchRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.CID().Bytes()) - - binary.BigEndian.PutUint32(p[off:], m.GetQueryVersion()) - off += 4 - - off += copy(p[off:], m.GetQuery()) - - return off, nil -} - -// SignedDataSize returns payload size of the request. -func (m SearchRequest) SignedDataSize() int { - var sz int - - sz += m.CID().Size() - - sz += 4 // uint32 Version - - sz += len(m.GetQuery()) - - return sz -} - -func rangeSetSize(rs []Range) int { - return 4 + len(rs)*16 // two uint64 fields -} - -func rangeSetBytes(rs []Range) []byte { - data := make([]byte, rangeSetSize(rs)) - - binary.BigEndian.PutUint32(data, uint32(len(rs))) - - off := 4 - - for i := range rs { - binary.BigEndian.PutUint64(data[off:], rs[i].Offset) - off += 8 - - binary.BigEndian.PutUint64(data[off:], rs[i].Length) - off += 8 - } - - return data -} - -func addressSize(addr Address) int { - return addr.CID.Size() + addr.ObjectID.Size() -} - -func addressBytes(addr Address) []byte { - return append(addr.CID.Bytes(), addr.ObjectID.Bytes()...) -} - -// SignedData returns the result of the ChecksumSignature field getter. -func (m IntegrityHeader) SignedData() ([]byte, error) { - return m.GetHeadersChecksum(), nil -} - -// AddSignKey calls the ChecksumSignature field setter with signature argument. -func (m *IntegrityHeader) AddSignKey(sign []byte, _ *ecdsa.PublicKey) { - m.SetSignature(sign) -} diff --git a/object/sign_test.go b/object/sign_test.go deleted file mode 100644 index dcfbd7e..0000000 --- a/object/sign_test.go +++ /dev/null @@ -1,239 +0,0 @@ -package object - -import ( - "crypto/rand" - "testing" - - "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/stretchr/testify/require" -) - -func TestSignVerifyRequests(t *testing.T) { - sk := test.DecodeKey(0) - - type sigType interface { - service.RequestData - service.SignKeyPairAccumulator - service.SignKeyPairSource - SetToken(*Token) - } - - items := []struct { - constructor func() sigType - payloadCorrupt []func(sigType) - }{ - { // PutRequest.PutHeader - constructor: func() sigType { - return MakePutRequestHeader(new(Object)) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - obj := s.(*PutRequest).GetR().(*PutRequest_Header).Header.GetObject() - obj.SystemHeader.PayloadLength++ - }, - }, - }, - { // PutRequest.Chunk - constructor: func() sigType { - return MakePutRequestChunk(make([]byte, 10)) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - h := s.(*PutRequest).GetR().(*PutRequest_Chunk) - h.Chunk[0]++ - }, - }, - }, - { // GetRequest - constructor: func() sigType { - return new(GetRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - s.(*GetRequest).Address.CID[0]++ - }, - func(s sigType) { - s.(*GetRequest).Address.ObjectID[0]++ - }, - }, - }, - { // HeadRequest - constructor: func() sigType { - return new(HeadRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - s.(*HeadRequest).Address.CID[0]++ - }, - func(s sigType) { - s.(*HeadRequest).Address.ObjectID[0]++ - }, - func(s sigType) { - s.(*HeadRequest).FullHeaders = true - }, - }, - }, - { // DeleteRequest - constructor: func() sigType { - return new(DeleteRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - s.(*DeleteRequest).OwnerID[0]++ - }, - func(s sigType) { - s.(*DeleteRequest).Address.CID[0]++ - }, - func(s sigType) { - s.(*DeleteRequest).Address.ObjectID[0]++ - }, - }, - }, - { // GetRangeRequest - constructor: func() sigType { - return new(GetRangeRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - s.(*GetRangeRequest).Range.Length++ - }, - func(s sigType) { - s.(*GetRangeRequest).Range.Offset++ - }, - func(s sigType) { - s.(*GetRangeRequest).Address.CID[0]++ - }, - func(s sigType) { - s.(*GetRangeRequest).Address.ObjectID[0]++ - }, - }, - }, - { // GetRangeHashRequest - constructor: func() sigType { - return &GetRangeHashRequest{ - Ranges: []Range{{}}, - Salt: []byte{1, 2, 3}, - } - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - s.(*GetRangeHashRequest).Address.CID[0]++ - }, - func(s sigType) { - s.(*GetRangeHashRequest).Address.ObjectID[0]++ - }, - func(s sigType) { - s.(*GetRangeHashRequest).Salt[0]++ - }, - func(s sigType) { - s.(*GetRangeHashRequest).Ranges[0].Length++ - }, - func(s sigType) { - s.(*GetRangeHashRequest).Ranges[0].Offset++ - }, - func(s sigType) { - s.(*GetRangeHashRequest).Ranges = nil - }, - }, - }, - { // GetRangeHashRequest - constructor: func() sigType { - return &SearchRequest{ - Query: []byte{1, 2, 3}, - } - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - s.(*SearchRequest).ContainerID[0]++ - }, - func(s sigType) { - s.(*SearchRequest).Query[0]++ - }, - func(s sigType) { - s.(*SearchRequest).QueryVersion++ - }, - }, - }, - } - - for _, item := range items { - { // token corruptions - v := item.constructor() - - token := new(Token) - v.SetToken(token) - - require.NoError(t, service.SignRequestData(sk, v)) - - require.NoError(t, service.VerifyRequestData(v)) - - token.SetSessionKey(append(token.GetSessionKey(), 1)) - - require.Error(t, service.VerifyRequestData(v)) - } - - { // payload corruptions - for _, corruption := range item.payloadCorrupt { - v := item.constructor() - - require.NoError(t, service.SignRequestData(sk, v)) - - require.NoError(t, service.VerifyRequestData(v)) - - corruption(v) - - require.Error(t, service.VerifyRequestData(v)) - } - } - } -} - -func TestHeadRequest_ReadSignedData(t *testing.T) { - t.Run("full headers", func(t *testing.T) { - req := new(HeadRequest) - - // unset FullHeaders flag - req.SetFullHeaders(false) - - // allocate two different buffers for reading - buf1 := testData(t, req.SignedDataSize()) - buf2 := testData(t, req.SignedDataSize()) - - // read to both buffers - n1, err := req.ReadSignedData(buf1) - require.NoError(t, err) - - n2, err := req.ReadSignedData(buf2) - require.NoError(t, err) - - require.Equal(t, buf1[:n1], buf2[:n2]) - }) -} - -func testData(t *testing.T, sz int) []byte { - data := make([]byte, sz) - - _, err := rand.Read(data) - require.NoError(t, err) - - return data -} - -func TestIntegrityHeaderSignMethods(t *testing.T) { - // create new IntegrityHeader - s := new(IntegrityHeader) - - // set test headers checksum - s.SetHeadersChecksum([]byte{1, 2, 3}) - - data, err := s.SignedData() - require.NoError(t, err) - require.Equal(t, data, s.GetHeadersChecksum()) - - // add signature - sig := []byte{4, 5, 6} - s.AddSignKey(sig, nil) - - require.Equal(t, sig, s.GetSignature()) -} diff --git a/object/types.go b/object/types.go deleted file mode 100644 index 392e624..0000000 --- a/object/types.go +++ /dev/null @@ -1,422 +0,0 @@ -package object - -import ( - "bytes" - "context" - "fmt" - "io" - "reflect" - - "github.com/gogo/protobuf/proto" - "github.com/nspcc-dev/neofs-api-go/internal" - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/pkg/errors" -) - -type ( - // Pred defines a predicate function that can check if passed header - // satisfies predicate condition. It is used to find headers of - // specific type. - Pred = func(*Header) bool - - // Address is a type alias of object Address. - Address = refs.Address - - // PositionReader defines object reader that returns slice of bytes - // for specified object and data range. - PositionReader interface { - PRead(ctx context.Context, addr refs.Address, rng Range) ([]byte, error) - } - - // RequestType of the object service requests. - RequestType int - - headerType int -) - -const ( - // ErrVerifyPayload is raised when payload checksum cannot be verified. - ErrVerifyPayload = internal.Error("can't verify payload") - - // ErrVerifyHeader is raised when object integrity cannot be verified. - ErrVerifyHeader = internal.Error("can't verify header") - - // ErrHeaderNotFound is raised when requested header not found. - ErrHeaderNotFound = internal.Error("header not found") - - // ErrVerifySignature is raised when signature cannot be verified. - ErrVerifySignature = internal.Error("can't verify signature") -) - -const ( - _ headerType = iota - // LinkHdr is a link header type. - LinkHdr - // RedirectHdr is a redirect header type. - RedirectHdr - // UserHdr is a user defined header type. - UserHdr - // TransformHdr is a transformation header type. - TransformHdr - // TombstoneHdr is a tombstone header type. - TombstoneHdr - // TokenHdr is a token header type. - TokenHdr - // HomoHashHdr is a homomorphic hash header type. - HomoHashHdr - // PayloadChecksumHdr is a payload checksum header type. - PayloadChecksumHdr - // IntegrityHdr is a integrity header type. - IntegrityHdr - // StorageGroupHdr is a storage group header type. - StorageGroupHdr - // PublicKeyHdr is a public key header type. - PublicKeyHdr -) - -const ( - _ RequestType = iota - // RequestPut is a type for object put request. - RequestPut - // RequestGet is a type for object get request. - RequestGet - // RequestHead is a type for object head request. - RequestHead - // RequestSearch is a type for object search request. - RequestSearch - // RequestRange is a type for object range request. - RequestRange - // RequestRangeHash is a type for object hash range request. - RequestRangeHash - // RequestDelete is a type for object delete request. - RequestDelete -) - -var ( - _ internal.Custom = (*Object)(nil) - - emptyObject = new(Object).Bytes() -) - -// String returns printable name of the request type. -func (s RequestType) String() string { - switch s { - case RequestPut: - return "PUT" - case RequestGet: - return "GET" - case RequestHead: - return "HEAD" - case RequestSearch: - return "SEARCH" - case RequestRange: - return "RANGE" - case RequestRangeHash: - return "RANGE_HASH" - case RequestDelete: - return "DELETE" - default: - return "UNKNOWN" - } -} - -// Bytes returns marshaled object in a binary format. -func (m Object) Bytes() []byte { data, _ := m.Marshal(); return data } - -// Empty checks if object does not contain any information. -func (m Object) Empty() bool { return bytes.Equal(m.Bytes(), emptyObject) } - -// LastHeader returns last header of the specified type. Type must be -// specified as a Pred function. -func (m Object) LastHeader(f Pred) (int, *Header) { - for i := len(m.Headers) - 1; i >= 0; i-- { - if f != nil && f(&m.Headers[i]) { - return i, &m.Headers[i] - } - } - return -1, nil -} - -// AddHeader adds passed header to the end of extended header list. -func (m *Object) AddHeader(h *Header) { - m.Headers = append(m.Headers, *h) -} - -// SetPayload sets payload field and payload length in the system header. -func (m *Object) SetPayload(payload []byte) { - m.Payload = payload - m.SystemHeader.PayloadLength = uint64(len(payload)) -} - -// SetHeader replaces existing extended header or adds new one to the end of -// extended header list. -func (m *Object) SetHeader(h *Header) { - // looking for the header of that type - for i := range m.Headers { - if m.Headers[i].typeOf(h.Value) { - // if we found one - set it with new value and return - m.Headers[i] = *h - return - } - } - // if we did not find one - add this header - m.AddHeader(h) -} - -// Merge used by proto.Clone -func (m *Object) Merge(src proto.Message) { - if tmp, ok := src.(*Object); ok { - tmp.CopyTo(m) - } -} - -func (m Header) typeOf(t isHeader_Value) (ok bool) { - switch t.(type) { - case *Header_Link: - _, ok = m.Value.(*Header_Link) - case *Header_Redirect: - _, ok = m.Value.(*Header_Redirect) - case *Header_UserHeader: - _, ok = m.Value.(*Header_UserHeader) - case *Header_Transform: - _, ok = m.Value.(*Header_Transform) - case *Header_Tombstone: - _, ok = m.Value.(*Header_Tombstone) - case *Header_Token: - _, ok = m.Value.(*Header_Token) - case *Header_HomoHash: - _, ok = m.Value.(*Header_HomoHash) - case *Header_PayloadChecksum: - _, ok = m.Value.(*Header_PayloadChecksum) - case *Header_Integrity: - _, ok = m.Value.(*Header_Integrity) - case *Header_StorageGroup: - _, ok = m.Value.(*Header_StorageGroup) - case *Header_PublicKey: - _, ok = m.Value.(*Header_PublicKey) - } - return -} - -// HeaderType returns predicate that check if extended header is a header -// of specified type. -func HeaderType(t headerType) Pred { - switch t { - case LinkHdr: - return func(h *Header) bool { _, ok := h.Value.(*Header_Link); return ok } - case RedirectHdr: - return func(h *Header) bool { _, ok := h.Value.(*Header_Redirect); return ok } - case UserHdr: - return func(h *Header) bool { _, ok := h.Value.(*Header_UserHeader); return ok } - case TransformHdr: - return func(h *Header) bool { _, ok := h.Value.(*Header_Transform); return ok } - case TombstoneHdr: - return func(h *Header) bool { _, ok := h.Value.(*Header_Tombstone); return ok } - case TokenHdr: - return func(h *Header) bool { _, ok := h.Value.(*Header_Token); return ok } - case HomoHashHdr: - return func(h *Header) bool { _, ok := h.Value.(*Header_HomoHash); return ok } - case PayloadChecksumHdr: - return func(h *Header) bool { _, ok := h.Value.(*Header_PayloadChecksum); return ok } - case IntegrityHdr: - return func(h *Header) bool { _, ok := h.Value.(*Header_Integrity); return ok } - case StorageGroupHdr: - return func(h *Header) bool { _, ok := h.Value.(*Header_StorageGroup); return ok } - case PublicKeyHdr: - return func(h *Header) bool { _, ok := h.Value.(*Header_PublicKey); return ok } - default: - return nil - } -} - -// Copy creates full copy of the object. -func (m *Object) Copy() (obj *Object) { - obj = new(Object) - m.CopyTo(obj) - return -} - -// CopyTo creates fills passed object with the data from the current object. -// This function creates copies on every available data slice. -func (m *Object) CopyTo(o *Object) { - o.SystemHeader = m.SystemHeader - - if m.Headers != nil { - o.Headers = make([]Header, len(m.Headers)) - } - - if m.Payload != nil { - o.Payload = make([]byte, len(m.Payload)) - copy(o.Payload, m.Payload) - } - - for i := range m.Headers { - switch v := m.Headers[i].Value.(type) { - case *Header_Link: - link := *v.Link - o.Headers[i] = Header{ - Value: &Header_Link{ - Link: &link, - }, - } - case *Header_HomoHash: - hash := proto.Clone(&v.HomoHash).(*Hash) - o.Headers[i] = Header{ - Value: &Header_HomoHash{ - HomoHash: *hash, - }, - } - case *Header_Token: - token := *v.Token - o.Headers[i] = Header{ - Value: &Header_Token{ - Token: &token, - }, - } - default: - o.Headers[i] = *proto.Clone(&m.Headers[i]).(*Header) - } - } -} - -// Address returns object's address. -func (m Object) Address() *refs.Address { - return &refs.Address{ - ObjectID: m.SystemHeader.ID, - CID: m.SystemHeader.CID, - } -} - -func (m CreationPoint) String() string { - return fmt.Sprintf(`{UnixTime=%d Epoch=%d}`, m.UnixTime, m.Epoch) -} - -// Stringify converts object into string format. -func Stringify(dst io.Writer, obj *Object) error { - // put empty line - if _, err := fmt.Fprintln(dst); err != nil { - return err - } - - // put object line - if _, err := fmt.Fprintln(dst, "Object:"); err != nil { - return err - } - - // put system headers - if _, err := fmt.Fprintln(dst, "\tSystemHeader:"); err != nil { - return err - } - - sysHeaders := []string{"ID", "CID", "OwnerID", "Version", "PayloadLength", "CreatedAt"} - v := reflect.ValueOf(obj.SystemHeader) - for _, key := range sysHeaders { - if !v.FieldByName(key).IsValid() { - return errors.Errorf("invalid system header key: %q", key) - } - - val := v.FieldByName(key).Interface() - if _, err := fmt.Fprintf(dst, "\t\t- %s=%v\n", key, val); err != nil { - return err - } - } - - // put user headers - if _, err := fmt.Fprintln(dst, "\tUserHeaders:"); err != nil { - return err - } - - for _, header := range obj.Headers { - var ( - typ = reflect.ValueOf(header.Value) - key string - val interface{} - ) - - switch t := typ.Interface().(type) { - case *Header_Link: - key = "Link" - val = fmt.Sprintf(`{Type=%s ID=%s}`, t.Link.Type, t.Link.ID) - case *Header_Redirect: - key = "Redirect" - val = fmt.Sprintf(`{CID=%s OID=%s}`, t.Redirect.CID, t.Redirect.ObjectID) - case *Header_UserHeader: - key = "UserHeader" - val = fmt.Sprintf(`{Key=%s Val=%s}`, t.UserHeader.Key, t.UserHeader.Value) - case *Header_Transform: - key = "Transform" - val = t.Transform.Type.String() - case *Header_Tombstone: - key = "Tombstone" - val = "MARKED" - case *Header_Token: - key = "Token" - val = fmt.Sprintf("{"+ - "ID=%s OwnerID=%s Verb=%s Address=%s Created=%d ValidUntil=%d SessionKey=%02x Signature=%02x"+ - "}", - t.Token.GetID(), - t.Token.GetOwnerID(), - t.Token.GetVerb(), - t.Token.GetAddress(), - t.Token.CreationEpoch(), - t.Token.ExpirationEpoch(), - t.Token.GetSessionKey(), - t.Token.GetSignature()) - case *Header_HomoHash: - key = "HomoHash" - val = t.HomoHash - case *Header_PayloadChecksum: - key = "PayloadChecksum" - val = t.PayloadChecksum - case *Header_Integrity: - key = "Integrity" - val = fmt.Sprintf(`{Checksum=%02x Signature=%02x}`, - t.Integrity.HeadersChecksum, - t.Integrity.ChecksumSignature) - case *Header_StorageGroup: - key = "StorageGroup" - val = fmt.Sprintf(`{DataSize=%d Hash=%02x Lifetime={Unit=%s Value=%d}}`, - t.StorageGroup.ValidationDataSize, - t.StorageGroup.ValidationHash, - t.StorageGroup.Lifetime.Unit, - t.StorageGroup.Lifetime.Value) - case *Header_PublicKey: - key = "PublicKey" - val = t.PublicKey.Value - default: - key = "Unknown" - val = t - } - - if _, err := fmt.Fprintf(dst, "\t\t- Type=%s\n\t\t Value=%v\n", key, val); err != nil { - return err - } - } - - // put payload - if _, err := fmt.Fprintf(dst, "\tPayload: %#v\n", obj.Payload); err != nil { - return err - } - - return nil -} - -// SetFullHeaders is a FullHeaders field setter. -func (m *HeadRequest) SetFullHeaders(v bool) { - m.FullHeaders = v -} - -// GetSignature is a ChecksumSignature field getter. -func (m IntegrityHeader) GetSignature() []byte { - return m.ChecksumSignature -} - -// SetSignature is a ChecksumSignature field setter. -func (m *IntegrityHeader) SetSignature(v []byte) { - m.ChecksumSignature = v -} - -// SetHeadersChecksum is a HeadersChecksum field setter. -func (m *IntegrityHeader) SetHeadersChecksum(v []byte) { - m.HeadersChecksum = v -} diff --git a/object/types.pb.go b/object/types.pb.go deleted file mode 100644 index 97106c7..0000000 Binary files a/object/types.pb.go and /dev/null differ diff --git a/object/types.proto b/object/types.proto deleted file mode 100644 index 46e1549..0000000 --- a/object/types.proto +++ /dev/null @@ -1,134 +0,0 @@ -syntax = "proto3"; -package object; -option go_package = "github.com/nspcc-dev/neofs-api-go/object"; -option csharp_namespace = "NeoFS.API.Object"; - -import "refs/types.proto"; -import "service/verify.proto"; -import "storagegroup/types.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -message Range { - // Offset of the data range - uint64 Offset = 1; - // Length of the data range - uint64 Length = 2; -} - -message UserHeader { - // Key of the user's header - string Key = 1; - // Value of the user's header - string Value = 2; -} - -message Header { - oneof Value { - // Link to other objects - Link Link = 1; - // Redirect not used yet - refs.Address Redirect = 2; - // UserHeader is a set of KV headers defined by user - UserHeader UserHeader = 3; - // Transform defines transform operation (e.g. payload split) - Transform Transform = 4; - // Tombstone header that set up in deleted objects - Tombstone Tombstone = 5; - // Token header contains token of the session within which the object was created - service.Token Token = 6; - // HomoHash is a homomorphic hash of original object payload - bytes HomoHash = 7 [(gogoproto.customtype) = "Hash"]; - // PayloadChecksum of actual object's payload - bytes PayloadChecksum = 8; - // Integrity header with checksum of all above headers in the object - IntegrityHeader Integrity = 9; - // StorageGroup contains meta information for the data audit - storagegroup.StorageGroup StorageGroup = 10; - // PublicKey of owner of the object. Key is used for verification and can be based on NeoID or x509 cert. - PublicKey PublicKey = 11; - } -} - -message Tombstone {} - -message SystemHeader { - // Version of the object structure - uint64 Version = 1; - // PayloadLength is an object payload length - uint64 PayloadLength = 2; - - // ID is an object identifier, is a valid UUIDv4 - bytes ID = 3 [(gogoproto.customtype) = "ID", (gogoproto.nullable) = false]; - // OwnerID is a wallet address - bytes OwnerID = 4 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // CID is a SHA256 hash of the container structure (container identifier) - bytes CID = 5 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false]; - // CreatedAt is a timestamp of object creation - CreationPoint CreatedAt = 6 [(gogoproto.nullable) = false]; -} - -message CreationPoint { - option (gogoproto.goproto_stringer) = false; - - // UnixTime is a date of creation in unixtime format - int64 UnixTime = 1; - // Epoch is a date of creation in NeoFS epochs - uint64 Epoch = 2; -} - -message IntegrityHeader { - // HeadersChecksum is a checksum of all above headers in the object - bytes HeadersChecksum = 1; - // ChecksumSignature is an user's signature of checksum to verify if it is correct - bytes ChecksumSignature = 2; -} - -message Link { - enum Type { - Unknown = 0; - // Parent object created during object transformation - Parent = 1; - // Previous object in the linked list created during object transformation - Previous = 2; - // Next object in the linked list created during object transformation - Next = 3; - // Child object created during object transformation - Child = 4; - // Object that included into this storage group - StorageGroup = 5; - } - // Type of link - Type type = 1; - // ID is an object identifier, is a valid UUIDv4 - bytes ID = 2 [(gogoproto.customtype) = "ID", (gogoproto.nullable) = false]; -} - -message Transform { - enum Type { - Unknown = 0; - // Split sets when object created after payload split - Split = 1; - // Sign sets when object created after re-signing (doesn't used) - Sign = 2; - // Mould sets when object created after filling missing headers in the object - Mould = 3; - } - // Type of object transformation - Type type = 1; -} - -message Object { - // SystemHeader describes system header - SystemHeader SystemHeader = 1 [(gogoproto.nullable) = false]; - // Headers describes a set of an extended headers - repeated Header Headers = 2 [(gogoproto.nullable) = false]; - // Payload is an object's payload - bytes Payload = 3; -} - -message PublicKey { - // Value contains marshaled ecdsa public key - bytes Value = 1; -} diff --git a/object/types_test.go b/object/types_test.go deleted file mode 100644 index 4a2ca92..0000000 --- a/object/types_test.go +++ /dev/null @@ -1,234 +0,0 @@ -package object - -import ( - "bytes" - "testing" - - "github.com/gogo/protobuf/proto" - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-api-go/storagegroup" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/stretchr/testify/require" -) - -func TestStringify(t *testing.T) { - res := ` -Object: - SystemHeader: - - ID=7e0b9c6c-aabc-4985-949e-2680e577b48b - - CID=11111111111111111111111111111111 - - OwnerID=NQHKh7fKGieCPrPuiEkY58ucRFwWMyU1Mc - - Version=1 - - PayloadLength=1 - - CreatedAt={UnixTime=1 Epoch=1} - UserHeaders: - - Type=Link - Value={Type=Child ID=7e0b9c6c-aabc-4985-949e-2680e577b48b} - - Type=Redirect - Value={CID=11111111111111111111111111111111 OID=7e0b9c6c-aabc-4985-949e-2680e577b48b} - - Type=UserHeader - Value={Key=test_key Val=test_value} - - Type=Transform - Value=Split - - Type=Tombstone - Value=MARKED - - Type=Token - Value={ID=7e0b9c6c-aabc-4985-949e-2680e577b48b OwnerID=NQHKh7fKGieCPrPuiEkY58ucRFwWMyU1Mc Verb=Search Address=11111111111111111111111111111111/7e0b9c6c-aabc-4985-949e-2680e577b48b Created=1 ValidUntil=2 SessionKey=010203040506 Signature=010203040506} - - Type=HomoHash - Value=1111111111111111111111111111111111111111111111111111111111111111 - - Type=PayloadChecksum - Value=[1 2 3 4 5 6] - - Type=Integrity - Value={Checksum=010203040506 Signature=010203040506} - - Type=StorageGroup - Value={DataSize=5 Hash=31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131 Lifetime={Unit=UnixTime Value=555}} - - Type=PublicKey - Value=[1 2 3 4 5 6] - Payload: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7} -` - - key := test.DecodeKey(0) - - uid, err := refs.NewOwnerID(&key.PublicKey) - require.NoError(t, err) - - var oid refs.UUID - - require.NoError(t, oid.Parse("7e0b9c6c-aabc-4985-949e-2680e577b48b")) - - obj := &Object{ - SystemHeader: SystemHeader{ - Version: 1, - PayloadLength: 1, - ID: oid, - OwnerID: uid, - CID: CID{}, - CreatedAt: CreationPoint{ - UnixTime: 1, - Epoch: 1, - }, - }, - Payload: []byte{1, 2, 3, 4, 5, 6, 7}, - } - - // *Header_Link - obj.Headers = append(obj.Headers, Header{ - Value: &Header_Link{ - Link: &Link{ID: oid, Type: Link_Child}, - }, - }) - - // *Header_Redirect - obj.Headers = append(obj.Headers, Header{ - Value: &Header_Redirect{ - Redirect: &Address{ObjectID: oid, CID: CID{}}, - }, - }) - - // *Header_UserHeader - obj.Headers = append(obj.Headers, Header{ - Value: &Header_UserHeader{ - UserHeader: &UserHeader{ - Key: "test_key", - Value: "test_value", - }, - }, - }) - - // *Header_Transform - obj.Headers = append(obj.Headers, Header{ - Value: &Header_Transform{ - Transform: &Transform{ - Type: Transform_Split, - }, - }, - }) - - // *Header_Tombstone - obj.Headers = append(obj.Headers, Header{ - Value: &Header_Tombstone{ - Tombstone: &Tombstone{}, - }, - }) - - token := new(Token) - token.SetID(oid) - token.SetOwnerID(uid) - token.SetVerb(service.Token_Info_Search) - token.SetAddress(Address{ObjectID: oid, CID: refs.CID{}}) - token.SetCreationEpoch(1) - token.SetExpirationEpoch(2) - token.SetSessionKey([]byte{1, 2, 3, 4, 5, 6}) - token.SetSignature([]byte{1, 2, 3, 4, 5, 6}) - - // *Header_Token - obj.Headers = append(obj.Headers, Header{ - Value: &Header_Token{ - Token: token, - }, - }) - - // *Header_HomoHash - obj.Headers = append(obj.Headers, Header{ - Value: &Header_HomoHash{ - HomoHash: Hash{}, - }, - }) - - // *Header_PayloadChecksum - obj.Headers = append(obj.Headers, Header{ - Value: &Header_PayloadChecksum{ - PayloadChecksum: []byte{1, 2, 3, 4, 5, 6}, - }, - }) - - // *Header_Integrity - obj.Headers = append(obj.Headers, Header{ - Value: &Header_Integrity{ - Integrity: &IntegrityHeader{ - HeadersChecksum: []byte{1, 2, 3, 4, 5, 6}, - ChecksumSignature: []byte{1, 2, 3, 4, 5, 6}, - }, - }, - }) - - // *Header_StorageGroup - obj.Headers = append(obj.Headers, Header{ - Value: &Header_StorageGroup{ - StorageGroup: &storagegroup.StorageGroup{ - ValidationDataSize: 5, - ValidationHash: storagegroup.Hash{}, - Lifetime: &storagegroup.StorageGroup_Lifetime{ - Unit: storagegroup.StorageGroup_Lifetime_UnixTime, - Value: 555, - }, - }, - }, - }) - - // *Header_PublicKey - obj.Headers = append(obj.Headers, Header{ - Value: &Header_PublicKey{ - PublicKey: &PublicKey{Value: []byte{1, 2, 3, 4, 5, 6}}, - }, - }) - - buf := new(bytes.Buffer) - - require.NoError(t, Stringify(buf, obj)) - require.Equal(t, res, buf.String()) -} - -func TestObject_Copy(t *testing.T) { - t.Run("token header", func(t *testing.T) { - token := new(Token) - token.SetID(service.TokenID{1, 2, 3}) - - obj := new(Object) - - obj.AddHeader(&Header{ - Value: &Header_Token{ - Token: token, - }, - }) - - { // Copying - cp := obj.Copy() - - _, h := cp.LastHeader(HeaderType(TokenHdr)) - require.NotNil(t, h) - require.Equal(t, token, h.GetValue().(*Header_Token).Token) - } - - { // Cloning - cl := proto.Clone(obj).(*Object) - require.Equal(t, obj, cl) - - _, h := cl.LastHeader(HeaderType(TokenHdr)) - h.GetToken().SetID(service.TokenID{3, 2, 1}) - - require.NotEqual(t, token, h.GetToken()) - } - }) -} - -func TestIntegrityHeaderGettersSetters(t *testing.T) { - t.Run("headers checksum", func(t *testing.T) { - data := []byte{1, 2, 3} - - v := new(IntegrityHeader) - - v.SetHeadersChecksum(data) - require.Equal(t, data, v.GetHeadersChecksum()) - }) - - t.Run("headers checksum", func(t *testing.T) { - data := []byte{1, 2, 3} - - v := new(IntegrityHeader) - - v.SetSignature(data) - require.Equal(t, data, v.GetSignature()) - }) -} diff --git a/object/utils.go b/object/utils.go deleted file mode 100644 index 33423aa..0000000 --- a/object/utils.go +++ /dev/null @@ -1,100 +0,0 @@ -package object - -import ( - "io" - "strconv" - - "github.com/pkg/errors" -) - -// FilenameHeader is a user header key for names of files, stored by third -// party apps. We recommend to use this header to be compatible with neofs -// http gate, neofs minio gate and neofs-dropper application. -const FilenameHeader = "filename" - -// ByteSize used to format bytes -type ByteSize uint64 - -// String represents ByteSize in string format -func (b ByteSize) String() string { - var ( - dec int64 - unit string - num = int64(b) - ) - - switch { - case num > UnitsTB: - unit = "TB" - dec = UnitsTB - case num > UnitsGB: - unit = "GB" - dec = UnitsGB - case num > UnitsMB: - unit = "MB" - dec = UnitsMB - case num > UnitsKB: - unit = "KB" - dec = UnitsKB - default: - dec = 1 - } - - return strconv.FormatFloat(float64(num)/float64(dec), 'g', 6, 64) + unit -} - -// MakePutRequestHeader combines object and session token value -// into header of object put request. -func MakePutRequestHeader(obj *Object) *PutRequest { - return &PutRequest{ - R: &PutRequest_Header{Header: &PutRequest_PutHeader{ - Object: obj, - }}, - } -} - -// MakePutRequestChunk splits data into chunks that will be transferred -// in the protobuf stream. -func MakePutRequestChunk(chunk []byte) *PutRequest { - return &PutRequest{R: &PutRequest_Chunk{Chunk: chunk}} -} - -func errMaxSizeExceeded(size uint64) error { - return errors.Errorf("object payload size exceed: %s", ByteSize(size).String()) -} - -// ReceiveGetResponse receives object by chunks from the protobuf stream -// and combine it into single get response structure. -func ReceiveGetResponse(c Service_GetClient, maxSize uint64) (*GetResponse, error) { - res, err := c.Recv() - if err == io.EOF { - return res, err - } else if err != nil { - return nil, err - } - - obj := res.GetObject() - if obj == nil { - return nil, ErrHeaderExpected - } - - if obj.SystemHeader.PayloadLength > maxSize { - return nil, errMaxSizeExceeded(maxSize) - } - - if res.NotFull() { - payload := make([]byte, obj.SystemHeader.PayloadLength) - offset := copy(payload, obj.Payload) - - var r *GetResponse - for r, err = c.Recv(); err == nil; r, err = c.Recv() { - offset += copy(payload[offset:], r.GetChunk()) - } - if err != io.EOF { - return nil, err - } - obj.Payload = payload - } - - return res, nil -} diff --git a/object/utils_test.go b/object/utils_test.go deleted file mode 100644 index d4d7fe2..0000000 --- a/object/utils_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package object - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestByteSize_String(t *testing.T) { - cases := []struct { - name string - expect string - actual ByteSize - }{ - { - name: "0 bytes", - expect: "0", - actual: ByteSize(0), - }, - { - name: "101 bytes", - expect: "101", - actual: ByteSize(101), - }, - { - name: "112.84KB", - expect: "112.84KB", - actual: ByteSize(115548), - }, - { - name: "80.44MB", - expect: "80.44MB", - actual: ByteSize(84347453), - }, - { - name: "905.144GB", - expect: "905.144GB", - actual: ByteSize(971891061884), - }, - { - name: "1.857TB", - expect: "1.857TB", - actual: ByteSize(2041793092780), - }, - } - - for i := range cases { - tt := cases[i] - t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.expect, tt.actual.String()) - }) - } -} diff --git a/object/verification.go b/object/verification.go deleted file mode 100644 index 0bcbc7c..0000000 --- a/object/verification.go +++ /dev/null @@ -1,149 +0,0 @@ -package object - -import ( - "bytes" - "crypto/ecdsa" - "crypto/sha256" - - crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/pkg/errors" -) - -func (m Object) headersData(check bool) ([]byte, error) { - var bytebuf = new(bytes.Buffer) - - // fixme: we must marshal fields one by one without protobuf marshaling - // protobuf marshaling does not guarantee the same result - - if sysheader, err := m.SystemHeader.Marshal(); err != nil { - return nil, err - } else if _, err := bytebuf.Write(sysheader); err != nil { - return nil, err - } - - n, _ := m.LastHeader(HeaderType(IntegrityHdr)) - for i := range m.Headers { - if check && i == n { - // ignore last integrity header in order to check headers data - continue - } - - if header, err := m.Headers[i].Marshal(); err != nil { - return nil, err - } else if _, err := bytebuf.Write(header); err != nil { - return nil, err - } - } - return bytebuf.Bytes(), nil -} - -func (m Object) headersChecksum(check bool) ([]byte, error) { - data, err := m.headersData(check) - if err != nil { - return nil, err - } - checksum := sha256.Sum256(data) - return checksum[:], nil -} - -// PayloadChecksum calculates sha256 checksum of object payload. -func (m Object) PayloadChecksum() []byte { - checksum := sha256.Sum256(m.Payload) - return checksum[:] -} - -func (m Object) verifySignature(key []byte, ih *IntegrityHeader) error { - pk := crypto.UnmarshalPublicKey(key) - if crypto.Verify(pk, ih.HeadersChecksum, ih.ChecksumSignature) == nil { - return nil - } - return ErrVerifySignature -} - -// Verify performs local integrity check by finding verification header and -// integrity header. If header integrity is passed, function verifies -// checksum of the object payload. -// todo: move this verification logic into separate library -func (m Object) Verify() error { - var ( - err error - checksum []byte - pubkey []byte - ) - ind, ih := m.LastHeader(HeaderType(IntegrityHdr)) - if ih == nil || ind != len(m.Headers)-1 { - return ErrHeaderNotFound - } - integrity := ih.Value.(*Header_Integrity).Integrity - - // Prepare structures - _, vh := m.LastHeader(HeaderType(TokenHdr)) - if vh == nil { - _, pkh := m.LastHeader(HeaderType(PublicKeyHdr)) - if pkh == nil { - return ErrHeaderNotFound - } - pubkey = pkh.Value.(*Header_PublicKey).PublicKey.Value - } else { - pubkey = vh.Value.(*Header_Token).Token.GetSessionKey() - } - - // Verify signature - err = m.verifySignature(pubkey, integrity) - if err != nil { - return errors.Wrapf(err, "public key: %x", pubkey) - } - - // Verify checksum of header - checksum, err = m.headersChecksum(true) - if err != nil { - return err - } - if !bytes.Equal(integrity.HeadersChecksum, checksum) { - return ErrVerifyHeader - } - - // Verify checksum of payload - if m.SystemHeader.PayloadLength > 0 && !m.IsLinking() { - checksum = m.PayloadChecksum() - - _, ph := m.LastHeader(HeaderType(PayloadChecksumHdr)) - if ph == nil { - return ErrHeaderNotFound - } - if !bytes.Equal(ph.Value.(*Header_PayloadChecksum).PayloadChecksum, checksum) { - return ErrVerifyPayload - } - } - return nil -} - -// CreateIntegrityHeader returns signed integrity header for the object -func CreateIntegrityHeader(obj *Object, key *ecdsa.PrivateKey) (*Header, error) { - headerChecksum, err := obj.headersChecksum(false) - if err != nil { - return nil, err - } - headerChecksumSignature, err := crypto.Sign(key, headerChecksum) - if err != nil { - return nil, err - } - - return &Header{Value: &Header_Integrity{ - Integrity: &IntegrityHeader{ - HeadersChecksum: headerChecksum, - ChecksumSignature: headerChecksumSignature, - }, - }}, nil -} - -// Sign creates new integrity header and adds it to the end of the list of -// extended headers. -func (m *Object) Sign(key *ecdsa.PrivateKey) error { - ih, err := CreateIntegrityHeader(m, key) - if err != nil { - return err - } - m.AddHeader(ih) - return nil -} diff --git a/object/verification_test.go b/object/verification_test.go deleted file mode 100644 index 95a6d32..0000000 --- a/object/verification_test.go +++ /dev/null @@ -1,144 +0,0 @@ -package object - -import ( - "testing" - - "github.com/google/uuid" - "github.com/nspcc-dev/neofs-api-go/container" - "github.com/nspcc-dev/neofs-api-go/refs" - crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/stretchr/testify/require" -) - -func TestObject_Verify(t *testing.T) { - key := test.DecodeKey(0) - sessionkey := test.DecodeKey(1) - - payload := make([]byte, 1024*1024) - - cnr, err := container.NewTestContainer() - require.NoError(t, err) - - cid, err := cnr.ID() - require.NoError(t, err) - - id, err := uuid.NewRandom() - uid := refs.UUID(id) - require.NoError(t, err) - - obj := &Object{ - SystemHeader: SystemHeader{ - ID: uid, - CID: cid, - OwnerID: refs.OwnerID([refs.OwnerIDSize]byte{}), - }, - Headers: []Header{ - { - Value: &Header_UserHeader{ - UserHeader: &UserHeader{ - Key: "Profession", - Value: "Developer", - }, - }, - }, - { - Value: &Header_UserHeader{ - UserHeader: &UserHeader{ - Key: "Language", - Value: "GO", - }, - }, - }, - }, - } - obj.SetPayload(payload) - obj.SetHeader(&Header{Value: &Header_PayloadChecksum{[]byte("incorrect checksum")}}) - - t.Run("error no integrity header and pubkey", func(t *testing.T) { - err = obj.Verify() - require.EqualError(t, err, ErrHeaderNotFound.Error()) - }) - - badHeaderChecksum := []byte("incorrect checksum") - signature, err := crypto.Sign(sessionkey, badHeaderChecksum) - require.NoError(t, err) - ih := &IntegrityHeader{ - HeadersChecksum: badHeaderChecksum, - ChecksumSignature: signature, - } - obj.SetHeader(&Header{Value: &Header_Integrity{ih}}) - - t.Run("error no validation header", func(t *testing.T) { - err = obj.Verify() - require.EqualError(t, err, ErrHeaderNotFound.Error()) - }) - - dataPK := crypto.MarshalPublicKey(&sessionkey.PublicKey) - signature, err = crypto.Sign(key, dataPK) - tok := new(Token) - tok.SetSignature(signature) - tok.SetSessionKey(dataPK) - obj.AddHeader(&Header{Value: &Header_Token{Token: tok}}) - - // validation header is not last - t.Run("error validation header is not last", func(t *testing.T) { - err = obj.Verify() - require.EqualError(t, err, ErrHeaderNotFound.Error()) - }) - - obj.Headers = obj.Headers[:len(obj.Headers)-2] - obj.AddHeader(&Header{Value: &Header_Token{Token: tok}}) - obj.SetHeader(&Header{Value: &Header_Integrity{ih}}) - - t.Run("error invalid header checksum", func(t *testing.T) { - err = obj.Verify() - require.EqualError(t, err, ErrVerifyHeader.Error()) - }) - - obj.Headers = obj.Headers[:len(obj.Headers)-1] - genIH, err := CreateIntegrityHeader(obj, sessionkey) - require.NoError(t, err) - obj.SetHeader(genIH) - - t.Run("error invalid payload checksum", func(t *testing.T) { - err = obj.Verify() - require.EqualError(t, err, ErrVerifyPayload.Error()) - }) - - obj.SetHeader(&Header{Value: &Header_PayloadChecksum{obj.PayloadChecksum()}}) - - obj.Headers = obj.Headers[:len(obj.Headers)-1] - genIH, err = CreateIntegrityHeader(obj, sessionkey) - require.NoError(t, err) - obj.SetHeader(genIH) - - t.Run("correct with tok", func(t *testing.T) { - err = obj.Verify() - require.NoError(t, err) - }) - - pkh := Header{Value: &Header_PublicKey{&PublicKey{ - Value: crypto.MarshalPublicKey(&key.PublicKey), - }}} - // replace tok with pkh - obj.Headers[len(obj.Headers)-2] = pkh - // re-sign object - obj.Sign(sessionkey) - - t.Run("incorrect with bad public key", func(t *testing.T) { - err = obj.Verify() - require.Error(t, err) - }) - - obj.SetHeader(&Header{Value: &Header_PublicKey{&PublicKey{ - Value: dataPK, - }}}) - obj.Sign(sessionkey) - - t.Run("correct with good public key", func(t *testing.T) { - err = obj.Verify() - require.NoError(t, err) - }) - -} diff --git a/query/types.go b/query/types.go deleted file mode 100644 index 15a54f6..0000000 --- a/query/types.go +++ /dev/null @@ -1,43 +0,0 @@ -package query - -import ( - "strings" - - "github.com/gogo/protobuf/proto" -) - -var ( - _ proto.Message = (*Query)(nil) - _ proto.Message = (*Filter)(nil) -) - -// String returns string representation of Filter. -func (m Filter) String() string { - b := new(strings.Builder) - b.WriteString("") - return b.String() -} - -// String returns string representation of Query. -func (m Query) String() string { - b := new(strings.Builder) - b.WriteString(" 0 { - // add previous key bytes to the signed message - - signKeyDataSrc := SignKeyPairsSignedData(items[i-1]) - - signKeyData, err := signKeyDataSrc.SignedData() - if err != nil { - return errors.Wrapf(err, "could not get signed data of key-signature #%d", i) - } - - data = append(data, signKeyData...) - } - - if err := crypto.Verify( - items[i].GetPublicKey(), - data, - items[i].GetSignature(), - ); err != nil { - return err - } - } - - return nil -} - -// VerifySignatures checks passed key-signature pairs for data from the passed container. -// -// If passed data source is nil, ErrNilSignedDataSource returns. -// If check data is not ready, corresponding error returns. -// If at least one of the pairs is invalid, an error returns. -func VerifySignatures(src SignedDataSource, items ...SignKeyPair) error { - return verifySignatures(src, items...) -} - -// VerifyAccumulatedSignatures checks if accumulated key-signature pairs are valid. -// -// Behaves like VerifySignatures. -// If passed key-signature source is empty, ErrNilSignatureKeySource returns. -func VerifyAccumulatedSignatures(src DataWithSignKeySource) error { - if src == nil { - return ErrNilSignatureKeySource - } - - return verifySignatures(src, src.GetSignKeyPairs()...) -} - -// VerifySignatureWithKey checks data signature from the passed container with passed key. -// -// If passed data with signature is nil, ErrEmptyDataWithSignature returns. -// If passed key is nil, crypto.ErrEmptyPublicKey returns. -// A non-nil error returns if and only if the signature does not pass verification. -func VerifySignatureWithKey(key *ecdsa.PublicKey, src DataWithSignature) error { - if src == nil { - return ErrEmptyDataWithSignature - } else if key == nil { - return crypto.ErrEmptyPublicKey - } - - return verifySignatures( - src, - newSignatureKeyPair( - key, - src.GetSignature(), - ), - ) -} - -// SignRequestData calculates request data signature and adds it to accumulator. -// -// Any change of request data provoke signature breakdown. -// -// If passed private key is nil, crypto.ErrEmptyPrivateKey returns. -// If passed RequestSignedData is nil, ErrNilRequestSignedData returns. -func SignRequestData(key *ecdsa.PrivateKey, src RequestSignedData) error { - if src == nil { - return ErrNilRequestSignedData - } - - sigSrc, err := GroupSignedPayloads( - src, - src, - NewSignedSessionToken( - src.GetSessionToken(), - ), - NewSignedBearerToken( - src.GetBearerToken(), - ), - ExtendedHeadersSignedData(src), - SignKeyPairsSignedData(src.GetSignKeyPairs()...), - ) - if err != nil { - return err - } - - return AddSignatureWithKey(key, sigSrc) -} - -// VerifyRequestData checks if accumulated key-signature pairs of data with token are valid. -// -// If passed RequestVerifyData is nil, ErrNilRequestVerifyData returns. -func VerifyRequestData(src RequestVerifyData) error { - if src == nil { - return ErrNilRequestVerifyData - } - - verSrc, err := GroupVerifyPayloads( - src, - src, - NewVerifiedSessionToken( - src.GetSessionToken(), - ), - NewVerifiedBearerToken( - src.GetBearerToken(), - ), - ExtendedHeadersSignedData(src), - ) - if err != nil { - return err - } - - return VerifyAccumulatedSignatures(verSrc) -} - -// SignedData returns payload bytes concatenation from all sources keeping order. -func (s signSourceGroup) SignedData() ([]byte, error) { - chunks := make([][]byte, 0, len(s.sources)) - sz := 0 - - for i := range s.sources { - data, err := s.sources[i].SignedData() - if err != nil { - return nil, errors.Wrapf(err, "could not get signed payload of element #%d", i) - } - - chunks = append(chunks, data) - - sz += len(data) - } - - res := make([]byte, sz) - off := 0 - - for i := range chunks { - off += copy(res[off:], chunks[i]) - } - - return res, nil -} - -// SignedData returns payload bytes concatenation from all readers. -func (s signReadersGroup) SignedData() ([]byte, error) { - return SignedDataFromReader(s) -} - -// SignedDataSize returns the sum of sizes of all readers. -func (s signReadersGroup) SignedDataSize() (sz int) { - for i := range s.readers { - sz += s.readers[i].SignedDataSize() - } - - return -} - -// ReadSignedData reads data from all readers to passed buffer keeping order. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (s signReadersGroup) ReadSignedData(p []byte) (int, error) { - sz := s.SignedDataSize() - if len(p) < sz { - return 0, io.ErrUnexpectedEOF - } - - off := 0 - - for i := range s.readers { - n, err := s.readers[i].ReadSignedData(p[off:]) - off += n - if err != nil { - return off, errors.Wrapf(err, "could not read signed payload of element #%d", i) - } - } - - return off, nil -} - -// GroupSignedPayloads groups SignKeyPairAccumulator and SignedDataSource list to DataWithSignKeyAccumulator. -// -// If passed SignKeyPairAccumulator is nil, ErrNilSignKeyPairAccumulator returns. -// -// Signed payload of the result is a concatenation of payloads of list elements keeping order. -// Nil elements in list are ignored. -// -// If all elements implement SignedDataReader, result implements it too. -func GroupSignedPayloads(acc SignKeyPairAccumulator, sources ...SignedDataSource) (DataWithSignKeyAccumulator, error) { - if acc == nil { - return nil, ErrNilSignKeyPairAccumulator - } - - return groupPayloads(acc, nil, sources...), nil -} - -// GroupVerifyPayloads groups SignKeyPairSource and SignedDataSource list to DataWithSignKeySource. -// -// If passed SignKeyPairSource is nil, ErrNilSignatureKeySource returns. -// -// Signed payload of the result is a concatenation of payloads of list elements keeping order. -// Nil elements in list are ignored. -// -// If all elements implement SignedDataReader, result implements it too. -func GroupVerifyPayloads(src SignKeyPairSource, sources ...SignedDataSource) (DataWithSignKeySource, error) { - if src == nil { - return nil, ErrNilSignatureKeySource - } - - return groupPayloads(nil, src, sources...), nil -} - -func groupPayloads(acc SignKeyPairAccumulator, src SignKeyPairSource, sources ...SignedDataSource) interface { - SignedDataSource - SignKeyPairSource - SignKeyPairAccumulator -} { - var allReaders bool - - for i := range sources { - if sources[i] == nil { - continue - } else if _, allReaders = sources[i].(SignedDataReader); !allReaders { - break - } - } - - if !allReaders { - res := &signSourceGroup{ - SignKeyPairSource: src, - SignKeyPairAccumulator: acc, - - sources: make([]SignedDataSource, 0, len(sources)), - } - - for i := range sources { - if sources[i] != nil { - res.sources = append(res.sources, sources[i]) - } - } - - return res - } - - res := &signReadersGroup{ - SignKeyPairSource: src, - SignKeyPairAccumulator: acc, - - readers: make([]SignedDataReader, 0, len(sources)), - } - - for i := range sources { - if sources[i] != nil { - res.readers = append(res.readers, sources[i].(SignedDataReader)) - } - } - - return res -} diff --git a/service/sign_test.go b/service/sign_test.go deleted file mode 100644 index 3c54e8c..0000000 --- a/service/sign_test.go +++ /dev/null @@ -1,400 +0,0 @@ -package service - -import ( - "crypto/ecdsa" - "crypto/rand" - "errors" - "io" - "testing" - - crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/stretchr/testify/require" -) - -type testSignedDataSrc struct { - err error - data []byte - token SessionToken - - bearer BearerToken - - extHdrs []ExtendedHeader - - signKeys []SignKeyPair -} - -type testSignedDataReader struct { - *testSignedDataSrc -} - -func (s testSignedDataSrc) GetSignature() []byte { - if len(s.signKeys) > 0 { - return s.signKeys[0].GetSignature() - } - - return nil -} - -func (s testSignedDataSrc) GetSignKeyPairs() []SignKeyPair { - return s.signKeys -} - -func (s testSignedDataSrc) SignedData() ([]byte, error) { - return s.data, s.err -} - -func (s *testSignedDataSrc) AddSignKey(sig []byte, key *ecdsa.PublicKey) { - s.signKeys = append(s.signKeys, - newSignatureKeyPair(key, sig), - ) -} - -func testData(t *testing.T, sz int) []byte { - d := make([]byte, sz) - _, err := rand.Read(d) - require.NoError(t, err) - return d -} - -func (s testSignedDataSrc) GetSessionToken() SessionToken { - return s.token -} - -func (s testSignedDataSrc) GetBearerToken() BearerToken { - return s.bearer -} - -func (s testSignedDataSrc) ExtendedHeaders() []ExtendedHeader { - return s.extHdrs -} - -func (s testSignedDataReader) SignedDataSize() int { - return len(s.data) -} - -func (s testSignedDataReader) ReadSignedData(buf []byte) (int, error) { - if s.err != nil { - return 0, s.err - } - - var err error - if len(buf) < len(s.data) { - err = io.ErrUnexpectedEOF - } - return copy(buf, s.data), err -} - -func TestDataSignature(t *testing.T) { - var err error - - // nil private key - _, err = DataSignature(nil, nil) - require.EqualError(t, err, crypto.ErrEmptyPrivateKey.Error()) - - // create test private key - sk := test.DecodeKey(0) - - // nil private key - _, err = DataSignature(sk, nil) - require.EqualError(t, err, ErrNilSignedDataSource.Error()) - - t.Run("common signed data source", func(t *testing.T) { - // create test data source - src := &testSignedDataSrc{ - data: testData(t, 10), - } - - // create custom error for data source - src.err = errors.New("test error for data source") - - _, err = DataSignature(sk, src) - require.EqualError(t, err, src.err.Error()) - - // reset error to nil - src.err = nil - - // calculate data signature - sig, err := DataSignature(sk, src) - require.NoError(t, err) - - // ascertain that the signature passes verification - require.NoError(t, crypto.Verify(&sk.PublicKey, src.data, sig)) - }) - - t.Run("signed data reader", func(t *testing.T) { - // create test signed data reader - src := &testSignedDataSrc{ - data: testData(t, 10), - } - - // create custom error for signed data reader - src.err = errors.New("test error for signed data reader") - - sig, err := DataSignature(sk, src) - require.EqualError(t, err, src.err.Error()) - - // reset error to nil - src.err = nil - - // calculate data signature - sig, err = DataSignature(sk, src) - require.NoError(t, err) - - // ascertain that the signature passes verification - require.NoError(t, crypto.Verify(&sk.PublicKey, src.data, sig)) - }) -} - -func TestAddSignatureWithKey(t *testing.T) { - require.NoError(t, - AddSignatureWithKey( - test.DecodeKey(0), - &testSignedDataSrc{ - data: testData(t, 10), - }, - ), - ) -} - -func TestVerifySignatures(t *testing.T) { - // empty signatures - require.NoError(t, VerifySignatures(nil)) - - // create test signature source - src := &testSignedDataSrc{ - data: testData(t, 10), - } - - // create private key for test - sk := test.DecodeKey(0) - - // calculate a signature of the data - sig, err := crypto.Sign(sk, src.data) - require.NoError(t, err) - - // ascertain that verification is passed - require.NoError(t, - VerifySignatures( - src, - newSignatureKeyPair(&sk.PublicKey, sig), - ), - ) - - // break the signature - sig[0]++ - - require.Error(t, - VerifySignatures( - src, - newSignatureKeyPair(&sk.PublicKey, sig), - ), - ) - - // restore the signature - sig[0]-- - - // empty data source - require.EqualError(t, - VerifySignatures(nil, nil), - ErrNilSignedDataSource.Error(), - ) - -} - -func TestVerifyAccumulatedSignatures(t *testing.T) { - // nil signature source - require.EqualError(t, - VerifyAccumulatedSignatures(nil), - ErrNilSignatureKeySource.Error(), - ) - - // create test private key - sk := test.DecodeKey(0) - - signKey := new(RequestVerificationHeader_Signature) - signKey.Peer = crypto.MarshalPublicKey(&sk.PublicKey) - - // create signature source - src := &testSignedDataSrc{ - data: testData(t, 10), - - signKeys: []SignKeyPair{signKey}, - } - - var err error - - // calculate a signature - signKey.Sign, err = crypto.Sign(sk, src.data) - require.NoError(t, err) - - // ascertain that verification is passed - require.NoError(t, VerifyAccumulatedSignatures(src)) - - // break the signature - signKey.Sign[0]++ - - // ascertain that verification is failed - require.Error(t, VerifyAccumulatedSignatures(src)) -} - -func TestVerifySignatureWithKey(t *testing.T) { - // nil signature source - require.EqualError(t, - VerifySignatureWithKey(nil, nil), - ErrEmptyDataWithSignature.Error(), - ) - - signKey := new(RequestVerificationHeader_Signature) - - // create test signature source - src := &testSignedDataSrc{ - data: testData(t, 10), - - signKeys: []SignKeyPair{signKey}, - } - - // nil public key - require.EqualError(t, - VerifySignatureWithKey(nil, src), - crypto.ErrEmptyPublicKey.Error(), - ) - - // create test private key - sk := test.DecodeKey(0) - - var err error - - // calculate a signature - signKey.Sign, err = crypto.Sign(sk, src.data) - require.NoError(t, err) - - // ascertain that verification is passed - require.NoError(t, VerifySignatureWithKey(&sk.PublicKey, src)) - - // break the signature - signKey.Sign[0]++ - - // ascertain that verification is failed - require.Error(t, VerifySignatureWithKey(&sk.PublicKey, src)) -} - -func TestSignVerifyRequestData(t *testing.T) { - // sign with empty RequestSignedData - require.EqualError(t, - SignRequestData(nil, nil), - ErrNilRequestSignedData.Error(), - ) - - // verify with empty RequestVerifyData - require.EqualError(t, - VerifyRequestData(nil), - ErrNilRequestVerifyData.Error(), - ) - - // create test session token - var ( - token = new(Token) - initVerb = Token_Info_Verb(1) - - bearer = new(BearerTokenMsg) - bearerEpoch = uint64(8) - - extHdrKey = "key" - extHdr = new(RequestExtendedHeader_KV) - ) - - token.SetVerb(initVerb) - - bearer.SetExpirationEpoch(bearerEpoch) - - extHdr.SetK(extHdrKey) - - // create test data with token - src := &testSignedDataSrc{ - data: testData(t, 10), - token: token, - - bearer: bearer, - - extHdrs: []ExtendedHeader{ - wrapExtendedHeaderKV(extHdr), - }, - } - - // create test private key - sk := test.DecodeKey(0) - - // sign with private key - require.NoError(t, SignRequestData(sk, src)) - - // ascertain that verification is passed - require.NoError(t, VerifyRequestData(src)) - - // break the data - src.data[0]++ - - // ascertain that verification is failed - require.Error(t, VerifyRequestData(src)) - - // restore the data - src.data[0]-- - - // break the token - token.SetVerb(initVerb + 1) - - // ascertain that verification is failed - require.Error(t, VerifyRequestData(src)) - - // restore the token - token.SetVerb(initVerb) - - // ascertain that verification is passed - require.NoError(t, VerifyRequestData(src)) - - // break the Bearer token - bearer.SetExpirationEpoch(bearerEpoch + 1) - - // ascertain that verification is failed - require.Error(t, VerifyRequestData(src)) - - // restore the Bearer token - bearer.SetExpirationEpoch(bearerEpoch) - - // ascertain that verification is passed - require.NoError(t, VerifyRequestData(src)) - - // break the extended header - extHdr.SetK(extHdrKey + "1") - - // ascertain that verification is failed - require.Error(t, VerifyRequestData(src)) - - // restore the extended header - extHdr.SetK(extHdrKey) - - // ascertain that verification is passed - require.NoError(t, VerifyRequestData(src)) - - // wrap to data reader - rdr := &testSignedDataReader{ - testSignedDataSrc: src, - } - - // sign with private key - require.NoError(t, SignRequestData(sk, rdr)) - - // ascertain that verification is passed - require.NoError(t, VerifyRequestData(rdr)) - - if len(rdr.GetSignKeyPairs()) < 2 { - // add one more signature - require.NoError(t, SignRequestData(test.DecodeKey(1), rdr)) - } - - // change key-signature order - rdr.signKeys[0], rdr.signKeys[1] = rdr.signKeys[1], rdr.signKeys[0] - - // ascertain that verification is failed - require.Error(t, VerifyRequestData(src)) -} diff --git a/service/token.go b/service/token.go deleted file mode 100644 index 638539f..0000000 --- a/service/token.go +++ /dev/null @@ -1,265 +0,0 @@ -package service - -import ( - "crypto/ecdsa" - "encoding/binary" - "io" - - "github.com/nspcc-dev/neofs-api-go/refs" - crypto "github.com/nspcc-dev/neofs-crypto" -) - -type signAccumWithToken struct { - SignedDataSource - SignKeyPairAccumulator - SignKeyPairSource - - token SessionToken -} - -type signDataReaderWithToken struct { - SignedDataSource - SignKeyPairAccumulator - SignKeyPairSource - - rdr SignedDataReader - - token SessionToken -} - -type signedSessionToken struct { - SessionToken -} - -const verbSize = 4 - -const fixedTokenDataSize = 0 + - refs.UUIDSize + - refs.OwnerIDSize + - verbSize + - refs.UUIDSize + - refs.CIDSize + - 8 + - 8 - -var tokenEndianness = binary.BigEndian - -// GetID is an ID field getter. -func (m Token_Info) GetID() TokenID { - return m.ID -} - -// SetID is an ID field setter. -func (m *Token_Info) SetID(id TokenID) { - m.ID = id -} - -// GetOwnerID is an OwnerID field getter. -func (m Token_Info) GetOwnerID() OwnerID { - return m.OwnerID -} - -// SetOwnerID is an OwnerID field setter. -func (m *Token_Info) SetOwnerID(id OwnerID) { - m.OwnerID = id -} - -// SetVerb is a Verb field setter. -func (m *Token_Info) SetVerb(verb Token_Info_Verb) { - m.Verb = verb -} - -// GetAddress is an Address field getter. -func (m Token_Info) GetAddress() Address { - return m.Address -} - -// SetAddress is an Address field setter. -func (m *Token_Info) SetAddress(addr Address) { - m.Address = addr -} - -// CreationEpoch is a Created field getter. -func (m TokenLifetime) CreationEpoch() uint64 { - return m.Created -} - -// SetCreationEpoch is a Created field setter. -func (m *TokenLifetime) SetCreationEpoch(e uint64) { - m.Created = e -} - -// ExpirationEpoch is a ValidUntil field getter. -func (m TokenLifetime) ExpirationEpoch() uint64 { - return m.ValidUntil -} - -// SetExpirationEpoch is a ValidUntil field setter. -func (m *TokenLifetime) SetExpirationEpoch(e uint64) { - m.ValidUntil = e -} - -// SetSessionKey is a SessionKey field setter. -func (m *Token_Info) SetSessionKey(key []byte) { - m.SessionKey = key -} - -// SetOwnerKey is an OwnerKey field setter. -func (m *Token_Info) SetOwnerKey(key []byte) { - m.OwnerKey = key -} - -// SetSignature is a Signature field setter. -func (m *Token) SetSignature(sig []byte) { - m.Signature = sig -} - -// Size returns the size of a binary representation of the verb. -func (x Token_Info_Verb) Size() int { - return verbSize -} - -// Bytes returns a binary representation of the verb. -func (x Token_Info_Verb) Bytes() []byte { - data := make([]byte, verbSize) - tokenEndianness.PutUint32(data, uint32(x)) - return data -} - -// AddSignKey calls a Signature field setter and an OwnerKey field setter with corresponding arguments. -func (s signedSessionToken) AddSignKey(sig []byte, key *ecdsa.PublicKey) { - if s.SessionToken != nil { - s.SessionToken.SetSignature(sig) - - s.SessionToken.SetOwnerKey( - crypto.MarshalPublicKey(key), - ) - } -} - -// SignedData returns token information in a binary representation. -func (s signedSessionToken) SignedData() ([]byte, error) { - return SignedDataFromReader(s) -} - -// SignedDataSize returns the length of signed token information slice. -func (s signedSessionToken) SignedDataSize() int { - return tokenInfoSize(s.SessionToken) -} - -// ReadSignedData copies a binary representation of the token information to passed buffer. -// -// If buffer length is less than required, io.ErrUnexpectedEOF returns. -func (s signedSessionToken) ReadSignedData(p []byte) (int, error) { - sz := s.SignedDataSize() - if len(p) < sz { - return 0, io.ErrUnexpectedEOF - } - - copyTokenSignedData(p, s.SessionToken) - - return sz, nil -} - -// NewSignedSessionToken wraps passed SessionToken in a component suitable for signing. -// -// Result can be used in AddSignatureWithKey function. -func NewSignedSessionToken(token SessionToken) DataWithSignKeyAccumulator { - return &signedSessionToken{ - SessionToken: token, - } -} - -// NewVerifiedSessionToken wraps passed SessionToken in a component suitable for signature verification. -// -// Result can be used in VerifySignatureWithKey function. -func NewVerifiedSessionToken(token SessionToken) DataWithSignature { - return &signedSessionToken{ - SessionToken: token, - } -} - -func tokenInfoSize(v SessionKeySource) int { - if v == nil { - return 0 - } - return fixedTokenDataSize + len(v.GetSessionKey()) -} - -// Fills passed buffer with signing token information bytes. -// Does not check buffer length, it is understood that enough space is allocated in it. -// -// If passed SessionTokenInfo, buffer remains unchanged. -func copyTokenSignedData(buf []byte, token SessionTokenInfo) { - if token == nil { - return - } - - var off int - - off += copy(buf[off:], token.GetID().Bytes()) - - off += copy(buf[off:], token.GetOwnerID().Bytes()) - - off += copy(buf[off:], token.GetVerb().Bytes()) - - addr := token.GetAddress() - off += copy(buf[off:], addr.CID.Bytes()) - off += copy(buf[off:], addr.ObjectID.Bytes()) - - tokenEndianness.PutUint64(buf[off:], token.CreationEpoch()) - off += 8 - - tokenEndianness.PutUint64(buf[off:], token.ExpirationEpoch()) - off += 8 - - copy(buf[off:], token.GetSessionKey()) -} - -// SignedData concatenates signed data with session token information. Returns concatenation result. -// -// Token bytes are added if and only if token is not nil. -func (s signAccumWithToken) SignedData() ([]byte, error) { - data, err := s.SignedDataSource.SignedData() - if err != nil { - return nil, err - } - - tokenData := make([]byte, tokenInfoSize(s.token)) - - copyTokenSignedData(tokenData, s.token) - - return append(data, tokenData...), nil -} - -func (s signDataReaderWithToken) SignedDataSize() int { - sz := s.rdr.SignedDataSize() - if sz < 0 { - return -1 - } - - sz += tokenInfoSize(s.token) - - return sz -} - -func (s signDataReaderWithToken) ReadSignedData(p []byte) (int, error) { - dataSize := s.rdr.SignedDataSize() - if dataSize < 0 { - return 0, ErrNegativeLength - } - - sumSize := dataSize + tokenInfoSize(s.token) - - if len(p) < sumSize { - return 0, io.ErrUnexpectedEOF - } - - if n, err := s.rdr.ReadSignedData(p); err != nil { - return n, err - } - - copyTokenSignedData(p[dataSize:], s.token) - - return sumSize, nil -} diff --git a/service/token_test.go b/service/token_test.go deleted file mode 100644 index 4fb430a..0000000 --- a/service/token_test.go +++ /dev/null @@ -1,248 +0,0 @@ -package service - -import ( - "crypto/rand" - "testing" - - "github.com/nspcc-dev/neofs-api-go/refs" - crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/stretchr/testify/require" -) - -func TestTokenGettersSetters(t *testing.T) { - var tok SessionToken = new(Token) - - { // ID - id, err := refs.NewUUID() - require.NoError(t, err) - - tok.SetID(id) - - require.Equal(t, id, tok.GetID()) - } - - { // OwnerID - ownerID := OwnerID{} - _, err := rand.Read(ownerID[:]) - require.NoError(t, err) - - tok.SetOwnerID(ownerID) - - require.Equal(t, ownerID, tok.GetOwnerID()) - } - - { // Verb - verb := Token_Info_Verb(3) - - tok.SetVerb(verb) - - require.Equal(t, verb, tok.GetVerb()) - } - - { // Address - addr := Address{} - _, err := rand.Read(addr.CID[:]) - require.NoError(t, err) - _, err = rand.Read(addr.ObjectID[:]) - require.NoError(t, err) - - tok.SetAddress(addr) - - require.Equal(t, addr, tok.GetAddress()) - } - - { // Created - e := uint64(5) - - tok.SetCreationEpoch(e) - - require.Equal(t, e, tok.CreationEpoch()) - } - - { // ValidUntil - e := uint64(5) - - tok.SetExpirationEpoch(e) - - require.Equal(t, e, tok.ExpirationEpoch()) - } - - { // SessionKey - key := make([]byte, 10) - _, err := rand.Read(key) - require.NoError(t, err) - - tok.SetSessionKey(key) - - require.Equal(t, key, tok.GetSessionKey()) - } - - { // Signature - sig := make([]byte, 10) - _, err := rand.Read(sig) - require.NoError(t, err) - - tok.SetSignature(sig) - - require.Equal(t, sig, tok.GetSignature()) - } -} - -func TestSignToken(t *testing.T) { - var token SessionToken = new(Token) - - // create private key for signing - sk := test.DecodeKey(0) - pk := &sk.PublicKey - - id := TokenID{} - _, err := rand.Read(id[:]) - require.NoError(t, err) - token.SetID(id) - - ownerID := OwnerID{} - _, err = rand.Read(ownerID[:]) - require.NoError(t, err) - token.SetOwnerID(ownerID) - - verb := Token_Info_Verb(1) - token.SetVerb(verb) - - addr := Address{} - _, err = rand.Read(addr.ObjectID[:]) - require.NoError(t, err) - _, err = rand.Read(addr.CID[:]) - require.NoError(t, err) - token.SetAddress(addr) - - cEpoch := uint64(1) - token.SetCreationEpoch(cEpoch) - - fEpoch := uint64(2) - token.SetExpirationEpoch(fEpoch) - - sessionKey := make([]byte, 10) - _, err = rand.Read(sessionKey[:]) - require.NoError(t, err) - token.SetSessionKey(sessionKey) - - signedToken := NewSignedSessionToken(token) - verifiedToken := NewVerifiedSessionToken(token) - - // sign and verify token - require.NoError(t, AddSignatureWithKey(sk, signedToken)) - require.NoError(t, VerifySignatureWithKey(pk, verifiedToken)) - - items := []struct { - corrupt func() - restore func() - }{ - { // ID - corrupt: func() { - id[0]++ - token.SetID(id) - }, - restore: func() { - id[0]-- - token.SetID(id) - }, - }, - { // Owner ID - corrupt: func() { - ownerID[0]++ - token.SetOwnerID(ownerID) - }, - restore: func() { - ownerID[0]-- - token.SetOwnerID(ownerID) - }, - }, - { // Verb - corrupt: func() { - token.SetVerb(verb + 1) - }, - restore: func() { - token.SetVerb(verb) - }, - }, - { // ObjectID - corrupt: func() { - addr.ObjectID[0]++ - token.SetAddress(addr) - }, - restore: func() { - addr.ObjectID[0]-- - token.SetAddress(addr) - }, - }, - { // CID - corrupt: func() { - addr.CID[0]++ - token.SetAddress(addr) - }, - restore: func() { - addr.CID[0]-- - token.SetAddress(addr) - }, - }, - { // Creation epoch - corrupt: func() { - token.SetCreationEpoch(cEpoch + 1) - }, - restore: func() { - token.SetCreationEpoch(cEpoch) - }, - }, - { // Expiration epoch - corrupt: func() { - token.SetExpirationEpoch(fEpoch + 1) - }, - restore: func() { - token.SetExpirationEpoch(fEpoch) - }, - }, - { // Session key - corrupt: func() { - sessionKey[0]++ - token.SetSessionKey(sessionKey) - }, - restore: func() { - sessionKey[0]-- - token.SetSessionKey(sessionKey) - }, - }, - } - - for _, v := range items { - v.corrupt() - require.Error(t, VerifySignatureWithKey(pk, verifiedToken)) - v.restore() - require.NoError(t, VerifySignatureWithKey(pk, verifiedToken)) - } -} - -func TestSignedSessionToken_AddSignKey(t *testing.T) { - // nil SessionToken - s := new(signedSessionToken) - - require.NotPanics(t, func() { - s.AddSignKey(nil, nil) - }) - - // create test public key and signature - pk := &test.DecodeKey(0).PublicKey - sig := []byte{1, 2, 3} - - s.SessionToken = new(Token) - - // add key-signature pair to SessionToken - s.AddSignKey(sig, pk) - - require.Equal(t, sig, s.GetSignature()) - - require.Equal(t, - crypto.MarshalPublicKey(pk), - s.GetOwnerKey(), - ) -} diff --git a/service/ttl.go b/service/ttl.go deleted file mode 100644 index 28a5092..0000000 --- a/service/ttl.go +++ /dev/null @@ -1,63 +0,0 @@ -package service - -import ( - "github.com/pkg/errors" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// TTL constants. -const ( - // ZeroTTL is an upper bound of invalid TTL values. - ZeroTTL = iota - - // NonForwardingTTL is a TTL value that does not imply a request forwarding. - NonForwardingTTL - - // SingleForwardingTTL is a TTL value that imply potential forwarding with NonForwardingTTL. - SingleForwardingTTL -) - -// SetTTL is a TTL field setter. -func (m *RequestMetaHeader) SetTTL(v uint32) { - m.TTL = v -} - -// IRNonForwarding condition that allows NonForwardingTTL only for IR. -func IRNonForwarding(role NodeRole) TTLCondition { - return func(ttl uint32) error { - if ttl == NonForwardingTTL && role != InnerRingNode { - return ErrInvalidTTL - } - - return nil - } -} - -// ProcessRequestTTL validates and updates requests with TTL. -func ProcessRequestTTL(req TTLContainer, cond ...TTLCondition) error { - ttl := req.GetTTL() - - if ttl == ZeroTTL { - return status.New(codes.InvalidArgument, ErrInvalidTTL.Error()).Err() - } - - for i := range cond { - if cond[i] == nil { - continue - } - - // check specific condition: - if err := cond[i](ttl); err != nil { - if st, ok := status.FromError(errors.Cause(err)); ok { - return st.Err() - } - - return status.New(codes.InvalidArgument, err.Error()).Err() - } - } - - req.SetTTL(ttl - 1) - - return nil -} diff --git a/service/ttl_test.go b/service/ttl_test.go deleted file mode 100644 index 1c982f5..0000000 --- a/service/ttl_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package service - -import ( - "testing" - - "github.com/pkg/errors" - "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -type mockedRequest struct { - msg string - name string - code codes.Code - handler TTLCondition - RequestMetaHeader -} - -func TestMetaRequest(t *testing.T) { - tests := []mockedRequest{ - { - name: "direct to ir node", - handler: IRNonForwarding(InnerRingNode), - RequestMetaHeader: RequestMetaHeader{TTL: NonForwardingTTL}, - }, - { - code: codes.InvalidArgument, - msg: ErrInvalidTTL.Error(), - name: "direct to storage node", - handler: IRNonForwarding(StorageNode), - RequestMetaHeader: RequestMetaHeader{TTL: NonForwardingTTL}, - }, - { - msg: ErrInvalidTTL.Error(), - code: codes.InvalidArgument, - name: "zero ttl", - handler: IRNonForwarding(StorageNode), - RequestMetaHeader: RequestMetaHeader{TTL: ZeroTTL}, - }, - { - name: "default to ir node", - handler: IRNonForwarding(InnerRingNode), - RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, - }, - { - name: "default to storage node", - handler: IRNonForwarding(StorageNode), - RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, - }, - { - msg: "not found", - code: codes.NotFound, - name: "custom status error", - RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, - handler: func(_ uint32) error { return status.Error(codes.NotFound, "not found") }, - }, - { - msg: "not found", - code: codes.NotFound, - name: "custom wrapped status error", - RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, - handler: func(_ uint32) error { - err := status.Error(codes.NotFound, "not found") - err = errors.Wrap(err, "some error context") - err = errors.Wrap(err, "another error context") - return err - }, - }, - } - - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - before := tt.GetTTL() - err := ProcessRequestTTL(&tt, tt.handler) - if tt.msg != "" { - require.Errorf(t, err, tt.msg) - - state, ok := status.FromError(err) - require.True(t, ok) - require.Equal(t, tt.code, state.Code()) - require.Equal(t, tt.msg, state.Message()) - } else { - require.NoError(t, err) - require.NotEqualf(t, before, tt.GetTTL(), "ttl should be changed: %d vs %d", before, tt.GetTTL()) - } - }) - } -} - -func TestRequestMetaHeader_SetTTL(t *testing.T) { - m := new(RequestMetaHeader) - ttl := uint32(3) - - m.SetTTL(ttl) - - require.Equal(t, ttl, m.GetTTL()) -} diff --git a/service/types.go b/service/types.go deleted file mode 100644 index 785a30a..0000000 --- a/service/types.go +++ /dev/null @@ -1,317 +0,0 @@ -package service - -import ( - "crypto/ecdsa" -) - -// NodeRole to identify in Bootstrap service. -type NodeRole int32 - -// TTLCondition is a function type that used to verify that TTL values match a specific criterion. -// Nil error indicates compliance with the criterion. -type TTLCondition func(uint32) error - -// RawSource is an interface of the container of a boolean Raw value with read access. -type RawSource interface { - GetRaw() bool -} - -// RawContainer is an interface of the container of a boolean Raw value. -type RawContainer interface { - RawSource - SetRaw(bool) -} - -// VersionSource is an interface of the container of a numerical Version value with read access. -type VersionSource interface { - GetVersion() uint32 -} - -// VersionContainer is an interface of the container of a numerical Version value. -type VersionContainer interface { - VersionSource - SetVersion(uint32) -} - -// EpochSource is an interface of the container of a NeoFS epoch number with read access. -type EpochSource interface { - GetEpoch() uint64 -} - -// EpochContainer is an interface of the container of a NeoFS epoch number. -type EpochContainer interface { - EpochSource - SetEpoch(uint64) -} - -// TTLSource is an interface of the container of a numerical TTL value with read access. -type TTLSource interface { - GetTTL() uint32 -} - -// TTLContainer is an interface of the container of a numerical TTL value. -type TTLContainer interface { - TTLSource - SetTTL(uint32) -} - -// SeizedMetaHeaderContainer is an interface of container of RequestMetaHeader that can be cut and restored. -type SeizedMetaHeaderContainer interface { - CutMeta() RequestMetaHeader - RestoreMeta(RequestMetaHeader) -} - -// RequestMetaContainer is an interface of a fixed set of request meta value containers. -// Contains: -// - TTL value; -// - NeoFS epoch number; -// - Protocol version; -// - Raw toggle option. -type RequestMetaContainer interface { - TTLContainer - EpochContainer - VersionContainer - RawContainer -} - -// SeizedRequestMetaContainer is a RequestMetaContainer with seized meta. -type SeizedRequestMetaContainer interface { - RequestMetaContainer - SeizedMetaHeaderContainer -} - -// VerbSource is an interface of the container of a token verb value with read access. -type VerbSource interface { - GetVerb() Token_Info_Verb -} - -// VerbContainer is an interface of the container of a token verb value. -type VerbContainer interface { - VerbSource - SetVerb(Token_Info_Verb) -} - -// TokenIDSource is an interface of the container of a token ID value with read access. -type TokenIDSource interface { - GetID() TokenID -} - -// TokenIDContainer is an interface of the container of a token ID value. -type TokenIDContainer interface { - TokenIDSource - SetID(TokenID) -} - -// CreationEpochSource is an interface of the container of a creation epoch number with read access. -type CreationEpochSource interface { - CreationEpoch() uint64 -} - -// CreationEpochContainer is an interface of the container of a creation epoch number. -type CreationEpochContainer interface { - CreationEpochSource - SetCreationEpoch(uint64) -} - -// ExpirationEpochSource is an interface of the container of an expiration epoch number with read access. -type ExpirationEpochSource interface { - ExpirationEpoch() uint64 -} - -// ExpirationEpochContainer is an interface of the container of an expiration epoch number. -type ExpirationEpochContainer interface { - ExpirationEpochSource - SetExpirationEpoch(uint64) -} - -// LifetimeSource is an interface of the container of creation-expiration epoch pair with read access. -type LifetimeSource interface { - CreationEpochSource - ExpirationEpochSource -} - -// LifetimeContainer is an interface of the container of creation-expiration epoch pair. -type LifetimeContainer interface { - CreationEpochContainer - ExpirationEpochContainer -} - -// SessionKeySource is an interface of the container of session key bytes with read access. -type SessionKeySource interface { - GetSessionKey() []byte -} - -// SessionKeyContainer is an interface of the container of public session key bytes. -type SessionKeyContainer interface { - SessionKeySource - SetSessionKey([]byte) -} - -// SignatureSource is an interface of the container of signature bytes with read access. -type SignatureSource interface { - GetSignature() []byte -} - -// SignatureContainer is an interface of the container of signature bytes. -type SignatureContainer interface { - SignatureSource - SetSignature([]byte) -} - -// OwnerKeySource is an interface of the container of owner key bytes with read access. -type OwnerKeySource interface { - GetOwnerKey() []byte -} - -// OwnerKeyContainer is an interface of the container of owner key bytes. -type OwnerKeyContainer interface { - OwnerKeySource - SetOwnerKey([]byte) -} - -// SessionTokenSource is an interface of the container of a SessionToken with read access. -type SessionTokenSource interface { - GetSessionToken() SessionToken -} - -// SessionTokenInfo is an interface of a fixed set of token information value containers. -// Contains: -// - ID of the token; -// - ID of the token's owner; -// - verb of the session; -// - address of the session object; -// - token lifetime; -// - public session key bytes; -// - owner's public key bytes. -type SessionTokenInfo interface { - TokenIDContainer - OwnerIDContainer - VerbContainer - AddressContainer - LifetimeContainer - SessionKeyContainer - OwnerKeyContainer -} - -// SessionToken is an interface of token information and signature pair. -type SessionToken interface { - SessionTokenInfo - SignatureContainer -} - -// SignedDataSource is an interface of the container of a data for signing. -type SignedDataSource interface { - // Must return the required for signature byte slice. - // A non-nil error indicates that the data is not ready for signature. - SignedData() ([]byte, error) -} - -// SignedDataReader is an interface of signed data reader. -type SignedDataReader interface { - // Must return the minimum length of the slice for full reading. - // Must return a negative value if the length cannot be calculated. - SignedDataSize() int - - // Must behave like Read method of io.Reader and differ only in the reading of the signed data. - ReadSignedData([]byte) (int, error) -} - -// SignKeyPairAccumulator is an interface of a set of key-signature pairs with append access. -type SignKeyPairAccumulator interface { - AddSignKey([]byte, *ecdsa.PublicKey) -} - -// SignKeyPairSource is an interface of a set of key-signature pairs with read access. -type SignKeyPairSource interface { - GetSignKeyPairs() []SignKeyPair -} - -// SignKeyPair is an interface of key-signature pair with read access. -type SignKeyPair interface { - SignatureSource - GetPublicKey() *ecdsa.PublicKey -} - -// DataWithSignature is an interface of data-signature pair with read access. -type DataWithSignature interface { - SignedDataSource - SignatureSource -} - -// DataWithSignKeyAccumulator is an interface of data and key-signature accumulator pair. -type DataWithSignKeyAccumulator interface { - SignedDataSource - SignKeyPairAccumulator -} - -// DataWithSignKeySource is an interface of data and key-signature source pair. -type DataWithSignKeySource interface { - SignedDataSource - SignKeyPairSource -} - -// RequestData is an interface of the request information with read access. -type RequestData interface { - SignedDataSource - SessionTokenSource - BearerTokenSource - ExtendedHeadersSource -} - -// RequestSignedData is an interface of request information with signature write access. -type RequestSignedData interface { - RequestData - SignKeyPairAccumulator - SignKeyPairSource -} - -// RequestVerifyData is an interface of request information with signature read access. -type RequestVerifyData interface { - RequestData - SignKeyPairSource -} - -// ACLRulesSource is an interface of the container of binary extended ACL rules with read access. -type ACLRulesSource interface { - GetACLRules() []byte -} - -// ACLRulesContainer is an interface of the container of binary extended ACL rules. -type ACLRulesContainer interface { - ACLRulesSource - SetACLRules([]byte) -} - -// BearerTokenInfo is an interface of a fixed set of Bearer token information value containers. -// Contains: -// - binary extended ACL rules; -// - expiration epoch number; -// - ID of the token's owner. -type BearerTokenInfo interface { - ACLRulesContainer - ExpirationEpochContainer - OwnerIDContainer -} - -// BearerToken is an interface of Bearer token information and key-signature pair. -type BearerToken interface { - BearerTokenInfo - OwnerKeyContainer - SignatureContainer -} - -// BearerTokenSource is an interface of the container of a BearerToken with read access. -type BearerTokenSource interface { - GetBearerToken() BearerToken -} - -// ExtendedHeader is an interface of string key-value pair with read access. -type ExtendedHeader interface { - Key() string - Value() string -} - -// ExtendedHeadersSource is an interface of ExtendedHeader list with read access. -type ExtendedHeadersSource interface { - ExtendedHeaders() []ExtendedHeader -} diff --git a/service/utils.go b/service/utils.go deleted file mode 100644 index 17b23bb..0000000 --- a/service/utils.go +++ /dev/null @@ -1,18 +0,0 @@ -package service - -// SignedDataFromReader allocates buffer and reads bytes from passed reader to it. -// -// If passed SignedDataReader is nil, ErrNilSignedDataReader returns. -func SignedDataFromReader(r SignedDataReader) ([]byte, error) { - if r == nil { - return nil, ErrNilSignedDataReader - } - - data := make([]byte, r.SignedDataSize()) - - if _, err := r.ReadSignedData(data); err != nil { - return nil, err - } - - return data, nil -} diff --git a/service/utils_test.go b/service/utils_test.go deleted file mode 100644 index 60a2352..0000000 --- a/service/utils_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package service - -import ( - "testing" - - "github.com/pkg/errors" - "github.com/stretchr/testify/require" -) - -func TestSignedDataFromReader(t *testing.T) { - // nil SignedDataReader - _, err := SignedDataFromReader(nil) - require.EqualError(t, err, ErrNilSignedDataReader.Error()) - - rdr := &testSignedDataReader{ - testSignedDataSrc: new(testSignedDataSrc), - } - - // make reader to return an error - rdr.err = errors.New("test error") - - _, err = SignedDataFromReader(rdr) - require.EqualError(t, err, rdr.err.Error()) - - // remove the error - rdr.err = nil - - // fill the data - rdr.data = testData(t, 10) - - res, err := SignedDataFromReader(rdr) - require.NoError(t, err) - require.Equal(t, rdr.data, res) -} diff --git a/service/verify.go b/service/verify.go deleted file mode 100644 index 2aca7cb..0000000 --- a/service/verify.go +++ /dev/null @@ -1,174 +0,0 @@ -package service - -import ( - "crypto/ecdsa" - "io" - - "github.com/gogo/protobuf/proto" - "github.com/nspcc-dev/neofs-api-go/internal" - crypto "github.com/nspcc-dev/neofs-crypto" -) - -type signKeyPairsWrapper struct { - items []SignKeyPair -} - -// GetSessionToken returns SessionToken interface of Token field. -// -// If token field value is nil, nil returns. -func (m RequestVerificationHeader) GetSessionToken() SessionToken { - if t := m.GetToken(); t != nil { - return t - } - - return nil -} - -// AddSignKey adds new element to Signatures field. -// -// Sets Sign field to passed sign. Set Peer field to marshaled passed key. -func (m *RequestVerificationHeader) AddSignKey(sign []byte, key *ecdsa.PublicKey) { - m.SetSignatures( - append( - m.GetSignatures(), - &RequestVerificationHeader_Signature{ - Sign: sign, - Peer: crypto.MarshalPublicKey(key), - }, - ), - ) -} - -// GetSignKeyPairs returns the elements of Signatures field as SignKeyPair slice. -func (m RequestVerificationHeader) GetSignKeyPairs() []SignKeyPair { - var ( - signs = m.GetSignatures() - res = make([]SignKeyPair, len(signs)) - ) - - for i := range signs { - res[i] = signs[i] - } - - return res -} - -// GetSignature returns the result of a Sign field getter. -func (m RequestVerificationHeader_Signature) GetSignature() []byte { - return m.GetSign() -} - -// GetPublicKey unmarshals and returns the result of a Peer field getter. -func (m RequestVerificationHeader_Signature) GetPublicKey() *ecdsa.PublicKey { - return crypto.UnmarshalPublicKey(m.GetPeer()) -} - -// SetSignatures replaces signatures stored in RequestVerificationHeader. -func (m *RequestVerificationHeader) SetSignatures(signatures []*RequestVerificationHeader_Signature) { - m.Signatures = signatures -} - -// SetToken is a Token field setter. -func (m *RequestVerificationHeader) SetToken(token *Token) { - m.Token = token -} - -// SetBearer is a Bearer field setter. -func (m *RequestVerificationHeader) SetBearer(v *BearerTokenMsg) { - m.Bearer = v -} - -// testCustomField for test usage only. -type testCustomField [8]uint32 - -var _ internal.Custom = (*testCustomField)(nil) - -// Reset skip, it's for test usage only. -func (t testCustomField) Reset() {} - -// ProtoMessage skip, it's for test usage only. -func (t testCustomField) ProtoMessage() {} - -// Size skip, it's for test usage only. -func (t testCustomField) Size() int { return 32 } - -// String skip, it's for test usage only. -func (t testCustomField) String() string { return "" } - -// Bytes skip, it's for test usage only. -func (t testCustomField) Bytes() []byte { return nil } - -// Unmarshal skip, it's for test usage only. -func (t testCustomField) Unmarshal(data []byte) error { return nil } - -// Empty skip, it's for test usage only. -func (t testCustomField) Empty() bool { return false } - -// UnmarshalTo skip, it's for test usage only. -func (t testCustomField) MarshalTo(data []byte) (int, error) { return 0, nil } - -// Marshal skip, it's for test usage only. -func (t testCustomField) Marshal() ([]byte, error) { return nil, nil } - -// Merge used by proto.Clone -func (t *testCustomField) Merge(src proto.Message) { - if tmp, ok := src.(*testCustomField); ok { - *t = *tmp - } -} - -// GetBearerToken wraps Bearer field and return BearerToken interface. -// -// If Bearer field value is nil, nil returns. -func (m RequestVerificationHeader) GetBearerToken() BearerToken { - if t := m.GetBearer(); t != nil { - return t - } - - return nil -} - -// SignKeyPairsSignedData wraps passed SignKeyPair slice and returns SignedDataSource interface. -func SignKeyPairsSignedData(v ...SignKeyPair) SignedDataSource { - return &signKeyPairsWrapper{ - items: v, - } -} - -// SignedData returns signed SignKeyPair slice in a binary representation. -func (s signKeyPairsWrapper) SignedData() ([]byte, error) { - return SignedDataFromReader(s) -} - -// SignedDataSize returns the length of signed SignKeyPair slice. -func (s signKeyPairsWrapper) SignedDataSize() (sz int) { - for i := range s.items { - // add key length - sz += len( - crypto.MarshalPublicKey(s.items[i].GetPublicKey()), - ) - } - - return -} - -// ReadSignedData copies a binary representation of the signed SignKeyPair slice to passed buffer. -// -// If buffer length is less than required, io.ErrUnexpectedEOF returns. -func (s signKeyPairsWrapper) ReadSignedData(p []byte) (int, error) { - sz := s.SignedDataSize() - if len(p) < sz { - return 0, io.ErrUnexpectedEOF - } - - off := 0 - - for i := range s.items { - // copy public key bytes - off += copy(p[off:], crypto.MarshalPublicKey( - s.items[i].GetPublicKey(), - )) - } - - return off, nil -} diff --git a/service/verify.pb.go b/service/verify.pb.go deleted file mode 100644 index 02f3f39..0000000 Binary files a/service/verify.pb.go and /dev/null differ diff --git a/service/verify.proto b/service/verify.proto deleted file mode 100644 index a7e694f..0000000 --- a/service/verify.proto +++ /dev/null @@ -1,119 +0,0 @@ -syntax = "proto3"; -package service; -option go_package = "github.com/nspcc-dev/neofs-api-go/service"; -option csharp_namespace = "NeoFS.API.Service"; - -import "refs/types.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -// RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request -// (should be embedded into message). -message RequestVerificationHeader { - message Signature { - // Sign is signature of the request or session key. - bytes Sign = 1; - // Peer is compressed public key used for signature. - bytes Peer = 2; - } - - // Signatures is a set of signatures of every passed NeoFS Node - repeated Signature Signatures = 1; - - // Token is a token of the session within which the request is sent - Token Token = 2; - - // Bearer is a Bearer token of the request - BearerTokenMsg Bearer = 3; -} - -// User token granting rights for object manipulation -message Token { - message Info { - // ID is a token identifier. valid UUIDv4 represented in bytes - bytes ID = 1 [(gogoproto.customtype) = "TokenID", (gogoproto.nullable) = false]; - - // OwnerID is an owner of manipulation object - bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - - // Verb is an enumeration of session request types - enum Verb { - // Put refers to object.Put RPC call - Put = 0; - // Get refers to object.Get RPC call - Get = 1; - // Head refers to object.Head RPC call - Head = 2; - // Search refers to object.Search RPC call - Search = 3; - // Delete refers to object.Delete RPC call - Delete = 4; - // Range refers to object.GetRange RPC call - Range = 5; - // RangeHash refers to object.GetRangeHash RPC call - RangeHash = 6; - } - - // Verb is a type of request for which the token is issued - Verb verb = 3 [(gogoproto.customname) = "Verb"]; - - // Address is an object address for which token is issued - refs.Address Address = 4 [(gogoproto.nullable) = false, (gogoproto.customtype) = "Address"]; - - // Lifetime is a lifetime of the session - TokenLifetime Lifetime = 5 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - - // SessionKey is a public key of session key - bytes SessionKey = 6; - - // OwnerKey is a public key of the token owner - bytes OwnerKey = 7; - } - - // TokenInfo is a grouped information about token - Info TokenInfo = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - - // Signature is a signature of session token information - bytes Signature = 8; -} - -// TokenLifetime carries a group of lifetime parameters of the token -message TokenLifetime { - // Created carries an initial epoch of token lifetime - uint64 Created = 1; - - // ValidUntil carries a last epoch of token lifetime - uint64 ValidUntil = 2; -} - -// TODO: for variable token types and version redefine message -// Example: -// message Token { -// TokenType TokenType = 1; -// uint32 Version = 2; -// bytes Data = 3; -// } - -// BearerTokenMsg carries information about request ACL rules with limited lifetime -message BearerTokenMsg { - message Info { - // ACLRules carries a binary representation of the table of extended ACL rules - bytes ACLRules = 1; - - // OwnerID is an owner of token - bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - - // ValidUntil carries a last epoch of token lifetime - uint64 ValidUntil = 3; - } - - // TokenInfo is a grouped information about token - Info TokenInfo = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - - // OwnerKey is a public key of the token owner - bytes OwnerKey = 2; - - // Signature is a signature of token information - bytes Signature = 3; -} diff --git a/service/verify_test.go b/service/verify_test.go deleted file mode 100644 index 5ab8753..0000000 --- a/service/verify_test.go +++ /dev/null @@ -1,140 +0,0 @@ -package service - -import ( - "encoding/binary" - "io" - "math" - "testing" - - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/stretchr/testify/require" -) - -func (m TestRequest) SignedData() ([]byte, error) { - return SignedDataFromReader(m) -} - -func (m TestRequest) SignedDataSize() (sz int) { - sz += 4 - - sz += len(m.StringField) - - sz += len(m.BytesField) - - sz += m.CustomField.Size() - - return -} - -func (m TestRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - binary.BigEndian.PutUint32(p[off:], uint32(m.IntField)) - off += 4 - - off += copy(p[off:], []byte(m.StringField)) - - off += copy(p[off:], m.BytesField) - - n, err := m.CustomField.MarshalTo(p[off:]) - off += n - - return off, err -} - -func BenchmarkSignDataWithSessionToken(b *testing.B) { - key := test.DecodeKey(0) - - customField := testCustomField{1, 2, 3, 4, 5, 6, 7, 8} - - token := new(Token) - - req := &TestRequest{ - IntField: math.MaxInt32, - StringField: "TestRequestStringField", - BytesField: make([]byte, 1<<22), - CustomField: &customField, - } - - req.SetTTL(math.MaxInt32 - 8) - req.SetEpoch(math.MaxInt64 - 12) - req.SetToken(token) - - b.ResetTimer() - b.ReportAllocs() - - for i := 0; i < b.N; i++ { - require.NoError(b, SignRequestData(key, req)) - } -} - -func BenchmarkVerifyAccumulatedSignaturesWithToken(b *testing.B) { - customField := testCustomField{1, 2, 3, 4, 5, 6, 7, 8} - - token := new(Token) - - req := &TestRequest{ - IntField: math.MaxInt32, - StringField: "TestRequestStringField", - BytesField: make([]byte, 1<<22), - CustomField: &customField, - } - - req.SetTTL(math.MaxInt32 - 8) - req.SetEpoch(math.MaxInt64 - 12) - req.SetToken(token) - - for i := 0; i < 10; i++ { - key := test.DecodeKey(i) - require.NoError(b, SignRequestData(key, req)) - } - - b.ResetTimer() - b.ReportAllocs() - - for i := 0; i < b.N; i++ { - require.NoError(b, VerifyRequestData(req)) - } -} - -func TestRequestVerificationHeader_SetToken(t *testing.T) { - id, err := refs.NewUUID() - require.NoError(t, err) - - token := new(Token) - token.SetID(id) - - h := new(RequestVerificationHeader) - - h.SetToken(token) - - require.Equal(t, token, h.GetToken()) -} - -func TestRequestVerificationHeader_SetBearer(t *testing.T) { - aclRules := []byte{1, 2, 3} - - token := new(BearerTokenMsg) - token.SetACLRules(aclRules) - - h := new(RequestVerificationHeader) - - h.SetBearer(token) - - require.Equal(t, token, h.GetBearer()) -} - -func TestRequestVerificationHeader_GetBearerToken(t *testing.T) { - s := new(RequestVerificationHeader) - - require.Nil(t, s.GetBearerToken()) - - bearer := new(BearerTokenMsg) - s.SetBearer(bearer) - require.Equal(t, bearer, s.GetBearerToken()) -} diff --git a/service/verify_test.pb.go b/service/verify_test.pb.go deleted file mode 100644 index 6bc8748..0000000 Binary files a/service/verify_test.pb.go and /dev/null differ diff --git a/service/verify_test.proto b/service/verify_test.proto deleted file mode 100644 index 901d6fd..0000000 --- a/service/verify_test.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; -package service; -option go_package = "github.com/nspcc-dev/neofs-api-go/service"; - -import "service/meta.proto"; -import "service/verify.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -message TestRequest { - int32 IntField = 1; - string StringField = 2; - bytes BytesField = 3; - bytes CustomField = 4 [(gogoproto.customtype) = "testCustomField"]; - RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - RequestVerificationHeader Header = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} diff --git a/service/version.go b/service/version.go deleted file mode 100644 index 6f4839c..0000000 --- a/service/version.go +++ /dev/null @@ -1,11 +0,0 @@ -package service - -// SetVersion is a Version field setter. -func (m *ResponseMetaHeader) SetVersion(v uint32) { - m.Version = v -} - -// SetVersion is a Version field setter. -func (m *RequestMetaHeader) SetVersion(v uint32) { - m.Version = v -} diff --git a/service/version_test.go b/service/version_test.go deleted file mode 100644 index d102d30..0000000 --- a/service/version_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package service - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestGetSetVersion(t *testing.T) { - v := uint32(7) - - items := []VersionContainer{ - new(ResponseMetaHeader), - new(RequestMetaHeader), - } - - for _, item := range items { - item.SetVersion(v) - require.Equal(t, v, item.GetVersion()) - } -} diff --git a/session/alias.go b/session/alias.go deleted file mode 100644 index aa49d55..0000000 --- a/session/alias.go +++ /dev/null @@ -1,15 +0,0 @@ -package session - -import ( - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/service" -) - -// OwnerID is a type alias of OwnerID ref. -type OwnerID = refs.OwnerID - -// TokenID is a type alias of TokenID ref. -type TokenID = service.TokenID - -// Token is a type alias of Token. -type Token = service.Token diff --git a/session/create.go b/session/create.go deleted file mode 100644 index 412d1fd..0000000 --- a/session/create.go +++ /dev/null @@ -1,62 +0,0 @@ -package session - -import ( - "context" - "crypto/ecdsa" - - "github.com/nspcc-dev/neofs-api-go/service" - crypto "github.com/nspcc-dev/neofs-crypto" - "google.golang.org/grpc" -) - -type gRPCCreator struct { - conn *grpc.ClientConn - - key *ecdsa.PrivateKey - - clientFunc func(*grpc.ClientConn) SessionClient -} - -// NewGRPCCreator unites virtual gRPC client with private ket and returns Creator interface. -// -// If passed ClientConn is nil, ErrNilGPRCClientConn returns. -// If passed private key is nil, crypto.ErrEmptyPrivateKey returns. -func NewGRPCCreator(conn *grpc.ClientConn, key *ecdsa.PrivateKey) (Creator, error) { - if conn == nil { - return nil, ErrNilGPRCClientConn - } else if key == nil { - return nil, crypto.ErrEmptyPrivateKey - } - - return &gRPCCreator{ - conn: conn, - - key: key, - - clientFunc: NewSessionClient, - }, nil -} - -// Create constructs message, signs it with private key and sends it to a gRPC client. -// -// If passed CreateParamsSource is nil, ErrNilCreateParamsSource returns. -// If message could not be signed, an error returns. -func (s gRPCCreator) Create(ctx context.Context, p CreateParamsSource) (CreateResult, error) { - if p == nil { - return nil, ErrNilCreateParamsSource - } - - // create and fill a message - req := new(CreateRequest) - req.SetOwnerID(p.GetOwnerID()) - req.SetCreationEpoch(p.CreationEpoch()) - req.SetExpirationEpoch(p.ExpirationEpoch()) - - // sign with private key - if err := service.SignRequestData(s.key, req); err != nil { - return nil, err - } - - // make gRPC call - return s.clientFunc(s.conn).Create(ctx, req) -} diff --git a/session/create_test.go b/session/create_test.go deleted file mode 100644 index 943c5da..0000000 --- a/session/create_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package session - -import ( - "context" - "crypto/ecdsa" - "testing" - - "github.com/nspcc-dev/neofs-api-go/service" - crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/pkg/errors" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" -) - -type testSessionClient struct { - fn func(*CreateRequest) - resp *CreateResponse - err error -} - -func (s testSessionClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) { - if s.fn != nil { - s.fn(in) - } - - return s.resp, s.err -} - -func TestNewGRPCCreator(t *testing.T) { - var ( - err error - conn = new(grpc.ClientConn) - sk = new(ecdsa.PrivateKey) - ) - - // nil client connection - _, err = NewGRPCCreator(nil, sk) - require.EqualError(t, err, ErrNilGPRCClientConn.Error()) - - // nil private key - _, err = NewGRPCCreator(conn, nil) - require.EqualError(t, err, crypto.ErrEmptyPrivateKey.Error()) - - // valid params - res, err := NewGRPCCreator(conn, sk) - require.NoError(t, err) - - v := res.(*gRPCCreator) - require.Equal(t, conn, v.conn) - require.Equal(t, sk, v.key) - require.NotNil(t, v.clientFunc) -} - -func TestGRPCCreator_Create(t *testing.T) { - ctx := context.TODO() - s := new(gRPCCreator) - - // nil CreateParamsSource - _, err := s.Create(ctx, nil) - require.EqualError(t, err, ErrNilCreateParamsSource.Error()) - - var ( - ownerID = OwnerID{1, 2, 3} - created = uint64(2) - expired = uint64(4) - ) - - p := NewParams() - p.SetOwnerID(ownerID) - p.SetCreationEpoch(created) - p.SetExpirationEpoch(expired) - - // nil private key - _, err = s.Create(ctx, p) - require.Error(t, err) - - // create test private key - s.key = test.DecodeKey(0) - - // create test client - c := &testSessionClient{ - fn: func(req *CreateRequest) { - require.Equal(t, ownerID, req.GetOwnerID()) - require.Equal(t, created, req.CreationEpoch()) - require.Equal(t, expired, req.ExpirationEpoch()) - require.NoError(t, service.VerifyRequestData(req)) - }, - resp: &CreateResponse{ - ID: TokenID{1, 2, 3}, - SessionKey: []byte{1, 2, 3}, - }, - err: errors.New("test error"), - } - - s.clientFunc = func(*grpc.ClientConn) SessionClient { - return c - } - - res, err := s.Create(ctx, p) - require.EqualError(t, err, c.err.Error()) - require.Equal(t, c.resp, res) -} diff --git a/session/errors.go b/session/errors.go deleted file mode 100644 index d35bed4..0000000 --- a/session/errors.go +++ /dev/null @@ -1,19 +0,0 @@ -package session - -import "github.com/nspcc-dev/neofs-api-go/internal" - -// ErrNilCreateParamsSource is returned by functions that expect a non-nil -// CreateParamsSource, but received nil. -const ErrNilCreateParamsSource = internal.Error("create params source is nil") - -// ErrNilGPRCClientConn is returned by functions that expect a non-nil -// grpc.ClientConn, but received nil. -const ErrNilGPRCClientConn = internal.Error("gRPC client connection is nil") - -// ErrPrivateTokenNotFound is returned when addressed private token was -// not found in storage. -const ErrPrivateTokenNotFound = internal.Error("private token not found") - -// ErrNilPrivateToken is returned by functions that expect a non-nil -// PrivateToken, but received nil. -const ErrNilPrivateToken = internal.Error("private token is nil") diff --git a/session/private.go b/session/private.go deleted file mode 100644 index bb9242f..0000000 --- a/session/private.go +++ /dev/null @@ -1,67 +0,0 @@ -package session - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - - crypto "github.com/nspcc-dev/neofs-crypto" -) - -type pToken struct { - // private session token - sessionKey *ecdsa.PrivateKey - // last epoch of the lifetime - validUntil uint64 -} - -// NewPrivateToken creates PrivateToken instance that expires after passed epoch. -// -// Returns non-nil error on key generation error. -func NewPrivateToken(validUntil uint64) (PrivateToken, error) { - sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return nil, err - } - - return &pToken{ - sessionKey: sk, - validUntil: validUntil, - }, nil -} - -// PublicSessionToken returns a binary representation of session public key. -// -// If passed PrivateToken is nil, ErrNilPrivateToken returns. -// If passed PrivateToken carries nil private key, crypto.ErrEmptyPrivateKey returns. -func PublicSessionToken(pToken PrivateToken) ([]byte, error) { - if pToken == nil { - return nil, ErrNilPrivateToken - } - - sk := pToken.PrivateKey() - if sk == nil { - return nil, crypto.ErrEmptyPrivateKey - } - - return crypto.MarshalPublicKey(&sk.PublicKey), nil -} - -// PrivateKey is a session private key getter. -func (t *pToken) PrivateKey() *ecdsa.PrivateKey { - return t.sessionKey -} - -func (t *pToken) Expired(epoch uint64) bool { - return t.validUntil < epoch -} - -// SetOwnerID is an owner ID field setter. -func (s *PrivateTokenKey) SetOwnerID(id OwnerID) { - s.owner = id -} - -// SetTokenID is a token ID field setter. -func (s *PrivateTokenKey) SetTokenID(id TokenID) { - s.token = id -} diff --git a/session/private_test.go b/session/private_test.go deleted file mode 100644 index c6f8125..0000000 --- a/session/private_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package session - -import ( - "testing" - - crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/stretchr/testify/require" -) - -func TestPToken_PrivateKey(t *testing.T) { - // create new private token - pToken, err := NewPrivateToken(0) - require.NoError(t, err) - require.NotNil(t, pToken.PrivateKey()) -} - -func TestPToken_Expired(t *testing.T) { - e := uint64(10) - - var token PrivateToken = &pToken{ - validUntil: e, - } - - // must not be expired in the epoch before last - require.False(t, token.Expired(e-1)) - - // must not be expired in the last epoch - require.False(t, token.Expired(e)) - - // must be expired in the epoch after last - require.True(t, token.Expired(e+1)) -} - -func TestPrivateTokenKey_SetOwnerID(t *testing.T) { - ownerID := OwnerID{1, 2, 3} - - s := new(PrivateTokenKey) - - s.SetOwnerID(ownerID) - - require.Equal(t, ownerID, s.owner) -} - -func TestPrivateTokenKey_SetTokenID(t *testing.T) { - tokenID := TokenID{1, 2, 3} - - s := new(PrivateTokenKey) - - s.SetTokenID(tokenID) - - require.Equal(t, tokenID, s.token) -} - -func TestPublicSessionToken(t *testing.T) { - var err error - - // nil PrivateToken - _, err = PublicSessionToken(nil) - require.EqualError(t, err, ErrNilPrivateToken.Error()) - - // empty private key - var pToken PrivateToken = new(pToken) - _, err = PublicSessionToken(pToken) - require.EqualError(t, err, crypto.ErrEmptyPrivateKey.Error()) - - // correct PrivateToken - pToken, err = NewPrivateToken(0) - require.NoError(t, err) - - key := pToken.PrivateKey() - require.NotNil(t, key) - - res, err := PublicSessionToken(pToken) - require.NoError(t, err) - require.Equal(t, res, crypto.MarshalPublicKey(&key.PublicKey)) -} diff --git a/session/request.go b/session/request.go deleted file mode 100644 index 73c05e5..0000000 --- a/session/request.go +++ /dev/null @@ -1,62 +0,0 @@ -package session - -import ( - "encoding/binary" - "io" - - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/service" -) - -const signedRequestDataSize = 0 + - refs.OwnerIDSize + - 8 + - 8 - -var requestEndianness = binary.BigEndian - -// NewParams creates a new CreateRequest message and returns CreateParamsContainer interface. -func NewParams() CreateParamsContainer { - return new(CreateRequest) -} - -// GetOwnerID is an OwnerID field getter. -func (m CreateRequest) GetOwnerID() OwnerID { - return m.OwnerID -} - -// SetOwnerID is an OwnerID field setter. -func (m *CreateRequest) SetOwnerID(id OwnerID) { - m.OwnerID = id -} - -// SignedData returns payload bytes of the request. -func (m CreateRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m CreateRequest) SignedDataSize() int { - return signedRequestDataSize -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m CreateRequest) ReadSignedData(p []byte) (int, error) { - sz := m.SignedDataSize() - if len(p) < sz { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.GetOwnerID().Bytes()) - - requestEndianness.PutUint64(p[off:], m.CreationEpoch()) - off += 8 - - requestEndianness.PutUint64(p[off:], m.ExpirationEpoch()) - - return sz, nil -} diff --git a/session/request_test.go b/session/request_test.go deleted file mode 100644 index 094ca66..0000000 --- a/session/request_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package session - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestCreateRequestGettersSetters(t *testing.T) { - t.Run("owner ID", func(t *testing.T) { - id := OwnerID{1, 2, 3} - m := new(CreateRequest) - - m.SetOwnerID(id) - - require.Equal(t, id, m.GetOwnerID()) - }) - - t.Run("lifetime", func(t *testing.T) { - e1, e2 := uint64(3), uint64(4) - m := new(CreateRequest) - - m.SetCreationEpoch(e1) - m.SetExpirationEpoch(e2) - - require.Equal(t, e1, m.CreationEpoch()) - require.Equal(t, e2, m.ExpirationEpoch()) - }) -} - -func TestCreateRequest_SignedData(t *testing.T) { - var ( - id = OwnerID{1, 2, 3} - e1 = uint64(1) - e2 = uint64(2) - ) - - // create new message - m := new(CreateRequest) - - // fill the fields - m.SetOwnerID(id) - m.SetCreationEpoch(e1) - m.SetExpirationEpoch(e2) - - // calculate initial signed data - d, err := m.SignedData() - require.NoError(t, err) - - items := []struct { - change func() - reset func() - }{ - { // OwnerID - change: func() { - id2 := id - id2[0]++ - m.SetOwnerID(id2) - }, - reset: func() { - m.SetOwnerID(id) - }, - }, - { // CreationEpoch - change: func() { - m.SetCreationEpoch(e1 + 1) - }, - reset: func() { - m.SetCreationEpoch(e1) - }, - }, - { // ExpirationEpoch - change: func() { - m.SetExpirationEpoch(e2 + 1) - }, - reset: func() { - m.SetExpirationEpoch(e2) - }, - }, - } - - for _, item := range items { - item.change() - - d2, err := m.SignedData() - require.NoError(t, err) - - require.NotEqual(t, d, d2) - - item.reset() - } -} diff --git a/session/response.go b/session/response.go deleted file mode 100644 index 3426d7c..0000000 --- a/session/response.go +++ /dev/null @@ -1,16 +0,0 @@ -package session - -// GetID is an ID field getter. -func (m CreateResponse) GetID() TokenID { - return m.ID -} - -// SetID is an ID field setter. -func (m *CreateResponse) SetID(id TokenID) { - m.ID = id -} - -// SetSessionKey is a SessionKey field setter. -func (m *CreateResponse) SetSessionKey(key []byte) { - m.SessionKey = key -} diff --git a/session/response_test.go b/session/response_test.go deleted file mode 100644 index 0e1de0b..0000000 --- a/session/response_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package session - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestCreateResponseGettersSetters(t *testing.T) { - t.Run("id", func(t *testing.T) { - id := TokenID{1, 2, 3} - m := new(CreateResponse) - - m.SetID(id) - - require.Equal(t, id, m.GetID()) - }) - - t.Run("session key", func(t *testing.T) { - key := []byte{1, 2, 3} - m := new(CreateResponse) - - m.SetSessionKey(key) - - require.Equal(t, key, m.GetSessionKey()) - }) -} diff --git a/session/service.pb.go b/session/service.pb.go deleted file mode 100644 index e68c0fd..0000000 Binary files a/session/service.pb.go and /dev/null differ diff --git a/session/service.proto b/session/service.proto deleted file mode 100644 index b7eb0df..0000000 --- a/session/service.proto +++ /dev/null @@ -1,39 +0,0 @@ -syntax = "proto3"; -package session; -option go_package = "github.com/nspcc-dev/neofs-api-go/session"; -option csharp_namespace = "NeoFS.API.Session"; - -import "service/meta.proto"; -import "service/verify.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - - -service Session { - // Create opens new session between the client and the server - rpc Create (CreateRequest) returns (CreateResponse); -} - -// CreateRequest carries an information necessary for opening a session -message CreateRequest { - // OwnerID carries an identifier of a session initiator - bytes OwnerID = 1 [(gogoproto.nullable) = false, (gogoproto.customtype) = "OwnerID"]; - - // Lifetime carries a lifetime of the session - service.TokenLifetime Lifetime = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -// CreateResponse carries an information about the opened session -message CreateResponse { - // ID carries an identifier of session token - bytes ID = 1 [(gogoproto.customtype) = "TokenID", (gogoproto.nullable) = false]; - - // SessionKey carries a session public key - bytes SessionKey = 2; -} diff --git a/session/store.go b/session/store.go deleted file mode 100644 index fa33b7e..0000000 --- a/session/store.go +++ /dev/null @@ -1,64 +0,0 @@ -package session - -import ( - "sync" -) - -type mapTokenStore struct { - *sync.RWMutex - - tokens map[PrivateTokenKey]PrivateToken -} - -// NewMapTokenStore creates new PrivateTokenStore instance. -// -// The elements of the instance are stored in the map. -func NewMapTokenStore() PrivateTokenStore { - return &mapTokenStore{ - RWMutex: new(sync.RWMutex), - tokens: make(map[PrivateTokenKey]PrivateToken), - } -} - -// Store adds passed token to the map. -// -// Resulting error is always nil. -func (s *mapTokenStore) Store(key PrivateTokenKey, token PrivateToken) error { - s.Lock() - s.tokens[key] = token - s.Unlock() - - return nil -} - -// Fetch returns the map element corresponding to the given key. -// -// Returns ErrPrivateTokenNotFound is there is no element in map. -func (s *mapTokenStore) Fetch(key PrivateTokenKey) (PrivateToken, error) { - s.RLock() - defer s.RUnlock() - - t, ok := s.tokens[key] - if !ok { - return nil, ErrPrivateTokenNotFound - } - - return t, nil -} - -// RemoveExpired removes all the map elements that are expired in the passed epoch. -// -// Resulting error is always nil. -func (s *mapTokenStore) RemoveExpired(epoch uint64) error { - s.Lock() - - for key, token := range s.tokens { - if token.Expired(epoch) { - delete(s.tokens, key) - } - } - - s.Unlock() - - return nil -} diff --git a/session/store_test.go b/session/store_test.go deleted file mode 100644 index 74e0023..0000000 --- a/session/store_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package session - -import ( - "testing" - - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/stretchr/testify/require" -) - -func TestMapTokenStore(t *testing.T) { - // create new private token - pToken, err := NewPrivateToken(0) - require.NoError(t, err) - - // create map token store - s := NewMapTokenStore() - - // create test TokenID - tid, err := refs.NewUUID() - require.NoError(t, err) - - // create test OwnerID - ownerID := OwnerID{1, 2, 3} - - key := PrivateTokenKey{} - key.SetOwnerID(ownerID) - key.SetTokenID(tid) - - // ascertain that there is no record for the key - _, err = s.Fetch(key) - require.EqualError(t, err, ErrPrivateTokenNotFound.Error()) - - // save private token record - require.NoError(t, s.Store(key, pToken)) - - // fetch private token by the key - res, err := s.Fetch(key) - require.NoError(t, err) - - // ascertain that returned token equals to initial - require.Equal(t, pToken, res) -} - -func TestMapTokenStore_RemoveExpired(t *testing.T) { - // create some epoch number - e1 := uint64(1) - - // create private token that expires after e1 - tok1, err := NewPrivateToken(e1) - require.NoError(t, err) - - // create some greater than e1 epoch number - e2 := e1 + 1 - - // create private token that expires after e2 - tok2, err := NewPrivateToken(e2) - require.NoError(t, err) - - // create token store instance - s := NewMapTokenStore() - - // create test PrivateTokenKey - key := PrivateTokenKey{} - key.SetOwnerID(OwnerID{1, 2, 3}) - - // create IDs for tokens - id1, err := refs.NewUUID() - require.NoError(t, err) - id2, err := refs.NewUUID() - require.NoError(t, err) - - assertPresence := func(ids ...TokenID) { - for i := range ids { - key.SetTokenID(ids[i]) - _, err = s.Fetch(key) - require.NoError(t, err) - } - } - - assertAbsence := func(ids ...TokenID) { - for i := range ids { - key.SetTokenID(ids[i]) - _, err = s.Fetch(key) - require.EqualError(t, err, ErrPrivateTokenNotFound.Error()) - } - } - - // store both tokens - key.SetTokenID(id1) - require.NoError(t, s.Store(key, tok1)) - key.SetTokenID(id2) - require.NoError(t, s.Store(key, tok2)) - - // ascertain that both tokens are available - assertPresence(id1, id2) - - // perform cleaning for epoch in which both tokens are not expired - require.NoError(t, s.RemoveExpired(e1)) - - // ascertain that both tokens are still available - assertPresence(id1, id2) - - // perform cleaning for epoch greater than e1 and not greater than e2 - require.NoError(t, s.RemoveExpired(e1+1)) - - // ascertain that tok1 was removed - assertAbsence(id1) - - // ascertain that tok2 was not removed - assertPresence(id2) -} diff --git a/session/types.go b/session/types.go deleted file mode 100644 index 95a0065..0000000 --- a/session/types.go +++ /dev/null @@ -1,80 +0,0 @@ -package session - -import ( - "context" - "crypto/ecdsa" - - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/service" -) - -// PrivateToken is an interface of session private part. -type PrivateToken interface { - // PrivateKey must return session private key. - PrivateKey() *ecdsa.PrivateKey - - // Expired must return true if and only if private token is expired in the given epoch number. - Expired(uint64) bool -} - -// PrivateTokenKey is a structure of private token storage key. -type PrivateTokenKey struct { - owner OwnerID - token TokenID -} - -// PrivateTokenSource is an interface of private token storage with read access. -type PrivateTokenSource interface { - // Fetch must return the storage record corresponding to the passed key. - // - // Resulting error must be ErrPrivateTokenNotFound if there is no corresponding record. - Fetch(PrivateTokenKey) (PrivateToken, error) -} - -// EpochLifetimeStore is an interface of the storage of elements that lifetime is limited by NeoFS epoch. -type EpochLifetimeStore interface { - // RemoveExpired must remove all elements that are expired in the given epoch. - RemoveExpired(uint64) error -} - -// PrivateTokenStore is an interface of the storage of private tokens addressable by TokenID. -type PrivateTokenStore interface { - PrivateTokenSource - EpochLifetimeStore - - // Store must save passed private token in the storage under the given key. - // - // Resulting error must be nil if private token was stored successfully. - Store(PrivateTokenKey, PrivateToken) error -} - -// KeyStore is an interface of the storage of public keys addressable by OwnerID, -type KeyStore interface { - // Get must return the storage record corresponding to the passed key. - // - // Resulting error must be ErrKeyNotFound if there is no corresponding record. - Get(context.Context, OwnerID) ([]*ecdsa.PublicKey, error) -} - -// CreateParamsSource is an interface of the container of session parameters with read access. -type CreateParamsSource interface { - refs.OwnerIDSource - service.LifetimeSource -} - -// CreateParamsContainer is an interface of the container of session parameters. -type CreateParamsContainer interface { - refs.OwnerIDContainer - service.LifetimeContainer -} - -// CreateResult is an interface of the container of an opened session info with read access. -type CreateResult interface { - service.TokenIDSource - service.SessionKeySource -} - -// Creator is an interface of the tool for a session opening. -type Creator interface { - Create(context.Context, CreateParamsSource) (CreateResult, error) -} diff --git a/state/service.go b/state/service.go deleted file mode 100644 index 577046c..0000000 --- a/state/service.go +++ /dev/null @@ -1,88 +0,0 @@ -package state - -import ( - "bytes" - "encoding/json" - "expvar" - "fmt" - - "github.com/golang/protobuf/proto" - "github.com/prometheus/client_golang/prometheus" - dto "github.com/prometheus/client_model/go" - "github.com/spf13/viper" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// MetricFamily is type alias for proto.Message generated -// from github.com/prometheus/client_model/metrics.proto. -type MetricFamily = dto.MetricFamily - -// EncodeMetrics encodes metrics from gatherer into MetricsResponse message, -// if something went wrong returns gRPC Status error (can be returned from service). -func EncodeMetrics(g prometheus.Gatherer) (*MetricsResponse, error) { - metrics, err := g.Gather() - if err != nil { - return nil, status.New(codes.Internal, err.Error()).Err() - } - - results := make([][]byte, 0, len(metrics)) - for _, mf := range metrics { - item, err := proto.Marshal(mf) - if err != nil { - return nil, status.New(codes.Internal, err.Error()).Err() - } - - results = append(results, item) - } - - return &MetricsResponse{Metrics: results}, nil -} - -// DecodeMetrics decodes metrics from MetricsResponse to []MetricFamily, -// if something went wrong returns error. -func DecodeMetrics(r *MetricsResponse) ([]*MetricFamily, error) { - metrics := make([]*dto.MetricFamily, 0, len(r.Metrics)) - for i := range r.Metrics { - mf := new(MetricFamily) - if err := proto.Unmarshal(r.Metrics[i], mf); err != nil { - return nil, err - } - metrics = append(metrics, mf) - } - - return metrics, nil -} - -// EncodeConfig encodes viper settings into DumpConfig message, -// if something went wrong returns gRPC Status error (can be returned from service). -func EncodeConfig(v *viper.Viper) (*DumpResponse, error) { - data, err := json.Marshal(v.AllSettings()) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - - return &DumpResponse{Config: data}, nil -} - -// EncodeVariables encodes debug variables into DumpVarsResponse message. -// Variables encoded into JSON and stored as slice of bytes. -func EncodeVariables() *DumpVarsResponse { - buf := new(bytes.Buffer) - buf.WriteString("{\n") - first := true - - expvar.Do(func(kv expvar.KeyValue) { - if !first { - buf.WriteString(",\n") - } - - first = false - - _, _ = fmt.Fprintf(buf, "%q: %s", kv.Key, kv.Value) - }) - - buf.WriteString("\n}\n") - - return &DumpVarsResponse{Variables: buf.Bytes()} -} diff --git a/state/service.pb.go b/state/service.pb.go deleted file mode 100644 index 187949b..0000000 Binary files a/state/service.pb.go and /dev/null differ diff --git a/state/service.proto b/state/service.proto deleted file mode 100644 index 410a000..0000000 --- a/state/service.proto +++ /dev/null @@ -1,124 +0,0 @@ -syntax = "proto3"; -package state; -option go_package = "github.com/nspcc-dev/neofs-api-go/state"; -option csharp_namespace = "NeoFS.API.State"; - -import "service/meta.proto"; -import "service/verify.proto"; -import "bootstrap/types.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -// Status service provides node's healthcheck and status info. -// TODO: decide how to describe auth and were contains permissions. -service Status { - // Netmap request allows to receive current [bootstrap.SpreadMap](bootstrap.md#bootstrap.SpreadMap) - rpc Netmap(NetmapRequest) returns (bootstrap.SpreadMap); - // Metrics request allows to receive metrics in prometheus format - rpc Metrics(MetricsRequest) returns (MetricsResponse); - // HealthCheck request allows to check health status of the node. - // If node unhealthy field Status would contains detailed info. - rpc HealthCheck(HealthRequest) returns (HealthResponse); - // DumpConfig request allows dumping settings for the current node. - // To permit access, used server config options. - // The request should be signed. - rpc DumpConfig(DumpRequest) returns (DumpResponse); - // DumpVars returns debug variables for the current node. - // To permit access, used server config options. - // The request should be signed. - rpc DumpVars(DumpVarsRequest) returns (DumpVarsResponse); - // ChangeState allows to change current node state of node. - // To permit access, used server config options. - // The request should be signed. - rpc ChangeState(ChangeStateRequest) returns (ChangeStateResponse); -} - -// NetmapRequest message to request current node netmap -message NetmapRequest { - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -// MetricsRequest message to request node metrics -message MetricsRequest { - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -// MetricsResponse contains [][]byte, -// every []byte is marshaled MetricFamily proto message -// from github.com/prometheus/client_model/metrics.proto -message MetricsResponse { - repeated bytes Metrics = 1; -} - -// HealthRequest message to check current state -message HealthRequest { - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -// HealthResponse message with current state -message HealthResponse { - // Healthy is true when node alive and healthy - bool Healthy = 1; - // Status contains detailed information about health status - string Status = 2; -} - -// DumpRequest message to fetch current server config. -message DumpRequest { - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -// DumpResponse message contains current server config. -// Config stored in JSON encoded into slice of bytes. -message DumpResponse { - bytes Config = 1; -} - -// DumpVarsRequest message to fetch current server debug variables. -message DumpVarsRequest { - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -// DumpVarsResponse message contains current server debug variables. -// Variables stored in JSON encoded into slice of bytes. -message DumpVarsResponse { - bytes Variables = 1; -} - -// ChangeStateRequest contains a new state of node. -message ChangeStateRequest { - enum State { - // Unknown is default value. Does nothing. - Unknown = 0; - // Online used when need to set node to the online state. - Online = 1; - // Offline used when need to set node to the offline state. - Offline = 2; - } - - // State is a new state of node. - State state = 1; - // RequestMetaHeader contains information about request meta headers (should be embedded into message) - service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) - service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; -} - -// ChangeStateResponse is an empty response, that returns when RPC invoked without errors. -message ChangeStateResponse {} \ No newline at end of file diff --git a/state/service_test.go b/state/service_test.go deleted file mode 100644 index d545cb0..0000000 --- a/state/service_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package state - -import ( - "bytes" - "encoding/json" - "expvar" - "testing" - - "github.com/prometheus/client_golang/prometheus" - "github.com/spf13/viper" - "github.com/stretchr/testify/require" -) - -type testCollector struct { - testA *prometheus.Desc - testB *prometheus.Desc -} - -func (c *testCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- c.testA - ch <- c.testB -} - -func (c *testCollector) Collect(ch chan<- prometheus.Metric) { - ch <- prometheus.MustNewConstMetric(c.testA, prometheus.GaugeValue, 1, "label_1") - ch <- prometheus.MustNewConstMetric(c.testB, prometheus.GaugeValue, 2, "label_2") -} - -func TestEncodeVariables(t *testing.T) { - dump := make(map[string]interface{}) - - expvar.NewString("test1").Set("test1") - expvar.NewString("test2").Set("test2") - - res := EncodeVariables() - - require.NoError(t, json.Unmarshal(res.Variables, &dump)) - require.NotEmpty(t, dump) - - // dump should contains keys `test1` and `test2` - require.Contains(t, dump, "test1") - require.Equal(t, "test1", dump["test1"]) - - require.Contains(t, dump, "test2") - require.Equal(t, "test2", dump["test2"]) -} - -func TestEncodeConfig(t *testing.T) { - v := viper.New() - v.Set("test1", "test1") - v.Set("test2", "test2") - - res, err := EncodeConfig(v) - require.NoError(t, err) - - dump := make(map[string]interface{}) - require.NoError(t, json.Unmarshal(res.Config, &dump)) - - require.NotEmpty(t, dump) - - require.Contains(t, dump, "test1") - require.Equal(t, dump["test1"], "test1") - - require.Contains(t, dump, "test2") - require.Equal(t, dump["test2"], "test2") -} - -func TestEncodeAndDecodeMetrics(t *testing.T) { - registry := prometheus.NewRegistry() - - collector := &testCollector{ - testA: prometheus.NewDesc("test1", "test1", []string{"test1"}, prometheus.Labels{"label_1": "test1"}), - testB: prometheus.NewDesc("test2", "test2", []string{"test2"}, prometheus.Labels{"label_2": "test2"}), - } - - require.NoError(t, registry.Register(collector)) - - gather, err := registry.Gather() - require.NoError(t, err) - - res, err := EncodeMetrics(registry) - require.NoError(t, err) - - metrics, err := DecodeMetrics(res) - require.NoError(t, err) - - require.Len(t, metrics, len(gather)) - - { // Check that JSON encoded metrics are equal: - expect := new(bytes.Buffer) - actual := new(bytes.Buffer) - - require.NoError(t, json.NewEncoder(expect).Encode(gather)) - require.NoError(t, json.NewEncoder(actual).Encode(metrics)) - - require.Equal(t, expect.Bytes(), actual.Bytes()) - } - - { // Deep comparison of metrics: - for i := range metrics { - require.Equal(t, gather[i].Help, metrics[i].Help) - require.Equal(t, gather[i].Name, metrics[i].Name) - require.Equal(t, gather[i].Type, metrics[i].Type) - - require.Len(t, metrics[i].Metric, len(gather[i].Metric)) - - for j := range metrics[i].Metric { - require.Equal(t, gather[i].Metric[j].Gauge, metrics[i].Metric[j].Gauge) - require.Len(t, metrics[i].Metric[j].Label, len(gather[i].Metric[j].Label)) - - for k := range metrics[i].Metric[j].Label { - require.Equal(t, gather[i].Metric[j].Label[k].Name, metrics[i].Metric[j].Label[k].Name) - require.Equal(t, gather[i].Metric[j].Label[k].Value, metrics[i].Metric[j].Label[k].Value) - } - } - } - } -} diff --git a/state/sign.go b/state/sign.go deleted file mode 100644 index 88f038c..0000000 --- a/state/sign.go +++ /dev/null @@ -1,67 +0,0 @@ -package state - -import ( - "io" - - "github.com/nspcc-dev/neofs-api-go/service" -) - -// SignedData returns payload bytes of the request. -// -// Always returns an empty slice. -func (m NetmapRequest) SignedData() ([]byte, error) { - return make([]byte, 0), nil -} - -// SignedData returns payload bytes of the request. -// -// Always returns an empty slice. -func (m MetricsRequest) SignedData() ([]byte, error) { - return make([]byte, 0), nil -} - -// SignedData returns payload bytes of the request. -// -// Always returns an empty slice. -func (m HealthRequest) SignedData() ([]byte, error) { - return make([]byte, 0), nil -} - -// SignedData returns payload bytes of the request. -// -// Always returns an empty slice. -func (m DumpRequest) SignedData() ([]byte, error) { - return make([]byte, 0), nil -} - -// SignedData returns payload bytes of the request. -// -// Always returns an empty slice. -func (m DumpVarsRequest) SignedData() ([]byte, error) { - return make([]byte, 0), nil -} - -// SignedData returns payload bytes of the request. -func (m ChangeStateRequest) SignedData() ([]byte, error) { - return service.SignedDataFromReader(m) -} - -// SignedDataSize returns payload size of the request. -func (m ChangeStateRequest) SignedDataSize() int { - return m.GetState().Size() -} - -// ReadSignedData copies payload bytes to passed buffer. -// -// If the Request size is insufficient, io.ErrUnexpectedEOF returns. -func (m ChangeStateRequest) ReadSignedData(p []byte) (int, error) { - if len(p) < m.SignedDataSize() { - return 0, io.ErrUnexpectedEOF - } - - var off int - - off += copy(p[off:], m.GetState().Bytes()) - - return off, nil -} diff --git a/state/sign_test.go b/state/sign_test.go deleted file mode 100644 index 05af654..0000000 --- a/state/sign_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package state - -import ( - "testing" - - "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/stretchr/testify/require" -) - -func TestRequestSign(t *testing.T) { - sk := test.DecodeKey(0) - - type sigType interface { - service.RequestData - service.SignKeyPairAccumulator - service.SignKeyPairSource - SetToken(*service.Token) - } - - items := []struct { - constructor func() sigType - payloadCorrupt []func(sigType) - }{ - { // NetmapRequest - constructor: func() sigType { - return new(NetmapRequest) - }, - }, - { // MetricsRequest - constructor: func() sigType { - return new(MetricsRequest) - }, - }, - { // HealthRequest - constructor: func() sigType { - return new(HealthRequest) - }, - }, - { // DumpRequest - constructor: func() sigType { - return new(DumpRequest) - }, - }, - { // DumpVarsRequest - constructor: func() sigType { - return new(DumpVarsRequest) - }, - }, - { - constructor: func() sigType { - return new(ChangeStateRequest) - }, - payloadCorrupt: []func(sigType){ - func(s sigType) { - req := s.(*ChangeStateRequest) - - req.SetState(req.GetState() + 1) - }, - }, - }, - } - - for _, item := range items { - { // token corruptions - v := item.constructor() - - token := new(service.Token) - v.SetToken(token) - - require.NoError(t, service.SignRequestData(sk, v)) - - require.NoError(t, service.VerifyRequestData(v)) - - token.SetSessionKey(append(token.GetSessionKey(), 1)) - - require.Error(t, service.VerifyRequestData(v)) - } - - { // payload corruptions - for _, corruption := range item.payloadCorrupt { - v := item.constructor() - - require.NoError(t, service.SignRequestData(sk, v)) - - require.NoError(t, service.VerifyRequestData(v)) - - corruption(v) - - require.Error(t, service.VerifyRequestData(v)) - } - } - } -} diff --git a/state/types.go b/state/types.go deleted file mode 100644 index 6b572db..0000000 --- a/state/types.go +++ /dev/null @@ -1,24 +0,0 @@ -package state - -import ( - "encoding/binary" -) - -// SetState is a State field setter. -func (m *ChangeStateRequest) SetState(st ChangeStateRequest_State) { - m.State = st -} - -// Size returns the size of the state binary representation. -func (ChangeStateRequest_State) Size() int { - return 4 -} - -// Bytes returns the state binary representation. -func (x ChangeStateRequest_State) Bytes() []byte { - data := make([]byte, x.Size()) - - binary.BigEndian.PutUint32(data, uint32(x)) - - return data -} diff --git a/state/types_test.go b/state/types_test.go deleted file mode 100644 index 5d5f5de..0000000 --- a/state/types_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package state - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestChangeStateRequestGettersSetters(t *testing.T) { - t.Run("state", func(t *testing.T) { - st := ChangeStateRequest_State(1) - m := new(ChangeStateRequest) - - m.SetState(st) - - require.Equal(t, st, m.GetState()) - }) -} diff --git a/storagegroup/storage.go b/storagegroup/storage.go deleted file mode 100644 index 7438b2b..0000000 --- a/storagegroup/storage.go +++ /dev/null @@ -1,39 +0,0 @@ -package storagegroup - -import ( - "context" - - "github.com/nspcc-dev/neofs-api-go/refs" -) - -type ( - // Store is a interface for storing users storage group - Store interface { - Lister - Receiver - Receptacle - } - - // Lister defines list function that returns all storage groups - // created for the passed container - Lister interface { - List(ctx context.Context, cid refs.CID) ([]refs.SGID, error) - } - - // Receiver defines get function that returns asked storage group - Receiver interface { - Get(ctx context.Context, cid refs.CID, sgid refs.SGID) (Provider, error) - } - - // Receptacle defines put function that places storage group in the - // store. - Receptacle interface { - Put(ctx context.Context, sg Provider) error - } - - // InfoReceiver defines GetSGInfo function that returns storage group - // that contains passed object ids. - InfoReceiver interface { - GetSGInfo(ctx context.Context, cid refs.CID, group []refs.ObjectID) (*StorageGroup, error) - } -) diff --git a/storagegroup/types.go b/storagegroup/types.go deleted file mode 100644 index d16382b..0000000 --- a/storagegroup/types.go +++ /dev/null @@ -1,97 +0,0 @@ -package storagegroup - -import ( - "bytes" - "strconv" - "strings" - - "github.com/gogo/protobuf/proto" - "github.com/mr-tron/base58" - "github.com/nspcc-dev/neofs-api-go/hash" - "github.com/nspcc-dev/neofs-api-go/refs" -) - -type ( - // Hash is alias of hash.Hash for proto definition. - Hash = hash.Hash - - // Provider is an interface that defines storage group instance. - // There was different storage group implementation. Right now it - // is implemented as extended header in the object. - Provider interface { - // Group returns list of object ids of the storage group. - // This list **should be** sorted. - Group() []refs.ObjectID - - IDInfo() *IdentificationInfo - Zones() []ZoneInfo - } - - // ZoneInfo provides validation information of storage group. - ZoneInfo struct { - hash.Hash - Size uint64 - } - - // IdentificationInfo provides meta information about storage group. - IdentificationInfo struct { - CID refs.CID - SGID refs.SGID - OwnerID refs.OwnerID - } - - // IDList is a slice of object ids, that can be sorted. - IDList []refs.ObjectID -) - -var _ proto.Message = (*StorageGroup)(nil) - -// String returns string representation of StorageGroup. -func (m *StorageGroup) String() string { - b := new(strings.Builder) - b.WriteString("') - return b.String() -} - -// Empty checks if storage group has some data for validation. -func (m StorageGroup) Empty() bool { - return m.ValidationDataSize == 0 && m.ValidationHash.Equal(hash.Hash{}) -} - -// Len returns amount of object ids in IDList. -func (s IDList) Len() int { return len(s) } - -// Less returns byte comparision between IDList[i] and IDList[j]. -func (s IDList) Less(i, j int) bool { return bytes.Compare(s[i].Bytes(), s[j].Bytes()) == -1 } - -// Swap swaps element with i and j index in IDList. -func (s IDList) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -// CalculateSize combines length of all zones in storage group. -func CalculateSize(sg Provider) (size uint64) { - zoneList := sg.Zones() - for i := range zoneList { - size += zoneList[i].Size - } - return -} - -// CalculateHash returns homomorphic sum of hashes -// fromm all zones in storage group. -func CalculateHash(sg Provider) (hash.Hash, error) { - var ( - zones = sg.Zones() - hashes = make([]hash.Hash, len(zones)) - ) - for i := range zones { - hashes[i] = zones[i].Hash - } - return hash.Concat(hashes) -} diff --git a/storagegroup/types.pb.go b/storagegroup/types.pb.go deleted file mode 100644 index fa4b248..0000000 Binary files a/storagegroup/types.pb.go and /dev/null differ diff --git a/storagegroup/types.proto b/storagegroup/types.proto deleted file mode 100644 index fde33aa..0000000 --- a/storagegroup/types.proto +++ /dev/null @@ -1,36 +0,0 @@ -syntax = "proto3"; -package storagegroup; -option go_package = "github.com/nspcc-dev/neofs-api-go/storagegroup"; -option csharp_namespace = "NeoFS.API.StorageGroup"; - -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -message StorageGroup { - option (gogoproto.goproto_stringer) = false; - - // ValidationDataSize is size of the all object's payloads included into storage group - uint64 ValidationDataSize = 1; - // ValidationHash is homomorphic hash of all object's payloads included into storage group - bytes ValidationHash = 2 [(gogoproto.customtype) = "Hash", (gogoproto.nullable) = false]; - - message Lifetime { - enum Unit { - // Unlimited set if storage group always valid - Unlimited = 0; - // NeoFSEpoch set if storage group is valid until lifetime NeoFS epoch - NeoFSEpoch = 1; - // UnixTime set if storage group is valid until lifetime unix timestamp - UnixTime = 2; - } - - // Unit is lifetime type - Unit unit = 1 [(gogoproto.customname) = "Unit"]; - // Value for lifetime - int64 Value = 2; - } - - // Lifetime is time until storage group is valid - Lifetime lifetime = 3 [(gogoproto.customname) = "Lifetime"]; -}