github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/services/rpcsrv/server_test.go (about) 1 package rpcsrv 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/base64" 7 "encoding/binary" 8 "encoding/json" 9 "fmt" 10 gio "io" 11 "math" 12 "math/big" 13 "net" 14 "net/http" 15 "net/http/httptest" 16 "reflect" 17 "sort" 18 "strconv" 19 "strings" 20 "sync/atomic" 21 "testing" 22 "time" 23 24 "github.com/google/uuid" 25 "github.com/gorilla/websocket" 26 "github.com/nspcc-dev/neo-go/internal/random" 27 "github.com/nspcc-dev/neo-go/internal/testchain" 28 "github.com/nspcc-dev/neo-go/internal/testserdes" 29 "github.com/nspcc-dev/neo-go/pkg/config" 30 "github.com/nspcc-dev/neo-go/pkg/core" 31 "github.com/nspcc-dev/neo-go/pkg/core/block" 32 "github.com/nspcc-dev/neo-go/pkg/core/fee" 33 "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" 34 "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" 35 "github.com/nspcc-dev/neo-go/pkg/core/state" 36 "github.com/nspcc-dev/neo-go/pkg/core/storage/dboper" 37 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 38 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 39 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 40 "github.com/nspcc-dev/neo-go/pkg/encoding/address" 41 "github.com/nspcc-dev/neo-go/pkg/io" 42 "github.com/nspcc-dev/neo-go/pkg/neorpc" 43 "github.com/nspcc-dev/neo-go/pkg/neorpc/result" 44 "github.com/nspcc-dev/neo-go/pkg/network" 45 "github.com/nspcc-dev/neo-go/pkg/network/payload" 46 rpc2 "github.com/nspcc-dev/neo-go/pkg/services/oracle/broadcaster" 47 "github.com/nspcc-dev/neo-go/pkg/services/rpcsrv/params" 48 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 49 "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" 50 "github.com/nspcc-dev/neo-go/pkg/util" 51 "github.com/nspcc-dev/neo-go/pkg/vm/emit" 52 "github.com/nspcc-dev/neo-go/pkg/vm/invocations" 53 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 54 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 55 "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" 56 "github.com/nspcc-dev/neo-go/pkg/wallet" 57 "github.com/stretchr/testify/assert" 58 "github.com/stretchr/testify/require" 59 "go.uber.org/zap/zapcore" 60 ) 61 62 type executor struct { 63 chain *core.Blockchain 64 httpSrv *httptest.Server 65 } 66 67 type rpcTestCase struct { 68 name string 69 params string 70 fail bool 71 errCode int64 72 result func(e *executor) any 73 check func(t *testing.T, e *executor, result any) 74 } 75 76 const genesisBlockHash = "0f8fb4e17d2ab9f3097af75ca7fd16064160fb8043db94909e00dd4e257b9dc4" 77 const testContractHash = "565cff9508ebc75aadd7fe59f38dac610ab6093c" 78 const deploymentTxHash = "a14390941cc3a1d87393eff720a722e9cd350bd6ed233c5fe2001326c80eb68e" 79 80 const ( 81 verifyContractHash = "06ed5314c2e4cb103029a60b86d46afa2fb8f67c" 82 verifyContractAVM = "VwIAQS1RCDBwDBTunqIsJ+NL0BSPxBCOCPdOj1BIskrZMCQE2zBxaBPOStkoJATbKGlK2SgkBNsol0A=" 83 verifyWithArgsContractHash = "4dc916254efd2947c93b11207e8ffc0bb56161c5" 84 nnsContractHash = "892429fcd47c30f8451781acc627e8b20e0d64f3" 85 nnsToken1ID = "6e656f2e636f6d" 86 nfsoContractHash = "730ebe719ab8e3b69d11dafc95cdb9bf409db179" 87 nfsoToken1ID = "7e244ffd6aa85fb1579d2ed22e9b761ab62e3486" 88 storageContractHash = "ebc0c16a76c808cd4dde6bcc063f09e45e331ec7" 89 faultedTxHashLE = "82279bfe9bada282ca0f8cb8e0bb124b921af36f00c69a518320322c6f4fef60" 90 faultedTxBlock uint32 = 23 91 invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA" 92 block20StateRootLE = "2d95b1149230d40c3043a84d42249b7b344f8755ea9fd0b2d95c5d011af85fc7" 93 ) 94 95 var ( 96 nnsHash, _ = util.Uint160DecodeStringLE(nnsContractHash) 97 nfsoHash, _ = util.Uint160DecodeStringLE(nfsoContractHash) 98 nfsoToken1ContainerID = util.Uint256{1, 2, 3} 99 nfsoToken1ObjectID = util.Uint256{4, 5, 6} 100 ) 101 102 var rpcFunctionsWithUnsupportedStatesTestCases = map[string][]rpcTestCase{ 103 "getproof": { 104 { 105 name: "unsupported state", 106 params: `[]`, 107 fail: true, 108 errCode: neorpc.ErrUnsupportedStateCode, 109 }, 110 }, 111 "verifyproof": { 112 { 113 name: "unsupported state", 114 params: `[]`, 115 fail: true, 116 errCode: neorpc.ErrUnsupportedStateCode, 117 }, 118 }, 119 "getstate": { 120 { 121 name: "unsupported state", 122 params: `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHash + `", "QQ=="]`, 123 fail: true, 124 errCode: neorpc.ErrUnsupportedStateCode, 125 }, 126 }, 127 "findstates": { 128 { 129 name: "unsupported state", 130 params: `["` + block20StateRootLE + `", "0xabcdef"]`, 131 fail: true, 132 errCode: neorpc.ErrUnsupportedStateCode, 133 }, 134 }, 135 "findstoragehistoric": { 136 { 137 name: "unsupported state", 138 params: `["` + block20StateRootLE + `", "0xabcdef"]`, 139 fail: true, 140 errCode: neorpc.ErrUnsupportedStateCode, 141 }, 142 }, 143 "invokefunctionhistoric": { 144 { 145 name: "unsupported state", 146 params: `[]`, 147 fail: true, 148 errCode: neorpc.ErrUnsupportedStateCode, 149 }, 150 }, 151 } 152 153 var rpcTestCases = map[string][]rpcTestCase{ 154 "getapplicationlog": { 155 { 156 name: "positive", 157 params: `["` + deploymentTxHash + `"]`, 158 result: func(e *executor) any { return &result.ApplicationLog{} }, 159 check: func(t *testing.T, e *executor, acc any) { 160 res, ok := acc.(*result.ApplicationLog) 161 require.True(t, ok) 162 expectedTxHash, err := util.Uint256DecodeStringLE(deploymentTxHash) 163 require.NoError(t, err) 164 assert.Equal(t, 1, len(res.Executions)) 165 assert.Equal(t, expectedTxHash, res.Container) 166 assert.Equal(t, trigger.Application, res.Executions[0].Trigger) 167 assert.Equal(t, vmstate.Halt, res.Executions[0].VMState) 168 }, 169 }, 170 { 171 name: "positive, genesis block", 172 params: `["` + genesisBlockHash + `"]`, 173 result: func(e *executor) any { return &result.ApplicationLog{} }, 174 check: func(t *testing.T, e *executor, acc any) { 175 res, ok := acc.(*result.ApplicationLog) 176 require.True(t, ok) 177 assert.Equal(t, genesisBlockHash, res.Container.StringLE()) 178 assert.Equal(t, 2, len(res.Executions)) 179 assert.Equal(t, trigger.OnPersist, res.Executions[0].Trigger) 180 assert.Equal(t, trigger.PostPersist, res.Executions[1].Trigger) 181 assert.Equal(t, vmstate.Halt, res.Executions[0].VMState) 182 }, 183 }, 184 { 185 name: "positive, genesis block, postPersist", 186 params: `["` + genesisBlockHash + `", "PostPersist"]`, 187 result: func(e *executor) any { return &result.ApplicationLog{} }, 188 check: func(t *testing.T, e *executor, acc any) { 189 res, ok := acc.(*result.ApplicationLog) 190 require.True(t, ok) 191 assert.Equal(t, genesisBlockHash, res.Container.StringLE()) 192 assert.Equal(t, 1, len(res.Executions)) 193 assert.Equal(t, trigger.PostPersist, res.Executions[0].Trigger) 194 assert.Equal(t, vmstate.Halt, res.Executions[0].VMState) 195 }, 196 }, 197 { 198 name: "positive, genesis block, onPersist", 199 params: `["` + genesisBlockHash + `", "OnPersist"]`, 200 result: func(e *executor) any { return &result.ApplicationLog{} }, 201 check: func(t *testing.T, e *executor, acc any) { 202 res, ok := acc.(*result.ApplicationLog) 203 require.True(t, ok) 204 assert.Equal(t, genesisBlockHash, res.Container.StringLE()) 205 assert.Equal(t, 1, len(res.Executions)) 206 assert.Equal(t, trigger.OnPersist, res.Executions[0].Trigger) 207 assert.Equal(t, vmstate.Halt, res.Executions[0].VMState) 208 }, 209 }, 210 { 211 name: "invalid trigger (not a string)", 212 params: `["` + genesisBlockHash + `", 1]`, 213 fail: true, 214 errCode: neorpc.InvalidParamsCode, 215 }, 216 { 217 name: "no params", 218 params: `[]`, 219 fail: true, 220 errCode: neorpc.InvalidParamsCode, 221 }, 222 { 223 name: "invalid address", 224 params: `["notahash"]`, 225 fail: true, 226 errCode: neorpc.InvalidParamsCode, 227 }, 228 { 229 name: "invalid tx hash", 230 params: `["d24cc1d52b5c0216cbf3835bb5bac8ccf32639fa1ab6627ec4e2b9f33f7ec02f"]`, 231 fail: true, 232 errCode: neorpc.ErrUnknownScriptContainerCode, 233 }, 234 }, 235 "getcontractstate": { 236 { 237 name: "positive, by hash", 238 params: fmt.Sprintf(`["%s"]`, testContractHash), 239 result: func(e *executor) any { return &state.Contract{} }, 240 check: func(t *testing.T, e *executor, cs any) { 241 res, ok := cs.(*state.Contract) 242 require.True(t, ok) 243 assert.Equal(t, testContractHash, res.Hash.StringLE()) 244 }, 245 }, 246 { 247 name: "positive, by id", 248 params: `[1]`, 249 result: func(e *executor) any { return &state.Contract{} }, 250 check: func(t *testing.T, e *executor, cs any) { 251 res, ok := cs.(*state.Contract) 252 require.True(t, ok) 253 assert.Equal(t, int32(1), res.ID) 254 }, 255 }, 256 { 257 name: "positive, native by id", 258 params: `[-3]`, 259 result: func(e *executor) any { return &state.Contract{} }, 260 check: func(t *testing.T, e *executor, cs any) { 261 res, ok := cs.(*state.Contract) 262 require.True(t, ok) 263 assert.Equal(t, int32(-3), res.ID) 264 }, 265 }, 266 { 267 name: "positive, native by name", 268 params: `["PolicyContract"]`, 269 result: func(e *executor) any { return &state.Contract{} }, 270 check: func(t *testing.T, e *executor, cs any) { 271 res, ok := cs.(*state.Contract) 272 require.True(t, ok) 273 assert.Equal(t, int32(-7), res.ID) 274 }, 275 }, 276 { 277 name: "negative, bad hash", 278 params: `["6d1eeca891ee93de2b7a77eb91c26f3b3c04d6c3"]`, 279 fail: true, 280 errCode: neorpc.ErrUnknownContractCode, 281 }, 282 { 283 name: "negative, bad ID", 284 params: `[-100]`, 285 fail: true, 286 errCode: neorpc.ErrUnknownContractCode, 287 }, 288 { 289 name: "negative, bad native name", 290 params: `["unknown_native"]`, 291 fail: true, 292 errCode: neorpc.InvalidParamsCode, 293 }, 294 { 295 name: "no params", 296 params: `[]`, 297 fail: true, 298 errCode: neorpc.InvalidParamsCode, 299 }, 300 { 301 name: "invalid hash", 302 params: `["notahex"]`, 303 fail: true, 304 errCode: neorpc.InvalidParamsCode, 305 }, 306 }, 307 "getnep11balances": { 308 { 309 name: "no params", 310 params: `[]`, 311 fail: true, 312 errCode: neorpc.InvalidParamsCode, 313 }, 314 { 315 name: "invalid address", 316 params: `["notahex"]`, 317 fail: true, 318 errCode: neorpc.InvalidParamsCode, 319 }, 320 { 321 name: "positive", 322 params: `["` + testchain.PrivateKeyByID(0).GetScriptHash().StringLE() + `"]`, 323 result: func(e *executor) any { return &result.NEP11Balances{} }, 324 check: checkNep11Balances, 325 }, 326 { 327 name: "positive_address", 328 params: `["` + address.Uint160ToString(testchain.PrivateKeyByID(0).GetScriptHash()) + `"]`, 329 result: func(e *executor) any { return &result.NEP11Balances{} }, 330 check: checkNep11Balances, 331 }, 332 }, 333 "getnep11properties": { 334 { 335 name: "no params", 336 params: `[]`, 337 fail: true, 338 errCode: neorpc.InvalidParamsCode, 339 }, 340 { 341 name: "invalid address", 342 params: `["notahex"]`, 343 fail: true, 344 errCode: neorpc.InvalidParamsCode, 345 }, 346 { 347 name: "no token", 348 params: `["` + nnsContractHash + `"]`, 349 fail: true, 350 errCode: neorpc.InvalidParamsCode, 351 }, 352 { 353 name: "bad token", 354 params: `["` + nnsContractHash + `", "abcdef"]`, 355 fail: true, 356 errCode: neorpc.ErrExecutionFailedCode, 357 }, 358 { 359 name: "positive", 360 params: `["` + nnsContractHash + `", "6e656f2e636f6d"]`, 361 result: func(e *executor) any { 362 return &map[string]any{ 363 "name": "neo.com", 364 "expiration": "lhbLRl0B", 365 "admin": nil, 366 } 367 }, 368 }, 369 }, 370 "getnep11transfers": { 371 { 372 name: "no params", 373 params: `[]`, 374 fail: true, 375 errCode: neorpc.InvalidParamsCode, 376 }, 377 { 378 name: "invalid address", 379 params: `["notahex"]`, 380 fail: true, 381 errCode: neorpc.InvalidParamsCode, 382 }, 383 { 384 name: "invalid timestamp", 385 params: `["` + testchain.PrivateKeyByID(0).Address() + `", "notanumber"]`, 386 fail: true, 387 errCode: neorpc.InvalidParamsCode, 388 }, 389 { 390 name: "invalid stop timestamp", 391 params: `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "blah"]`, 392 fail: true, 393 errCode: neorpc.InvalidParamsCode, 394 }, 395 { 396 name: "positive", 397 params: `["` + testchain.PrivateKeyByID(0).Address() + `", 0]`, 398 result: func(e *executor) any { return &result.NEP11Transfers{} }, 399 check: checkNep11Transfers, 400 }, 401 }, 402 "getnep17balances": { 403 { 404 name: "no params", 405 params: `[]`, 406 fail: true, 407 errCode: neorpc.InvalidParamsCode, 408 }, 409 { 410 name: "invalid address", 411 params: `["notahex"]`, 412 fail: true, 413 errCode: neorpc.InvalidParamsCode, 414 }, 415 { 416 name: "positive", 417 params: `["` + testchain.PrivateKeyByID(0).GetScriptHash().StringLE() + `"]`, 418 result: func(e *executor) any { return &result.NEP17Balances{} }, 419 check: checkNep17Balances, 420 }, 421 { 422 name: "positive_address", 423 params: `["` + address.Uint160ToString(testchain.PrivateKeyByID(0).GetScriptHash()) + `"]`, 424 result: func(e *executor) any { return &result.NEP17Balances{} }, 425 check: checkNep17Balances, 426 }, 427 }, 428 "getnep17transfers": { 429 { 430 name: "no params", 431 params: `[]`, 432 fail: true, 433 errCode: neorpc.InvalidParamsCode, 434 }, 435 { 436 name: "invalid address", 437 params: `["notahex"]`, 438 fail: true, 439 errCode: neorpc.InvalidParamsCode, 440 }, 441 { 442 name: "invalid timestamp", 443 params: `["` + testchain.PrivateKeyByID(0).Address() + `", "notanumber"]`, 444 fail: true, 445 errCode: neorpc.InvalidParamsCode, 446 }, 447 { 448 name: "invalid stop timestamp", 449 params: `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "blah"]`, 450 fail: true, 451 errCode: neorpc.InvalidParamsCode, 452 }, 453 { 454 name: "invalid limit", 455 params: `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "2", "0"]`, 456 fail: true, 457 errCode: neorpc.InvalidParamsCode, 458 }, 459 { 460 name: "invalid limit 2", 461 params: `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "2", "bleh"]`, 462 fail: true, 463 errCode: neorpc.InvalidParamsCode, 464 }, 465 { 466 name: "invalid limit 3", 467 params: `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "2", "100500"]`, 468 fail: true, 469 errCode: neorpc.InvalidParamsCode, 470 }, 471 { 472 name: "invalid page", 473 params: `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "2", "3", "-1"]`, 474 fail: true, 475 errCode: neorpc.InvalidParamsCode, 476 }, 477 { 478 name: "invalid page 2", 479 params: `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "2", "3", "jajaja"]`, 480 fail: true, 481 errCode: neorpc.InvalidParamsCode, 482 }, 483 { 484 name: "positive", 485 params: `["` + testchain.PrivateKeyByID(0).Address() + `", 0]`, 486 result: func(e *executor) any { return &result.NEP17Transfers{} }, 487 check: checkNep17Transfers, 488 }, 489 { 490 name: "positive_hash", 491 params: `["` + testchain.PrivateKeyByID(0).GetScriptHash().StringLE() + `", 0]`, 492 result: func(e *executor) any { return &result.NEP17Transfers{} }, 493 check: checkNep17Transfers, 494 }, 495 }, 496 "getproof": { 497 { 498 name: "no params", 499 params: `[]`, 500 fail: true, 501 errCode: neorpc.InvalidParamsCode, 502 }, 503 { 504 name: "invalid root", 505 params: `["0xabcdef"]`, 506 fail: true, 507 errCode: neorpc.InvalidParamsCode, 508 }, 509 { 510 name: "invalid contract", 511 params: `["0000000000000000000000000000000000000000000000000000000000000000", "0xabcdef"]`, 512 fail: true, 513 errCode: neorpc.InvalidParamsCode, 514 }, 515 { 516 name: "invalid key", 517 params: `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHash + `", "notahex"]`, 518 fail: true, 519 errCode: neorpc.InvalidParamsCode, 520 }, 521 }, 522 "getstate": { 523 { 524 name: "no params", 525 params: `[]`, 526 fail: true, 527 errCode: neorpc.InvalidParamsCode, 528 }, 529 { 530 name: "invalid root", 531 params: `["0xabcdef"]`, 532 fail: true, 533 errCode: neorpc.InvalidParamsCode, 534 }, 535 { 536 name: "invalid contract", 537 params: `["0000000000000000000000000000000000000000000000000000000000000000", "0xabcdef"]`, 538 fail: true, 539 errCode: neorpc.InvalidParamsCode, 540 }, 541 { 542 name: "invalid key", 543 params: `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHash + `", "notabase64%"]`, 544 fail: true, 545 errCode: neorpc.InvalidParamsCode, 546 }, 547 { 548 name: "unknown contract", 549 params: `["0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000", "QQ=="]`, 550 fail: true, 551 errCode: neorpc.ErrUnknownContractCode, 552 }, 553 { 554 name: "unknown root/item", 555 params: `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHash + `", "QQ=="]`, 556 fail: true, 557 errCode: neorpc.ErrUnknownContractCode, 558 }, 559 }, 560 "findstates": { 561 { 562 name: "no params", 563 params: `[]`, 564 fail: true, 565 errCode: neorpc.InvalidParamsCode, 566 }, 567 { 568 name: "invalid root", 569 params: `["0xabcdef"]`, 570 fail: true, 571 errCode: neorpc.InvalidParamsCode, 572 }, 573 { 574 name: "invalid contract", 575 params: `["` + block20StateRootLE + `", "0xabcdef"]`, 576 fail: true, 577 errCode: neorpc.InvalidParamsCode, 578 }, 579 { 580 name: "invalid prefix", 581 params: `["` + block20StateRootLE + `", "` + testContractHash + `", "notabase64%"]`, 582 fail: true, 583 errCode: neorpc.InvalidParamsCode, 584 }, 585 { 586 name: "invalid key", 587 params: `["` + block20StateRootLE + `", "` + testContractHash + `", "QQ==", "notabase64%"]`, 588 fail: true, 589 errCode: neorpc.InvalidParamsCode, 590 }, 591 { 592 name: "unknown contract/large count", 593 params: `["` + block20StateRootLE + `", "0000000000000000000000000000000000000000", "QQ==", "QQ==", 101]`, 594 fail: true, 595 errCode: neorpc.ErrUnknownContractCode, 596 }, 597 }, 598 "getstateheight": { 599 { 600 name: "positive", 601 params: `[]`, 602 result: func(_ *executor) any { return new(result.StateHeight) }, 603 check: func(t *testing.T, e *executor, res any) { 604 sh, ok := res.(*result.StateHeight) 605 require.True(t, ok) 606 607 require.Equal(t, e.chain.BlockHeight(), sh.Local) 608 require.Equal(t, uint32(0), sh.Validated) 609 }, 610 }, 611 }, 612 "getstateroot": { 613 { 614 name: "no params", 615 params: `[]`, 616 fail: true, 617 errCode: neorpc.InvalidParamsCode, 618 }, 619 { 620 name: "invalid hash", 621 params: `["0x1234567890"]`, 622 fail: true, 623 errCode: neorpc.ErrUnknownStateRootCode, 624 }, 625 }, 626 "getstorage": { 627 { 628 name: "positive", 629 params: fmt.Sprintf(`["%s", "dGVzdGtleQ=="]`, testContractHash), 630 result: func(e *executor) any { 631 v := base64.StdEncoding.EncodeToString([]byte("newtestvalue")) 632 return &v 633 }, 634 }, 635 { 636 name: "missing key", 637 params: fmt.Sprintf(`["%s", "dGU="]`, testContractHash), 638 fail: true, 639 errCode: neorpc.ErrUnknownStorageItemCode, 640 }, 641 { 642 name: "no params", 643 params: `[]`, 644 fail: true, 645 errCode: neorpc.InvalidParamsCode, 646 }, 647 { 648 name: "no second parameter", 649 params: fmt.Sprintf(`["%s"]`, testContractHash), 650 fail: true, 651 errCode: neorpc.InvalidParamsCode, 652 }, 653 { 654 name: "invalid hash", 655 params: `["notahex"]`, 656 fail: true, 657 errCode: neorpc.InvalidParamsCode, 658 }, 659 { 660 name: "invalid key", 661 params: fmt.Sprintf(`["%s", "notabase64$"]`, testContractHash), 662 fail: true, 663 errCode: neorpc.InvalidParamsCode, 664 }, 665 }, 666 "getstoragehistoric": { 667 { 668 name: "positive", 669 params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa10"))), 670 result: func(e *executor) any { 671 v := base64.StdEncoding.EncodeToString([]byte("v2")) 672 return &v 673 }, 674 }, 675 { 676 name: "missing key", 677 params: fmt.Sprintf(`["%s", "%s", "dGU="]`, block20StateRootLE, testContractHash), 678 fail: true, 679 errCode: neorpc.ErrUnknownStorageItemCode, 680 }, 681 { 682 name: "no params", 683 params: `[]`, 684 fail: true, 685 errCode: neorpc.InvalidParamsCode, 686 }, 687 { 688 name: "no second parameter", 689 params: fmt.Sprintf(`["%s"]`, block20StateRootLE), 690 fail: true, 691 errCode: neorpc.InvalidParamsCode, 692 }, 693 { 694 name: "no third parameter", 695 params: fmt.Sprintf(`["%s", "%s"]`, block20StateRootLE, testContractHash), 696 fail: true, 697 errCode: neorpc.InvalidParamsCode, 698 }, 699 { 700 name: "invalid stateroot", 701 params: `["notahex"]`, 702 fail: true, 703 errCode: neorpc.InvalidParamsCode, 704 }, 705 { 706 name: "invalid hash", 707 params: fmt.Sprintf(`["%s", "notahex"]`, block20StateRootLE), 708 fail: true, 709 errCode: neorpc.InvalidParamsCode, 710 }, 711 { 712 name: "invalid key", 713 params: fmt.Sprintf(`["%s", "%s", "notabase64$"]`, block20StateRootLE, testContractHash), 714 fail: true, 715 errCode: neorpc.InvalidParamsCode, 716 }, 717 }, 718 "findstorage": { 719 { 720 name: "not truncated", 721 params: fmt.Sprintf(`["%s", "%s"]`, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa1"))), 722 result: func(_ *executor) any { return new(result.FindStorage) }, 723 check: func(t *testing.T, e *executor, res any) { 724 actual, ok := res.(*result.FindStorage) 725 require.True(t, ok) 726 727 expected := &result.FindStorage{ 728 Results: []result.KeyValue{ 729 { 730 Key: []byte("aa10"), 731 Value: []byte("v2"), 732 }, 733 }, 734 Next: 1, 735 Truncated: false, 736 } 737 require.Equal(t, expected, actual) 738 }, 739 }, 740 { 741 name: "truncated first page", 742 params: fmt.Sprintf(`["%s", "%s"]`, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))), 743 result: func(_ *executor) any { return new(result.FindStorage) }, 744 check: func(t *testing.T, e *executor, res any) { 745 actual, ok := res.(*result.FindStorage) 746 require.True(t, ok) 747 748 expected := &result.FindStorage{ 749 Results: []result.KeyValue{ 750 { 751 Key: []byte("aa"), 752 Value: []byte("v1"), 753 }, 754 { 755 Key: []byte("aa10"), 756 Value: []byte("v2"), 757 }, 758 }, 759 Next: 2, 760 Truncated: true, 761 } 762 require.Equal(t, expected, actual) 763 }, 764 }, 765 { 766 name: "truncated second page", 767 params: fmt.Sprintf(`["%s", "%s", 2]`, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))), 768 result: func(_ *executor) any { return new(result.FindStorage) }, 769 check: func(t *testing.T, e *executor, res any) { 770 actual, ok := res.(*result.FindStorage) 771 require.True(t, ok) 772 773 expected := &result.FindStorage{ 774 Results: []result.KeyValue{ 775 { 776 Key: []byte("aa50"), 777 Value: []byte("v3"), 778 }, 779 }, 780 Next: 3, 781 Truncated: false, 782 } 783 require.Equal(t, expected, actual) 784 }, 785 }, 786 { 787 name: "empty prefix", 788 params: fmt.Sprintf(`["%s", ""]`, storageContractHash), 789 result: func(_ *executor) any { return new(result.FindStorage) }, 790 check: func(t *testing.T, e *executor, res any) { 791 actual, ok := res.(*result.FindStorage) 792 require.True(t, ok) 793 794 expected := &result.FindStorage{ 795 Results: []result.KeyValue{ 796 { 797 Key: []byte{0x01, 0x00}, 798 Value: []byte{}, 799 }, 800 { 801 Key: []byte{0x01, 0x01}, 802 Value: []byte{0x01}, 803 }, 804 }, 805 Next: 2, 806 Truncated: true, 807 } 808 require.Equal(t, expected, actual) 809 }, 810 }, 811 { 812 name: "unknown key", 813 params: fmt.Sprintf(`["%s", "%s"]`, testContractHash, base64.StdEncoding.EncodeToString([]byte("unknown-key"))), 814 result: func(_ *executor) any { return new(result.FindStorage) }, 815 check: func(t *testing.T, e *executor, res any) { 816 actual, ok := res.(*result.FindStorage) 817 require.True(t, ok) 818 819 expected := &result.FindStorage{ 820 Results: []result.KeyValue{}, 821 Next: 0, 822 Truncated: false, 823 } 824 require.Equal(t, expected, actual) 825 }, 826 }, 827 { 828 name: "no params", 829 params: `[]`, 830 fail: true, 831 errCode: neorpc.InvalidParamsCode, 832 }, 833 { 834 name: "no second parameter", 835 params: fmt.Sprintf(`["%s"]`, testContractHash), 836 fail: true, 837 errCode: neorpc.InvalidParamsCode, 838 }, 839 { 840 name: "invalid hash", 841 params: `["notahex"]`, 842 fail: true, 843 errCode: neorpc.InvalidParamsCode, 844 }, 845 { 846 name: "invalid key", 847 params: fmt.Sprintf(`["%s", "notabase64$"]`, testContractHash), 848 fail: true, 849 errCode: neorpc.InvalidParamsCode, 850 }, 851 { 852 name: "invalid page", 853 params: fmt.Sprintf(`["%s", "", "not-an-int"]`, testContractHash), 854 fail: true, 855 errCode: neorpc.InvalidParamsCode, 856 }, 857 }, 858 "findstoragehistoric": { 859 { 860 name: "not truncated", 861 params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa1"))), 862 result: func(_ *executor) any { return new(result.FindStorage) }, 863 check: func(t *testing.T, e *executor, res any) { 864 actual, ok := res.(*result.FindStorage) 865 require.True(t, ok) 866 867 expected := &result.FindStorage{ 868 Results: []result.KeyValue{ 869 { 870 Key: []byte("aa10"), 871 Value: []byte("v2"), 872 }, 873 }, 874 Next: 1, 875 Truncated: false, 876 } 877 require.Equal(t, expected, actual) 878 }, 879 }, 880 { 881 name: "truncated first page", 882 params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))), 883 result: func(_ *executor) any { return new(result.FindStorage) }, 884 check: func(t *testing.T, e *executor, res any) { 885 actual, ok := res.(*result.FindStorage) 886 require.True(t, ok) 887 888 expected := &result.FindStorage{ 889 Results: []result.KeyValue{ 890 { 891 Key: []byte("aa10"), // items traversal order may differ from the one provided by `findstorage` due to MPT traversal strategy. 892 Value: []byte("v2"), 893 }, 894 { 895 Key: []byte("aa50"), 896 Value: []byte("v3"), 897 }, 898 }, 899 Next: 2, 900 Truncated: true, 901 } 902 require.Equal(t, expected, actual) 903 }, 904 }, 905 { 906 name: "truncated second page", 907 params: fmt.Sprintf(`["%s","%s", "%s", 2]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))), 908 result: func(_ *executor) any { return new(result.FindStorage) }, 909 check: func(t *testing.T, e *executor, res any) { 910 actual, ok := res.(*result.FindStorage) 911 require.True(t, ok) 912 913 expected := &result.FindStorage{ 914 Results: []result.KeyValue{ 915 { 916 Key: []byte("aa"), 917 Value: []byte("v1"), 918 }, 919 }, 920 Next: 3, 921 Truncated: false, 922 } 923 require.Equal(t, expected, actual) 924 }, 925 }, 926 { 927 name: "empty prefix", 928 params: fmt.Sprintf(`["%s", "%s", ""]`, block20StateRootLE, nnsContractHash), 929 result: func(_ *executor) any { return new(result.FindStorage) }, 930 check: func(t *testing.T, e *executor, res any) { 931 actual, ok := res.(*result.FindStorage) 932 require.True(t, ok) 933 934 expected := &result.FindStorage{ 935 Results: []result.KeyValue{ 936 { 937 Key: []byte{0x00}, // total supply 938 Value: []byte{0x01}, 939 }, 940 { 941 Key: append([]byte{0x01}, testchain.PrivateKeyByID(0).GetScriptHash().BytesBE()...), // balance of priv0 942 Value: []byte{0x01}, 943 }, 944 }, 945 Next: 2, 946 Truncated: true, 947 } 948 require.Equal(t, expected, actual) 949 }, 950 }, 951 { 952 name: "unknown key", 953 params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("unknown-key"))), 954 result: func(_ *executor) any { return new(result.FindStorage) }, 955 check: func(t *testing.T, e *executor, res any) { 956 actual, ok := res.(*result.FindStorage) 957 require.True(t, ok) 958 959 expected := &result.FindStorage{ 960 Results: []result.KeyValue{}, 961 Next: 0, 962 Truncated: false, 963 } 964 require.Equal(t, expected, actual) 965 }, 966 }, 967 { 968 name: "no params", 969 params: `[]`, 970 fail: true, 971 errCode: neorpc.InvalidParamsCode, 972 }, 973 { 974 name: "invalid stateroot", 975 params: `[12345]`, 976 fail: true, 977 errCode: neorpc.InvalidParamsCode, 978 }, 979 { 980 name: "no second parameter", 981 params: fmt.Sprintf(`["%s"]`, block20StateRootLE), 982 fail: true, 983 errCode: neorpc.InvalidParamsCode, 984 }, 985 { 986 name: "no third parameter", 987 params: fmt.Sprintf(`["%s", "%s"]`, block20StateRootLE, testContractHash), 988 fail: true, 989 errCode: neorpc.InvalidParamsCode, 990 }, 991 { 992 name: "invalid hash", 993 params: fmt.Sprintf(`["%s", "notahex"]`, block20StateRootLE), 994 fail: true, 995 errCode: neorpc.InvalidParamsCode, 996 }, 997 { 998 name: "invalid key", 999 params: fmt.Sprintf(`["%s", "%s", "notabase64$"]`, block20StateRootLE, testContractHash), 1000 fail: true, 1001 errCode: neorpc.InvalidParamsCode, 1002 }, 1003 { 1004 name: "invalid page", 1005 params: fmt.Sprintf(`["%s", "%s", "", "not-an-int"]`, block20StateRootLE, testContractHash), 1006 fail: true, 1007 errCode: neorpc.InvalidParamsCode, 1008 }, 1009 }, 1010 "getbestblockhash": { 1011 { 1012 params: "[]", 1013 result: func(e *executor) any { 1014 v := "0x" + e.chain.CurrentBlockHash().StringLE() 1015 return &v 1016 }, 1017 }, 1018 }, 1019 "getblock": { 1020 { 1021 name: "positive", 1022 params: "[3, 1]", 1023 result: func(_ *executor) any { return &result.Block{} }, 1024 check: func(t *testing.T, e *executor, blockRes any) { 1025 res, ok := blockRes.(*result.Block) 1026 require.True(t, ok) 1027 1028 block, err := e.chain.GetBlock(e.chain.GetHeaderHash(3)) 1029 require.NoErrorf(t, err, "could not get block") 1030 1031 assert.Equal(t, block.Hash(), res.Hash()) 1032 for i, tx := range res.Transactions { 1033 actualTx := block.Transactions[i] 1034 require.True(t, ok) 1035 require.Equal(t, actualTx.Nonce, tx.Nonce) 1036 require.Equal(t, block.Transactions[i].Hash(), tx.Hash()) 1037 } 1038 }, 1039 }, 1040 { 1041 name: "no params", 1042 params: `[]`, 1043 fail: true, 1044 errCode: neorpc.InvalidParamsCode, 1045 }, 1046 { 1047 name: "bad params", 1048 params: `[[]]`, 1049 fail: true, 1050 errCode: neorpc.InvalidParamsCode, 1051 }, 1052 { 1053 name: "invalid height", 1054 params: `[-1]`, 1055 fail: true, 1056 errCode: neorpc.ErrUnknownHeightCode, 1057 }, 1058 { 1059 name: "invalid hash", 1060 params: `["notahex"]`, 1061 fail: true, 1062 errCode: neorpc.InvalidParamsCode, 1063 }, 1064 { 1065 name: "missing hash", 1066 params: `["` + util.Uint256{}.String() + `"]`, 1067 fail: true, 1068 errCode: neorpc.ErrUnknownBlockCode, 1069 }, 1070 }, 1071 "getblockcount": { 1072 { 1073 params: "[]", 1074 result: func(e *executor) any { 1075 v := int(e.chain.BlockHeight() + 1) 1076 return &v 1077 }, 1078 }, 1079 }, 1080 "getblockhash": { 1081 { 1082 params: "[1]", 1083 result: func(e *executor) any { 1084 // We don't have `t` here for proper handling, but 1085 // error here would lead to panic down below. 1086 block, _ := e.chain.GetBlock(e.chain.GetHeaderHash(1)) 1087 expectedHash := "0x" + block.Hash().StringLE() 1088 return &expectedHash 1089 }, 1090 }, 1091 { 1092 name: "string height", 1093 params: `["first"]`, 1094 fail: true, 1095 errCode: neorpc.InvalidParamsCode, 1096 }, 1097 { 1098 name: "invalid number height", 1099 params: `[-2]`, 1100 fail: true, 1101 errCode: neorpc.ErrUnknownHeightCode, 1102 }, 1103 }, 1104 "getblockheader": { 1105 { 1106 name: "invalid verbose type", 1107 params: `["9673799c5b5a294427401cb07d6cc615ada3a0d5c5bf7ed6f0f54f24abb2e2ac", true]`, 1108 fail: true, 1109 errCode: neorpc.ErrUnknownBlockCode, 1110 }, 1111 { 1112 name: "invalid block hash", 1113 params: `["notahash"]`, 1114 fail: true, 1115 errCode: neorpc.InvalidParamsCode, 1116 }, 1117 { 1118 name: "unknown block", 1119 params: `["a6e526375a780335112299f2262501e5e9574c3ba61b16bbc1e282b344f6c141"]`, 1120 fail: true, 1121 errCode: neorpc.ErrUnknownBlockCode, 1122 }, 1123 { 1124 name: "no params", 1125 params: `[]`, 1126 fail: true, 1127 errCode: neorpc.InvalidParamsCode, 1128 }, 1129 }, 1130 "getblockheadercount": { 1131 { 1132 params: "[]", 1133 result: func(e *executor) any { 1134 v := int(e.chain.HeaderHeight() + 1) 1135 return &v 1136 }, 1137 }, 1138 }, 1139 "getblocksysfee": { 1140 { 1141 name: "positive", 1142 params: "[1]", 1143 result: func(e *executor) any { 1144 block, _ := e.chain.GetBlock(e.chain.GetHeaderHash(1)) 1145 1146 var expectedBlockSysFee int64 1147 for _, tx := range block.Transactions { 1148 expectedBlockSysFee += tx.SystemFee 1149 } 1150 return &expectedBlockSysFee 1151 }, 1152 }, 1153 { 1154 name: "no params", 1155 params: `[]`, 1156 fail: true, 1157 errCode: neorpc.InvalidParamsCode, 1158 }, 1159 { 1160 name: "string height", 1161 params: `["first"]`, 1162 fail: true, 1163 errCode: neorpc.InvalidParamsCode, 1164 }, 1165 { 1166 name: "invalid number height", 1167 params: `[-2]`, 1168 fail: true, 1169 errCode: neorpc.ErrUnknownHeightCode, 1170 }, 1171 }, 1172 "getcommittee": { 1173 { 1174 params: "[]", 1175 result: func(e *executor) any { 1176 expected, _ := e.chain.GetCommittee() 1177 sort.Sort(expected) 1178 return &expected 1179 }, 1180 }, 1181 }, 1182 "getconnectioncount": { 1183 { 1184 params: "[]", 1185 result: func(*executor) any { 1186 v := 0 1187 return &v 1188 }, 1189 }, 1190 }, 1191 "getnativecontracts": { 1192 { 1193 params: "[]", 1194 result: func(e *executor) any { 1195 return new([]state.Contract) 1196 }, 1197 check: func(t *testing.T, e *executor, res any) { 1198 lst := res.(*[]state.Contract) 1199 for i := range *lst { 1200 cs := e.chain.GetContractState((*lst)[i].Hash) 1201 require.Equal(t, *cs, (*lst)[i]) 1202 require.True(t, cs.ID <= 0) 1203 } 1204 }, 1205 }, 1206 }, 1207 "getpeers": { 1208 { 1209 params: "[]", 1210 result: func(*executor) any { 1211 return &result.GetPeers{ 1212 Unconnected: []result.Peer{}, 1213 Connected: []result.Peer{}, 1214 Bad: []result.Peer{}, 1215 } 1216 }, 1217 }, 1218 }, 1219 "getrawtransaction": { 1220 { 1221 name: "no params", 1222 params: `[]`, 1223 fail: true, 1224 errCode: neorpc.InvalidParamsCode, 1225 }, 1226 { 1227 name: "invalid hash", 1228 params: `["notahex"]`, 1229 fail: true, 1230 errCode: neorpc.InvalidParamsCode, 1231 }, 1232 { 1233 name: "missing hash", 1234 params: `["` + util.Uint256{}.String() + `"]`, 1235 fail: true, 1236 errCode: neorpc.ErrUnknownTransactionCode, 1237 }, 1238 }, 1239 "gettransactionheight": { 1240 { 1241 name: "positive", 1242 params: `["` + deploymentTxHash + `"]`, 1243 result: func(e *executor) any { 1244 h := 0 1245 return &h 1246 }, 1247 check: func(t *testing.T, e *executor, resp any) { 1248 h, ok := resp.(*int) 1249 require.True(t, ok) 1250 assert.Equal(t, 2, *h) 1251 }, 1252 }, 1253 { 1254 name: "no params", 1255 params: `[]`, 1256 fail: true, 1257 errCode: neorpc.InvalidParamsCode, 1258 }, 1259 { 1260 name: "invalid hash", 1261 params: `["notahex"]`, 1262 fail: true, 1263 errCode: neorpc.InvalidParamsCode, 1264 }, 1265 { 1266 name: "missing hash", 1267 params: `["` + util.Uint256{}.String() + `"]`, 1268 fail: true, 1269 errCode: neorpc.ErrUnknownTransactionCode, 1270 }, 1271 }, 1272 "getunclaimedgas": { 1273 { 1274 name: "no params", 1275 params: "[]", 1276 fail: true, 1277 errCode: neorpc.InvalidParamsCode, 1278 }, 1279 { 1280 name: "invalid address", 1281 params: `["invalid"]`, 1282 fail: true, 1283 errCode: neorpc.InvalidParamsCode, 1284 }, 1285 { 1286 name: "positive", 1287 params: `["` + testchain.MultisigAddress() + `"]`, 1288 result: func(*executor) any { 1289 return &result.UnclaimedGas{} 1290 }, 1291 check: func(t *testing.T, e *executor, resp any) { 1292 actual, ok := resp.(*result.UnclaimedGas) 1293 require.True(t, ok) 1294 expected := result.UnclaimedGas{ 1295 Address: testchain.MultisigScriptHash(), 1296 Unclaimed: *big.NewInt(11500), 1297 } 1298 assert.Equal(t, expected, *actual) 1299 }, 1300 }, 1301 }, 1302 "getcandidates": { 1303 { 1304 params: "[]", 1305 result: func(*executor) any { 1306 return &[]result.Candidate{} 1307 }, 1308 }, 1309 }, 1310 "getnextblockvalidators": { 1311 { 1312 params: "[]", 1313 result: func(*executor) any { 1314 return &[]result.Validator{} 1315 }, 1316 /* preview3 doesn't return any validators until there is a vote 1317 check: func(t *testing.T, e *executor, validators any) { 1318 var expected []result.Validator 1319 sBValidators := e.chain.GetStandByValidators() 1320 for _, sbValidator := range sBValidators { 1321 expected = append(expected, result.Validator{ 1322 PublicKey: *sbValidator, 1323 Votes: 0, 1324 Active: true, 1325 }) 1326 } 1327 1328 actual, ok := validators.(*[]result.Validator) 1329 require.True(t, ok) 1330 1331 assert.ElementsMatch(t, expected, *actual) 1332 }, 1333 */ 1334 }, 1335 }, 1336 "getversion": { 1337 { 1338 params: "[]", 1339 result: func(*executor) any { return &result.Version{} }, 1340 check: func(t *testing.T, e *executor, ver any) { 1341 resp, ok := ver.(*result.Version) 1342 require.True(t, ok) 1343 require.Equal(t, "/NEO-GO:0.98.6-test/", resp.UserAgent) 1344 1345 cfg := e.chain.GetConfig() 1346 require.EqualValues(t, address.NEO3Prefix, resp.Protocol.AddressVersion) 1347 require.EqualValues(t, cfg.Magic, resp.Protocol.Network) 1348 require.EqualValues(t, cfg.TimePerBlock/time.Millisecond, resp.Protocol.MillisecondsPerBlock) 1349 require.EqualValues(t, cfg.MaxTraceableBlocks, resp.Protocol.MaxTraceableBlocks) 1350 require.EqualValues(t, cfg.MaxValidUntilBlockIncrement, resp.Protocol.MaxValidUntilBlockIncrement) 1351 require.EqualValues(t, cfg.MaxTransactionsPerBlock, resp.Protocol.MaxTransactionsPerBlock) 1352 require.EqualValues(t, cfg.MemPoolSize, resp.Protocol.MemoryPoolMaxTransactions) 1353 require.EqualValues(t, cfg.ValidatorsCount, resp.Protocol.ValidatorsCount) 1354 require.EqualValues(t, cfg.InitialGASSupply, resp.Protocol.InitialGasDistribution) 1355 1356 require.Equal(t, 0, len(resp.Protocol.CommitteeHistory)) 1357 require.True(t, resp.Protocol.P2PSigExtensions) // Yeah, notary is enabled. 1358 require.False(t, resp.Protocol.StateRootInHeader) 1359 require.Equal(t, 0, len(resp.Protocol.ValidatorsHistory)) 1360 }, 1361 }, 1362 }, 1363 "invokefunction": { 1364 { 1365 name: "positive", 1366 params: `["50befd26fdf6e4d957c11e078b24ebce6291456f", "test", []]`, 1367 result: func(e *executor) any { return &result.Invoke{} }, 1368 check: func(t *testing.T, e *executor, inv any) { 1369 res, ok := inv.(*result.Invoke) 1370 require.True(t, ok) 1371 assert.NotNil(t, res.Script) 1372 assert.NotEqual(t, "", res.State) 1373 assert.NotEqual(t, 0, res.GasConsumed) 1374 }, 1375 }, 1376 { 1377 name: "positive, with notifications", 1378 params: `["` + nnsContractHash + `", "transfer", [{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"String", "value":"neo.com"},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"]]`, 1379 result: func(e *executor) any { 1380 script := append([]byte{0x0b, 0x0c, 0x07, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x0c, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb, 0x13, 0xc0, 0x1f, 0xc, 0x8, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0xc, 0x14}, nnsHash.BytesBE()...) 1381 script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52) 1382 return &result.Invoke{ 1383 State: "HALT", 1384 GasConsumed: 31922970, 1385 Script: script, 1386 Stack: []stackitem.Item{stackitem.Make(true)}, 1387 Notifications: []state.NotificationEvent{{ 1388 ScriptHash: nnsHash, 1389 Name: "Transfer", 1390 Item: stackitem.NewArray([]stackitem.Item{ 1391 stackitem.Make([]byte{0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x08, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2}), 1392 stackitem.Make([]byte{0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0x0b}), 1393 stackitem.Make(1), 1394 stackitem.Make("neo.com"), 1395 }), 1396 }}, 1397 } 1398 }, 1399 }, 1400 { 1401 name: "positive, with storage changes", 1402 params: `["0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5", "transfer", [{"type":"Hash160", "value":"0xb248508f4ef7088e10c48f14d04be3272ca29eee"},{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"Integer", "value":1},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"],true]`, 1403 result: func(e *executor) any { return &result.Invoke{} }, 1404 check: func(t *testing.T, e *executor, inv any) { 1405 res, ok := inv.(*result.Invoke) 1406 require.True(t, ok) 1407 assert.NotNil(t, res.Script) 1408 assert.Equal(t, "HALT", res.State) 1409 assert.Equal(t, []stackitem.Item{stackitem.Make(true)}, res.Stack) 1410 assert.NotEqual(t, 0, res.GasConsumed) 1411 chg := []dboper.Operation{{ 1412 State: "Changed", 1413 Key: []byte{0xfa, 0xff, 0xff, 0xff, 0xb}, 1414 Value: []byte{0x54, 0xb2, 0xd2, 0xa3, 0x51, 0x79, 0x12}, 1415 }, { 1416 State: "Added", 1417 Key: []byte{0xfb, 0xff, 0xff, 0xff, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb}, 1418 Value: []byte{0x41, 0x04, 0x21, 0x01, 0x01, 0x21, 0x01, 0x18, 0x00, 0x21, 0x00}, 1419 }, { 1420 State: "Changed", 1421 Key: []byte{0xfb, 0xff, 0xff, 0xff, 0x14, 0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x8, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2}, 1422 Value: []byte{0x41, 0x04, 0x21, 0x04, 0x2f, 0xd9, 0xf5, 0x05, 0x21, 0x01, 0x18, 0x00, 0x21, 0x00}, 1423 }, { 1424 State: "Changed", 1425 Key: []byte{0xfa, 0xff, 0xff, 0xff, 0x14, 0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x8, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2}, 1426 Value: []byte{0x41, 0x01, 0x21, 0x05, 0x0c, 0x76, 0x4f, 0xdf, 0x08}, 1427 }} 1428 // Can be returned in any order. 1429 assert.ElementsMatch(t, chg, res.Diagnostics.Changes) 1430 }, 1431 }, 1432 { 1433 name: "positive, verbose", 1434 params: `["` + nnsContractHash + `", "resolve", [{"type":"String", "value":"neo.com"},{"type":"Integer","value":1}], [], true]`, 1435 result: func(e *executor) any { 1436 script := append([]byte{0x11, 0xc, 0x7, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0xc0, 0x1f, 0xc, 0x7, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0xc, 0x14}, nnsHash.BytesBE()...) 1437 script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52) 1438 stdHash, _ := e.chain.GetNativeContractScriptHash(nativenames.StdLib) 1439 cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib) 1440 return &result.Invoke{ 1441 State: "HALT", 1442 GasConsumed: 13970250, 1443 Script: script, 1444 Stack: []stackitem.Item{stackitem.Make("1.2.3.4")}, 1445 Notifications: []state.NotificationEvent{}, 1446 Diagnostics: &result.InvokeDiag{ 1447 Changes: []dboper.Operation{}, 1448 Invocations: []*invocations.Tree{{ 1449 Current: hash.Hash160(script), 1450 Calls: []*invocations.Tree{ 1451 { 1452 Current: nnsHash, 1453 Calls: []*invocations.Tree{ 1454 { 1455 Current: stdHash, 1456 }, 1457 { 1458 Current: cryptoHash, 1459 }, 1460 { 1461 Current: stdHash, 1462 }, 1463 { 1464 Current: cryptoHash, 1465 }, 1466 { 1467 Current: cryptoHash, 1468 }, 1469 }, 1470 }, 1471 }, 1472 }}, 1473 }, 1474 } 1475 }, 1476 }, 1477 { 1478 name: "no params", 1479 params: `[]`, 1480 fail: true, 1481 errCode: neorpc.InvalidParamsCode, 1482 }, 1483 { 1484 name: "not a string", 1485 params: `[42, "test", []]`, 1486 fail: true, 1487 errCode: neorpc.ErrUnknownContractCode, 1488 }, 1489 { 1490 name: "not a scripthash", 1491 params: `["qwerty", "test", []]`, 1492 fail: true, 1493 errCode: neorpc.InvalidParamsCode, 1494 }, 1495 { 1496 name: "bad params", 1497 params: `["50befd26fdf6e4d957c11e078b24ebce6291456f", "test", [{"type": "Integer", "value": "qwerty"}]]`, 1498 fail: true, 1499 errCode: neorpc.InvalidParamsCode, 1500 }, 1501 }, 1502 "invokefunctionhistoric": { 1503 { 1504 name: "positive, by index", 1505 params: `[20, "50befd26fdf6e4d957c11e078b24ebce6291456f", "test", []]`, 1506 result: func(e *executor) any { return &result.Invoke{} }, 1507 check: func(t *testing.T, e *executor, inv any) { 1508 res, ok := inv.(*result.Invoke) 1509 require.True(t, ok) 1510 assert.NotNil(t, res.Script) 1511 assert.NotEqual(t, "", res.State) 1512 assert.NotEqual(t, 0, res.GasConsumed) 1513 }, 1514 }, 1515 { 1516 name: "positive, by stateroot", 1517 params: `["` + block20StateRootLE + `", "50befd26fdf6e4d957c11e078b24ebce6291456f", "test", []]`, 1518 result: func(e *executor) any { return &result.Invoke{} }, 1519 check: func(t *testing.T, e *executor, inv any) { 1520 res, ok := inv.(*result.Invoke) 1521 require.True(t, ok) 1522 assert.NotNil(t, res.Script) 1523 assert.NotEqual(t, "", res.State) 1524 assert.NotEqual(t, 0, res.GasConsumed) 1525 }, 1526 }, 1527 { 1528 name: "positive, with notifications", 1529 params: `[20, "` + nnsContractHash + `", "transfer", [{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"String", "value":"neo.com"},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"]]`, 1530 result: func(e *executor) any { 1531 script := append([]byte{0x0b, 0x0c, 0x07, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x0c, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb, 0x13, 0xc0, 0x1f, 0xc, 0x8, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0xc, 0x14}, nnsHash.BytesBE()...) 1532 script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52) 1533 return &result.Invoke{ 1534 State: "HALT", 1535 GasConsumed: 31922970, 1536 Script: script, 1537 Stack: []stackitem.Item{stackitem.Make(true)}, 1538 Notifications: []state.NotificationEvent{{ 1539 ScriptHash: nnsHash, 1540 Name: "Transfer", 1541 Item: stackitem.NewArray([]stackitem.Item{ 1542 stackitem.Make([]byte{0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x08, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2}), 1543 stackitem.Make([]byte{0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0x0b}), 1544 stackitem.Make(1), 1545 stackitem.Make("neo.com"), 1546 }), 1547 }}, 1548 } 1549 }, 1550 }, 1551 { 1552 name: "positive, verbose", 1553 params: `[20, "` + nnsContractHash + `", "resolve", [{"type":"String", "value":"neo.com"},{"type":"Integer","value":1}], [], true]`, 1554 result: func(e *executor) any { 1555 script := append([]byte{0x11, 0xc, 0x7, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0xc0, 0x1f, 0xc, 0x7, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0xc, 0x14}, nnsHash.BytesBE()...) 1556 script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52) 1557 stdHash, _ := e.chain.GetNativeContractScriptHash(nativenames.StdLib) 1558 cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib) 1559 return &result.Invoke{ 1560 State: "HALT", 1561 GasConsumed: 13970250, 1562 Script: script, 1563 Stack: []stackitem.Item{stackitem.Make("1.2.3.4")}, 1564 Notifications: []state.NotificationEvent{}, 1565 Diagnostics: &result.InvokeDiag{ 1566 Changes: []dboper.Operation{}, 1567 Invocations: []*invocations.Tree{{ 1568 Current: hash.Hash160(script), 1569 Calls: []*invocations.Tree{ 1570 { 1571 Current: nnsHash, 1572 Calls: []*invocations.Tree{ 1573 { 1574 Current: stdHash, 1575 }, 1576 { 1577 Current: cryptoHash, 1578 }, 1579 { 1580 Current: stdHash, 1581 }, 1582 { 1583 Current: cryptoHash, 1584 }, 1585 { 1586 Current: cryptoHash, 1587 }, 1588 }, 1589 }, 1590 }, 1591 }}, 1592 }, 1593 } 1594 }, 1595 }, 1596 { 1597 name: "no params", 1598 params: `[]`, 1599 fail: true, 1600 errCode: neorpc.InvalidParamsCode, 1601 }, 1602 { 1603 name: "no args", 1604 params: `[20]`, 1605 fail: true, 1606 errCode: neorpc.InvalidParamsCode, 1607 }, 1608 { 1609 name: "not a string", 1610 params: `[20, 42, "test", []]`, 1611 fail: true, 1612 errCode: neorpc.ErrUnknownContractCode, 1613 }, 1614 { 1615 name: "not a scripthash", 1616 params: `[20,"qwerty", "test", []]`, 1617 fail: true, 1618 errCode: neorpc.InvalidParamsCode, 1619 }, 1620 { 1621 name: "bad params", 1622 params: `[20,"50befd26fdf6e4d957c11e078b24ebce6291456f", "test", [{"type": "Integer", "value": "qwerty"}]]`, 1623 fail: true, 1624 errCode: neorpc.InvalidParamsCode, 1625 }, 1626 { 1627 name: "bad height", 1628 params: `[100500,"50befd26fdf6e4d957c11e078b24ebce6291456f", "test", [{"type": "Integer", "value": 1}]]`, 1629 fail: true, 1630 errCode: neorpc.InvalidParamsCode, 1631 }, 1632 { 1633 name: "bad stateroot", 1634 params: `["` + util.Uint256{1, 2, 3}.StringLE() + `","50befd26fdf6e4d957c11e078b24ebce6291456f", "test", [{"type": "Integer", "value": 1}]]`, 1635 fail: true, 1636 errCode: neorpc.InvalidParamsCode, 1637 }, 1638 }, 1639 "invokescript": { 1640 { 1641 name: "positive", 1642 params: `["UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY="]`, 1643 result: func(e *executor) any { return &result.Invoke{} }, 1644 check: func(t *testing.T, e *executor, inv any) { 1645 res, ok := inv.(*result.Invoke) 1646 require.True(t, ok) 1647 assert.NotEqual(t, "", res.Script) 1648 assert.NotEqual(t, "", res.State) 1649 assert.NotEqual(t, 0, res.GasConsumed) 1650 }, 1651 }, 1652 { 1653 name: "positive,verbose", 1654 params: `["UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY=",[],true]`, 1655 result: func(e *executor) any { 1656 script := []byte{0x51, 0xc5, 0x6b, 0xd, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x68, 0xf, 0x4e, 0x65, 0x6f, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x61, 0x6c, 0x75, 0x66} 1657 return &result.Invoke{ 1658 State: "FAULT", 1659 GasConsumed: 60, 1660 Script: script, 1661 Stack: []stackitem.Item{}, 1662 FaultException: "at instruction 0 (ROT): too big index", 1663 Notifications: []state.NotificationEvent{}, 1664 Diagnostics: &result.InvokeDiag{ 1665 Changes: []dboper.Operation{}, 1666 Invocations: []*invocations.Tree{{ 1667 Current: hash.Hash160(script), 1668 }}, 1669 }, 1670 } 1671 }, 1672 }, 1673 { 1674 name: "positive, good witness", 1675 // script is base64-encoded `invokescript_contract.avm` representation, hashes are hex-encoded LE bytes of hashes used in the contract with `0x` prefix 1676 params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c01","0x090c060e00010205040307030102000902030f0d"]]`, invokescriptContractAVM), 1677 result: func(e *executor) any { return &result.Invoke{} }, 1678 check: func(t *testing.T, e *executor, inv any) { 1679 res, ok := inv.(*result.Invoke) 1680 require.True(t, ok) 1681 assert.Equal(t, "HALT", res.State) 1682 require.Equal(t, 1, len(res.Stack)) 1683 require.Equal(t, big.NewInt(3), res.Stack[0].Value()) 1684 }, 1685 }, 1686 { 1687 name: "positive, bad witness of second hash", 1688 params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c01"]]`, invokescriptContractAVM), 1689 result: func(e *executor) any { return &result.Invoke{} }, 1690 check: func(t *testing.T, e *executor, inv any) { 1691 res, ok := inv.(*result.Invoke) 1692 require.True(t, ok) 1693 assert.Equal(t, "HALT", res.State) 1694 require.Equal(t, 1, len(res.Stack)) 1695 require.Equal(t, big.NewInt(2), res.Stack[0].Value()) 1696 }, 1697 }, 1698 { 1699 name: "positive, no good hashes", 1700 params: fmt.Sprintf(`["%s"]`, invokescriptContractAVM), 1701 result: func(e *executor) any { return &result.Invoke{} }, 1702 check: func(t *testing.T, e *executor, inv any) { 1703 res, ok := inv.(*result.Invoke) 1704 require.True(t, ok) 1705 assert.Equal(t, "HALT", res.State) 1706 require.Equal(t, 1, len(res.Stack)) 1707 require.Equal(t, big.NewInt(1), res.Stack[0].Value()) 1708 }, 1709 }, 1710 { 1711 name: "positive, bad hashes witness", 1712 params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c02"]]`, invokescriptContractAVM), 1713 result: func(e *executor) any { return &result.Invoke{} }, 1714 check: func(t *testing.T, e *executor, inv any) { 1715 res, ok := inv.(*result.Invoke) 1716 require.True(t, ok) 1717 assert.Equal(t, "HALT", res.State) 1718 assert.Equal(t, 1, len(res.Stack)) 1719 assert.Equal(t, big.NewInt(1), res.Stack[0].Value()) 1720 }, 1721 }, 1722 { 1723 name: "no params", 1724 params: `[]`, 1725 fail: true, 1726 errCode: neorpc.InvalidParamsCode, 1727 }, 1728 { 1729 name: "not a string", 1730 params: `[42]`, 1731 fail: true, 1732 errCode: neorpc.InvalidParamsCode, 1733 }, 1734 { 1735 name: "bas string", 1736 params: `["qwerty"]`, 1737 fail: true, 1738 errCode: neorpc.InvalidParamsCode, 1739 }, 1740 }, 1741 "invokescripthistoric": { 1742 { 1743 name: "positive, by index", 1744 params: `[20,"UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY="]`, 1745 result: func(e *executor) any { return &result.Invoke{} }, 1746 check: func(t *testing.T, e *executor, inv any) { 1747 res, ok := inv.(*result.Invoke) 1748 require.True(t, ok) 1749 assert.NotEqual(t, "", res.Script) 1750 assert.NotEqual(t, "", res.State) 1751 assert.NotEqual(t, 0, res.GasConsumed) 1752 }, 1753 }, 1754 { 1755 name: "positive, by stateroot", 1756 params: `["` + block20StateRootLE + `","UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY="]`, 1757 result: func(e *executor) any { return &result.Invoke{} }, 1758 check: func(t *testing.T, e *executor, inv any) { 1759 res, ok := inv.(*result.Invoke) 1760 require.True(t, ok) 1761 assert.NotEqual(t, "", res.Script) 1762 assert.NotEqual(t, "", res.State) 1763 assert.NotEqual(t, 0, res.GasConsumed) 1764 }, 1765 }, 1766 { 1767 name: "positive,verbose", 1768 params: `[20, "UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY=",[],true]`, 1769 result: func(e *executor) any { 1770 script := []byte{0x51, 0xc5, 0x6b, 0xd, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x68, 0xf, 0x4e, 0x65, 0x6f, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x61, 0x6c, 0x75, 0x66} 1771 return &result.Invoke{ 1772 State: "FAULT", 1773 GasConsumed: 60, 1774 Script: script, 1775 Stack: []stackitem.Item{}, 1776 FaultException: "at instruction 0 (ROT): too big index", 1777 Notifications: []state.NotificationEvent{}, 1778 Diagnostics: &result.InvokeDiag{ 1779 Changes: []dboper.Operation{}, 1780 Invocations: []*invocations.Tree{{ 1781 Current: hash.Hash160(script), 1782 }}, 1783 }, 1784 } 1785 }, 1786 }, 1787 { 1788 name: "positive, good witness", 1789 // script is base64-encoded `invokescript_contract.avm` representation, hashes are hex-encoded LE bytes of hashes used in the contract with `0x` prefix 1790 params: fmt.Sprintf(`[20,"%s",["0x0000000009070e030d0f0e020d0c06050e030c01","0x090c060e00010205040307030102000902030f0d"]]`, invokescriptContractAVM), 1791 result: func(e *executor) any { return &result.Invoke{} }, 1792 check: func(t *testing.T, e *executor, inv any) { 1793 res, ok := inv.(*result.Invoke) 1794 require.True(t, ok) 1795 assert.Equal(t, "HALT", res.State) 1796 require.Equal(t, 1, len(res.Stack)) 1797 require.Equal(t, big.NewInt(3), res.Stack[0].Value()) 1798 }, 1799 }, 1800 { 1801 name: "positive, bad witness of second hash", 1802 params: fmt.Sprintf(`[20,"%s",["0x0000000009070e030d0f0e020d0c06050e030c01"]]`, invokescriptContractAVM), 1803 result: func(e *executor) any { return &result.Invoke{} }, 1804 check: func(t *testing.T, e *executor, inv any) { 1805 res, ok := inv.(*result.Invoke) 1806 require.True(t, ok) 1807 assert.Equal(t, "HALT", res.State) 1808 require.Equal(t, 1, len(res.Stack)) 1809 require.Equal(t, big.NewInt(2), res.Stack[0].Value()) 1810 }, 1811 }, 1812 { 1813 name: "positive, no good hashes", 1814 params: fmt.Sprintf(`[20,"%s"]`, invokescriptContractAVM), 1815 result: func(e *executor) any { return &result.Invoke{} }, 1816 check: func(t *testing.T, e *executor, inv any) { 1817 res, ok := inv.(*result.Invoke) 1818 require.True(t, ok) 1819 assert.Equal(t, "HALT", res.State) 1820 require.Equal(t, 1, len(res.Stack)) 1821 require.Equal(t, big.NewInt(1), res.Stack[0].Value()) 1822 }, 1823 }, 1824 { 1825 name: "positive, bad hashes witness", 1826 params: fmt.Sprintf(`[20,"%s",["0x0000000009070e030d0f0e020d0c06050e030c02"]]`, invokescriptContractAVM), 1827 result: func(e *executor) any { return &result.Invoke{} }, 1828 check: func(t *testing.T, e *executor, inv any) { 1829 res, ok := inv.(*result.Invoke) 1830 require.True(t, ok) 1831 assert.Equal(t, "HALT", res.State) 1832 assert.Equal(t, 1, len(res.Stack)) 1833 assert.Equal(t, big.NewInt(1), res.Stack[0].Value()) 1834 }, 1835 }, 1836 { 1837 name: "no params", 1838 params: `[]`, 1839 fail: true, 1840 errCode: neorpc.InvalidParamsCode, 1841 }, 1842 { 1843 name: "no script", 1844 params: `[20]`, 1845 fail: true, 1846 errCode: neorpc.InvalidParamsCode, 1847 }, 1848 { 1849 name: "not a string", 1850 params: `[20,42]`, 1851 fail: true, 1852 errCode: neorpc.InvalidParamsCode, 1853 }, 1854 { 1855 name: "bad string", 1856 params: `[20, "qwerty"]`, 1857 fail: true, 1858 errCode: neorpc.InvalidParamsCode, 1859 }, 1860 { 1861 name: "bad height", 1862 params: `[100500,"qwerty"]`, 1863 fail: true, 1864 errCode: neorpc.InvalidParamsCode, 1865 }, 1866 { 1867 name: "bad stateroot", 1868 params: `["` + util.Uint256{1, 2, 3}.StringLE() + `","UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY="]`, 1869 fail: true, 1870 errCode: neorpc.InvalidParamsCode, 1871 }, 1872 }, 1873 "invokecontractverify": { 1874 { 1875 name: "positive", 1876 params: fmt.Sprintf(`["%s", [], [{"account":"%s"}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()), 1877 result: func(e *executor) any { return &result.Invoke{} }, 1878 check: func(t *testing.T, e *executor, inv any) { 1879 res, ok := inv.(*result.Invoke) 1880 require.True(t, ok) 1881 assert.Nil(t, res.Script) // empty witness invocation script (pushes args of `verify` on stack, but this `verify` don't have args) 1882 assert.Equal(t, "HALT", res.State) 1883 assert.NotEqual(t, 0, res.GasConsumed) 1884 assert.Equal(t, true, res.Stack[0].Value().(bool), fmt.Sprintf("check address in verification_contract.go: expected %s", testchain.PrivateKeyByID(0).Address())) 1885 }, 1886 }, 1887 { 1888 name: "positive, no signers", 1889 params: fmt.Sprintf(`["%s", []]`, verifyContractHash), 1890 result: func(e *executor) any { return &result.Invoke{} }, 1891 check: func(t *testing.T, e *executor, inv any) { 1892 res, ok := inv.(*result.Invoke) 1893 require.True(t, ok) 1894 assert.Nil(t, res.Script) 1895 assert.Equal(t, "HALT", res.State, res.FaultException) 1896 assert.NotEqual(t, 0, res.GasConsumed) 1897 assert.Equal(t, false, res.Stack[0].Value().(bool)) 1898 }, 1899 }, 1900 { 1901 name: "positive, no arguments", 1902 params: fmt.Sprintf(`["%s"]`, verifyContractHash), 1903 result: func(e *executor) any { return &result.Invoke{} }, 1904 check: func(t *testing.T, e *executor, inv any) { 1905 res, ok := inv.(*result.Invoke) 1906 require.True(t, ok) 1907 assert.Nil(t, res.Script) 1908 assert.Equal(t, "HALT", res.State, res.FaultException) 1909 assert.NotEqual(t, 0, res.GasConsumed) 1910 assert.Equal(t, false, res.Stack[0].Value().(bool)) 1911 }, 1912 }, 1913 { 1914 name: "positive, with signers and scripts", 1915 params: fmt.Sprintf(`["%s", [], [{"account":"%s", "invocation":"MQo=", "verification": ""}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()), 1916 result: func(e *executor) any { return &result.Invoke{} }, 1917 check: func(t *testing.T, e *executor, inv any) { 1918 res, ok := inv.(*result.Invoke) 1919 require.True(t, ok) 1920 assert.Nil(t, res.Script) 1921 assert.Equal(t, "HALT", res.State) 1922 assert.NotEqual(t, 0, res.GasConsumed) 1923 assert.Equal(t, true, res.Stack[0].Value().(bool)) 1924 }, 1925 }, 1926 { 1927 name: "positive, with arguments, result=true", 1928 params: fmt.Sprintf(`["%s", [{"type": "String", "value": "good_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": false}]]`, verifyWithArgsContractHash), 1929 result: func(e *executor) any { return &result.Invoke{} }, 1930 check: func(t *testing.T, e *executor, inv any) { 1931 res, ok := inv.(*result.Invoke) 1932 require.True(t, ok) 1933 expectedInvScript := io.NewBufBinWriter() 1934 emit.Bool(expectedInvScript.BinWriter, false) 1935 emit.Int(expectedInvScript.BinWriter, int64(4)) 1936 emit.String(expectedInvScript.BinWriter, "good_string") 1937 require.NoError(t, expectedInvScript.Err) 1938 assert.Equal(t, expectedInvScript.Bytes(), res.Script) // witness invocation script (pushes args of `verify` on stack) 1939 assert.Equal(t, "HALT", res.State, res.FaultException) 1940 assert.NotEqual(t, 0, res.GasConsumed) 1941 assert.Equal(t, true, res.Stack[0].Value().(bool)) 1942 }, 1943 }, 1944 { 1945 name: "positive, with arguments, result=false", 1946 params: fmt.Sprintf(`["%s", [{"type": "String", "value": "invalid_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": false}]]`, verifyWithArgsContractHash), 1947 result: func(e *executor) any { return &result.Invoke{} }, 1948 check: func(t *testing.T, e *executor, inv any) { 1949 res, ok := inv.(*result.Invoke) 1950 require.True(t, ok) 1951 expectedInvScript := io.NewBufBinWriter() 1952 emit.Bool(expectedInvScript.BinWriter, false) 1953 emit.Int(expectedInvScript.BinWriter, int64(4)) 1954 emit.String(expectedInvScript.BinWriter, "invalid_string") 1955 require.NoError(t, expectedInvScript.Err) 1956 assert.Equal(t, expectedInvScript.Bytes(), res.Script) 1957 assert.Equal(t, "HALT", res.State, res.FaultException) 1958 assert.NotEqual(t, 0, res.GasConsumed) 1959 assert.Equal(t, false, res.Stack[0].Value().(bool)) 1960 }, 1961 }, 1962 { 1963 name: "invalid call args", 1964 params: fmt.Sprintf(`["%s", [{"type":"Map","value":{"key":"value"}}]]`, verifyWithArgsContractHash), 1965 fail: true, 1966 errCode: neorpc.InternalServerErrorCode, 1967 }, 1968 { 1969 name: "negative, wrong signer", 1970 params: fmt.Sprintf(`["%s", [], [{"account":"aaa"}]]`, verifyContractHash), 1971 fail: true, 1972 errCode: neorpc.InvalidParamsCode, 1973 }, 1974 { 1975 name: "unknown contract", 1976 params: fmt.Sprintf(`["%s", []]`, util.Uint160{}.String()), 1977 fail: true, 1978 errCode: neorpc.ErrUnknownContractCode, 1979 }, 1980 { 1981 name: "no params", 1982 params: `[]`, 1983 fail: true, 1984 errCode: neorpc.InvalidParamsCode, 1985 }, 1986 { 1987 name: "not a string", 1988 params: `[42, []]`, 1989 fail: true, 1990 errCode: neorpc.ErrUnknownContractCode, 1991 }, 1992 }, 1993 "invokecontractverifyhistoric": { 1994 { 1995 name: "positive, by index", 1996 params: fmt.Sprintf(`[20,"%s", [], [{"account":"%s"}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()), 1997 result: func(e *executor) any { return &result.Invoke{} }, 1998 check: func(t *testing.T, e *executor, inv any) { 1999 res, ok := inv.(*result.Invoke) 2000 require.True(t, ok) 2001 assert.Nil(t, res.Script) // empty witness invocation script (pushes args of `verify` on stack, but this `verify` don't have args) 2002 assert.Equal(t, "HALT", res.State) 2003 assert.NotEqual(t, 0, res.GasConsumed) 2004 assert.Equal(t, true, res.Stack[0].Value().(bool), fmt.Sprintf("check address in verification_contract.go: expected %s", testchain.PrivateKeyByID(0).Address())) 2005 }, 2006 }, 2007 { 2008 name: "positive, by stateroot", 2009 params: fmt.Sprintf(`["`+block20StateRootLE+`","%s", [], [{"account":"%s"}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()), 2010 result: func(e *executor) any { return &result.Invoke{} }, 2011 check: func(t *testing.T, e *executor, inv any) { 2012 res, ok := inv.(*result.Invoke) 2013 require.True(t, ok) 2014 assert.Nil(t, res.Script) // empty witness invocation script (pushes args of `verify` on stack, but this `verify` don't have args) 2015 assert.Equal(t, "HALT", res.State) 2016 assert.NotEqual(t, 0, res.GasConsumed) 2017 assert.Equal(t, true, res.Stack[0].Value().(bool), fmt.Sprintf("check address in verification_contract.go: expected %s", testchain.PrivateKeyByID(0).Address())) 2018 }, 2019 }, 2020 { 2021 name: "positive, no signers", 2022 params: fmt.Sprintf(`[20,"%s", []]`, verifyContractHash), 2023 result: func(e *executor) any { return &result.Invoke{} }, 2024 check: func(t *testing.T, e *executor, inv any) { 2025 res, ok := inv.(*result.Invoke) 2026 require.True(t, ok) 2027 assert.Nil(t, res.Script) 2028 assert.Equal(t, "HALT", res.State, res.FaultException) 2029 assert.NotEqual(t, 0, res.GasConsumed) 2030 assert.Equal(t, false, res.Stack[0].Value().(bool)) 2031 }, 2032 }, 2033 { 2034 name: "positive, no arguments", 2035 params: fmt.Sprintf(`[20,"%s"]`, verifyContractHash), 2036 result: func(e *executor) any { return &result.Invoke{} }, 2037 check: func(t *testing.T, e *executor, inv any) { 2038 res, ok := inv.(*result.Invoke) 2039 require.True(t, ok) 2040 assert.Nil(t, res.Script) 2041 assert.Equal(t, "HALT", res.State, res.FaultException) 2042 assert.NotEqual(t, 0, res.GasConsumed) 2043 assert.Equal(t, false, res.Stack[0].Value().(bool)) 2044 }, 2045 }, 2046 { 2047 name: "positive, with signers and scripts", 2048 params: fmt.Sprintf(`[20,"%s", [], [{"account":"%s", "invocation":"MQo=", "verification": ""}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()), 2049 result: func(e *executor) any { return &result.Invoke{} }, 2050 check: func(t *testing.T, e *executor, inv any) { 2051 res, ok := inv.(*result.Invoke) 2052 require.True(t, ok) 2053 assert.Nil(t, res.Script) 2054 assert.Equal(t, "HALT", res.State) 2055 assert.NotEqual(t, 0, res.GasConsumed) 2056 assert.Equal(t, true, res.Stack[0].Value().(bool)) 2057 }, 2058 }, 2059 { 2060 name: "positive, with arguments, result=true", 2061 params: fmt.Sprintf(`[20,"%s", [{"type": "String", "value": "good_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": false}]]`, verifyWithArgsContractHash), 2062 result: func(e *executor) any { return &result.Invoke{} }, 2063 check: func(t *testing.T, e *executor, inv any) { 2064 res, ok := inv.(*result.Invoke) 2065 require.True(t, ok) 2066 expectedInvScript := io.NewBufBinWriter() 2067 emit.Bool(expectedInvScript.BinWriter, false) 2068 emit.Int(expectedInvScript.BinWriter, int64(4)) 2069 emit.String(expectedInvScript.BinWriter, "good_string") 2070 require.NoError(t, expectedInvScript.Err) 2071 assert.Equal(t, expectedInvScript.Bytes(), res.Script) // witness invocation script (pushes args of `verify` on stack) 2072 assert.Equal(t, "HALT", res.State, res.FaultException) 2073 assert.NotEqual(t, 0, res.GasConsumed) 2074 assert.Equal(t, true, res.Stack[0].Value().(bool)) 2075 }, 2076 }, 2077 { 2078 name: "positive, with arguments, result=false", 2079 params: fmt.Sprintf(`[20, "%s", [{"type": "String", "value": "invalid_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": false}]]`, verifyWithArgsContractHash), 2080 result: func(e *executor) any { return &result.Invoke{} }, 2081 check: func(t *testing.T, e *executor, inv any) { 2082 res, ok := inv.(*result.Invoke) 2083 require.True(t, ok) 2084 expectedInvScript := io.NewBufBinWriter() 2085 emit.Bool(expectedInvScript.BinWriter, false) 2086 emit.Int(expectedInvScript.BinWriter, int64(4)) 2087 emit.String(expectedInvScript.BinWriter, "invalid_string") 2088 require.NoError(t, expectedInvScript.Err) 2089 assert.Equal(t, expectedInvScript.Bytes(), res.Script) 2090 assert.Equal(t, "HALT", res.State, res.FaultException) 2091 assert.NotEqual(t, 0, res.GasConsumed) 2092 assert.Equal(t, false, res.Stack[0].Value().(bool)) 2093 }, 2094 }, 2095 { 2096 name: "unknown contract", 2097 params: fmt.Sprintf(`[20, "%s", []]`, util.Uint160{}.String()), 2098 fail: true, 2099 errCode: neorpc.ErrUnknownContractCode, 2100 }, 2101 { 2102 name: "no params", 2103 params: `[]`, 2104 fail: true, 2105 errCode: neorpc.InvalidParamsCode, 2106 }, 2107 { 2108 name: "no args", 2109 params: `[20]`, 2110 fail: true, 2111 errCode: neorpc.InvalidParamsCode, 2112 }, 2113 { 2114 name: "not a string", 2115 params: `[20,42, []]`, 2116 fail: true, 2117 errCode: neorpc.ErrUnknownContractCode, 2118 }, 2119 }, 2120 "sendrawtransaction": { 2121 { 2122 name: "positive", 2123 params: `["AB0AAACWP5gAAAAAAEDaEgAAAAAAGAAAAAHunqIsJ+NL0BSPxBCOCPdOj1BIsoAAXgsDAOh2SBcAAAAMFBEmW7QXJQBBvgTo+iQOOPV8HlabDBTunqIsJ+NL0BSPxBCOCPdOj1BIshTAHwwIdHJhbnNmZXIMFPVj6kC8KD1NDgXEjqMFs/Kgc0DvQWJ9W1IBQgxAJ6norhWoZxp+Hj1JFhi+Z3qI9DUkLSbfsbaLSaJIqxTfdmPbNFDVK1G+oa+LWmpRp/bj9+QZM7yC+S6HXUI7rigMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwkFW57Mn"]`, 2124 result: func(e *executor) any { return &result.RelayResult{} }, 2125 check: func(t *testing.T, e *executor, inv any) { 2126 res, ok := inv.(*result.RelayResult) 2127 require.True(t, ok) 2128 expectedHash := "c11861dec1dd0f188608b725095041fcfc90abe51eea044993f122f22472753e" 2129 assert.Equal(t, expectedHash, res.Hash.StringLE()) 2130 }, 2131 }, 2132 { 2133 name: "already in pool", 2134 params: `["AB0AAACWP5gAAAAAAEDaEgAAAAAAGAAAAAHunqIsJ+NL0BSPxBCOCPdOj1BIsoAAXgsDAOh2SBcAAAAMFBEmW7QXJQBBvgTo+iQOOPV8HlabDBTunqIsJ+NL0BSPxBCOCPdOj1BIshTAHwwIdHJhbnNmZXIMFPVj6kC8KD1NDgXEjqMFs/Kgc0DvQWJ9W1IBQgxAJ6norhWoZxp+Hj1JFhi+Z3qI9DUkLSbfsbaLSaJIqxTfdmPbNFDVK1G+oa+LWmpRp/bj9+QZM7yC+S6HXUI7rigMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwkFW57Mn"]`, 2135 fail: true, 2136 errCode: neorpc.ErrAlreadyInPoolCode, 2137 }, 2138 { 2139 name: "negative", 2140 params: `["AAoAAAAxboUQOQGdOd/Cw31sP+4Z/VgJhwAAAAAAAAAA8q0FAAAAAACwBAAAAAExboUQOQGdOd/Cw31sP+4Z/VgJhwFdAwDodkgXAAAADBQgcoJ0r6/Db0OgcdMoz6PmKdnLsAwUMW6FEDkBnTnfwsN9bD/uGf1YCYcTwAwIdHJhbnNmZXIMFIl3INjNdvTwCr+jfA7diJwgj96bQWJ9W1I4AUIMQN+VMUEnEWlCHOurXSegFj4pTXx/LQUltEmHRTRIFP09bFxZHJsXI9BdQoVvQJrbCEz2esySHPr8YpEzpeteen4pDCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQQqQav8="]`, 2141 fail: true, 2142 errCode: neorpc.InvalidParamsCode, 2143 }, 2144 { 2145 name: "no params", 2146 params: `[]`, 2147 fail: true, 2148 errCode: neorpc.InvalidParamsCode, 2149 }, 2150 { 2151 name: "invalid string", 2152 params: `["notabase64%"]`, 2153 fail: true, 2154 errCode: neorpc.InvalidParamsCode, 2155 }, 2156 { 2157 name: "invalid tx", 2158 params: `["AnTXkgcmF3IGNvbnRyYWNw=="]`, 2159 fail: true, 2160 errCode: neorpc.InvalidParamsCode, 2161 }, 2162 }, 2163 "submitblock": { 2164 { 2165 name: "invalid base64", 2166 params: `["%%%"]`, 2167 fail: true, 2168 errCode: neorpc.InvalidParamsCode, 2169 }, 2170 { 2171 name: "invalid block bytes", 2172 params: `["AAAAACc="]`, 2173 fail: true, 2174 errCode: neorpc.InvalidParamsCode, 2175 }, 2176 { 2177 name: "no params", 2178 params: `[]`, 2179 fail: true, 2180 errCode: neorpc.InvalidParamsCode, 2181 }, 2182 }, 2183 "submitoracleresponse": { 2184 { 2185 name: "no params", 2186 params: `[]`, 2187 fail: true, 2188 errCode: neorpc.ErrOracleDisabledCode, 2189 }, 2190 }, 2191 "submitnotaryrequest": { 2192 { 2193 name: "no params", 2194 params: `[]`, 2195 fail: true, 2196 errCode: neorpc.InvalidParamsCode, 2197 }, 2198 }, 2199 "validateaddress": { 2200 { 2201 name: "positive", 2202 params: `["Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2"]`, 2203 result: func(*executor) any { return &result.ValidateAddress{} }, 2204 check: func(t *testing.T, e *executor, va any) { 2205 res, ok := va.(*result.ValidateAddress) 2206 require.True(t, ok) 2207 assert.Equal(t, "Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2", res.Address) 2208 assert.True(t, res.IsValid) 2209 }, 2210 }, 2211 { 2212 name: "negative", 2213 params: "[1]", 2214 result: func(*executor) any { 2215 return &result.ValidateAddress{ 2216 Address: float64(1), 2217 IsValid: false, 2218 } 2219 }, 2220 }, 2221 { 2222 name: "no params", 2223 params: "[]", 2224 fail: true, 2225 errCode: neorpc.InvalidParamsCode, 2226 }, 2227 }, 2228 } 2229 2230 func TestRPC(t *testing.T) { 2231 t.Run("http", func(t *testing.T) { 2232 testRPCProtocol(t, doRPCCallOverHTTP) 2233 }) 2234 2235 t.Run("websocket", func(t *testing.T) { 2236 testRPCProtocol(t, doRPCCallOverWS) 2237 }) 2238 } 2239 2240 func TestSubmitOracle(t *testing.T) { 2241 rpc := `{"jsonrpc": "2.0", "id": 1, "method": "submitoracleresponse", "params": %s}` 2242 2243 t.Run("OracleDisabled", func(t *testing.T) { 2244 _, _, httpSrv := initClearServerWithCustomConfig(t, func(c *config.Config) { 2245 c.ApplicationConfiguration.Oracle.Enabled = false 2246 }) 2247 req := fmt.Sprintf(rpc, "[]") 2248 body := doRPCCallOverHTTP(req, httpSrv.URL, t) 2249 checkErrGetResult(t, body, true, neorpc.ErrOracleDisabledCode) 2250 }) 2251 2252 _, _, httpSrv := initClearServerWithServices(t, true, false, false) 2253 2254 runCase := func(t *testing.T, fail bool, errCode int64, params ...string) func(t *testing.T) { 2255 return func(t *testing.T) { 2256 ps := `[` + strings.Join(params, ",") + `]` 2257 req := fmt.Sprintf(rpc, ps) 2258 body := doRPCCallOverHTTP(req, httpSrv.URL, t) 2259 checkErrGetResult(t, body, fail, errCode) 2260 } 2261 } 2262 t.Run("MissingKey", runCase(t, true, neorpc.InvalidParamsCode)) 2263 t.Run("InvalidKey", runCase(t, true, neorpc.InvalidParamsCode, `"1234"`)) 2264 2265 priv, err := keys.NewPrivateKey() 2266 require.NoError(t, err) 2267 pubStr := `"` + base64.StdEncoding.EncodeToString(priv.PublicKey().Bytes()) + `"` 2268 t.Run("InvalidReqID", runCase(t, true, neorpc.InvalidParamsCode, pubStr, `"notanumber"`)) 2269 t.Run("InvalidTxSignature", runCase(t, true, neorpc.InvalidParamsCode, pubStr, `1`, `"qwerty"`)) 2270 2271 txSig := priv.Sign([]byte{1, 2, 3}) 2272 txSigStr := `"` + base64.StdEncoding.EncodeToString(txSig) + `"` 2273 t.Run("MissingMsgSignature", runCase(t, true, neorpc.InvalidParamsCode, pubStr, `1`, txSigStr)) 2274 t.Run("InvalidMsgSignature", runCase(t, true, neorpc.ErrInvalidSignatureCode, pubStr, `1`, txSigStr, `"0123"`)) 2275 2276 msg := rpc2.GetMessage(priv.PublicKey().Bytes(), 1, txSig) 2277 msgSigStr := `"` + base64.StdEncoding.EncodeToString(priv.Sign(msg)) + `"` 2278 t.Run("Valid", runCase(t, false, 0, pubStr, `1`, txSigStr, msgSigStr)) 2279 } 2280 2281 func TestNotaryRequestRPC(t *testing.T) { 2282 var notaryRequest1, notaryRequest2 *payload.P2PNotaryRequest 2283 rpcSubmit := `{"jsonrpc": "2.0", "id": 1, "method": "submitnotaryrequest", "params": %s}` 2284 rpcPool := `{"jsonrpc": "2.0", "id": 1, "method": "getrawnotarypool", "params": []}` 2285 rpcTx := `{"jsonrpc": "2.0", "id": 1, "method": "getrawnotarytransaction", "params": ["%s", %d]}` 2286 2287 t.Run("disabled P2PSigExtensions", func(t *testing.T) { 2288 _, _, httpSrv := initClearServerWithCustomConfig(t, func(c *config.Config) { 2289 c.ProtocolConfiguration.P2PSigExtensions = false 2290 }) 2291 t.Run("submitnotaryrequest", func(t *testing.T) { 2292 body := doRPCCallOverHTTP(fmt.Sprintf(rpcSubmit, "[]"), httpSrv.URL, t) 2293 checkErrGetResult(t, body, true, neorpc.InternalServerErrorCode) 2294 }) 2295 t.Run("getrawnotarypool", func(t *testing.T) { 2296 body := doRPCCallOverHTTP(rpcPool, httpSrv.URL, t) 2297 checkErrGetResult(t, body, true, neorpc.InternalServerErrorCode) 2298 }) 2299 t.Run("getrawnotarytransaction", func(t *testing.T) { 2300 body := doRPCCallOverHTTP(fmt.Sprintf(rpcTx, " ", 1), httpSrv.URL, t) 2301 checkErrGetResult(t, body, true, neorpc.InternalServerErrorCode) 2302 }) 2303 }) 2304 2305 chain, _, httpSrv := initServerWithInMemoryChainAndServices(t, false, true, false) 2306 2307 submitNotaryRequest := func(t *testing.T, fail bool, errCode int64, params ...string) func(t *testing.T) { 2308 return func(t *testing.T) { 2309 ps := `[` + strings.Join(params, ",") + `]` 2310 req := fmt.Sprintf(rpcSubmit, ps) 2311 body := doRPCCallOverHTTP(req, httpSrv.URL, t) 2312 checkErrGetResult(t, body, fail, errCode) 2313 } 2314 } 2315 2316 t.Run("getrawnotarypool", func(t *testing.T) { 2317 t.Run("empty pool", func(t *testing.T) { 2318 body := doRPCCallOverHTTP(rpcPool, httpSrv.URL, t) 2319 res := checkErrGetResult(t, body, false, 0) 2320 actual := new(result.RawNotaryPool) 2321 require.NoError(t, json.Unmarshal(res, actual)) 2322 require.Equal(t, 0, len(actual.Hashes)) 2323 }) 2324 2325 sender := testchain.PrivateKeyByID(0) // owner of the deposit in testchain 2326 notaryRequest1 = createValidNotaryRequest(chain, sender, 1, 2_0000_0000, nil) 2327 nrBytes, err := notaryRequest1.Bytes() 2328 require.NoError(t, err) 2329 str := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(nrBytes)) 2330 submitNotaryRequest(t, false, 0, str)(t) 2331 2332 t.Run("nonempty pool", func(t *testing.T) { 2333 // get notary pool & check tx hashes 2334 body := doRPCCallOverHTTP(rpcPool, httpSrv.URL, t) 2335 res := checkErrGetResult(t, body, false, 0) 2336 actual := new(result.RawNotaryPool) 2337 require.NoError(t, json.Unmarshal(res, actual)) 2338 require.Equal(t, 1, len(actual.Hashes)) 2339 for actMain, actFallbacks := range actual.Hashes { 2340 require.Equal(t, notaryRequest1.MainTransaction.Hash(), actMain) 2341 require.Equal(t, 1, len(actFallbacks)) 2342 require.Equal(t, notaryRequest1.FallbackTransaction.Hash(), actFallbacks[0]) 2343 } 2344 }) 2345 2346 notaryRequest2 = createValidNotaryRequest(chain, sender, 2, 3_0000_0000, notaryRequest1.MainTransaction) 2347 nrBytes2, err := notaryRequest2.Bytes() 2348 require.NoError(t, err) 2349 str2 := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(nrBytes2)) 2350 submitNotaryRequest(t, false, 0, str2)(t) 2351 2352 t.Run("pool with 2", func(t *testing.T) { 2353 // get notary pool & check tx hashes 2354 body := doRPCCallOverHTTP(rpcPool, httpSrv.URL, t) 2355 res := checkErrGetResult(t, body, false, 0) 2356 actual := new(result.RawNotaryPool) 2357 require.NoError(t, json.Unmarshal(res, actual)) 2358 require.Equal(t, 1, len(actual.Hashes)) 2359 for actMain, actFallbacks := range actual.Hashes { 2360 require.Equal(t, notaryRequest1.MainTransaction.Hash(), actMain) 2361 require.Equal(t, 2, len(actFallbacks)) 2362 // The second fallback transaction has higher priority, so it's first in the slice. 2363 require.Equal(t, notaryRequest1.FallbackTransaction.Hash(), actFallbacks[1]) 2364 require.Equal(t, notaryRequest2.FallbackTransaction.Hash(), actFallbacks[0]) 2365 } 2366 }) 2367 }) 2368 2369 t.Run("submitnotaryrequest", func(t *testing.T) { 2370 t.Run("missing request", submitNotaryRequest(t, true, neorpc.InvalidParamsCode)) 2371 t.Run("not a base64", submitNotaryRequest(t, true, neorpc.InvalidParamsCode, `"not-a-base64$"`)) 2372 t.Run("invalid request bytes", submitNotaryRequest(t, true, neorpc.InvalidParamsCode, `"not-a-request"`)) 2373 t.Run("invalid request", func(t *testing.T) { 2374 mainTx := &transaction.Transaction{ 2375 Attributes: []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}}, 2376 Script: []byte{byte(opcode.RET)}, 2377 ValidUntilBlock: 123, 2378 Signers: []transaction.Signer{{Account: util.Uint160{1, 5, 9}}}, 2379 Scripts: []transaction.Witness{{ 2380 InvocationScript: []byte{1, 4, 7}, 2381 VerificationScript: []byte{3, 6, 9}, 2382 }}, 2383 } 2384 fallbackTx := &transaction.Transaction{ 2385 Script: []byte{byte(opcode.RET)}, 2386 ValidUntilBlock: 123, 2387 Attributes: []transaction.Attribute{ 2388 {Type: transaction.NotValidBeforeT, Value: &transaction.NotValidBefore{Height: 123}}, 2389 {Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: mainTx.Hash()}}, 2390 {Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}}, 2391 }, 2392 Signers: []transaction.Signer{{Account: util.Uint160{1, 4, 7}}, {Account: util.Uint160{9, 8, 7}}}, 2393 Scripts: []transaction.Witness{ 2394 {InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 0)}, 2395 {InvocationScript: []byte{1, 2, 3}, VerificationScript: []byte{1, 2, 3}}}, 2396 } 2397 p := &payload.P2PNotaryRequest{ 2398 MainTransaction: mainTx, 2399 FallbackTransaction: fallbackTx, 2400 Witness: transaction.Witness{ 2401 InvocationScript: []byte{1, 2, 3}, 2402 VerificationScript: []byte{7, 8, 9}, 2403 }, 2404 } 2405 nrBytes, err := p.Bytes() 2406 require.NoError(t, err) 2407 str := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(nrBytes)) 2408 submitNotaryRequest(t, true, neorpc.ErrVerificationFailedCode, str)(t) 2409 }) 2410 t.Run("valid request", func(t *testing.T) { 2411 sender := testchain.PrivateKeyByID(0) // owner of the deposit in testchain 2412 notaryRequest1 = createValidNotaryRequest(chain, sender, 3, 2_0000_0000, nil) 2413 nrBytes, err := notaryRequest1.Bytes() 2414 require.NoError(t, err) 2415 str := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(nrBytes)) 2416 submitNotaryRequest(t, false, 0, str)(t) 2417 }) 2418 }) 2419 2420 t.Run("getrawnotarytransaction", func(t *testing.T) { 2421 t.Run("invalid param", func(t *testing.T) { 2422 req := fmt.Sprintf(rpcTx, "invalid", 1) 2423 body := doRPCCallOverHTTP(req, httpSrv.URL, t) 2424 checkErrGetResult(t, body, true, neorpc.InvalidParamsCode) 2425 }) 2426 t.Run("unknown transaction", func(t *testing.T) { 2427 req := fmt.Sprintf(rpcTx, (util.Uint256{0, 0, 0}).StringLE(), 1) 2428 body := doRPCCallOverHTTP(req, httpSrv.URL, t) 2429 checkErrGetResult(t, body, true, neorpc.ErrUnknownTransactionCode) 2430 }) 2431 2432 checkGetTxVerbose := func(t *testing.T, tx *transaction.Transaction) { 2433 req := fmt.Sprintf(rpcTx, tx.Hash().StringLE(), 1) 2434 body := doRPCCallOverHTTP(req, httpSrv.URL, t) 2435 res := checkErrGetResult(t, body, false, 0) 2436 actual := new(transaction.Transaction) 2437 require.NoError(t, json.Unmarshal(res, actual)) 2438 _ = tx.Size() 2439 require.Equal(t, tx, actual) 2440 } 2441 t.Run("mainTx verbose", func(t *testing.T) { 2442 checkGetTxVerbose(t, notaryRequest1.MainTransaction) 2443 }) 2444 t.Run("fallbackTx verbose", func(t *testing.T) { 2445 checkGetTxVerbose(t, notaryRequest1.FallbackTransaction) 2446 checkGetTxVerbose(t, notaryRequest2.FallbackTransaction) 2447 }) 2448 2449 checkGetTxBytes := func(t *testing.T, tx *transaction.Transaction) { 2450 req := fmt.Sprintf(rpcTx, tx.Hash().StringLE(), 0) 2451 body := doRPCCallOverHTTP(req, httpSrv.URL, t) 2452 res := checkErrGetResult(t, body, false, 0) 2453 2454 var s string 2455 err := json.Unmarshal(res, &s) 2456 require.NoErrorf(t, err, "could not parse response: %s", res) 2457 txBin, err := testserdes.EncodeBinary(tx) 2458 require.NoError(t, err) 2459 expected := base64.StdEncoding.EncodeToString(txBin) 2460 assert.Equal(t, expected, s) 2461 } 2462 t.Run("mainTx bytes", func(t *testing.T) { 2463 checkGetTxBytes(t, notaryRequest1.MainTransaction) 2464 }) 2465 t.Run("fallbackTx bytes", func(t *testing.T) { 2466 checkGetTxBytes(t, notaryRequest1.FallbackTransaction) 2467 checkGetTxBytes(t, notaryRequest2.FallbackTransaction) 2468 }) 2469 }) 2470 } 2471 2472 // createValidNotaryRequest creates and signs P2PNotaryRequest payload which can 2473 // pass verification. It uses the provided mainTx if it's a nonempty structure. 2474 func createValidNotaryRequest(chain *core.Blockchain, sender *keys.PrivateKey, nonce uint32, networkFee int64, mainTx *transaction.Transaction) *payload.P2PNotaryRequest { 2475 h := chain.BlockHeight() 2476 // If mainTx is nil, then generate it. 2477 if mainTx == nil { 2478 mainTx = &transaction.Transaction{ 2479 Nonce: nonce, 2480 Attributes: []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}}, 2481 Script: []byte{byte(opcode.RET)}, 2482 ValidUntilBlock: h + 100, 2483 Signers: []transaction.Signer{{Account: sender.GetScriptHash()}}, 2484 Scripts: []transaction.Witness{{ 2485 InvocationScript: []byte{1, 4, 7}, 2486 VerificationScript: []byte{3, 6, 9}, 2487 }}, 2488 } 2489 } 2490 fallbackTx := &transaction.Transaction{ 2491 Script: []byte{byte(opcode.RET)}, 2492 ValidUntilBlock: h + 100, 2493 Attributes: []transaction.Attribute{ 2494 {Type: transaction.NotValidBeforeT, Value: &transaction.NotValidBefore{Height: h + 50}}, 2495 {Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: mainTx.Hash()}}, 2496 {Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}}, 2497 }, 2498 Signers: []transaction.Signer{{Account: chain.GetNotaryContractScriptHash()}, {Account: sender.GetScriptHash()}}, 2499 Scripts: []transaction.Witness{ 2500 {InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: []byte{}}, 2501 }, 2502 NetworkFee: networkFee, 2503 } 2504 fallbackTx.Scripts = append(fallbackTx.Scripts, transaction.Witness{ 2505 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, sender.SignHashable(uint32(testchain.Network()), fallbackTx)...), 2506 VerificationScript: sender.PublicKey().GetVerificationScript(), 2507 }) 2508 p := &payload.P2PNotaryRequest{ 2509 MainTransaction: mainTx, 2510 FallbackTransaction: fallbackTx, 2511 } 2512 p.Witness = transaction.Witness{ 2513 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, sender.SignHashable(uint32(testchain.Network()), p)...), 2514 VerificationScript: sender.PublicKey().GetVerificationScript(), 2515 } 2516 return p 2517 } 2518 2519 func runTestCasesWithExecutor(t *testing.T, e *executor, rpcCall string, method string, testCases []rpcTestCase, doRPCCall func(string, string, *testing.T) []byte, checkErrResult func(t *testing.T, body []byte, expectingFail bool, expectedErrCode int64, expectedErr ...string) json.RawMessage) { 2520 t.Run(method, func(t *testing.T) { 2521 for _, tc := range testCases { 2522 t.Run(tc.name, func(t *testing.T) { 2523 body := doRPCCall(fmt.Sprintf(rpcCall, method, tc.params), e.httpSrv.URL, t) 2524 result := checkErrResult(t, body, tc.fail, tc.errCode) 2525 if tc.fail { 2526 return 2527 } 2528 2529 expected, res := tc.getResultPair(e) 2530 err := json.Unmarshal(result, res) 2531 require.NoErrorf(t, err, "could not parse response: %s", result) 2532 2533 if tc.check == nil { 2534 assert.Equal(t, expected, res) 2535 } else { 2536 tc.check(t, e, res) 2537 } 2538 }) 2539 } 2540 }) 2541 } 2542 2543 // testRPCProtocol runs a full set of tests using given callback to make actual 2544 // calls. Some tests change the chain state, thus we reinitialize the chain from 2545 // scratch here. 2546 func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []byte) { 2547 chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) 2548 2549 e := &executor{chain: chain, httpSrv: httpSrv} 2550 t.Run("single request", func(t *testing.T) { 2551 rpc := `{"jsonrpc": "2.0", "id": 1, "method": "%s", "params": %s}` 2552 for method, cases := range rpcTestCases { 2553 runTestCasesWithExecutor(t, e, rpc, method, cases, doRPCCall, checkErrGetResult) 2554 } 2555 }) 2556 t.Run("batch with single request", func(t *testing.T) { 2557 for method, cases := range rpcTestCases { 2558 if method == "sendrawtransaction" { 2559 continue // cannot send the same transaction twice 2560 } 2561 rpc := `[{"jsonrpc": "2.0", "id": 1, "method": "%s", "params": %s}]` 2562 runTestCasesWithExecutor(t, e, rpc, method, cases, doRPCCall, checkErrGetBatchResult) 2563 } 2564 }) 2565 2566 t.Run("batch with multiple requests", func(t *testing.T) { 2567 for method, cases := range rpcTestCases { 2568 if method == "sendrawtransaction" { 2569 continue // cannot send the same transaction twice 2570 } 2571 t.Run(method, func(t *testing.T) { 2572 rpc := `{"jsonrpc": "2.0", "id": %d, "method": "%s", "params": %s},` 2573 var resultRPC string 2574 for i, tc := range cases { 2575 resultRPC += fmt.Sprintf(rpc, i, method, tc.params) 2576 } 2577 resultRPC = `[` + resultRPC[:len(resultRPC)-1] + `]` 2578 body := doRPCCall(resultRPC, httpSrv.URL, t) 2579 var responses []neorpc.Response 2580 err := json.Unmarshal(body, &responses) 2581 require.Nil(t, err) 2582 for i, tc := range cases { 2583 var resp neorpc.Response 2584 for _, r := range responses { 2585 if bytes.Equal(r.ID, []byte(strconv.Itoa(i))) { 2586 resp = r 2587 break 2588 } 2589 } 2590 if tc.fail { 2591 require.NotNil(t, resp.Error) 2592 assert.NotEqual(t, 0, resp.Error.Code) 2593 assert.NotEqual(t, "", resp.Error.Message) 2594 } else { 2595 assert.Nil(t, resp.Error) 2596 } 2597 if tc.fail { 2598 return 2599 } 2600 expected, res := tc.getResultPair(e) 2601 err := json.Unmarshal(resp.Result, res) 2602 require.NoErrorf(t, err, "could not parse response: %s", resp.Result) 2603 2604 if tc.check == nil { 2605 assert.Equal(t, expected, res) 2606 } else { 2607 tc.check(t, e, res) 2608 } 2609 } 2610 }) 2611 } 2612 }) 2613 2614 t.Run("getapplicationlog for block", func(t *testing.T) { 2615 rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getapplicationlog", "params": ["%s"]}` 2616 body := doRPCCall(fmt.Sprintf(rpc, e.chain.GetHeaderHash(1).StringLE()), httpSrv.URL, t) 2617 data := checkErrGetResult(t, body, false, 0) 2618 var res result.ApplicationLog 2619 require.NoError(t, json.Unmarshal(data, &res)) 2620 require.Equal(t, 2, len(res.Executions)) 2621 require.Equal(t, trigger.OnPersist, res.Executions[0].Trigger) 2622 require.Equal(t, vmstate.Halt, res.Executions[0].VMState) 2623 require.Equal(t, trigger.PostPersist, res.Executions[1].Trigger) 2624 require.Equal(t, vmstate.Halt, res.Executions[1].VMState) 2625 }) 2626 t.Run("submitblock", func(t *testing.T) { 2627 rpc := `{"jsonrpc": "2.0", "id": 1, "method": "submitblock", "params": ["%s"]}` 2628 t.Run("invalid signature", func(t *testing.T) { 2629 s := testchain.NewBlock(t, chain, 1, 0) 2630 s.Script.VerificationScript[8] ^= 0xff 2631 body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, s)), httpSrv.URL, t) 2632 checkErrGetResult(t, body, true, neorpc.ErrVerificationFailedCode) 2633 }) 2634 2635 t.Run("invalid height", func(t *testing.T) { 2636 b := testchain.NewBlock(t, chain, 2, 0, newTxWithParams(t, chain, opcode.PUSH1, 10, 0, 1, false)) 2637 body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t) 2638 checkErrGetResult(t, body, true, neorpc.ErrAlreadyExistsCode) 2639 }) 2640 t.Run("invalid script", func(t *testing.T) { 2641 b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, 0xDD, 10, 0, 1, false)) 2642 body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t) 2643 checkErrGetResult(t, body, true, neorpc.ErrInvalidScriptCode) 2644 }) 2645 t.Run("invalid ValidUntilBlock", func(t *testing.T) { 2646 b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, opcode.PUSH1, 0, 0, 1, false)) 2647 body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t) 2648 checkErrGetResult(t, body, true, neorpc.ErrExpiredTransactionCode) 2649 }) 2650 t.Run("invalid SystemFee", func(t *testing.T) { 2651 b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, opcode.PUSH1, 10, 999999999999, 1, false)) 2652 body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t) 2653 checkErrGetResult(t, body, true, neorpc.ErrPolicyFailedCode) 2654 }) 2655 t.Run("invalid NetworkFee", func(t *testing.T) { 2656 b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, opcode.PUSH1, 10, 0, 0, false)) 2657 body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t) 2658 checkErrGetResult(t, body, true, neorpc.ErrInsufficientNetworkFeeCode) 2659 }) 2660 t.Run("invalid attribute", func(t *testing.T) { 2661 b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, opcode.PUSH1, 10, 0, 2, true)) 2662 body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t) 2663 checkErrGetResult(t, body, true, neorpc.ErrInvalidAttributeCode) 2664 }) 2665 t.Run("insufficient funds", func(t *testing.T) { 2666 b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, opcode.PUSH1, 10, 899999999999, 1, false)) 2667 body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t) 2668 checkErrGetResult(t, body, true, neorpc.ErrInsufficientFundsCode) 2669 }) 2670 t.Run("positive", func(t *testing.T) { 2671 b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, opcode.PUSH1, 10, 0, 1, false)) 2672 body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t) 2673 data := checkErrGetResult(t, body, false, 0) 2674 var res = new(result.RelayResult) 2675 require.NoError(t, json.Unmarshal(data, res)) 2676 require.Equal(t, b.Hash(), res.Hash) 2677 }) 2678 }) 2679 t.Run("getproof", func(t *testing.T) { 2680 r, err := chain.GetStateModule().GetStateRoot(3) 2681 require.NoError(t, err) 2682 2683 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getproof", "params": ["%s", "%s", "%s"]}`, 2684 r.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("testkey"))) 2685 body := doRPCCall(rpc, httpSrv.URL, t) 2686 rawRes := checkErrGetResult(t, body, false, 0) 2687 res := new(result.ProofWithKey) 2688 require.NoError(t, json.Unmarshal(rawRes, res)) 2689 h, _ := util.Uint160DecodeStringLE(testContractHash) 2690 skey := makeStorageKey(chain.GetContractState(h).ID, []byte("testkey")) 2691 require.Equal(t, skey, res.Key) 2692 require.True(t, len(res.Proof) > 0) 2693 2694 rpc = fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "verifyproof", "params": ["%s", "%s"]}`, 2695 r.Root.StringLE(), res.String()) 2696 body = doRPCCall(rpc, httpSrv.URL, t) 2697 rawRes = checkErrGetResult(t, body, false, 0) 2698 vp := new(result.VerifyProof) 2699 require.NoError(t, json.Unmarshal(rawRes, vp)) 2700 require.Equal(t, []byte("testvalue"), vp.Value) 2701 }) 2702 t.Run("getstateroot", func(t *testing.T) { 2703 testRoot := func(t *testing.T, p string) { 2704 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getstateroot", "params": [%s]}`, p) 2705 body := doRPCCall(rpc, httpSrv.URL, t) 2706 rawRes := checkErrGetResult(t, body, false, 0) 2707 2708 res := &state.MPTRoot{} 2709 require.NoError(t, json.Unmarshal(rawRes, res)) 2710 require.NotEqual(t, util.Uint256{}, res.Root) // be sure this test uses valid height 2711 2712 expected, err := e.chain.GetStateModule().GetStateRoot(5) 2713 require.NoError(t, err) 2714 require.Equal(t, expected, res) 2715 } 2716 t.Run("ByHeight", func(t *testing.T) { testRoot(t, strconv.FormatInt(5, 10)) }) 2717 t.Run("ByHash", func(t *testing.T) { testRoot(t, `"`+chain.GetHeaderHash(5).StringLE()+`"`) }) 2718 t.Run("20", func(t *testing.T) { 2719 rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getstateroot", "params": [20]}` 2720 body := doRPCCall(rpc, httpSrv.URL, t) 2721 rawRes := checkErrGetResult(t, body, false, 0) 2722 2723 res := &state.MPTRoot{} 2724 require.NoError(t, json.Unmarshal(rawRes, res)) 2725 require.Equal(t, block20StateRootLE, res.Root.StringLE()) 2726 }) 2727 }) 2728 t.Run("getstate", func(t *testing.T) { 2729 rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getstate", "params": [%s]}` 2730 testGetState := func(t *testing.T, p string, expected string) { 2731 body := doRPCCall(fmt.Sprintf(rpc, p), httpSrv.URL, t) 2732 rawRes := checkErrGetResult(t, body, false, 0) 2733 2734 var actual string 2735 require.NoError(t, json.Unmarshal(rawRes, &actual)) 2736 require.Equal(t, expected, actual) 2737 } 2738 t.Run("good: historical state", func(t *testing.T) { 2739 root, err := e.chain.GetStateModule().GetStateRoot(4) 2740 require.NoError(t, err) 2741 // `testkey`-`testvalue` pair was put to the contract storage at block #3 2742 params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("testkey"))) 2743 testGetState(t, params, base64.StdEncoding.EncodeToString([]byte("testvalue"))) 2744 }) 2745 t.Run("negative: invalid key", func(t *testing.T) { 2746 root, err := e.chain.GetStateModule().GetStateRoot(4) 2747 require.NoError(t, err) 2748 // `testkey`-`testvalue` pair was put to the contract storage at block #3 2749 params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("invalidkey"))) 2750 body := doRPCCall(fmt.Sprintf(rpc, params), httpSrv.URL, t) 2751 checkErrGetResult(t, body, true, neorpc.InvalidParamsCode) 2752 }) 2753 t.Run("good: fresh state", func(t *testing.T) { 2754 root, err := e.chain.GetStateModule().GetStateRoot(16) 2755 require.NoError(t, err) 2756 // `testkey`-`newtestvalue` pair was put to the contract storage at block #16 2757 params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("testkey"))) 2758 testGetState(t, params, base64.StdEncoding.EncodeToString([]byte("newtestvalue"))) 2759 }) 2760 }) 2761 t.Run("findstates", func(t *testing.T) { 2762 testFindStates := func(t *testing.T, p string, root util.Uint256, expected result.FindStates) { 2763 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "findstates", "params": [%s]}`, p) 2764 body := doRPCCall(rpc, httpSrv.URL, t) 2765 rawRes := checkErrGetResult(t, body, false, 0) 2766 2767 var actual result.FindStates 2768 require.NoError(t, json.Unmarshal(rawRes, &actual)) 2769 require.Equal(t, expected.Results, actual.Results) 2770 2771 checkProof := func(t *testing.T, proof *result.ProofWithKey, value []byte) { 2772 rpc = fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "verifyproof", "params": ["%s", "%s"]}`, 2773 root.StringLE(), proof.String()) 2774 body = doRPCCall(rpc, httpSrv.URL, t) 2775 rawRes = checkErrGetResult(t, body, false, 0) 2776 vp := new(result.VerifyProof) 2777 require.NoError(t, json.Unmarshal(rawRes, vp)) 2778 require.Equal(t, value, vp.Value) 2779 } 2780 if len(actual.Results) > 0 { 2781 checkProof(t, actual.FirstProof, actual.Results[0].Value) 2782 } 2783 if len(actual.Results) > 1 { 2784 checkProof(t, actual.LastProof, actual.Results[len(actual.Results)-1].Value) 2785 } 2786 require.Equal(t, expected.Truncated, actual.Truncated) 2787 } 2788 t.Run("good: no prefix, no limit", func(t *testing.T) { 2789 // pairs for this test where put to the contract storage at block #16 2790 root, err := e.chain.GetStateModule().GetStateRoot(16) 2791 require.NoError(t, err) 2792 params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))) 2793 testFindStates(t, params, root.Root, result.FindStates{ 2794 Results: []result.KeyValue{ 2795 {Key: []byte("aa10"), Value: []byte("v2")}, 2796 {Key: []byte("aa50"), Value: []byte("v3")}, 2797 {Key: []byte("aa"), Value: []byte("v1")}, 2798 }, 2799 Truncated: false, 2800 }) 2801 }) 2802 t.Run("good: empty prefix, no limit", func(t *testing.T) { 2803 // empty prefix should be considered as no prefix specified. 2804 root, err := e.chain.GetStateModule().GetStateRoot(16) 2805 require.NoError(t, err) 2806 params := fmt.Sprintf(`"%s", "%s", "%s", ""`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))) 2807 testFindStates(t, params, root.Root, result.FindStates{ 2808 Results: []result.KeyValue{ 2809 {Key: []byte("aa10"), Value: []byte("v2")}, 2810 {Key: []byte("aa50"), Value: []byte("v3")}, 2811 {Key: []byte("aa"), Value: []byte("v1")}, 2812 }, 2813 Truncated: false, 2814 }) 2815 }) 2816 t.Run("good: empty prefix, no limit, no data", func(t *testing.T) { 2817 // empty prefix should be considered as no prefix specified. 2818 root, err := e.chain.GetStateModule().GetStateRoot(20) 2819 require.NoError(t, err) 2820 stdHash, _ := e.chain.GetNativeContractScriptHash(nativenames.StdLib) // It has no data. 2821 params := fmt.Sprintf(`"%s", "%s", ""`, root.Root.StringLE(), stdHash.StringLE()) 2822 testFindStates(t, params, root.Root, result.FindStates{ 2823 Results: []result.KeyValue{}, 2824 Truncated: false, 2825 }) 2826 }) 2827 t.Run("good: with prefix, no limit", func(t *testing.T) { 2828 // pairs for this test where put to the contract storage at block #16 2829 root, err := e.chain.GetStateModule().GetStateRoot(16) 2830 require.NoError(t, err) 2831 params := fmt.Sprintf(`"%s", "%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa")), base64.StdEncoding.EncodeToString([]byte("aa10"))) 2832 testFindStates(t, params, root.Root, result.FindStates{ 2833 Results: []result.KeyValue{ 2834 {Key: []byte("aa50"), Value: []byte("v3")}, 2835 }, 2836 Truncated: false, 2837 }) 2838 }) 2839 t.Run("good: empty prefix, with limit", func(t *testing.T) { 2840 for limit := 2; limit < 5; limit++ { 2841 // pairs for this test where put to the contract storage at block #16 2842 root, err := e.chain.GetStateModule().GetStateRoot(16) 2843 require.NoError(t, err) 2844 params := fmt.Sprintf(`"%s", "%s", "%s", "", %d`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa")), limit) 2845 expected := result.FindStates{ 2846 Results: []result.KeyValue{ 2847 {Key: []byte("aa10"), Value: []byte("v2")}, 2848 {Key: []byte("aa50"), Value: []byte("v3")}, 2849 }, 2850 Truncated: limit == 2, 2851 } 2852 if limit != 2 { 2853 expected.Results = append(expected.Results, result.KeyValue{Key: []byte("aa"), Value: []byte("v1")}) 2854 } 2855 testFindStates(t, params, root.Root, expected) 2856 } 2857 }) 2858 t.Run("good: with prefix, with limit", func(t *testing.T) { 2859 // pairs for this test where put to the contract storage at block #16 2860 root, err := e.chain.GetStateModule().GetStateRoot(16) 2861 require.NoError(t, err) 2862 params := fmt.Sprintf(`"%s", "%s", "%s", "%s", %d`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa")), base64.StdEncoding.EncodeToString([]byte("aa00")), 1) 2863 testFindStates(t, params, root.Root, result.FindStates{ 2864 Results: []result.KeyValue{ 2865 {Key: []byte("aa10"), Value: []byte("v2")}, 2866 }, 2867 Truncated: true, 2868 }) 2869 }) 2870 }) 2871 2872 t.Run("getrawtransaction", func(t *testing.T) { 2873 block, _ := chain.GetBlock(chain.GetHeaderHash(1)) 2874 tx := block.Transactions[0] 2875 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s"]}"`, tx.Hash().StringLE()) 2876 body := doRPCCall(rpc, httpSrv.URL, t) 2877 result := checkErrGetResult(t, body, false, 0) 2878 var res string 2879 err := json.Unmarshal(result, &res) 2880 require.NoErrorf(t, err, "could not parse response: %s", result) 2881 txBin, err := testserdes.EncodeBinary(tx) 2882 require.NoError(t, err) 2883 expected := base64.StdEncoding.EncodeToString(txBin) 2884 assert.Equal(t, expected, res) 2885 }) 2886 2887 t.Run("getrawtransaction 2 arguments", func(t *testing.T) { 2888 block, _ := chain.GetBlock(chain.GetHeaderHash(1)) 2889 tx := block.Transactions[0] 2890 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 0]}"`, tx.Hash().StringLE()) 2891 body := doRPCCall(rpc, httpSrv.URL, t) 2892 result := checkErrGetResult(t, body, false, 0) 2893 var res string 2894 err := json.Unmarshal(result, &res) 2895 require.NoErrorf(t, err, "could not parse response: %s", result) 2896 txBin, err := testserdes.EncodeBinary(tx) 2897 require.NoError(t, err) 2898 expected := base64.StdEncoding.EncodeToString(txBin) 2899 assert.Equal(t, expected, res) 2900 }) 2901 2902 t.Run("getrawtransaction 2 arguments, verbose", func(t *testing.T) { 2903 block, _ := chain.GetBlock(chain.GetHeaderHash(1)) 2904 TXHash := block.Transactions[0].Hash() 2905 _ = block.Transactions[0].Size() 2906 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 1]}"`, TXHash.StringLE()) 2907 body := doRPCCall(rpc, httpSrv.URL, t) 2908 txOut := checkErrGetResult(t, body, false, 0) 2909 actual := result.TransactionOutputRaw{Transaction: transaction.Transaction{}} 2910 err := json.Unmarshal(txOut, &actual) 2911 require.NoErrorf(t, err, "could not parse response: %s", txOut) 2912 2913 assert.Equal(t, *block.Transactions[0], actual.Transaction) 2914 assert.Equal(t, 24, actual.Confirmations) 2915 assert.Equal(t, TXHash, actual.Transaction.Hash()) 2916 }) 2917 2918 t.Run("getblockheader_positive", func(t *testing.T) { 2919 rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getblockheader", "params": %s}` 2920 testHeaderHash := chain.GetHeaderHash(1).StringLE() 2921 hdr := e.getHeader(testHeaderHash) 2922 2923 runCase := func(t *testing.T, rpc string, expected, actual any) { 2924 body := doRPCCall(rpc, httpSrv.URL, t) 2925 data := checkErrGetResult(t, body, false, 0) 2926 require.NoError(t, json.Unmarshal(data, actual)) 2927 require.Equal(t, expected, actual) 2928 } 2929 2930 t.Run("no verbose", func(t *testing.T) { 2931 w := io.NewBufBinWriter() 2932 hdr.EncodeBinary(w.BinWriter) 2933 require.NoError(t, w.Err) 2934 encoded := base64.StdEncoding.EncodeToString(w.Bytes()) 2935 2936 t.Run("missing", func(t *testing.T) { 2937 runCase(t, fmt.Sprintf(rpc, `["`+testHeaderHash+`"]`), &encoded, new(string)) 2938 }) 2939 2940 t.Run("verbose=0", func(t *testing.T) { 2941 runCase(t, fmt.Sprintf(rpc, `["`+testHeaderHash+`", 0]`), &encoded, new(string)) 2942 }) 2943 2944 t.Run("by number", func(t *testing.T) { 2945 runCase(t, fmt.Sprintf(rpc, `[1]`), &encoded, new(string)) 2946 }) 2947 }) 2948 2949 t.Run("verbose != 0", func(t *testing.T) { 2950 nextHash := chain.GetHeaderHash(hdr.Index + 1) 2951 expected := &result.Header{ 2952 Header: *hdr, 2953 BlockMetadata: result.BlockMetadata{ 2954 Size: io.GetVarSize(hdr), 2955 NextBlockHash: &nextHash, 2956 Confirmations: e.chain.BlockHeight() - hdr.Index + 1, 2957 }, 2958 } 2959 2960 rpc := fmt.Sprintf(rpc, `["`+testHeaderHash+`", 2]`) 2961 runCase(t, rpc, expected, new(result.Header)) 2962 }) 2963 }) 2964 2965 t.Run("getrawmempool", func(t *testing.T) { 2966 mp := chain.GetMemPool() 2967 // `expected` stores hashes of previously added txs 2968 expected := make([]util.Uint256, 0) 2969 for _, tx := range mp.GetVerifiedTransactions() { 2970 expected = append(expected, tx.Hash()) 2971 } 2972 for i := 0; i < 5; i++ { 2973 tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) 2974 tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} 2975 assert.NoError(t, mp.Add(tx, &FeerStub{})) 2976 expected = append(expected, tx.Hash()) 2977 } 2978 2979 rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getrawmempool", "params": []}` 2980 body := doRPCCall(rpc, httpSrv.URL, t) 2981 res := checkErrGetResult(t, body, false, 0) 2982 2983 var actual []util.Uint256 2984 err := json.Unmarshal(res, &actual) 2985 require.NoErrorf(t, err, "could not parse response: %s", res) 2986 2987 assert.ElementsMatch(t, expected, actual) 2988 }) 2989 2990 t.Run("getnep17transfers", func(t *testing.T) { 2991 testNEP17T := func(t *testing.T, start, stop, limit, page int, sent, rcvd []int) { 2992 ps := []string{`"` + testchain.PrivateKeyByID(0).Address() + `"`} 2993 if start != 0 { 2994 h, err := e.chain.GetHeader(e.chain.GetHeaderHash(uint32(start))) 2995 var ts uint64 2996 if err == nil { 2997 ts = h.Timestamp 2998 } else { 2999 ts = uint64(time.Now().UnixNano() / 1_000_000) 3000 } 3001 ps = append(ps, strconv.FormatUint(ts, 10)) 3002 } 3003 if stop != 0 { 3004 h, err := e.chain.GetHeader(e.chain.GetHeaderHash(uint32(stop))) 3005 var ts uint64 3006 if err == nil { 3007 ts = h.Timestamp 3008 } else { 3009 ts = uint64(time.Now().UnixNano() / 1_000_000) 3010 } 3011 ps = append(ps, strconv.FormatUint(ts, 10)) 3012 } 3013 if limit != 0 { 3014 ps = append(ps, strconv.FormatInt(int64(limit), 10)) 3015 } 3016 if page != 0 { 3017 ps = append(ps, strconv.FormatInt(int64(page), 10)) 3018 } 3019 p := strings.Join(ps, ", ") 3020 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getnep17transfers", "params": [%s]}`, p) 3021 body := doRPCCall(rpc, httpSrv.URL, t) 3022 res := checkErrGetResult(t, body, false, 0) 3023 actual := new(result.NEP17Transfers) 3024 require.NoError(t, json.Unmarshal(res, actual)) 3025 checkNep17TransfersAux(t, e, actual, sent, rcvd) 3026 } 3027 t.Run("time frame only", func(t *testing.T) { testNEP17T(t, 4, 5, 0, 0, []int{19, 20, 21, 22}, []int{3, 4}) }) 3028 t.Run("no res", func(t *testing.T) { testNEP17T(t, 100, 100, 0, 0, []int{}, []int{}) }) 3029 t.Run("limit", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 0, []int{16, 17}, []int{2}) }) 3030 t.Run("limit 2", func(t *testing.T) { testNEP17T(t, 4, 5, 2, 0, []int{19}, []int{3}) }) 3031 t.Run("limit with page", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 1, []int{18, 19}, []int{3}) }) 3032 t.Run("limit with page 2", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 2, []int{20, 21}, []int{4}) }) 3033 }) 3034 3035 prepareIteratorSession := func(t *testing.T) (uuid.UUID, uuid.UUID) { 3036 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "invokefunction", "params": ["%s", "iterateOverValues"]}"`, storageContractHash) 3037 body := doRPCCall(rpc, httpSrv.URL, t) 3038 resp := checkErrGetResult(t, body, false, 0) 3039 res := new(result.Invoke) 3040 err := json.Unmarshal(resp, &res) 3041 require.NoErrorf(t, err, "could not parse response: %s", resp) 3042 require.NotEmpty(t, res.Session) 3043 require.Equal(t, 1, len(res.Stack)) 3044 require.Equal(t, stackitem.InteropT, res.Stack[0].Type()) 3045 iterator, ok := res.Stack[0].Value().(result.Iterator) 3046 require.True(t, ok) 3047 require.NotEmpty(t, iterator.ID) 3048 return res.Session, *iterator.ID 3049 } 3050 t.Run("traverseiterator", func(t *testing.T) { 3051 t.Run("sessions disabled", func(t *testing.T) { 3052 _, _, httpSrv2 := initClearServerWithCustomConfig(t, func(c *config.Config) { 3053 c.ApplicationConfiguration.RPC.SessionEnabled = false 3054 }) 3055 body := doRPCCall(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": []}"`, httpSrv2.URL, t) 3056 checkErrGetResult(t, body, true, neorpc.ErrSessionsDisabledCode) 3057 }) 3058 t.Run("good", func(t *testing.T) { 3059 sID, iID := prepareIteratorSession(t) 3060 expectedCount := 99 3061 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "%s", %d]}"`, sID.String(), iID.String(), expectedCount) 3062 body := doRPCCall(rpc, httpSrv.URL, t) 3063 resp := checkErrGetResult(t, body, false, 0) 3064 res := new([]json.RawMessage) 3065 require.NoError(t, json.Unmarshal(resp, res)) 3066 require.Equal(t, expectedCount, len(*res)) 3067 }) 3068 t.Run("invalid session id", func(t *testing.T) { 3069 _, iID := prepareIteratorSession(t) 3070 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["not-a-uuid", "%s", %d]}"`, iID.String(), 1) 3071 body := doRPCCall(rpc, httpSrv.URL, t) 3072 checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "invalid session ID: not a valid UUID") 3073 }) 3074 t.Run("invalid iterator id", func(t *testing.T) { 3075 sID, _ := prepareIteratorSession(t) 3076 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "not-a-uuid", %d]}"`, sID.String(), 1) 3077 body := doRPCCall(rpc, httpSrv.URL, t) 3078 checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "invalid iterator ID: not a valid UUID") 3079 }) 3080 t.Run("invalid items count", func(t *testing.T) { 3081 sID, iID := prepareIteratorSession(t) 3082 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "%s"]}"`, sID.String(), iID.String()) 3083 body := doRPCCall(rpc, httpSrv.URL, t) 3084 checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "invalid iterator items count") 3085 }) 3086 t.Run("items count is not an int32", func(t *testing.T) { 3087 sID, iID := prepareIteratorSession(t) 3088 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "%s", %d]}"`, sID.String(), iID.String(), math.MaxInt32+1) 3089 body := doRPCCall(rpc, httpSrv.URL, t) 3090 checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "invalid iterator items count: not an int32") 3091 }) 3092 t.Run("count is out of range", func(t *testing.T) { 3093 sID, iID := prepareIteratorSession(t) 3094 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "%s", %d]}"`, sID.String(), iID.String(), config.DefaultMaxIteratorResultItems+1) 3095 body := doRPCCall(rpc, httpSrv.URL, t) 3096 checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, fmt.Sprintf("iterator items count (%d) is out of range (%d at max)", config.DefaultMaxIteratorResultItems+1, config.DefaultMaxIteratorResultItems)) 3097 }) 3098 t.Run("unknown session", func(t *testing.T) { 3099 _, iID := prepareIteratorSession(t) 3100 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "%s", %d]}"`, uuid.NewString(), iID.String(), 1) 3101 body := doRPCCall(rpc, httpSrv.URL, t) 3102 checkErrGetResult(t, body, true, neorpc.ErrUnknownSessionCode) 3103 }) 3104 t.Run("unknown iterator", func(t *testing.T) { 3105 sID, _ := prepareIteratorSession(t) 3106 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "%s", %d]}"`, sID.String(), uuid.NewString(), 1) 3107 body := doRPCCall(rpc, httpSrv.URL, t) 3108 checkErrGetResult(t, body, true, neorpc.ErrUnknownIteratorCode) 3109 }) 3110 }) 3111 t.Run("terminatesession", func(t *testing.T) { 3112 rpc := `{"jsonrpc": "2.0", "id": 1, "method": "terminatesession", "params": ["%s"]}"` 3113 t.Run("sessions disabled", func(t *testing.T) { 3114 _, _, httpSrv2 := initClearServerWithCustomConfig(t, func(c *config.Config) { 3115 c.ApplicationConfiguration.RPC.SessionEnabled = false 3116 }) 3117 body := doRPCCall(fmt.Sprintf(rpc, uuid.NewString()), httpSrv2.URL, t) 3118 checkErrGetResult(t, body, true, neorpc.ErrSessionsDisabledCode) 3119 }) 3120 t.Run("true", func(t *testing.T) { 3121 sID, _ := prepareIteratorSession(t) 3122 body := doRPCCall(fmt.Sprintf(rpc, sID.String()), httpSrv.URL, t) 3123 resp := checkErrGetResult(t, body, false, 0) 3124 res := new(bool) 3125 require.NoError(t, json.Unmarshal(resp, res)) 3126 require.Equal(t, true, *res) 3127 }) 3128 t.Run("false", func(t *testing.T) { 3129 body := doRPCCall(fmt.Sprintf(rpc, uuid.NewString()), httpSrv.URL, t) 3130 checkErrGetResult(t, body, true, neorpc.ErrUnknownSessionCode) 3131 }) 3132 t.Run("expired", func(t *testing.T) { 3133 _, _ = prepareIteratorSession(t) 3134 // Wait until session is terminated by timer. 3135 require.Eventually(t, func() bool { 3136 rpcSrv.sessionsLock.Lock() 3137 defer rpcSrv.sessionsLock.Unlock() 3138 return len(rpcSrv.sessions) == 0 3139 }, 2*time.Duration(rpcSrv.config.SessionExpirationTime)*time.Second, 10*time.Millisecond) 3140 }) 3141 }) 3142 t.Run("calculatenetworkfee", func(t *testing.T) { 3143 t.Run("no parameters", func(t *testing.T) { 3144 body := doRPCCall(`{"jsonrpc": "2.0", "id": 1, "method": "calculatenetworkfee", "params": []}"`, httpSrv.URL, t) 3145 _ = checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "Invalid params") 3146 }) 3147 t.Run("non-base64 parameter", func(t *testing.T) { 3148 body := doRPCCall(`{"jsonrpc": "2.0", "id": 1, "method": "calculatenetworkfee", "params": ["noatbase64"]}"`, httpSrv.URL, t) 3149 _ = checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "Invalid params") 3150 }) 3151 t.Run("non-transaction parameter", func(t *testing.T) { 3152 body := doRPCCall(`{"jsonrpc": "2.0", "id": 1, "method": "calculatenetworkfee", "params": ["bm90IGEgdHJhbnNhY3Rpb24K"]}"`, httpSrv.URL, t) 3153 _ = checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "Invalid params") 3154 }) 3155 calcReqExactly := func(t *testing.T, tx string) []byte { 3156 rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "calculatenetworkfee", "params": ["%s"]}"`, tx) 3157 return doRPCCall(rpc, httpSrv.URL, t) 3158 } 3159 calcReq := func(t *testing.T, tx *transaction.Transaction) []byte { 3160 return calcReqExactly(t, base64.StdEncoding.EncodeToString(tx.Bytes())) 3161 } 3162 t.Run("non-contract with zero verification", func(t *testing.T) { 3163 tx := &transaction.Transaction{ 3164 Script: []byte{byte(opcode.RET)}, 3165 Signers: []transaction.Signer{{Account: util.Uint160{1, 2, 3}, Scopes: transaction.CalledByEntry}}, 3166 Scripts: []transaction.Witness{{ 3167 InvocationScript: []byte{}, 3168 VerificationScript: []byte{}, 3169 }}, 3170 } 3171 body := calcReq(t, tx) 3172 _ = checkErrGetResult(t, body, true, neorpc.ErrInvalidVerificationFunctionCode, "signer 0 has no verification script and no deployed contract") 3173 }) 3174 t.Run("contract with no verify", func(t *testing.T) { 3175 tx := &transaction.Transaction{ 3176 Script: []byte{byte(opcode.RET)}, 3177 Signers: []transaction.Signer{{Account: nnsHash, Scopes: transaction.CalledByEntry}}, 3178 Scripts: []transaction.Witness{{ 3179 InvocationScript: []byte{}, 3180 VerificationScript: []byte{}, 3181 }}, 3182 } 3183 body := calcReq(t, tx) 3184 _ = checkErrGetResult(t, body, true, neorpc.ErrInvalidVerificationFunctionCode, "signer 0 has no verify method in deployed contract") 3185 }) 3186 t.Run("execution limit, fail", func(t *testing.T) { 3187 // 1_6000_0000 GAS with the default 1.5 allowed by Policy 3188 verifScript := []byte{byte(opcode.PUSHINT32), 0x00, 0x58, 0x89, 0x09, byte(opcode.SYSCALL), 0, 0, 0, 0, byte(opcode.PUSHT)} 3189 binary.LittleEndian.PutUint32(verifScript[6:], interopnames.ToID([]byte(interopnames.SystemRuntimeBurnGas))) 3190 tx := &transaction.Transaction{ 3191 Script: []byte{byte(opcode.RET)}, 3192 Signers: []transaction.Signer{{Account: hash.Hash160(verifScript)}}, 3193 Scripts: []transaction.Witness{{ 3194 InvocationScript: []byte{byte(opcode.NOP)}, 3195 VerificationScript: verifScript, 3196 }}, 3197 } 3198 body := calcReq(t, tx) 3199 _ = checkErrGetResult(t, body, true, neorpc.ErrInvalidSignatureCode, "GAS limit exceeded") 3200 }) 3201 checkCalc := func(t *testing.T, tx *transaction.Transaction, fee int64) { 3202 resp := checkErrGetResult(t, calcReq(t, tx), false, 0) 3203 res := new(result.NetworkFee) 3204 require.NoError(t, json.Unmarshal(resp, res)) 3205 require.Equal(t, fee, res.Value) 3206 } 3207 t.Run("simple GAS transfer", func(t *testing.T) { 3208 priv0 := testchain.PrivateKeyByID(0) 3209 script, err := smartcontract.CreateCallWithAssertScript(chain.UtilityTokenHash(), "transfer", 3210 priv0.GetScriptHash(), priv0.GetScriptHash(), 1, nil) 3211 require.NoError(t, err) 3212 tx := &transaction.Transaction{ 3213 Script: script, 3214 Signers: []transaction.Signer{{Account: priv0.GetScriptHash(), Scopes: transaction.CalledByEntry}}, 3215 Scripts: []transaction.Witness{{ 3216 InvocationScript: []byte{}, 3217 VerificationScript: priv0.PublicKey().GetVerificationScript(), 3218 }}, 3219 } 3220 checkCalc(t, tx, 1228520) // Perfectly matches FeeIsSignatureContractDetailed() C# test. 3221 }) 3222 t.Run("multisignature tx", func(t *testing.T) { 3223 priv0 := testchain.PrivateKeyByID(0) 3224 priv1 := testchain.PrivateKeyByID(1) 3225 accScript, err := smartcontract.CreateDefaultMultiSigRedeemScript(keys.PublicKeys{priv0.PublicKey(), priv1.PublicKey()}) 3226 require.NoError(t, err) 3227 multiAcc := hash.Hash160(accScript) 3228 txScript, err := smartcontract.CreateCallWithAssertScript(chain.UtilityTokenHash(), "transfer", 3229 multiAcc, priv0.GetScriptHash(), 1, nil) 3230 require.NoError(t, err) 3231 tx := &transaction.Transaction{ 3232 Script: txScript, 3233 Signers: []transaction.Signer{{Account: multiAcc, Scopes: transaction.CalledByEntry}}, 3234 Scripts: []transaction.Witness{{ 3235 InvocationScript: []byte{}, 3236 VerificationScript: accScript, 3237 }}, 3238 } 3239 checkCalc(t, tx, 2315100) // Perfectly matches FeeIsMultiSigContract() C# test. 3240 }) 3241 t.Run("Koblitz custom multisignature witness", func(t *testing.T) { 3242 tx := "AAIAAACWP5gAAAAAAAAAAAAAAAAAAgAAAAEGyZgQyJQyWjzvqUZochi8rGE9RQEAVgsVDBQBAgMAAAAAAAAAAAAAAAAAAAAAAAwUBsmYEMiUMlo876lGaHIYvKxhPUUUwB8MCHRyYW5zZmVyDBTPduKL0AYsSkeO41VhARMZ88+k0kFifVtSAcYMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9CAETDCECbSVpJ0BN2xiveIGNU0LzEz4V7FUAp1NV8s7YI4kq9eQMIQOaHY3bh+3OmXuU9t72Pj62loLM7gZDgXJwnBV2zO4u1wwhAsvP18ohoYHcHBPt4wwPqAOstOhazEegr4klYmDlWpzeDCED9EsK7L0qFMP1QpBBfKMMVXPLa894ONINLRtjtBLBM6oUVwcAdW3AcXZDbigDOG7AcEHF+6DgAwAAAAABAAAAnhSNQS1RCDAQzotyEHMQdGtuuGxtuJIkQgB6aGvOaWzOahTAEAwPdmVyaWZ5V2l0aEVDRHNhDBQb9XWrEYlohBNhCjWhKIbN4LZsckFifVtSa55zbJx0IrlrbrM=" 3243 resp := checkErrGetResult(t, calcReqExactly(t, tx), false, 0) 3244 res := new(result.NetworkFee) 3245 require.NoError(t, json.Unmarshal(resp, res)) 3246 require.Equal(t, int64(8992070), res.Value) 3247 }) 3248 checkContract := func(t *testing.T, verAcc util.Uint160, invoc []byte, fee int64) { 3249 txScript, err := smartcontract.CreateCallWithAssertScript(chain.UtilityTokenHash(), "transfer", 3250 verAcc, verAcc, 1, nil) 3251 require.NoError(t, err) 3252 tx := &transaction.Transaction{ 3253 Script: txScript, 3254 Signers: []transaction.Signer{{Account: verAcc, Scopes: transaction.CalledByEntry}}, 3255 Scripts: []transaction.Witness{{ 3256 InvocationScript: invoc, 3257 VerificationScript: []byte{}, 3258 }}, 3259 } 3260 checkCalc(t, tx, fee) 3261 } 3262 t.Run("contract-based verification", func(t *testing.T) { 3263 verAcc, err := util.Uint160DecodeStringLE(verifyContractHash) 3264 require.NoError(t, err) 3265 checkContract(t, verAcc, []byte{}, 636610) // No C# match, but we believe it's OK. 3266 }) 3267 t.Run("contract-based verification with parameters", func(t *testing.T) { 3268 verAcc, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash) 3269 require.NoError(t, err) 3270 checkContract(t, verAcc, []byte{}, 244130) // No C# match, but we believe it's OK and it differs from the one above. 3271 }) 3272 t.Run("contract-based verification with invocation script", func(t *testing.T) { 3273 verAcc, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash) 3274 require.NoError(t, err) 3275 invocWriter := io.NewBufBinWriter() 3276 emit.Bool(invocWriter.BinWriter, false) 3277 emit.Int(invocWriter.BinWriter, 5) 3278 emit.String(invocWriter.BinWriter, "") 3279 invocScript := invocWriter.Bytes() 3280 checkContract(t, verAcc, invocScript, 146960) // No C# match, but we believe it's OK and it has a specific invocation script overriding anything server-side. 3281 }) 3282 t.Run("execution limit, ok", func(t *testing.T) { 3283 // 1_4000_0000 GAS with the default 1.5 allowed by Policy 3284 verifScript := []byte{byte(opcode.PUSHINT32), 0x00, 0x3b, 0x58, 0x08, byte(opcode.SYSCALL), 0, 0, 0, 0, byte(opcode.PUSHT)} 3285 binary.LittleEndian.PutUint32(verifScript[6:], interopnames.ToID([]byte(interopnames.SystemRuntimeBurnGas))) 3286 tx := &transaction.Transaction{ 3287 Script: []byte{byte(opcode.RET)}, 3288 Signers: []transaction.Signer{{Account: hash.Hash160(verifScript)}}, 3289 Scripts: []transaction.Witness{{ 3290 InvocationScript: []byte{byte(opcode.NOP)}, 3291 VerificationScript: verifScript, 3292 }}, 3293 } 3294 checkCalc(t, tx, 140065570) 3295 }) 3296 }) 3297 t.Run("sendrawtransaction", func(t *testing.T) { 3298 rpc := `{"jsonrpc": "2.0", "id": 1, "method": "sendrawtransaction", "params": ["%s"]}` 3299 t.Run("invalid signature", func(t *testing.T) { 3300 tx := newTxWithParams(t, chain, opcode.PUSH1, 10, 1, 1, false) 3301 tx.Scripts[0].InvocationScript[10] = ^tx.Scripts[0].InvocationScript[10] 3302 rawTx := encodeBinaryToString(t, tx) 3303 body := doRPCCall(fmt.Sprintf(rpc, rawTx), httpSrv.URL, t) 3304 checkErrGetResult(t, body, true, neorpc.ErrInvalidSignatureCode) 3305 }) 3306 t.Run("too big tx", func(t *testing.T) { 3307 script := make([]byte, transaction.MaxScriptLength) 3308 for i := range script { 3309 script[i] = byte(opcode.PUSH0) 3310 } 3311 groups := make([]*keys.PublicKey, 16) 3312 for i := range groups { 3313 pk, _ := keys.NewPrivateKey() 3314 groups[i] = pk.PublicKey() 3315 } 3316 signers := make([]transaction.Signer, transaction.MaxAttributes) 3317 for i := range signers { 3318 signers[i] = transaction.Signer{ 3319 Account: random.Uint160(), 3320 Scopes: transaction.CustomContracts | transaction.CustomGroups, 3321 AllowedContracts: make([]util.Uint160, 16), 3322 AllowedGroups: groups, 3323 } 3324 } 3325 scripts := make([]transaction.Witness, len(signers)) 3326 for i := range scripts { 3327 scripts[i] = transaction.Witness{ 3328 InvocationScript: random.Bytes(transaction.MaxInvocationScript), 3329 VerificationScript: random.Bytes(transaction.MaxVerificationScript), 3330 } 3331 } 3332 tx := &transaction.Transaction{ 3333 ValidUntilBlock: chain.BlockHeight() + 1, 3334 Script: script, 3335 Attributes: []transaction.Attribute{}, 3336 Signers: signers, 3337 Scripts: scripts, 3338 } 3339 rawTx := encodeBinaryToString(t, tx) 3340 body := doRPCCall(fmt.Sprintf(rpc, rawTx), httpSrv.URL, t) 3341 checkErrGetResult(t, body, true, neorpc.ErrInvalidSizeCode) 3342 }) 3343 t.Run("mempool OOM", func(t *testing.T) { 3344 chain, _, httpSrv := initClearServerWithCustomConfig(t, func(c *config.Config) { 3345 c.ProtocolConfiguration.MemPoolSize = 1 3346 }) 3347 3348 // create and push the first (prioritized) transaction with increased networkFee 3349 tx := newTxWithParams(t, chain, opcode.PUSH1, 10, 1, 2, false) 3350 rawTx := encodeBinaryToString(t, tx) 3351 body := doRPCCall(fmt.Sprintf(rpc, rawTx), httpSrv.URL, t) 3352 checkErrGetResult(t, body, false, 0) 3353 3354 // create and push the second transaction with standard networkFee 3355 tx2 := newTxWithParams(t, chain, opcode.PUSH1, 10, 1, 1, false) 3356 rawTx2 := encodeBinaryToString(t, tx2) 3357 body2 := doRPCCall(fmt.Sprintf(rpc, rawTx2), httpSrv.URL, t) 3358 checkErrGetResult(t, body2, true, neorpc.ErrMempoolCapReachedCode) 3359 }) 3360 t.Run("mempool conflict", func(t *testing.T) { 3361 chain, _, httpSrv := initClearServerWithCustomConfig(t, func(c *config.Config) { 3362 c.ProtocolConfiguration.MemPoolSize = 2 3363 }) 3364 3365 // Create and push the first transaction with large network fee. 3366 tx := newTxWithParams(t, chain, opcode.PUSH1, 10, 1, 25, false) 3367 rawTx := encodeBinaryToString(t, tx) 3368 body := doRPCCall(fmt.Sprintf(rpc, rawTx), httpSrv.URL, t) 3369 checkErrGetResult(t, body, false, 0) 3370 3371 // Create and push the second transaction, sender doesn't have enough balance to pay for two transactions. 3372 tx2 := newTxWithParams(t, chain, opcode.PUSH1, 10, 1, 25, false) 3373 rawTx2 := encodeBinaryToString(t, tx2) 3374 body2 := doRPCCall(fmt.Sprintf(rpc, rawTx2), httpSrv.URL, t) 3375 checkErrGetResult(t, body2, true, neorpc.ErrInsufficientFundsCode) 3376 }) 3377 }) 3378 t.Run("test functions with unsupported states", func(t *testing.T) { 3379 chain, _, httpSrv := initClearServerWithCustomConfig(t, func(c *config.Config) { 3380 c.ApplicationConfiguration.Ledger.KeepOnlyLatestState = true 3381 }) 3382 3383 e := &executor{chain: chain, httpSrv: httpSrv} 3384 rpc := `{"jsonrpc": "2.0", "id": 1, "method": "%s", "params": %s}` 3385 for method, cases := range rpcFunctionsWithUnsupportedStatesTestCases { 3386 runTestCasesWithExecutor(t, e, rpc, method, cases, doRPCCall, checkErrGetResult) 3387 } 3388 }) 3389 } 3390 3391 func (e *executor) getHeader(s string) *block.Header { 3392 hash, err := util.Uint256DecodeStringLE(s) 3393 if err != nil { 3394 panic("can not decode hash parameter") 3395 } 3396 block, err := e.chain.GetBlock(hash) 3397 if err != nil { 3398 panic("unknown block (update block hash)") 3399 } 3400 return &block.Header 3401 } 3402 3403 func encodeBinaryToString(t *testing.T, a io.Serializable) string { 3404 bytes, err := testserdes.EncodeBinary(a) 3405 require.NoError(t, err) 3406 return base64.StdEncoding.EncodeToString(bytes) 3407 } 3408 3409 func newTxWithParams(t *testing.T, chain *core.Blockchain, code opcode.Opcode, validUntilIncr uint32, systemFee int64, 3410 networkFeeMultiplier int64, addAttrNotValidBeforeT bool) *transaction.Transaction { 3411 priv0 := testchain.PrivateKeyByID(0) 3412 acc0 := wallet.NewAccountFromPrivateKey(priv0) 3413 3414 height := chain.BlockHeight() 3415 tx := transaction.New([]byte{byte(code)}, 0) 3416 tx.Nonce = uint32(random.Int(0, math.MaxUint32)) 3417 tx.ValidUntilBlock = height + validUntilIncr 3418 tx.Signers = []transaction.Signer{{Account: acc0.PrivateKey().GetScriptHash()}} 3419 tx.SystemFee = systemFee 3420 // add network fee 3421 size := io.GetVarSize(tx) 3422 netFee, sizeDelta := fee.Calculate(chain.GetBaseExecFee(), acc0.Contract.Script) 3423 tx.NetworkFee += netFee 3424 size += sizeDelta 3425 tx.NetworkFee += int64(size) * chain.FeePerByte() 3426 tx.NetworkFee = tx.NetworkFee * networkFeeMultiplier 3427 if addAttrNotValidBeforeT { 3428 tx.Attributes = []transaction.Attribute{ 3429 {Type: transaction.NotValidBeforeT, Value: &transaction.NotValidBefore{Height: height + 1}}, 3430 } 3431 } 3432 require.NoError(t, acc0.SignTx(testchain.Network(), tx)) 3433 return tx 3434 } 3435 3436 func (tc rpcTestCase) getResultPair(e *executor) (expected any, res any) { 3437 expected = tc.result(e) 3438 resVal := reflect.New(reflect.TypeOf(expected).Elem()) 3439 res = resVal.Interface() 3440 return expected, res 3441 } 3442 3443 func checkErrGetResult(t *testing.T, body []byte, expectingFail bool, expectedErrCode int64, expectedErr ...string) json.RawMessage { 3444 var resp neorpc.Response 3445 err := json.Unmarshal(body, &resp) 3446 require.Nil(t, err) 3447 if expectingFail { 3448 require.NotNil(t, resp.Error) 3449 assert.NotEqual(t, 0, resp.Error.Code) 3450 assert.Equal(t, expectedErrCode, resp.Error.Code) 3451 assert.NotEqual(t, "", resp.Error.Message) 3452 if len(expectedErr) != 0 { 3453 assert.True(t, strings.Contains(resp.Error.Error(), expectedErr[0]), fmt.Sprintf("expected: %s, got: %s", expectedErr[0], resp.Error.Error())) 3454 } 3455 } else { 3456 assert.Nil(t, resp.Error) 3457 } 3458 return resp.Result 3459 } 3460 3461 func checkErrGetBatchResult(t *testing.T, body []byte, expectingFail bool, expectedErrCode int64, expectedErr ...string) json.RawMessage { 3462 var resp []neorpc.Response 3463 err := json.Unmarshal(body, &resp) 3464 require.Nil(t, err) 3465 require.Equal(t, 1, len(resp)) 3466 if expectingFail { 3467 require.NotNil(t, resp[0].Error) 3468 assert.NotEqual(t, 0, resp[0].Error.Code) 3469 assert.NotEqual(t, "", resp[0].Error.Message) 3470 } else { 3471 assert.Nil(t, resp[0].Error) 3472 } 3473 return resp[0].Result 3474 } 3475 3476 func doRPCCallOverWS(rpcCall string, url string, t *testing.T) []byte { 3477 dialer := websocket.Dialer{ 3478 HandshakeTimeout: 5 * time.Second, 3479 NetDialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { 3480 dialer := net.Dialer{Timeout: 5 * time.Second, 3481 KeepAlive: 30 * time.Second, 3482 FallbackDelay: -1} 3483 return dialer.DialContext(ctx, "tcp4", addr) 3484 }, 3485 } 3486 url = "ws" + strings.TrimPrefix(url, "http") 3487 c, r, err := dialer.Dial(url+"/ws", nil) 3488 require.NoError(t, err) 3489 defer r.Body.Close() 3490 err = c.SetWriteDeadline(time.Now().Add(5 * time.Second)) 3491 require.NoError(t, err) 3492 require.NoError(t, c.WriteMessage(1, []byte(rpcCall))) 3493 err = c.SetReadDeadline(time.Now().Add(5 * time.Second)) 3494 require.NoError(t, err) 3495 _, body, err := c.ReadMessage() 3496 require.NoError(t, err) 3497 require.NoError(t, c.Close()) 3498 return bytes.TrimSpace(body) 3499 } 3500 3501 func doRPCCallOverHTTP(rpcCall string, url string, t *testing.T) []byte { 3502 cl := http.Client{Timeout: 5 * time.Second, Transport: &http.Transport{ 3503 MaxIdleConns: 50, 3504 MaxConnsPerHost: 50, 3505 MaxIdleConnsPerHost: 50, 3506 IdleConnTimeout: 5 * time.Second, 3507 DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { 3508 dialer := net.Dialer{Timeout: 5 * time.Second, 3509 KeepAlive: 30 * time.Second, 3510 FallbackDelay: -1} 3511 return dialer.DialContext(ctx, "tcp4", addr) 3512 }, 3513 }} 3514 resp, err := cl.Post(url, "application/json", strings.NewReader(rpcCall)) 3515 require.NoErrorf(t, err, "could not make a POST request") 3516 body, err := gio.ReadAll(resp.Body) 3517 resp.Body.Close() 3518 assert.NoErrorf(t, err, "could not read response from the request: %s", rpcCall) 3519 cl.CloseIdleConnections() 3520 return bytes.TrimSpace(body) 3521 } 3522 3523 func checkNep11Balances(t *testing.T, e *executor, acc any) { 3524 res, ok := acc.(*result.NEP11Balances) 3525 require.True(t, ok) 3526 3527 expected := result.NEP11Balances{ 3528 Balances: []result.NEP11AssetBalance{ 3529 { 3530 Asset: nnsHash, 3531 Name: "NameService", 3532 Symbol: "NNS", 3533 Tokens: []result.NEP11TokenBalance{ 3534 { 3535 ID: nnsToken1ID, 3536 Amount: "1", 3537 LastUpdated: 14, 3538 }, 3539 }, 3540 }, 3541 { 3542 Asset: nfsoHash, 3543 Decimals: 2, 3544 Name: "NeoFS Object NFT", 3545 Symbol: "NFSO", 3546 Tokens: []result.NEP11TokenBalance{ 3547 { 3548 ID: nfsoToken1ID, 3549 Amount: "80", 3550 LastUpdated: 21, 3551 }, 3552 }, 3553 }, 3554 }, 3555 Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), 3556 } 3557 require.Equal(t, testchain.PrivateKeyByID(0).Address(), res.Address) 3558 require.ElementsMatch(t, expected.Balances, res.Balances) 3559 } 3560 3561 func checkNep17Balances(t *testing.T, e *executor, acc any) { 3562 res, ok := acc.(*result.NEP17Balances) 3563 require.True(t, ok) 3564 rubles, err := util.Uint160DecodeStringLE(testContractHash) 3565 require.NoError(t, err) 3566 expected := result.NEP17Balances{ 3567 Balances: []result.NEP17Balance{ 3568 { 3569 Asset: rubles, 3570 Amount: "877", 3571 Decimals: 2, 3572 LastUpdated: 6, 3573 Name: "Rubl", 3574 Symbol: "RUB", 3575 }, 3576 { 3577 Asset: e.chain.GoverningTokenHash(), 3578 Amount: "99998000", 3579 LastUpdated: 4, 3580 Name: "NeoToken", 3581 Symbol: "NEO", 3582 }, 3583 { 3584 Asset: e.chain.UtilityTokenHash(), 3585 Amount: "37106285100", 3586 LastUpdated: 23, 3587 Decimals: 8, 3588 Name: "GasToken", 3589 Symbol: "GAS", 3590 }}, 3591 Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), 3592 } 3593 require.Equal(t, testchain.PrivateKeyByID(0).Address(), res.Address) 3594 require.ElementsMatch(t, expected.Balances, res.Balances) 3595 } 3596 3597 func checkNep11Transfers(t *testing.T, e *executor, acc any) { 3598 checkNep11TransfersAux(t, e, acc, []int{0}, []int{0, 1, 2}) 3599 } 3600 3601 func checkNep11TransfersAux(t *testing.T, e *executor, acc any, sent, rcvd []int) { 3602 res, ok := acc.(*result.NEP11Transfers) 3603 require.True(t, ok) 3604 3605 blockReceiveNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(21)) // transfer 0.05 NFSO from priv1 back to priv0. 3606 require.NoError(t, err) 3607 require.Equal(t, 1, len(blockReceiveNFSO.Transactions)) 3608 txReceiveNFSO := blockReceiveNFSO.Transactions[0] 3609 3610 blockSendNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(19)) // transfer 0.25 NFSO from priv0 to priv1. 3611 require.NoError(t, err) 3612 require.Equal(t, 1, len(blockSendNFSO.Transactions)) 3613 txSendNFSO := blockSendNFSO.Transactions[0] 3614 3615 blockMintNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(18)) // mint 1.00 NFSO token by transferring 10 GAS to NFSO contract. 3616 require.NoError(t, err) 3617 require.Equal(t, 1, len(blockMintNFSO.Transactions)) 3618 txMintNFSO := blockMintNFSO.Transactions[0] 3619 3620 blockRegisterNSRecordA, err := e.chain.GetBlock(e.chain.GetHeaderHash(14)) // register `neo.com` with A record type and priv0 owner via NS 3621 require.NoError(t, err) 3622 require.Equal(t, 1, len(blockRegisterNSRecordA.Transactions)) 3623 txRegisterNSRecordA := blockRegisterNSRecordA.Transactions[0] 3624 3625 // These are laid out here explicitly for 2 purposes: 3626 // * to be able to reference any particular event for paging 3627 // * to check chain events consistency 3628 // Technically these could be retrieved from application log, but that would almost 3629 // duplicate the Server method. 3630 expected := result.NEP11Transfers{ 3631 Sent: []result.NEP11Transfer{ 3632 { 3633 Timestamp: blockSendNFSO.Timestamp, 3634 Asset: nfsoHash, 3635 Address: testchain.PrivateKeyByID(1).Address(), // to priv1 3636 ID: nfsoToken1ID, // NFSO ID 3637 Amount: big.NewInt(25).String(), 3638 Index: 19, 3639 TxHash: txSendNFSO.Hash(), 3640 }, 3641 }, 3642 Received: []result.NEP11Transfer{ 3643 { 3644 Timestamp: blockReceiveNFSO.Timestamp, 3645 Asset: nfsoHash, 3646 ID: nfsoToken1ID, 3647 Address: testchain.PrivateKeyByID(1).Address(), // from priv1 3648 Amount: "5", 3649 Index: 21, 3650 TxHash: txReceiveNFSO.Hash(), 3651 }, 3652 { 3653 Timestamp: blockMintNFSO.Timestamp, 3654 Asset: nfsoHash, 3655 ID: nfsoToken1ID, 3656 Address: "", // minting 3657 Amount: "100", 3658 Index: 18, 3659 TxHash: txMintNFSO.Hash(), 3660 }, 3661 { 3662 Timestamp: blockRegisterNSRecordA.Timestamp, 3663 Asset: nnsHash, 3664 ID: nnsToken1ID, 3665 Address: "", // minting 3666 Amount: "1", 3667 Index: 14, 3668 TxHash: txRegisterNSRecordA.Hash(), 3669 }, 3670 }, 3671 Address: testchain.PrivateKeyByID(0).Address(), 3672 } 3673 3674 require.Equal(t, expected.Address, res.Address) 3675 3676 arr := make([]result.NEP11Transfer, 0, len(expected.Sent)) 3677 for i := range expected.Sent { 3678 for _, j := range sent { 3679 if i == j { 3680 arr = append(arr, expected.Sent[i]) 3681 break 3682 } 3683 } 3684 } 3685 require.Equal(t, arr, res.Sent) 3686 3687 arr = arr[:0] 3688 for i := range expected.Received { 3689 for _, j := range rcvd { 3690 if i == j { 3691 arr = append(arr, expected.Received[i]) 3692 break 3693 } 3694 } 3695 } 3696 require.Equal(t, arr, res.Received) 3697 } 3698 3699 func checkNep17Transfers(t *testing.T, e *executor, acc any) { 3700 checkNep17TransfersAux(t, e, acc, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}, []int{0, 1, 2, 3, 4, 5, 6, 7, 8}) 3701 } 3702 3703 func checkNep17TransfersAux(t *testing.T, e *executor, acc any, sent, rcvd []int) { 3704 res, ok := acc.(*result.NEP17Transfers) 3705 require.True(t, ok) 3706 rublesHash, err := util.Uint160DecodeStringLE(testContractHash) 3707 require.NoError(t, err) 3708 3709 blockWithFAULTedTx, err := e.chain.GetBlock(e.chain.GetHeaderHash(faultedTxBlock)) // Transaction with ABORT inside. 3710 require.NoError(t, err) 3711 require.Equal(t, 1, len(blockWithFAULTedTx.Transactions)) 3712 txFAULTed := blockWithFAULTedTx.Transactions[0] 3713 3714 blockDeploy6, err := e.chain.GetBlock(e.chain.GetHeaderHash(22)) // deploy Storage contract (storage_contract.go) 3715 require.NoError(t, err) 3716 require.Equal(t, 1, len(blockDeploy6.Transactions)) 3717 txDeploy6 := blockDeploy6.Transactions[0] 3718 3719 blockTransferNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(19)) // transfer 0.25 NFSO from priv0 to priv1. 3720 require.NoError(t, err) 3721 require.Equal(t, 1, len(blockTransferNFSO.Transactions)) 3722 txTransferNFSO := blockTransferNFSO.Transactions[0] 3723 3724 blockMintNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(18)) // mint 1.00 NFSO token for priv0 by transferring 10 GAS to NFSO contract. 3725 require.NoError(t, err) 3726 require.Equal(t, 1, len(blockMintNFSO.Transactions)) 3727 txMintNFSO := blockMintNFSO.Transactions[0] 3728 3729 blockDeploy5, err := e.chain.GetBlock(e.chain.GetHeaderHash(17)) // deploy NeoFS Object contract (NEP11-Divisible) 3730 require.NoError(t, err) 3731 require.Equal(t, 1, len(blockDeploy5.Transactions)) 3732 txDeploy5 := blockDeploy5.Transactions[0] 3733 3734 blockPutNewTestValue, err := e.chain.GetBlock(e.chain.GetHeaderHash(16)) // invoke `put` method of `test_contract.go` with `testkey`, `newtestvalue` args 3735 require.NoError(t, err) 3736 require.Equal(t, 4, len(blockPutNewTestValue.Transactions)) 3737 txPutNewTestValue := blockPutNewTestValue.Transactions[0] 3738 txPutValue1 := blockPutNewTestValue.Transactions[1] // invoke `put` method of `test_contract.go` with `aa`, `v1` args 3739 txPutValue2 := blockPutNewTestValue.Transactions[2] // invoke `put` method of `test_contract.go` with `aa10`, `v2` args 3740 txPutValue3 := blockPutNewTestValue.Transactions[3] // invoke `put` method of `test_contract.go` with `aa50`, `v3` args 3741 3742 blockSetRecord, err := e.chain.GetBlock(e.chain.GetHeaderHash(15)) // add type A record to `neo.com` domain via NNS 3743 require.NoError(t, err) 3744 require.Equal(t, 1, len(blockSetRecord.Transactions)) 3745 txSetRecord := blockSetRecord.Transactions[0] 3746 3747 blockRegisterDomain, err := e.chain.GetBlock(e.chain.GetHeaderHash(14)) // register `neo.com` domain via NNS 3748 require.NoError(t, err) 3749 require.Equal(t, 1, len(blockRegisterDomain.Transactions)) 3750 txRegisterDomain := blockRegisterDomain.Transactions[0] 3751 3752 blockGASBounty2, err := e.chain.GetBlock(e.chain.GetHeaderHash(12)) // size of committee = 6 3753 require.NoError(t, err) 3754 3755 blockDeploy4, err := e.chain.GetBlock(e.chain.GetHeaderHash(11)) // deploy ns.go (non-native neo name service contract) 3756 require.NoError(t, err) 3757 require.Equal(t, 1, len(blockDeploy4.Transactions)) 3758 txDeploy4 := blockDeploy4.Transactions[0] 3759 3760 blockDeploy3, err := e.chain.GetBlock(e.chain.GetHeaderHash(10)) // deploy verification_with_args_contract.go 3761 require.NoError(t, err) 3762 require.Equal(t, 1, len(blockDeploy3.Transactions)) 3763 txDeploy3 := blockDeploy3.Transactions[0] 3764 3765 blockDepositGAS, err := e.chain.GetBlock(e.chain.GetHeaderHash(8)) 3766 require.NoError(t, err) 3767 require.Equal(t, 1, len(blockDepositGAS.Transactions)) 3768 txDepositGAS := blockDepositGAS.Transactions[0] 3769 3770 blockDeploy2, err := e.chain.GetBlock(e.chain.GetHeaderHash(7)) // deploy verification_contract.go 3771 require.NoError(t, err) 3772 require.Equal(t, 1, len(blockDeploy2.Transactions)) 3773 txDeploy2 := blockDeploy2.Transactions[0] 3774 3775 blockSendRubles, err := e.chain.GetBlock(e.chain.GetHeaderHash(6)) 3776 require.NoError(t, err) 3777 require.Equal(t, 1, len(blockSendRubles.Transactions)) 3778 txSendRubles := blockSendRubles.Transactions[0] 3779 blockGASBounty1 := blockSendRubles // index 6 = size of committee 3780 3781 blockReceiveRubles, err := e.chain.GetBlock(e.chain.GetHeaderHash(5)) 3782 require.NoError(t, err) 3783 require.Equal(t, 2, len(blockReceiveRubles.Transactions)) 3784 txInitCall := blockReceiveRubles.Transactions[0] 3785 txReceiveRubles := blockReceiveRubles.Transactions[1] 3786 3787 blockSendNEO, err := e.chain.GetBlock(e.chain.GetHeaderHash(4)) 3788 require.NoError(t, err) 3789 require.Equal(t, 1, len(blockSendNEO.Transactions)) 3790 txSendNEO := blockSendNEO.Transactions[0] 3791 3792 blockCtrInv1, err := e.chain.GetBlock(e.chain.GetHeaderHash(3)) 3793 require.NoError(t, err) 3794 require.Equal(t, 1, len(blockCtrInv1.Transactions)) 3795 txCtrInv1 := blockCtrInv1.Transactions[0] 3796 3797 blockCtrDeploy, err := e.chain.GetBlock(e.chain.GetHeaderHash(2)) 3798 require.NoError(t, err) 3799 require.Equal(t, 1, len(blockCtrDeploy.Transactions)) 3800 txCtrDeploy := blockCtrDeploy.Transactions[0] 3801 3802 blockReceiveGAS, err := e.chain.GetBlock(e.chain.GetHeaderHash(1)) 3803 require.NoError(t, err) 3804 require.Equal(t, 2, len(blockReceiveGAS.Transactions)) 3805 txReceiveNEO := blockReceiveGAS.Transactions[0] 3806 txReceiveGAS := blockReceiveGAS.Transactions[1] 3807 3808 blockGASBounty0, err := e.chain.GetBlock(e.chain.GetHeaderHash(0)) 3809 require.NoError(t, err) 3810 3811 // These are laid out here explicitly for 2 purposes: 3812 // * to be able to reference any particular event for paging 3813 // * to check chain events consistency 3814 // Technically these could be retrieved from application log, but that would almost 3815 // duplicate the Server method. 3816 expected := result.NEP17Transfers{ 3817 Sent: []result.NEP17Transfer{ 3818 { 3819 Timestamp: blockWithFAULTedTx.Timestamp, 3820 Asset: e.chain.UtilityTokenHash(), 3821 Address: "", // burn 3822 Amount: big.NewInt(txFAULTed.SystemFee + txFAULTed.NetworkFee).String(), 3823 Index: 23, 3824 TxHash: blockWithFAULTedTx.Hash(), 3825 }, 3826 { 3827 Timestamp: blockDeploy6.Timestamp, 3828 Asset: e.chain.UtilityTokenHash(), 3829 Address: "", // burn 3830 Amount: big.NewInt(txDeploy6.SystemFee + txDeploy6.NetworkFee).String(), 3831 Index: 22, 3832 TxHash: blockDeploy6.Hash(), 3833 }, 3834 { 3835 Timestamp: blockTransferNFSO.Timestamp, 3836 Asset: e.chain.UtilityTokenHash(), 3837 Address: "", // burn 3838 Amount: big.NewInt(txTransferNFSO.SystemFee + txTransferNFSO.NetworkFee).String(), 3839 Index: 19, 3840 TxHash: blockTransferNFSO.Hash(), 3841 }, 3842 { 3843 Timestamp: blockMintNFSO.Timestamp, 3844 Asset: e.chain.UtilityTokenHash(), 3845 Address: address.Uint160ToString(nfsoHash), 3846 Amount: "1000000000", 3847 Index: 18, 3848 NotifyIndex: 0, 3849 TxHash: txMintNFSO.Hash(), 3850 }, 3851 { 3852 Timestamp: blockMintNFSO.Timestamp, 3853 Asset: e.chain.UtilityTokenHash(), 3854 Address: "", // burn 3855 Amount: big.NewInt(txMintNFSO.SystemFee + txMintNFSO.NetworkFee).String(), 3856 Index: 18, 3857 TxHash: blockMintNFSO.Hash(), 3858 }, 3859 { 3860 Timestamp: blockDeploy5.Timestamp, 3861 Asset: e.chain.UtilityTokenHash(), 3862 Address: "", // burn 3863 Amount: big.NewInt(txDeploy5.SystemFee + txDeploy5.NetworkFee).String(), 3864 Index: 17, 3865 TxHash: blockDeploy5.Hash(), 3866 }, 3867 { 3868 Timestamp: blockPutNewTestValue.Timestamp, 3869 Asset: e.chain.UtilityTokenHash(), 3870 Address: "", // burn 3871 Amount: big.NewInt(txPutValue3.SystemFee + txPutValue3.NetworkFee).String(), 3872 Index: 16, 3873 TxHash: blockPutNewTestValue.Hash(), 3874 }, 3875 { 3876 Timestamp: blockPutNewTestValue.Timestamp, 3877 Asset: e.chain.UtilityTokenHash(), 3878 Address: "", // burn 3879 Amount: big.NewInt(txPutValue2.SystemFee + txPutValue2.NetworkFee).String(), 3880 Index: 16, 3881 TxHash: blockPutNewTestValue.Hash(), 3882 }, 3883 { 3884 Timestamp: blockPutNewTestValue.Timestamp, 3885 Asset: e.chain.UtilityTokenHash(), 3886 Address: "", // burn 3887 Amount: big.NewInt(txPutValue1.SystemFee + txPutValue1.NetworkFee).String(), 3888 Index: 16, 3889 TxHash: blockPutNewTestValue.Hash(), 3890 }, 3891 { 3892 Timestamp: blockPutNewTestValue.Timestamp, 3893 Asset: e.chain.UtilityTokenHash(), 3894 Address: "", // burn 3895 Amount: big.NewInt(txPutNewTestValue.SystemFee + txPutNewTestValue.NetworkFee).String(), 3896 Index: 16, 3897 TxHash: blockPutNewTestValue.Hash(), 3898 }, 3899 { 3900 Timestamp: blockSetRecord.Timestamp, 3901 Asset: e.chain.UtilityTokenHash(), 3902 Address: "", // burn 3903 Amount: big.NewInt(txSetRecord.SystemFee + txSetRecord.NetworkFee).String(), 3904 Index: 15, 3905 TxHash: blockSetRecord.Hash(), 3906 }, 3907 { 3908 Timestamp: blockRegisterDomain.Timestamp, 3909 Asset: e.chain.UtilityTokenHash(), 3910 Address: "", // burn 3911 Amount: big.NewInt(txRegisterDomain.SystemFee + txRegisterDomain.NetworkFee).String(), 3912 Index: 14, 3913 TxHash: blockRegisterDomain.Hash(), 3914 }, 3915 { 3916 Timestamp: blockDeploy4.Timestamp, 3917 Asset: e.chain.UtilityTokenHash(), 3918 Address: "", // burn 3919 Amount: big.NewInt(txDeploy4.SystemFee + txDeploy4.NetworkFee).String(), 3920 Index: 11, 3921 TxHash: blockDeploy4.Hash(), 3922 }, 3923 { 3924 Timestamp: blockDeploy3.Timestamp, 3925 Asset: e.chain.UtilityTokenHash(), 3926 Address: "", // burn 3927 Amount: big.NewInt(txDeploy3.SystemFee + txDeploy3.NetworkFee).String(), 3928 Index: 10, 3929 TxHash: blockDeploy3.Hash(), 3930 }, 3931 { 3932 Timestamp: blockDepositGAS.Timestamp, 3933 Asset: e.chain.UtilityTokenHash(), 3934 Address: address.Uint160ToString(e.chain.GetNotaryContractScriptHash()), 3935 Amount: "1000000000", 3936 Index: 8, 3937 NotifyIndex: 0, 3938 TxHash: txDepositGAS.Hash(), 3939 }, 3940 { 3941 Timestamp: blockDepositGAS.Timestamp, 3942 Asset: e.chain.UtilityTokenHash(), 3943 Address: "", // burn 3944 Amount: big.NewInt(txDepositGAS.SystemFee + txDepositGAS.NetworkFee).String(), 3945 Index: 8, 3946 TxHash: blockDepositGAS.Hash(), 3947 }, 3948 { 3949 Timestamp: blockDeploy2.Timestamp, 3950 Asset: e.chain.UtilityTokenHash(), 3951 Address: "", // burn 3952 Amount: big.NewInt(txDeploy2.SystemFee + txDeploy2.NetworkFee).String(), 3953 Index: 7, 3954 TxHash: blockDeploy2.Hash(), 3955 }, 3956 { 3957 Timestamp: blockSendRubles.Timestamp, 3958 Asset: rublesHash, 3959 Address: testchain.PrivateKeyByID(1).Address(), 3960 Amount: "123", 3961 Index: 6, 3962 NotifyIndex: 0, 3963 TxHash: txSendRubles.Hash(), 3964 }, 3965 { 3966 Timestamp: blockSendRubles.Timestamp, 3967 Asset: e.chain.UtilityTokenHash(), 3968 Address: "", // burn 3969 Amount: big.NewInt(txSendRubles.SystemFee + txSendRubles.NetworkFee).String(), 3970 Index: 6, 3971 TxHash: blockSendRubles.Hash(), 3972 }, 3973 { 3974 Timestamp: blockReceiveRubles.Timestamp, 3975 Asset: e.chain.UtilityTokenHash(), 3976 Address: "", // burn 3977 Amount: big.NewInt(txReceiveRubles.SystemFee + txReceiveRubles.NetworkFee).String(), 3978 Index: 5, 3979 TxHash: blockReceiveRubles.Hash(), 3980 }, 3981 { 3982 Timestamp: blockReceiveRubles.Timestamp, 3983 Asset: e.chain.UtilityTokenHash(), 3984 Address: "", // burn 3985 Amount: big.NewInt(txInitCall.SystemFee + txInitCall.NetworkFee).String(), 3986 Index: 5, 3987 TxHash: blockReceiveRubles.Hash(), 3988 }, 3989 { 3990 Timestamp: blockSendNEO.Timestamp, 3991 Asset: e.chain.GoverningTokenHash(), 3992 Address: testchain.PrivateKeyByID(1).Address(), 3993 Amount: "1000", 3994 Index: 4, 3995 NotifyIndex: 0, 3996 TxHash: txSendNEO.Hash(), 3997 }, 3998 { 3999 Timestamp: blockSendNEO.Timestamp, 4000 Asset: e.chain.UtilityTokenHash(), 4001 Address: "", // burn 4002 Amount: big.NewInt(txSendNEO.SystemFee + txSendNEO.NetworkFee).String(), 4003 Index: 4, 4004 TxHash: blockSendNEO.Hash(), 4005 }, 4006 { 4007 Timestamp: blockCtrInv1.Timestamp, 4008 Asset: e.chain.UtilityTokenHash(), 4009 Address: "", // burn has empty receiver 4010 Amount: big.NewInt(txCtrInv1.SystemFee + txCtrInv1.NetworkFee).String(), 4011 Index: 3, 4012 TxHash: blockCtrInv1.Hash(), 4013 }, 4014 { 4015 Timestamp: blockCtrDeploy.Timestamp, 4016 Asset: e.chain.UtilityTokenHash(), 4017 Address: "", // burn has empty receiver 4018 Amount: big.NewInt(txCtrDeploy.SystemFee + txCtrDeploy.NetworkFee).String(), 4019 Index: 2, 4020 TxHash: blockCtrDeploy.Hash(), 4021 }, 4022 }, 4023 Received: []result.NEP17Transfer{ 4024 { 4025 Timestamp: blockMintNFSO.Timestamp, // GAS bounty 4026 Asset: e.chain.UtilityTokenHash(), 4027 Address: "", 4028 Amount: "50000000", 4029 Index: 18, 4030 NotifyIndex: 0, 4031 TxHash: blockMintNFSO.Hash(), 4032 }, 4033 { 4034 Timestamp: blockGASBounty2.Timestamp, 4035 Asset: e.chain.UtilityTokenHash(), 4036 Address: "", 4037 Amount: "50000000", 4038 Index: 12, 4039 NotifyIndex: 0, 4040 TxHash: blockGASBounty2.Hash(), 4041 }, 4042 { 4043 Timestamp: blockGASBounty1.Timestamp, 4044 Asset: e.chain.UtilityTokenHash(), 4045 Address: "", 4046 Amount: "50000000", 4047 Index: 6, 4048 NotifyIndex: 0, 4049 TxHash: blockGASBounty1.Hash(), 4050 }, 4051 { 4052 Timestamp: blockReceiveRubles.Timestamp, 4053 Asset: rublesHash, 4054 Address: address.Uint160ToString(rublesHash), 4055 Amount: "1000", 4056 Index: 5, 4057 NotifyIndex: 0, 4058 TxHash: txReceiveRubles.Hash(), 4059 }, 4060 { 4061 Timestamp: blockSendNEO.Timestamp, 4062 Asset: e.chain.UtilityTokenHash(), 4063 Address: "", // Minted GAS. 4064 Amount: "149998500", 4065 Index: 4, 4066 NotifyIndex: 0, 4067 TxHash: txSendNEO.Hash(), 4068 }, 4069 { 4070 Timestamp: blockReceiveGAS.Timestamp, 4071 Asset: e.chain.UtilityTokenHash(), 4072 Address: testchain.MultisigAddress(), 4073 Amount: "100000000000", 4074 Index: 1, 4075 NotifyIndex: 0, 4076 TxHash: txReceiveGAS.Hash(), 4077 }, 4078 { 4079 Timestamp: blockReceiveGAS.Timestamp, 4080 Asset: e.chain.GoverningTokenHash(), 4081 Address: testchain.MultisigAddress(), 4082 Amount: "99999000", 4083 Index: 1, 4084 NotifyIndex: 0, 4085 TxHash: txReceiveNEO.Hash(), 4086 }, 4087 { 4088 Timestamp: blockGASBounty0.Timestamp, 4089 Asset: e.chain.UtilityTokenHash(), 4090 Address: "", 4091 Amount: "50000000", 4092 Index: 0, 4093 TxHash: blockGASBounty0.Hash(), 4094 }, 4095 }, 4096 Address: testchain.PrivateKeyByID(0).Address(), 4097 } 4098 4099 require.Equal(t, expected.Address, res.Address) 4100 4101 arr := make([]result.NEP17Transfer, 0, len(expected.Sent)) 4102 for i := range expected.Sent { 4103 for _, j := range sent { 4104 if i == j { 4105 arr = append(arr, expected.Sent[i]) 4106 break 4107 } 4108 } 4109 } 4110 require.Equal(t, arr, res.Sent) 4111 4112 arr = arr[:0] 4113 for i := range expected.Received { 4114 for _, j := range rcvd { 4115 if i == j { 4116 arr = append(arr, expected.Received[i]) 4117 break 4118 } 4119 } 4120 } 4121 require.Equal(t, arr, res.Received) 4122 } 4123 4124 func TestEscapeForLog(t *testing.T) { 4125 in := "\n\tbad" 4126 require.Equal(t, "bad", escapeForLog(in)) 4127 } 4128 4129 func BenchmarkHandleIn(b *testing.B) { 4130 chain, orc, cfg, logger := getUnitTestChain(b, false, false, false) 4131 4132 serverConfig, err := network.NewServerConfig(cfg) 4133 require.NoError(b, err) 4134 serverConfig.UserAgent = fmt.Sprintf(config.UserAgentFormat, "0.98.6-test") 4135 serverConfig.LogLevel = zapcore.FatalLevel 4136 server, err := network.NewServer(serverConfig, chain, chain.GetStateSyncModule(), logger) 4137 require.NoError(b, err) 4138 rpcServer := New(chain, cfg.ApplicationConfiguration.RPC, server, orc, logger, make(chan error)) 4139 4140 do := func(b *testing.B, req []byte) { 4141 b.ReportAllocs() 4142 b.ResetTimer() 4143 for i := 0; i < b.N; i++ { 4144 b.StopTimer() 4145 in := new(params.In) 4146 b.StartTimer() 4147 err := json.Unmarshal(req, in) 4148 if err != nil { 4149 b.FailNow() 4150 } 4151 4152 res := rpcServer.handleIn(in, nil) 4153 if res.Error != nil { 4154 b.FailNow() 4155 } 4156 } 4157 b.StopTimer() 4158 } 4159 4160 b.Run("no extra params", func(b *testing.B) { 4161 do(b, []byte(`{"jsonrpc":"2.0", "method":"validateaddress","params":["Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2"]}`)) 4162 }) 4163 4164 b.Run("with extra params", func(b *testing.B) { 4165 do(b, []byte(`{"jsonrpc":"2.0", "method":"validateaddress","params":["Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2", 4166 "set", "of", "different", "parameters", "to", "see", "the", "difference", "between", "unmarshalling", "algorithms", 1234, 5678, 1234567, 765432, true, false, null, 4167 "0x50befd26fdf6e4d957c11e078b24ebce6291456f", "someMethod", [{"type": "String", "value": "50befd26fdf6e4d957c11e078b24ebce6291456f"}, 4168 {"type": "Integer", "value": "42"}, {"type": "Boolean", "value": false}]]}`)) 4169 }) 4170 } 4171 4172 func TestFailedPreconditionShutdown(t *testing.T) { 4173 _, srv, _ := initClearServerWithCustomConfig(t, func(c *config.Config) { 4174 c.ApplicationConfiguration.RPC.Addresses = []string{"not an address"} 4175 }) 4176 4177 srv.Start() 4178 require.Positive(t, len(srv.errChan)) // this is how Start reports internal failures 4179 4180 var stopped atomic.Bool 4181 4182 go func() { 4183 srv.Shutdown() 4184 stopped.Store(true) 4185 }() 4186 4187 require.Eventually(t, stopped.Load, 5*time.Second, 100*time.Millisecond, "Shutdown should return") 4188 } 4189 4190 func TestErrorResponseContentType(t *testing.T) { 4191 _, _, httpSrv := initClearServerWithServices(t, true, false, false) 4192 4193 const ( 4194 expectedContentType = "application/json; charset=utf-8" 4195 req = `{"jsonrpc":"2.0", "method":"unknown","params":[]}` 4196 ) 4197 4198 cl := http.Client{Timeout: 5 * time.Second, Transport: &http.Transport{MaxIdleConns: 50, MaxIdleConnsPerHost: 50, IdleConnTimeout: 5 * time.Second, 4199 DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { 4200 dialer := net.Dialer{Timeout: 5 * time.Second} 4201 return dialer.DialContext(ctx, "tcp4", addr) 4202 }, 4203 }} 4204 resp, err := cl.Post(httpSrv.URL, "application/json", strings.NewReader(req)) 4205 require.NoErrorf(t, err, "could not make a POST request") 4206 resp.Body.Close() 4207 contentType := resp.Header.Get("Content-Type") 4208 require.Equal(t, expectedContentType, contentType) 4209 }