forked from TrueCloudLab/neoneo-go
Merge pull request #2045 from nspcc-dev/improve-stackitem-serialization
Improve stackitem package
This commit is contained in:
commit
ab480f78c5
25 changed files with 258 additions and 150 deletions
|
@ -61,7 +61,7 @@ func Notify(ic *interop.Context) error {
|
||||||
// But it has to be serializable, otherwise we either have some broken
|
// But it has to be serializable, otherwise we either have some broken
|
||||||
// (recursive) structure inside or an interop item that can't be used
|
// (recursive) structure inside or an interop item that can't be used
|
||||||
// outside of the interop subsystem anyway.
|
// outside of the interop subsystem anyway.
|
||||||
bytes, err := stackitem.SerializeItem(elem.Item())
|
bytes, err := stackitem.Serialize(elem.Item())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("bad notification: %w", err)
|
return fmt.Errorf("bad notification: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ func (s *Iterator) Value() stackitem.Item {
|
||||||
if s.opts&FindDeserialize != 0 {
|
if s.opts&FindDeserialize != 0 {
|
||||||
bs := s.m[s.index].Value.Value().([]byte)
|
bs := s.m[s.index].Value.Value().([]byte)
|
||||||
var err error
|
var err error
|
||||||
value, err = stackitem.DeserializeItem(bs)
|
value, err = stackitem.Deserialize(bs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,11 +266,11 @@ func TestStorageFind(t *testing.T) {
|
||||||
stackitem.NewByteArray([]byte("second")),
|
stackitem.NewByteArray([]byte("second")),
|
||||||
stackitem.Null{},
|
stackitem.Null{},
|
||||||
}
|
}
|
||||||
rawArr, err := stackitem.SerializeItem(stackitem.NewArray(arr))
|
rawArr, err := stackitem.Serialize(stackitem.NewArray(arr))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rawArr0, err := stackitem.SerializeItem(stackitem.NewArray(arr[:0]))
|
rawArr0, err := stackitem.Serialize(stackitem.NewArray(arr[:0]))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rawArr1, err := stackitem.SerializeItem(stackitem.NewArray(arr[:1]))
|
rawArr1, err := stackitem.Serialize(stackitem.NewArray(arr[:1]))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
skeys := [][]byte{{0x01, 0x02}, {0x02, 0x01}, {0x01, 0x01},
|
skeys := [][]byte{{0x01, 0x02}, {0x02, 0x01}, {0x01, 0x01},
|
||||||
|
|
|
@ -904,7 +904,7 @@ func (n *NEO) getAccountState(ic *interop.Context, args []stackitem.Item) stacki
|
||||||
}
|
}
|
||||||
|
|
||||||
r := io.NewBinReaderFromBuf(si)
|
r := io.NewBinReaderFromBuf(si)
|
||||||
item := stackitem.DecodeBinaryStackItem(r)
|
item := stackitem.DecodeBinary(r)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
panic(r.Err) // no errors are expected but we better be sure
|
panic(r.Err) // no errors are expected but we better be sure
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,14 +15,14 @@ type candidate struct {
|
||||||
// Bytes marshals c to byte array.
|
// Bytes marshals c to byte array.
|
||||||
func (c *candidate) Bytes() []byte {
|
func (c *candidate) Bytes() []byte {
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
stackitem.EncodeBinaryStackItem(c.toStackItem(), w.BinWriter)
|
stackitem.EncodeBinary(c.toStackItem(), w.BinWriter)
|
||||||
return w.Bytes()
|
return w.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromBytes unmarshals candidate from byte array.
|
// FromBytes unmarshals candidate from byte array.
|
||||||
func (c *candidate) FromBytes(data []byte) *candidate {
|
func (c *candidate) FromBytes(data []byte) *candidate {
|
||||||
r := io.NewBinReaderFromBuf(data)
|
r := io.NewBinReaderFromBuf(data)
|
||||||
item := stackitem.DecodeBinaryStackItem(r)
|
item := stackitem.DecodeBinary(r)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
panic(r.Err)
|
panic(r.Err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ func (k *keysWithVotes) fromStackItem(item stackitem.Item) error {
|
||||||
func (k keysWithVotes) Bytes() []byte {
|
func (k keysWithVotes) Bytes() []byte {
|
||||||
var it = k.toStackItem()
|
var it = k.toStackItem()
|
||||||
var w = io.NewBufBinWriter()
|
var w = io.NewBufBinWriter()
|
||||||
stackitem.EncodeBinaryStackItem(it, w.BinWriter)
|
stackitem.EncodeBinary(it, w.BinWriter)
|
||||||
if w.Err != nil {
|
if w.Err != nil {
|
||||||
panic(w.Err)
|
panic(w.Err)
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ func (k keysWithVotes) Bytes() []byte {
|
||||||
// DecodeBytes deserializes keys and votes slice.
|
// DecodeBytes deserializes keys and votes slice.
|
||||||
func (k *keysWithVotes) DecodeBytes(data []byte) error {
|
func (k *keysWithVotes) DecodeBytes(data []byte) error {
|
||||||
var r = io.NewBinReaderFromBuf(data)
|
var r = io.NewBinReaderFromBuf(data)
|
||||||
var it = stackitem.DecodeBinaryStackItem(r)
|
var it = stackitem.DecodeBinary(r)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return r.Err
|
return r.Err
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,7 +278,7 @@ func (o *Oracle) FinishInternal(ic *interop.Context) error {
|
||||||
})
|
})
|
||||||
|
|
||||||
r := io.NewBinReaderFromBuf(req.UserData)
|
r := io.NewBinReaderFromBuf(req.UserData)
|
||||||
userData := stackitem.DecodeBinaryStackItem(r)
|
userData := stackitem.DecodeBinary(r)
|
||||||
args := []stackitem.Item{
|
args := []stackitem.Item{
|
||||||
stackitem.Make(req.URL),
|
stackitem.Make(req.URL),
|
||||||
stackitem.Make(userData),
|
stackitem.Make(userData),
|
||||||
|
@ -358,7 +358,7 @@ func (o *Oracle) RequestInternal(ic *interop.Context, url string, filter *string
|
||||||
}
|
}
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
stackitem.EncodeBinaryStackItem(userData, w.BinWriter)
|
stackitem.EncodeBinary(userData, w.BinWriter)
|
||||||
if w.Err != nil {
|
if w.Err != nil {
|
||||||
return w.Err
|
return w.Err
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,12 +25,12 @@ func (l IDList) Bytes() []byte {
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable.
|
// EncodeBinary implements io.Serializable.
|
||||||
func (l IDList) EncodeBinary(w *io.BinWriter) {
|
func (l IDList) EncodeBinary(w *io.BinWriter) {
|
||||||
stackitem.EncodeBinaryStackItem(l.toStackItem(), w)
|
stackitem.EncodeBinary(l.toStackItem(), w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable.
|
// DecodeBinary implements io.Serializable.
|
||||||
func (l *IDList) DecodeBinary(r *io.BinReader) {
|
func (l *IDList) DecodeBinary(r *io.BinReader) {
|
||||||
item := stackitem.DecodeBinaryStackItem(r)
|
item := stackitem.DecodeBinary(r)
|
||||||
if r.Err != nil || item == nil {
|
if r.Err != nil || item == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -84,12 +84,12 @@ func (l NodeList) Bytes() []byte {
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable.
|
// EncodeBinary implements io.Serializable.
|
||||||
func (l NodeList) EncodeBinary(w *io.BinWriter) {
|
func (l NodeList) EncodeBinary(w *io.BinWriter) {
|
||||||
stackitem.EncodeBinaryStackItem(l.toStackItem(), w)
|
stackitem.EncodeBinary(l.toStackItem(), w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable.
|
// DecodeBinary implements io.Serializable.
|
||||||
func (l *NodeList) DecodeBinary(r *io.BinReader) {
|
func (l *NodeList) DecodeBinary(r *io.BinReader) {
|
||||||
item := stackitem.DecodeBinaryStackItem(r)
|
item := stackitem.DecodeBinary(r)
|
||||||
if r.Err != nil || item == nil {
|
if r.Err != nil || item == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ func getInvalidTestFunc(actual io.Serializable, value interface{}) func(t *testi
|
||||||
return func(t *testing.T) {
|
return func(t *testing.T) {
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
it := stackitem.Make(value)
|
it := stackitem.Make(value)
|
||||||
stackitem.EncodeBinaryStackItem(it, w.BinWriter)
|
stackitem.EncodeBinary(it, w.BinWriter)
|
||||||
require.NoError(t, w.Err)
|
require.NoError(t, w.Err)
|
||||||
require.Error(t, testserdes.DecodeBinary(w.Bytes(), actual))
|
require.Error(t, testserdes.DecodeBinary(w.Bytes(), actual))
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,7 @@ func newStd() *Std {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Std) serialize(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (s *Std) serialize(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
data, err := stackitem.SerializeItem(args[0])
|
data, err := stackitem.Serialize(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,7 @@ func (s *Std) deserialize(_ *interop.Context, args []stackitem.Item) stackitem.I
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
item, err := stackitem.DeserializeItem(data)
|
item, err := stackitem.Deserialize(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -276,7 +276,7 @@ func TestStdLibSerialize(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
stackitem.EncodeBinaryStackItem(stackitem.Make(42), w.BinWriter)
|
stackitem.EncodeBinary(stackitem.Make(42), w.BinWriter)
|
||||||
require.NoError(t, w.Err)
|
require.NoError(t, w.Err)
|
||||||
|
|
||||||
encoded := w.Bytes()
|
encoded := w.Bytes()
|
||||||
|
@ -368,7 +368,7 @@ func TestStdLibSerializeDeserialize(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.Run("Deserialize unknown", func(t *testing.T) {
|
t.Run("Deserialize unknown", func(t *testing.T) {
|
||||||
data, err := stackitem.SerializeItem(stackitem.NewBigInteger(big.NewInt(123)))
|
data, err := stackitem.Serialize(stackitem.NewBigInteger(big.NewInt(123)))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
data[0] = 0xFF
|
data[0] = 0xFF
|
||||||
|
|
|
@ -94,7 +94,7 @@ func TestContractDeployAndUpdateWithParameter(t *testing.T) {
|
||||||
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(res.Stack))
|
require.Equal(t, 1, len(res.Stack))
|
||||||
item, err := stackitem.DeserializeItem(res.Stack[0].Value().([]byte))
|
item, err := stackitem.Deserialize(res.Stack[0].Value().([]byte))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected := []stackitem.Item{stackitem.Make("create"), stackitem.Make(42)}
|
expected := []stackitem.Item{stackitem.Make("create"), stackitem.Make(42)}
|
||||||
require.Equal(t, stackitem.NewArray(expected), item)
|
require.Equal(t, stackitem.NewArray(expected), item)
|
||||||
|
@ -114,7 +114,7 @@ func TestContractDeployAndUpdateWithParameter(t *testing.T) {
|
||||||
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(res.Stack))
|
require.Equal(t, 1, len(res.Stack))
|
||||||
item, err := stackitem.DeserializeItem(res.Stack[0].Value().([]byte))
|
item, err := stackitem.Deserialize(res.Stack[0].Value().([]byte))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected := []stackitem.Item{stackitem.Make("update"), stackitem.Make("new data")}
|
expected := []stackitem.Item{stackitem.Make("update"), stackitem.Make("new data")}
|
||||||
require.Equal(t, stackitem.NewArray(expected), item)
|
require.Equal(t, stackitem.NewArray(expected), item)
|
||||||
|
@ -259,7 +259,7 @@ func TestContractDeploy(t *testing.T) {
|
||||||
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(res.Stack))
|
require.Equal(t, 1, len(res.Stack))
|
||||||
item, err := stackitem.DeserializeItem(res.Stack[0].Value().([]byte))
|
item, err := stackitem.Deserialize(res.Stack[0].Value().([]byte))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected := []stackitem.Item{stackitem.Make("create"), stackitem.Null{}}
|
expected := []stackitem.Item{stackitem.Make("create"), stackitem.Null{}}
|
||||||
require.Equal(t, stackitem.NewArray(expected), item)
|
require.Equal(t, stackitem.NewArray(expected), item)
|
||||||
|
@ -466,7 +466,7 @@ func TestContractUpdate(t *testing.T) {
|
||||||
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(res.Stack))
|
require.Equal(t, 1, len(res.Stack))
|
||||||
item, err := stackitem.DeserializeItem(res.Stack[0].Value().([]byte))
|
item, err := stackitem.Deserialize(res.Stack[0].Value().([]byte))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected := []stackitem.Item{stackitem.Make("update"), stackitem.Null{}}
|
expected := []stackitem.Item{stackitem.Make("update"), stackitem.Null{}}
|
||||||
require.Equal(t, stackitem.NewArray(expected), item)
|
require.Equal(t, stackitem.NewArray(expected), item)
|
||||||
|
|
|
@ -183,7 +183,7 @@ func TestOracle_Request(t *testing.T) {
|
||||||
|
|
||||||
si := ic.DAO.GetStorageItem(cs.ID, []byte("lastOracleResponse"))
|
si := ic.DAO.GetStorageItem(cs.ID, []byte("lastOracleResponse"))
|
||||||
require.NotNil(t, si)
|
require.NotNil(t, si)
|
||||||
item, err := stackitem.DeserializeItem(si)
|
item, err := stackitem.Deserialize(si)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
arr, ok := item.Value().([]stackitem.Item)
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
|
@ -37,7 +37,7 @@ type NativeContract struct {
|
||||||
|
|
||||||
// DecodeBinary implements Serializable interface.
|
// DecodeBinary implements Serializable interface.
|
||||||
func (c *Contract) DecodeBinary(r *io.BinReader) {
|
func (c *Contract) DecodeBinary(r *io.BinReader) {
|
||||||
si := stackitem.DecodeBinaryStackItem(r)
|
si := stackitem.DecodeBinary(r)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ func (c *Contract) EncodeBinary(w *io.BinWriter) {
|
||||||
w.Err = err
|
w.Err = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
stackitem.EncodeBinaryStackItem(si, w)
|
stackitem.EncodeBinary(si, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToStackItem converts state.Contract to stackitem.Item.
|
// ToStackItem converts state.Contract to stackitem.Item.
|
||||||
|
|
|
@ -58,12 +58,12 @@ func (s *NEP17BalanceState) fromStackItem(item stackitem.Item) {
|
||||||
// EncodeBinary implements io.Serializable interface.
|
// EncodeBinary implements io.Serializable interface.
|
||||||
func (s *NEP17BalanceState) EncodeBinary(w *io.BinWriter) {
|
func (s *NEP17BalanceState) EncodeBinary(w *io.BinWriter) {
|
||||||
si := s.toStackItem()
|
si := s.toStackItem()
|
||||||
stackitem.EncodeBinaryStackItem(si, w)
|
stackitem.EncodeBinary(si, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
// DecodeBinary implements io.Serializable interface.
|
||||||
func (s *NEP17BalanceState) DecodeBinary(r *io.BinReader) {
|
func (s *NEP17BalanceState) DecodeBinary(r *io.BinReader) {
|
||||||
si := stackitem.DecodeBinaryStackItem(r)
|
si := stackitem.DecodeBinary(r)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -98,12 +98,12 @@ func (s *NEOBalanceState) Bytes() []byte {
|
||||||
// EncodeBinary implements io.Serializable interface.
|
// EncodeBinary implements io.Serializable interface.
|
||||||
func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) {
|
func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) {
|
||||||
si := s.toStackItem()
|
si := s.toStackItem()
|
||||||
stackitem.EncodeBinaryStackItem(si, w)
|
stackitem.EncodeBinary(si, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
// DecodeBinary implements io.Serializable interface.
|
||||||
func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) {
|
func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) {
|
||||||
si := stackitem.DecodeBinaryStackItem(r)
|
si := stackitem.DecodeBinary(r)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,14 @@ type AppExecResult struct {
|
||||||
func (ne *NotificationEvent) EncodeBinary(w *io.BinWriter) {
|
func (ne *NotificationEvent) EncodeBinary(w *io.BinWriter) {
|
||||||
ne.ScriptHash.EncodeBinary(w)
|
ne.ScriptHash.EncodeBinary(w)
|
||||||
w.WriteString(ne.Name)
|
w.WriteString(ne.Name)
|
||||||
stackitem.EncodeBinaryStackItem(ne.Item, w)
|
stackitem.EncodeBinary(ne.Item, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements the Serializable interface.
|
// DecodeBinary implements the Serializable interface.
|
||||||
func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) {
|
func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) {
|
||||||
ne.ScriptHash.DecodeBinary(r)
|
ne.ScriptHash.DecodeBinary(r)
|
||||||
ne.Name = r.ReadString()
|
ne.Name = r.ReadString()
|
||||||
item := stackitem.DecodeBinaryStackItem(r)
|
item := stackitem.DecodeBinary(r)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) {
|
||||||
// Stack items are expected to be marshaled one by one.
|
// Stack items are expected to be marshaled one by one.
|
||||||
w.WriteVarUint(uint64(len(aer.Stack)))
|
w.WriteVarUint(uint64(len(aer.Stack)))
|
||||||
for _, it := range aer.Stack {
|
for _, it := range aer.Stack {
|
||||||
stackitem.EncodeBinaryStackItemAppExec(it, w)
|
stackitem.EncodeBinaryProtected(it, w)
|
||||||
}
|
}
|
||||||
w.WriteArray(aer.Events)
|
w.WriteArray(aer.Events)
|
||||||
w.WriteVarBytes([]byte(aer.FaultException))
|
w.WriteVarBytes([]byte(aer.FaultException))
|
||||||
|
@ -80,7 +80,7 @@ func (aer *AppExecResult) DecodeBinary(r *io.BinReader) {
|
||||||
}
|
}
|
||||||
arr := make([]stackitem.Item, sz)
|
arr := make([]stackitem.Item, sz)
|
||||||
for i := 0; i < int(sz); i++ {
|
for i := 0; i < int(sz); i++ {
|
||||||
arr[i] = stackitem.DecodeBinaryStackItemAppExec(r)
|
arr[i] = stackitem.DecodeBinaryProtected(r)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ type notificationEventAux struct {
|
||||||
func (ne NotificationEvent) MarshalJSON() ([]byte, error) {
|
func (ne NotificationEvent) MarshalJSON() ([]byte, error) {
|
||||||
item, err := stackitem.ToJSONWithTypes(ne.Item)
|
item, err := stackitem.ToJSONWithTypes(ne.Item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
item = []byte(`"error: recursive reference"`)
|
item = []byte(fmt.Sprintf(`"error: %v"`, err))
|
||||||
}
|
}
|
||||||
return json.Marshal(¬ificationEventAux{
|
return json.Marshal(¬ificationEventAux{
|
||||||
ScriptHash: ne.ScriptHash,
|
ScriptHash: ne.ScriptHash,
|
||||||
|
@ -191,16 +191,11 @@ type executionAux struct {
|
||||||
|
|
||||||
// MarshalJSON implements implements json.Marshaler interface.
|
// MarshalJSON implements implements json.Marshaler interface.
|
||||||
func (e Execution) MarshalJSON() ([]byte, error) {
|
func (e Execution) MarshalJSON() ([]byte, error) {
|
||||||
var errRecursive = []byte(`"error: recursive reference"`)
|
|
||||||
arr := make([]json.RawMessage, len(e.Stack))
|
arr := make([]json.RawMessage, len(e.Stack))
|
||||||
for i := range arr {
|
for i := range arr {
|
||||||
if e.Stack[i] == nil {
|
|
||||||
arr[i] = errRecursive
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
data, err := stackitem.ToJSONWithTypes(e.Stack[i])
|
data, err := stackitem.ToJSONWithTypes(e.Stack[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
data = errRecursive
|
data = []byte(fmt.Sprintf(`"error: %v"`, err))
|
||||||
}
|
}
|
||||||
arr[i] = data
|
arr[i] = data
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ func TestEncodeDecodeAppExecResult(t *testing.T) {
|
||||||
w.WriteB(byte(aer.Trigger))
|
w.WriteB(byte(aer.Trigger))
|
||||||
w.WriteB(byte(aer.VMState))
|
w.WriteB(byte(aer.VMState))
|
||||||
w.WriteU64LE(uint64(aer.GasConsumed))
|
w.WriteU64LE(uint64(aer.GasConsumed))
|
||||||
stackitem.EncodeBinaryStackItem(stackitem.NewBool(true), w.BinWriter)
|
stackitem.EncodeBinary(stackitem.NewBool(true), w.BinWriter)
|
||||||
require.NoError(t, w.Err)
|
require.NoError(t, w.Err)
|
||||||
require.Error(t, testserdes.DecodeBinary(w.Bytes(), new(AppExecResult)))
|
require.Error(t, testserdes.DecodeBinary(w.Bytes(), new(AppExecResult)))
|
||||||
})
|
})
|
||||||
|
@ -184,7 +184,13 @@ func TestMarshalUnmarshalJSONAppExecResult(t *testing.T) {
|
||||||
|
|
||||||
bs1, err := json.Marshal(actual)
|
bs1, err := json.Marshal(actual)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, bs, bs1)
|
require.NotEqual(t, bs, bs1) // recursive ref error vs. unserializable nil
|
||||||
|
|
||||||
|
actual2 := new(AppExecResult)
|
||||||
|
require.NoError(t, json.Unmarshal(bs, actual2))
|
||||||
|
bs2, err := json.Marshal(actual2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, bs1, bs2) // unserializable nil in both cases
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("UnmarshalJSON error", func(t *testing.T) {
|
t.Run("UnmarshalJSON error", func(t *testing.T) {
|
||||||
|
|
|
@ -30,12 +30,12 @@ func (o *OracleRequest) Bytes() []byte {
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable.
|
// EncodeBinary implements io.Serializable.
|
||||||
func (o *OracleRequest) EncodeBinary(w *io.BinWriter) {
|
func (o *OracleRequest) EncodeBinary(w *io.BinWriter) {
|
||||||
stackitem.EncodeBinaryStackItem(o.toStackItem(), w)
|
stackitem.EncodeBinary(o.toStackItem(), w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable.
|
// DecodeBinary implements io.Serializable.
|
||||||
func (o *OracleRequest) DecodeBinary(r *io.BinReader) {
|
func (o *OracleRequest) DecodeBinary(r *io.BinReader) {
|
||||||
item := stackitem.DecodeBinaryStackItem(r)
|
item := stackitem.DecodeBinary(r)
|
||||||
if r.Err != nil || item == nil {
|
if r.Err != nil || item == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ func TestOracleRequest_EncodeBinary(t *testing.T) {
|
||||||
t.Run("NotArray", func(t *testing.T) {
|
t.Run("NotArray", func(t *testing.T) {
|
||||||
w.Reset()
|
w.Reset()
|
||||||
it := stackitem.NewByteArray([]byte{})
|
it := stackitem.NewByteArray([]byte{})
|
||||||
stackitem.EncodeBinaryStackItem(it, w.BinWriter)
|
stackitem.EncodeBinary(it, w.BinWriter)
|
||||||
require.Error(t, testserdes.DecodeBinary(w.Bytes(), new(OracleRequest)))
|
require.Error(t, testserdes.DecodeBinary(w.Bytes(), new(OracleRequest)))
|
||||||
})
|
})
|
||||||
t.Run("NotStackItem", func(t *testing.T) {
|
t.Run("NotStackItem", func(t *testing.T) {
|
||||||
|
@ -57,7 +57,7 @@ func TestOracleRequest_EncodeBinary(t *testing.T) {
|
||||||
w.Reset()
|
w.Reset()
|
||||||
before := items[i]
|
before := items[i]
|
||||||
items[i] = elem
|
items[i] = elem
|
||||||
stackitem.EncodeBinaryStackItem(arrItem, w.BinWriter)
|
stackitem.EncodeBinary(arrItem, w.BinWriter)
|
||||||
items[i] = before
|
items[i] = before
|
||||||
require.Error(t, testserdes.DecodeBinary(w.Bytes(), new(OracleRequest)))
|
require.Error(t, testserdes.DecodeBinary(w.Bytes(), new(OracleRequest)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ func (r Invoke) MarshalJSON() ([]byte, error) {
|
||||||
for j := range iteratorValues {
|
for j := range iteratorValues {
|
||||||
value[j], err = stackitem.ToJSONWithTypes(iteratorValues[j])
|
value[j], err = stackitem.ToJSONWithTypes(iteratorValues[j])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
st = []byte(`"error: recursive reference"`)
|
st = []byte(fmt.Sprintf(`"error: %v"`, err))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ func (r Invoke) MarshalJSON() ([]byte, error) {
|
||||||
} else {
|
} else {
|
||||||
data, err = stackitem.ToJSONWithTypes(r.Stack[i])
|
data, err = stackitem.ToJSONWithTypes(r.Stack[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
st = []byte(`"error: recursive reference"`)
|
st = []byte(fmt.Sprintf(`"error: %v"`, err))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,10 +53,28 @@ type Item interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errInvalidConversion = errors.New("invalid conversion type")
|
// ErrInvalidConversion is returned on attempt to make an incorrect
|
||||||
errExceedingMaxComparableSize = errors.New("the operand exceeds the maximum comparable size")
|
// conversion between item types.
|
||||||
|
ErrInvalidConversion = errors.New("invalid conversion")
|
||||||
|
|
||||||
|
// ErrTooBig is returned when item exceeds some size constraints like
|
||||||
|
// maximum allowed integer value of number of elements in array. It
|
||||||
|
// can also be returned by serialization functions if resulting
|
||||||
|
// value exceeds MaxSize.
|
||||||
|
ErrTooBig = errors.New("too big")
|
||||||
|
|
||||||
|
errTooBigComparable = fmt.Errorf("%w: uncomparable", ErrTooBig)
|
||||||
|
errTooBigInteger = fmt.Errorf("%w: integer", ErrTooBig)
|
||||||
|
errTooBigKey = fmt.Errorf("%w: map key", ErrTooBig)
|
||||||
|
errTooBigSize = fmt.Errorf("%w: size", ErrTooBig)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// mkInvConversion creates conversion error with additional metadata (from and
|
||||||
|
// to types).
|
||||||
|
func mkInvConversion(from Item, to Type) error {
|
||||||
|
return fmt.Errorf("%w: %s/%s", ErrInvalidConversion, from, to)
|
||||||
|
}
|
||||||
|
|
||||||
// Make tries to make appropriate stack item from provided value.
|
// Make tries to make appropriate stack item from provided value.
|
||||||
// It will panic if it's not possible.
|
// It will panic if it's not possible.
|
||||||
func Make(v interface{}) Item {
|
func Make(v interface{}) Item {
|
||||||
|
@ -138,7 +156,7 @@ func ToString(item Item) (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if !utf8.Valid(bs) {
|
if !utf8.Valid(bs) {
|
||||||
return "", errors.New("not a valid UTF-8")
|
return "", fmt.Errorf("%w: not UTF-8", ErrInvalidValue)
|
||||||
}
|
}
|
||||||
return string(bs), nil
|
return string(bs), nil
|
||||||
}
|
}
|
||||||
|
@ -174,7 +192,7 @@ func convertPrimitive(item Item, typ Type) (Item, error) {
|
||||||
}
|
}
|
||||||
return NewBool(b), nil
|
return NewBool(b), nil
|
||||||
default:
|
default:
|
||||||
return nil, errInvalidConversion
|
return nil, mkInvConversion(item, typ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,12 +250,12 @@ func (i *Struct) TryBool() (bool, error) { return true, nil }
|
||||||
|
|
||||||
// TryBytes implements Item interface.
|
// TryBytes implements Item interface.
|
||||||
func (i *Struct) TryBytes() ([]byte, error) {
|
func (i *Struct) TryBytes() ([]byte, error) {
|
||||||
return nil, errors.New("can't convert Struct to ByteString")
|
return nil, mkInvConversion(i, ByteArrayT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryInteger implements Item interface.
|
// TryInteger implements Item interface.
|
||||||
func (i *Struct) TryInteger() (*big.Int, error) {
|
func (i *Struct) TryInteger() (*big.Int, error) {
|
||||||
return nil, errors.New("can't convert Struct to Integer")
|
return nil, mkInvConversion(i, IntegerT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals implements Item interface.
|
// Equals implements Item interface.
|
||||||
|
@ -274,7 +292,7 @@ func (i *Struct) Convert(typ Type) (Item, error) {
|
||||||
case BooleanT:
|
case BooleanT:
|
||||||
return NewBool(true), nil
|
return NewBool(true), nil
|
||||||
default:
|
default:
|
||||||
return nil, errInvalidConversion
|
return nil, mkInvConversion(i, typ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,12 +336,12 @@ func (i Null) TryBool() (bool, error) { return false, nil }
|
||||||
|
|
||||||
// TryBytes implements Item interface.
|
// TryBytes implements Item interface.
|
||||||
func (i Null) TryBytes() ([]byte, error) {
|
func (i Null) TryBytes() ([]byte, error) {
|
||||||
return nil, errors.New("can't convert Null to ByteString")
|
return nil, mkInvConversion(i, ByteArrayT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryInteger implements Item interface.
|
// TryInteger implements Item interface.
|
||||||
func (i Null) TryInteger() (*big.Int, error) {
|
func (i Null) TryInteger() (*big.Int, error) {
|
||||||
return nil, errors.New("can't convert Null to Integer")
|
return nil, mkInvConversion(i, IntegerT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals implements Item interface.
|
// Equals implements Item interface.
|
||||||
|
@ -338,7 +356,7 @@ func (i Null) Type() Type { return AnyT }
|
||||||
// Convert implements Item interface.
|
// Convert implements Item interface.
|
||||||
func (i Null) Convert(typ Type) (Item, error) {
|
func (i Null) Convert(typ Type) (Item, error) {
|
||||||
if typ == AnyT || !typ.IsValid() {
|
if typ == AnyT || !typ.IsValid() {
|
||||||
return nil, errInvalidConversion
|
return nil, mkInvConversion(i, typ)
|
||||||
}
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
@ -350,18 +368,16 @@ type BigInteger struct {
|
||||||
|
|
||||||
// NewBigInteger returns an new BigInteger object.
|
// NewBigInteger returns an new BigInteger object.
|
||||||
func NewBigInteger(value *big.Int) *BigInteger {
|
func NewBigInteger(value *big.Int) *BigInteger {
|
||||||
const tooBigErrMsg = "integer is too big"
|
|
||||||
|
|
||||||
// There are 2 cases, when `BitLen` differs from actual size:
|
// There are 2 cases, when `BitLen` differs from actual size:
|
||||||
// 1. Positive integer with highest bit on byte boundary = 1.
|
// 1. Positive integer with highest bit on byte boundary = 1.
|
||||||
// 2. Negative integer with highest bit on byte boundary = 1
|
// 2. Negative integer with highest bit on byte boundary = 1
|
||||||
// minus some value. (-0x80 -> 0x80, -0x7F -> 0x81, -0x81 -> 0x7FFF).
|
// minus some value. (-0x80 -> 0x80, -0x7F -> 0x81, -0x81 -> 0x7FFF).
|
||||||
sz := value.BitLen()
|
sz := value.BitLen()
|
||||||
if sz > MaxBigIntegerSizeBits {
|
if sz > MaxBigIntegerSizeBits {
|
||||||
panic(tooBigErrMsg)
|
panic(errTooBigInteger)
|
||||||
} else if sz == MaxBigIntegerSizeBits {
|
} else if sz == MaxBigIntegerSizeBits {
|
||||||
if value.Sign() == 1 || value.TrailingZeroBits() != MaxBigIntegerSizeBits-1 {
|
if value.Sign() == 1 || value.TrailingZeroBits() != MaxBigIntegerSizeBits-1 {
|
||||||
panic(tooBigErrMsg)
|
panic(errTooBigInteger)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &BigInteger{
|
return &BigInteger{
|
||||||
|
@ -531,7 +547,7 @@ func (i *ByteArray) String() string {
|
||||||
// TryBool implements Item interface.
|
// TryBool implements Item interface.
|
||||||
func (i *ByteArray) TryBool() (bool, error) {
|
func (i *ByteArray) TryBool() (bool, error) {
|
||||||
if len(i.value) > MaxBigIntegerSizeBits/8 {
|
if len(i.value) > MaxBigIntegerSizeBits/8 {
|
||||||
return false, errors.New("too big byte string")
|
return false, errTooBigInteger
|
||||||
}
|
}
|
||||||
for _, b := range i.value {
|
for _, b := range i.value {
|
||||||
if b != 0 {
|
if b != 0 {
|
||||||
|
@ -549,7 +565,7 @@ func (i *ByteArray) TryBytes() ([]byte, error) {
|
||||||
// TryInteger implements Item interface.
|
// TryInteger implements Item interface.
|
||||||
func (i *ByteArray) TryInteger() (*big.Int, error) {
|
func (i *ByteArray) TryInteger() (*big.Int, error) {
|
||||||
if len(i.value) > MaxBigIntegerSizeBits/8 {
|
if len(i.value) > MaxBigIntegerSizeBits/8 {
|
||||||
return nil, errors.New("integer is too big")
|
return nil, errTooBigInteger
|
||||||
}
|
}
|
||||||
return bigint.FromBytes(i.value), nil
|
return bigint.FromBytes(i.value), nil
|
||||||
}
|
}
|
||||||
|
@ -557,7 +573,7 @@ func (i *ByteArray) TryInteger() (*big.Int, error) {
|
||||||
// Equals implements Item interface.
|
// Equals implements Item interface.
|
||||||
func (i *ByteArray) Equals(s Item) bool {
|
func (i *ByteArray) Equals(s Item) bool {
|
||||||
if len(i.value) > MaxByteArrayComparableSize {
|
if len(i.value) > MaxByteArrayComparableSize {
|
||||||
panic(errExceedingMaxComparableSize)
|
panic(errTooBigComparable)
|
||||||
}
|
}
|
||||||
if i == s {
|
if i == s {
|
||||||
return true
|
return true
|
||||||
|
@ -569,7 +585,7 @@ func (i *ByteArray) Equals(s Item) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if len(val.value) > MaxByteArrayComparableSize {
|
if len(val.value) > MaxByteArrayComparableSize {
|
||||||
panic(errExceedingMaxComparableSize)
|
panic(errTooBigComparable)
|
||||||
}
|
}
|
||||||
return bytes.Equal(i.value, val.value)
|
return bytes.Equal(i.value, val.value)
|
||||||
}
|
}
|
||||||
|
@ -641,12 +657,12 @@ func (i *Array) TryBool() (bool, error) { return true, nil }
|
||||||
|
|
||||||
// TryBytes implements Item interface.
|
// TryBytes implements Item interface.
|
||||||
func (i *Array) TryBytes() ([]byte, error) {
|
func (i *Array) TryBytes() ([]byte, error) {
|
||||||
return nil, errors.New("can't convert Array to ByteString")
|
return nil, mkInvConversion(i, ByteArrayT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryInteger implements Item interface.
|
// TryInteger implements Item interface.
|
||||||
func (i *Array) TryInteger() (*big.Int, error) {
|
func (i *Array) TryInteger() (*big.Int, error) {
|
||||||
return nil, errors.New("can't convert Array to Integer")
|
return nil, mkInvConversion(i, IntegerT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals implements Item interface.
|
// Equals implements Item interface.
|
||||||
|
@ -675,7 +691,7 @@ func (i *Array) Convert(typ Type) (Item, error) {
|
||||||
case BooleanT:
|
case BooleanT:
|
||||||
return NewBool(true), nil
|
return NewBool(true), nil
|
||||||
default:
|
default:
|
||||||
return nil, errInvalidConversion
|
return nil, mkInvConversion(i, typ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -731,12 +747,12 @@ func (i *Map) TryBool() (bool, error) { return true, nil }
|
||||||
|
|
||||||
// TryBytes implements Item interface.
|
// TryBytes implements Item interface.
|
||||||
func (i *Map) TryBytes() ([]byte, error) {
|
func (i *Map) TryBytes() ([]byte, error) {
|
||||||
return nil, errors.New("can't convert Map to ByteString")
|
return nil, mkInvConversion(i, ByteArrayT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryInteger implements Item interface.
|
// TryInteger implements Item interface.
|
||||||
func (i *Map) TryInteger() (*big.Int, error) {
|
func (i *Map) TryInteger() (*big.Int, error) {
|
||||||
return nil, errors.New("can't convert Map to Integer")
|
return nil, mkInvConversion(i, IntegerT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals implements Item interface.
|
// Equals implements Item interface.
|
||||||
|
@ -780,7 +796,7 @@ func (i *Map) Convert(typ Type) (Item, error) {
|
||||||
case BooleanT:
|
case BooleanT:
|
||||||
return NewBool(true), nil
|
return NewBool(true), nil
|
||||||
default:
|
default:
|
||||||
return nil, errInvalidConversion
|
return nil, mkInvConversion(i, typ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -812,11 +828,11 @@ func IsValidMapKey(key Item) error {
|
||||||
case *ByteArray:
|
case *ByteArray:
|
||||||
size := len(key.Value().([]byte))
|
size := len(key.Value().([]byte))
|
||||||
if size > MaxKeySize {
|
if size > MaxKeySize {
|
||||||
return fmt.Errorf("invalid map key size: %d", size)
|
return errTooBigKey
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid map key of type %s", key.Type())
|
return fmt.Errorf("%w: %s map key", ErrInvalidType, key.Type())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -853,12 +869,12 @@ func (i *Interop) TryBool() (bool, error) { return true, nil }
|
||||||
|
|
||||||
// TryBytes implements Item interface.
|
// TryBytes implements Item interface.
|
||||||
func (i *Interop) TryBytes() ([]byte, error) {
|
func (i *Interop) TryBytes() ([]byte, error) {
|
||||||
return nil, errors.New("can't convert Interop to ByteString")
|
return nil, mkInvConversion(i, ByteArrayT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryInteger implements Item interface.
|
// TryInteger implements Item interface.
|
||||||
func (i *Interop) TryInteger() (*big.Int, error) {
|
func (i *Interop) TryInteger() (*big.Int, error) {
|
||||||
return nil, errors.New("can't convert Interop to Integer")
|
return nil, mkInvConversion(i, IntegerT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals implements Item interface.
|
// Equals implements Item interface.
|
||||||
|
@ -883,7 +899,7 @@ func (i *Interop) Convert(typ Type) (Item, error) {
|
||||||
case BooleanT:
|
case BooleanT:
|
||||||
return NewBool(true), nil
|
return NewBool(true), nil
|
||||||
default:
|
default:
|
||||||
return nil, errInvalidConversion
|
return nil, mkInvConversion(i, typ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -946,12 +962,12 @@ func (p *Pointer) TryBool() (bool, error) {
|
||||||
|
|
||||||
// TryBytes implements Item interface.
|
// TryBytes implements Item interface.
|
||||||
func (p *Pointer) TryBytes() ([]byte, error) {
|
func (p *Pointer) TryBytes() ([]byte, error) {
|
||||||
return nil, errors.New("can't convert Pointer to ByteString")
|
return nil, mkInvConversion(p, ByteArrayT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryInteger implements Item interface.
|
// TryInteger implements Item interface.
|
||||||
func (p *Pointer) TryInteger() (*big.Int, error) {
|
func (p *Pointer) TryInteger() (*big.Int, error) {
|
||||||
return nil, errors.New("can't convert Pointer to Integer")
|
return nil, mkInvConversion(p, IntegerT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals implements Item interface.
|
// Equals implements Item interface.
|
||||||
|
@ -976,7 +992,7 @@ func (p *Pointer) Convert(typ Type) (Item, error) {
|
||||||
case BooleanT:
|
case BooleanT:
|
||||||
return NewBool(true), nil
|
return NewBool(true), nil
|
||||||
default:
|
default:
|
||||||
return nil, errInvalidConversion
|
return nil, mkInvConversion(p, typ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1024,7 +1040,7 @@ func (i *Buffer) TryBytes() ([]byte, error) {
|
||||||
|
|
||||||
// TryInteger implements Item interface.
|
// TryInteger implements Item interface.
|
||||||
func (i *Buffer) TryInteger() (*big.Int, error) {
|
func (i *Buffer) TryInteger() (*big.Int, error) {
|
||||||
return nil, errors.New("can't convert Buffer to Integer")
|
return nil, mkInvConversion(i, IntegerT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals implements Item interface.
|
// Equals implements Item interface.
|
||||||
|
@ -1058,11 +1074,11 @@ func (i *Buffer) Convert(typ Type) (Item, error) {
|
||||||
return NewByteArray(val), nil
|
return NewByteArray(val), nil
|
||||||
case IntegerT:
|
case IntegerT:
|
||||||
if len(i.value) > MaxBigIntegerSizeBits/8 {
|
if len(i.value) > MaxBigIntegerSizeBits/8 {
|
||||||
return nil, errInvalidConversion
|
return nil, errTooBigInteger
|
||||||
}
|
}
|
||||||
return NewBigInteger(bigint.FromBytes(i.value)), nil
|
return NewBigInteger(bigint.FromBytes(i.value)), nil
|
||||||
default:
|
default:
|
||||||
return nil, errInvalidConversion
|
return nil, mkInvConversion(i, typ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,16 @@ type decoder struct {
|
||||||
// MaxAllowedInteger is the maximum integer allowed to be encoded.
|
// MaxAllowedInteger is the maximum integer allowed to be encoded.
|
||||||
const MaxAllowedInteger = 2<<53 - 1
|
const MaxAllowedInteger = 2<<53 - 1
|
||||||
|
|
||||||
// maxJSONDepth is a maximum allowed depth-level of decoded JSON.
|
// MaxJSONDepth is the maximum allowed nesting level of encoded/decoded JSON.
|
||||||
const maxJSONDepth = 10
|
const MaxJSONDepth = 10
|
||||||
|
|
||||||
|
// ErrInvalidValue is returned when item value doesn't fit some constraints
|
||||||
|
// during serialization or deserialization.
|
||||||
|
var ErrInvalidValue = errors.New("invalid value")
|
||||||
|
|
||||||
|
// ErrTooDeep is returned when JSON encoder/decoder goes beyond MaxJSONDepth in
|
||||||
|
// its processing.
|
||||||
|
var ErrTooDeep = errors.New("too deep")
|
||||||
|
|
||||||
// ToJSON encodes Item to JSON.
|
// ToJSON encodes Item to JSON.
|
||||||
// It behaves as following:
|
// It behaves as following:
|
||||||
|
@ -48,7 +56,7 @@ func toJSON(buf *io.BufBinWriter, item Item) {
|
||||||
if w.Err != nil {
|
if w.Err != nil {
|
||||||
return
|
return
|
||||||
} else if buf.Len() > MaxSize {
|
} else if buf.Len() > MaxSize {
|
||||||
w.Err = errors.New("item is too big")
|
w.Err = errTooBigSize
|
||||||
}
|
}
|
||||||
switch it := item.(type) {
|
switch it := item.(type) {
|
||||||
case *Array, *Struct:
|
case *Array, *Struct:
|
||||||
|
@ -76,7 +84,7 @@ func toJSON(buf *io.BufBinWriter, item Item) {
|
||||||
w.WriteB('}')
|
w.WriteB('}')
|
||||||
case *BigInteger:
|
case *BigInteger:
|
||||||
if it.value.CmpAbs(big.NewInt(MaxAllowedInteger)) == 1 {
|
if it.value.CmpAbs(big.NewInt(MaxAllowedInteger)) == 1 {
|
||||||
w.Err = errors.New("too big integer")
|
w.Err = fmt.Errorf("%w (MaxAllowedInteger)", ErrInvalidValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteBytes([]byte(it.value.String()))
|
w.WriteBytes([]byte(it.value.String()))
|
||||||
|
@ -91,11 +99,11 @@ func toJSON(buf *io.BufBinWriter, item Item) {
|
||||||
case Null:
|
case Null:
|
||||||
w.WriteBytes([]byte("null"))
|
w.WriteBytes([]byte("null"))
|
||||||
default:
|
default:
|
||||||
w.Err = fmt.Errorf("invalid item: %s", it.String())
|
w.Err = fmt.Errorf("%w: %s", ErrUnserializable, it.String())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if w.Err == nil && buf.Len() > MaxSize {
|
if w.Err == nil && buf.Len() > MaxSize {
|
||||||
w.Err = errors.New("item is too big")
|
w.Err = errTooBigSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +139,7 @@ func FromJSON(data []byte) (Item, error) {
|
||||||
if item, err := d.decode(); err != nil {
|
if item, err := d.decode(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if _, err := d.Token(); err != gio.EOF {
|
} else if _, err := d.Token(); err != gio.EOF {
|
||||||
return nil, errors.New("unexpected items")
|
return nil, fmt.Errorf("%w: unexpected items", ErrInvalidValue)
|
||||||
} else {
|
} else {
|
||||||
return item, nil
|
return item, nil
|
||||||
}
|
}
|
||||||
|
@ -146,8 +154,8 @@ func (d *decoder) decode() (Item, error) {
|
||||||
case json.Delim:
|
case json.Delim:
|
||||||
switch t {
|
switch t {
|
||||||
case json.Delim('{'), json.Delim('['):
|
case json.Delim('{'), json.Delim('['):
|
||||||
if d.depth == maxJSONDepth {
|
if d.depth == MaxJSONDepth {
|
||||||
return nil, errors.New("JSON depth limit exceeded")
|
return nil, ErrTooDeep
|
||||||
}
|
}
|
||||||
d.depth++
|
d.depth++
|
||||||
var item Item
|
var item Item
|
||||||
|
@ -167,7 +175,7 @@ func (d *decoder) decode() (Item, error) {
|
||||||
return NewByteArray([]byte(t)), nil
|
return NewByteArray([]byte(t)), nil
|
||||||
case float64:
|
case float64:
|
||||||
if math.Floor(t) != t {
|
if math.Floor(t) != t {
|
||||||
return nil, fmt.Errorf("real value is not allowed: %v", t)
|
return nil, fmt.Errorf("%w (real value for int)", ErrInvalidValue)
|
||||||
}
|
}
|
||||||
return NewBigInteger(big.NewInt(int64(t))), nil
|
return NewBigInteger(big.NewInt(int64(t))), nil
|
||||||
case bool:
|
case bool:
|
||||||
|
@ -221,15 +229,14 @@ func ToJSONWithTypes(item Item) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func toJSONWithTypes(item Item, seen map[Item]bool) (interface{}, error) {
|
func toJSONWithTypes(item Item, seen map[Item]bool) (interface{}, error) {
|
||||||
typ := item.Type()
|
if len(seen) > MaxJSONDepth {
|
||||||
result := map[string]interface{}{
|
return "", ErrTooDeep
|
||||||
"type": typ.String(),
|
|
||||||
}
|
}
|
||||||
var value interface{}
|
var value interface{}
|
||||||
switch it := item.(type) {
|
switch it := item.(type) {
|
||||||
case *Array, *Struct:
|
case *Array, *Struct:
|
||||||
if seen[item] {
|
if seen[item] {
|
||||||
return "", errors.New("recursive structures can't be serialized to json")
|
return "", ErrRecursive
|
||||||
}
|
}
|
||||||
seen[item] = true
|
seen[item] = true
|
||||||
arr := []interface{}{}
|
arr := []interface{}{}
|
||||||
|
@ -241,6 +248,7 @@ func toJSONWithTypes(item Item, seen map[Item]bool) (interface{}, error) {
|
||||||
arr = append(arr, s)
|
arr = append(arr, s)
|
||||||
}
|
}
|
||||||
value = arr
|
value = arr
|
||||||
|
delete(seen, item)
|
||||||
case *Bool:
|
case *Bool:
|
||||||
value = it.value
|
value = it.value
|
||||||
case *Buffer, *ByteArray:
|
case *Buffer, *ByteArray:
|
||||||
|
@ -249,7 +257,7 @@ func toJSONWithTypes(item Item, seen map[Item]bool) (interface{}, error) {
|
||||||
value = it.value.String()
|
value = it.value.String()
|
||||||
case *Map:
|
case *Map:
|
||||||
if seen[item] {
|
if seen[item] {
|
||||||
return "", errors.New("recursive structures can't be serialized to json")
|
return "", ErrRecursive
|
||||||
}
|
}
|
||||||
seen[item] = true
|
seen[item] = true
|
||||||
arr := []interface{}{}
|
arr := []interface{}{}
|
||||||
|
@ -266,8 +274,14 @@ func toJSONWithTypes(item Item, seen map[Item]bool) (interface{}, error) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
value = arr
|
value = arr
|
||||||
|
delete(seen, item)
|
||||||
case *Pointer:
|
case *Pointer:
|
||||||
value = it.pos
|
value = it.pos
|
||||||
|
case nil:
|
||||||
|
return "", fmt.Errorf("%w: nil", ErrUnserializable)
|
||||||
|
}
|
||||||
|
result := map[string]interface{}{
|
||||||
|
"type": item.Type().String(),
|
||||||
}
|
}
|
||||||
if value != nil {
|
if value != nil {
|
||||||
result["value"] = value
|
result["value"] = value
|
||||||
|
@ -287,6 +301,10 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func mkErrValue(err error) error {
|
||||||
|
return fmt.Errorf("%w: %v", ErrInvalidValue, err)
|
||||||
|
}
|
||||||
|
|
||||||
// FromJSONWithTypes deserializes an item from typed-json representation.
|
// FromJSONWithTypes deserializes an item from typed-json representation.
|
||||||
func FromJSONWithTypes(data []byte) (Item, error) {
|
func FromJSONWithTypes(data []byte) (Item, error) {
|
||||||
raw := new(rawItem)
|
raw := new(rawItem)
|
||||||
|
@ -295,7 +313,7 @@ func FromJSONWithTypes(data []byte) (Item, error) {
|
||||||
}
|
}
|
||||||
typ, err := FromString(raw.Type)
|
typ, err := FromString(raw.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("invalid type")
|
return nil, fmt.Errorf("%w: %v", ErrInvalidType, raw.Type)
|
||||||
}
|
}
|
||||||
switch typ {
|
switch typ {
|
||||||
case AnyT:
|
case AnyT:
|
||||||
|
@ -303,33 +321,33 @@ func FromJSONWithTypes(data []byte) (Item, error) {
|
||||||
case PointerT:
|
case PointerT:
|
||||||
var pos int
|
var pos int
|
||||||
if err := json.Unmarshal(raw.Value, &pos); err != nil {
|
if err := json.Unmarshal(raw.Value, &pos); err != nil {
|
||||||
return nil, err
|
return nil, mkErrValue(err)
|
||||||
}
|
}
|
||||||
return NewPointer(pos, nil), nil
|
return NewPointer(pos, nil), nil
|
||||||
case BooleanT:
|
case BooleanT:
|
||||||
var b bool
|
var b bool
|
||||||
if err := json.Unmarshal(raw.Value, &b); err != nil {
|
if err := json.Unmarshal(raw.Value, &b); err != nil {
|
||||||
return nil, err
|
return nil, mkErrValue(err)
|
||||||
}
|
}
|
||||||
return NewBool(b), nil
|
return NewBool(b), nil
|
||||||
case IntegerT:
|
case IntegerT:
|
||||||
var s string
|
var s string
|
||||||
if err := json.Unmarshal(raw.Value, &s); err != nil {
|
if err := json.Unmarshal(raw.Value, &s); err != nil {
|
||||||
return nil, err
|
return nil, mkErrValue(err)
|
||||||
}
|
}
|
||||||
val, ok := new(big.Int).SetString(s, 10)
|
val, ok := new(big.Int).SetString(s, 10)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("invalid integer")
|
return nil, mkErrValue(errors.New("not an integer"))
|
||||||
}
|
}
|
||||||
return NewBigInteger(val), nil
|
return NewBigInteger(val), nil
|
||||||
case ByteArrayT, BufferT:
|
case ByteArrayT, BufferT:
|
||||||
var s string
|
var s string
|
||||||
if err := json.Unmarshal(raw.Value, &s); err != nil {
|
if err := json.Unmarshal(raw.Value, &s); err != nil {
|
||||||
return nil, err
|
return nil, mkErrValue(err)
|
||||||
}
|
}
|
||||||
val, err := base64.StdEncoding.DecodeString(s)
|
val, err := base64.StdEncoding.DecodeString(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, mkErrValue(err)
|
||||||
}
|
}
|
||||||
if typ == ByteArrayT {
|
if typ == ByteArrayT {
|
||||||
return NewByteArray(val), nil
|
return NewByteArray(val), nil
|
||||||
|
@ -338,7 +356,7 @@ func FromJSONWithTypes(data []byte) (Item, error) {
|
||||||
case ArrayT, StructT:
|
case ArrayT, StructT:
|
||||||
var arr []json.RawMessage
|
var arr []json.RawMessage
|
||||||
if err := json.Unmarshal(raw.Value, &arr); err != nil {
|
if err := json.Unmarshal(raw.Value, &arr); err != nil {
|
||||||
return nil, err
|
return nil, mkErrValue(err)
|
||||||
}
|
}
|
||||||
items := make([]Item, len(arr))
|
items := make([]Item, len(arr))
|
||||||
for i := range arr {
|
for i := range arr {
|
||||||
|
@ -355,7 +373,7 @@ func FromJSONWithTypes(data []byte) (Item, error) {
|
||||||
case MapT:
|
case MapT:
|
||||||
var arr []rawMapElement
|
var arr []rawMapElement
|
||||||
if err := json.Unmarshal(raw.Value, &arr); err != nil {
|
if err := json.Unmarshal(raw.Value, &arr); err != nil {
|
||||||
return nil, err
|
return nil, mkErrValue(err)
|
||||||
}
|
}
|
||||||
m := NewMap()
|
m := NewMap()
|
||||||
for i := range arr {
|
for i := range arr {
|
||||||
|
@ -375,6 +393,6 @@ func FromJSONWithTypes(data []byte) (Item, error) {
|
||||||
case InteropT:
|
case InteropT:
|
||||||
return NewInterop(nil), nil
|
return NewInterop(nil), nil
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unexpected type")
|
return nil, fmt.Errorf("%w: %v", ErrInvalidType, typ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,28 +9,64 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SerializeItem encodes given Item into the byte slice.
|
// ErrRecursive is returned on attempts to serialize some recursive stack item
|
||||||
func SerializeItem(item Item) ([]byte, error) {
|
// (like array including an item with reference to the same array).
|
||||||
|
var ErrRecursive = errors.New("recursive item")
|
||||||
|
|
||||||
|
// ErrUnserializable is returned on attempt to serialize some item that can't
|
||||||
|
// be serialized (like Interop item or Pointer).
|
||||||
|
var ErrUnserializable = errors.New("unserializable")
|
||||||
|
|
||||||
|
// serContext is an internal serialization context.
|
||||||
|
type serContext struct {
|
||||||
|
*io.BinWriter
|
||||||
|
buf *io.BufBinWriter
|
||||||
|
allowInvalid bool
|
||||||
|
seen map[Item]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize encodes given Item into the byte slice.
|
||||||
|
func Serialize(item Item) ([]byte, error) {
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
EncodeBinaryStackItem(item, w.BinWriter)
|
sc := serContext{
|
||||||
|
BinWriter: w.BinWriter,
|
||||||
|
buf: w,
|
||||||
|
allowInvalid: false,
|
||||||
|
seen: make(map[Item]bool),
|
||||||
|
}
|
||||||
|
sc.serialize(item)
|
||||||
if w.Err != nil {
|
if w.Err != nil {
|
||||||
return nil, w.Err
|
return nil, w.Err
|
||||||
}
|
}
|
||||||
return w.Bytes(), nil
|
return w.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeBinaryStackItem encodes given Item into the given BinWriter. It's
|
// EncodeBinary encodes given Item into the given BinWriter. It's
|
||||||
// similar to io.Serializable's EncodeBinary, but works with Item
|
// similar to io.Serializable's EncodeBinary, but works with Item
|
||||||
// interface.
|
// interface.
|
||||||
func EncodeBinaryStackItem(item Item, w *io.BinWriter) {
|
func EncodeBinary(item Item, w *io.BinWriter) {
|
||||||
serializeItemTo(item, w, false, make(map[Item]bool))
|
sc := serContext{
|
||||||
|
BinWriter: w,
|
||||||
|
allowInvalid: false,
|
||||||
|
seen: make(map[Item]bool),
|
||||||
|
}
|
||||||
|
sc.serialize(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeBinaryStackItemAppExec encodes given Item into the given BinWriter. It's
|
// EncodeBinaryProtected encodes given Item into the given BinWriter. It's
|
||||||
// similar to EncodeBinaryStackItem but allows to encode interop (only type, value is lost).
|
// similar to EncodeBinary but allows to encode interop items (only type,
|
||||||
func EncodeBinaryStackItemAppExec(item Item, w *io.BinWriter) {
|
// value is lost) and doesn't return any errors in w, instead if error
|
||||||
|
// (like recursive array) is encountered it just writes special InvalidT
|
||||||
|
// type of element to w.
|
||||||
|
func EncodeBinaryProtected(item Item, w *io.BinWriter) {
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
serializeItemTo(item, bw.BinWriter, true, make(map[Item]bool))
|
sc := serContext{
|
||||||
|
BinWriter: bw.BinWriter,
|
||||||
|
buf: bw,
|
||||||
|
allowInvalid: true,
|
||||||
|
seen: make(map[Item]bool),
|
||||||
|
}
|
||||||
|
sc.serialize(item)
|
||||||
if bw.Err != nil {
|
if bw.Err != nil {
|
||||||
w.WriteBytes([]byte{byte(InvalidT)})
|
w.WriteBytes([]byte{byte(InvalidT)})
|
||||||
return
|
return
|
||||||
|
@ -38,13 +74,12 @@ func EncodeBinaryStackItemAppExec(item Item, w *io.BinWriter) {
|
||||||
w.WriteBytes(bw.Bytes())
|
w.WriteBytes(bw.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func serializeItemTo(item Item, w *io.BinWriter, allowInvalid bool, seen map[Item]bool) {
|
func (w *serContext) serialize(item Item) {
|
||||||
if seen[item] {
|
if w.Err != nil {
|
||||||
w.Err = errors.New("recursive structures can't be serialized")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if item == nil && allowInvalid {
|
if w.seen[item] {
|
||||||
w.WriteBytes([]byte{byte(InvalidT)})
|
w.Err = ErrRecursive
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,13 +97,13 @@ func serializeItemTo(item Item, w *io.BinWriter, allowInvalid bool, seen map[Ite
|
||||||
w.WriteBytes([]byte{byte(IntegerT)})
|
w.WriteBytes([]byte{byte(IntegerT)})
|
||||||
w.WriteVarBytes(bigint.ToBytes(t.Value().(*big.Int)))
|
w.WriteVarBytes(bigint.ToBytes(t.Value().(*big.Int)))
|
||||||
case *Interop:
|
case *Interop:
|
||||||
if allowInvalid {
|
if w.allowInvalid {
|
||||||
w.WriteBytes([]byte{byte(InteropT)})
|
w.WriteBytes([]byte{byte(InteropT)})
|
||||||
return
|
} else {
|
||||||
|
w.Err = fmt.Errorf("%w: Interop", ErrUnserializable)
|
||||||
}
|
}
|
||||||
w.Err = errors.New("interop item can't be serialized")
|
|
||||||
case *Array, *Struct:
|
case *Array, *Struct:
|
||||||
seen[item] = true
|
w.seen[item] = true
|
||||||
|
|
||||||
_, isArray := t.(*Array)
|
_, isArray := t.(*Array)
|
||||||
if isArray {
|
if isArray {
|
||||||
|
@ -80,47 +115,59 @@ func serializeItemTo(item Item, w *io.BinWriter, allowInvalid bool, seen map[Ite
|
||||||
arr := t.Value().([]Item)
|
arr := t.Value().([]Item)
|
||||||
w.WriteVarUint(uint64(len(arr)))
|
w.WriteVarUint(uint64(len(arr)))
|
||||||
for i := range arr {
|
for i := range arr {
|
||||||
serializeItemTo(arr[i], w, allowInvalid, seen)
|
w.serialize(arr[i])
|
||||||
}
|
}
|
||||||
|
delete(w.seen, item)
|
||||||
case *Map:
|
case *Map:
|
||||||
seen[item] = true
|
w.seen[item] = true
|
||||||
|
|
||||||
w.WriteBytes([]byte{byte(MapT)})
|
w.WriteBytes([]byte{byte(MapT)})
|
||||||
w.WriteVarUint(uint64(len(t.Value().([]MapElement))))
|
w.WriteVarUint(uint64(len(t.Value().([]MapElement))))
|
||||||
for i := range t.Value().([]MapElement) {
|
for i := range t.Value().([]MapElement) {
|
||||||
serializeItemTo(t.Value().([]MapElement)[i].Key, w, allowInvalid, seen)
|
w.serialize(t.Value().([]MapElement)[i].Key)
|
||||||
serializeItemTo(t.Value().([]MapElement)[i].Value, w, allowInvalid, seen)
|
w.serialize(t.Value().([]MapElement)[i].Value)
|
||||||
}
|
}
|
||||||
|
delete(w.seen, item)
|
||||||
case Null:
|
case Null:
|
||||||
w.WriteB(byte(AnyT))
|
w.WriteB(byte(AnyT))
|
||||||
|
case nil:
|
||||||
|
if w.allowInvalid {
|
||||||
|
w.WriteBytes([]byte{byte(InvalidT)})
|
||||||
|
} else {
|
||||||
|
w.Err = fmt.Errorf("%w: nil", ErrUnserializable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeserializeItem decodes Item from the given byte slice.
|
if w.Err == nil && w.buf != nil && w.buf.Len() > MaxSize {
|
||||||
func DeserializeItem(data []byte) (Item, error) {
|
w.Err = errTooBigSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize decodes Item from the given byte slice.
|
||||||
|
func Deserialize(data []byte) (Item, error) {
|
||||||
r := io.NewBinReaderFromBuf(data)
|
r := io.NewBinReaderFromBuf(data)
|
||||||
item := DecodeBinaryStackItem(r)
|
item := DecodeBinary(r)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return nil, r.Err
|
return nil, r.Err
|
||||||
}
|
}
|
||||||
return item, nil
|
return item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinaryStackItem decodes previously serialized Item from the given
|
// DecodeBinary decodes previously serialized Item from the given
|
||||||
// reader. It's similar to the io.Serializable's DecodeBinary(), but implemented
|
// reader. It's similar to the io.Serializable's DecodeBinary(), but implemented
|
||||||
// as a function because Item itself is an interface. Caveat: always check
|
// as a function because Item itself is an interface. Caveat: always check
|
||||||
// reader's error value before using the returned Item.
|
// reader's error value before using the returned Item.
|
||||||
func DecodeBinaryStackItem(r *io.BinReader) Item {
|
func DecodeBinary(r *io.BinReader) Item {
|
||||||
return decodeBinaryStackItem(r, false)
|
return decodeBinary(r, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinaryStackItemAppExec is similar to DecodeBinaryStackItem
|
// DecodeBinaryProtected is similar to DecodeBinary but allows Interop and
|
||||||
// but allows Interop values to be present.
|
// Invalid values to be present (making it symmetric to EncodeBinaryProtected).
|
||||||
func DecodeBinaryStackItemAppExec(r *io.BinReader) Item {
|
func DecodeBinaryProtected(r *io.BinReader) Item {
|
||||||
return decodeBinaryStackItem(r, true)
|
return decodeBinary(r, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeBinaryStackItem(r *io.BinReader, allowInvalid bool) Item {
|
func decodeBinary(r *io.BinReader, allowInvalid bool) Item {
|
||||||
var t = Type(r.ReadB())
|
var t = Type(r.ReadB())
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -144,7 +191,7 @@ func decodeBinaryStackItem(r *io.BinReader, allowInvalid bool) Item {
|
||||||
size := int(r.ReadVarUint())
|
size := int(r.ReadVarUint())
|
||||||
arr := make([]Item, size)
|
arr := make([]Item, size)
|
||||||
for i := 0; i < size; i++ {
|
for i := 0; i < size; i++ {
|
||||||
arr[i] = DecodeBinaryStackItem(r)
|
arr[i] = DecodeBinary(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
if t == ArrayT {
|
if t == ArrayT {
|
||||||
|
@ -155,8 +202,8 @@ func decodeBinaryStackItem(r *io.BinReader, allowInvalid bool) Item {
|
||||||
size := int(r.ReadVarUint())
|
size := int(r.ReadVarUint())
|
||||||
m := NewMap()
|
m := NewMap()
|
||||||
for i := 0; i < size; i++ {
|
for i := 0; i < size; i++ {
|
||||||
key := DecodeBinaryStackItem(r)
|
key := DecodeBinary(r)
|
||||||
value := DecodeBinaryStackItem(r)
|
value := DecodeBinary(r)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -174,7 +221,7 @@ func decodeBinaryStackItem(r *io.BinReader, allowInvalid bool) Item {
|
||||||
if t == InvalidT && allowInvalid {
|
if t == InvalidT && allowInvalid {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
r.Err = fmt.Errorf("unknown type: %v", t)
|
r.Err = fmt.Errorf("%w: %v", ErrInvalidType, t)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
23
pkg/vm/stackitem/serialization_test.go
Normal file
23
pkg/vm/stackitem/serialization_test.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package stackitem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSerializationMaxErr(t *testing.T) {
|
||||||
|
base := make([]byte, MaxSize/2+1)
|
||||||
|
item := Make(base)
|
||||||
|
|
||||||
|
// Pointer is unserializable, but we specifically want to catch ErrTooBig.
|
||||||
|
arr := []Item{item, item.Dup(), NewPointer(0, []byte{})}
|
||||||
|
aitem := Make(arr)
|
||||||
|
|
||||||
|
_, err := Serialize(item)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = Serialize(aitem)
|
||||||
|
require.True(t, errors.Is(err, ErrTooBig), err)
|
||||||
|
}
|
|
@ -2,6 +2,9 @@ package stackitem
|
||||||
|
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
|
// ErrInvalidType is returned on attempts to deserialize some unknown item type.
|
||||||
|
var ErrInvalidType = errors.New("invalid type")
|
||||||
|
|
||||||
// Type represents type of the stack item.
|
// Type represents type of the stack item.
|
||||||
type Type byte
|
type Type byte
|
||||||
|
|
||||||
|
@ -82,6 +85,6 @@ func FromString(s string) (Type, error) {
|
||||||
case "Interop":
|
case "Interop":
|
||||||
return InteropT, nil
|
return InteropT, nil
|
||||||
default:
|
default:
|
||||||
return 0xFF, errors.New("invalid type")
|
return 0xFF, ErrInvalidType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue