github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/services/rpcsrv/client_test.go (about) 1 package rpcsrv 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/base64" 7 "encoding/hex" 8 "encoding/json" 9 "fmt" 10 "math/big" 11 "net/http" 12 "net/http/httptest" 13 "sort" 14 "strings" 15 "sync" 16 "testing" 17 "time" 18 19 "github.com/google/uuid" 20 "github.com/gorilla/websocket" 21 "github.com/nspcc-dev/neo-go/internal/basicchain" 22 "github.com/nspcc-dev/neo-go/internal/testchain" 23 "github.com/nspcc-dev/neo-go/pkg/config" 24 "github.com/nspcc-dev/neo-go/pkg/core" 25 "github.com/nspcc-dev/neo-go/pkg/core/block" 26 "github.com/nspcc-dev/neo-go/pkg/core/fee" 27 "github.com/nspcc-dev/neo-go/pkg/core/native" 28 "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" 29 "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" 30 "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" 31 "github.com/nspcc-dev/neo-go/pkg/core/state" 32 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 33 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 34 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 35 "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" 36 "github.com/nspcc-dev/neo-go/pkg/io" 37 "github.com/nspcc-dev/neo-go/pkg/neorpc" 38 "github.com/nspcc-dev/neo-go/pkg/neorpc/result" 39 "github.com/nspcc-dev/neo-go/pkg/network" 40 "github.com/nspcc-dev/neo-go/pkg/rpcclient" 41 "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" 42 "github.com/nspcc-dev/neo-go/pkg/rpcclient/gas" 43 "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" 44 "github.com/nspcc-dev/neo-go/pkg/rpcclient/management" 45 "github.com/nspcc-dev/neo-go/pkg/rpcclient/neo" 46 "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" 47 "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" 48 "github.com/nspcc-dev/neo-go/pkg/rpcclient/neptoken" 49 "github.com/nspcc-dev/neo-go/pkg/rpcclient/nns" 50 "github.com/nspcc-dev/neo-go/pkg/rpcclient/notary" 51 "github.com/nspcc-dev/neo-go/pkg/rpcclient/oracle" 52 "github.com/nspcc-dev/neo-go/pkg/rpcclient/policy" 53 "github.com/nspcc-dev/neo-go/pkg/rpcclient/rolemgmt" 54 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 55 "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" 56 "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" 57 "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" 58 "github.com/nspcc-dev/neo-go/pkg/util" 59 "github.com/nspcc-dev/neo-go/pkg/vm/emit" 60 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 61 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 62 "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" 63 "github.com/nspcc-dev/neo-go/pkg/wallet" 64 "github.com/stretchr/testify/assert" 65 "github.com/stretchr/testify/require" 66 ) 67 68 func TestClient_NEP17(t *testing.T) { 69 _, _, httpSrv := initServerWithInMemoryChain(t) 70 71 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 72 require.NoError(t, err) 73 t.Cleanup(c.Close) 74 require.NoError(t, c.Init()) 75 76 h, err := util.Uint160DecodeStringLE(testContractHash) 77 require.NoError(t, err) 78 rub := nep17.NewReader(invoker.New(c, nil), h) 79 80 t.Run("Decimals", func(t *testing.T) { 81 d, err := rub.Decimals() 82 require.NoError(t, err) 83 require.EqualValues(t, 2, d) 84 }) 85 t.Run("TotalSupply", func(t *testing.T) { 86 s, err := rub.TotalSupply() 87 require.NoError(t, err) 88 require.EqualValues(t, big.NewInt(1_000_000), s) 89 }) 90 t.Run("Symbol", func(t *testing.T) { 91 sym, err := rub.Symbol() 92 require.NoError(t, err) 93 require.Equal(t, "RUB", sym) 94 }) 95 t.Run("TokenInfo", func(t *testing.T) { 96 tok, err := neptoken.Info(c, h) 97 require.NoError(t, err) 98 require.Equal(t, h, tok.Hash) 99 require.Equal(t, "Rubl", tok.Name) 100 require.Equal(t, "RUB", tok.Symbol) 101 require.EqualValues(t, 2, tok.Decimals) 102 }) 103 t.Run("BalanceOf", func(t *testing.T) { 104 acc := testchain.PrivateKeyByID(0).GetScriptHash() 105 b, err := rub.BalanceOf(acc) 106 require.NoError(t, err) 107 require.EqualValues(t, big.NewInt(877), b) 108 }) 109 } 110 111 func TestClientRoleManagement(t *testing.T) { 112 chain, _, httpSrv := initServerWithInMemoryChain(t) 113 114 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 115 require.NoError(t, err) 116 t.Cleanup(c.Close) 117 require.NoError(t, c.Init()) 118 119 act, err := actor.New(c, []actor.SignerAccount{{ 120 Signer: transaction.Signer{ 121 Account: testchain.CommitteeScriptHash(), 122 Scopes: transaction.CalledByEntry, 123 }, 124 Account: &wallet.Account{ 125 Address: testchain.CommitteeAddress(), 126 Contract: &wallet.Contract{ 127 Script: testchain.CommitteeVerificationScript(), 128 }, 129 }, 130 }}) 131 require.NoError(t, err) 132 133 height, err := c.GetBlockCount() 134 require.NoError(t, err) 135 136 rm := rolemgmt.New(act) 137 ks, err := rm.GetDesignatedByRole(noderoles.Oracle, height) 138 require.NoError(t, err) 139 require.Equal(t, 0, len(ks)) 140 141 testKeys := keys.PublicKeys{ 142 testchain.PrivateKeyByID(0).PublicKey(), 143 testchain.PrivateKeyByID(1).PublicKey(), 144 testchain.PrivateKeyByID(2).PublicKey(), 145 testchain.PrivateKeyByID(3).PublicKey(), 146 } 147 148 tx, err := rm.DesignateAsRoleUnsigned(noderoles.Oracle, testKeys) 149 require.NoError(t, err) 150 151 tx.Scripts[0].InvocationScript = testchain.SignCommittee(tx) 152 bl := testchain.NewBlock(t, chain, 1, 0, tx) 153 _, err = c.SubmitBlock(*bl) 154 require.NoError(t, err) 155 156 sort.Sort(testKeys) 157 ks, err = rm.GetDesignatedByRole(noderoles.Oracle, height+1) 158 require.NoError(t, err) 159 require.Equal(t, testKeys, ks) 160 } 161 162 func TestClientPolicyContract(t *testing.T) { 163 chain, _, httpSrv := initServerWithInMemoryChain(t) 164 165 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 166 require.NoError(t, err) 167 t.Cleanup(c.Close) 168 require.NoError(t, c.Init()) 169 170 polizei := policy.NewReader(invoker.New(c, nil)) 171 172 val, err := polizei.GetExecFeeFactor() 173 require.NoError(t, err) 174 require.Equal(t, int64(30), val) 175 176 val, err = polizei.GetFeePerByte() 177 require.NoError(t, err) 178 require.Equal(t, int64(1000), val) 179 180 val, err = polizei.GetStoragePrice() 181 require.NoError(t, err) 182 require.Equal(t, int64(100000), val) 183 184 val, err = polizei.GetAttributeFee(transaction.NotaryAssistedT) 185 require.NoError(t, err) 186 require.Equal(t, int64(1000_0000), val) 187 188 ret, err := polizei.IsBlocked(util.Uint160{}) 189 require.NoError(t, err) 190 require.False(t, ret) 191 192 act, err := actor.New(c, []actor.SignerAccount{{ 193 Signer: transaction.Signer{ 194 Account: testchain.CommitteeScriptHash(), 195 Scopes: transaction.CalledByEntry, 196 }, 197 Account: &wallet.Account{ 198 Address: testchain.CommitteeAddress(), 199 Contract: &wallet.Contract{ 200 Script: testchain.CommitteeVerificationScript(), 201 }, 202 }, 203 }}) 204 require.NoError(t, err) 205 206 polis := policy.New(act) 207 208 txexec, err := polis.SetExecFeeFactorUnsigned(100) 209 require.NoError(t, err) 210 211 txnetfee, err := polis.SetFeePerByteUnsigned(500) 212 require.NoError(t, err) 213 214 txstorage, err := polis.SetStoragePriceUnsigned(100500) 215 require.NoError(t, err) 216 217 txattr, err := polis.SetAttributeFeeUnsigned(transaction.NotaryAssistedT, 100500) 218 require.NoError(t, err) 219 220 txblock, err := polis.BlockAccountUnsigned(util.Uint160{1, 2, 3}) 221 require.NoError(t, err) 222 223 for _, tx := range []*transaction.Transaction{txattr, txblock, txstorage, txnetfee, txexec} { 224 tx.Scripts[0].InvocationScript = testchain.SignCommittee(tx) 225 } 226 227 bl := testchain.NewBlock(t, chain, 1, 0, txattr, txblock, txstorage, txnetfee, txexec) 228 _, err = c.SubmitBlock(*bl) 229 require.NoError(t, err) 230 231 val, err = polizei.GetExecFeeFactor() 232 require.NoError(t, err) 233 require.Equal(t, int64(100), val) 234 235 val, err = polizei.GetFeePerByte() 236 require.NoError(t, err) 237 require.Equal(t, int64(500), val) 238 239 val, err = polizei.GetStoragePrice() 240 require.NoError(t, err) 241 require.Equal(t, int64(100500), val) 242 243 val, err = polizei.GetAttributeFee(transaction.NotaryAssistedT) 244 require.NoError(t, err) 245 require.Equal(t, int64(100500), val) 246 247 ret, err = polizei.IsBlocked(util.Uint160{1, 2, 3}) 248 require.NoError(t, err) 249 require.True(t, ret) 250 } 251 252 func TestClientManagementContract(t *testing.T) { 253 chain, _, httpSrv := initServerWithInMemoryChain(t) 254 255 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 256 require.NoError(t, err) 257 t.Cleanup(c.Close) 258 require.NoError(t, c.Init()) 259 260 manReader := management.NewReader(invoker.New(c, nil)) 261 262 fee, err := manReader.GetMinimumDeploymentFee() 263 require.NoError(t, err) 264 require.Equal(t, big.NewInt(10*1_0000_0000), fee) 265 266 cs1, err := manReader.GetContract(gas.Hash) 267 require.NoError(t, err) 268 cs2, err := c.GetContractStateByHash(gas.Hash) 269 require.NoError(t, err) 270 require.Equal(t, cs2, cs1) 271 cs1, err = manReader.GetContractByID(-6) 272 require.NoError(t, err) 273 require.Equal(t, cs2, cs1) 274 275 ret, err := manReader.HasMethod(gas.Hash, "transfer", 4) 276 require.NoError(t, err) 277 require.True(t, ret) 278 279 act, err := actor.New(c, []actor.SignerAccount{{ 280 Signer: transaction.Signer{ 281 Account: testchain.CommitteeScriptHash(), 282 Scopes: transaction.CalledByEntry, 283 }, 284 Account: &wallet.Account{ 285 Address: testchain.CommitteeAddress(), 286 Contract: &wallet.Contract{ 287 Script: testchain.CommitteeVerificationScript(), 288 }, 289 }, 290 }}) 291 require.NoError(t, err) 292 293 ids, err := manReader.GetContractHashesExpanded(10) 294 require.NoError(t, err) 295 ctrs := make([]management.IDHash, 0) 296 for i, s := range []string{testContractHash, verifyContractHash, verifyWithArgsContractHash, nnsContractHash, nfsoContractHash, storageContractHash} { 297 h, err := util.Uint160DecodeStringLE(s) 298 require.NoError(t, err) 299 ctrs = append(ctrs, management.IDHash{ID: int32(i) + 1, Hash: h}) 300 } 301 require.Equal(t, ctrs, ids) 302 303 iter, err := manReader.GetContractHashes() 304 require.NoError(t, err) 305 ids, err = iter.Next(3) 306 require.NoError(t, err) 307 require.Equal(t, ctrs[:3], ids) 308 ids, err = iter.Next(10) 309 require.NoError(t, err) 310 require.Equal(t, ctrs[3:], ids) 311 312 man := management.New(act) 313 314 txfee, err := man.SetMinimumDeploymentFeeUnsigned(big.NewInt(1 * 1_0000_0000)) 315 require.NoError(t, err) 316 txdepl, err := man.DeployUnsigned(&cs1.NEF, &cs1.Manifest, nil) // Redeploy from a different account. 317 require.NoError(t, err) 318 319 for _, tx := range []*transaction.Transaction{txfee, txdepl} { 320 tx.Scripts[0].InvocationScript = testchain.SignCommittee(tx) 321 } 322 323 bl := testchain.NewBlock(t, chain, 1, 0, txfee, txdepl) 324 _, err = c.SubmitBlock(*bl) 325 require.NoError(t, err) 326 327 fee, err = manReader.GetMinimumDeploymentFee() 328 require.NoError(t, err) 329 require.Equal(t, big.NewInt(1_0000_0000), fee) 330 331 appLog, err := c.GetApplicationLog(txdepl.Hash(), nil) 332 require.NoError(t, err) 333 require.Equal(t, vmstate.Halt, appLog.Executions[0].VMState) 334 require.Equal(t, 1, len(appLog.Executions[0].Events)) 335 } 336 337 func TestClientNEOContract(t *testing.T) { 338 chain, _, httpSrv := initServerWithInMemoryChain(t) 339 340 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 341 require.NoError(t, err) 342 t.Cleanup(c.Close) 343 require.NoError(t, c.Init()) 344 345 neoR := neo.NewReader(invoker.New(c, nil)) 346 347 sym, err := neoR.Symbol() 348 require.NoError(t, err) 349 require.Equal(t, "NEO", sym) 350 351 dec, err := neoR.Decimals() 352 require.NoError(t, err) 353 require.Equal(t, 0, dec) 354 355 ts, err := neoR.TotalSupply() 356 require.NoError(t, err) 357 require.Equal(t, big.NewInt(1_0000_0000), ts) 358 359 comm, err := neoR.GetCommittee() 360 require.NoError(t, err) 361 commScript, err := smartcontract.CreateMajorityMultiSigRedeemScript(comm) 362 require.NoError(t, err) 363 require.Equal(t, testchain.CommitteeScriptHash(), hash.Hash160(commScript)) 364 365 vals, err := neoR.GetNextBlockValidators() 366 require.NoError(t, err) 367 valsScript, err := smartcontract.CreateDefaultMultiSigRedeemScript(vals) 368 require.NoError(t, err) 369 require.Equal(t, testchain.MultisigScriptHash(), hash.Hash160(valsScript)) 370 371 gpb, err := neoR.GetGasPerBlock() 372 require.NoError(t, err) 373 require.Equal(t, int64(5_0000_0000), gpb) 374 375 regP, err := neoR.GetRegisterPrice() 376 require.NoError(t, err) 377 require.Equal(t, int64(1000_0000_0000), regP) 378 379 acc0 := testchain.PrivateKey(0).PublicKey().GetScriptHash() 380 uncl, err := neoR.UnclaimedGas(acc0, chain.BlockHeight()+1) 381 require.NoError(t, err) 382 require.Equal(t, big.NewInt(10000), uncl) 383 384 accState, err := neoR.GetAccountState(acc0) 385 require.NoError(t, err) 386 require.Equal(t, big.NewInt(1000), &accState.Balance) 387 require.Equal(t, uint32(4), accState.BalanceHeight) 388 389 cands, err := neoR.GetCandidates() 390 require.NoError(t, err) 391 require.Equal(t, 0, len(cands)) // No registrations. 392 393 cands, err = neoR.GetAllCandidatesExpanded(100) 394 require.NoError(t, err) 395 require.Equal(t, 0, len(cands)) // No registrations. 396 397 iter, err := neoR.GetAllCandidates() 398 require.NoError(t, err) 399 cands, err = iter.Next(10) 400 require.NoError(t, err) 401 require.Equal(t, 0, len(cands)) // No registrations. 402 require.NoError(t, iter.Terminate()) 403 404 act, err := actor.New(c, []actor.SignerAccount{{ 405 Signer: transaction.Signer{ 406 Account: testchain.CommitteeScriptHash(), 407 Scopes: transaction.CalledByEntry, 408 }, 409 Account: &wallet.Account{ 410 Address: testchain.CommitteeAddress(), 411 Contract: &wallet.Contract{ 412 Script: testchain.CommitteeVerificationScript(), 413 }, 414 }, 415 }}) 416 417 require.NoError(t, err) 418 419 neoC := neo.New(act) 420 421 txgpb, err := neoC.SetGasPerBlockUnsigned(10 * 1_0000_0000) 422 require.NoError(t, err) 423 txregp, err := neoC.SetRegisterPriceUnsigned(1_0000) 424 require.NoError(t, err) 425 426 for _, tx := range []*transaction.Transaction{txgpb, txregp} { 427 tx.Scripts[0].InvocationScript = testchain.SignCommittee(tx) 428 } 429 430 bl := testchain.NewBlock(t, chain, 1, 0, txgpb, txregp) 431 _, err = c.SubmitBlock(*bl) 432 require.NoError(t, err) 433 434 gpb, err = neoR.GetGasPerBlock() 435 require.NoError(t, err) 436 require.Equal(t, int64(10_0000_0000), gpb) 437 438 regP, err = neoR.GetRegisterPrice() 439 require.NoError(t, err) 440 require.Equal(t, int64(10000), regP) 441 442 act0, err := actor.NewSimple(c, wallet.NewAccountFromPrivateKey(testchain.PrivateKey(0))) 443 require.NoError(t, err) 444 neo0 := neo.New(act0) 445 446 txreg, err := neo0.RegisterCandidateTransaction(testchain.PrivateKey(0).PublicKey()) 447 require.NoError(t, err) 448 bl = testchain.NewBlock(t, chain, 1, 0, txreg) 449 _, err = c.SubmitBlock(*bl) 450 require.NoError(t, err) 451 452 txvote, err := neo0.VoteTransaction(acc0, testchain.PrivateKey(0).PublicKey()) 453 require.NoError(t, err) 454 bl = testchain.NewBlock(t, chain, 1, 0, txvote) 455 _, err = c.SubmitBlock(*bl) 456 require.NoError(t, err) 457 458 txunreg, err := neo0.UnregisterCandidateTransaction(testchain.PrivateKey(0).PublicKey()) 459 require.NoError(t, err) 460 bl = testchain.NewBlock(t, chain, 1, 0, txunreg) 461 _, err = c.SubmitBlock(*bl) 462 require.NoError(t, err) 463 } 464 465 func TestClientNotary(t *testing.T) { 466 chain, _, httpSrv := initServerWithInMemoryChain(t) 467 468 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 469 require.NoError(t, err) 470 t.Cleanup(c.Close) 471 require.NoError(t, c.Init()) 472 473 notaReader := notary.NewReader(invoker.New(c, nil)) 474 475 priv0 := testchain.PrivateKeyByID(0) 476 priv0Hash := priv0.PublicKey().GetScriptHash() 477 bal, err := notaReader.BalanceOf(priv0Hash) 478 require.NoError(t, err) 479 require.Equal(t, big.NewInt(10_0000_0000), bal) 480 481 expir, err := notaReader.ExpirationOf(priv0Hash) 482 require.NoError(t, err) 483 require.Equal(t, uint32(1007), expir) 484 485 maxNVBd, err := notaReader.GetMaxNotValidBeforeDelta() 486 require.NoError(t, err) 487 require.Equal(t, uint32(140), maxNVBd) 488 489 commAct, err := actor.New(c, []actor.SignerAccount{{ 490 Signer: transaction.Signer{ 491 Account: testchain.CommitteeScriptHash(), 492 Scopes: transaction.CalledByEntry, 493 }, 494 Account: &wallet.Account{ 495 Address: testchain.CommitteeAddress(), 496 Contract: &wallet.Contract{ 497 Script: testchain.CommitteeVerificationScript(), 498 }, 499 }, 500 }}) 501 require.NoError(t, err) 502 notaComm := notary.New(commAct) 503 504 txNVB, err := notaComm.SetMaxNotValidBeforeDeltaUnsigned(210) 505 require.NoError(t, err) 506 507 txNVB.Scripts[0].InvocationScript = testchain.SignCommittee(txNVB) 508 bl := testchain.NewBlock(t, chain, 1, 0, txNVB) 509 _, err = c.SubmitBlock(*bl) 510 require.NoError(t, err) 511 512 maxNVBd, err = notaReader.GetMaxNotValidBeforeDelta() 513 require.NoError(t, err) 514 require.Equal(t, uint32(210), maxNVBd) 515 516 privAct, err := actor.New(c, []actor.SignerAccount{{ 517 Signer: transaction.Signer{ 518 Account: priv0Hash, 519 Scopes: transaction.CalledByEntry, 520 }, 521 Account: wallet.NewAccountFromPrivateKey(priv0), 522 }}) 523 require.NoError(t, err) 524 notaPriv := notary.New(privAct) 525 526 txLock, err := notaPriv.LockDepositUntilTransaction(priv0Hash, 1111) 527 require.NoError(t, err) 528 529 bl = testchain.NewBlock(t, chain, 1, 0, txLock) 530 _, err = c.SubmitBlock(*bl) 531 require.NoError(t, err) 532 533 expir, err = notaReader.ExpirationOf(priv0Hash) 534 require.NoError(t, err) 535 require.Equal(t, uint32(1111), expir) 536 537 _, err = notaPriv.WithdrawTransaction(priv0Hash, priv0Hash) 538 require.Error(t, err) // Can't be withdrawn until 1111. 539 } 540 541 func TestCalculateNetworkFee_Base(t *testing.T) { 542 chain, _, httpSrv := initServerWithInMemoryChain(t) 543 const extraFee = 10 544 var nonce uint32 545 546 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 547 require.NoError(t, err) 548 t.Cleanup(c.Close) 549 require.NoError(t, c.Init()) 550 551 feePerByte := chain.FeePerByte() 552 553 t.Run("Simple", func(t *testing.T) { 554 acc0 := wallet.NewAccountFromPrivateKey(testchain.PrivateKeyByID(0)) 555 check := func(t *testing.T, extraFee int64) { 556 tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) 557 tx.ValidUntilBlock = 25 558 tx.Signers = []transaction.Signer{{ 559 Account: acc0.PrivateKey().GetScriptHash(), 560 Scopes: transaction.CalledByEntry, 561 }} 562 tx.Nonce = nonce 563 nonce++ 564 565 tx.Scripts = []transaction.Witness{ 566 {VerificationScript: acc0.GetVerificationScript()}, 567 } 568 actualCalculatedNetFee, err := c.CalculateNetworkFee(tx) 569 require.NoError(t, err) 570 tx.NetworkFee = actualCalculatedNetFee + extraFee 571 572 require.NoError(t, acc0.SignTx(testchain.Network(), tx)) 573 cFee, _ := fee.Calculate(chain.GetBaseExecFee(), acc0.Contract.Script) 574 expected := int64(io.GetVarSize(tx))*feePerByte + cFee + extraFee 575 576 require.Equal(t, expected, actualCalculatedNetFee+extraFee) 577 err = chain.VerifyTx(tx) 578 if extraFee < 0 { 579 require.Error(t, err) 580 } else { 581 require.NoError(t, err) 582 } 583 } 584 585 t.Run("with extra fee", func(t *testing.T) { 586 // check that calculated network fee with extra value is enough 587 check(t, extraFee) 588 }) 589 t.Run("without extra fee", func(t *testing.T) { 590 // check that calculated network fee without extra value is enough 591 check(t, 0) 592 }) 593 t.Run("exactFee-1", func(t *testing.T) { 594 // check that we don't add unexpected extra GAS 595 check(t, -1) 596 }) 597 }) 598 599 t.Run("Multi", func(t *testing.T) { 600 acc0 := wallet.NewAccountFromPrivateKey(testchain.PrivateKeyByID(0)) 601 acc1 := wallet.NewAccountFromPrivateKey(testchain.PrivateKeyByID(0)) 602 err = acc1.ConvertMultisig(3, keys.PublicKeys{ 603 testchain.PrivateKeyByID(0).PublicKey(), 604 testchain.PrivateKeyByID(1).PublicKey(), 605 testchain.PrivateKeyByID(2).PublicKey(), 606 testchain.PrivateKeyByID(3).PublicKey(), 607 }) 608 require.NoError(t, err) 609 check := func(t *testing.T, extraFee int64) { 610 tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) 611 tx.ValidUntilBlock = 25 612 tx.Signers = []transaction.Signer{ 613 { 614 Account: acc0.PrivateKey().GetScriptHash(), 615 Scopes: transaction.CalledByEntry, 616 }, 617 { 618 Account: hash.Hash160(acc1.Contract.Script), 619 Scopes: transaction.Global, 620 }, 621 } 622 tx.Nonce = nonce 623 nonce++ 624 625 tx.Scripts = []transaction.Witness{ 626 {VerificationScript: acc0.GetVerificationScript()}, 627 {VerificationScript: acc1.GetVerificationScript()}, 628 } 629 actualCalculatedNetFee, err := c.CalculateNetworkFee(tx) 630 require.NoError(t, err) 631 tx.NetworkFee = actualCalculatedNetFee + extraFee 632 633 tx.Scripts = nil 634 require.NoError(t, acc0.SignTx(testchain.Network(), tx)) 635 tx.Scripts = append(tx.Scripts, transaction.Witness{ 636 InvocationScript: testchain.Sign(tx), 637 VerificationScript: acc1.Contract.Script, 638 }) 639 cFee, _ := fee.Calculate(chain.GetBaseExecFee(), acc0.Contract.Script) 640 cFeeM, _ := fee.Calculate(chain.GetBaseExecFee(), acc1.Contract.Script) 641 expected := int64(io.GetVarSize(tx))*feePerByte + cFee + cFeeM + extraFee 642 643 require.Equal(t, expected, actualCalculatedNetFee+extraFee) 644 err = chain.VerifyTx(tx) 645 if extraFee < 0 { 646 require.Error(t, err) 647 } else { 648 require.NoError(t, err) 649 } 650 } 651 652 t.Run("with extra fee", func(t *testing.T) { 653 // check that calculated network fee with extra value is enough 654 check(t, extraFee) 655 }) 656 t.Run("without extra fee", func(t *testing.T) { 657 // check that calculated network fee without extra value is enough 658 check(t, 0) 659 }) 660 t.Run("exactFee-1", func(t *testing.T) { 661 // check that we don't add unexpected extra GAS 662 check(t, -1) 663 }) 664 }) 665 t.Run("Contract", func(t *testing.T) { 666 h, err := util.Uint160DecodeStringLE(verifyContractHash) 667 require.NoError(t, err) 668 priv := testchain.PrivateKeyByID(0) 669 acc0 := wallet.NewAccountFromPrivateKey(priv) 670 acc1 := wallet.NewAccountFromPrivateKey(priv) // contract account 671 acc1.Contract.Deployed = true 672 acc1.Contract.Script, err = base64.StdEncoding.DecodeString(verifyContractAVM) 673 require.NoError(t, err) 674 675 newTx := func(t *testing.T) *transaction.Transaction { 676 tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) 677 tx.ValidUntilBlock = chain.BlockHeight() + 10 678 return tx 679 } 680 681 t.Run("Valid", func(t *testing.T) { 682 check := func(t *testing.T, extraFee int64) { 683 tx := newTx(t) 684 tx.Signers = []transaction.Signer{ 685 { 686 Account: acc0.PrivateKey().GetScriptHash(), 687 Scopes: transaction.CalledByEntry, 688 }, 689 { 690 Account: h, 691 Scopes: transaction.Global, 692 }, 693 } 694 // we need to fill standard verification scripts to use CalculateNetworkFee. 695 tx.Scripts = []transaction.Witness{ 696 {VerificationScript: acc0.GetVerificationScript()}, 697 {}, 698 } 699 actual, err := c.CalculateNetworkFee(tx) 700 require.NoError(t, err) 701 tx.NetworkFee = actual + extraFee 702 703 tx.Scripts = nil 704 require.NoError(t, acc0.SignTx(testchain.Network(), tx)) 705 tx.Scripts = append(tx.Scripts, transaction.Witness{}) 706 err = chain.VerifyTx(tx) 707 if extraFee < 0 { 708 require.Error(t, err) 709 } else { 710 require.NoError(t, err) 711 } 712 } 713 714 t.Run("with extra fee", func(t *testing.T) { 715 // check that calculated network fee with extra value is enough 716 check(t, extraFee) 717 }) 718 t.Run("without extra fee", func(t *testing.T) { 719 // check that calculated network fee without extra value is enough 720 check(t, 0) 721 }) 722 t.Run("exactFee-1", func(t *testing.T) { 723 // check that we don't add unexpected extra GAS 724 check(t, -1) 725 }) 726 }) 727 }) 728 } 729 730 func TestCalculateNetworkFee(t *testing.T) { 731 chain, _, httpSrv := initServerWithInMemoryChain(t) 732 const extraFee = 10 733 734 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 735 require.NoError(t, err) 736 t.Cleanup(c.Close) 737 require.NoError(t, c.Init()) 738 739 h, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash) 740 require.NoError(t, err) 741 priv := testchain.PrivateKeyByID(0) 742 acc0 := wallet.NewAccountFromPrivateKey(priv) 743 744 t.Run("ContractWithArgs", func(t *testing.T) { 745 check := func(t *testing.T, extraFee int64) { 746 tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) 747 require.NoError(t, err) 748 tx.ValidUntilBlock = chain.BlockHeight() + 10 749 tx.Signers = []transaction.Signer{ 750 { 751 Account: acc0.PrivateKey().GetScriptHash(), 752 Scopes: transaction.CalledByEntry, 753 }, 754 { 755 Account: h, 756 Scopes: transaction.Global, 757 }, 758 } 759 760 bw := io.NewBufBinWriter() 761 emit.Bool(bw.BinWriter, false) 762 emit.Int(bw.BinWriter, int64(4)) 763 emit.String(bw.BinWriter, "good_string") // contract's `verify` return `true` with this string 764 require.NoError(t, bw.Err) 765 contractInv := bw.Bytes() 766 // we need to fill standard verification scripts to use CalculateNetworkFee. 767 tx.Scripts = []transaction.Witness{ 768 {VerificationScript: acc0.GetVerificationScript()}, 769 {InvocationScript: contractInv}, 770 } 771 tx.NetworkFee, err = c.CalculateNetworkFee(tx) 772 require.NoError(t, err) 773 tx.NetworkFee += extraFee 774 tx.Scripts = nil 775 776 require.NoError(t, acc0.SignTx(testchain.Network(), tx)) 777 tx.Scripts = append(tx.Scripts, transaction.Witness{InvocationScript: contractInv}) 778 err = chain.VerifyTx(tx) 779 if extraFee < 0 { 780 require.Error(t, err) 781 } else { 782 require.NoError(t, err) 783 } 784 } 785 786 t.Run("with extra fee", func(t *testing.T) { 787 // check that calculated network fee with extra value is enough 788 check(t, extraFee) 789 }) 790 t.Run("without extra fee", func(t *testing.T) { 791 // check that calculated network fee without extra value is enough 792 check(t, 0) 793 }) 794 t.Run("exactFee-1", func(t *testing.T) { 795 // check that we don't add unexpected extra GAS 796 check(t, -1) 797 }) 798 }) 799 t.Run("extra attribute fee", func(t *testing.T) { 800 const conflictsFee = 100 801 802 tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) 803 tx.ValidUntilBlock = chain.BlockHeight() + 10 804 signer0 := transaction.Signer{ 805 Account: acc0.ScriptHash(), 806 Scopes: transaction.CalledByEntry, 807 } 808 priv1 := testchain.PrivateKeyByID(1) 809 acc1 := wallet.NewAccountFromPrivateKey(priv1) 810 signer1 := transaction.Signer{ 811 Account: acc1.ScriptHash(), 812 Scopes: transaction.CalledByEntry, 813 } 814 tx.Signers = []transaction.Signer{signer0, signer1} 815 tx.Attributes = []transaction.Attribute{ 816 { 817 Type: transaction.ConflictsT, 818 Value: &transaction.Conflicts{Hash: util.Uint256{1, 2, 3}}, 819 }, 820 } 821 tx.Scripts = []transaction.Witness{ 822 {VerificationScript: acc0.Contract.Script}, 823 {VerificationScript: acc1.Contract.Script}, 824 } 825 oldFee, err := c.CalculateNetworkFee(tx) 826 require.NoError(t, err) 827 828 // Set fee per Conflicts attribute. 829 script, err := smartcontract.CreateCallScript(nativehashes.PolicyContract, "setAttributeFee", byte(transaction.ConflictsT), conflictsFee) 830 require.NoError(t, err) 831 txSetFee := transaction.New(script, 1_0000_0000) 832 txSetFee.ValidUntilBlock = chain.BlockHeight() + 1 833 txSetFee.Signers = []transaction.Signer{ 834 signer0, 835 { 836 Account: testchain.CommitteeScriptHash(), 837 Scopes: transaction.CalledByEntry, 838 }, 839 } 840 txSetFee.NetworkFee = 10_0000_0000 841 require.NoError(t, acc0.SignTx(testchain.Network(), txSetFee)) 842 txSetFee.Scripts = append(txSetFee.Scripts, transaction.Witness{ 843 InvocationScript: testchain.SignCommittee(txSetFee), 844 VerificationScript: testchain.CommitteeVerificationScript(), 845 }) 846 require.NoError(t, chain.AddBlock(testchain.NewBlock(t, chain, 1, 0, txSetFee))) 847 848 // Calculate network fee one more time with updated Conflicts price. 849 newFee, err := c.CalculateNetworkFee(tx) 850 require.NoError(t, err) 851 852 expectedDiff := len(tx.Signers) * len(tx.GetAttributes(transaction.ConflictsT)) * conflictsFee 853 require.Equal(t, int64(expectedDiff), newFee-oldFee) 854 }) 855 } 856 857 func TestNotaryActor(t *testing.T) { 858 _, _, httpSrv := initServerWithInMemoryChainAndServices(t, false, true, false) 859 860 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 861 require.NoError(t, err) 862 t.Cleanup(c.Close) 863 864 sender := testchain.PrivateKeyByID(0) // owner of the deposit in testchain 865 acc := wallet.NewAccountFromPrivateKey(sender) 866 867 comm, err := c.GetCommittee() 868 require.NoError(t, err) 869 870 multiAcc := &wallet.Account{} 871 *multiAcc = *acc 872 require.NoError(t, multiAcc.ConvertMultisig(smartcontract.GetMajorityHonestNodeCount(len(comm)), comm)) 873 874 nact, err := notary.NewActor(c, []actor.SignerAccount{{ 875 Signer: transaction.Signer{ 876 Account: multiAcc.Contract.ScriptHash(), 877 Scopes: transaction.CalledByEntry, 878 }, 879 Account: multiAcc, 880 }}, acc) 881 require.NoError(t, err) 882 neoW := neo.New(nact) 883 _, _, _, err = nact.Notarize(neoW.SetRegisterPriceTransaction(1_0000_0000)) 884 require.NoError(t, err) 885 } 886 887 func TestGetRawNotaryPoolAndTransaction(t *testing.T) { 888 var ( 889 mainHash1, fallbackHash1, mainHash2, fallbackHash2 util.Uint256 890 tx1, tx2 *transaction.Transaction 891 ) 892 893 _, _, httpSrv := initServerWithInMemoryChainAndServices(t, false, true, false) 894 895 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 896 require.NoError(t, err) 897 t.Cleanup(c.Close) 898 t.Run("getrawnotarypool", func(t *testing.T) { 899 t.Run("empty pool", func(t *testing.T) { 900 np, err := c.GetRawNotaryPool() 901 require.NoError(t, err) 902 require.Equal(t, 0, len(np.Hashes)) 903 }) 904 905 sender := testchain.PrivateKeyByID(0) // owner of the deposit in testchain 906 acc := wallet.NewAccountFromPrivateKey(sender) 907 908 comm, err := c.GetCommittee() 909 require.NoError(t, err) 910 911 multiAcc := &wallet.Account{} 912 *multiAcc = *acc 913 require.NoError(t, multiAcc.ConvertMultisig(smartcontract.GetMajorityHonestNodeCount(len(comm)), comm)) 914 915 nact, err := notary.NewActor(c, []actor.SignerAccount{{ 916 Signer: transaction.Signer{ 917 Account: multiAcc.Contract.ScriptHash(), 918 Scopes: transaction.CalledByEntry, 919 }, 920 Account: multiAcc, 921 }}, acc) 922 require.NoError(t, err) 923 neoW := neo.New(nact) 924 // Send the 1st notary request 925 tx1, err = neoW.SetRegisterPriceTransaction(1_0000_0000) 926 require.NoError(t, err) 927 mainHash1, fallbackHash1, _, err = nact.Notarize(tx1, err) 928 require.NoError(t, err) 929 930 checkTxInPool := func(t *testing.T, mainHash, fallbackHash util.Uint256, res *result.RawNotaryPool) { 931 actFallbacks, ok := res.Hashes[mainHash] 932 require.Equal(t, true, ok) 933 require.Equal(t, 1, len(actFallbacks)) 934 require.Equal(t, fallbackHash, actFallbacks[0]) 935 } 936 t.Run("nonempty pool", func(t *testing.T) { 937 actNotaryPool, err := c.GetRawNotaryPool() 938 require.NoError(t, err) 939 require.Equal(t, 1, len(actNotaryPool.Hashes)) 940 checkTxInPool(t, mainHash1, fallbackHash1, actNotaryPool) 941 }) 942 943 // Send the 2nd notary request 944 tx2, err = neoW.SetRegisterPriceTransaction(2_0000_0000) 945 require.NoError(t, err) 946 mainHash2, fallbackHash2, _, err = nact.Notarize(tx2, err) 947 require.NoError(t, err) 948 949 t.Run("pool with 2", func(t *testing.T) { 950 actNotaryPool, err := c.GetRawNotaryPool() 951 require.NoError(t, err) 952 require.Equal(t, 2, len(actNotaryPool.Hashes)) 953 checkTxInPool(t, mainHash1, fallbackHash1, actNotaryPool) 954 checkTxInPool(t, mainHash2, fallbackHash2, actNotaryPool) 955 }) 956 }) 957 t.Run("getrawnotarytransaction", func(t *testing.T) { 958 t.Run("client GetRawNotaryTransaction", func(t *testing.T) { 959 t.Run("unknown transaction", func(t *testing.T) { 960 _, err := c.GetRawNotaryTransaction(util.Uint256{0, 0, 0}) 961 require.Error(t, err) 962 require.ErrorIs(t, err, neorpc.ErrUnknownTransaction) 963 }) 964 _ = tx1.Size() 965 _ = tx2.Size() 966 // RPC server returns empty scripts in transaction.Witness, 967 // thus here the nil-value was changed to empty value. 968 if tx1.Scripts[1].InvocationScript == nil && tx1.Scripts[1].VerificationScript == nil { 969 tx1.Scripts[1] = transaction.Witness{ 970 InvocationScript: []byte{}, 971 VerificationScript: []byte{}, 972 } 973 } 974 if tx2.Scripts[1].InvocationScript == nil && tx2.Scripts[1].VerificationScript == nil { 975 tx2.Scripts[1] = transaction.Witness{ 976 InvocationScript: []byte{}, 977 VerificationScript: []byte{}, 978 } 979 } 980 t.Run("transactions from pool", func(t *testing.T) { 981 mainTx1, err := c.GetRawNotaryTransaction(mainHash1) 982 require.NoError(t, err) 983 require.Equal(t, tx1, mainTx1) 984 _, err = c.GetRawNotaryTransaction(fallbackHash1) 985 require.NoError(t, err) 986 987 mainTx2, err := c.GetRawNotaryTransaction(mainHash2) 988 require.NoError(t, err) 989 require.Equal(t, tx2, mainTx2) 990 _, err = c.GetRawNotaryTransaction(fallbackHash2) 991 require.NoError(t, err) 992 }) 993 }) 994 t.Run("client GetRawNotaryTransactionVerbose", func(t *testing.T) { 995 t.Run("unknown transaction", func(t *testing.T) { 996 _, err := c.GetRawNotaryTransactionVerbose(util.Uint256{0, 0, 0}) 997 require.Error(t, err) 998 require.ErrorIs(t, err, neorpc.ErrUnknownTransaction) 999 }) 1000 t.Run("transactions from pool", func(t *testing.T) { 1001 mainTx1, err := c.GetRawNotaryTransactionVerbose(mainHash1) 1002 require.NoError(t, err) 1003 require.Equal(t, tx1, mainTx1) 1004 _, err = c.GetRawNotaryTransactionVerbose(fallbackHash1) 1005 require.NoError(t, err) 1006 1007 mainTx2, err := c.GetRawNotaryTransactionVerbose(mainHash2) 1008 require.NoError(t, err) 1009 require.Equal(t, tx2, mainTx2) 1010 _, err = c.GetRawNotaryTransactionVerbose(fallbackHash2) 1011 require.NoError(t, err) 1012 }) 1013 }) 1014 }) 1015 } 1016 1017 func TestPing(t *testing.T) { 1018 _, rpcSrv, httpSrv := initServerWithInMemoryChain(t) 1019 1020 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 1021 require.NoError(t, err) 1022 t.Cleanup(c.Close) 1023 require.NoError(t, c.Init()) 1024 1025 require.NoError(t, c.Ping()) 1026 rpcSrv.Shutdown() 1027 httpSrv.Close() 1028 require.Error(t, c.Ping()) 1029 } 1030 1031 func TestCreateNEP17TransferTx(t *testing.T) { 1032 chain, _, httpSrv := initServerWithInMemoryChain(t) 1033 1034 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 1035 require.NoError(t, err) 1036 t.Cleanup(c.Close) 1037 require.NoError(t, c.Init()) 1038 1039 priv := testchain.PrivateKeyByID(0) 1040 acc := wallet.NewAccountFromPrivateKey(priv) 1041 addr := priv.PublicKey().GetScriptHash() 1042 1043 t.Run("default scope", func(t *testing.T) { 1044 act, err := actor.NewSimple(c, acc) 1045 require.NoError(t, err) 1046 gasprom := gas.New(act) 1047 tx, err := gasprom.TransferUnsigned(addr, util.Uint160{}, big.NewInt(1000), nil) 1048 require.NoError(t, err) 1049 require.NoError(t, acc.SignTx(testchain.Network(), tx)) 1050 require.NoError(t, chain.VerifyTx(tx)) 1051 ic, err := chain.GetTestVM(trigger.Application, tx, nil) 1052 require.NoError(t, err) 1053 ic.VM.LoadScriptWithFlags(tx.Script, callflag.All) 1054 require.NoError(t, ic.VM.Run()) 1055 }) 1056 t.Run("default scope, multitransfer", func(t *testing.T) { 1057 act, err := actor.NewSimple(c, acc) 1058 require.NoError(t, err) 1059 gazprom := gas.New(act) 1060 tx, err := gazprom.MultiTransferTransaction([]nep17.TransferParameters{ 1061 {From: addr, To: util.Uint160{3, 2, 1}, Amount: big.NewInt(1000), Data: nil}, 1062 {From: addr, To: util.Uint160{1, 2, 3}, Amount: big.NewInt(1000), Data: nil}, 1063 }) 1064 require.NoError(t, err) 1065 require.NoError(t, chain.VerifyTx(tx)) 1066 ic, err := chain.GetTestVM(trigger.Application, tx, nil) 1067 require.NoError(t, err) 1068 ic.VM.LoadScriptWithFlags(tx.Script, callflag.All) 1069 require.NoError(t, ic.VM.Run()) 1070 require.Equal(t, 2, len(ic.Notifications)) 1071 }) 1072 t.Run("none scope", func(t *testing.T) { 1073 act, err := actor.New(c, []actor.SignerAccount{{ 1074 Signer: transaction.Signer{ 1075 Account: addr, 1076 Scopes: transaction.None, 1077 }, 1078 Account: acc, 1079 }}) 1080 require.NoError(t, err) 1081 gasprom := gas.New(act) 1082 _, err = gasprom.TransferUnsigned(addr, util.Uint160{}, big.NewInt(1000), nil) 1083 require.Error(t, err) 1084 }) 1085 t.Run("customcontracts scope", func(t *testing.T) { 1086 act, err := actor.New(c, []actor.SignerAccount{{ 1087 Signer: transaction.Signer{ 1088 Account: priv.PublicKey().GetScriptHash(), 1089 Scopes: transaction.CustomContracts, 1090 AllowedContracts: []util.Uint160{gas.Hash}, 1091 }, 1092 Account: acc, 1093 }}) 1094 require.NoError(t, err) 1095 gasprom := gas.New(act) 1096 tx, err := gasprom.TransferUnsigned(addr, util.Uint160{}, big.NewInt(1000), nil) 1097 require.NoError(t, err) 1098 require.NoError(t, acc.SignTx(testchain.Network(), tx)) 1099 require.NoError(t, chain.VerifyTx(tx)) 1100 ic, err := chain.GetTestVM(trigger.Application, tx, nil) 1101 require.NoError(t, err) 1102 ic.VM.LoadScriptWithFlags(tx.Script, callflag.All) 1103 require.NoError(t, ic.VM.Run()) 1104 }) 1105 } 1106 1107 func TestInvokeVerify(t *testing.T) { 1108 chain, _, httpSrv := initServerWithInMemoryChain(t) 1109 1110 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 1111 require.NoError(t, err) 1112 t.Cleanup(c.Close) 1113 require.NoError(t, c.Init()) 1114 1115 contract, err := util.Uint160DecodeStringLE(verifyContractHash) 1116 require.NoError(t, err) 1117 1118 t.Run("positive, with signer", func(t *testing.T) { 1119 res, err := c.InvokeContractVerify(contract, []smartcontract.Parameter{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}}) 1120 require.NoError(t, err) 1121 require.Equal(t, "HALT", res.State) 1122 require.Equal(t, 1, len(res.Stack)) 1123 require.True(t, res.Stack[0].Value().(bool)) 1124 }) 1125 1126 t.Run("positive, historic, by height, with signer", func(t *testing.T) { 1127 h := chain.BlockHeight() - 1 1128 res, err := c.InvokeContractVerifyAtHeight(h, contract, []smartcontract.Parameter{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}}) 1129 require.NoError(t, err) 1130 require.Equal(t, "HALT", res.State) 1131 require.Equal(t, 1, len(res.Stack)) 1132 require.True(t, res.Stack[0].Value().(bool)) 1133 }) 1134 1135 t.Run("positive, historic, by block, with signer", func(t *testing.T) { 1136 res, err := c.InvokeContractVerifyWithState(chain.GetHeaderHash(chain.BlockHeight()-1), contract, []smartcontract.Parameter{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}}) 1137 require.NoError(t, err) 1138 require.Equal(t, "HALT", res.State) 1139 require.Equal(t, 1, len(res.Stack)) 1140 require.True(t, res.Stack[0].Value().(bool)) 1141 }) 1142 1143 t.Run("positive, historic, by stateroot, with signer", func(t *testing.T) { 1144 h := chain.BlockHeight() - 1 1145 sr, err := chain.GetStateModule().GetStateRoot(h) 1146 require.NoError(t, err) 1147 res, err := c.InvokeContractVerifyWithState(sr.Root, contract, []smartcontract.Parameter{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}}) 1148 require.NoError(t, err) 1149 require.Equal(t, "HALT", res.State) 1150 require.Equal(t, 1, len(res.Stack)) 1151 require.True(t, res.Stack[0].Value().(bool)) 1152 }) 1153 1154 t.Run("bad, historic, by hash: contract not found", func(t *testing.T) { 1155 var h uint32 = 1 1156 _, err = c.InvokeContractVerifyAtHeight(h, contract, []smartcontract.Parameter{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}}) 1157 require.Error(t, err) 1158 require.True(t, strings.Contains(err.Error(), core.ErrUnknownVerificationContract.Error())) // contract wasn't deployed at block #1 yet 1159 }) 1160 1161 t.Run("bad, historic, by block: contract not found", func(t *testing.T) { 1162 _, err = c.InvokeContractVerifyWithState(chain.GetHeaderHash(1), contract, []smartcontract.Parameter{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}}) 1163 require.Error(t, err) 1164 require.True(t, strings.Contains(err.Error(), core.ErrUnknownVerificationContract.Error())) // contract wasn't deployed at block #1 yet 1165 }) 1166 1167 t.Run("bad, historic, by stateroot: contract not found", func(t *testing.T) { 1168 var h uint32 = 1 1169 sr, err := chain.GetStateModule().GetStateRoot(h) 1170 require.NoError(t, err) 1171 _, err = c.InvokeContractVerifyWithState(sr.Root, contract, []smartcontract.Parameter{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}}) 1172 require.Error(t, err) 1173 require.True(t, strings.Contains(err.Error(), core.ErrUnknownVerificationContract.Error())) // contract wasn't deployed at block #1 yet 1174 }) 1175 1176 t.Run("positive, with signer and witness", func(t *testing.T) { 1177 res, err := c.InvokeContractVerify(contract, []smartcontract.Parameter{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}}, transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH1), byte(opcode.RET)}}) 1178 require.NoError(t, err) 1179 require.Equal(t, "HALT", res.State) 1180 require.Equal(t, 1, len(res.Stack)) 1181 require.True(t, res.Stack[0].Value().(bool)) 1182 }) 1183 1184 t.Run("error, invalid witness number", func(t *testing.T) { 1185 _, err := c.InvokeContractVerify(contract, []smartcontract.Parameter{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}}, transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH1), byte(opcode.RET)}}, transaction.Witness{InvocationScript: []byte{byte(opcode.RET)}}) 1186 require.Error(t, err) 1187 }) 1188 1189 t.Run("false", func(t *testing.T) { 1190 res, err := c.InvokeContractVerify(contract, []smartcontract.Parameter{}, []transaction.Signer{{Account: util.Uint160{}}}) 1191 require.NoError(t, err) 1192 require.Equal(t, "HALT", res.State) 1193 require.Equal(t, 1, len(res.Stack)) 1194 require.False(t, res.Stack[0].Value().(bool)) 1195 }) 1196 } 1197 1198 func TestClient_GetNativeContracts(t *testing.T) { 1199 chain, _, httpSrv := initServerWithInMemoryChain(t) 1200 1201 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 1202 require.NoError(t, err) 1203 t.Cleanup(c.Close) 1204 require.NoError(t, c.Init()) 1205 1206 cs, err := c.GetNativeContracts() 1207 require.NoError(t, err) 1208 require.Equal(t, chain.GetNatives(), cs) 1209 } 1210 1211 func TestClient_NEP11_ND(t *testing.T) { 1212 chain, _, httpSrv := initServerWithInMemoryChain(t) 1213 1214 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 1215 require.NoError(t, err) 1216 t.Cleanup(c.Close) 1217 require.NoError(t, c.Init()) 1218 1219 h, err := util.Uint160DecodeStringLE(nnsContractHash) 1220 require.NoError(t, err) 1221 priv0 := testchain.PrivateKeyByID(0) 1222 act, err := actor.NewSimple(c, wallet.NewAccountFromPrivateKey(priv0)) 1223 require.NoError(t, err) 1224 n11 := nep11.NewNonDivisible(act, h) 1225 acc := priv0.GetScriptHash() 1226 1227 t.Run("Decimals", func(t *testing.T) { 1228 d, err := n11.Decimals() 1229 require.NoError(t, err) 1230 require.EqualValues(t, 0, d) // non-divisible 1231 }) 1232 t.Run("TotalSupply", func(t *testing.T) { 1233 s, err := n11.TotalSupply() 1234 require.NoError(t, err) 1235 require.EqualValues(t, big.NewInt(1), s) // the only `neo.com` of acc0 1236 }) 1237 t.Run("Symbol", func(t *testing.T) { 1238 sym, err := n11.Symbol() 1239 require.NoError(t, err) 1240 require.Equal(t, "NNS", sym) 1241 }) 1242 t.Run("TokenInfo", func(t *testing.T) { 1243 tok, err := neptoken.Info(c, h) 1244 require.NoError(t, err) 1245 require.Equal(t, &wallet.Token{ 1246 Name: "NameService", 1247 Hash: h, 1248 Decimals: 0, 1249 Symbol: "NNS", 1250 Standard: manifest.NEP11StandardName, 1251 }, tok) 1252 }) 1253 t.Run("BalanceOf", func(t *testing.T) { 1254 b, err := n11.BalanceOf(acc) 1255 require.NoError(t, err) 1256 require.EqualValues(t, big.NewInt(1), b) 1257 }) 1258 t.Run("OwnerOf", func(t *testing.T) { 1259 b, err := n11.OwnerOf([]byte("neo.com")) 1260 require.NoError(t, err) 1261 require.EqualValues(t, acc, b) 1262 }) 1263 t.Run("Tokens", func(t *testing.T) { 1264 iter, err := n11.Tokens() 1265 require.NoError(t, err) 1266 items, err := iter.Next(config.DefaultMaxIteratorResultItems) 1267 require.NoError(t, err) 1268 require.Equal(t, 1, len(items)) 1269 require.Equal(t, [][]byte{[]byte("neo.com")}, items) 1270 require.NoError(t, iter.Terminate()) 1271 }) 1272 t.Run("TokensExpanded", func(t *testing.T) { 1273 items, err := n11.TokensExpanded(config.DefaultMaxIteratorResultItems) 1274 require.NoError(t, err) 1275 require.Equal(t, [][]byte{[]byte("neo.com")}, items) 1276 }) 1277 t.Run("Properties", func(t *testing.T) { 1278 p, err := n11.Properties([]byte("neo.com")) 1279 require.NoError(t, err) 1280 blockRegisterDomain, err := chain.GetBlock(chain.GetHeaderHash(14)) // `neo.com` domain was registered in 14th block 1281 require.NoError(t, err) 1282 require.Equal(t, 1, len(blockRegisterDomain.Transactions)) 1283 expected := stackitem.NewMap() 1284 expected.Add(stackitem.Make([]byte("name")), stackitem.Make([]byte("neo.com"))) 1285 expected.Add(stackitem.Make([]byte("expiration")), stackitem.Make(blockRegisterDomain.Timestamp+365*24*3600*1000)) // expiration formula 1286 expected.Add(stackitem.Make([]byte("admin")), stackitem.Null{}) 1287 require.EqualValues(t, expected, p) 1288 }) 1289 t.Run("Transfer", func(t *testing.T) { 1290 _, _, err := n11.Transfer(testchain.PrivateKeyByID(1).GetScriptHash(), []byte("neo.com"), nil) 1291 require.NoError(t, err) 1292 }) 1293 } 1294 1295 func TestClient_NEP11_D(t *testing.T) { 1296 _, _, httpSrv := initServerWithInMemoryChain(t) 1297 1298 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 1299 require.NoError(t, err) 1300 t.Cleanup(c.Close) 1301 require.NoError(t, c.Init()) 1302 1303 pkey0 := testchain.PrivateKeyByID(0) 1304 priv0 := pkey0.GetScriptHash() 1305 priv1 := testchain.PrivateKeyByID(1).GetScriptHash() 1306 token1ID, err := hex.DecodeString(nfsoToken1ID) 1307 require.NoError(t, err) 1308 1309 act, err := actor.NewSimple(c, wallet.NewAccountFromPrivateKey(pkey0)) 1310 require.NoError(t, err) 1311 n11 := nep11.NewDivisible(act, nfsoHash) 1312 1313 t.Run("Decimals", func(t *testing.T) { 1314 d, err := n11.Decimals() 1315 require.NoError(t, err) 1316 require.EqualValues(t, 2, d) // Divisible. 1317 }) 1318 t.Run("TotalSupply", func(t *testing.T) { 1319 s, err := n11.TotalSupply() 1320 require.NoError(t, err) 1321 require.EqualValues(t, big.NewInt(1), s) // the only NFSO of acc0 1322 }) 1323 t.Run("Symbol", func(t *testing.T) { 1324 sym, err := n11.Symbol() 1325 require.NoError(t, err) 1326 require.Equal(t, "NFSO", sym) 1327 }) 1328 t.Run("TokenInfo", func(t *testing.T) { 1329 tok, err := neptoken.Info(c, nfsoHash) 1330 require.NoError(t, err) 1331 require.Equal(t, &wallet.Token{ 1332 Name: "NeoFS Object NFT", 1333 Hash: nfsoHash, 1334 Decimals: 2, 1335 Symbol: "NFSO", 1336 Standard: manifest.NEP11StandardName, 1337 }, tok) 1338 }) 1339 t.Run("BalanceOf", func(t *testing.T) { 1340 b, err := n11.BalanceOf(priv0) 1341 require.NoError(t, err) 1342 require.EqualValues(t, big.NewInt(80), b) 1343 }) 1344 t.Run("BalanceOfD", func(t *testing.T) { 1345 b, err := n11.BalanceOfD(priv0, token1ID) 1346 require.NoError(t, err) 1347 require.EqualValues(t, big.NewInt(80), b) 1348 }) 1349 t.Run("OwnerOf", func(t *testing.T) { 1350 iter, err := n11.OwnerOf(token1ID) 1351 require.NoError(t, err) 1352 items, err := iter.Next(config.DefaultMaxIteratorResultItems) 1353 require.NoError(t, err) 1354 require.Equal(t, 2, len(items)) 1355 require.Equal(t, []util.Uint160{priv1, priv0}, items) 1356 require.NoError(t, iter.Terminate()) 1357 }) 1358 t.Run("OwnerOfExpanded", func(t *testing.T) { 1359 b, err := n11.OwnerOfExpanded(token1ID, config.DefaultMaxIteratorResultItems) 1360 require.NoError(t, err) 1361 require.Equal(t, []util.Uint160{priv1, priv0}, b) 1362 }) 1363 t.Run("Properties", func(t *testing.T) { 1364 p, err := n11.Properties(token1ID) 1365 require.NoError(t, err) 1366 expected := stackitem.NewMap() 1367 expected.Add(stackitem.Make([]byte("name")), stackitem.NewBuffer([]byte("NeoFS Object "+base64.StdEncoding.EncodeToString(token1ID)))) 1368 expected.Add(stackitem.Make([]byte("containerID")), stackitem.Make([]byte(base64.StdEncoding.EncodeToString(nfsoToken1ContainerID.BytesBE())))) 1369 expected.Add(stackitem.Make([]byte("objectID")), stackitem.Make([]byte(base64.StdEncoding.EncodeToString(nfsoToken1ObjectID.BytesBE())))) 1370 require.EqualValues(t, expected, p) 1371 }) 1372 t.Run("Transfer", func(t *testing.T) { 1373 _, _, err := n11.TransferD(priv0, priv1, big.NewInt(20), token1ID, nil) 1374 require.NoError(t, err) 1375 }) 1376 } 1377 1378 func TestClient_NNS(t *testing.T) { 1379 _, _, httpSrv := initServerWithInMemoryChain(t) 1380 1381 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 1382 require.NoError(t, err) 1383 t.Cleanup(c.Close) 1384 require.NoError(t, c.Init()) 1385 nnc := nns.NewReader(invoker.New(c, nil), nnsHash) 1386 1387 t.Run("IsAvailable, false", func(t *testing.T) { 1388 b, err := nnc.IsAvailable("neo.com") 1389 require.NoError(t, err) 1390 require.Equal(t, false, b) 1391 }) 1392 t.Run("IsAvailable, true", func(t *testing.T) { 1393 b, err := nnc.IsAvailable("neogo.com") 1394 require.NoError(t, err) 1395 require.Equal(t, true, b) 1396 }) 1397 t.Run("Resolve, good", func(t *testing.T) { 1398 b, err := nnc.Resolve("neo.com", nns.A) 1399 require.NoError(t, err) 1400 require.Equal(t, "1.2.3.4", b) 1401 }) 1402 t.Run("Resolve, bad", func(t *testing.T) { 1403 _, err := nnc.Resolve("neogo.com", nns.A) 1404 require.Error(t, err) 1405 }) 1406 t.Run("Resolve, CNAME", func(t *testing.T) { 1407 _, err := nnc.Resolve("neogo.com", nns.CNAME) 1408 require.Error(t, err) 1409 }) 1410 t.Run("GetAllRecords, good", func(t *testing.T) { 1411 iter, err := nnc.GetAllRecords("neo.com") 1412 require.NoError(t, err) 1413 arr, err := iter.Next(config.DefaultMaxIteratorResultItems) 1414 require.NoError(t, err) 1415 require.Equal(t, 1, len(arr)) 1416 require.Equal(t, nns.RecordState{ 1417 Name: "neo.com", 1418 Type: nns.A, 1419 Data: "1.2.3.4", 1420 }, arr[0]) 1421 }) 1422 t.Run("GetAllRecordsExpanded, good", func(t *testing.T) { 1423 rss, err := nnc.GetAllRecordsExpanded("neo.com", 42) 1424 require.NoError(t, err) 1425 require.Equal(t, []nns.RecordState{ 1426 { 1427 Name: "neo.com", 1428 Type: nns.A, 1429 Data: "1.2.3.4", 1430 }, 1431 }, rss) 1432 }) 1433 t.Run("GetAllRecords, bad", func(t *testing.T) { 1434 _, err := nnc.GetAllRecords("neopython.com") 1435 require.Error(t, err) 1436 }) 1437 t.Run("GetAllRecordsExpanded, bad", func(t *testing.T) { 1438 _, err := nnc.GetAllRecordsExpanded("neopython.com", 7) 1439 require.Error(t, err) 1440 }) 1441 } 1442 1443 func TestClient_IteratorSessions(t *testing.T) { 1444 _, rpcSrv, httpSrv := initServerWithInMemoryChain(t) 1445 1446 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{MaxConnsPerHost: 50}) 1447 require.NoError(t, err) 1448 t.Cleanup(c.Close) 1449 require.NoError(t, c.Init()) 1450 1451 storageHash, err := util.Uint160DecodeStringLE(storageContractHash) 1452 require.NoError(t, err) 1453 1454 // storageItemsCount is the amount of storage items stored in Storage contract, it's hard-coded in the contract code. 1455 const storageItemsCount = 255 1456 expected := make([][]byte, storageItemsCount) 1457 for i := 0; i < storageItemsCount; i++ { 1458 expected[i] = stackitem.NewBigInteger(big.NewInt(int64(i))).Bytes() 1459 } 1460 sort.Slice(expected, func(i, j int) bool { 1461 if len(expected[i]) != len(expected[j]) { 1462 return len(expected[i]) < len(expected[j]) 1463 } 1464 return bytes.Compare(expected[i], expected[j]) < 0 1465 }) 1466 1467 prepareSession := func(t *testing.T) (uuid.UUID, uuid.UUID) { 1468 res, err := c.InvokeFunction(storageHash, "iterateOverValues", []smartcontract.Parameter{}, nil) 1469 require.NoError(t, err) 1470 require.NotEmpty(t, res.Session) 1471 require.Equal(t, 1, len(res.Stack)) 1472 require.Equal(t, stackitem.InteropT, res.Stack[0].Type()) 1473 iterator, ok := res.Stack[0].Value().(result.Iterator) 1474 require.True(t, ok) 1475 require.NotEmpty(t, iterator.ID) 1476 return res.Session, *iterator.ID 1477 } 1478 t.Run("traverse with max constraint", func(t *testing.T) { 1479 sID, iID := prepareSession(t) 1480 check := func(t *testing.T, start, end int) { 1481 max := end - start 1482 set, err := c.TraverseIterator(sID, iID, max) 1483 require.NoError(t, err) 1484 require.Equal(t, max, len(set)) 1485 for i := 0; i < max; i++ { 1486 // According to the Storage contract code. 1487 require.Equal(t, expected[start+i], set[i].Value().([]byte), start+i) 1488 } 1489 } 1490 check(t, 0, 30) 1491 check(t, 30, 48) 1492 check(t, 48, 49) 1493 check(t, 49, 49+config.DefaultMaxIteratorResultItems) 1494 check(t, 49+config.DefaultMaxIteratorResultItems, 49+2*config.DefaultMaxIteratorResultItems-1) 1495 check(t, 49+2*config.DefaultMaxIteratorResultItems-1, 255) 1496 1497 // Iterator ends on 255-th element, so no more elements should be returned. 1498 set, err := c.TraverseIterator(sID, iID, config.DefaultMaxIteratorResultItems) 1499 require.NoError(t, err) 1500 require.Equal(t, 0, len(set)) 1501 }) 1502 1503 t.Run("traverse, request more than exists", func(t *testing.T) { 1504 sID, iID := prepareSession(t) 1505 for i := 0; i < storageItemsCount/config.DefaultMaxIteratorResultItems; i++ { 1506 set, err := c.TraverseIterator(sID, iID, config.DefaultMaxIteratorResultItems) 1507 require.NoError(t, err) 1508 require.Equal(t, config.DefaultMaxIteratorResultItems, len(set)) 1509 } 1510 1511 // Request more items than left untraversed. 1512 set, err := c.TraverseIterator(sID, iID, config.DefaultMaxIteratorResultItems) 1513 require.NoError(t, err) 1514 require.Equal(t, storageItemsCount%config.DefaultMaxIteratorResultItems, len(set)) 1515 }) 1516 1517 t.Run("traverse, no max constraint", func(t *testing.T) { 1518 sID, iID := prepareSession(t) 1519 1520 set, err := c.TraverseIterator(sID, iID, -1) 1521 require.NoError(t, err) 1522 require.Equal(t, config.DefaultMaxIteratorResultItems, len(set)) 1523 }) 1524 1525 t.Run("traverse, concurrent access", func(t *testing.T) { 1526 sID, iID := prepareSession(t) 1527 wg := sync.WaitGroup{} 1528 wg.Add(storageItemsCount) 1529 check := func(t *testing.T) { 1530 set, err := c.TraverseIterator(sID, iID, 1) 1531 assert.NoError(t, err) 1532 assert.Equal(t, 1, len(set)) 1533 wg.Done() 1534 } 1535 for i := 0; i < storageItemsCount; i++ { 1536 go check(t) 1537 } 1538 wg.Wait() 1539 }) 1540 1541 t.Run("terminate session", func(t *testing.T) { 1542 t.Run("manually", func(t *testing.T) { 1543 sID, iID := prepareSession(t) 1544 1545 // Check session is created. 1546 set, err := c.TraverseIterator(sID, iID, 1) 1547 require.NoError(t, err) 1548 require.Equal(t, 1, len(set)) 1549 1550 ok, err := c.TerminateSession(sID) 1551 require.NoError(t, err) 1552 require.True(t, ok) 1553 1554 ok, err = c.TerminateSession(sID) 1555 require.Error(t, err) 1556 require.ErrorIs(t, err, neorpc.ErrUnknownSession) 1557 require.False(t, ok) // session has already been terminated. 1558 }) 1559 t.Run("automatically", func(t *testing.T) { 1560 sID, iID := prepareSession(t) 1561 1562 // Check session is created. 1563 set, err := c.TraverseIterator(sID, iID, 1) 1564 require.NoError(t, err) 1565 require.Equal(t, 1, len(set)) 1566 1567 require.Eventually(t, func() bool { 1568 rpcSrv.sessionsLock.Lock() 1569 defer rpcSrv.sessionsLock.Unlock() 1570 1571 _, ok := rpcSrv.sessions[sID.String()] 1572 return !ok 1573 }, time.Duration(rpcSrv.config.SessionExpirationTime)*time.Second*3, 1574 // Sessions list is updated once per SessionExpirationTime, thus, no need to ask for update more frequently than 1575 // sessions cleaning occurs. 1576 time.Duration(rpcSrv.config.SessionExpirationTime)*time.Second/4) 1577 1578 ok, err := c.TerminateSession(sID) 1579 require.Error(t, err) 1580 require.ErrorIs(t, err, neorpc.ErrUnknownSession) 1581 require.False(t, ok) // session has already been terminated. 1582 }) 1583 }) 1584 } 1585 1586 func TestClient_States(t *testing.T) { 1587 chain, _, httpSrv := initServerWithInMemoryChain(t) 1588 1589 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 1590 require.NoError(t, err) 1591 t.Cleanup(c.Close) 1592 require.NoError(t, c.Init()) 1593 1594 stateheight, err := c.GetStateHeight() 1595 assert.NoError(t, err) 1596 assert.Equal(t, chain.BlockHeight(), stateheight.Local) 1597 1598 stateroot, err := c.GetStateRootByHeight(stateheight.Local) 1599 assert.NoError(t, err) 1600 1601 t.Run("proof", func(t *testing.T) { 1602 policy, err := chain.GetNativeContractScriptHash(nativenames.Policy) 1603 assert.NoError(t, err) 1604 proof, err := c.GetProof(stateroot.Root, policy, []byte{19}) // storagePrice key in policy contract 1605 assert.NoError(t, err) 1606 value, err := c.VerifyProof(stateroot.Root, proof) 1607 assert.NoError(t, err) 1608 assert.Equal(t, big.NewInt(native.DefaultStoragePrice), bigint.FromBytes(value)) 1609 }) 1610 } 1611 1612 func TestClientOracle(t *testing.T) { 1613 chain, _, httpSrv := initServerWithInMemoryChain(t) 1614 1615 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 1616 require.NoError(t, err) 1617 t.Cleanup(c.Close) 1618 require.NoError(t, c.Init()) 1619 1620 oraRe := oracle.NewReader(invoker.New(c, nil)) 1621 1622 var defaultOracleRequestPrice = big.NewInt(5000_0000) 1623 actual, err := oraRe.GetPrice() 1624 require.NoError(t, err) 1625 require.Equal(t, defaultOracleRequestPrice, actual) 1626 1627 act, err := actor.New(c, []actor.SignerAccount{{ 1628 Signer: transaction.Signer{ 1629 Account: testchain.CommitteeScriptHash(), 1630 Scopes: transaction.CalledByEntry, 1631 }, 1632 Account: &wallet.Account{ 1633 Address: testchain.CommitteeAddress(), 1634 Contract: &wallet.Contract{ 1635 Script: testchain.CommitteeVerificationScript(), 1636 }, 1637 }, 1638 }}) 1639 require.NoError(t, err) 1640 1641 ora := oracle.New(act) 1642 1643 newPrice := big.NewInt(1_0000_0000) 1644 tx, err := ora.SetPriceUnsigned(newPrice) 1645 require.NoError(t, err) 1646 1647 tx.Scripts[0].InvocationScript = testchain.SignCommittee(tx) 1648 bl := testchain.NewBlock(t, chain, 1, 0, tx) 1649 _, err = c.SubmitBlock(*bl) 1650 require.NoError(t, err) 1651 1652 actual, err = ora.GetPrice() 1653 require.NoError(t, err) 1654 require.Equal(t, newPrice, actual) 1655 } 1656 1657 func TestClient_Iterator_SessionConfigVariations(t *testing.T) { 1658 var expected [][]byte 1659 storageHash, err := util.Uint160DecodeStringLE(storageContractHash) 1660 require.NoError(t, err) 1661 // storageItemsCount is the amount of storage items stored in Storage contract, it's hard-coded in the contract code. 1662 const storageItemsCount = 255 1663 1664 checkSessionEnabled := func(t *testing.T, c *rpcclient.Client) { 1665 // We expect Iterator with designated ID to be presented on stack. It should be possible to retrieve its values via `traverseiterator` call. 1666 res, err := c.InvokeFunction(storageHash, "iterateOverValues", []smartcontract.Parameter{}, nil) 1667 require.NoError(t, err) 1668 require.NotEmpty(t, res.Session) 1669 require.Equal(t, 1, len(res.Stack)) 1670 require.Equal(t, stackitem.InteropT, res.Stack[0].Type()) 1671 iterator, ok := res.Stack[0].Value().(result.Iterator) 1672 require.True(t, ok) 1673 require.NotEmpty(t, iterator.ID) 1674 require.Empty(t, iterator.Values) 1675 max := 84 1676 actual, err := c.TraverseIterator(res.Session, *iterator.ID, max) 1677 require.NoError(t, err) 1678 require.Equal(t, max, len(actual)) 1679 for i := 0; i < max; i++ { 1680 // According to the Storage contract code. 1681 require.Equal(t, expected[i], actual[i].Value().([]byte), i) 1682 } 1683 } 1684 t.Run("default sessions enabled", func(t *testing.T) { 1685 chain, _, httpSrv := initClearServerWithServices(t, false, false, false) 1686 1687 for _, b := range getTestBlocks(t) { 1688 require.NoError(t, chain.AddBlock(b)) 1689 } 1690 1691 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 1692 require.NoError(t, err) 1693 t.Cleanup(c.Close) 1694 require.NoError(t, c.Init()) 1695 1696 // Fill in expected stackitems set during the first test. 1697 expected = make([][]byte, storageItemsCount) 1698 for i := 0; i < storageItemsCount; i++ { 1699 expected[i] = stackitem.NewBigInteger(big.NewInt(int64(i))).Bytes() 1700 } 1701 sort.Slice(expected, func(i, j int) bool { 1702 if len(expected[i]) != len(expected[j]) { 1703 return len(expected[i]) < len(expected[j]) 1704 } 1705 return bytes.Compare(expected[i], expected[j]) < 0 1706 }) 1707 checkSessionEnabled(t, c) 1708 }) 1709 t.Run("MPT-based sessions enables", func(t *testing.T) { 1710 // Prepare MPT-enabled RPC server. 1711 chain, orc, cfg, logger := getUnitTestChainWithCustomConfig(t, false, false, func(cfg *config.Config) { 1712 cfg.ApplicationConfiguration.RPC.SessionEnabled = true 1713 cfg.ApplicationConfiguration.RPC.SessionBackedByMPT = true 1714 }) 1715 serverConfig, err := network.NewServerConfig(cfg) 1716 require.NoError(t, err) 1717 serverConfig.UserAgent = fmt.Sprintf(config.UserAgentFormat, "0.98.6-test") 1718 serverConfig.Addresses = []config.AnnounceableAddress{{Address: ":0"}} 1719 server, err := network.NewServer(serverConfig, chain, chain.GetStateSyncModule(), logger) 1720 require.NoError(t, err) 1721 errCh := make(chan error, 2) 1722 rpcSrv := New(chain, cfg.ApplicationConfiguration.RPC, server, orc, logger, errCh) 1723 rpcSrv.Start() 1724 handler := http.HandlerFunc(rpcSrv.handleHTTPRequest) 1725 httpSrv := httptest.NewServer(handler) 1726 t.Cleanup(httpSrv.Close) 1727 defer rpcSrv.Shutdown() 1728 for _, b := range getTestBlocks(t) { 1729 require.NoError(t, chain.AddBlock(b)) 1730 } 1731 1732 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 1733 require.NoError(t, err) 1734 t.Cleanup(c.Close) 1735 require.NoError(t, c.Init()) 1736 1737 checkSessionEnabled(t, c) 1738 }) 1739 t.Run("sessions disabled", func(t *testing.T) { 1740 chain, rpcSrv, httpSrv := initClearServerWithServices(t, false, false, true) 1741 1742 for _, b := range getTestBlocks(t) { 1743 require.NoError(t, chain.AddBlock(b)) 1744 } 1745 1746 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 1747 require.NoError(t, err) 1748 t.Cleanup(c.Close) 1749 require.NoError(t, c.Init()) 1750 1751 // We expect unpacked iterator values to be present on stack under InteropInterface cover. 1752 res, err := c.InvokeFunction(storageHash, "iterateOverValues", []smartcontract.Parameter{}, nil) 1753 require.NoError(t, err) 1754 require.Empty(t, res.Session) 1755 require.Equal(t, 1, len(res.Stack)) 1756 require.Equal(t, stackitem.InteropT, res.Stack[0].Type()) 1757 iterator, ok := res.Stack[0].Value().(result.Iterator) 1758 require.True(t, ok) 1759 require.Empty(t, iterator.ID) 1760 require.NotEmpty(t, iterator.Values) 1761 require.True(t, iterator.Truncated) 1762 require.Equal(t, rpcSrv.config.MaxIteratorResultItems, len(iterator.Values)) 1763 for i := 0; i < rpcSrv.config.MaxIteratorResultItems; i++ { 1764 // According to the Storage contract code. 1765 require.Equal(t, expected[i], iterator.Values[i].Value().([]byte), i) 1766 } 1767 }) 1768 } 1769 1770 func TestClient_Wait(t *testing.T) { 1771 chain, _, httpSrv := initServerWithInMemoryChain(t) 1772 1773 run := func(t *testing.T, ws bool) { 1774 acc, err := wallet.NewAccount() 1775 require.NoError(t, err) 1776 1777 var act *actor.Actor 1778 if ws { 1779 c, err := rpcclient.NewWS(context.Background(), "ws"+strings.TrimPrefix(httpSrv.URL, "http")+"/ws", rpcclient.WSOptions{}) 1780 require.NoError(t, err) 1781 t.Cleanup(c.Close) 1782 require.NoError(t, c.Init()) 1783 act, err = actor.New(c, []actor.SignerAccount{ 1784 { 1785 Signer: transaction.Signer{ 1786 Account: acc.ScriptHash(), 1787 }, 1788 Account: acc, 1789 }, 1790 }) 1791 require.NoError(t, err) 1792 } else { 1793 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 1794 require.NoError(t, err) 1795 t.Cleanup(c.Close) 1796 require.NoError(t, c.Init()) 1797 act, err = actor.New(c, []actor.SignerAccount{ 1798 { 1799 Signer: transaction.Signer{ 1800 Account: acc.ScriptHash(), 1801 }, 1802 Account: acc, 1803 }, 1804 }) 1805 require.NoError(t, err) 1806 } 1807 1808 b, err := chain.GetBlock(chain.GetHeaderHash(1)) 1809 require.NoError(t, err) 1810 require.True(t, len(b.Transactions) > 0) 1811 1812 check := func(t *testing.T, h util.Uint256, vub uint32, errExpected bool) { 1813 rcvr := make(chan struct{}) 1814 go func() { 1815 aer, err := act.Wait(h, vub, nil) 1816 if errExpected { 1817 require.Error(t, err) 1818 } else { 1819 require.NoError(t, err) 1820 require.Equal(t, h, aer.Container) 1821 } 1822 rcvr <- struct{}{} 1823 }() 1824 waitloop: 1825 for { 1826 select { 1827 case <-rcvr: 1828 break waitloop 1829 case <-time.NewTimer(chain.GetConfig().TimePerBlock).C: 1830 t.Fatal("transaction failed to be awaited") 1831 } 1832 } 1833 } 1834 1835 // Wait for transaction that has been persisted and VUB block has been persisted. 1836 check(t, b.Transactions[0].Hash(), chain.BlockHeight()-1, false) 1837 // Wait for transaction that has been persisted and VUB block hasn't yet been persisted. 1838 check(t, b.Transactions[0].Hash(), chain.BlockHeight()+1, false) 1839 if !ws { 1840 // Wait for transaction that hasn't been persisted and VUB block has been persisted. 1841 // WS client waits for the next block to be accepted to ensure that transaction wasn't 1842 // persisted, and this test doesn't run chain, thus, don't run this test for WS client. 1843 check(t, util.Uint256{1, 2, 3}, chain.BlockHeight()-1, true) 1844 } 1845 } 1846 1847 t.Run("client", func(t *testing.T) { 1848 run(t, false) 1849 }) 1850 t.Run("ws client", func(t *testing.T) { 1851 run(t, true) 1852 }) 1853 } 1854 1855 func mkSubsClient(t *testing.T, rpcSrv *Server, httpSrv *httptest.Server, local bool) *rpcclient.WSClient { 1856 var ( 1857 c *rpcclient.WSClient 1858 err error 1859 icl *rpcclient.Internal 1860 ) 1861 if local { 1862 icl, err = rpcclient.NewInternal(context.Background(), rpcSrv.RegisterLocal) 1863 } else { 1864 url := "ws" + strings.TrimPrefix(httpSrv.URL, "http") + "/ws" 1865 c, err = rpcclient.NewWS(context.Background(), url, rpcclient.WSOptions{}) 1866 t.Cleanup(c.Close) 1867 } 1868 require.NoError(t, err) 1869 if local { 1870 c = &icl.WSClient 1871 } 1872 require.NoError(t, c.Init()) 1873 return c 1874 } 1875 1876 func runWSAndLocal(t *testing.T, test func(*testing.T, bool)) { 1877 t.Run("ws", func(t *testing.T) { 1878 test(t, false) 1879 }) 1880 t.Run("local", func(t *testing.T) { 1881 test(t, true) 1882 }) 1883 } 1884 1885 func TestSubClientWait(t *testing.T) { 1886 runWSAndLocal(t, testSubClientWait) 1887 } 1888 1889 func testSubClientWait(t *testing.T, local bool) { 1890 chain, rpcSrv, httpSrv := initClearServerWithServices(t, false, false, true) 1891 1892 c := mkSubsClient(t, rpcSrv, httpSrv, local) 1893 acc, err := wallet.NewAccount() 1894 require.NoError(t, err) 1895 act, err := actor.New(c, []actor.SignerAccount{ 1896 { 1897 Signer: transaction.Signer{ 1898 Account: acc.ScriptHash(), 1899 }, 1900 Account: acc, 1901 }, 1902 }) 1903 require.NoError(t, err) 1904 1905 rcvr := make(chan *state.AppExecResult) 1906 check := func(t *testing.T, b *block.Block, h util.Uint256, vub uint32) { 1907 go func() { 1908 aer, err := act.Wait(h, vub, nil) 1909 require.NoError(t, err, b.Index) 1910 rcvr <- aer 1911 }() 1912 go func() { 1913 // Wait until client is properly subscribed. The real node won't behave like this, 1914 // but the real node has the subsequent blocks to be added that will trigger client's 1915 // waitloops to finish anyway (and the test has only single block, thus, use it careful). 1916 require.Eventually(t, func() bool { 1917 rpcSrv.subsLock.Lock() 1918 defer rpcSrv.subsLock.Unlock() 1919 if len(rpcSrv.subscribers) == 1 { // single client 1920 for s := range rpcSrv.subscribers { 1921 var count int 1922 for _, f := range s.feeds { 1923 if f.event != neorpc.InvalidEventID { 1924 count++ 1925 } 1926 } 1927 return count == 2 // subscription for blocks + AERs 1928 } 1929 } 1930 return false 1931 }, time.Second, 100*time.Millisecond) 1932 require.NoError(t, chain.AddBlock(b)) 1933 }() 1934 waitloop: 1935 for { 1936 select { 1937 case aer := <-rcvr: 1938 require.Equal(t, h, aer.Container) 1939 require.Equal(t, trigger.Application, aer.Trigger) 1940 if h.StringLE() == faultedTxHashLE { 1941 require.Equal(t, vmstate.Fault, aer.VMState) 1942 } else { 1943 require.Equal(t, vmstate.Halt, aer.VMState) 1944 } 1945 break waitloop 1946 case <-time.NewTimer(chain.GetConfig().TimePerBlock).C: 1947 t.Fatalf("transaction from block %d failed to be awaited: deadline exceeded", b.Index) 1948 } 1949 } 1950 // Wait for server/client to properly unsubscribe. In real life subsequent awaiter 1951 // requests may be run concurrently, and it's OK, but it's important for the test 1952 // not to run subscription requests in parallel because block addition is bounded to 1953 // the number of subscribers. 1954 require.Eventually(t, func() bool { 1955 rpcSrv.subsLock.Lock() 1956 defer rpcSrv.subsLock.Unlock() 1957 if len(rpcSrv.subscribers) != 1 { 1958 return false 1959 } 1960 for s := range rpcSrv.subscribers { 1961 for _, f := range s.feeds { 1962 if f.event != neorpc.InvalidEventID { 1963 return false 1964 } 1965 } 1966 } 1967 return true 1968 }, time.Second, 100*time.Millisecond) 1969 } 1970 1971 var faultedChecked bool 1972 for _, b := range getTestBlocks(t) { 1973 if len(b.Transactions) > 0 { 1974 tx := b.Transactions[0] 1975 check(t, b, tx.Hash(), tx.ValidUntilBlock) 1976 if tx.Hash().StringLE() == faultedTxHashLE { 1977 faultedChecked = true 1978 } 1979 } else { 1980 require.NoError(t, chain.AddBlock(b)) 1981 } 1982 } 1983 require.True(t, faultedChecked, "FAULTed transaction wasn't checked") 1984 } 1985 1986 func TestSubClientWaitWithLateSubscription(t *testing.T) { 1987 runWSAndLocal(t, testSubClientWaitWithLateSubscription) 1988 } 1989 1990 func testSubClientWaitWithLateSubscription(t *testing.T, local bool) { 1991 chain, rpcSrv, httpSrv := initClearServerWithServices(t, false, false, true) 1992 1993 c := mkSubsClient(t, rpcSrv, httpSrv, local) 1994 acc, err := wallet.NewAccount() 1995 require.NoError(t, err) 1996 act, err := actor.New(c, []actor.SignerAccount{ 1997 { 1998 Signer: transaction.Signer{ 1999 Account: acc.ScriptHash(), 2000 }, 2001 Account: acc, 2002 }, 2003 }) 2004 require.NoError(t, err) 2005 2006 // Firstly, accept the block. 2007 blocks := getTestBlocks(t) 2008 b1 := blocks[0] 2009 tx := b1.Transactions[0] 2010 require.NoError(t, chain.AddBlock(b1)) 2011 2012 // After that, wait and get the result immediately. 2013 aer, err := act.Wait(tx.Hash(), tx.ValidUntilBlock, nil) 2014 require.NoError(t, err) 2015 require.Equal(t, tx.Hash(), aer.Container) 2016 require.Equal(t, trigger.Application, aer.Trigger) 2017 require.Equal(t, vmstate.Halt, aer.VMState) 2018 } 2019 2020 func TestWSClientHandshakeError(t *testing.T) { 2021 _, _, httpSrv := initClearServerWithCustomConfig(t, func(cfg *config.Config) { 2022 cfg.ApplicationConfiguration.RPC.MaxWebSocketClients = -1 2023 }) 2024 2025 url := "ws" + strings.TrimPrefix(httpSrv.URL, "http") + "/ws" 2026 _, err := rpcclient.NewWS(context.Background(), url, rpcclient.WSOptions{}) 2027 require.ErrorContains(t, err, "websocket users limit reached") 2028 } 2029 2030 func TestSubClientWaitWithMissedEvent(t *testing.T) { 2031 runWSAndLocal(t, testSubClientWaitWithMissedEvent) 2032 } 2033 2034 func testSubClientWaitWithMissedEvent(t *testing.T, local bool) { 2035 chain, rpcSrv, httpSrv := initClearServerWithServices(t, false, false, true) 2036 2037 c := mkSubsClient(t, rpcSrv, httpSrv, local) 2038 acc, err := wallet.NewAccount() 2039 require.NoError(t, err) 2040 act, err := actor.New(c, []actor.SignerAccount{ 2041 { 2042 Signer: transaction.Signer{ 2043 Account: acc.ScriptHash(), 2044 }, 2045 Account: acc, 2046 }, 2047 }) 2048 require.NoError(t, err) 2049 2050 blocks := getTestBlocks(t) 2051 b1 := blocks[0] 2052 tx := b1.Transactions[0] 2053 2054 rcvr := make(chan *state.AppExecResult) 2055 errCh := make(chan error) // Error channel for goroutine errors 2056 2057 go func() { 2058 aer, err := act.Wait(tx.Hash(), tx.ValidUntilBlock, nil) 2059 if err != nil { 2060 errCh <- err 2061 return 2062 } 2063 rcvr <- aer 2064 }() 2065 2066 // Wait until client is properly subscribed. The real node won't behave like this, 2067 // but the real node has the subsequent blocks to be added that will trigger client's 2068 // waitloops to finish anyway (and the test has only single block, thus, use it careful). 2069 require.Eventually(t, func() bool { 2070 rpcSrv.subsLock.Lock() 2071 defer rpcSrv.subsLock.Unlock() 2072 return len(rpcSrv.subscribers) == 1 2073 }, 2*time.Second, 100*time.Millisecond) 2074 2075 rpcSrv.subsLock.Lock() 2076 // Suppress normal event delivery. 2077 for s := range rpcSrv.subscribers { 2078 s.overflown.Store(true) 2079 } 2080 rpcSrv.subsLock.Unlock() 2081 2082 // Accept the next block, but subscriber will get no events because it's overflown. 2083 require.NoError(t, chain.AddBlock(b1)) 2084 2085 overNotification := neorpc.Notification{ 2086 JSONRPC: neorpc.JSONRPCVersion, 2087 Event: neorpc.MissedEventID, 2088 Payload: make([]any, 0), 2089 } 2090 overEvent, err := json.Marshal(overNotification) 2091 require.NoError(t, err) 2092 overflowMsg, err := websocket.NewPreparedMessage(websocket.TextMessage, overEvent) 2093 require.NoError(t, err) 2094 rpcSrv.subsLock.Lock() 2095 // Deliver overflow message -> triggers subscriber to retry with polling waiter. 2096 for s := range rpcSrv.subscribers { 2097 s.writer <- intEvent{overflowMsg, &overNotification} 2098 } 2099 rpcSrv.subsLock.Unlock() 2100 2101 // Wait for the result. 2102 waitloop: 2103 for { 2104 select { 2105 case aer := <-rcvr: 2106 require.Equal(t, tx.Hash(), aer.Container) 2107 require.Equal(t, trigger.Application, aer.Trigger) 2108 require.Equal(t, vmstate.Halt, aer.VMState) 2109 break waitloop 2110 case err := <-errCh: 2111 t.Fatalf("Error waiting for transaction: %v", err) 2112 case <-time.NewTimer(chain.GetConfig().TimePerBlock).C: 2113 t.Fatal("transaction failed to be awaited") 2114 } 2115 } 2116 } 2117 2118 // TestWSClient_SubscriptionsCompat is aimed to test both deprecated and relevant 2119 // subscriptions API with filtered and non-filtered subscriptions from the WSClient 2120 // user side. 2121 func TestWSClient_SubscriptionsCompat(t *testing.T) { 2122 chain, rpcSrv, httpSrv := initClearServerWithServices(t, false, false, true) 2123 2124 c := mkSubsClient(t, rpcSrv, httpSrv, false) 2125 blocks := getTestBlocks(t) 2126 bCount := uint32(0) 2127 2128 getData := func(t *testing.T) (*block.Block, *block.Block, byte, util.Uint160, string, string) { 2129 b1 := blocks[bCount] 2130 primary := b1.PrimaryIndex 2131 tx := b1.Transactions[0] 2132 sender := tx.Sender() 2133 ntfName := "Transfer" 2134 st := vmstate.Halt.String() 2135 b2 := blocks[bCount+1] 2136 bCount += 2 2137 return b1, b2, primary, sender, ntfName, st 2138 } 2139 2140 checkRelevant := func(t *testing.T, filtered bool) { 2141 b, bNext, primary, sender, ntfName, st := getData(t) 2142 var ( 2143 bID, txID, ntfID, aerID string 2144 blockCh = make(chan *block.Block) 2145 txCh = make(chan *transaction.Transaction) 2146 ntfCh = make(chan *state.ContainedNotificationEvent) 2147 aerCh = make(chan *state.AppExecResult) 2148 bFlt *neorpc.BlockFilter 2149 txFlt *neorpc.TxFilter 2150 ntfFlt *neorpc.NotificationFilter 2151 aerFlt *neorpc.ExecutionFilter 2152 err error 2153 ) 2154 if filtered { 2155 bFlt = &neorpc.BlockFilter{Primary: &primary} 2156 txFlt = &neorpc.TxFilter{Sender: &sender} 2157 ntfFlt = &neorpc.NotificationFilter{Name: &ntfName} 2158 aerFlt = &neorpc.ExecutionFilter{State: &st} 2159 } 2160 bID, err = c.ReceiveBlocks(bFlt, blockCh) 2161 require.NoError(t, err) 2162 txID, err = c.ReceiveTransactions(txFlt, txCh) 2163 require.NoError(t, err) 2164 ntfID, err = c.ReceiveExecutionNotifications(ntfFlt, ntfCh) 2165 require.NoError(t, err) 2166 aerID, err = c.ReceiveExecutions(aerFlt, aerCh) 2167 require.NoError(t, err) 2168 2169 var ( 2170 lock sync.RWMutex 2171 received byte 2172 exitCh = make(chan struct{}) 2173 ) 2174 go func() { 2175 dispatcher: 2176 for { 2177 select { 2178 case <-blockCh: 2179 lock.Lock() 2180 received |= 1 2181 lock.Unlock() 2182 case <-txCh: 2183 lock.Lock() 2184 received |= 1 << 1 2185 lock.Unlock() 2186 case <-ntfCh: 2187 lock.Lock() 2188 received |= 1 << 2 2189 lock.Unlock() 2190 case <-aerCh: 2191 lock.Lock() 2192 received |= 1 << 3 2193 lock.Unlock() 2194 case <-exitCh: 2195 break dispatcher 2196 } 2197 } 2198 drainLoop: 2199 for { 2200 select { 2201 case <-blockCh: 2202 case <-txCh: 2203 case <-ntfCh: 2204 case <-aerCh: 2205 default: 2206 break drainLoop 2207 } 2208 } 2209 close(blockCh) 2210 close(txCh) 2211 close(ntfCh) 2212 close(aerCh) 2213 }() 2214 2215 // Accept the next block and wait for events. 2216 require.NoError(t, chain.AddBlock(b)) 2217 // Blockchain's events channel is not buffered, and thus, by adding one more extra block 2218 // we're ensuring that the previous block event receiving was successfully handled by Blockchain's 2219 // notificationDispatcher loop. Once we're sure in that, we may start to check the actual notifications. 2220 require.NoError(t, chain.AddBlock(bNext)) 2221 assert.Eventually(t, func() bool { 2222 lock.RLock() 2223 defer lock.RUnlock() 2224 2225 return received == 1<<4-1 2226 }, time.Second, 100*time.Millisecond) 2227 2228 require.NoError(t, c.Unsubscribe(bID)) 2229 require.NoError(t, c.Unsubscribe(txID)) 2230 require.NoError(t, c.Unsubscribe(ntfID)) 2231 require.NoError(t, c.Unsubscribe(aerID)) 2232 exitCh <- struct{}{} 2233 } 2234 t.Run("relevant, filtered", func(t *testing.T) { 2235 checkRelevant(t, true) 2236 }) 2237 t.Run("relevant, non-filtered", func(t *testing.T) { 2238 checkRelevant(t, false) 2239 }) 2240 } 2241 2242 func TestActor_CallWithNilParam(t *testing.T) { 2243 chain, _, httpSrv := initServerWithInMemoryChain(t) 2244 2245 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 2246 require.NoError(t, err) 2247 t.Cleanup(c.Close) 2248 acc, err := wallet.NewAccount() 2249 require.NoError(t, err) 2250 act, err := actor.New(c, []actor.SignerAccount{ 2251 { 2252 Signer: transaction.Signer{ 2253 Account: acc.ScriptHash(), 2254 }, 2255 Account: acc, 2256 }, 2257 }) 2258 require.NoError(t, err) 2259 2260 rubles, err := chain.GetContractScriptHash(basicchain.RublesContractID) 2261 require.NoError(t, err) 2262 2263 // We don't have a suitable contract, thus use Rubles with simple put method, 2264 // it should fail at the moment of conversion Null value to ByteString (not earlier, 2265 // and that's the point of the test!). 2266 res, err := act.Call(rubles, "putValue", "123", (*util.Uint160)(nil)) 2267 require.NoError(t, err) 2268 2269 require.True(t, strings.Contains(res.FaultException, "invalid conversion: Null/ByteString"), res.FaultException) 2270 } 2271 2272 func TestClient_FindStorage(t *testing.T) { 2273 _, _, httpSrv := initServerWithInMemoryChain(t) 2274 2275 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 2276 require.NoError(t, err) 2277 t.Cleanup(c.Close) 2278 require.NoError(t, c.Init()) 2279 2280 h, err := util.Uint160DecodeStringLE(testContractHash) 2281 require.NoError(t, err) 2282 prefix := []byte("aa") 2283 expected := result.FindStorage{ 2284 Results: []result.KeyValue{ 2285 { 2286 Key: []byte("aa"), 2287 Value: []byte("v1"), 2288 }, 2289 { 2290 Key: []byte("aa10"), 2291 Value: []byte("v2"), 2292 }, 2293 }, 2294 Next: 2, 2295 Truncated: true, 2296 } 2297 2298 // By hash. 2299 actual, err := c.FindStorageByHash(h, prefix, nil) 2300 require.NoError(t, err) 2301 require.Equal(t, expected, actual) 2302 2303 // By ID. 2304 actual, err = c.FindStorageByID(1, prefix, nil) // Rubles contract 2305 require.NoError(t, err) 2306 require.Equal(t, expected, actual) 2307 2308 // Non-nil start. 2309 start := 1 2310 actual, err = c.FindStorageByHash(h, prefix, &start) 2311 require.NoError(t, err) 2312 require.Equal(t, result.FindStorage{ 2313 Results: []result.KeyValue{ 2314 { 2315 Key: []byte("aa10"), 2316 Value: []byte("v2"), 2317 }, 2318 { 2319 Key: []byte("aa50"), 2320 Value: []byte("v3"), 2321 }, 2322 }, 2323 Next: 3, 2324 Truncated: false, 2325 }, actual) 2326 2327 // Missing item. 2328 actual, err = c.FindStorageByHash(h, []byte("unknown prefix"), nil) 2329 require.NoError(t, err) 2330 require.Equal(t, result.FindStorage{ 2331 Results: []result.KeyValue{}, 2332 Next: 0, 2333 Truncated: false, 2334 }, actual) 2335 } 2336 2337 func TestClient_FindStorageHistoric(t *testing.T) { 2338 chain, _, httpSrv := initServerWithInMemoryChain(t) 2339 2340 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 2341 require.NoError(t, err) 2342 t.Cleanup(c.Close) 2343 require.NoError(t, c.Init()) 2344 2345 root, err := util.Uint256DecodeStringLE(block20StateRootLE) 2346 require.NoError(t, err) 2347 h, err := util.Uint160DecodeStringLE(testContractHash) 2348 require.NoError(t, err) 2349 prefix := []byte("aa") 2350 expected := result.FindStorage{ 2351 Results: []result.KeyValue{ 2352 { 2353 Key: []byte("aa10"), 2354 Value: []byte("v2"), 2355 }, 2356 { 2357 Key: []byte("aa50"), 2358 Value: []byte("v3"), 2359 }, 2360 }, 2361 Next: 2, 2362 Truncated: true, 2363 } 2364 2365 // By hash. 2366 actual, err := c.FindStorageByHashHistoric(root, h, prefix, nil) 2367 require.NoError(t, err) 2368 require.Equal(t, expected, actual) 2369 2370 // By ID. 2371 actual, err = c.FindStorageByIDHistoric(root, 1, prefix, nil) // Rubles contract 2372 require.NoError(t, err) 2373 require.Equal(t, expected, actual) 2374 2375 // Non-nil start. 2376 start := 1 2377 actual, err = c.FindStorageByHashHistoric(root, h, prefix, &start) 2378 require.NoError(t, err) 2379 require.Equal(t, result.FindStorage{ 2380 Results: []result.KeyValue{ 2381 { 2382 Key: []byte("aa50"), 2383 Value: []byte("v3"), 2384 }, 2385 { 2386 Key: []byte("aa"), // order differs due to MPT traversal strategy. 2387 Value: []byte("v1"), 2388 }, 2389 }, 2390 Next: 3, 2391 Truncated: false, 2392 }, actual) 2393 2394 // Missing item. 2395 earlyRoot, err := chain.GetStateRoot(15) // there's no `aa10` value in Rubles contract by the moment of block #15 2396 require.NoError(t, err) 2397 actual, err = c.FindStorageByHashHistoric(earlyRoot.Root, h, prefix, nil) 2398 require.NoError(t, err) 2399 require.Equal(t, result.FindStorage{ 2400 Results: []result.KeyValue{}, 2401 Next: 0, 2402 Truncated: false, 2403 }, actual) 2404 } 2405 2406 func TestClient_GetStorageHistoric(t *testing.T) { 2407 chain, _, httpSrv := initServerWithInMemoryChain(t) 2408 2409 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 2410 require.NoError(t, err) 2411 t.Cleanup(c.Close) 2412 require.NoError(t, c.Init()) 2413 2414 root, err := util.Uint256DecodeStringLE(block20StateRootLE) 2415 require.NoError(t, err) 2416 h, err := util.Uint160DecodeStringLE(testContractHash) 2417 require.NoError(t, err) 2418 key := []byte("aa10") 2419 expected := []byte("v2") 2420 2421 // By hash. 2422 actual, err := c.GetStorageByHashHistoric(root, h, key) 2423 require.NoError(t, err) 2424 require.Equal(t, expected, actual) 2425 2426 // By ID. 2427 actual, err = c.GetStorageByIDHistoric(root, 1, key) // Rubles contract 2428 require.NoError(t, err) 2429 require.Equal(t, expected, actual) 2430 2431 // Missing item. 2432 earlyRoot, err := chain.GetStateRoot(15) // there's no `aa10` value in Rubles contract by the moment of block #15 2433 require.NoError(t, err) 2434 _, err = c.GetStorageByHashHistoric(earlyRoot.Root, h, key) 2435 require.ErrorIs(t, neorpc.ErrUnknownStorageItem, err) 2436 } 2437 2438 func TestClient_GetVersion_Hardforks(t *testing.T) { 2439 _, _, httpSrv := initServerWithInMemoryChain(t) 2440 2441 c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) 2442 require.NoError(t, err) 2443 t.Cleanup(c.Close) 2444 require.NoError(t, c.Init()) 2445 2446 v, err := c.GetVersion() 2447 require.NoError(t, err) 2448 expected := map[config.Hardfork]uint32{ 2449 config.HFAspidochelone: 25, 2450 } 2451 require.InDeltaMapValues(t, expected, v.Protocol.Hardforks, 0) 2452 }