github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/vm/stackitem/serialization_test.go (about)

     1  package stackitem
     2  
     3  import (
     4  	"strconv"
     5  	"testing"
     6  
     7  	"github.com/nspcc-dev/neo-go/pkg/io"
     8  	"github.com/stretchr/testify/require"
     9  )
    10  
    11  func TestSerializationMaxErr(t *testing.T) {
    12  	base := make([]byte, MaxSize/2+1)
    13  	item := Make(base)
    14  
    15  	// Pointer is unserializable, but we specifically want to catch ErrTooBig.
    16  	arr := []Item{item, item.Dup(), NewPointer(0, []byte{})}
    17  	aitem := Make(arr)
    18  
    19  	_, err := Serialize(item)
    20  	require.NoError(t, err)
    21  
    22  	_, err = Serialize(aitem)
    23  	require.ErrorIs(t, err, ErrTooBig)
    24  }
    25  
    26  func testSerialize(t *testing.T, expectedErr error, item Item) {
    27  	testSerializeLimited(t, expectedErr, item, -1)
    28  }
    29  
    30  func testSerializeLimited(t *testing.T, expectedErr error, item Item, limit int) {
    31  	var (
    32  		data []byte
    33  		err  error
    34  	)
    35  	if limit > 0 {
    36  		data, err = SerializeLimited(item, limit)
    37  	} else {
    38  		data, err = Serialize(item)
    39  	}
    40  	if expectedErr != nil {
    41  		require.ErrorIs(t, err, expectedErr)
    42  		return
    43  	}
    44  	require.NoError(t, err)
    45  
    46  	actual, err := Deserialize(data)
    47  	require.NoError(t, err)
    48  	require.Equal(t, item, actual)
    49  }
    50  
    51  func TestSerialize(t *testing.T) {
    52  	bigByteArray := NewByteArray(make([]byte, MaxSize/2))
    53  	smallByteArray := NewByteArray(make([]byte, MaxSize/4))
    54  	zeroByteArray := NewByteArray(make([]byte, 0))
    55  	testArray := func(t *testing.T, newItem func([]Item) Item) {
    56  		arr := newItem([]Item{bigByteArray})
    57  		testSerialize(t, nil, arr)
    58  		testSerialize(t, ErrTooBig, newItem([]Item{bigByteArray, bigByteArray}))
    59  		testSerialize(t, ErrTooBig, newItem([]Item{arr, arr}))
    60  
    61  		arr.Value().([]Item)[0] = smallByteArray
    62  		testSerialize(t, nil, newItem([]Item{arr, arr}))
    63  
    64  		arr.Value().([]Item)[0] = arr
    65  		testSerialize(t, ErrRecursive, arr)
    66  
    67  		items := make([]Item, 0, MaxDeserialized-1)
    68  		for i := 0; i < MaxDeserialized-1; i++ {
    69  			items = append(items, zeroByteArray)
    70  		}
    71  		testSerialize(t, nil, newItem(items))
    72  
    73  		items = append(items, zeroByteArray)
    74  		_, err := Serialize(newItem(items))
    75  		require.ErrorIs(t, err, errTooBigElements)
    76  		data, err := SerializeLimited(newItem(items), MaxSerialized+1) // a tiny hack to check deserialization error.
    77  		require.NoError(t, err)
    78  		_, err = Deserialize(data)
    79  		require.ErrorIs(t, err, ErrTooBig)
    80  	}
    81  	t.Run("array", func(t *testing.T) {
    82  		testArray(t, func(items []Item) Item { return NewArray(items) })
    83  	})
    84  	t.Run("struct", func(t *testing.T) {
    85  		testArray(t, func(items []Item) Item { return NewStruct(items) })
    86  	})
    87  	t.Run("buffer", func(t *testing.T) {
    88  		testSerialize(t, nil, NewBuffer(make([]byte, MaxSize/2)))
    89  		testSerialize(t, errTooBigSize, NewBuffer(make([]byte, MaxSize)))
    90  	})
    91  	t.Run("invalid", func(t *testing.T) {
    92  		testSerialize(t, ErrUnserializable, NewInterop(42))
    93  		testSerialize(t, ErrUnserializable, NewPointer(42, []byte{}))
    94  		testSerialize(t, ErrUnserializable, nil)
    95  
    96  		t.Run("protected interop", func(t *testing.T) {
    97  			w := io.NewBufBinWriter()
    98  			EncodeBinaryProtected(NewInterop(42), w.BinWriter)
    99  			require.NoError(t, w.Err)
   100  
   101  			data := w.Bytes()
   102  			r := io.NewBinReaderFromBuf(data)
   103  			DecodeBinary(r)
   104  			require.Error(t, r.Err)
   105  
   106  			r = io.NewBinReaderFromBuf(data)
   107  			item := DecodeBinaryProtected(r)
   108  			require.NoError(t, r.Err)
   109  			require.IsType(t, (*Interop)(nil), item)
   110  		})
   111  		t.Run("protected pointer", func(t *testing.T) {
   112  			w := io.NewBufBinWriter()
   113  			EncodeBinaryProtected(NewPointer(42, []byte{}), w.BinWriter)
   114  			require.NoError(t, w.Err)
   115  
   116  			data := w.Bytes()
   117  			r := io.NewBinReaderFromBuf(data)
   118  			DecodeBinary(r)
   119  			require.Error(t, r.Err)
   120  
   121  			r = io.NewBinReaderFromBuf(data)
   122  			item := DecodeBinaryProtected(r)
   123  			require.NoError(t, r.Err)
   124  			require.IsType(t, (*Pointer)(nil), item)
   125  			require.Equal(t, 42, item.Value())
   126  		})
   127  		t.Run("protected nil", func(t *testing.T) {
   128  			w := io.NewBufBinWriter()
   129  			EncodeBinaryProtected(nil, w.BinWriter)
   130  			require.NoError(t, w.Err)
   131  
   132  			data := w.Bytes()
   133  			r := io.NewBinReaderFromBuf(data)
   134  			DecodeBinary(r)
   135  			require.Error(t, r.Err)
   136  
   137  			r = io.NewBinReaderFromBuf(data)
   138  			item := DecodeBinaryProtected(r)
   139  			require.NoError(t, r.Err)
   140  			require.Nil(t, item)
   141  		})
   142  	})
   143  	t.Run("bool", func(t *testing.T) {
   144  		testSerialize(t, nil, NewBool(true))
   145  		testSerialize(t, nil, NewBool(false))
   146  	})
   147  	t.Run("null", func(t *testing.T) {
   148  		testSerialize(t, nil, Null{})
   149  	})
   150  	t.Run("integer", func(t *testing.T) {
   151  		testSerialize(t, nil, Make(0xF))          // 1-byte
   152  		testSerialize(t, nil, Make(0xFAB))        // 2-byte
   153  		testSerialize(t, nil, Make(0xFABCD))      // 4-byte
   154  		testSerialize(t, nil, Make(0xFABCDEFEDC)) // 8-byte
   155  	})
   156  	t.Run("map", func(t *testing.T) {
   157  		one := Make(1)
   158  		m := NewMap()
   159  		m.Add(one, m)
   160  		testSerialize(t, ErrRecursive, m)
   161  
   162  		m.Add(one, bigByteArray)
   163  		testSerialize(t, nil, m)
   164  
   165  		m.Add(Make(2), bigByteArray)
   166  		testSerialize(t, ErrTooBig, m)
   167  
   168  		// Cover code path when result becomes too big after key encode.
   169  		m = NewMap()
   170  		m.Add(Make(0), NewByteArray(make([]byte, MaxSize-MaxKeySize)))
   171  		m.Add(NewByteArray(make([]byte, MaxKeySize)), Make(1))
   172  		testSerialize(t, ErrTooBig, m)
   173  
   174  		m = NewMap()
   175  		for i := 0; i < MaxDeserialized/2-1; i++ {
   176  			m.Add(Make(i), zeroByteArray)
   177  		}
   178  		testSerialize(t, nil, m)
   179  
   180  		for i := 0; i <= MaxDeserialized; i++ {
   181  			m.Add(Make(i), zeroByteArray)
   182  		}
   183  		_, err := Serialize(m)
   184  		require.ErrorIs(t, err, errTooBigElements)
   185  		data, err := SerializeLimited(m, (MaxSerialized+1)*2+1) // a tiny hack to check deserialization error.
   186  		require.NoError(t, err)
   187  		_, err = Deserialize(data)
   188  		require.ErrorIs(t, err, ErrTooBig)
   189  	})
   190  }
   191  
   192  func TestSerializeLimited(t *testing.T) {
   193  	const customLimit = 10
   194  
   195  	smallArray := make([]Item, customLimit-1)
   196  	for i := range smallArray {
   197  		smallArray[i] = NewBool(true)
   198  	}
   199  	bigArray := make([]Item, customLimit)
   200  	for i := range bigArray {
   201  		bigArray[i] = NewBool(true)
   202  	}
   203  	t.Run("array", func(t *testing.T) {
   204  		testSerializeLimited(t, nil, NewArray(smallArray), customLimit)
   205  		testSerializeLimited(t, errTooBigElements, NewArray(bigArray), customLimit)
   206  	})
   207  	t.Run("struct", func(t *testing.T) {
   208  		testSerializeLimited(t, nil, NewStruct(smallArray), customLimit)
   209  		testSerializeLimited(t, errTooBigElements, NewStruct(bigArray), customLimit)
   210  	})
   211  	t.Run("map", func(t *testing.T) {
   212  		smallMap := make([]MapElement, (customLimit-1)/2)
   213  		for i := range smallMap {
   214  			smallMap[i] = MapElement{
   215  				Key:   NewByteArray([]byte(strconv.Itoa(i))),
   216  				Value: NewBool(true),
   217  			}
   218  		}
   219  		bigMap := make([]MapElement, customLimit/2)
   220  		for i := range bigMap {
   221  			bigMap[i] = MapElement{
   222  				Key:   NewByteArray([]byte("key")),
   223  				Value: NewBool(true),
   224  			}
   225  		}
   226  		testSerializeLimited(t, nil, NewMapWithValue(smallMap), customLimit)
   227  		testSerializeLimited(t, errTooBigElements, NewMapWithValue(bigMap), customLimit)
   228  	})
   229  	t.Run("seen", func(t *testing.T) {
   230  		t.Run("OK", func(t *testing.T) {
   231  			tinyArray := NewArray(make([]Item, (customLimit-3)/2)) // 1 for outer array, 1+1 for two inner arrays and the rest are for arrays' elements.
   232  			for i := range tinyArray.value {
   233  				tinyArray.value[i] = NewBool(true)
   234  			}
   235  			testSerializeLimited(t, nil, NewArray([]Item{tinyArray, tinyArray}), customLimit)
   236  		})
   237  		t.Run("big", func(t *testing.T) {
   238  			tinyArray := NewArray(make([]Item, (customLimit-2)/2)) // should break on the second array serialisation.
   239  			for i := range tinyArray.value {
   240  				tinyArray.value[i] = NewBool(true)
   241  			}
   242  			testSerializeLimited(t, errTooBigElements, NewArray([]Item{tinyArray, tinyArray}), customLimit)
   243  		})
   244  	})
   245  }
   246  
   247  func TestEmptyDeserialization(t *testing.T) {
   248  	empty := []byte{}
   249  	_, err := Deserialize(empty)
   250  	require.Error(t, err)
   251  }
   252  
   253  func TestMapDeserializationError(t *testing.T) {
   254  	m := NewMap()
   255  	m.Add(Make(1), Make(1))
   256  	m.Add(Make(2), nil) // Bad value
   257  	m.Add(Make(3), Make(3))
   258  
   259  	w := io.NewBufBinWriter()
   260  	EncodeBinaryProtected(m, w.BinWriter)
   261  	require.NoError(t, w.Err)
   262  	_, err := Deserialize(w.Bytes())
   263  	require.ErrorIs(t, err, ErrInvalidType)
   264  }
   265  
   266  func TestDeserializeTooManyElements(t *testing.T) {
   267  	item := Make(0)
   268  	for i := 0; i < MaxDeserialized-1; i++ { // 1 for zero inner element.
   269  		item = Make([]Item{item})
   270  	}
   271  	data, err := Serialize(item)
   272  	require.NoError(t, err)
   273  	_, err = Deserialize(data)
   274  	require.NoError(t, err)
   275  
   276  	item = Make([]Item{item})
   277  	data, err = SerializeLimited(item, MaxSerialized+1) // tiny hack to avoid serialization error.
   278  	require.NoError(t, err)
   279  	_, err = Deserialize(data)
   280  	require.ErrorIs(t, err, ErrTooBig)
   281  }
   282  
   283  func TestDeserializeLimited(t *testing.T) {
   284  	customLimit := MaxDeserialized + 1
   285  	item := Make(0)
   286  	for i := 0; i < customLimit-1; i++ { // 1 for zero inner element.
   287  		item = Make([]Item{item})
   288  	}
   289  	data, err := SerializeLimited(item, customLimit) // tiny hack to avoid serialization error.
   290  	require.NoError(t, err)
   291  	actual, err := DeserializeLimited(data, customLimit)
   292  	require.NoError(t, err)
   293  	require.Equal(t, item, actual)
   294  
   295  	item = Make([]Item{item})
   296  	data, err = SerializeLimited(item, customLimit+1) // tiny hack to avoid serialization error.
   297  	require.NoError(t, err)
   298  	_, err = DeserializeLimited(data, customLimit)
   299  	require.Error(t, err)
   300  	require.ErrorIs(t, err, ErrTooBig)
   301  }
   302  
   303  func BenchmarkEncodeBinary(b *testing.B) {
   304  	arr := getBigArray(15)
   305  
   306  	w := io.NewBufBinWriter()
   307  
   308  	b.ResetTimer()
   309  	b.ReportAllocs()
   310  	for i := 0; i < b.N; i++ {
   311  		w.Reset()
   312  		EncodeBinary(arr, w.BinWriter)
   313  		if w.Err != nil {
   314  			b.FailNow()
   315  		}
   316  	}
   317  }
   318  
   319  func BenchmarkSerializeSimple(b *testing.B) {
   320  	s := NewStruct(nil)
   321  	s.Append(Make(100500))
   322  	s.Append(Make("1aada0032aba1ef6d1f0")) // Mimicking uint160.
   323  	for i := 0; i < b.N; i++ {
   324  		_, err := Serialize(s)
   325  		if err != nil {
   326  			b.FailNow()
   327  		}
   328  	}
   329  }