github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/services/rpcsrv/params/param_test.go (about) 1 package params 2 3 import ( 4 "encoding/base64" 5 "encoding/hex" 6 "encoding/json" 7 "fmt" 8 "math" 9 "math/big" 10 "strings" 11 "testing" 12 13 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 14 "github.com/nspcc-dev/neo-go/pkg/encoding/address" 15 "github.com/nspcc-dev/neo-go/pkg/neorpc" 16 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 17 "github.com/nspcc-dev/neo-go/pkg/util" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 ) 21 22 func TestParam_UnmarshalJSON(t *testing.T) { 23 type testCase struct { 24 check func(t *testing.T, p *Param) 25 expectedRawMessage []byte 26 } 27 msg := `["123", 123, null, ["str2", 3], [{"type": "String", "value": "jajaja"}], 28 {"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569"}, 29 {"account": "NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag", "scopes": "Global"}, 30 [{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": "Global"}]]` 31 expected := []testCase{ 32 { 33 check: func(t *testing.T, p *Param) { 34 expectedS := "123" 35 actualS, err := p.GetStringStrict() 36 require.NoError(t, err) 37 require.Equal(t, expectedS, actualS) 38 actualS, err = p.GetString() 39 require.NoError(t, err) 40 require.Equal(t, expectedS, actualS) 41 42 expectedI := 123 43 _, err = p.GetIntStrict() 44 require.Error(t, err) 45 actualI, err := p.GetInt() 46 require.NoError(t, err) 47 require.Equal(t, expectedI, actualI) 48 49 expectedB := true 50 _, err = p.GetBooleanStrict() 51 require.Error(t, err) 52 actualB, err := p.GetBoolean() 53 require.NoError(t, err) 54 require.Equal(t, expectedB, actualB) 55 56 _, err = p.GetArray() 57 require.Error(t, err) 58 }, 59 expectedRawMessage: []byte(`"123"`), 60 }, 61 { 62 check: func(t *testing.T, p *Param) { 63 expectedS := "123" 64 _, err := p.GetStringStrict() 65 require.Error(t, err) 66 actualS, err := p.GetString() 67 require.NoError(t, err) 68 require.Equal(t, expectedS, actualS) 69 70 expectedI := 123 71 actualI, err := p.GetIntStrict() 72 require.NoError(t, err) 73 require.Equal(t, expectedI, actualI) 74 actualI, err = p.GetInt() 75 require.NoError(t, err) 76 require.Equal(t, expectedI, actualI) 77 78 expectedB := true 79 _, err = p.GetBooleanStrict() 80 require.Error(t, err) 81 actualB, err := p.GetBoolean() 82 require.NoError(t, err) 83 require.Equal(t, expectedB, actualB) 84 85 _, err = p.GetArray() 86 require.Error(t, err) 87 }, 88 expectedRawMessage: []byte(`123`), 89 }, 90 { 91 check: func(t *testing.T, p *Param) { 92 _, err := p.GetStringStrict() 93 require.Error(t, err) 94 _, err = p.GetString() 95 require.Error(t, err) 96 97 _, err = p.GetIntStrict() 98 require.Error(t, err) 99 _, err = p.GetInt() 100 require.Error(t, err) 101 102 _, err = p.GetBooleanStrict() 103 require.Error(t, err) 104 _, err = p.GetBoolean() 105 require.Error(t, err) 106 107 _, err = p.GetArray() 108 require.Error(t, err) 109 }, 110 expectedRawMessage: []byte(`null`), 111 }, 112 { 113 check: func(t *testing.T, p *Param) { 114 _, err := p.GetStringStrict() 115 require.Error(t, err) 116 _, err = p.GetString() 117 require.Error(t, err) 118 119 _, err = p.GetIntStrict() 120 require.Error(t, err) 121 _, err = p.GetInt() 122 require.Error(t, err) 123 124 _, err = p.GetBooleanStrict() 125 require.Error(t, err) 126 _, err = p.GetBoolean() 127 require.Error(t, err) 128 129 a, err := p.GetArray() 130 require.NoError(t, err) 131 require.Equal(t, []Param{ 132 {RawMessage: json.RawMessage(`"str2"`)}, 133 {RawMessage: json.RawMessage(`3`)}, 134 }, a) 135 }, 136 expectedRawMessage: []byte(`["str2", 3]`), 137 }, 138 { 139 check: func(t *testing.T, p *Param) { 140 a, err := p.GetArray() 141 require.NoError(t, err) 142 require.Equal(t, 1, len(a)) 143 fp, err := a[0].GetFuncParam() 144 require.NoError(t, err) 145 require.Equal(t, smartcontract.StringType, fp.Type) 146 strVal, err := fp.Value.GetStringStrict() 147 require.NoError(t, err) 148 require.Equal(t, "jajaja", strVal) 149 }, 150 expectedRawMessage: []byte(`[{"type": "String", "value": "jajaja"}]`), 151 }, 152 { 153 check: func(t *testing.T, p *Param) { 154 actual, err := p.GetSignerWithWitness() 155 require.NoError(t, err) 156 expectedAcc, err := util.Uint160DecodeStringLE("cadb3dc2faa3ef14a13b619c9a43124755aa2569") 157 require.NoError(t, err) 158 require.Equal(t, neorpc.SignerWithWitness{Signer: transaction.Signer{Account: expectedAcc}}, actual) 159 }, 160 expectedRawMessage: []byte(`{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569"}`), 161 }, 162 { 163 check: func(t *testing.T, p *Param) { 164 actual, err := p.GetSignerWithWitness() 165 require.NoError(t, err) 166 expectedAcc, err := address.StringToUint160("NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag") 167 require.NoError(t, err) 168 require.Equal(t, neorpc.SignerWithWitness{Signer: transaction.Signer{Account: expectedAcc, Scopes: transaction.Global}}, actual) 169 }, 170 expectedRawMessage: []byte(`{"account": "NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag", "scopes": "Global"}`), 171 }, 172 { 173 check: func(t *testing.T, p *Param) { 174 actualSigs, actualWtns, err := p.GetSignersWithWitnesses() 175 require.NoError(t, err) 176 require.Equal(t, 1, len(actualSigs)) 177 require.Equal(t, 1, len(actualWtns)) 178 expectedAcc, err := util.Uint160DecodeStringLE("cadb3dc2faa3ef14a13b619c9a43124755aa2569") 179 require.NoError(t, err) 180 require.Equal(t, transaction.Signer{Account: expectedAcc, Scopes: transaction.Global}, actualSigs[0]) 181 require.Equal(t, transaction.Witness{}, actualWtns[0]) 182 }, 183 expectedRawMessage: []byte(`[{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": "Global"}]`), 184 }, 185 } 186 187 var ps Params 188 require.NoError(t, json.Unmarshal([]byte(msg), &ps)) 189 require.Equal(t, len(expected), len(ps)) 190 for i, tc := range expected { 191 require.NotNil(t, ps[i]) 192 require.Equal(t, json.RawMessage(tc.expectedRawMessage), ps[i].RawMessage, i) 193 tc.check(t, &ps[i]) 194 } 195 } 196 197 func TestGetBigInt(t *testing.T) { 198 maxUint64 := new(big.Int).SetUint64(math.MaxUint64) 199 minInt64 := big.NewInt(math.MinInt64) 200 testCases := []struct { 201 raw string 202 expected *big.Int 203 }{ 204 {"true", big.NewInt(1)}, 205 {"false", new(big.Int)}, 206 {"42", big.NewInt(42)}, 207 {`"` + minInt64.String() + `"`, minInt64}, 208 {`"` + maxUint64.String() + `"`, maxUint64}, 209 {`"` + minInt64.String() + `000"`, new(big.Int).Mul(minInt64, big.NewInt(1000))}, 210 {`"` + maxUint64.String() + `000"`, new(big.Int).Mul(maxUint64, big.NewInt(1000))}, 211 {`"abc"`, nil}, 212 {`[]`, nil}, 213 {`null`, nil}, 214 } 215 216 for _, tc := range testCases { 217 var p Param 218 require.NoError(t, json.Unmarshal([]byte(tc.raw), &p)) 219 220 actual, err := p.GetBigInt() 221 if tc.expected == nil { 222 require.Error(t, err) 223 continue 224 } 225 require.NoError(t, err) 226 require.Equal(t, tc.expected, actual) 227 228 expected := tc.expected.Int64() 229 actualInt, err := p.GetInt() 230 if !actual.IsInt64() || int64(int(expected)) != expected { 231 require.Error(t, err) 232 } else { 233 require.NoError(t, err) 234 require.Equal(t, int(expected), actualInt) 235 } 236 } 237 } 238 239 func TestGetWitness(t *testing.T) { 240 accountHash, err := util.Uint160DecodeStringLE("cadb3dc2faa3ef14a13b619c9a43124755aa2569") 241 require.NoError(t, err) 242 addrHash, err := address.StringToUint160("NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag") 243 require.NoError(t, err) 244 245 testCases := []struct { 246 raw string 247 expected neorpc.SignerWithWitness 248 shouldFail bool 249 }{ 250 { 251 raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569"}`, 252 expected: neorpc.SignerWithWitness{ 253 Signer: transaction.Signer{ 254 Account: accountHash, 255 Scopes: transaction.None, 256 }, 257 }, 258 }, 259 { 260 raw: `{"account": "NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag", "scopes": "Global"}`, 261 expected: neorpc.SignerWithWitness{ 262 Signer: transaction.Signer{ 263 Account: addrHash, 264 Scopes: transaction.Global, 265 }, 266 }, 267 }, 268 { 269 raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": "Global"}`, 270 expected: neorpc.SignerWithWitness{ 271 Signer: transaction.Signer{ 272 Account: accountHash, 273 Scopes: transaction.Global, 274 }, 275 }, 276 }, 277 { 278 raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 128}`, 279 expected: neorpc.SignerWithWitness{ 280 Signer: transaction.Signer{ 281 Account: accountHash, 282 Scopes: transaction.Global, 283 }, 284 }, 285 }, 286 { 287 raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 0}`, 288 expected: neorpc.SignerWithWitness{ 289 Signer: transaction.Signer{ 290 Account: accountHash, 291 Scopes: transaction.None, 292 }, 293 }, 294 }, 295 { 296 raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 1}`, 297 expected: neorpc.SignerWithWitness{ 298 Signer: transaction.Signer{ 299 Account: accountHash, 300 Scopes: transaction.CalledByEntry, 301 }, 302 }, 303 }, 304 { 305 raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 17}`, 306 expected: neorpc.SignerWithWitness{ 307 Signer: transaction.Signer{ 308 Account: accountHash, 309 Scopes: transaction.CalledByEntry | transaction.CustomContracts, 310 }, 311 }, 312 }, 313 { 314 raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 178}`, 315 shouldFail: true, 316 }, 317 { 318 raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 2}`, 319 shouldFail: true, 320 }, 321 } 322 323 for _, tc := range testCases { 324 p := Param{RawMessage: json.RawMessage(tc.raw)} 325 actual, err := p.GetSignerWithWitness() 326 if tc.shouldFail { 327 require.Error(t, err, tc.raw) 328 } else { 329 require.NoError(t, err, tc.raw) 330 require.Equal(t, tc.expected, actual) 331 332 actual, err = p.GetSignerWithWitness() // valid second invocation. 333 require.NoError(t, err, tc.raw) 334 require.Equal(t, tc.expected, actual) 335 } 336 } 337 } 338 339 func TestParamGetUint256(t *testing.T) { 340 gas := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7" 341 u256, _ := util.Uint256DecodeStringLE(gas) 342 p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, gas))} 343 u, err := p.GetUint256() 344 assert.Equal(t, u256, u) 345 require.Nil(t, err) 346 347 p = Param{RawMessage: []byte(fmt.Sprintf(`"0x%s"`, gas))} 348 u, err = p.GetUint256() 349 require.NoError(t, err) 350 assert.Equal(t, u256, u) 351 352 p = Param{RawMessage: []byte(`42`)} 353 _, err = p.GetUint256() 354 require.NotNil(t, err) 355 356 p = Param{RawMessage: []byte(`"qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"`)} 357 _, err = p.GetUint256() 358 require.NotNil(t, err) 359 } 360 361 func TestParamGetUint160FromHex(t *testing.T) { 362 in := "50befd26fdf6e4d957c11e078b24ebce6291456f" 363 u160, _ := util.Uint160DecodeStringLE(in) 364 p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, in))} 365 u, err := p.GetUint160FromHex() 366 assert.Equal(t, u160, u) 367 require.Nil(t, err) 368 369 p = Param{RawMessage: []byte(`42`)} 370 _, err = p.GetUint160FromHex() 371 require.NotNil(t, err) 372 373 p = Param{RawMessage: []byte(`"wwbefd26fdf6e4d957c11e078b24ebce6291456f"`)} 374 _, err = p.GetUint160FromHex() 375 require.NotNil(t, err) 376 } 377 378 func TestParamGetUint160FromAddress(t *testing.T) { 379 in := "NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8" 380 u160, _ := address.StringToUint160(in) 381 p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, in))} 382 u, err := p.GetUint160FromAddress() 383 assert.Equal(t, u160, u) 384 require.Nil(t, err) 385 386 p = Param{RawMessage: []byte(`42`)} 387 _, err = p.GetUint160FromAddress() 388 require.NotNil(t, err) 389 390 p = Param{RawMessage: []byte(`"QK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"`)} 391 _, err = p.GetUint160FromAddress() 392 require.NotNil(t, err) 393 } 394 395 func TestParam_GetUint160FromAddressOrHex(t *testing.T) { 396 in := "NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8" 397 inHex, _ := address.StringToUint160(in) 398 399 t.Run("Address", func(t *testing.T) { 400 p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, in))} 401 u, err := p.GetUint160FromAddressOrHex() 402 require.NoError(t, err) 403 require.Equal(t, inHex, u) 404 }) 405 406 t.Run("Hex", func(t *testing.T) { 407 p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, inHex.StringLE()))} 408 u, err := p.GetUint160FromAddressOrHex() 409 require.NoError(t, err) 410 require.Equal(t, inHex, u) 411 }) 412 } 413 414 func TestParamGetFuncParam(t *testing.T) { 415 fp := FuncParam{ 416 Type: smartcontract.StringType, 417 Value: Param{RawMessage: []byte(`"jajaja"`)}, 418 } 419 p := Param{RawMessage: []byte(`{"type": "String", "value": "jajaja"}`)} 420 newfp, err := p.GetFuncParam() 421 assert.Equal(t, fp, newfp) 422 require.Nil(t, err) 423 424 p = Param{RawMessage: []byte(`42`)} 425 _, err = p.GetFuncParam() 426 require.NotNil(t, err) 427 } 428 429 func TestParamGetFuncParamPair(t *testing.T) { 430 fp := FuncParam{ 431 Type: smartcontract.MapType, 432 Value: Param{RawMessage: []byte(`[{"key": {"type": "String", "value": "key1"}, "value": {"type": "Integer", "value": 123}}, {"key": {"type": "String", "value": "key2"}, "value": {"type": "Integer", "value": 456}}]`)}, 433 } 434 p := Param{RawMessage: []byte(`{"type": "Map", "value": [{"key": {"type": "String", "value": "key1"}, "value": {"type": "Integer", "value": 123}}, {"key": {"type": "String", "value": "key2"}, "value": {"type": "Integer", "value": 456}}]}`)} 435 newfp, err := p.GetFuncParam() 436 assert.Equal(t, fp, newfp) 437 require.NoError(t, err) 438 439 kvs, err := newfp.Value.GetArray() 440 require.NoError(t, err) 441 442 p1, err := kvs[0].GetFuncParamPair() 443 require.NoError(t, err) 444 445 fp1Key := FuncParam{ 446 Type: smartcontract.StringType, 447 Value: Param{RawMessage: []byte(`"key1"`)}, 448 } 449 fp1Value := FuncParam{ 450 Type: smartcontract.IntegerType, 451 Value: Param{RawMessage: []byte(`123`)}, 452 } 453 assert.Equal(t, fp1Key, p1.Key) 454 assert.Equal(t, fp1Value, p1.Value) 455 456 p2, err := kvs[1].GetFuncParamPair() 457 require.NoError(t, err) 458 459 fp2Key := FuncParam{ 460 Type: smartcontract.StringType, 461 Value: Param{RawMessage: []byte(`"key2"`)}, 462 } 463 fp2Value := FuncParam{ 464 Type: smartcontract.IntegerType, 465 Value: Param{RawMessage: []byte(`456`)}, 466 } 467 assert.Equal(t, fp2Key, p2.Key) 468 assert.Equal(t, fp2Value, p2.Value) 469 } 470 471 func TestParamGetBytesHex(t *testing.T) { 472 in := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7" 473 inb, _ := hex.DecodeString(in) 474 p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, in))} 475 bh, err := p.GetBytesHex() 476 assert.Equal(t, inb, bh) 477 require.Nil(t, err) 478 479 p = Param{RawMessage: []byte(`42`)} 480 h, err := p.GetBytesHex() 481 assert.Equal(t, []byte{0x42}, h) // that's the way how C# works: 42 -> "42" -> []byte{0x42} 482 require.Nil(t, err) 483 484 p = Param{RawMessage: []byte(`"qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"`)} 485 _, err = p.GetBytesHex() 486 require.NotNil(t, err) 487 } 488 489 func TestParamGetBytesBase64(t *testing.T) { 490 in := "Aj4A8DoW6HB84EXrQu6A05JFFUHuUQ3BjhyL77rFTXQm" 491 inb, err := base64.StdEncoding.DecodeString(in) 492 require.NoError(t, err) 493 p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, in))} 494 bh, err := p.GetBytesBase64() 495 assert.Equal(t, inb, bh) 496 require.Nil(t, err) 497 498 p = Param{RawMessage: []byte(`42`)} 499 _, err = p.GetBytesBase64() 500 require.NotNil(t, err) 501 502 p = Param{RawMessage: []byte("@j4A8DoW6HB84EXrQu6A05JFFUHuUQ3BjhyL77rFTXQm")} 503 _, err = p.GetBytesBase64() 504 require.NotNil(t, err) 505 } 506 507 func TestParamGetSigner(t *testing.T) { 508 c := neorpc.SignerWithWitness{ 509 Signer: transaction.Signer{ 510 Account: util.Uint160{1, 2, 3, 4}, 511 Scopes: transaction.Global, 512 }, 513 Witness: transaction.Witness{ 514 515 InvocationScript: []byte{1, 2, 3}, 516 VerificationScript: []byte{1, 2, 3}, 517 }, 518 } 519 p := Param{RawMessage: []byte(`{"account": "0x0000000000000000000000000000000004030201", "scopes": "Global", "invocation": "AQID", "verification": "AQID"}`)} 520 actual, err := p.GetSignerWithWitness() 521 require.NoError(t, err) 522 require.Equal(t, c, actual) 523 524 p = Param{RawMessage: []byte(`"not a signer"`)} 525 _, err = p.GetSignerWithWitness() 526 require.Error(t, err) 527 } 528 529 func TestParamGetSigners(t *testing.T) { 530 u1 := util.Uint160{1, 2, 3, 4} 531 u2 := util.Uint160{5, 6, 7, 8} 532 t.Run("from hashes", func(t *testing.T) { 533 p := Param{RawMessage: []byte(fmt.Sprintf(`["%s", "%s"]`, u1.StringLE(), u2.StringLE()))} 534 actual, _, err := p.GetSignersWithWitnesses() 535 require.NoError(t, err) 536 require.Equal(t, 2, len(actual)) 537 require.True(t, u1.Equals(actual[0].Account)) 538 require.True(t, u2.Equals(actual[1].Account)) 539 }) 540 541 t.Run("overflow", func(t *testing.T) { 542 var hashes = make([]util.Uint256, transaction.MaxAttributes+1) 543 msg, err := json.Marshal(hashes) 544 require.NoError(t, err) 545 p := Param{RawMessage: msg} 546 _, _, err = p.GetSignersWithWitnesses() 547 require.Error(t, err) 548 }) 549 550 t.Run("bad format", func(t *testing.T) { 551 p := Param{RawMessage: []byte(`"not a signer"`)} 552 _, _, err := p.GetSignersWithWitnesses() 553 require.Error(t, err) 554 }) 555 } 556 557 func TestParamGetUUID(t *testing.T) { 558 t.Run("from null", func(t *testing.T) { 559 p := Param{RawMessage: []byte("null")} 560 _, err := p.GetUUID() 561 require.ErrorIs(t, err, errNotAString) 562 }) 563 t.Run("invalid uuid", func(t *testing.T) { 564 p := Param{RawMessage: []byte(`"not-a-uuid"`)} 565 _, err := p.GetUUID() 566 require.Error(t, err) 567 require.True(t, strings.Contains(err.Error(), "not a valid UUID"), err.Error()) 568 }) 569 t.Run("compat", func(t *testing.T) { 570 expected := "2107da59-4f9c-462c-9c51-7666842519a9" 571 p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, expected))} 572 id, err := p.GetUUID() 573 require.NoError(t, err) 574 require.Equal(t, id.String(), expected) 575 }) 576 }