Return errors instead of panic (#414)

close #390
This commit is contained in:
Roman Khimov 2023-05-19 14:58:58 +03:00 committed by GitHub
commit 7002b3b0df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 210 additions and 140 deletions

View file

@ -41,14 +41,14 @@ func (x ResBalanceGet) Amount() accounting.Decimal {
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
//
// Immediately panics if parameters are set incorrectly (see PrmBalanceGet docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingAccount]
func (c *Client) BalanceGet(ctx context.Context, prm PrmBalanceGet) (*ResBalanceGet, error) {
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case !prm.accountSet:
panic("account not set")
return nil, ErrMissingAccount
}
// form request body

View file

@ -74,15 +74,19 @@ func (c *Client) Init(prm PrmInit) {
// One-time method call during application start-up stage (after Init ) is expected.
// Calling multiple times leads to undefined behavior.
//
// Return client errors:
// - [ErrMissingServer]
// - [ErrNonPositiveTimeout]
//
// See also Init / Close.
func (c *Client) Dial(prm PrmDial) error {
if prm.endpoint == "" {
panic("server address is unset or empty")
return ErrMissingServer
}
if prm.timeoutDialSet {
if prm.timeoutDial <= 0 {
panic("non-positive timeout")
return ErrNonPositiveTimeout
}
} else {
prm.timeoutDial = 5 * time.Second
@ -90,7 +94,7 @@ func (c *Client) Dial(prm PrmDial) error {
if prm.streamTimeoutSet {
if prm.streamTimeout <= 0 {
panic("non-positive timeout")
return ErrNonPositiveTimeout
}
} else {
prm.streamTimeout = 10 * time.Second

View file

@ -47,14 +47,6 @@ func writeXHeadersToMeta(xHeaders []string, h *v2session.RequestMetaHeader) {
h.SetXHeaders(hs)
}
// panic messages.
const (
panicMsgMissingContext = "missing context"
panicMsgMissingContainer = "missing container"
panicMsgMissingObject = "missing object"
panicMsgOwnerExtract = "extract owner failed"
)
// groups all the details required to send a single request and process a response to it.
type contextCall struct {
// ==================================================

View file

@ -84,15 +84,15 @@ func (c *Client) defaultSigner() neofscrypto.Signer {
//
// Success can be verified by reading by identifier (see ResContainerPut.ID).
//
// Immediately panics if parameters are set incorrectly (see PrmContainerPut docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingContainer]
func (c *Client) ContainerPut(ctx context.Context, prm PrmContainerPut) (*ResContainerPut, error) {
// check parameters
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case !prm.cnrSet:
panic(panicMsgMissingContainer)
return nil, ErrMissingContainer
}
// TODO: check private signer is set before forming the request
@ -206,14 +206,14 @@ func (x ResContainerGet) Container() container.Container {
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
//
// Immediately panics if parameters are set incorrectly (see PrmContainerGet docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingContainer]
func (c *Client) ContainerGet(ctx context.Context, prm PrmContainerGet) (*ResContainerGet, error) {
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case !prm.idSet:
panic(panicMsgMissingContainer)
return nil, ErrMissingContainer
}
var cidV2 refs.ContainerID
@ -296,15 +296,15 @@ func (x ResContainerList) Containers() []cid.ID {
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
//
// Immediately panics if parameters are set incorrectly (see PrmContainerList docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingAccount]
func (c *Client) ContainerList(ctx context.Context, prm PrmContainerList) (*ResContainerList, error) {
// check parameters
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case !prm.ownerSet:
panic("account not set")
return nil, ErrMissingAccount
}
// form request body
@ -402,17 +402,17 @@ func (x *PrmContainerDelete) WithinSession(tok session.Container) {
//
// Success can be verified by reading by identifier (see GetContainer).
//
// Immediately panics if parameters are set incorrectly (see PrmContainerDelete docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingContainer]
//
// Reflects all internal errors in second return value (transport problems, response processing, etc.).
func (c *Client) ContainerDelete(ctx context.Context, prm PrmContainerDelete) error {
// check parameters
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case !prm.idSet:
panic(panicMsgMissingContainer)
return ErrMissingContainer
}
// sign container ID
@ -510,15 +510,15 @@ func (x ResContainerEACL) Table() eacl.Table {
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
//
// Immediately panics if parameters are set incorrectly (see PrmContainerEACL docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingContainer]
func (c *Client) ContainerEACL(ctx context.Context, prm PrmContainerEACL) (*ResContainerEACL, error) {
// check parameters
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case !prm.idSet:
panic(panicMsgMissingContainer)
return nil, ErrMissingContainer
}
var cidV2 refs.ContainerID
@ -619,20 +619,21 @@ func (x *PrmContainerSetEACL) WithinSession(s session.Container) {
//
// Success can be verified by reading by identifier (see EACL).
//
// Immediately panics if parameters are set incorrectly (see PrmContainerSetEACL docs).
// Return errors:
// - [ErrMissingEACL]
// - [ErrMissingEACLContainer]
//
// Context is required and must not be nil. It is used for network communication.
func (c *Client) ContainerSetEACL(ctx context.Context, prm PrmContainerSetEACL) error {
// check parameters
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case !prm.tableSet:
panic("eACL table not set")
return ErrMissingEACL
}
_, isCIDSet := prm.table.CID()
if !isCIDSet {
panic("missing container in eACL table")
return ErrMissingEACLContainer
}
// sign the eACL table
@ -720,15 +721,15 @@ func (x *PrmAnnounceSpace) SetValues(vs []container.SizeEstimation) {
//
// At this moment success can not be checked.
//
// Immediately panics if parameters are set incorrectly (see PrmAnnounceSpace docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingAnnouncements]
func (c *Client) ContainerAnnounceUsedSpace(ctx context.Context, prm PrmAnnounceSpace) error {
// check parameters
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case len(prm.announcements) == 0:
panic("missing announcements")
return ErrMissingAnnouncements
}
// convert list of SDK announcement structures into NeoFS-API v2 list

View file

@ -1,33 +1,72 @@
package client
import (
"errors"
"fmt"
)
var (
errMissingResponseField missingResponseFieldErr
// ErrMissingServer is returned when server endpoint is empty in parameters.
ErrMissingServer = errors.New("server address is unset or empty")
// ErrNonPositiveTimeout is returned when any timeout is below zero in parameters.
ErrNonPositiveTimeout = errors.New("non-positive timeout")
// ErrMissingContainer is returned when container is not provided.
ErrMissingContainer = errors.New("missing container")
// ErrMissingObject is returned when object is not provided.
ErrMissingObject = errors.New("missing object")
// ErrMissingAccount is returned when account/owner is not provided.
ErrMissingAccount = errors.New("missing account")
// ErrMissingEACL is returned when eACL table is not provided.
ErrMissingEACL = errors.New("missing eACL table")
// ErrMissingEACLContainer is returned when container info is not provided in eACL table.
ErrMissingEACLContainer = errors.New("missing container in eACL table")
// ErrMissingAnnouncements is returned when announcements are not provided.
ErrMissingAnnouncements = errors.New("missing announcements")
// ErrZeroRangeLength is returned when range parameter has zero length.
ErrZeroRangeLength = errors.New("zero range length")
// ErrMissingRanges is returned when empty ranges list is provided.
ErrMissingRanges = errors.New("missing ranges")
// ErrZeroEpoch is returned when zero epoch is provided.
ErrZeroEpoch = errors.New("zero epoch")
// ErrMissingTrusts is returned when empty slice of trusts is provided.
ErrMissingTrusts = errors.New("missing trusts")
// ErrMissingTrust is returned when empty trust is not provided.
ErrMissingTrust = errors.New("missing trust")
// ErrUnexpectedReadCall is returned when we already got all data but truing to get more.
ErrUnexpectedReadCall = errors.New("unexpected call to `Read`")
// ErrSign is returned when unable to sign service message.
ErrSign SignError
// ErrMissingResponseField is returned when required field is not exists in NeoFS api response.
ErrMissingResponseField MissingResponseFieldErr
)
type missingResponseFieldErr struct {
// MissingResponseFieldErr contains field name which should be in NeoFS API response.
type MissingResponseFieldErr struct {
name string
}
func (e missingResponseFieldErr) Error() string {
// Error implements the error interface.
func (e MissingResponseFieldErr) Error() string {
return fmt.Sprintf("missing %s field in the response", e.name)
}
func (e missingResponseFieldErr) Is(target error) bool {
// Is implements interface for correct checking current error type with [errors.Is].
func (e MissingResponseFieldErr) Is(target error) bool {
switch target.(type) {
default:
return false
case missingResponseFieldErr, *missingResponseFieldErr:
case MissingResponseFieldErr, *MissingResponseFieldErr:
return true
}
}
// returns error describing missing field with the given name.
func newErrMissingResponseField(name string) error {
return missingResponseFieldErr{name: name}
return MissingResponseFieldErr{name: name}
}
// returns error describing invalid field (according to the NeoFS protocol)
@ -35,3 +74,33 @@ func newErrMissingResponseField(name string) error {
func newErrInvalidResponseField(name string, err error) error {
return fmt.Errorf("invalid %s field in the response: %w", name, err)
}
// SignError wraps another error with reason why sign process was failed.
type SignError struct {
err error
}
// NewSignError is a constructor for [SignError].
func NewSignError(err error) SignError {
return SignError{err: err}
}
// Error implements the error interface.
func (e SignError) Error() string {
return fmt.Sprintf("sign: %v", e.err)
}
// Unwrap implements the error interface.
func (e SignError) Unwrap() error {
return e.err
}
// Is implements interface for correct checking current error type with [errors.Is].
func (e SignError) Is(target error) bool {
switch target.(type) {
default:
return false
case SignError, *SignError:
return true
}
}

17
client/errors_test.go Normal file
View file

@ -0,0 +1,17 @@
package client_test
import (
"errors"
"testing"
"github.com/nspcc-dev/neofs-sdk-go/client"
"github.com/stretchr/testify/require"
)
func Test_SignError(t *testing.T) {
someErr := errors.New("some error")
signErr := client.NewSignError(someErr)
require.ErrorIs(t, signErr, someErr)
require.ErrorIs(t, signErr, client.ErrSign)
}

View file

@ -41,17 +41,11 @@ func (x ResEndpointInfo) NodeInfo() netmap.NodeInfo {
// Any client's internal or transport errors are returned as `error`,
// see [apistatus] package for NeoFS-specific error types.
//
// Immediately panics if parameters are set incorrectly (see PrmEndpointInfo docs).
// Context is required and must not be nil. It is used for network communication.
//
// Exactly one return value is non-nil. Server status return is returned in ResEndpointInfo.
// Reflects all internal errors in second return value (transport problems, response processing, etc.).
func (c *Client) EndpointInfo(ctx context.Context, prm PrmEndpointInfo) (*ResEndpointInfo, error) {
// check context
if ctx == nil {
panic(panicMsgMissingContext)
}
// form request
var req v2netmap.LocalNodeInfoRequest
@ -130,17 +124,11 @@ func (x ResNetworkInfo) Info() netmap.NetworkInfo {
// Any client's internal or transport errors are returned as `error`,
// see [apistatus] package for NeoFS-specific error types.
//
// Immediately panics if parameters are set incorrectly (see PrmNetworkInfo docs).
// Context is required and must not be nil. It is used for network communication.
//
// Exactly one return value is non-nil. Server status return is returned in ResNetworkInfo.
// Reflects all internal errors in second return value (transport problems, response processing, etc.).
func (c *Client) NetworkInfo(ctx context.Context, prm PrmNetworkInfo) (*ResNetworkInfo, error) {
// check context
if ctx == nil {
panic(panicMsgMissingContext)
}
// form request
var req v2netmap.NetworkInfoRequest
@ -207,11 +195,6 @@ func (x ResNetMapSnapshot) NetMap() netmap.NetMap {
// Exactly one return value is non-nil. Server status return is returned in ResNetMapSnapshot.
// Reflects all internal errors in second return value (transport problems, response processing, etc.).
func (c *Client) NetMapSnapshot(ctx context.Context, _ PrmNetMapSnapshot) (*ResNetMapSnapshot, error) {
// check context
if ctx == nil {
panic(panicMsgMissingContext)
}
// form request body
var body v2netmap.SnapshotRequestBody

View file

@ -82,12 +82,6 @@ func TestClient_NetMapSnapshot(t *testing.T) {
c := newClient(signer, &srv)
ctx := context.Background()
// missing context
require.PanicsWithValue(t, panicMsgMissingContext, func() {
//nolint:staticcheck
_, _ = c.NetMapSnapshot(nil, prm)
})
// request signature
srv.errTransport = errors.New("any error")

View file

@ -114,23 +114,22 @@ func (x ResObjectDelete) Tombstone() oid.ID {
// Any client's internal or transport errors are returned as `error`,
// see [apistatus] package for NeoFS-specific error types.
//
// Immediately panics if parameters are set incorrectly (see PrmObjectDelete docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - global (see Client docs)
// - [ErrMissingContainer];
// - [ErrMissingObject];
// - [apistatus.ErrContainerNotFound];
// - [apistatus.ErrObjectAccessDenied];
// - [apistatus.ErrObjectLocked];
// - [apistatus.ErrSessionTokenExpired].
func (c *Client) ObjectDelete(ctx context.Context, prm PrmObjectDelete) (*ResObjectDelete, error) {
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case prm.addr.GetContainerID() == nil:
panic(panicMsgMissingContainer)
return nil, ErrMissingContainer
case prm.addr.GetObjectID() == nil:
panic(panicMsgMissingObject)
return nil, ErrMissingObject
}
// form request body

View file

@ -289,17 +289,18 @@ func (x *ObjectReader) Read(p []byte) (int, error) {
// The call only opens the transmission channel, explicit fetching is done using the ObjectReader.
// Exactly one return value is non-nil. Resulting reader must be finally closed.
//
// Immediately panics if parameters are set incorrectly (see PrmObjectGet docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingContainer]
// - [ErrMissingObject]
func (c *Client) ObjectGetInit(ctx context.Context, prm PrmObjectGet) (*ObjectReader, error) {
// check parameters
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case prm.addr.GetContainerID() == nil:
panic(panicMsgMissingContainer)
return nil, ErrMissingContainer
case prm.addr.GetObjectID() == nil:
panic(panicMsgMissingObject)
return nil, ErrMissingObject
}
// form request body
@ -387,11 +388,12 @@ func (x *ResObjectHead) ReadHeader(dst *object.Object) bool {
// Any client's internal or transport errors are returned as `error`,
// see [apistatus] package for NeoFS-specific error types.
//
// Immediately panics if parameters are set incorrectly (see PrmObjectHead docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - global (see Client docs);
// - [ErrMissingContainer];
// - [ErrMissingObject];
// - *[object.SplitInfoError] (returned on virtual objects with PrmObjectHead.MakeRaw).
// - [apistatus.ErrContainerNotFound];
// - [apistatus.ErrObjectNotFound];
@ -400,12 +402,10 @@ func (x *ResObjectHead) ReadHeader(dst *object.Object) bool {
// - [apistatus.ErrSessionTokenExpired].
func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectHead, error) {
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case prm.addr.GetContainerID() == nil:
panic(panicMsgMissingContainer)
return nil, ErrMissingContainer
case prm.addr.GetObjectID() == nil:
panic(panicMsgMissingObject)
return nil, ErrMissingObject
}
var body v2object.HeadRequestBody
@ -639,19 +639,21 @@ func (x *ObjectRangeReader) Read(p []byte) (int, error) {
// The call only opens the transmission channel, explicit fetching is done using the ObjectRangeReader.
// Exactly one return value is non-nil. Resulting reader must be finally closed.
//
// Immediately panics if parameters are set incorrectly (see PrmObjectRange docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingContainer]
// - [ErrMissingObject]
// - [ErrZeroRangeLength]
func (c *Client) ObjectRangeInit(ctx context.Context, prm PrmObjectRange) (*ObjectRangeReader, error) {
// check parameters
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case prm.addr.GetContainerID() == nil:
panic(panicMsgMissingContainer)
return nil, ErrMissingContainer
case prm.addr.GetObjectID() == nil:
panic(panicMsgMissingObject)
return nil, ErrMissingObject
case prm.rng.GetLength() == 0:
panic("zero range length")
return nil, ErrZeroRangeLength
}
// form request body

View file

@ -152,18 +152,20 @@ func (x ResObjectHash) Checksums() [][]byte {
// Any client's internal or transport errors are returned as `error`,
// see [apistatus] package for NeoFS-specific error types.
//
// Immediately panics if parameters are set incorrectly (see PrmObjectHash docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingContainer]
// - [ErrMissingObject]
// - [ErrMissingRanges]
func (c *Client) ObjectHash(ctx context.Context, prm PrmObjectHash) (*ResObjectHash, error) {
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case prm.addr.GetContainerID() == nil:
panic(panicMsgMissingContainer)
return nil, ErrMissingContainer
case prm.addr.GetObjectID() == nil:
panic(panicMsgMissingObject)
return nil, ErrMissingObject
case len(prm.body.GetRanges()) == 0:
panic("missing ranges")
return nil, ErrMissingRanges
}
prm.body.SetAddress(&prm.addr)

View file

@ -233,11 +233,6 @@ func (x *ObjectWriter) Close() (*ResObjectPut, error) {
//
// Context is required and must not be nil. It is used for network communication.
func (c *Client) ObjectPutInit(ctx context.Context, prm PrmObjectPutInit) (*ObjectWriter, error) {
// check parameters
if ctx == nil {
panic(panicMsgMissingContext)
}
var w ObjectWriter
ctx, cancel := context.WithCancel(ctx)

View file

@ -205,15 +205,15 @@ func (x *ObjectListReader) Close() error {
// is done using the ObjectListReader. Exactly one return value is non-nil.
// Resulting reader must be finally closed.
//
// Immediately panics if parameters are set incorrectly (see PrmObjectSearch docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingContainer]
func (c *Client) ObjectSearchInit(ctx context.Context, prm PrmObjectSearch) (*ObjectListReader, error) {
// check parameters
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case !prm.cnrSet:
panic(panicMsgMissingContainer)
return nil, ErrMissingContainer
}
var cidV2 v2refs.ContainerID

View file

@ -2,7 +2,6 @@ package client
import (
"errors"
"fmt"
"io"
"testing"
@ -136,7 +135,7 @@ func (s *singleStreamResponder) Read(resp *v2object.SearchResponse) error {
if s.endError != nil {
return s.endError
}
panic("unexpected call to `Read`")
return ErrUnexpectedReadCall
}
var body v2object.SearchResponseBody
@ -152,7 +151,7 @@ func (s *singleStreamResponder) Read(resp *v2object.SearchResponse) error {
err := signServiceMessage(s.signer, resp)
if err != nil {
panic(fmt.Errorf("error: %w", err))
return err
}
s.n++

View file

@ -37,17 +37,18 @@ func (x *PrmAnnounceLocalTrust) SetValues(trusts []reputation.Trust) {
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
//
// Immediately panics if parameters are set incorrectly (see PrmAnnounceLocalTrust docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrZeroEpoch]
// - [ErrMissingTrusts]
func (c *Client) AnnounceLocalTrust(ctx context.Context, prm PrmAnnounceLocalTrust) error {
// check parameters
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case prm.epoch == 0:
panic("zero epoch")
return ErrZeroEpoch
case len(prm.trusts) == 0:
panic("missing trusts")
return ErrMissingTrusts
}
// form request body
@ -125,17 +126,18 @@ func (x *PrmAnnounceIntermediateTrust) SetCurrentValue(trust reputation.PeerToPe
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
//
// Immediately panics if parameters are set incorrectly (see PrmAnnounceIntermediateTrust docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrZeroEpoch]
// - [ErrMissingTrust]
func (c *Client) AnnounceIntermediateTrust(ctx context.Context, prm PrmAnnounceIntermediateTrust) error {
// check parameters
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case prm.epoch == 0:
panic("zero epoch")
return ErrZeroEpoch
case !prm.trustSet:
panic("current trust value not set")
return ErrMissingTrust
}
var trust v2reputation.PeerToPeerTrust

View file

@ -2,6 +2,7 @@ package client
import (
"context"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
@ -64,17 +65,11 @@ func (x ResSessionCreate) PublicKey() []byte {
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
//
// Immediately panics if parameters are set incorrectly (see PrmSessionCreate docs).
// Context is required and must not be nil. It is used for network communication.
func (c *Client) SessionCreate(ctx context.Context, prm PrmSessionCreate) (*ResSessionCreate, error) {
// check context
if ctx == nil {
panic(panicMsgMissingContext)
}
var ownerID user.ID
if err := user.IDFromSigner(&ownerID, prm.signer); err != nil {
panic(panicMsgOwnerExtract)
return nil, fmt.Errorf("IDFromSigner: %w", err)
}
var ownerIDV2 refs.OwnerID

View file

@ -56,7 +56,7 @@ func TestClient_SessionCreate(t *testing.T) {
result, err := c.SessionCreate(ctx, prmSessionCreate)
require.Nil(t, result)
require.ErrorIs(t, err, errMissingResponseField)
require.ErrorIs(t, err, ErrMissingResponseField)
require.Equal(t, "missing session id field in the response", err.Error())
})
@ -67,7 +67,7 @@ func TestClient_SessionCreate(t *testing.T) {
result, err := c.SessionCreate(ctx, prmSessionCreate)
require.Nil(t, result)
require.ErrorIs(t, err, errMissingResponseField)
require.ErrorIs(t, err, ErrMissingResponseField)
require.Equal(t, "missing session key field in the response", err.Error())
})
}

View file

@ -132,6 +132,8 @@ func (s stableMarshalerWrapper) SignedDataSize() int {
}
// signServiceMessage signing request or response messages which can be sent or received from neofs endpoint.
// Return errors:
// - [ErrSign]
func signServiceMessage(signer neofscrypto.Signer, msg interface{}) error {
var (
body, meta, verifyOrigin stableMarshaler
@ -165,24 +167,24 @@ func signServiceMessage(signer neofscrypto.Signer, msg interface{}) error {
verifyOrigin = h
}
default:
panic(fmt.Sprintf("unsupported session message %T", v))
return NewSignError(fmt.Errorf("unsupported session message %T", v))
}
if verifyOrigin == nil {
// sign session message body
if err := signServiceMessagePart(signer, body, verifyHdr.SetBodySignature); err != nil {
return fmt.Errorf("could not sign body: %w", err)
return NewSignError(fmt.Errorf("body: %w", err))
}
}
// sign meta header
if err := signServiceMessagePart(signer, meta, verifyHdr.SetMetaSignature); err != nil {
return fmt.Errorf("could not sign meta header: %w", err)
return NewSignError(fmt.Errorf("meta header: %w", err))
}
// sign verification header origin
if err := signServiceMessagePart(signer, verifyOrigin, verifyHdr.SetOriginSignature); err != nil {
return fmt.Errorf("could not sign origin of verification header: %w", err)
return NewSignError(fmt.Errorf("origin of verification header: %w", err))
}
// wrap origin verification header
@ -240,7 +242,7 @@ func verifyServiceMessage(msg interface{}) error {
ResponseVerificationHeader: v.GetVerificationHeader(),
}
default:
panic(fmt.Sprintf("unsupported session message %T", v))
return fmt.Errorf("unsupported session message %T", v)
}
body := serviceMessageBody(msg)

View file

@ -19,6 +19,11 @@ import (
"github.com/nspcc-dev/tzhash/tz"
)
var (
// ErrInvalidAttributeAmount indicates wrong number of arguments. Amount of arguments MUST be even number.
ErrInvalidAttributeAmount = errors.New("attributes must be even number of strings")
)
// ObjectWriter represents a virtual object recorder.
type ObjectWriter interface {
// InitDataStream initializes and returns a stream of writable data associated
@ -133,15 +138,16 @@ func (x *Slicer) childPayloadSizeLimit() uint64 {
//
// See New for details.
func (x *Slicer) Slice(data io.Reader, attributes ...string) (oid.ID, error) {
var rootID oid.ID
if len(attributes)%2 != 0 {
panic("attributes must be even number of strings")
return rootID, ErrInvalidAttributeAmount
}
if x.opts.objectPayloadLimit == 0 {
x.opts.objectPayloadLimit = 1 << 20
}
var rootID oid.ID
var rootHeader object.Object
var offset uint64
var isSplit bool

View file

@ -155,7 +155,7 @@ func (x commonData) signedData(w contextWriter) []byte {
func (x *commonData) sign(signer neofscrypto.Signer, w contextWriter) error {
if err := user.IDFromSigner(&x.issuer, signer); err != nil {
return err
return fmt.Errorf("IDFromSigner: %w", err)
}
x.issuerSet = true

View file

@ -1,17 +1,21 @@
package user
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
)
// ErrOwnerExtract is returned when failed to extract account info from key.
var ErrOwnerExtract = errors.New("decode owner failed")
// IDFromKey forms the ID using script hash calculated for the given key.
func IDFromKey(id *ID, key []byte) error {
var pk keys.PublicKey
if err := pk.DecodeBytes(key); err != nil {
return fmt.Errorf("IDFromKey %v", err)
return fmt.Errorf("%w: %v", ErrOwnerExtract, err)
}
id.SetScriptHash(pk.GetScriptHash())

View file

@ -20,4 +20,8 @@ func TestIDFromKey(t *testing.T) {
require.NoError(t, user.IDFromKey(&id, rawPub))
require.Equal(t, "NNLi44dJNXtDNSBkofB48aTVYtb1zZrNEs", id.EncodeToString())
err := user.IDFromKey(&id, []byte{1})
require.Error(t, err)
require.ErrorIs(t, err, user.ErrOwnerExtract)
}