*: return errors.ErrUnsupported where appropriate

Signed-off-by: Roman Khimov <roman@nspcc.ru>
This commit is contained in:
Roman Khimov 2024-08-26 21:27:09 +03:00
parent 97506fb48d
commit 6d4ebdcef3
9 changed files with 43 additions and 36 deletions

View file

@ -21,10 +21,6 @@ type TrieStore struct {
trie *Trie trie *Trie
} }
// ErrForbiddenTrieStoreOperation is returned when operation is not supposed to
// be performed over MPT-based Store.
var ErrForbiddenTrieStoreOperation = errors.New("operation is not allowed to be performed over TrieStore")
// NewTrieStore returns a new ready to use MPT-backed storage. // NewTrieStore returns a new ready to use MPT-backed storage.
func NewTrieStore(root util.Uint256, mode TrieMode, backed storage.Store) *TrieStore { func NewTrieStore(root util.Uint256, mode TrieMode, backed storage.Store) *TrieStore {
cache, ok := backed.(*storage.MemCachedStore) cache, ok := backed.(*storage.MemCachedStore)
@ -37,10 +33,11 @@ func NewTrieStore(root util.Uint256, mode TrieMode, backed storage.Store) *TrieS
} }
} }
// Get implements the Store interface. // Get implements the Store interface. It can return [errors.ErrUnsupported]
// for unsupported operations.
func (m *TrieStore) Get(key []byte) ([]byte, error) { func (m *TrieStore) Get(key []byte) ([]byte, error) {
if len(key) == 0 { if len(key) == 0 {
return nil, fmt.Errorf("%w: Get is supported only for contract storage items", ErrForbiddenTrieStoreOperation) return nil, fmt.Errorf("%w: Get is supported only for contract storage items", errors.ErrUnsupported)
} }
switch storage.KeyPrefix(key[0]) { switch storage.KeyPrefix(key[0]) {
case storage.STStorage, storage.STTempStorage: case storage.STStorage, storage.STTempStorage:
@ -51,22 +48,23 @@ func (m *TrieStore) Get(key []byte) ([]byte, error) {
} }
return res, err return res, err
default: default:
return nil, fmt.Errorf("%w: Get is supported only for contract storage items", ErrForbiddenTrieStoreOperation) return nil, fmt.Errorf("%w: Get is supported only for contract storage items", errors.ErrUnsupported)
} }
} }
// PutChangeSet implements the Store interface. // PutChangeSet implements the Store interface. It's not supported, so it
// always returns [errors.ErrUnsupported].
func (m *TrieStore) PutChangeSet(puts map[string][]byte, stor map[string][]byte) error { func (m *TrieStore) PutChangeSet(puts map[string][]byte, stor map[string][]byte) error {
// Only Get and Seek should be supported, as TrieStore is read-only and is always // Only Get and Seek should be supported, as TrieStore is read-only and is always
// should be wrapped by MemCachedStore to properly support put operations (if any). // should be wrapped by MemCachedStore to properly support put operations (if any).
return fmt.Errorf("%w: PutChangeSet is not supported", ErrForbiddenTrieStoreOperation) return fmt.Errorf("%w: PutChangeSet is not supported", errors.ErrUnsupported)
} }
// Seek implements the Store interface. // Seek implements the Store interface.
func (m *TrieStore) Seek(rng storage.SeekRange, f func(k, v []byte) bool) { func (m *TrieStore) Seek(rng storage.SeekRange, f func(k, v []byte) bool) {
prefix := storage.KeyPrefix(rng.Prefix[0]) prefix := storage.KeyPrefix(rng.Prefix[0])
if prefix != storage.STStorage && prefix != storage.STTempStorage { // Prefix is always non-empty. if prefix != storage.STStorage && prefix != storage.STTempStorage { // Prefix is always non-empty.
panic(fmt.Errorf("%w: Seek is supported only for contract storage items", ErrForbiddenTrieStoreOperation)) panic(fmt.Errorf("%w: Seek is supported only for contract storage items", errors.ErrUnsupported))
} }
prefixP := toNibbles(rng.Prefix[1:]) prefixP := toNibbles(rng.Prefix[1:])
fromP := []byte{} fromP := []byte{}
@ -113,9 +111,10 @@ func (m *TrieStore) Seek(rng storage.SeekRange, f func(k, v []byte) bool) {
} }
} }
// SeekGC implements the Store interface. // SeekGC implements the Store interface. It's not supported, so it always
// returns [errors.ErrUnsupported].
func (m *TrieStore) SeekGC(rng storage.SeekRange, keep func(k, v []byte) bool) error { func (m *TrieStore) SeekGC(rng storage.SeekRange, keep func(k, v []byte) bool) error {
return fmt.Errorf("%w: SeekGC is not supported", ErrForbiddenTrieStoreOperation) return fmt.Errorf("%w: SeekGC is not supported", errors.ErrUnsupported)
} }
// Close implements the Store interface. // Close implements the Store interface.

View file

@ -2,6 +2,7 @@ package mpt
import ( import (
"bytes" "bytes"
"errors"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage"
@ -15,10 +16,10 @@ func TestTrieStore_TestTrieOperations(t *testing.T) {
st := NewTrieStore(source.root.Hash(), ModeAll, backed) st := NewTrieStore(source.root.Hash(), ModeAll, backed)
t.Run("forbidden operations", func(t *testing.T) { t.Run("forbidden operations", func(t *testing.T) {
require.ErrorIs(t, st.SeekGC(storage.SeekRange{}, nil), ErrForbiddenTrieStoreOperation) require.ErrorIs(t, st.SeekGC(storage.SeekRange{}, nil), errors.ErrUnsupported)
_, err := st.Get([]byte{byte(storage.STTokenTransferInfo)}) _, err := st.Get([]byte{byte(storage.STTokenTransferInfo)})
require.ErrorIs(t, err, ErrForbiddenTrieStoreOperation) require.ErrorIs(t, err, errors.ErrUnsupported)
require.ErrorIs(t, st.PutChangeSet(nil, nil), ErrForbiddenTrieStoreOperation) require.ErrorIs(t, st.PutChangeSet(nil, nil), errors.ErrUnsupported)
}) })
t.Run("Get", func(t *testing.T) { t.Run("Get", func(t *testing.T) {

View file

@ -201,16 +201,16 @@ func curveHasherFromStackitem(si stackitem.Item, allowKeccak bool) (elliptic.Cur
return elliptic.P256(), hash.Sha256, nil return elliptic.P256(), hash.Sha256, nil
case int64(Secp256k1Keccak256): case int64(Secp256k1Keccak256):
if !allowKeccak { if !allowKeccak {
return nil, nil, errors.New("unsupported hash type") return nil, nil, fmt.Errorf("%w: keccak hash", errors.ErrUnsupported)
} }
return secp256k1.S256(), Keccak256, nil return secp256k1.S256(), Keccak256, nil
case int64(Secp256r1Keccak256): case int64(Secp256r1Keccak256):
if !allowKeccak { if !allowKeccak {
return nil, nil, errors.New("unsupported hash type") return nil, nil, fmt.Errorf("%w: keccak hash", errors.ErrUnsupported)
} }
return elliptic.P256(), Keccak256, nil return elliptic.P256(), Keccak256, nil
default: default:
return nil, nil, errors.New("unsupported curve/hash type") return nil, nil, fmt.Errorf("%w: unknown curve/hash", errors.ErrUnsupported)
} }
} }

View file

@ -29,8 +29,8 @@ var (
// of transaction awaiting process and no result was received yet. // of transaction awaiting process and no result was received yet.
ErrContextDone = errors.New("waiter context done") ErrContextDone = errors.New("waiter context done")
// ErrAwaitingNotSupported is returned from Wait method if Waiter instance // ErrAwaitingNotSupported is returned from Wait method if Waiter instance
// doesn't support transaction awaiting. // doesn't support transaction awaiting. It's compatible with [errors.ErrUnsupported].
ErrAwaitingNotSupported = errors.New("awaiting not supported") ErrAwaitingNotSupported = fmt.Errorf("%w: awaiting", errors.ErrUnsupported)
// ErrMissedEvent is returned when RPCEventBased closes receiver channel // ErrMissedEvent is returned when RPCEventBased closes receiver channel
// which happens if missed event was received from the RPC server. // which happens if missed event was received from the RPC server.
ErrMissedEvent = errors.New("some event was missed") ErrMissedEvent = errors.New("some event was missed")

View file

@ -14,7 +14,8 @@ import (
) )
// ExpandFuncParameterIntoScript pushes provided FuncParam parameter // ExpandFuncParameterIntoScript pushes provided FuncParam parameter
// into the given buffer. // into the given buffer. Returns [errors.ErrUnsupported] for types it can't
// process.
func ExpandFuncParameterIntoScript(script *io.BinWriter, fp FuncParam) error { func ExpandFuncParameterIntoScript(script *io.BinWriter, fp FuncParam) error {
switch fp.Type { switch fp.Type {
case smartcontract.ByteArrayType: case smartcontract.ByteArrayType:
@ -92,7 +93,7 @@ func ExpandFuncParameterIntoScript(script *io.BinWriter, fp FuncParam) error {
emit.Opcodes(script, opcode.PUSHNULL) emit.Opcodes(script, opcode.PUSHNULL)
} }
default: default:
return fmt.Errorf("parameter type %v is not supported", fp.Type) return fmt.Errorf("%w: parameter type %v", errors.ErrUnsupported, fp.Type)
} }
return script.Err return script.Err
} }

View file

@ -200,7 +200,8 @@ func (p *Parameter) UnmarshalJSON(data []byte) (err error) {
// NewParameterFromString returns a new Parameter initialized from the given // NewParameterFromString returns a new Parameter initialized from the given
// string in neo-go-specific format. It is intended to be used in user-facing // string in neo-go-specific format. It is intended to be used in user-facing
// interfaces and has some heuristics in it to simplify parameter passing. The exact // interfaces and has some heuristics in it to simplify parameter passing. The exact
// syntax is documented in the cli documentation. // syntax is documented in the cli documentation. [errors.ErrUnsupported] will be
// returned in case of unsupported parameter types.
func NewParameterFromString(in string) (*Parameter, error) { func NewParameterFromString(in string) (*Parameter, error) {
var ( var (
char rune char rune
@ -227,7 +228,7 @@ func NewParameterFromString(in string) (*Parameter, error) {
} }
// We currently do not support following types: // We currently do not support following types:
if res.Type == ArrayType || res.Type == MapType || res.Type == InteropInterfaceType || res.Type == VoidType { if res.Type == ArrayType || res.Type == MapType || res.Type == InteropInterfaceType || res.Type == VoidType {
return nil, fmt.Errorf("unsupported parameter type %s", res.Type) return nil, fmt.Errorf("%w: type %s", errors.ErrUnsupported, res.Type)
} }
buf.Reset() buf.Reset()
hadType = true hadType = true
@ -264,7 +265,8 @@ func NewParameterFromString(in string) (*Parameter, error) {
// NewParameterFromValue infers Parameter type from the value given and adjusts // NewParameterFromValue infers Parameter type from the value given and adjusts
// the value if needed. It does not copy the value if it can avoid doing so. All // the value if needed. It does not copy the value if it can avoid doing so. All
// regular integers, util.*, keys.PublicKey*, string and bool types are supported, // regular integers, util.*, keys.PublicKey*, string and bool types are supported,
// slice of byte slices is accepted and converted as well. // slice of byte slices is accepted and converted as well. [errors.ErrUnsupported]
// will be returned for types that can't be used now.
func NewParameterFromValue(value any) (Parameter, error) { func NewParameterFromValue(value any) (Parameter, error) {
var result = Parameter{ var result = Parameter{
Value: value, Value: value,
@ -373,7 +375,7 @@ func NewParameterFromValue(value any) (Parameter, error) {
case nil: case nil:
result.Type = AnyType result.Type = AnyType
default: default:
return result, fmt.Errorf("unsupported parameter %T", value) return result, fmt.Errorf("%w: %T type", errors.ErrUnsupported, value)
} }
return result, nil return result, nil
@ -396,6 +398,7 @@ func NewParametersFromValues(values ...any) ([]Parameter, error) {
// ExpandParameterToEmitable converts a parameter to a type which can be handled as // ExpandParameterToEmitable converts a parameter to a type which can be handled as
// an array item by emit.Array. It correlates with the way an RPC server handles // an array item by emit.Array. It correlates with the way an RPC server handles
// FuncParams for invoke* calls inside the request.ExpandArrayIntoScript function. // FuncParams for invoke* calls inside the request.ExpandArrayIntoScript function.
// [errors.ErrUnsupported] is returned for unsupported types.
func ExpandParameterToEmitable(param Parameter) (any, error) { func ExpandParameterToEmitable(param Parameter) (any, error) {
var err error var err error
switch t := param.Type; t { switch t := param.Type; t {
@ -410,7 +413,7 @@ func ExpandParameterToEmitable(param Parameter) (any, error) {
} }
return res, nil return res, nil
case MapType, InteropInterfaceType, UnknownType, VoidType: case MapType, InteropInterfaceType, UnknownType, VoidType:
return nil, fmt.Errorf("unsupported parameter type: %s", t.String()) return nil, fmt.Errorf("%w: %s type", errors.ErrUnsupported, t.String())
default: default:
return param.Value, nil return param.Value, nil
} }

View file

@ -787,11 +787,11 @@ func TestParameterFromValue(t *testing.T) {
}, },
{ {
value: make(map[string]int), value: make(map[string]int),
err: "unsupported parameter map[string]int", err: "unsupported operation: map[string]int type",
}, },
{ {
value: []any{1, 2, make(map[string]int)}, value: []any{1, 2, make(map[string]int)},
err: "unsupported parameter map[string]int", err: "unsupported operation: map[string]int type",
}, },
} }

View file

@ -127,6 +127,8 @@ func Array(w *io.BinWriter, es ...any) {
// - stackitem.Convertible, stackitem.Item // - stackitem.Convertible, stackitem.Item
// - nil // - nil
// - []any // - []any
//
// [errors.ErrUnsupported] is returned for unsupported types.
func Any(w *io.BinWriter, something any) { func Any(w *io.BinWriter, something any) {
switch e := something.(type) { switch e := something.(type) {
case []any: case []any:
@ -181,7 +183,7 @@ func Any(w *io.BinWriter, something any) {
StackItem(w, e) StackItem(w, e)
default: default:
if something != nil { if something != nil {
w.Err = fmt.Errorf("unsupported type: %T", e) w.Err = fmt.Errorf("%w: %T type", errors.ErrUnsupported, e)
return return
} }
Opcodes(w, opcode.PUSHNULL) Opcodes(w, opcode.PUSHNULL)
@ -199,14 +201,15 @@ func Convertible(w *io.BinWriter, c stackitem.Convertible) {
StackItem(w, si) StackItem(w, si)
} }
// StackItem emits provided stackitem.Item to the given buffer. // StackItem emits provided stackitem.Item to the given buffer. If it can't
// be emitted [errors.ErrUnsupported] is returned.
func StackItem(w *io.BinWriter, si stackitem.Item) { func StackItem(w *io.BinWriter, si stackitem.Item) {
switch t := si.Type(); t { switch t := si.Type(); t {
case stackitem.AnyT: case stackitem.AnyT:
if si.Value() == nil { if si.Value() == nil {
Opcodes(w, opcode.PUSHNULL) Opcodes(w, opcode.PUSHNULL)
} else { } else {
w.Err = fmt.Errorf("only nil value supported for %s", t) w.Err = fmt.Errorf("%w: %s can only be nil", errors.ErrUnsupported, t)
return return
} }
case stackitem.BooleanT: case stackitem.BooleanT:
@ -240,7 +243,7 @@ func StackItem(w *io.BinWriter, si stackitem.Item) {
Int(w, int64(len(arr))) Int(w, int64(len(arr)))
Opcodes(w, opcode.PACKMAP) Opcodes(w, opcode.PACKMAP)
default: default:
w.Err = fmt.Errorf("%s is unsuppoted", t) w.Err = fmt.Errorf("%w: %s type", errors.ErrUnsupported, t)
return return
} }
} }

View file

@ -547,7 +547,7 @@ func TestEmitStackitem(t *testing.T) {
for _, si := range itms { for _, si := range itms {
buf := io.NewBufBinWriter() buf := io.NewBufBinWriter()
StackItem(buf.BinWriter, si) StackItem(buf.BinWriter, si)
require.Error(t, buf.Err) require.ErrorIs(t, buf.Err, errors.ErrUnsupported)
} }
}) })
@ -555,8 +555,8 @@ func TestEmitStackitem(t *testing.T) {
buf := io.NewBufBinWriter() buf := io.NewBufBinWriter()
StackItem(buf.BinWriter, StrangeStackItem{}) StackItem(buf.BinWriter, StrangeStackItem{})
actualErr := buf.Err actualErr := buf.Err
require.Error(t, actualErr) require.ErrorIs(t, actualErr, errors.ErrUnsupported)
require.True(t, strings.Contains(actualErr.Error(), "only nil value supported"), actualErr.Error()) require.True(t, strings.Contains(actualErr.Error(), "Any can only be nil"), actualErr.Error())
}) })
} }