forked from TrueCloudLab/neoneo-go
Merge pull request #3438 from nspcc-dev/unwrap-fault-exception
unwrap: implement Exception type for better exception handling
This commit is contained in:
commit
2d4993a837
2 changed files with 32 additions and 4 deletions
|
@ -25,12 +25,24 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
"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
|
// ErrNoSessionID is returned from the SessionIterator when the server does not
|
||||||
// have sessions enabled and does not perform automatic iterator expansion. It
|
// 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
|
// means you have no way to get the data from returned iterators using this
|
||||||
// server, other than expanding it in the VM script.
|
// server, other than expanding it in the VM script.
|
||||||
var ErrNoSessionID = errors.New("server returned iterator ID, but no session ID")
|
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
|
// BigInt expects correct execution (HALT state) with a single stack item
|
||||||
// returned. A big.Int is extracted from this item and returned.
|
// returned. A big.Int is extracted from this item and returned.
|
||||||
func BigInt(r *result.Invoke, err error) (*big.Int, error) {
|
func BigInt(r *result.Invoke, err error) (*big.Int, error) {
|
||||||
|
@ -399,10 +411,10 @@ func checkResOK(r *result.Invoke, err error) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if r.State != vmstate.Halt.String() {
|
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 != "" {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,6 +95,20 @@ func TestStdErrors(t *testing.T) {
|
||||||
for _, f := range funcs {
|
for _, f := range funcs {
|
||||||
_, err := f(&result.Invoke{State: "FAULT", Stack: []stackitem.Item{stackitem.Make(42)}}, nil)
|
_, err := f(&result.Invoke{State: "FAULT", Stack: []stackitem.Item{stackitem.Make(42)}}, nil)
|
||||||
require.Error(t, err)
|
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) {
|
t.Run("nothing returned", func(t *testing.T) {
|
||||||
|
@ -207,10 +221,12 @@ func TestItemJSONError(t *testing.T) {
|
||||||
|
|
||||||
var received result.Invoke
|
var received result.Invoke
|
||||||
require.NoError(t, json.Unmarshal(data, &received))
|
require.NoError(t, json.Unmarshal(data, &received))
|
||||||
|
require.True(t, len(received.FaultException) != 0)
|
||||||
|
|
||||||
_, err = Item(&received, nil)
|
_, err = Item(&received, nil)
|
||||||
require.True(t, len(received.FaultException) != 0)
|
var fault Exception
|
||||||
require.Contains(t, err.Error(), received.FaultException)
|
require.True(t, errors.As(err, &fault))
|
||||||
|
require.Equal(t, received.FaultException, string(fault))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUTF8String(t *testing.T) {
|
func TestUTF8String(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue