github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/rpcclient/nns/contract_test.go (about) 1 package nns 2 3 import ( 4 "errors" 5 "math/big" 6 "testing" 7 8 "github.com/google/uuid" 9 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 10 "github.com/nspcc-dev/neo-go/pkg/neorpc/result" 11 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 12 "github.com/nspcc-dev/neo-go/pkg/util" 13 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 14 "github.com/stretchr/testify/require" 15 ) 16 17 type testAct struct { 18 err error 19 res *result.Invoke 20 tx *transaction.Transaction 21 txh util.Uint256 22 vub uint32 23 } 24 25 func (t *testAct) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { 26 return t.res, t.err 27 } 28 func (t *testAct) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error) { 29 return t.res, t.err 30 } 31 func (t *testAct) TerminateSession(sessionID uuid.UUID) error { 32 return t.err 33 } 34 func (t *testAct) TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error) { 35 return t.res.Stack, t.err 36 } 37 38 func (t *testAct) MakeRun(script []byte) (*transaction.Transaction, error) { 39 return t.tx, t.err 40 } 41 func (t *testAct) MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) { 42 return t.tx, t.err 43 } 44 func (t *testAct) SendRun(script []byte) (util.Uint256, uint32, error) { 45 return t.txh, t.vub, t.err 46 } 47 func (t *testAct) MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) { 48 return t.tx, t.err 49 } 50 func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) { 51 return t.tx, t.err 52 } 53 func (t *testAct) SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) { 54 return t.txh, t.vub, t.err 55 } 56 57 func (t *testAct) SignAndSend(tx *transaction.Transaction) (util.Uint256, uint32, error) { 58 return t.txh, t.vub, t.err 59 } 60 61 func TestSimpleGetters(t *testing.T) { 62 ta := &testAct{} 63 nns := NewReader(ta, util.Uint160{1, 2, 3}) 64 65 ta.err = errors.New("") 66 _, err := nns.GetPrice(uint8(A)) 67 require.Error(t, err) 68 _, err = nns.IsAvailable("nspcc.neo") 69 require.Error(t, err) 70 _, err = nns.Resolve("nspcc.neo", A) 71 require.Error(t, err) 72 _, err = nns.GetRecord("nspcc.neo", A) 73 require.Error(t, err) 74 75 ta.err = nil 76 ta.res = &result.Invoke{ 77 State: "HALT", 78 Stack: []stackitem.Item{ 79 stackitem.Make(100500), 80 }, 81 } 82 price, err := nns.GetPrice(uint8(A)) 83 require.NoError(t, err) 84 require.Equal(t, new(big.Int).SetInt64(100500), price) 85 86 ta.res = &result.Invoke{ 87 State: "HALT", 88 Stack: []stackitem.Item{ 89 stackitem.Make(true), 90 }, 91 } 92 ava, err := nns.IsAvailable("nspcc.neo") 93 require.NoError(t, err) 94 require.Equal(t, true, ava) 95 96 ta.res = &result.Invoke{ 97 State: "HALT", 98 Stack: []stackitem.Item{ 99 stackitem.Make("some text"), 100 }, 101 } 102 txt, err := nns.Resolve("nspcc.neo", TXT) 103 require.NoError(t, err) 104 require.Equal(t, "some text", txt) 105 106 rec, err := nns.GetRecord("nspcc.neo", TXT) 107 require.NoError(t, err) 108 require.Equal(t, "some text", rec) 109 } 110 111 func TestGetAllRecords(t *testing.T) { 112 ta := &testAct{} 113 nns := NewReader(ta, util.Uint160{1, 2, 3}) 114 115 ta.err = errors.New("") 116 _, err := nns.GetAllRecords("nspcc.neo") 117 require.Error(t, err) 118 119 ta.err = nil 120 iid := uuid.New() 121 ta.res = &result.Invoke{ 122 State: "HALT", 123 Stack: []stackitem.Item{ 124 stackitem.NewInterop(result.Iterator{ 125 ID: &iid, 126 }), 127 }, 128 } 129 _, err = nns.GetAllRecords("nspcc.neo") 130 require.Error(t, err) 131 132 // Session-based iterator. 133 sid := uuid.New() 134 ta.res = &result.Invoke{ 135 Session: sid, 136 State: "HALT", 137 Stack: []stackitem.Item{ 138 stackitem.NewInterop(result.Iterator{ 139 ID: &iid, 140 }), 141 }, 142 } 143 iter, err := nns.GetAllRecords("nspcc.neo") 144 require.NoError(t, err) 145 146 ta.res = &result.Invoke{ 147 Stack: []stackitem.Item{ 148 stackitem.Make([]stackitem.Item{ 149 stackitem.Make("n3"), 150 stackitem.Make(16), 151 stackitem.Make("cool"), 152 }), 153 }, 154 } 155 vals, err := iter.Next(10) 156 require.NoError(t, err) 157 require.Equal(t, 1, len(vals)) 158 require.Equal(t, RecordState{ 159 Name: "n3", 160 Type: TXT, 161 Data: "cool", 162 }, vals[0]) 163 164 ta.err = errors.New("") 165 _, err = iter.Next(1) 166 require.Error(t, err) 167 168 err = iter.Terminate() 169 require.Error(t, err) 170 171 // Value-based iterator. 172 ta.err = nil 173 ta.res = &result.Invoke{ 174 State: "HALT", 175 Stack: []stackitem.Item{ 176 stackitem.NewInterop(result.Iterator{ 177 Values: []stackitem.Item{ 178 stackitem.Make("n3"), 179 stackitem.Make(16), 180 stackitem.Make("cool"), 181 }, 182 }), 183 }, 184 } 185 iter, err = nns.GetAllRecords("nspcc.neo") 186 require.NoError(t, err) 187 188 ta.err = errors.New("") 189 err = iter.Terminate() 190 require.NoError(t, err) 191 192 ta.err = nil 193 ta.res = &result.Invoke{ 194 State: "HALT", 195 Stack: []stackitem.Item{ 196 stackitem.NewInterop(result.Iterator{ 197 Values: []stackitem.Item{ 198 stackitem.Make("valid data"), 199 stackitem.Make(-1), 200 }, 201 }), 202 }, 203 } 204 iter, err = nns.GetAllRecords("nspcc.neo") 205 require.NoError(t, err) 206 207 _, err = iter.Next(10) 208 209 require.Error(t, err) 210 require.Contains(t, err.Error(), "item #0: ") 211 } 212 213 func TestGetAllRecordsExpanded(t *testing.T) { 214 ta := &testAct{} 215 nns := NewReader(ta, util.Uint160{1, 2, 3}) 216 217 ta.err = errors.New("") 218 _, err := nns.GetAllRecordsExpanded("nspcc.neo", 8) 219 require.Error(t, err) 220 221 ta.err = nil 222 ta.res = &result.Invoke{ 223 State: "HALT", 224 Stack: []stackitem.Item{ 225 stackitem.Make(42), 226 }, 227 } 228 _, err = nns.GetAllRecordsExpanded("nspcc.neo", 8) 229 require.Error(t, err) 230 231 ta.res = &result.Invoke{ 232 State: "HALT", 233 Stack: []stackitem.Item{ 234 stackitem.Make([]stackitem.Item{ 235 stackitem.Make([]stackitem.Item{ 236 stackitem.Make("n3"), 237 stackitem.Make(16), 238 stackitem.Make("cool"), 239 }), 240 }), 241 }, 242 } 243 vals, err := nns.GetAllRecordsExpanded("nspcc.neo", 8) 244 require.NoError(t, err) 245 require.Equal(t, 1, len(vals)) 246 require.Equal(t, RecordState{ 247 Name: "n3", 248 Type: TXT, 249 Data: "cool", 250 }, vals[0]) 251 } 252 253 func TestRoots(t *testing.T) { 254 ta := &testAct{} 255 nns := NewReader(ta, util.Uint160{1, 2, 3}) 256 ta.err = errors.New("") 257 _, err := nns.Roots() 258 require.Error(t, err) 259 iid := uuid.New() 260 261 // Session-based iterator. 262 sid := uuid.New() 263 ta.res = &result.Invoke{ 264 Session: sid, 265 State: "HALT", 266 Stack: []stackitem.Item{ 267 stackitem.NewInterop(result.Iterator{ 268 ID: &iid, 269 }), 270 }, 271 } 272 ta.err = nil 273 iter, err := nns.Roots() 274 require.NoError(t, err) 275 276 ta.res = &result.Invoke{ 277 Stack: []stackitem.Item{ 278 stackitem.Make([]stackitem.Item{ 279 stackitem.Make("n3"), 280 stackitem.Make("aaaaaa"), 281 stackitem.Make("cool"), 282 }), 283 }, 284 } 285 vals, err := iter.Next(10) 286 require.NoError(t, err) 287 require.Equal(t, 1, len(vals)) 288 require.Equal(t, "n3", vals[0]) 289 290 ta.err = errors.New("") 291 _, err = iter.Next(1) 292 require.Error(t, err) 293 294 err = iter.Terminate() 295 require.Error(t, err) 296 297 // Value-based iterator. 298 ta.err = nil 299 ta.res = &result.Invoke{ 300 State: "HALT", 301 Stack: []stackitem.Item{ 302 stackitem.NewInterop(result.Iterator{ 303 Values: []stackitem.Item{ 304 stackitem.Make("n3"), 305 stackitem.Make("aaaaaa"), 306 stackitem.Make("cool"), 307 }, 308 }), 309 }, 310 } 311 iter, err = nns.Roots() 312 require.NoError(t, err) 313 314 ta.err = errors.New("") 315 err = iter.Terminate() 316 require.NoError(t, err) 317 318 sid = uuid.New() 319 iid = uuid.New() 320 ta.res = &result.Invoke{ 321 Session: sid, 322 State: "HALT", 323 Stack: []stackitem.Item{ 324 stackitem.NewInterop(result.Iterator{ 325 ID: &iid, 326 Values: []stackitem.Item{ 327 stackitem.Make("incorrect format"), 328 }, 329 }), 330 }, 331 } 332 ta.err = nil 333 iter, err = nns.Roots() 334 require.NoError(t, err) 335 336 _, err = iter.Next(10) 337 require.Error(t, err) 338 require.Equal(t, "wrong number of elements", err.Error()) 339 340 ta.res = &result.Invoke{ 341 State: "HALT", 342 Stack: []stackitem.Item{ 343 stackitem.Make([]stackitem.Item{ 344 stackitem.Make([]stackitem.Item{ 345 stackitem.Make("root1"), 346 }), 347 stackitem.Make([]stackitem.Item{ 348 stackitem.Make("root2"), 349 }), 350 }), 351 }, 352 } 353 354 roots, err := nns.RootsExpanded(10) 355 require.NoError(t, err) 356 require.Equal(t, []string{"root1", "root2"}, roots) 357 358 ta.res = &result.Invoke{ 359 State: "HALT", 360 Stack: []stackitem.Item{ 361 stackitem.Make("incorrect format"), // Not a slice of stackitem.Item 362 }, 363 } 364 365 _, err = nns.RootsExpanded(10) 366 require.Error(t, err) 367 require.Equal(t, "not an array", err.Error()) 368 369 ta.err = errors.New("call and expand iterator error") 370 _, err = nns.RootsExpanded(10) 371 require.Error(t, err) 372 require.Equal(t, "call and expand iterator error", err.Error()) 373 } 374 375 func TestUpdate(t *testing.T) { 376 ta := &testAct{} 377 nns := New(ta, util.Uint160{1, 2, 3}) 378 379 nef := []byte{0x01, 0x02, 0x03} 380 manifest := "manifest data" 381 382 ta.err = errors.New("test error") 383 _, _, err := nns.Update(nef, manifest) 384 require.Error(t, err) 385 386 // Test successful update 387 ta.err = nil 388 ta.txh = util.Uint256{0x04, 0x05, 0x06} 389 txh, vub, err := nns.Update(nef, manifest) 390 require.NoError(t, err) 391 require.Equal(t, ta.txh, txh) 392 require.Equal(t, ta.vub, vub) 393 394 for _, fun := range []func(nef []byte, manifest string) (*transaction.Transaction, error){ 395 nns.UpdateTransaction, 396 nns.UpdateUnsigned, 397 } { 398 ta.err = errors.New("") 399 _, err := fun(nil, "") 400 require.Error(t, err) 401 402 ta.err = nil 403 ta.tx = &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42} 404 tx, err := fun(nil, "") 405 require.NoError(t, err) 406 require.Equal(t, ta.tx, tx) 407 } 408 } 409 410 func TestAddRoot(t *testing.T) { 411 ta := &testAct{} 412 nns := New(ta, util.Uint160{1, 2, 3}) 413 414 root := "example.root" 415 params, err := smartcontract.NewParameterFromValue(root) 416 require.NoError(t, err) 417 ta.err = errors.New("test error") 418 _, _, err = nns.AddRoot(params.Value.(string)) 419 require.Error(t, err) 420 421 // Test success case 422 ta.err = nil 423 ta.txh = util.Uint256{0x07, 0x08, 0x09} 424 txh, vub, err := nns.AddRoot(root) 425 require.NoError(t, err) 426 require.Equal(t, ta.txh, txh) 427 require.Equal(t, ta.vub, vub) 428 429 ta.tx = &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42} 430 tx, err := nns.AddRootTransaction(root) 431 require.NoError(t, err) 432 require.Equal(t, ta.tx, tx) 433 tx, err = nns.AddRootUnsigned(root) 434 require.NoError(t, err) 435 require.Equal(t, ta.tx, tx) 436 437 ta.err = errors.New("") 438 _, err = nns.AddRootTransaction(root) 439 require.Error(t, err) 440 441 ta.err = errors.New("") 442 _, err = nns.AddRootUnsigned(root) 443 require.Error(t, err) 444 } 445 446 func TestSetPrice(t *testing.T) { 447 ta := &testAct{} 448 nns := New(ta, util.Uint160{1, 2, 3}) 449 450 priceList := []int64{100, 200} 451 ta.err = errors.New("test error") 452 _, _, err := nns.SetPrice(priceList) 453 require.Error(t, err) 454 _, err = nns.SetPriceTransaction(priceList) 455 require.Error(t, err) 456 _, err = nns.SetPriceUnsigned(priceList) 457 require.Error(t, err) 458 459 // Test success case 460 ta.err = nil 461 ta.txh = util.Uint256{0x0A, 0x0B, 0x0C} 462 ta.vub = 42 463 464 ta.res = &result.Invoke{ 465 State: "HALT", 466 Stack: []stackitem.Item{ 467 stackitem.Make(42), 468 }, 469 } 470 471 txh, vub, err := nns.SetPrice(priceList) 472 require.NoError(t, err) 473 require.Equal(t, ta.txh, txh) 474 require.Equal(t, ta.vub, vub) 475 476 ta.tx = &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42} 477 tx, err := nns.SetPriceTransaction(priceList) 478 require.NoError(t, err) 479 require.Equal(t, ta.tx, tx) 480 tx, err = nns.SetPriceUnsigned(priceList) 481 require.NoError(t, err) 482 require.Equal(t, ta.tx, tx) 483 484 ta.err = errors.New("") 485 _, err = nns.SetPriceTransaction(priceList) 486 require.Error(t, err) 487 488 ta.err = errors.New("") 489 _, err = nns.SetPriceUnsigned(priceList) 490 require.Error(t, err) 491 } 492 493 func TestRegister(t *testing.T) { 494 ta := &testAct{} 495 nns := New(ta, util.Uint160{1, 2, 3}) 496 497 name := "example.neo" 498 owner := util.Uint160{0x0D, 0x0E, 0x0F} 499 500 ta.err = errors.New("test error") 501 txh, vub, err := nns.Register(name, owner) 502 require.Error(t, err) 503 require.Equal(t, util.Uint256{}, txh) // Check if returned Uint256 is zero-initialized 504 require.Equal(t, uint32(0), vub) 505 506 // Test success case 507 ta.err = nil 508 ta.txh = util.Uint256{0x10, 0x11, 0x12} 509 txh, vub, err = nns.Register(name, owner) 510 require.NoError(t, err) 511 require.Equal(t, ta.txh, txh) 512 require.Equal(t, ta.vub, vub) 513 514 ta.tx = &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42} 515 tx, err := nns.RegisterTransaction(name, owner) 516 require.NoError(t, err) 517 require.Equal(t, ta.tx, tx) 518 519 tx, err = nns.RegisterUnsigned(name, owner) 520 require.NoError(t, err) 521 require.Equal(t, ta.tx, tx) 522 523 ta.err = errors.New("") 524 _, err = nns.RegisterTransaction(name, owner) 525 require.Error(t, err) 526 527 ta.err = errors.New("") 528 _, err = nns.RegisterUnsigned(name, owner) 529 require.Error(t, err) 530 } 531 532 func TestRenew(t *testing.T) { 533 ta := &testAct{} 534 nns := New(ta, util.Uint160{1, 2, 3}) 535 536 name := "example.neo" 537 538 ta.err = errors.New("test error") 539 _, _, err := nns.Renew(name) 540 require.Error(t, err) 541 542 // Test success case 543 ta.err = nil 544 ta.txh = util.Uint256{0x13, 0x14, 0x15} 545 txh, vub, err := nns.Renew(name) 546 require.NoError(t, err) 547 require.Equal(t, ta.txh, txh) 548 require.Equal(t, ta.vub, vub) 549 550 txh, vub, err = nns.Renew2(name, 1) 551 require.NoError(t, err) 552 require.Equal(t, ta.txh, txh) 553 require.Equal(t, ta.vub, vub) 554 555 ta.tx = &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42} 556 tx, err := nns.RenewTransaction(name) 557 require.NoError(t, err) 558 require.Equal(t, ta.tx, tx) 559 560 tx, err = nns.RenewUnsigned(name) 561 require.NoError(t, err) 562 require.Equal(t, ta.tx, tx) 563 564 ta.err = errors.New("") 565 _, err = nns.RenewTransaction(name) 566 require.Error(t, err) 567 568 ta.err = errors.New("") 569 _, err = nns.RenewUnsigned(name) 570 require.Error(t, err) 571 } 572 573 func TestSetAdmin(t *testing.T) { 574 ta := &testAct{} 575 c := New(ta, util.Uint160{1, 2, 3}) 576 577 name := "example.neo" 578 admin := util.Uint160{4, 5, 6} 579 txMock := &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42} 580 txhMock := util.Uint256{0x13, 0x14, 0x15} 581 582 testCases := []struct { 583 name string 584 setup func() 585 testFunc func() (interface{}, error) 586 want interface{} 587 wantErr bool 588 }{ 589 { 590 name: "SetAdmin - Error", 591 setup: func() { 592 ta.err = errors.New("test error") 593 }, 594 testFunc: func() (interface{}, error) { 595 txh, vub, err := c.SetAdmin(name, admin) 596 return []interface{}{txh, vub}, err 597 }, 598 wantErr: true, 599 }, 600 { 601 name: "SetAdmin - Success", 602 setup: func() { 603 ta.err = nil 604 ta.txh = txhMock 605 ta.vub = 42 606 }, 607 testFunc: func() (interface{}, error) { 608 txh, vub, err := c.SetAdmin(name, admin) 609 return []interface{}{txh, vub}, err 610 }, 611 want: []interface{}{txhMock, uint32(42)}, 612 }, 613 { 614 name: "SetAdminTransaction - Success", 615 setup: func() { 616 ta.err = nil 617 ta.tx = txMock 618 }, 619 testFunc: func() (interface{}, error) { 620 return c.SetAdminTransaction(name, admin) 621 }, 622 want: txMock, 623 }, 624 { 625 name: "SetAdminTransaction - Error", 626 setup: func() { 627 ta.err = errors.New("test error") 628 }, 629 testFunc: func() (interface{}, error) { 630 return c.SetAdminTransaction(name, admin) 631 }, 632 wantErr: true, 633 }, 634 { 635 name: "SetAdminUnsigned - Success", 636 setup: func() { 637 ta.err = nil 638 ta.tx = txMock 639 }, 640 testFunc: func() (interface{}, error) { 641 return c.SetAdminUnsigned(name, admin) 642 }, 643 want: txMock, 644 }, 645 { 646 name: "SetAdminUnsigned - Error", 647 setup: func() { 648 ta.err = errors.New("test error") 649 }, 650 testFunc: func() (interface{}, error) { 651 return c.SetAdminUnsigned(name, admin) 652 }, 653 wantErr: true, 654 }, 655 } 656 657 for _, tc := range testCases { 658 t.Run(tc.name, func(t *testing.T) { 659 tc.setup() 660 got, err := tc.testFunc() 661 if tc.wantErr { 662 require.Error(t, err) 663 } else { 664 require.NoError(t, err) 665 require.Equal(t, tc.want, got) 666 } 667 }) 668 } 669 } 670 671 func TestSetRecord(t *testing.T) { 672 ta := &testAct{} 673 c := New(ta, util.Uint160{1, 2, 3}) 674 675 name := "example.neo" 676 typev := A 677 data := "record data" 678 txMock := &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42} 679 txhMock := util.Uint256{0x13, 0x14, 0x15} 680 681 testCases := []struct { 682 name string 683 setup func() 684 testFunc func() (interface{}, error) 685 want interface{} 686 wantErr bool 687 }{ 688 { 689 name: "SetRecord - Error", 690 setup: func() { 691 ta.err = errors.New("test error") 692 }, 693 testFunc: func() (interface{}, error) { 694 txh, vub, err := c.SetRecord(name, typev, data) 695 return []interface{}{txh, vub}, err 696 }, 697 wantErr: true, 698 }, 699 { 700 name: "SetRecord - Success", 701 setup: func() { 702 ta.err = nil 703 ta.txh = txhMock 704 ta.vub = 42 705 }, 706 testFunc: func() (interface{}, error) { 707 txh, vub, err := c.SetRecord(name, typev, data) 708 return []interface{}{txh, vub}, err 709 }, 710 want: []interface{}{txhMock, uint32(42)}, 711 }, 712 { 713 name: "SetRecordTransaction - Success", 714 setup: func() { 715 ta.err = nil 716 ta.tx = txMock 717 }, 718 testFunc: func() (interface{}, error) { 719 return c.SetRecordTransaction(name, typev, data) 720 }, 721 want: txMock, 722 }, 723 { 724 name: "SetRecordTransaction - Error", 725 setup: func() { 726 ta.err = errors.New("test error") 727 }, 728 testFunc: func() (interface{}, error) { 729 return c.SetRecordTransaction(name, typev, data) 730 }, 731 wantErr: true, 732 }, 733 { 734 name: "SetRecordUnsigned - Success", 735 setup: func() { 736 ta.err = nil 737 ta.tx = txMock 738 }, 739 testFunc: func() (interface{}, error) { 740 return c.SetRecordUnsigned(name, typev, data) 741 }, 742 want: txMock, 743 }, 744 { 745 name: "SetRecordUnsigned - Error", 746 setup: func() { 747 ta.err = errors.New("test error") 748 }, 749 testFunc: func() (interface{}, error) { 750 return c.SetRecordUnsigned(name, typev, data) 751 }, 752 wantErr: true, 753 }, 754 } 755 756 for _, tc := range testCases { 757 t.Run(tc.name, func(t *testing.T) { 758 tc.setup() 759 got, err := tc.testFunc() 760 if tc.wantErr { 761 require.Error(t, err) 762 } else { 763 require.NoError(t, err) 764 require.Equal(t, tc.want, got) 765 } 766 }) 767 } 768 }