From 3b7e884e7485960682ef201b921b831e22d4611d Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 31 Aug 2021 13:16:25 +0300 Subject: [PATCH] [#746] morph/client: Distinguish between neo-go errors and NeoFS logic Implement `error` interface on new `neofsError` type which is a wrapper over NeoFS-specific error. Wrap all `Client` errors except neo-go client API ones into `neofsError`. Wrapped errors are going to be used for multi-endpoint loop control. Signed-off-by: Leonard Lyubich --- pkg/morph/client/client.go | 34 ++++++++++++++++++++++++++++++---- pkg/morph/client/notary.go | 14 ++++++++------ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/pkg/morph/client/client.go b/pkg/morph/client/client.go index 5e0fff3a6..44b758286 100644 --- a/pkg/morph/client/client.go +++ b/pkg/morph/client/client.go @@ -79,6 +79,29 @@ var errEmptyInvocationScript = errors.New("got empty invocation script from neo var errScriptDecode = errors.New("could not decode invocation script from neo node") +// implementation of error interface for NeoFS-specific errors. +type neofsError struct { + err error +} + +func (e neofsError) Error() string { + return fmt.Sprintf("neofs error: %v", e.err) +} + +// wraps NeoFS-specific error into neofsError. Arg must not be nil. +func wrapNeoFSError(err error) error { + return neofsError{err} +} + +// unwraps NeoFS-specific error if err is type of neofsError. Otherwise, returns nil. +func unwrapNeoFSError(err error) error { + if e := new(neofsError); errors.As(err, e) { + return e.err + } + + return nil +} + // Invoke invokes contract method by sending transaction into blockchain. // Supported args types: int64, string, util.Uint160, []byte and bool. func (c *Client) Invoke(contract util.Uint160, fee fixedn.Fixed8, method string, args ...interface{}) error { @@ -121,11 +144,11 @@ func (c *Client) Invoke(contract util.Uint160, fee fixedn.Fixed8, method string, } if resp.State != HaltState { - return ¬HaltStateError{state: resp.State, exception: resp.FaultException} + return wrapNeoFSError(¬HaltStateError{state: resp.State, exception: resp.FaultException}) } if len(resp.Script) == 0 { - return errEmptyInvocationScript + return wrapNeoFSError(errEmptyInvocationScript) } script := resp.Script @@ -178,7 +201,7 @@ func (c *Client) TestInvoke(contract util.Uint160, method string, args ...interf } if val.State != HaltState { - return nil, ¬HaltStateError{state: val.State, exception: val.FaultException} + return nil, wrapNeoFSError(¬HaltStateError{state: val.State, exception: val.FaultException}) } return val.Stack, nil @@ -330,6 +353,9 @@ func (c *Client) roleList(r noderoles.Role) (keys.PublicKeys, error) { return c.client.GetDesignatedByRole(r, height) } +// tries to resolve sc.Parameter from the arg. +// +// Wraps any error to neofsError. func toStackParameter(value interface{}) (sc.Parameter, error) { var result = sc.Parameter{ Value: value, @@ -380,7 +406,7 @@ func toStackParameter(value interface{}) (sc.Parameter, error) { result.Value = int64(0) } default: - return result, fmt.Errorf("chain/client: unsupported parameter %v", value) + return result, wrapNeoFSError(fmt.Errorf("chain/client: unsupported parameter %v", value)) } return result, nil diff --git a/pkg/morph/client/notary.go b/pkg/morph/client/notary.go index 8d01fa41b..f3ff02e30 100644 --- a/pkg/morph/client/notary.go +++ b/pkg/morph/client/notary.go @@ -196,12 +196,12 @@ func (c *Client) GetNotaryDeposit() (res int64, err error) { } if len(items) != 1 { - return 0, fmt.Errorf("%v: %w", notaryBalanceErrMsg, errUnexpectedItems) + return 0, wrapNeoFSError(fmt.Errorf("%v: %w", notaryBalanceErrMsg, errUnexpectedItems)) } bigIntDeposit, err := items[0].TryInteger() if err != nil { - return 0, fmt.Errorf("%v: %w", notaryBalanceErrMsg, err) + return 0, wrapNeoFSError(fmt.Errorf("%v: %w", notaryBalanceErrMsg, err)) } return bigIntDeposit.Int64(), nil @@ -307,12 +307,12 @@ func (c *Client) notaryInvoke(committee bool, contract util.Uint160, method stri // check invocation state if test.State != HaltState { - return ¬HaltStateError{state: test.State, exception: test.FaultException} + return wrapNeoFSError(¬HaltStateError{state: test.State, exception: test.FaultException}) } // if test invocation failed, then return error if len(test.Script) == 0 { - return errEmptyInvocationScript + return wrapNeoFSError(errEmptyInvocationScript) } // after test invocation we build main multisig transaction @@ -394,7 +394,8 @@ func (c *Client) notaryCosigners(ir []*keys.PublicKey, committee bool) ([]transa multisigScript, err := sc.CreateMultiSigRedeemScript(m, ir) if err != nil { - return nil, fmt.Errorf("can't create ir multisig redeem script: %w", err) + // wrap error as NeoFS-specific since the call is not related to any client + return nil, wrapNeoFSError(fmt.Errorf("can't create ir multisig redeem script: %w", err)) } s = append(s, transaction.Signer{ @@ -480,7 +481,8 @@ func (c *Client) notaryMultisigAccount(ir []*keys.PublicKey, committee bool) (*w err := multisigAccount.ConvertMultisig(m, ir) if err != nil { - return nil, fmt.Errorf("can't make inner ring multisig wallet: %w", err) + // wrap error as NeoFS-specific since the call is not related to any client + return nil, wrapNeoFSError(fmt.Errorf("can't make inner ring multisig wallet: %w", err)) } return multisigAccount, nil