mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-25 03:47:18 +00:00
unwrap: implement Exception type for better exception handling
Fix #3130. "Exception" is used for name since it's shorter and that's the name used in JSON. "VMFault" was also considered as well as "FaultException" (which mirrors result.Invoke). Signed-off-by: Roman Khimov <roman@nspcc.ru>
This commit is contained in:
parent
13020ccd02
commit
f4731eab91
2 changed files with 32 additions and 4 deletions
|
@ -25,12 +25,24 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
)
|
||||
|
||||
// Exception is a type used for VM fault messages (aka exceptions). If any of
|
||||
// unwrapper functions encounters a FAULT VM state it creates an instance of
|
||||
// this type as an error using exception string. It can be used with [errors.As]
|
||||
// to get the exact message from VM and compare with known contract-specific
|
||||
// errors.
|
||||
type Exception string
|
||||
|
||||
// ErrNoSessionID is returned from the SessionIterator when the server does not
|
||||
// have sessions enabled and does not perform automatic iterator expansion. It
|
||||
// means you have no way to get the data from returned iterators using this
|
||||
// server, other than expanding it in the VM script.
|
||||
var ErrNoSessionID = errors.New("server returned iterator ID, but no session ID")
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e Exception) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// BigInt expects correct execution (HALT state) with a single stack item
|
||||
// returned. A big.Int is extracted from this item and returned.
|
||||
func BigInt(r *result.Invoke, err error) (*big.Int, error) {
|
||||
|
@ -399,10 +411,10 @@ func checkResOK(r *result.Invoke, err error) error {
|
|||
return err
|
||||
}
|
||||
if r.State != vmstate.Halt.String() {
|
||||
return fmt.Errorf("invocation failed: %s", r.FaultException)
|
||||
return fmt.Errorf("invocation failed: %w", Exception(r.FaultException))
|
||||
}
|
||||
if r.FaultException != "" {
|
||||
return fmt.Errorf("inconsistent result, HALTed with exception: %s", r.FaultException)
|
||||
return fmt.Errorf("inconsistent result, HALTed with exception: %w", Exception(r.FaultException))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -95,6 +95,20 @@ func TestStdErrors(t *testing.T) {
|
|||
for _, f := range funcs {
|
||||
_, err := f(&result.Invoke{State: "FAULT", Stack: []stackitem.Item{stackitem.Make(42)}}, nil)
|
||||
require.Error(t, err)
|
||||
|
||||
var fault Exception
|
||||
require.True(t, errors.As(err, &fault))
|
||||
require.Equal(t, "", string(fault))
|
||||
}
|
||||
})
|
||||
t.Run("FAULT state with exception", func(t *testing.T) {
|
||||
for _, f := range funcs {
|
||||
_, err := f(&result.Invoke{State: "FAULT", FaultException: "something bad", Stack: []stackitem.Item{stackitem.Make(42)}}, nil)
|
||||
require.Error(t, err)
|
||||
|
||||
var fault Exception
|
||||
require.True(t, errors.As(err, &fault))
|
||||
require.Equal(t, "something bad", string(fault))
|
||||
}
|
||||
})
|
||||
t.Run("nothing returned", func(t *testing.T) {
|
||||
|
@ -207,10 +221,12 @@ func TestItemJSONError(t *testing.T) {
|
|||
|
||||
var received result.Invoke
|
||||
require.NoError(t, json.Unmarshal(data, &received))
|
||||
require.True(t, len(received.FaultException) != 0)
|
||||
|
||||
_, err = Item(&received, nil)
|
||||
require.True(t, len(received.FaultException) != 0)
|
||||
require.Contains(t, err.Error(), received.FaultException)
|
||||
var fault Exception
|
||||
require.True(t, errors.As(err, &fault))
|
||||
require.Equal(t, received.FaultException, string(fault))
|
||||
}
|
||||
|
||||
func TestUTF8String(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue