diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index de59f36..d846055 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -38,4 +38,6 @@ jobs: run: go test -coverprofile=coverage.txt -covermode=atomic ./... - name: Codecov + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} run: bash <(curl -s https://codecov.io/bash) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e4b71c8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,22 @@ +# Changelog +This is the changelog for NeoFS Proto + +## [0.2.0] - 2019-11-21 + +### Added +- Container not found error +- GitHub Actions as CI and Codecov +- Auto-generated proto documentation +- RequestMetaHeader to all RPC requests +- RequestVerificationHeader to all RPC requests + +### Changed +- Moved TTL and Epoch fields to RequestMetaHeader +- Renamed Version in object.SearchRequest to QueryVersion +- Removed SetTTL, GetTTL, SetEpoch, GetEpoch from all RPC requests + +## 0.1.0 - 2019-11-18 + +Initial public release + +[0.2.0]: https://github.com/nspcc-dev/neofs-proto/compare/v0.1.0...v0.2.0 diff --git a/Makefile b/Makefile index 64b0939..9609923 100644 --- a/Makefile +++ b/Makefile @@ -33,5 +33,5 @@ protoc: echo "${B}${G}⇒ Processing $$f ${R}"; \ protoc \ --proto_path=.:./vendor:/usr/local/include \ - --gofast_out=plugins=grpc,paths=source_relative:. $$f; \ + --gofast_out=plugins=grpc,paths=source_relative:. $$f; \ done diff --git a/accounting/service.go b/accounting/service.go index df74e58..ac1ceb5 100644 --- a/accounting/service.go +++ b/accounting/service.go @@ -31,9 +31,6 @@ const ( ErrEmptyParentAddress = internal.Error("empty parent address") ) -// SetTTL sets ttl to BalanceRequest to satisfy TTLRequest interface. -func (m BalanceRequest) SetTTL(v uint32) { m.TTL = v } - // SumFunds goes through all accounts and sums up active funds. func SumFunds(accounts []*Account) (res *decimal.Decimal) { res = decimal.Zero.Copy() diff --git a/accounting/service.pb.go b/accounting/service.pb.go index bc31ce1..7a8a086 100644 Binary files a/accounting/service.pb.go and b/accounting/service.pb.go differ diff --git a/accounting/service.proto b/accounting/service.proto index 9cfdb0f..f2696c4 100644 --- a/accounting/service.proto +++ b/accounting/service.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package accounting; option go_package = "github.com/nspcc-dev/neofs-proto/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"; @@ -17,10 +19,11 @@ service Accounting { message BalanceRequest { // OwnerID is a wallet address - bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 2; + 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 { diff --git a/accounting/withdraw.go b/accounting/withdraw.go index 3cd2766..0a8cf90 100644 --- a/accounting/withdraw.go +++ b/accounting/withdraw.go @@ -11,18 +11,6 @@ type ( MessageID = refs.MessageID ) -// SetTTL sets ttl to GetRequest to satisfy TTLRequest interface. -func (m *GetRequest) SetTTL(v uint32) { m.TTL = v } - -// SetTTL sets ttl to PutRequest to satisfy TTLRequest interface. -func (m *PutRequest) SetTTL(v uint32) { m.TTL = v } - -// SetTTL sets ttl to ListRequest to satisfy TTLRequest interface. -func (m *ListRequest) SetTTL(v uint32) { m.TTL = v } - -// SetTTL sets ttl to DeleteRequest to satisfy TTLRequest interface. -func (m *DeleteRequest) SetTTL(v uint32) { m.TTL = v } - // SetSignature sets signature to PutRequest to satisfy SignedRequest interface. func (m *PutRequest) SetSignature(v []byte) { m.Signature = v } diff --git a/accounting/withdraw.pb.go b/accounting/withdraw.pb.go index f0e78aa..a2116c0 100644 Binary files a/accounting/withdraw.pb.go and b/accounting/withdraw.pb.go differ diff --git a/accounting/withdraw.proto b/accounting/withdraw.proto index eb58d08..bb950ba 100644 --- a/accounting/withdraw.proto +++ b/accounting/withdraw.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package accounting; option go_package = "github.com/nspcc-dev/neofs-proto/accounting"; +import "service/meta.proto"; +import "service/verify.proto"; import "decimal/decimal.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; @@ -34,12 +36,13 @@ message Item { message GetRequest { // ID is cheque identifier - bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false]; + bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false]; // OwnerID is a wallet address - bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 3; + 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 { @@ -49,18 +52,19 @@ message GetResponse { message PutRequest { // OwnerID is a wallet address - bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; + bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; // Amount of funds - decimal.Decimal Amount = 2; + decimal.Decimal Amount = 2; // Height is the neo blockchain height until the cheque is valid - uint64 Height = 3; + uint64 Height = 3; // MessageID is a nonce for uniq request (UUIDv4) - bytes MessageID = 4 [(gogoproto.customtype) = "MessageID", (gogoproto.nullable) = false]; + bytes MessageID = 4 [(gogoproto.customtype) = "MessageID", (gogoproto.nullable) = false]; // Signature is a signature of the sent request - bytes Signature = 5; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 6; + bytes Signature = 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 { // ID is cheque identifier @@ -69,10 +73,11 @@ message PutResponse { message ListRequest { // OwnerID is a wallet address - bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 2; + 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 { @@ -82,16 +87,17 @@ message ListResponse { message DeleteRequest { // ID is cheque identifier - bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false]; + bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false]; // OwnerID is a wallet address - bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; + 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]; + bytes MessageID = 3 [(gogoproto.customtype) = "MessageID", (gogoproto.nullable) = false]; // Signature is a signature of the sent request - bytes Signature = 4; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 5; + bytes Signature = 4; + // 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 diff --git a/bootstrap/service.go b/bootstrap/service.go index 6d0d3ca..e67ea6d 100644 --- a/bootstrap/service.go +++ b/bootstrap/service.go @@ -6,6 +6,3 @@ import ( // NodeType type alias. type NodeType = service.NodeRole - -// SetTTL sets ttl to Request to satisfy TTLRequest interface. -func (m *Request) SetTTL(v uint32) { m.TTL = v } diff --git a/bootstrap/service.pb.go b/bootstrap/service.pb.go index f00f814..7165399 100644 Binary files a/bootstrap/service.pb.go and b/bootstrap/service.pb.go differ diff --git a/bootstrap/service.proto b/bootstrap/service.proto index cde94dc..f0bf755 100644 --- a/bootstrap/service.proto +++ b/bootstrap/service.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package bootstrap; option go_package = "github.com/nspcc-dev/neofs-proto/bootstrap"; +import "service/meta.proto"; +import "service/verify.proto"; import "bootstrap/types.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; @@ -17,10 +19,11 @@ service Bootstrap { message Request { // Type is NodeType, can be InnerRingNode (type=1) or StorageNode (type=2) - int32 type = 1 [(gogoproto.customname) = "Type" , (gogoproto.nullable) = false, (gogoproto.customtype) = "NodeType"]; + int32 type = 1 [(gogoproto.customname) = "Type" , (gogoproto.nullable) = false, (gogoproto.customtype) = "NodeType"]; // Info contains information about node - bootstrap.NodeInfo info = 2 [(gogoproto.nullable) = false]; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 3; + bootstrap.NodeInfo info = 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]; } diff --git a/container/service.go b/container/service.go index 27f39e7..4d3747e 100644 --- a/container/service.go +++ b/container/service.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/binary" + "github.com/nspcc-dev/neofs-proto/internal" "github.com/nspcc-dev/neofs-proto/refs" "github.com/pkg/errors" ) @@ -19,17 +20,10 @@ type ( MessageID = refs.MessageID ) -// SetTTL sets ttl to GetRequest to satisfy TTLRequest interface. -func (m *GetRequest) SetTTL(v uint32) { m.TTL = v } - -// SetTTL sets ttl to PutRequest to satisfy TTLRequest interface. -func (m *PutRequest) SetTTL(v uint32) { m.TTL = v } - -// SetTTL sets ttl to ListRequest to satisfy TTLRequest interface. -func (m *ListRequest) SetTTL(v uint32) { m.TTL = v } - -// SetTTL sets ttl to DeleteRequest to satisfy TTLRequest interface. -func (m *DeleteRequest) SetTTL(v uint32) { m.TTL = v } +const ( + // ErrNotFound is raised when container could not be found. + ErrNotFound = internal.Error("could not find container") +) // SetSignature sets signature to PutRequest to satisfy SignedRequest interface. func (m *PutRequest) SetSignature(v []byte) { m.Signature = v } diff --git a/container/service.pb.go b/container/service.pb.go index cfd401d..8b98cfa 100644 Binary files a/container/service.pb.go and b/container/service.pb.go differ diff --git a/container/service.proto b/container/service.proto index 1cf8227..d4d50ca 100644 --- a/container/service.proto +++ b/container/service.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package container; option go_package = "github.com/nspcc-dev/neofs-proto/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"; @@ -28,23 +30,24 @@ service Service { message PutRequest { // MessageID is a nonce for uniq container id calculation - bytes MessageID = 1 [(gogoproto.customtype) = "MessageID", (gogoproto.nullable) = false]; + 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; + uint64 Capacity = 2; // OwnerID is a wallet address - bytes OwnerID = 3 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; + 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]; + netmap.PlacementRule rules = 4 [(gogoproto.nullable) = false]; // Signature of the user (owner id) - bytes Signature = 5; + bytes Signature = 5; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 6; + // 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 { @@ -54,14 +57,15 @@ message PutResponse { message DeleteRequest { // CID (container id) is a SHA256 hash of the container structure - bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false]; - - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 2; + bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false]; // Signature of the container owner - bytes Signature = 3; + bytes Signature = 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]; } // DeleteResponse is empty because delete operation is asynchronous and done @@ -71,11 +75,12 @@ message DeleteResponse { } message GetRequest { // CID (container id) is a SHA256 hash of the container structure - bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false]; + bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false]; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 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 GetResponse { @@ -85,11 +90,11 @@ message GetResponse { message ListRequest { // OwnerID is a wallet address - bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 2; + 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 { diff --git a/docs/accounting.md b/docs/accounting.md index b4317ae..268a187 100644 --- a/docs/accounting.md +++ b/docs/accounting.md @@ -87,7 +87,8 @@ Balance returns current balance status of the NeoFS user | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | OwnerID | [bytes](#bytes) | | OwnerID is a wallet address | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -362,7 +363,8 @@ Delete allows user to remove unused cheque | OwnerID | [bytes](#bytes) | | OwnerID is a wallet address | | MessageID | [bytes](#bytes) | | MessageID is a nonce for uniq request (UUIDv4) | | Signature | [bytes](#bytes) | | Signature is a signature of the sent request | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -382,7 +384,8 @@ DeleteResponse is empty | ----- | ---- | ----- | ----------- | | ID | [bytes](#bytes) | | ID is cheque identifier | | OwnerID | [bytes](#bytes) | | OwnerID is a wallet address | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -420,7 +423,8 @@ DeleteResponse is empty | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | OwnerID | [bytes](#bytes) | | OwnerID is a wallet address | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -447,7 +451,8 @@ DeleteResponse is empty | Height | [uint64](#uint64) | | Height is the neo blockchain height until the cheque is valid | | MessageID | [bytes](#bytes) | | MessageID is a nonce for uniq request (UUIDv4) | | Signature | [bytes](#bytes) | | Signature is a signature of the sent request | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | diff --git a/docs/bootstrap.md b/docs/bootstrap.md index 19967bf..737e085 100644 --- a/docs/bootstrap.md +++ b/docs/bootstrap.md @@ -62,7 +62,8 @@ Process is method that allows to register node in the network and receive actual | ----- | ---- | ----- | ----------- | | type | [int32](#int32) | | Type is NodeType, can be InnerRingNode (type=1) or StorageNode (type=2) | | info | [NodeInfo](#bootstrap.NodeInfo) | | Info contains information about node | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | diff --git a/docs/container.md b/docs/container.md index 4f98a47..bc559ce 100644 --- a/docs/container.md +++ b/docs/container.md @@ -92,8 +92,9 @@ List returns all user's containers | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | CID | [bytes](#bytes) | | CID (container id) is a SHA256 hash of the container structure | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | | Signature | [bytes](#bytes) | | Signature of the container owner | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -113,7 +114,8 @@ via consensus in inner ring nodes | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | CID | [bytes](#bytes) | | CID (container id) is a SHA256 hash of the container structure | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -136,7 +138,8 @@ via consensus in inner ring nodes | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | OwnerID | [bytes](#bytes) | | OwnerID is a wallet address | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -163,7 +166,8 @@ via consensus in inner ring nodes | OwnerID | [bytes](#bytes) | | OwnerID is a wallet address | | rules | [netmap.PlacementRule](#netmap.PlacementRule) | | Rules define storage policy for the object inside the container. | | Signature | [bytes](#bytes) | | Signature of the user (owner id) | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | diff --git a/docs/object.md b/docs/object.md index 03e41c8..8407733 100644 --- a/docs/object.md +++ b/docs/object.md @@ -147,11 +147,11 @@ calculated for XORed data. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Epoch | [uint64](#uint64) | | Epoch is set by user to 0, node set epoch to the actual value Deprecated: will be replaced with RequestMetaHeader (see develop branch) | | Address | [refs.Address](#refs.Address) | | Address of object (container id + object id) | | OwnerID | [bytes](#bytes) | | OwnerID is a wallet address | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | | Token | [session.Token](#session.Token) | | Token with session public key and user's signature | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -170,11 +170,11 @@ in distributed system. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Epoch | [uint64](#uint64) | | Epoch is set by user to 0, node set epoch to the actual value Deprecated: will be replaced with RequestMetaHeader (see develop branch) | | Address | [refs.Address](#refs.Address) | | Address of object (container id + object id) | | Ranges | [Range](#object.Range) | repeated | Ranges of object's payload to calculate homomorphic hash | | Salt | [bytes](#bytes) | | Salt is used to XOR object's payload ranges before hashing, it can be nil | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -196,10 +196,10 @@ in distributed system. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Epoch | [uint64](#uint64) | | Epoch is set by user to 0, node set epoch to the actual value Deprecated: will be replaced with RequestMetaHeader (see develop branch) | | Address | [refs.Address](#refs.Address) | | Address of object (container id + object id) | | Ranges | [Range](#object.Range) | repeated | Ranges of object's payload to return | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -221,9 +221,9 @@ in distributed system. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Epoch | [uint64](#uint64) | | Epoch is set by user to 0, node set epoch to the actual value Deprecated: will be replaced with RequestMetaHeader (see develop branch) | | Address | [refs.Address](#refs.Address) | | Address of object (container id + object id) | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -246,10 +246,10 @@ in distributed system. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Epoch | [uint64](#uint64) | | Epoch should be empty on user side, node sets epoch to the actual value Deprecated: will be replaced with RequestMetaHeader (see develop branch) | | Address | [refs.Address](#refs.Address) | | Address of object (container id + object id) | | FullHeaders | [bool](#bool) | | FullHeaders can be set true for extended headers in the object | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -273,6 +273,8 @@ in distributed system. | ----- | ---- | ----- | ----------- | | Header | [PutRequest.PutHeader](#object.PutRequest.PutHeader) | | Header should be the first message in the stream | | Chunk | [bytes](#bytes) | | Chunk should be a remaining message in stream should be chunks | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -283,9 +285,7 @@ in distributed system. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Epoch | [uint64](#uint64) | | Epoch is set by user to 0, node set epoch to the actual value Deprecated: will be replaced with RequestMetaHeader (see develop branch) | | Object | [Object](#object.Object) | | Object with at least container id and owner id fields | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | | Token | [session.Token](#session.Token) | | Token with session public key and user's signature | @@ -308,11 +308,11 @@ in distributed system. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Epoch | [uint64](#uint64) | | Epoch is set by user to 0, node set epoch to the actual value Deprecated: will be replaced with RequestMetaHeader (see develop branch) | -| Version | [uint32](#uint32) | | Version of search query format | | ContainerID | [bytes](#bytes) | | ContainerID for searching the object | | Query | [bytes](#bytes) | | Query in the binary serialized format | -| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every neofs-node Deprecated: will be replaced with RequestMetaHeader (see develop branch) | +| QueryVersion | [uint32](#uint32) | | QueryVersion is a version of search query format | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | diff --git a/docs/service.md b/docs/service.md new file mode 100644 index 0000000..454fab4 --- /dev/null +++ b/docs/service.md @@ -0,0 +1,157 @@ +# Protocol Documentation + + +## Table of Contents + +- [service/meta.proto](#service/meta.proto) + + - Messages + - [RequestMetaHeader](#service.RequestMetaHeader) + + +- [service/verify.proto](#service/verify.proto) + + - Messages + - [RequestVerificationHeader](#service.RequestVerificationHeader) + - [RequestVerificationHeader.Sign](#service.RequestVerificationHeader.Sign) + - [RequestVerificationHeader.Signature](#service.RequestVerificationHeader.Signature) + + +- [service/verify_test.proto](#service/verify_test.proto) + + - Messages + - [TestRequest](#service.TestRequest) + + +- [Scalar Value Types](#scalar-value-types) + + + + +

Top

+ +## service/meta.proto + + + + + + + +### Message RequestMetaHeader +RequestMetaHeader contains information about request meta headers +(should be embedded into message) + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every NeoFS Node | +| Epoch | [uint64](#uint64) | | Epoch for user can be empty, because node sets epoch to the actual value | +| Version | [uint32](#uint32) | | Version defines protocol version TODO: not used for now, should be implemented in future | + + + + + + + + +

Top

+ +## service/verify.proto + + + + + + + +### Message RequestVerificationHeader +RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request +(should be embedded into message). + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| Signatures | [RequestVerificationHeader.Signature](#service.RequestVerificationHeader.Signature) | repeated | Signatures is a set of signatures of every passed NeoFS Node | + + + + +### Message RequestVerificationHeader.Sign + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| Sign | [bytes](#bytes) | | Sign is signature of the request or session key. | +| Peer | [bytes](#bytes) | | Peer is compressed public key used for signature. | + + + + +### Message RequestVerificationHeader.Signature + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| Sign | [RequestVerificationHeader.Sign](#service.RequestVerificationHeader.Sign) | | Sign is a signature and public key of the request. | +| Origin | [RequestVerificationHeader.Sign](#service.RequestVerificationHeader.Sign) | | Origin used for requests, when trusted node changes it and re-sign with session key. If session key used for signature request, then Origin should contain public key of user and signed session key. | + + + + + + + + +

Top

+ +## service/verify_test.proto + + + + + + + +### Message TestRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| IntField | [int32](#int32) | | | +| StringField | [string](#string) | | | +| BytesField | [bytes](#bytes) | | | +| CustomField | [bytes](#bytes) | | | +| Meta | [RequestMetaHeader](#service.RequestMetaHeader) | | | +| Header | [RequestVerificationHeader](#service.RequestVerificationHeader) | | | + + + + + + + +## Scalar Value Types + +| .proto Type | Notes | C++ Type | Java Type | Python Type | +| ----------- | ----- | -------- | --------- | ----------- | +| double | | double | double | float | +| float | | float | float | float | +| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | +| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | +| uint32 | Uses variable-length encoding. | uint32 | int | int/long | +| uint64 | Uses variable-length encoding. | uint64 | long | int/long | +| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | +| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | +| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | +| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | +| sfixed32 | Always four bytes. | int32 | int | int | +| sfixed64 | Always eight bytes. | int64 | long | int/long | +| bool | | bool | boolean | boolean | +| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | +| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | + diff --git a/docs/session.md b/docs/session.md index cf4a1fd..e8633d3 100644 --- a/docs/session.md +++ b/docs/session.md @@ -70,6 +70,8 @@ session key. Session is established during 4-step handshake in one gRPC stream | ----- | ---- | ----- | ----------- | | Init | [Token](#session.Token) | | Init is a message to initialize session opening. Carry: owner of manipulation object; ID of manipulation object; token lifetime bounds. | | Signed | [Token](#session.Token) | | Signed Init message response (Unsigned) from server with user private key | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | diff --git a/docs/state.md b/docs/state.md index 6d240c1..e678be3 100644 --- a/docs/state.md +++ b/docs/state.md @@ -70,6 +70,11 @@ If node unhealthy field Status would contains detailed info. HealthRequest message to check current state +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | + @@ -89,6 +94,11 @@ HealthResponse message with current state MetricsRequest message to request node metrics +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | + @@ -109,6 +119,11 @@ from github.com/prometheus/client_model/metrics.proto NetmapRequest message to request current node netmap +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | +| Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | + diff --git a/object/service.go b/object/service.go index 098f9c3..8482539 100644 --- a/object/service.go +++ b/object/service.go @@ -31,8 +31,7 @@ type ( // All object operations must have TTL, Epoch, Container ID and // permission of usage previous network map. Request interface { - service.TTLRequest - service.EpochRequest + service.MetaHeader CID() CID AllowPreviousNetMap() bool @@ -124,54 +123,6 @@ 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) } -// GetTTL returns TTL value from object put request. -func (m *PutRequest) GetTTL() uint32 { return m.GetHeader().TTL } - -// GetEpoch returns epoch value from object put request. -func (m *PutRequest) GetEpoch() uint64 { return m.GetHeader().GetEpoch() } - -// SetTTL sets TTL value into object put request. -func (m *PutRequest) SetTTL(ttl uint32) { m.GetHeader().TTL = ttl } - -// SetTTL sets TTL value into object get request. -func (m *GetRequest) SetTTL(ttl uint32) { m.TTL = ttl } - -// SetTTL sets TTL value into object head request. -func (m *HeadRequest) SetTTL(ttl uint32) { m.TTL = ttl } - -// SetTTL sets TTL value into object search request. -func (m *SearchRequest) SetTTL(ttl uint32) { m.TTL = ttl } - -// SetTTL sets TTL value into object delete request. -func (m *DeleteRequest) SetTTL(ttl uint32) { m.TTL = ttl } - -// SetTTL sets TTL value into object get range request. -func (m *GetRangeRequest) SetTTL(ttl uint32) { m.TTL = ttl } - -// SetTTL sets TTL value into object get range hash request. -func (m *GetRangeHashRequest) SetTTL(ttl uint32) { m.TTL = ttl } - -// SetEpoch sets epoch value into object put request. -func (m *PutRequest) SetEpoch(v uint64) { m.GetHeader().Epoch = v } - -// SetEpoch sets epoch value into object get request. -func (m *GetRequest) SetEpoch(v uint64) { m.Epoch = v } - -// SetEpoch sets epoch value into object head request. -func (m *HeadRequest) SetEpoch(v uint64) { m.Epoch = v } - -// SetEpoch sets epoch value into object search request. -func (m *SearchRequest) SetEpoch(v uint64) { m.Epoch = v } - -// SetEpoch sets epoch value into object delete request. -func (m *DeleteRequest) SetEpoch(v uint64) { m.Epoch = v } - -// SetEpoch sets epoch value into object get range request. -func (m *GetRangeRequest) SetEpoch(v uint64) { m.Epoch = v } - -// SetEpoch sets epoch value into object get range hash request. -func (m *GetRangeHashRequest) SetEpoch(v uint64) { m.Epoch = v } - // CID returns container id value from object put request. func (m *PutRequest) CID() CID { return m.GetHeader().Object.SystemHeader.CID } diff --git a/object/service.pb.go b/object/service.pb.go index b19ed93..4cad2f7 100644 Binary files a/object/service.pb.go and b/object/service.pb.go differ diff --git a/object/service.proto b/object/service.proto index 7a971db..da54447 100644 --- a/object/service.proto +++ b/object/service.proto @@ -5,6 +5,8 @@ option go_package = "github.com/nspcc-dev/neofs-proto/object"; import "refs/types.proto"; import "object/types.proto"; import "session/types.proto"; +import "service/meta.proto"; +import "service/verify.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; option (gogoproto.stable_marshaler_all) = true; @@ -52,14 +54,12 @@ service Service { } message GetRequest { - // Epoch is set by user to 0, node set epoch to the actual value - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint64 Epoch = 1; // Address of object (container id + object id) - refs.Address Address = 2 [(gogoproto.nullable) = false]; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 3; + 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 { @@ -73,16 +73,10 @@ message GetResponse { message PutRequest { message PutHeader { - // Epoch is set by user to 0, node set epoch to the actual value - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint64 Epoch = 1; // Object with at least container id and owner id fields - Object Object = 2; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 3; + Object Object = 1; // Token with session public key and user's signature - session.Token Token = 4; + session.Token Token = 2; } oneof R { @@ -91,6 +85,11 @@ message PutRequest { // 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 { @@ -98,18 +97,16 @@ message PutResponse { refs.Address Address = 1 [(gogoproto.nullable) = false]; } message DeleteRequest { - // Epoch is set by user to 0, node set epoch to the actual value - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint64 Epoch = 1; // Address of object (container id + object id) - refs.Address Address = 2 [(gogoproto.nullable) = false]; + refs.Address Address = 1 [(gogoproto.nullable) = false]; // OwnerID is a wallet address - bytes OwnerID = 3 [(gogoproto.nullable) = false, (gogoproto.customtype) = "OwnerID"]; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 4; + bytes OwnerID = 2 [(gogoproto.nullable) = false, (gogoproto.customtype) = "OwnerID"]; // Token with session public key and user's signature - session.Token Token = 5; + session.Token Token = 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]; } // DeleteResponse is empty because we cannot guarantee permanent object removal @@ -117,16 +114,14 @@ message DeleteRequest { message DeleteResponse {} message HeadRequest { - // Epoch should be empty on user side, node sets epoch to the actual value - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint64 Epoch = 1; // Address of object (container id + object id) - refs.Address Address = 2 [(gogoproto.nullable) = false, (gogoproto.customtype) = "Address"]; + refs.Address Address = 1 [(gogoproto.nullable) = false, (gogoproto.customtype) = "Address"]; // FullHeaders can be set true for extended headers in the object - bool FullHeaders = 3; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 4; + 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 @@ -134,18 +129,16 @@ message HeadResponse { } message SearchRequest { - // Epoch is set by user to 0, node set epoch to the actual value - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint64 Epoch = 1; - // Version of search query format - uint32 Version = 2; // ContainerID for searching the object - bytes ContainerID = 3 [(gogoproto.nullable) = false, (gogoproto.customtype) = "CID"]; + bytes ContainerID = 1 [(gogoproto.nullable) = false, (gogoproto.customtype) = "CID"]; // Query in the binary serialized format - bytes Query = 4; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 5; + 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 { @@ -154,16 +147,14 @@ message SearchResponse { } message GetRangeRequest { - // Epoch is set by user to 0, node set epoch to the actual value - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint64 Epoch = 1; // Address of object (container id + object id) - refs.Address Address = 2 [(gogoproto.nullable) = false]; + refs.Address Address = 1 [(gogoproto.nullable) = false]; // Ranges of object's payload to return - repeated Range Ranges = 3 [(gogoproto.nullable) = false]; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 4; + repeated Range Ranges = 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 { @@ -172,18 +163,16 @@ message GetRangeResponse { } message GetRangeHashRequest { - // Epoch is set by user to 0, node set epoch to the actual value - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint64 Epoch = 1; // Address of object (container id + object id) - refs.Address Address = 2 [(gogoproto.nullable) = false]; + refs.Address Address = 1 [(gogoproto.nullable) = false]; // Ranges of object's payload to calculate homomorphic hash - repeated Range Ranges = 3 [(gogoproto.nullable) = false]; + repeated Range Ranges = 2 [(gogoproto.nullable) = false]; // Salt is used to XOR object's payload ranges before hashing, it can be nil - bytes Salt = 4; - // TTL must be larger than zero, it decreased in every neofs-node - // Deprecated: will be replaced with RequestMetaHeader (see develop branch) - uint32 TTL = 5; + 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 { diff --git a/object/utils.go b/object/utils.go index 1799b24..f74fa2f 100644 --- a/object/utils.go +++ b/object/utils.go @@ -4,6 +4,7 @@ import ( "io" "code.cloudfoundry.org/bytefmt" + "github.com/nspcc-dev/neofs-proto/service" "github.com/nspcc-dev/neofs-proto/session" "github.com/pkg/errors" ) @@ -49,14 +50,11 @@ func SendPutRequest(s Service_PutClient, obj *Object, epoch uint64, ttl uint32) // into header of object put request. func MakePutRequestHeader(obj *Object, epoch uint64, ttl uint32, token *session.Token) *PutRequest { return &PutRequest{ - R: &PutRequest_Header{ - Header: &PutRequest_PutHeader{ - Epoch: epoch, - Object: obj, - TTL: ttl, - Token: token, - }, - }, + RequestMetaHeader: service.RequestMetaHeader{TTL: ttl, Epoch: epoch}, + R: &PutRequest_Header{Header: &PutRequest_PutHeader{ + Object: obj, + Token: token, + }}, } } diff --git a/service/meta.go b/service/meta.go new file mode 100644 index 0000000..6b5b8b7 --- /dev/null +++ b/service/meta.go @@ -0,0 +1,111 @@ +package service + +import ( + "github.com/nspcc-dev/neofs-proto/internal" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type ( + // MetaHeader contains meta information of request. + // It provides methods to get or set meta information meta header. + // Also contains methods to reset and restore meta header. + // Also contains methods to get or set request protocol version + MetaHeader interface { + ResetMeta() RequestMetaHeader + RestoreMeta(RequestMetaHeader) + + // TTLRequest to verify and update ttl requests. + GetTTL() uint32 + SetTTL(uint32) + + // EpochRequest gives possibility to get or set epoch in RPC Requests. + GetEpoch() uint64 + SetEpoch(uint64) + + // VersionHeader allows get or set version of protocol request + VersionHeader + } + + // VersionHeader allows get or set version of protocol request + VersionHeader interface { + GetVersion() uint32 + SetVersion(uint32) + } + + // TTLCondition is closure, that allows to validate request with ttl. + TTLCondition func(ttl uint32) error +) + +const ( + // ZeroTTL is empty ttl, should produce ErrZeroTTL. + ZeroTTL = iota + + // NonForwardingTTL is a ttl that allows direct connections only. + NonForwardingTTL + + // SingleForwardingTTL is a ttl that allows connections through another node. + SingleForwardingTTL +) + +const ( + // ErrZeroTTL is raised when zero ttl is passed. + ErrZeroTTL = internal.Error("zero ttl") + + // ErrIncorrectTTL is raised when NonForwardingTTL is passed and NodeRole != InnerRingNode. + ErrIncorrectTTL = internal.Error("incorrect ttl") +) + +// SetVersion sets protocol version to RequestMetaHeader. +func (m *RequestMetaHeader) SetVersion(v uint32) { m.Version = v } + +// SetTTL sets TTL to RequestMetaHeader. +func (m *RequestMetaHeader) SetTTL(v uint32) { m.TTL = v } + +// SetEpoch sets Epoch to RequestMetaHeader. +func (m *RequestMetaHeader) SetEpoch(v uint64) { m.Epoch = v } + +// ResetMeta returns current value and sets RequestMetaHeader to empty value. +func (m *RequestMetaHeader) ResetMeta() RequestMetaHeader { + cp := *m + m.Reset() + return cp +} + +// RestoreMeta sets current RequestMetaHeader to passed value. +func (m *RequestMetaHeader) RestoreMeta(v RequestMetaHeader) { *m = 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 ErrIncorrectTTL + } + + return nil + } +} + +// ProcessRequestTTL validates and update ttl requests. +func ProcessRequestTTL(req MetaHeader, cond ...TTLCondition) error { + var ttl = req.GetTTL() + + if ttl == ZeroTTL { + return status.New(codes.InvalidArgument, ErrZeroTTL.Error()).Err() + } + + for i := range cond { + if cond[i] == nil { + continue + } + + // check specific condition: + if err := cond[i](ttl); err != nil { + return status.New(codes.InvalidArgument, err.Error()).Err() + } + } + + req.SetTTL(ttl - 1) + + return nil +} diff --git a/service/meta.pb.go b/service/meta.pb.go new file mode 100644 index 0000000..d5f58fd Binary files /dev/null and b/service/meta.pb.go differ diff --git a/service/meta.proto b/service/meta.proto new file mode 100644 index 0000000..d3c3005 --- /dev/null +++ b/service/meta.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; +package service; +option go_package = "github.com/nspcc-dev/neofs-proto/service"; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +option (gogoproto.stable_marshaler_all) = true; + +// RequestMetaHeader contains information about request meta headers +// (should be embedded into message) +message RequestMetaHeader { + // TTL must be larger than zero, it decreased in every NeoFS Node + uint32 TTL = 1; + // Epoch for user can be empty, because node sets epoch to the actual value + uint64 Epoch = 2; + // Version defines protocol version + // TODO: not used for now, should be implemented in future + uint32 Version = 3; +} diff --git a/service/meta_test.go b/service/meta_test.go new file mode 100644 index 0000000..893ca5e --- /dev/null +++ b/service/meta_test.go @@ -0,0 +1,70 @@ +package service + +import ( + "testing" + + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type mockedRequest struct { + msg string + name string + role NodeRole + code codes.Code + RequestMetaHeader +} + +func TestMetaRequest(t *testing.T) { + tests := []mockedRequest{ + { + role: InnerRingNode, + name: "direct to ir node", + RequestMetaHeader: RequestMetaHeader{TTL: NonForwardingTTL}, + }, + { + role: StorageNode, + code: codes.InvalidArgument, + msg: ErrIncorrectTTL.Error(), + name: "direct to storage node", + RequestMetaHeader: RequestMetaHeader{TTL: NonForwardingTTL}, + }, + { + role: StorageNode, + msg: ErrZeroTTL.Error(), + code: codes.InvalidArgument, + name: "zero ttl", + RequestMetaHeader: RequestMetaHeader{TTL: ZeroTTL}, + }, + { + role: InnerRingNode, + name: "default to ir node", + RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, + }, + { + role: StorageNode, + name: "default to storage node", + RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, + }, + } + + for i := range tests { + tt := tests[i] + t.Run(tt.name, func(t *testing.T) { + before := tt.GetTTL() + err := ProcessRequestTTL(&tt, IRNonForwarding(tt.role)) + if tt.msg != "" { + require.Errorf(t, err, tt.msg) + + state, ok := status.FromError(err) + require.True(t, ok) + require.Equal(t, state.Code(), tt.code) + require.Equal(t, state.Message(), tt.msg) + } else { + require.NoError(t, err) + require.NotEqualf(t, before, tt.GetTTL(), "ttl should be changed: %d vs %d", before, tt.GetTTL()) + } + }) + } +} diff --git a/service/role_test.go b/service/role_test.go index a1f1cc0..01dbee2 100644 --- a/service/role_test.go +++ b/service/role_test.go @@ -1,8 +1,9 @@ package service import ( - "github.com/stretchr/testify/require" "testing" + + "github.com/stretchr/testify/require" ) func TestNodeRole_String(t *testing.T) { diff --git a/service/ttl.go b/service/ttl.go deleted file mode 100644 index 8ddf1de..0000000 --- a/service/ttl.go +++ /dev/null @@ -1,45 +0,0 @@ -package service - -import ( - "github.com/nspcc-dev/neofs-proto/internal" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// TTLRequest to verify and update ttl requests. -type TTLRequest interface { - GetTTL() uint32 - SetTTL(uint32) -} - -const ( - // ZeroTTL is empty ttl, should produce ErrZeroTTL. - ZeroTTL = iota - - // NonForwardingTTL is a ttl that allows direct connections only. - NonForwardingTTL - - // SingleForwardingTTL is a ttl that allows connections through another node. - SingleForwardingTTL - - // ErrZeroTTL is raised when zero ttl is passed. - ErrZeroTTL = internal.Error("zero ttl") - - // ErrIncorrectTTL is raised when NonForwardingTTL is passed and NodeRole != InnerRingNode. - ErrIncorrectTTL = internal.Error("incorrect ttl") -) - -// CheckTTLRequest validates and update ttl requests. -func CheckTTLRequest(req TTLRequest, role NodeRole) error { - var ttl = req.GetTTL() - - if ttl == ZeroTTL { - return status.New(codes.InvalidArgument, ErrZeroTTL.Error()).Err() - } else if ttl == NonForwardingTTL && role != InnerRingNode { - return status.New(codes.InvalidArgument, ErrIncorrectTTL.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 1349198..0000000 --- a/service/ttl_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package service - -import ( - "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "testing" -) - -type mockedRequest struct { - msg string - ttl uint32 - name string - role NodeRole - code codes.Code -} - -func (m *mockedRequest) SetTTL(v uint32) { m.ttl = v } -func (m mockedRequest) GetTTL() uint32 { return m.ttl } - -func TestCheckTTLRequest(t *testing.T) { - tests := []mockedRequest{ - { - ttl: NonForwardingTTL, - role: InnerRingNode, - name: "direct to ir node", - }, - { - ttl: NonForwardingTTL, - role: StorageNode, - code: codes.InvalidArgument, - msg: ErrIncorrectTTL.Error(), - name: "direct to storage node", - }, - { - ttl: ZeroTTL, - role: StorageNode, - msg: ErrZeroTTL.Error(), - code: codes.InvalidArgument, - name: "zero ttl", - }, - { - ttl: SingleForwardingTTL, - role: InnerRingNode, - name: "default to ir node", - }, - { - ttl: SingleForwardingTTL, - role: StorageNode, - name: "default to storage node", - }, - } - - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - before := tt.ttl - err := CheckTTLRequest(&tt, tt.role) - if tt.msg != "" { - require.Errorf(t, err, tt.msg) - - state, ok := status.FromError(err) - require.True(t, ok) - require.Equal(t, state.Code(), tt.code) - require.Equal(t, state.Message(), tt.msg) - } else { - require.NoError(t, err) - require.NotEqualf(t, before, tt.ttl, "ttl should be changed: %d vs %d", before, tt.ttl) - } - }) - } -} diff --git a/service/verify.go b/service/verify.go new file mode 100644 index 0000000..a6ac3a5 --- /dev/null +++ b/service/verify.go @@ -0,0 +1,219 @@ +package service + +import ( + "crypto/ecdsa" + + "github.com/gogo/protobuf/proto" + crypto "github.com/nspcc-dev/neofs-crypto" + "github.com/nspcc-dev/neofs-proto/internal" + "github.com/pkg/errors" +) + +type ( + // VerifiableRequest adds possibility to sign and verify request header. + VerifiableRequest interface { + proto.Message + Marshal() ([]byte, error) + AddSignature(*RequestVerificationHeader_Signature) + GetSignatures() []*RequestVerificationHeader_Signature + SetSignatures([]*RequestVerificationHeader_Signature) + } + + // MaintainableRequest adds possibility to set and get (+validate) + // owner (client) public key from RequestVerificationHeader. + MaintainableRequest interface { + proto.Message + GetOwner() (*ecdsa.PublicKey, error) + SetOwner(*ecdsa.PublicKey, []byte) + GetLastPeer() (*ecdsa.PublicKey, error) + } +) + +const ( + // ErrCannotLoadPublicKey is raised when cannot unmarshal public key from RequestVerificationHeader_Sign. + ErrCannotLoadPublicKey = internal.Error("cannot load public key") + + // ErrCannotFindOwner is raised when signatures empty in GetOwner. + ErrCannotFindOwner = internal.Error("cannot find owner public key") +) + +// SetSignatures replaces signatures stored in RequestVerificationHeader. +func (m *RequestVerificationHeader) SetSignatures(signatures []*RequestVerificationHeader_Signature) { + m.Signatures = signatures +} + +// AddSignature adds new Signature into RequestVerificationHeader. +func (m *RequestVerificationHeader) AddSignature(sig *RequestVerificationHeader_Signature) { + if sig == nil { + return + } + m.Signatures = append(m.Signatures, sig) +} + +// SetOwner adds origin (sign and public key) of owner (client) into first signature. +func (m *RequestVerificationHeader) SetOwner(pub *ecdsa.PublicKey, sign []byte) { + if len(m.Signatures) == 0 || pub == nil { + return + } + + m.Signatures[0].Origin = &RequestVerificationHeader_Sign{ + Sign: sign, + Peer: crypto.MarshalPublicKey(pub), + } +} + +// GetOwner tries to get owner (client) public key from signatures. +// If signatures contains not empty Origin, we should try to validate, +// that session key was signed by owner (client), otherwise return error. +func (m *RequestVerificationHeader) GetOwner() (*ecdsa.PublicKey, error) { + if len(m.Signatures) == 0 { + return nil, ErrCannotFindOwner + } + + // if first signature contains origin, we should try to validate session key + if m.Signatures[0].Origin != nil { + owner := crypto.UnmarshalPublicKey(m.Signatures[0].Origin.Peer) + if owner == nil { + return nil, ErrCannotLoadPublicKey + } else if err := crypto.Verify(owner, m.Signatures[0].Peer, m.Signatures[0].Origin.Sign); err != nil { + return nil, errors.Wrap(err, "could not verify session token") + } + + return owner, nil + } else if key := crypto.UnmarshalPublicKey(m.Signatures[0].Peer); key != nil { + return key, nil + } + + return nil, ErrCannotLoadPublicKey +} + +// GetLastPeer tries to get last peer public key from signatures. +// If signatures has zero length, returns ErrCannotFindOwner. +// If signatures has length equal to one, uses GetOwner. +// Otherwise tries to unmarshal last peer public key. +func (m *RequestVerificationHeader) GetLastPeer() (*ecdsa.PublicKey, error) { + switch ln := len(m.Signatures); ln { + case 0: + return nil, ErrCannotFindOwner + case 1: + return m.GetOwner() + default: + if key := crypto.UnmarshalPublicKey(m.Signatures[ln-1].Peer); key != nil { + return key, nil + } + + return nil, ErrCannotLoadPublicKey + } +} + +func newSignature(key *ecdsa.PrivateKey, data []byte) (*RequestVerificationHeader_Signature, error) { + sign, err := crypto.Sign(key, data) + if err != nil { + return nil, err + } + + return &RequestVerificationHeader_Signature{ + RequestVerificationHeader_Sign: RequestVerificationHeader_Sign{ + Sign: sign, + Peer: crypto.MarshalPublicKey(&key.PublicKey), + }, + }, nil +} + +// SignRequestHeader receives private key and request with RequestVerificationHeader, +// tries to marshal and sign request with passed PrivateKey, after that adds +// new signature to headers. If something went wrong, returns error. +func SignRequestHeader(key *ecdsa.PrivateKey, msg VerifiableRequest) error { + // ignore meta header + if meta, ok := msg.(MetaHeader); ok { + h := meta.ResetMeta() + + defer func() { + meta.RestoreMeta(h) + }() + } + + data, err := msg.Marshal() + if err != nil { + return err + } + + signature, err := newSignature(key, data) + if err != nil { + return err + } + + msg.AddSignature(signature) + + return nil +} + +// VerifyRequestHeader receives request with RequestVerificationHeader, +// tries to marshal and verify each signature from request. +// If something went wrong, returns error. +func VerifyRequestHeader(msg VerifiableRequest) error { + // ignore meta header + if meta, ok := msg.(MetaHeader); ok { + h := meta.ResetMeta() + + defer func() { + meta.RestoreMeta(h) + }() + } + + signatures := msg.GetSignatures() + defer func() { + msg.SetSignatures(signatures) + }() + + for i := range signatures { + msg.SetSignatures(signatures[:i]) + peer := signatures[i].GetPeer() + sign := signatures[i].GetSign() + + key := crypto.UnmarshalPublicKey(peer) + if key == nil { + return errors.Wrapf(ErrCannotLoadPublicKey, "%d: %02x", i, peer) + } + + if data, err := msg.Marshal(); err != nil { + return errors.Wrapf(err, "%d: %02x", i, peer) + } else if err := crypto.Verify(key, data, sign); err != nil { + return errors.Wrapf(err, "%d: %02x", i, peer) + } + } + + return nil +} + +// 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 } diff --git a/service/verify.pb.go b/service/verify.pb.go new file mode 100644 index 0000000..95a9452 Binary files /dev/null and b/service/verify.pb.go differ diff --git a/service/verify.proto b/service/verify.proto new file mode 100644 index 0000000..c70015b --- /dev/null +++ b/service/verify.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; +package service; +option go_package = "github.com/nspcc-dev/neofs-proto/service"; + +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 Sign { + // Sign is signature of the request or session key. + bytes Sign = 1; + // Peer is compressed public key used for signature. + bytes Peer = 2; + } + + message Signature { + // Sign is a signature and public key of the request. + Sign Sign = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; + // Origin used for requests, when trusted node changes it and re-sign with session key. + // If session key used for signature request, then Origin should contain + // public key of user and signed session key. + Sign Origin = 2; + } + + // Signatures is a set of signatures of every passed NeoFS Node + repeated Signature Signatures = 1; +} diff --git a/service/verify_test.go b/service/verify_test.go new file mode 100644 index 0000000..1403c67 --- /dev/null +++ b/service/verify_test.go @@ -0,0 +1,143 @@ +package service + +import ( + "bytes" + "log" + "math" + "testing" + + "github.com/gogo/protobuf/proto" + crypto "github.com/nspcc-dev/neofs-crypto" + "github.com/nspcc-dev/neofs-crypto/test" + "github.com/pkg/errors" + "github.com/stretchr/testify/require" +) + +func TestSignRequestHeader(t *testing.T) { + req := &TestRequest{ + IntField: math.MaxInt32, + StringField: "TestRequestStringField", + BytesField: []byte("TestRequestBytesField"), + } + + key := test.DecodeKey(0) + peer := crypto.MarshalPublicKey(&key.PublicKey) + + data, err := req.Marshal() + require.NoError(t, err) + + require.NoError(t, SignRequestHeader(key, req)) + + require.Len(t, req.Signatures, 1) + for i := range req.Signatures { + sign := req.Signatures[i].GetSign() + require.Equal(t, peer, req.Signatures[i].GetPeer()) + require.NoError(t, crypto.Verify(&key.PublicKey, data, sign)) + } +} + +func TestVerifyRequestHeader(t *testing.T) { + req := &TestRequest{ + IntField: math.MaxInt32, + StringField: "TestRequestStringField", + BytesField: []byte("TestRequestBytesField"), + RequestMetaHeader: RequestMetaHeader{TTL: 10}, + } + + for i := 0; i < 10; i++ { + req.TTL-- + require.NoError(t, SignRequestHeader(test.DecodeKey(i), req)) + } + + require.NoError(t, VerifyRequestHeader(req)) +} + +func TestMaintainableRequest(t *testing.T) { + req := &TestRequest{ + IntField: math.MaxInt32, + StringField: "TestRequestStringField", + BytesField: []byte("TestRequestBytesField"), + RequestMetaHeader: RequestMetaHeader{TTL: 10}, + } + + count := 10 + owner := test.DecodeKey(count + 1) + + for i := 0; i < count; i++ { + req.TTL-- + + key := test.DecodeKey(i) + require.NoError(t, SignRequestHeader(key, req)) + + // sign first key (session key) by owner key + if i == 0 { + sign, err := crypto.Sign(owner, crypto.MarshalPublicKey(&key.PublicKey)) + require.NoError(t, err) + + req.SetOwner(&owner.PublicKey, sign) + } + } + + { // Good case: + require.NoError(t, VerifyRequestHeader(req)) + + // validate, that first key (session key) was signed with owner + signatures := req.GetSignatures() + + require.Len(t, signatures, count) + + pub, err := req.GetOwner() + require.NoError(t, err) + + require.Equal(t, &owner.PublicKey, pub) + } + + { // wrong owner: + req.Signatures[0].Origin = nil + + pub, err := req.GetOwner() + require.NoError(t, err) + + require.NotEqual(t, &owner.PublicKey, pub) + } + + { // Wrong signatures: + copy(req.Signatures[count-1].Sign, req.Signatures[count-1].Peer) + err := VerifyRequestHeader(req) + require.EqualError(t, errors.Cause(err), crypto.ErrInvalidSignature.Error()) + } +} + +func TestVerifyAndSignRequestHeaderWithoutCloning(t *testing.T) { + key := test.DecodeKey(0) + + custom := testCustomField{1, 2, 3, 4, 5, 6, 7, 8} + + b := &TestRequest{ + IntField: math.MaxInt32, + StringField: "TestRequestStringField", + BytesField: []byte("TestRequestBytesField"), + CustomField: &custom, + RequestMetaHeader: RequestMetaHeader{ + TTL: math.MaxInt32 - 8, + Epoch: math.MaxInt64 - 12, + }, + } + + require.NoError(t, SignRequestHeader(key, b)) + require.NoError(t, VerifyRequestHeader(b)) + + require.Len(t, b.Signatures, 1) + require.Equal(t, custom, *b.CustomField) + require.Equal(t, uint32(math.MaxInt32-8), b.GetTTL()) + require.Equal(t, uint64(math.MaxInt64-12), b.GetEpoch()) + + buf := bytes.NewBuffer(nil) + log.SetOutput(buf) + + cp, ok := proto.Clone(b).(*TestRequest) + require.True(t, ok) + require.NotEqual(t, b, cp) + + require.Contains(t, buf.String(), "proto: don't know how to copy") +} diff --git a/service/verify_test.pb.go b/service/verify_test.pb.go new file mode 100644 index 0000000..06e7971 Binary files /dev/null and b/service/verify_test.pb.go differ diff --git a/service/verify_test.proto b/service/verify_test.proto new file mode 100644 index 0000000..c632615 --- /dev/null +++ b/service/verify_test.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; +package service; +option go_package = "github.com/nspcc-dev/neofs-proto/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/session/service.pb.go b/session/service.pb.go index b99b611..8532523 100644 Binary files a/session/service.pb.go and b/session/service.pb.go differ diff --git a/session/service.proto b/session/service.proto index 4b37aaf..377e77e 100644 --- a/session/service.proto +++ b/session/service.proto @@ -3,6 +3,8 @@ package session; option go_package = "github.com/nspcc-dev/neofs-proto/session"; import "session/types.proto"; +import "service/meta.proto"; +import "service/verify.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; option (gogoproto.stable_marshaler_all) = true; @@ -34,6 +36,10 @@ message CreateRequest { // Signed Init message response (Unsigned) from server with user private key session.Token Signed = 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 CreateResponse { diff --git a/state/service.pb.go b/state/service.pb.go index a368410..520c8ce 100644 Binary files a/state/service.pb.go and b/state/service.pb.go differ diff --git a/state/service.proto b/state/service.proto index 3c85d86..ed90e26 100644 --- a/state/service.proto +++ b/state/service.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package state; option go_package = "github.com/nspcc-dev/neofs-proto/state"; +import "service/meta.proto"; +import "service/verify.proto"; import "bootstrap/types.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; @@ -19,10 +21,20 @@ service Status { } // NetmapRequest message to request current node netmap -message NetmapRequest {} +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 {} +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 @@ -32,7 +44,12 @@ message MetricsResponse { } // HealthRequest message to check current state -message HealthRequest {} +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 {