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 }