github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/native/std.go (about) 1 package native 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/hex" 7 "errors" 8 "math/big" 9 "strings" 10 "unicode/utf8" 11 12 "github.com/mr-tron/base58" 13 "github.com/nspcc-dev/neo-go/pkg/config" 14 "github.com/nspcc-dev/neo-go/pkg/core/dao" 15 "github.com/nspcc-dev/neo-go/pkg/core/interop" 16 "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" 17 base58neogo "github.com/nspcc-dev/neo-go/pkg/encoding/base58" 18 "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" 19 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 20 "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" 21 "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" 22 "github.com/nspcc-dev/neo-go/pkg/util/slice" 23 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 24 ) 25 26 // Std represents an StdLib contract. 27 type Std struct { 28 interop.ContractMD 29 } 30 31 const ( 32 stdContractID = -2 33 34 // stdMaxInputLength is the maximum input length for string-related methods. 35 stdMaxInputLength = 1024 36 ) 37 38 var ( 39 // ErrInvalidBase is returned when the base is invalid. 40 ErrInvalidBase = errors.New("invalid base") 41 // ErrInvalidFormat is returned when the string is not a number. 42 ErrInvalidFormat = errors.New("invalid format") 43 // ErrTooBigInput is returned when the input exceeds the size limit. 44 ErrTooBigInput = errors.New("input is too big") 45 ) 46 47 func newStd() *Std { 48 s := &Std{ContractMD: *interop.NewContractMD(nativenames.StdLib, stdContractID)} 49 defer s.BuildHFSpecificMD(s.ActiveIn()) 50 51 desc := newDescriptor("serialize", smartcontract.ByteArrayType, 52 manifest.NewParameter("item", smartcontract.AnyType)) 53 md := newMethodAndPrice(s.serialize, 1<<12, callflag.NoneFlag) 54 s.AddMethod(md, desc) 55 56 desc = newDescriptor("deserialize", smartcontract.AnyType, 57 manifest.NewParameter("data", smartcontract.ByteArrayType)) 58 md = newMethodAndPrice(s.deserialize, 1<<14, callflag.NoneFlag) 59 s.AddMethod(md, desc) 60 61 desc = newDescriptor("jsonSerialize", smartcontract.ByteArrayType, 62 manifest.NewParameter("item", smartcontract.AnyType)) 63 md = newMethodAndPrice(s.jsonSerialize, 1<<12, callflag.NoneFlag) 64 s.AddMethod(md, desc) 65 66 desc = newDescriptor("jsonDeserialize", smartcontract.AnyType, 67 manifest.NewParameter("json", smartcontract.ByteArrayType)) 68 md = newMethodAndPrice(s.jsonDeserialize, 1<<14, callflag.NoneFlag) 69 s.AddMethod(md, desc) 70 71 desc = newDescriptor("itoa", smartcontract.StringType, 72 manifest.NewParameter("value", smartcontract.IntegerType), 73 manifest.NewParameter("base", smartcontract.IntegerType)) 74 md = newMethodAndPrice(s.itoa, 1<<12, callflag.NoneFlag) 75 s.AddMethod(md, desc) 76 77 desc = newDescriptor("itoa", smartcontract.StringType, 78 manifest.NewParameter("value", smartcontract.IntegerType)) 79 md = newMethodAndPrice(s.itoa10, 1<<12, callflag.NoneFlag) 80 s.AddMethod(md, desc) 81 82 desc = newDescriptor("atoi", smartcontract.IntegerType, 83 manifest.NewParameter("value", smartcontract.StringType), 84 manifest.NewParameter("base", smartcontract.IntegerType)) 85 md = newMethodAndPrice(s.atoi, 1<<6, callflag.NoneFlag) 86 s.AddMethod(md, desc) 87 88 desc = newDescriptor("atoi", smartcontract.IntegerType, 89 manifest.NewParameter("value", smartcontract.StringType)) 90 md = newMethodAndPrice(s.atoi10, 1<<6, callflag.NoneFlag) 91 s.AddMethod(md, desc) 92 93 desc = newDescriptor("base64Encode", smartcontract.StringType, 94 manifest.NewParameter("data", smartcontract.ByteArrayType)) 95 md = newMethodAndPrice(s.base64Encode, 1<<5, callflag.NoneFlag) 96 s.AddMethod(md, desc) 97 98 desc = newDescriptor("base64Decode", smartcontract.ByteArrayType, 99 manifest.NewParameter("s", smartcontract.StringType)) 100 md = newMethodAndPrice(s.base64Decode, 1<<5, callflag.NoneFlag) 101 s.AddMethod(md, desc) 102 103 desc = newDescriptor("base58Encode", smartcontract.StringType, 104 manifest.NewParameter("data", smartcontract.ByteArrayType)) 105 md = newMethodAndPrice(s.base58Encode, 1<<13, callflag.NoneFlag) 106 s.AddMethod(md, desc) 107 108 desc = newDescriptor("base58Decode", smartcontract.ByteArrayType, 109 manifest.NewParameter("s", smartcontract.StringType)) 110 md = newMethodAndPrice(s.base58Decode, 1<<10, callflag.NoneFlag) 111 s.AddMethod(md, desc) 112 113 desc = newDescriptor("base58CheckEncode", smartcontract.StringType, 114 manifest.NewParameter("data", smartcontract.ByteArrayType)) 115 md = newMethodAndPrice(s.base58CheckEncode, 1<<16, callflag.NoneFlag) 116 s.AddMethod(md, desc) 117 118 desc = newDescriptor("base58CheckDecode", smartcontract.ByteArrayType, 119 manifest.NewParameter("s", smartcontract.StringType)) 120 md = newMethodAndPrice(s.base58CheckDecode, 1<<16, callflag.NoneFlag) 121 s.AddMethod(md, desc) 122 123 desc = newDescriptor("memoryCompare", smartcontract.IntegerType, 124 manifest.NewParameter("str1", smartcontract.ByteArrayType), 125 manifest.NewParameter("str2", smartcontract.ByteArrayType)) 126 md = newMethodAndPrice(s.memoryCompare, 1<<5, callflag.NoneFlag) 127 s.AddMethod(md, desc) 128 129 desc = newDescriptor("memorySearch", smartcontract.IntegerType, 130 manifest.NewParameter("mem", smartcontract.ByteArrayType), 131 manifest.NewParameter("value", smartcontract.ByteArrayType)) 132 md = newMethodAndPrice(s.memorySearch2, 1<<6, callflag.NoneFlag) 133 s.AddMethod(md, desc) 134 135 desc = newDescriptor("memorySearch", smartcontract.IntegerType, 136 manifest.NewParameter("mem", smartcontract.ByteArrayType), 137 manifest.NewParameter("value", smartcontract.ByteArrayType), 138 manifest.NewParameter("start", smartcontract.IntegerType)) 139 md = newMethodAndPrice(s.memorySearch3, 1<<6, callflag.NoneFlag) 140 s.AddMethod(md, desc) 141 142 desc = newDescriptor("memorySearch", smartcontract.IntegerType, 143 manifest.NewParameter("mem", smartcontract.ByteArrayType), 144 manifest.NewParameter("value", smartcontract.ByteArrayType), 145 manifest.NewParameter("start", smartcontract.IntegerType), 146 manifest.NewParameter("backward", smartcontract.BoolType)) 147 md = newMethodAndPrice(s.memorySearch4, 1<<6, callflag.NoneFlag) 148 s.AddMethod(md, desc) 149 150 desc = newDescriptor("stringSplit", smartcontract.ArrayType, 151 manifest.NewParameter("str", smartcontract.StringType), 152 manifest.NewParameter("separator", smartcontract.StringType)) 153 md = newMethodAndPrice(s.stringSplit2, 1<<8, callflag.NoneFlag) 154 s.AddMethod(md, desc) 155 156 desc = newDescriptor("stringSplit", smartcontract.ArrayType, 157 manifest.NewParameter("str", smartcontract.StringType), 158 manifest.NewParameter("separator", smartcontract.StringType), 159 manifest.NewParameter("removeEmptyEntries", smartcontract.BoolType)) 160 md = newMethodAndPrice(s.stringSplit3, 1<<8, callflag.NoneFlag) 161 s.AddMethod(md, desc) 162 163 desc = newDescriptor("strLen", smartcontract.IntegerType, 164 manifest.NewParameter("str", smartcontract.StringType)) 165 md = newMethodAndPrice(s.strLen, 1<<8, callflag.NoneFlag) 166 s.AddMethod(md, desc) 167 168 return s 169 } 170 171 func (s *Std) serialize(ic *interop.Context, args []stackitem.Item) stackitem.Item { 172 data, err := ic.DAO.GetItemCtx().Serialize(args[0], false) 173 if err != nil { 174 panic(err) 175 } 176 if len(data) > stackitem.MaxSize { 177 panic(errors.New("too big item")) 178 } 179 180 return stackitem.NewByteArray(bytes.Clone(data)) // Serialization context can be reused. 181 } 182 183 func (s *Std) deserialize(_ *interop.Context, args []stackitem.Item) stackitem.Item { 184 data, err := args[0].TryBytes() 185 if err != nil { 186 panic(err) 187 } 188 189 item, err := stackitem.Deserialize(data) 190 if err != nil { 191 panic(err) 192 } 193 194 return item 195 } 196 197 func (s *Std) jsonSerialize(_ *interop.Context, args []stackitem.Item) stackitem.Item { 198 data, err := stackitem.ToJSON(args[0]) 199 if err != nil { 200 panic(err) 201 } 202 if len(data) > stackitem.MaxSize { 203 panic(errors.New("too big item")) 204 } 205 206 return stackitem.NewByteArray(data) 207 } 208 209 func (s *Std) jsonDeserialize(ic *interop.Context, args []stackitem.Item) stackitem.Item { 210 data, err := args[0].TryBytes() 211 if err != nil { 212 panic(err) 213 } 214 215 item, err := stackitem.FromJSON(data, stackitem.MaxDeserialized, ic.IsHardforkEnabled(config.HFBasilisk)) 216 if err != nil { 217 panic(err) 218 } 219 220 return item 221 } 222 223 func (s *Std) itoa10(_ *interop.Context, args []stackitem.Item) stackitem.Item { 224 num := toBigInt(args[0]) 225 return stackitem.NewByteArray([]byte(num.Text(10))) 226 } 227 228 func (s *Std) itoa(_ *interop.Context, args []stackitem.Item) stackitem.Item { 229 num := toBigInt(args[0]) 230 base := toBigInt(args[1]) 231 if !base.IsInt64() { 232 panic(ErrInvalidBase) 233 } 234 var str string 235 switch b := base.Int64(); b { 236 case 10: 237 str = num.Text(10) 238 case 16: 239 if num.Sign() == 0 { 240 str = "0" 241 break 242 } 243 bs := bigint.ToBytes(num) 244 slice.Reverse(bs) 245 str = hex.EncodeToString(bs) 246 if pad := bs[0] & 0xF8; pad == 0 || pad == 0xF8 { 247 str = str[1:] 248 } 249 default: 250 panic(ErrInvalidBase) 251 } 252 return stackitem.NewByteArray([]byte(str)) 253 } 254 255 func (s *Std) atoi10(_ *interop.Context, args []stackitem.Item) stackitem.Item { 256 num := s.toLimitedString(args[0]) 257 res := s.atoi10Aux(num) 258 return stackitem.NewBigInteger(res) 259 } 260 261 func (s *Std) atoi10Aux(num string) *big.Int { 262 bi, ok := new(big.Int).SetString(num, 10) 263 if !ok { 264 panic(ErrInvalidFormat) 265 } 266 return bi 267 } 268 269 func (s *Std) atoi(_ *interop.Context, args []stackitem.Item) stackitem.Item { 270 num := s.toLimitedString(args[0]) 271 base := toBigInt(args[1]) 272 if !base.IsInt64() { 273 panic(ErrInvalidBase) 274 } 275 var bi *big.Int 276 switch b := base.Int64(); b { 277 case 10: 278 bi = s.atoi10Aux(num) 279 case 16: 280 changed := len(num)%2 != 0 281 if changed { 282 num = "0" + num 283 } 284 bs, err := hex.DecodeString(num) 285 if err != nil { 286 panic(ErrInvalidFormat) 287 } 288 if changed && bs[0]&0x8 != 0 { 289 bs[0] |= 0xF0 290 } 291 slice.Reverse(bs) 292 bi = bigint.FromBytes(bs) 293 default: 294 panic(ErrInvalidBase) 295 } 296 297 return stackitem.NewBigInteger(bi) 298 } 299 300 func (s *Std) base64Encode(_ *interop.Context, args []stackitem.Item) stackitem.Item { 301 src := s.toLimitedBytes(args[0]) 302 result := base64.StdEncoding.EncodeToString(src) 303 304 return stackitem.NewByteArray([]byte(result)) 305 } 306 307 func (s *Std) base64Decode(_ *interop.Context, args []stackitem.Item) stackitem.Item { 308 src := s.toLimitedString(args[0]) 309 result, err := base64.StdEncoding.DecodeString(src) 310 if err != nil { 311 panic(err) 312 } 313 314 return stackitem.NewByteArray(result) 315 } 316 317 func (s *Std) base58Encode(_ *interop.Context, args []stackitem.Item) stackitem.Item { 318 src := s.toLimitedBytes(args[0]) 319 result := base58.Encode(src) 320 321 return stackitem.NewByteArray([]byte(result)) 322 } 323 324 func (s *Std) base58Decode(_ *interop.Context, args []stackitem.Item) stackitem.Item { 325 src := s.toLimitedString(args[0]) 326 result, err := base58.Decode(src) 327 if err != nil { 328 panic(err) 329 } 330 331 return stackitem.NewByteArray(result) 332 } 333 334 func (s *Std) base58CheckEncode(_ *interop.Context, args []stackitem.Item) stackitem.Item { 335 src := s.toLimitedBytes(args[0]) 336 result := base58neogo.CheckEncode(src) 337 338 return stackitem.NewByteArray([]byte(result)) 339 } 340 341 func (s *Std) base58CheckDecode(_ *interop.Context, args []stackitem.Item) stackitem.Item { 342 src := s.toLimitedString(args[0]) 343 result, err := base58neogo.CheckDecode(src) 344 if err != nil { 345 panic(err) 346 } 347 348 return stackitem.NewByteArray(result) 349 } 350 351 func (s *Std) memoryCompare(_ *interop.Context, args []stackitem.Item) stackitem.Item { 352 s1 := s.toLimitedBytes(args[0]) 353 s2 := s.toLimitedBytes(args[1]) 354 return stackitem.NewBigInteger(big.NewInt(int64(bytes.Compare(s1, s2)))) 355 } 356 357 func (s *Std) memorySearch2(_ *interop.Context, args []stackitem.Item) stackitem.Item { 358 mem := s.toLimitedBytes(args[0]) 359 val := s.toLimitedBytes(args[1]) 360 index := s.memorySearchAux(mem, val, 0, false) 361 return stackitem.NewBigInteger(big.NewInt(int64(index))) 362 } 363 364 func (s *Std) memorySearch3(_ *interop.Context, args []stackitem.Item) stackitem.Item { 365 mem := s.toLimitedBytes(args[0]) 366 val := s.toLimitedBytes(args[1]) 367 start := toUint32(args[2]) 368 index := s.memorySearchAux(mem, val, int(start), false) 369 return stackitem.NewBigInteger(big.NewInt(int64(index))) 370 } 371 372 func (s *Std) memorySearch4(_ *interop.Context, args []stackitem.Item) stackitem.Item { 373 mem := s.toLimitedBytes(args[0]) 374 val := s.toLimitedBytes(args[1]) 375 start := toUint32(args[2]) 376 backward, err := args[3].TryBool() 377 if err != nil { 378 panic(err) 379 } 380 381 index := s.memorySearchAux(mem, val, int(start), backward) 382 return stackitem.NewBigInteger(big.NewInt(int64(index))) 383 } 384 385 func (s *Std) memorySearchAux(mem, val []byte, start int, backward bool) int { 386 if backward { 387 if start > len(mem) { // panic in case if cap(mem) > len(mem) for some reasons 388 panic("invalid start index") 389 } 390 return bytes.LastIndex(mem[:start], val) 391 } 392 393 index := bytes.Index(mem[start:], val) 394 if index < 0 { 395 return -1 396 } 397 return index + start 398 } 399 400 func (s *Std) stringSplit2(_ *interop.Context, args []stackitem.Item) stackitem.Item { 401 str := s.toLimitedString(args[0]) 402 sep := toString(args[1]) 403 return stackitem.NewArray(s.stringSplitAux(str, sep, false)) 404 } 405 406 func (s *Std) stringSplit3(_ *interop.Context, args []stackitem.Item) stackitem.Item { 407 str := s.toLimitedString(args[0]) 408 sep := toString(args[1]) 409 removeEmpty, err := args[2].TryBool() 410 if err != nil { 411 panic(err) 412 } 413 414 return stackitem.NewArray(s.stringSplitAux(str, sep, removeEmpty)) 415 } 416 417 func (s *Std) stringSplitAux(str, sep string, removeEmpty bool) []stackitem.Item { 418 var result []stackitem.Item 419 420 arr := strings.Split(str, sep) 421 for i := range arr { 422 if !removeEmpty || len(arr[i]) != 0 { 423 result = append(result, stackitem.Make(arr[i])) 424 } 425 } 426 427 return result 428 } 429 430 func (s *Std) strLen(_ *interop.Context, args []stackitem.Item) stackitem.Item { 431 str := s.toLimitedString(args[0]) 432 433 return stackitem.NewBigInteger(big.NewInt(int64(utf8.RuneCountInString(str)))) 434 } 435 436 // Metadata implements the Contract interface. 437 func (s *Std) Metadata() *interop.ContractMD { 438 return &s.ContractMD 439 } 440 441 // Initialize implements the Contract interface. 442 func (s *Std) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { 443 return nil 444 } 445 446 // InitializeCache implements the Contract interface. 447 func (s *Std) InitializeCache(blockHeight uint32, d *dao.Simple) error { 448 return nil 449 } 450 451 // OnPersist implements the Contract interface. 452 func (s *Std) OnPersist(ic *interop.Context) error { 453 return nil 454 } 455 456 // PostPersist implements the Contract interface. 457 func (s *Std) PostPersist(ic *interop.Context) error { 458 return nil 459 } 460 461 // ActiveIn implements the Contract interface. 462 func (s *Std) ActiveIn() *config.Hardfork { 463 return nil 464 } 465 466 func (s *Std) toLimitedBytes(item stackitem.Item) []byte { 467 src, err := item.TryBytes() 468 if err != nil { 469 panic(err) 470 } 471 if len(src) > stdMaxInputLength { 472 panic(ErrTooBigInput) 473 } 474 return src 475 } 476 477 func (s *Std) toLimitedString(item stackitem.Item) string { 478 src := toString(item) 479 if len(src) > stdMaxInputLength { 480 panic(ErrTooBigInput) 481 } 482 return src 483 }