github.com/onflow/flow-go@v0.33.17/fvm/accounts_test.go (about) 1 package fvm_test 2 3 import ( 4 "fmt" 5 "strconv" 6 "testing" 7 8 "github.com/onflow/cadence" 9 "github.com/onflow/cadence/encoding/ccf" 10 jsoncdc "github.com/onflow/cadence/encoding/json" 11 "github.com/onflow/cadence/runtime/format" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 15 "github.com/onflow/flow-go/engine/execution/testutil" 16 "github.com/onflow/flow-go/fvm" 17 "github.com/onflow/flow-go/fvm/storage/snapshot" 18 "github.com/onflow/flow-go/model/flow" 19 "github.com/onflow/flow-go/utils/unittest" 20 ) 21 22 type errorOnAddressSnapshotWrapper struct { 23 snapshotTree snapshot.SnapshotTree 24 owner flow.Address 25 } 26 27 func (s errorOnAddressSnapshotWrapper) Get( 28 id flow.RegisterID, 29 ) ( 30 flow.RegisterValue, 31 error, 32 ) { 33 // return error if id.Owner is the same as the owner of the wrapper 34 if id.Owner == string(s.owner.Bytes()) { 35 return nil, fmt.Errorf("error getting register %s", id) 36 } 37 38 return s.snapshotTree.Get(id) 39 } 40 41 func createAccount( 42 t *testing.T, 43 vm fvm.VM, 44 chain flow.Chain, 45 ctx fvm.Context, 46 snapshotTree snapshot.SnapshotTree, 47 ) ( 48 snapshot.SnapshotTree, 49 flow.Address, 50 ) { 51 ctx = fvm.NewContextFromParent( 52 ctx, 53 fvm.WithAuthorizationChecksEnabled(false), 54 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 55 ) 56 57 txBody := flow.NewTransactionBody(). 58 SetScript([]byte(createAccountTransaction)). 59 AddAuthorizer(chain.ServiceAddress()) 60 61 executionSnapshot, output, err := vm.Run( 62 ctx, 63 fvm.Transaction(txBody, 0), 64 snapshotTree) 65 require.NoError(t, err) 66 require.NoError(t, output.Err) 67 68 snapshotTree = snapshotTree.Append(executionSnapshot) 69 70 accountCreatedEvents := filterAccountCreatedEvents(output.Events) 71 72 require.Len(t, accountCreatedEvents, 1) 73 74 data, err := ccf.Decode(nil, accountCreatedEvents[0].Payload) 75 require.NoError(t, err) 76 address := flow.ConvertAddress( 77 data.(cadence.Event).Fields[0].(cadence.Address)) 78 79 return snapshotTree, address 80 } 81 82 type accountKeyAPIVersion string 83 84 const ( 85 accountKeyAPIVersionV1 accountKeyAPIVersion = "V1" 86 accountKeyAPIVersionV2 accountKeyAPIVersion = "V2" 87 ) 88 89 func addAccountKey( 90 t *testing.T, 91 vm fvm.VM, 92 ctx fvm.Context, 93 snapshotTree snapshot.SnapshotTree, 94 address flow.Address, 95 apiVersion accountKeyAPIVersion, 96 ) ( 97 snapshot.SnapshotTree, 98 flow.AccountPublicKey, 99 ) { 100 101 privateKey, err := unittest.AccountKeyDefaultFixture() 102 require.NoError(t, err) 103 104 publicKeyA, cadencePublicKey := newAccountKey(t, privateKey, apiVersion) 105 106 var addAccountKeyTx accountKeyAPIVersion 107 if apiVersion == accountKeyAPIVersionV1 { 108 addAccountKeyTx = addAccountKeyTransaction 109 } else { 110 addAccountKeyTx = addAccountKeyTransactionV2 111 } 112 113 txBody := flow.NewTransactionBody(). 114 SetScript([]byte(addAccountKeyTx)). 115 AddArgument(cadencePublicKey). 116 AddAuthorizer(address) 117 118 executionSnapshot, output, err := vm.Run( 119 ctx, 120 fvm.Transaction(txBody, 0), 121 snapshotTree) 122 require.NoError(t, err) 123 require.NoError(t, output.Err) 124 125 snapshotTree = snapshotTree.Append(executionSnapshot) 126 127 return snapshotTree, publicKeyA 128 } 129 130 func addAccountCreator( 131 t *testing.T, 132 vm fvm.VM, 133 chain flow.Chain, 134 ctx fvm.Context, 135 snapshotTree snapshot.SnapshotTree, 136 account flow.Address, 137 ) snapshot.SnapshotTree { 138 script := []byte( 139 fmt.Sprintf(addAccountCreatorTransactionTemplate, 140 chain.ServiceAddress().String(), 141 account.String(), 142 ), 143 ) 144 145 txBody := flow.NewTransactionBody(). 146 SetScript(script). 147 AddAuthorizer(chain.ServiceAddress()) 148 149 executionSnapshot, output, err := vm.Run( 150 ctx, 151 fvm.Transaction(txBody, 0), 152 snapshotTree) 153 require.NoError(t, err) 154 require.NoError(t, output.Err) 155 156 return snapshotTree.Append(executionSnapshot) 157 } 158 159 func removeAccountCreator( 160 t *testing.T, 161 vm fvm.VM, 162 chain flow.Chain, 163 ctx fvm.Context, 164 snapshotTree snapshot.SnapshotTree, 165 account flow.Address, 166 ) snapshot.SnapshotTree { 167 script := []byte( 168 fmt.Sprintf( 169 removeAccountCreatorTransactionTemplate, 170 chain.ServiceAddress(), 171 account.String(), 172 ), 173 ) 174 175 txBody := flow.NewTransactionBody(). 176 SetScript(script). 177 AddAuthorizer(chain.ServiceAddress()) 178 179 executionSnapshot, output, err := vm.Run( 180 ctx, 181 fvm.Transaction(txBody, 0), 182 snapshotTree) 183 require.NoError(t, err) 184 require.NoError(t, output.Err) 185 186 return snapshotTree.Append(executionSnapshot) 187 } 188 189 const createAccountTransaction = ` 190 transaction { 191 prepare(signer: AuthAccount) { 192 let account = AuthAccount(payer: signer) 193 } 194 } 195 ` 196 197 const createMultipleAccountsTransaction = ` 198 transaction { 199 prepare(signer: AuthAccount) { 200 let accountA = AuthAccount(payer: signer) 201 let accountB = AuthAccount(payer: signer) 202 let accountC = AuthAccount(payer: signer) 203 } 204 } 205 ` 206 207 const addAccountKeyTransaction = ` 208 transaction(key: [UInt8]) { 209 prepare(signer: AuthAccount) { 210 signer.addPublicKey(key) 211 } 212 } 213 ` 214 const addAccountKeyTransactionV2 = ` 215 transaction(key: [UInt8]) { 216 prepare(signer: AuthAccount) { 217 let publicKey = PublicKey( 218 publicKey: key, 219 signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 220 ) 221 signer.keys.add( 222 publicKey: publicKey, 223 hashAlgorithm: HashAlgorithm.SHA3_256, 224 weight: 1000.0 225 ) 226 } 227 } 228 ` 229 230 const addMultipleAccountKeysTransaction = ` 231 transaction(key1: [UInt8], key2: [UInt8]) { 232 prepare(signer: AuthAccount) { 233 signer.addPublicKey(key1) 234 signer.addPublicKey(key2) 235 } 236 } 237 ` 238 239 const addMultipleAccountKeysTransactionV2 = ` 240 transaction(key1: [UInt8], key2: [UInt8]) { 241 prepare(signer: AuthAccount) { 242 for key in [key1, key2] { 243 let publicKey = PublicKey( 244 publicKey: key, 245 signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 246 ) 247 signer.keys.add( 248 publicKey: publicKey, 249 hashAlgorithm: HashAlgorithm.SHA3_256, 250 weight: 1000.0 251 ) 252 } 253 } 254 } 255 ` 256 257 const removeAccountKeyTransaction = ` 258 transaction(key: Int) { 259 prepare(signer: AuthAccount) { 260 signer.removePublicKey(key) 261 } 262 } 263 ` 264 265 const revokeAccountKeyTransaction = ` 266 transaction(keyIndex: Int) { 267 prepare(signer: AuthAccount) { 268 signer.keys.revoke(keyIndex: keyIndex) 269 } 270 } 271 ` 272 273 const removeMultipleAccountKeysTransaction = ` 274 transaction(key1: Int, key2: Int) { 275 prepare(signer: AuthAccount) { 276 signer.removePublicKey(key1) 277 signer.removePublicKey(key2) 278 } 279 } 280 ` 281 282 const revokeMultipleAccountKeysTransaction = ` 283 transaction(keyIndex1: Int, keyIndex2: Int) { 284 prepare(signer: AuthAccount) { 285 for keyIndex in [keyIndex1, keyIndex2] { 286 signer.keys.revoke(keyIndex: keyIndex) 287 } 288 } 289 } 290 ` 291 292 const removeAccountCreatorTransactionTemplate = ` 293 import FlowServiceAccount from 0x%s 294 transaction { 295 let serviceAccountAdmin: &FlowServiceAccount.Administrator 296 prepare(signer: AuthAccount) { 297 // Borrow reference to FlowServiceAccount Administrator resource. 298 // 299 self.serviceAccountAdmin = signer.borrow<&FlowServiceAccount.Administrator>(from: /storage/flowServiceAdmin) 300 ?? panic("Unable to borrow reference to administrator resource") 301 } 302 execute { 303 // Remove account from account creator allowlist. 304 // 305 // Will emit AccountCreatorRemoved(accountCreator: accountCreator). 306 // 307 self.serviceAccountAdmin.removeAccountCreator(0x%s) 308 } 309 } 310 ` 311 312 const addAccountCreatorTransactionTemplate = ` 313 import FlowServiceAccount from 0x%s 314 transaction { 315 let serviceAccountAdmin: &FlowServiceAccount.Administrator 316 prepare(signer: AuthAccount) { 317 // Borrow reference to FlowServiceAccount Administrator resource. 318 // 319 self.serviceAccountAdmin = signer.borrow<&FlowServiceAccount.Administrator>(from: /storage/flowServiceAdmin) 320 ?? panic("Unable to borrow reference to administrator resource") 321 } 322 execute { 323 // Add account to account creator allowlist. 324 // 325 // Will emit AccountCreatorAdded(accountCreator: accountCreator). 326 // 327 self.serviceAccountAdmin.addAccountCreator(0x%s) 328 } 329 } 330 ` 331 332 const getAccountKeyTransaction = ` 333 transaction(keyIndex: Int) { 334 prepare(signer: AuthAccount) { 335 var key :AccountKey? = signer.keys.get(keyIndex: keyIndex) 336 log(key) 337 } 338 } 339 ` 340 341 const getMultipleAccountKeysTransaction = ` 342 transaction(keyIndex1: Int, keyIndex2: Int) { 343 prepare(signer: AuthAccount) { 344 for keyIndex in [keyIndex1, keyIndex2] { 345 var key :AccountKey? = signer.keys.get(keyIndex: keyIndex) 346 log(key) 347 } 348 } 349 } 350 ` 351 352 func newAccountKey( 353 tb testing.TB, 354 privateKey *flow.AccountPrivateKey, 355 apiVersion accountKeyAPIVersion, 356 ) ( 357 publicKey flow.AccountPublicKey, 358 encodedCadencePublicKey []byte, 359 ) { 360 publicKey = privateKey.PublicKey(fvm.AccountKeyWeightThreshold) 361 362 var publicKeyBytes []byte 363 if apiVersion == accountKeyAPIVersionV1 { 364 var err error 365 publicKeyBytes, err = flow.EncodeRuntimeAccountPublicKey(publicKey) 366 require.NoError(tb, err) 367 } else { 368 publicKeyBytes = publicKey.PublicKey.Encode() 369 } 370 371 cadencePublicKey := testutil.BytesToCadenceArray(publicKeyBytes) 372 encodedCadencePublicKey, err := jsoncdc.Encode(cadencePublicKey) 373 require.NoError(tb, err) 374 375 return publicKey, encodedCadencePublicKey 376 } 377 378 func TestCreateAccount(t *testing.T) { 379 380 options := []fvm.Option{ 381 fvm.WithAuthorizationChecksEnabled(false), 382 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 383 } 384 385 t.Run("Single account", 386 newVMTest().withContextOptions(options...). 387 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 388 snapshotTree, payer := createAccount( 389 t, 390 vm, 391 chain, 392 ctx, 393 snapshotTree) 394 395 txBody := flow.NewTransactionBody(). 396 SetScript([]byte(createAccountTransaction)). 397 AddAuthorizer(payer) 398 399 executionSnapshot, output, err := vm.Run( 400 ctx, 401 fvm.Transaction(txBody, 0), 402 snapshotTree) 403 require.NoError(t, err) 404 assert.NoError(t, output.Err) 405 406 snapshotTree = snapshotTree.Append(executionSnapshot) 407 408 accountCreatedEvents := filterAccountCreatedEvents(output.Events) 409 require.Len(t, accountCreatedEvents, 1) 410 411 data, err := ccf.Decode(nil, accountCreatedEvents[0].Payload) 412 require.NoError(t, err) 413 address := flow.ConvertAddress( 414 data.(cadence.Event).Fields[0].(cadence.Address)) 415 416 account, err := vm.GetAccount(ctx, address, snapshotTree) 417 require.NoError(t, err) 418 require.NotNil(t, account) 419 }), 420 ) 421 422 t.Run("Multiple accounts", 423 newVMTest().withContextOptions(options...). 424 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 425 const count = 3 426 427 snapshotTree, payer := createAccount( 428 t, 429 vm, 430 chain, 431 ctx, 432 snapshotTree) 433 434 txBody := flow.NewTransactionBody(). 435 SetScript([]byte(createMultipleAccountsTransaction)). 436 AddAuthorizer(payer) 437 438 executionSnapshot, output, err := vm.Run( 439 ctx, 440 fvm.Transaction(txBody, 0), 441 snapshotTree) 442 require.NoError(t, err) 443 assert.NoError(t, output.Err) 444 445 snapshotTree = snapshotTree.Append(executionSnapshot) 446 447 accountCreatedEventCount := 0 448 for _, event := range output.Events { 449 if event.Type != flow.EventAccountCreated { 450 continue 451 } 452 accountCreatedEventCount += 1 453 454 data, err := ccf.Decode(nil, event.Payload) 455 require.NoError(t, err) 456 address := flow.ConvertAddress( 457 data.(cadence.Event).Fields[0].(cadence.Address)) 458 459 account, err := vm.GetAccount(ctx, address, snapshotTree) 460 require.NoError(t, err) 461 require.NotNil(t, account) 462 } 463 require.Equal(t, count, accountCreatedEventCount) 464 }), 465 ) 466 } 467 468 func TestCreateAccount_WithRestrictedAccountCreation(t *testing.T) { 469 470 options := []fvm.Option{ 471 fvm.WithAuthorizationChecksEnabled(false), 472 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 473 } 474 475 t.Run("Unauthorized account payer", 476 newVMTest(). 477 withContextOptions(options...). 478 withBootstrapProcedureOptions(fvm.WithRestrictedAccountCreationEnabled(true)). 479 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 480 snapshotTree, payer := createAccount( 481 t, 482 vm, 483 chain, 484 ctx, 485 snapshotTree) 486 487 txBody := flow.NewTransactionBody(). 488 SetScript([]byte(createAccountTransaction)). 489 AddAuthorizer(payer) 490 491 _, output, err := vm.Run( 492 ctx, 493 fvm.Transaction(txBody, 0), 494 snapshotTree) 495 require.NoError(t, err) 496 497 assert.Error(t, output.Err) 498 }), 499 ) 500 501 t.Run("Authorized account payer", 502 newVMTest().withContextOptions(options...). 503 withBootstrapProcedureOptions(fvm.WithRestrictedAccountCreationEnabled(true)). 504 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 505 txBody := flow.NewTransactionBody(). 506 SetScript([]byte(createAccountTransaction)). 507 AddAuthorizer(chain.ServiceAddress()) 508 509 _, output, err := vm.Run( 510 ctx, 511 fvm.Transaction(txBody, 0), 512 snapshotTree) 513 require.NoError(t, err) 514 515 assert.NoError(t, output.Err) 516 }), 517 ) 518 519 t.Run("Account payer added to allowlist", 520 newVMTest().withContextOptions(options...). 521 withBootstrapProcedureOptions(fvm.WithRestrictedAccountCreationEnabled(true)). 522 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 523 snapshotTree, payer := createAccount( 524 t, 525 vm, 526 chain, 527 ctx, 528 snapshotTree) 529 snapshotTree = addAccountCreator( 530 t, 531 vm, 532 chain, 533 ctx, 534 snapshotTree, 535 payer) 536 537 txBody := flow.NewTransactionBody(). 538 SetScript([]byte(createAccountTransaction)). 539 SetPayer(payer). 540 AddAuthorizer(payer) 541 542 _, output, err := vm.Run( 543 ctx, 544 fvm.Transaction(txBody, 0), 545 snapshotTree) 546 require.NoError(t, err) 547 548 assert.NoError(t, output.Err) 549 }), 550 ) 551 552 t.Run("Account payer removed from allowlist", 553 newVMTest().withContextOptions(options...). 554 withBootstrapProcedureOptions(fvm.WithRestrictedAccountCreationEnabled(true)). 555 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 556 snapshotTree, payer := createAccount( 557 t, 558 vm, 559 chain, 560 ctx, 561 snapshotTree) 562 snapshotTree = addAccountCreator( 563 t, 564 vm, 565 chain, 566 ctx, 567 snapshotTree, 568 payer) 569 570 txBody := flow.NewTransactionBody(). 571 SetScript([]byte(createAccountTransaction)). 572 AddAuthorizer(payer) 573 574 executionSnapshot, output, err := vm.Run( 575 ctx, 576 fvm.Transaction(txBody, 0), 577 snapshotTree) 578 require.NoError(t, err) 579 assert.NoError(t, output.Err) 580 581 snapshotTree = snapshotTree.Append(executionSnapshot) 582 583 snapshotTree = removeAccountCreator( 584 t, 585 vm, 586 chain, 587 ctx, 588 snapshotTree, 589 payer) 590 591 _, output, err = vm.Run( 592 ctx, 593 fvm.Transaction(txBody, 0), 594 snapshotTree) 595 require.NoError(t, err) 596 597 assert.Error(t, output.Err) 598 }), 599 ) 600 } 601 602 func TestAddAccountKey(t *testing.T) { 603 604 options := []fvm.Option{ 605 fvm.WithAuthorizationChecksEnabled(false), 606 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 607 } 608 609 type addKeyTest struct { 610 source string 611 apiVersion accountKeyAPIVersion 612 } 613 614 // Add a single key 615 616 singleKeyTests := []addKeyTest{ 617 { 618 source: addAccountKeyTransaction, 619 apiVersion: accountKeyAPIVersionV1, 620 }, 621 { 622 source: addAccountKeyTransactionV2, 623 apiVersion: accountKeyAPIVersionV2, 624 }, 625 } 626 627 for _, test := range singleKeyTests { 628 629 t.Run(fmt.Sprintf("Add to empty key list %s", test.apiVersion), 630 newVMTest().withContextOptions(options...). 631 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 632 snapshotTree, address := createAccount( 633 t, 634 vm, 635 chain, 636 ctx, 637 snapshotTree) 638 639 before, err := vm.GetAccount(ctx, address, snapshotTree) 640 require.NoError(t, err) 641 assert.Empty(t, before.Keys) 642 643 privateKey, err := unittest.AccountKeyDefaultFixture() 644 require.NoError(t, err) 645 646 publicKeyA, cadencePublicKey := newAccountKey(t, privateKey, test.apiVersion) 647 648 txBody := flow.NewTransactionBody(). 649 SetScript([]byte(test.source)). 650 AddArgument(cadencePublicKey). 651 AddAuthorizer(address) 652 653 executionSnapshot, output, err := vm.Run( 654 ctx, 655 fvm.Transaction(txBody, 0), 656 snapshotTree) 657 require.NoError(t, err) 658 659 assert.NoError(t, output.Err) 660 661 snapshotTree = snapshotTree.Append(executionSnapshot) 662 663 after, err := vm.GetAccount(ctx, address, snapshotTree) 664 require.NoError(t, err) 665 666 require.Len(t, after.Keys, 1) 667 668 publicKeyB := after.Keys[0] 669 670 assert.Equal(t, publicKeyA.PublicKey, publicKeyB.PublicKey) 671 assert.Equal(t, publicKeyA.SignAlgo, publicKeyB.SignAlgo) 672 assert.Equal(t, publicKeyA.HashAlgo, publicKeyB.HashAlgo) 673 assert.Equal(t, publicKeyA.Weight, publicKeyB.Weight) 674 }), 675 ) 676 677 t.Run(fmt.Sprintf("Add to non-empty key list %s", test.apiVersion), 678 newVMTest().withContextOptions(options...). 679 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 680 snapshotTree, address := createAccount( 681 t, 682 vm, 683 chain, 684 ctx, 685 snapshotTree) 686 687 snapshotTree, publicKey1 := addAccountKey( 688 t, 689 vm, 690 ctx, 691 snapshotTree, 692 address, 693 test.apiVersion) 694 695 before, err := vm.GetAccount(ctx, address, snapshotTree) 696 require.NoError(t, err) 697 assert.Len(t, before.Keys, 1) 698 699 privateKey, err := unittest.AccountKeyDefaultFixture() 700 require.NoError(t, err) 701 702 publicKey2, publicKey2Arg := newAccountKey(t, privateKey, test.apiVersion) 703 704 txBody := flow.NewTransactionBody(). 705 SetScript([]byte(test.source)). 706 AddArgument(publicKey2Arg). 707 AddAuthorizer(address) 708 709 executionSnapshot, output, err := vm.Run( 710 ctx, 711 fvm.Transaction(txBody, 0), 712 snapshotTree) 713 require.NoError(t, err) 714 assert.NoError(t, output.Err) 715 716 snapshotTree = snapshotTree.Append(executionSnapshot) 717 718 after, err := vm.GetAccount(ctx, address, snapshotTree) 719 require.NoError(t, err) 720 721 expectedKeys := []flow.AccountPublicKey{ 722 publicKey1, 723 publicKey2, 724 } 725 726 require.Len(t, after.Keys, len(expectedKeys)) 727 728 for i, expectedKey := range expectedKeys { 729 actualKey := after.Keys[i] 730 assert.Equal(t, i, actualKey.Index) 731 assert.Equal(t, expectedKey.PublicKey, actualKey.PublicKey) 732 assert.Equal(t, expectedKey.SignAlgo, actualKey.SignAlgo) 733 assert.Equal(t, expectedKey.HashAlgo, actualKey.HashAlgo) 734 assert.Equal(t, expectedKey.Weight, actualKey.Weight) 735 } 736 }), 737 ) 738 739 t.Run(fmt.Sprintf("Invalid key %s", test.apiVersion), 740 newVMTest().withContextOptions(options...). 741 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 742 snapshotTree, address := createAccount( 743 t, 744 vm, 745 chain, 746 ctx, 747 snapshotTree) 748 749 invalidPublicKey := testutil.BytesToCadenceArray([]byte{1, 2, 3}) 750 invalidPublicKeyArg, err := jsoncdc.Encode(invalidPublicKey) 751 require.NoError(t, err) 752 753 txBody := flow.NewTransactionBody(). 754 SetScript([]byte(test.source)). 755 AddArgument(invalidPublicKeyArg). 756 AddAuthorizer(address) 757 758 executionSnapshot, output, err := vm.Run( 759 ctx, 760 fvm.Transaction(txBody, 0), 761 snapshotTree) 762 require.NoError(t, err) 763 assert.Error(t, output.Err) 764 765 snapshotTree = snapshotTree.Append(executionSnapshot) 766 767 after, err := vm.GetAccount(ctx, address, snapshotTree) 768 require.NoError(t, err) 769 770 assert.Empty(t, after.Keys) 771 }), 772 ) 773 } 774 775 // Add multiple keys 776 777 multipleKeysTests := []addKeyTest{ 778 { 779 source: addMultipleAccountKeysTransaction, 780 apiVersion: accountKeyAPIVersionV1, 781 }, 782 { 783 source: addMultipleAccountKeysTransactionV2, 784 apiVersion: accountKeyAPIVersionV2, 785 }, 786 } 787 788 for _, test := range multipleKeysTests { 789 t.Run(fmt.Sprintf("Multiple keys %s", test.apiVersion), 790 newVMTest().withContextOptions(options...). 791 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 792 snapshotTree, address := createAccount( 793 t, 794 vm, 795 chain, 796 ctx, 797 snapshotTree) 798 799 before, err := vm.GetAccount(ctx, address, snapshotTree) 800 require.NoError(t, err) 801 assert.Empty(t, before.Keys) 802 803 privateKey1, err := unittest.AccountKeyDefaultFixture() 804 require.NoError(t, err) 805 806 privateKey2, err := unittest.AccountKeyDefaultFixture() 807 require.NoError(t, err) 808 809 publicKey1, publicKey1Arg := newAccountKey(t, privateKey1, test.apiVersion) 810 publicKey2, publicKey2Arg := newAccountKey(t, privateKey2, test.apiVersion) 811 812 txBody := flow.NewTransactionBody(). 813 SetScript([]byte(test.source)). 814 AddArgument(publicKey1Arg). 815 AddArgument(publicKey2Arg). 816 AddAuthorizer(address) 817 818 executionSnapshot, output, err := vm.Run( 819 ctx, 820 fvm.Transaction(txBody, 0), 821 snapshotTree) 822 require.NoError(t, err) 823 assert.NoError(t, output.Err) 824 825 snapshotTree = snapshotTree.Append(executionSnapshot) 826 827 after, err := vm.GetAccount(ctx, address, snapshotTree) 828 require.NoError(t, err) 829 830 expectedKeys := []flow.AccountPublicKey{ 831 publicKey1, 832 publicKey2, 833 } 834 835 require.Len(t, after.Keys, len(expectedKeys)) 836 837 for i, expectedKey := range expectedKeys { 838 actualKey := after.Keys[i] 839 assert.Equal(t, expectedKey.PublicKey, actualKey.PublicKey) 840 assert.Equal(t, expectedKey.SignAlgo, actualKey.SignAlgo) 841 assert.Equal(t, expectedKey.HashAlgo, actualKey.HashAlgo) 842 assert.Equal(t, expectedKey.Weight, actualKey.Weight) 843 } 844 }), 845 ) 846 } 847 848 t.Run("Invalid hash algorithms", func(t *testing.T) { 849 850 for _, hashAlgo := range []string{"SHA2_384", "SHA3_384"} { 851 852 t.Run(hashAlgo, 853 newVMTest().withContextOptions(options...). 854 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 855 snapshotTree, address := createAccount( 856 t, 857 vm, 858 chain, 859 ctx, 860 snapshotTree) 861 862 privateKey, err := unittest.AccountKeyDefaultFixture() 863 require.NoError(t, err) 864 865 _, publicKeyArg := newAccountKey(t, privateKey, accountKeyAPIVersionV2) 866 867 txBody := flow.NewTransactionBody(). 868 SetScript([]byte(fmt.Sprintf( 869 ` 870 transaction(key: [UInt8]) { 871 prepare(signer: AuthAccount) { 872 let publicKey = PublicKey( 873 publicKey: key, 874 signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 875 ) 876 signer.keys.add( 877 publicKey: publicKey, 878 hashAlgorithm: HashAlgorithm.%s, 879 weight: 1000.0 880 ) 881 } 882 } 883 `, 884 hashAlgo, 885 ))). 886 AddArgument(publicKeyArg). 887 AddAuthorizer(address) 888 889 executionSnapshot, output, err := vm.Run( 890 ctx, 891 fvm.Transaction(txBody, 0), 892 snapshotTree) 893 require.NoError(t, err) 894 895 require.Error(t, output.Err) 896 assert.ErrorContains( 897 t, 898 output.Err, 899 "hashing algorithm type not supported") 900 901 snapshotTree = snapshotTree.Append(executionSnapshot) 902 903 after, err := vm.GetAccount(ctx, address, snapshotTree) 904 require.NoError(t, err) 905 906 assert.Empty(t, after.Keys) 907 }), 908 ) 909 } 910 }) 911 } 912 913 func TestRemoveAccountKey(t *testing.T) { 914 915 options := []fvm.Option{ 916 fvm.WithAuthorizationChecksEnabled(false), 917 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 918 } 919 920 type removeKeyTest struct { 921 source string 922 apiVersion accountKeyAPIVersion 923 expectError bool 924 } 925 926 // Remove a single key 927 928 singleKeyTests := []removeKeyTest{ 929 { 930 source: removeAccountKeyTransaction, 931 apiVersion: accountKeyAPIVersionV1, 932 expectError: true, 933 }, 934 { 935 source: revokeAccountKeyTransaction, 936 apiVersion: accountKeyAPIVersionV2, 937 expectError: false, 938 }, 939 } 940 941 for _, test := range singleKeyTests { 942 943 t.Run(fmt.Sprintf("Non-existent key %s", test.apiVersion), 944 newVMTest().withContextOptions(options...). 945 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 946 snapshotTree, address := createAccount( 947 t, 948 vm, 949 chain, 950 ctx, 951 snapshotTree) 952 953 const keyCount = 2 954 955 for i := 0; i < keyCount; i++ { 956 snapshotTree, _ = addAccountKey( 957 t, 958 vm, 959 ctx, 960 snapshotTree, 961 address, 962 test.apiVersion) 963 } 964 965 before, err := vm.GetAccount(ctx, address, snapshotTree) 966 require.NoError(t, err) 967 assert.Len(t, before.Keys, keyCount) 968 969 for _, keyIndex := range []int{-1, keyCount, keyCount + 1} { 970 keyIndexArg, err := jsoncdc.Encode(cadence.NewInt(keyIndex)) 971 require.NoError(t, err) 972 973 txBody := flow.NewTransactionBody(). 974 SetScript([]byte(test.source)). 975 AddArgument(keyIndexArg). 976 AddAuthorizer(address) 977 978 executionSnapshot, output, err := vm.Run( 979 ctx, 980 fvm.Transaction(txBody, 0), 981 snapshotTree) 982 require.NoError(t, err) 983 984 if test.expectError { 985 assert.Error(t, output.Err) 986 } else { 987 assert.NoError(t, output.Err) 988 } 989 990 snapshotTree = snapshotTree.Append(executionSnapshot) 991 } 992 993 after, err := vm.GetAccount(ctx, address, snapshotTree) 994 require.NoError(t, err) 995 assert.Len(t, after.Keys, keyCount) 996 997 for _, publicKey := range after.Keys { 998 assert.False(t, publicKey.Revoked) 999 } 1000 }), 1001 ) 1002 1003 t.Run(fmt.Sprintf("Existing key %s", test.apiVersion), 1004 newVMTest().withContextOptions(options...). 1005 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1006 snapshotTree, address := createAccount( 1007 t, 1008 vm, 1009 chain, 1010 ctx, 1011 snapshotTree) 1012 1013 const keyCount = 2 1014 const keyIndex = keyCount - 1 1015 1016 for i := 0; i < keyCount; i++ { 1017 snapshotTree, _ = addAccountKey( 1018 t, 1019 vm, 1020 ctx, 1021 snapshotTree, 1022 address, 1023 test.apiVersion) 1024 } 1025 1026 before, err := vm.GetAccount(ctx, address, snapshotTree) 1027 require.NoError(t, err) 1028 assert.Len(t, before.Keys, keyCount) 1029 1030 keyIndexArg, err := jsoncdc.Encode(cadence.NewInt(keyIndex)) 1031 require.NoError(t, err) 1032 1033 txBody := flow.NewTransactionBody(). 1034 SetScript([]byte(test.source)). 1035 AddArgument(keyIndexArg). 1036 AddAuthorizer(address) 1037 1038 executionSnapshot, output, err := vm.Run( 1039 ctx, 1040 fvm.Transaction(txBody, 0), 1041 snapshotTree) 1042 require.NoError(t, err) 1043 assert.NoError(t, output.Err) 1044 1045 snapshotTree = snapshotTree.Append(executionSnapshot) 1046 1047 after, err := vm.GetAccount(ctx, address, snapshotTree) 1048 require.NoError(t, err) 1049 assert.Len(t, after.Keys, keyCount) 1050 1051 for _, publicKey := range after.Keys[:len(after.Keys)-1] { 1052 assert.False(t, publicKey.Revoked) 1053 } 1054 1055 assert.True(t, after.Keys[keyIndex].Revoked) 1056 }), 1057 ) 1058 1059 t.Run(fmt.Sprintf("Key added by a different api version %s", test.apiVersion), 1060 newVMTest().withContextOptions(options...). 1061 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1062 snapshotTree, address := createAccount( 1063 t, 1064 vm, 1065 chain, 1066 ctx, 1067 snapshotTree) 1068 1069 const keyCount = 2 1070 const keyIndex = keyCount - 1 1071 1072 // Use one version of API to add the keys, and a different version of the API to revoke the keys. 1073 var apiVersionForAdding accountKeyAPIVersion 1074 if test.apiVersion == accountKeyAPIVersionV1 { 1075 apiVersionForAdding = accountKeyAPIVersionV2 1076 } else { 1077 apiVersionForAdding = accountKeyAPIVersionV1 1078 } 1079 1080 for i := 0; i < keyCount; i++ { 1081 snapshotTree, _ = addAccountKey( 1082 t, 1083 vm, 1084 ctx, 1085 snapshotTree, 1086 address, 1087 apiVersionForAdding) 1088 } 1089 1090 before, err := vm.GetAccount(ctx, address, snapshotTree) 1091 require.NoError(t, err) 1092 assert.Len(t, before.Keys, keyCount) 1093 1094 keyIndexArg, err := jsoncdc.Encode(cadence.NewInt(keyIndex)) 1095 require.NoError(t, err) 1096 1097 txBody := flow.NewTransactionBody(). 1098 SetScript([]byte(test.source)). 1099 AddArgument(keyIndexArg). 1100 AddAuthorizer(address) 1101 1102 executionSnapshot, output, err := vm.Run( 1103 ctx, 1104 fvm.Transaction(txBody, 0), 1105 snapshotTree) 1106 require.NoError(t, err) 1107 assert.NoError(t, output.Err) 1108 1109 snapshotTree = snapshotTree.Append(executionSnapshot) 1110 1111 after, err := vm.GetAccount(ctx, address, snapshotTree) 1112 require.NoError(t, err) 1113 assert.Len(t, after.Keys, keyCount) 1114 1115 for _, publicKey := range after.Keys[:len(after.Keys)-1] { 1116 assert.False(t, publicKey.Revoked) 1117 } 1118 1119 assert.True(t, after.Keys[keyIndex].Revoked) 1120 }), 1121 ) 1122 } 1123 1124 // Remove multiple keys 1125 1126 multipleKeysTests := []removeKeyTest{ 1127 { 1128 source: removeMultipleAccountKeysTransaction, 1129 apiVersion: accountKeyAPIVersionV1, 1130 }, 1131 { 1132 source: revokeMultipleAccountKeysTransaction, 1133 apiVersion: accountKeyAPIVersionV2, 1134 }, 1135 } 1136 1137 for _, test := range multipleKeysTests { 1138 t.Run(fmt.Sprintf("Multiple keys %s", test.apiVersion), 1139 newVMTest().withContextOptions(options...). 1140 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1141 snapshotTree, address := createAccount( 1142 t, 1143 vm, 1144 chain, 1145 ctx, 1146 snapshotTree) 1147 1148 const keyCount = 2 1149 1150 for i := 0; i < keyCount; i++ { 1151 snapshotTree, _ = addAccountKey( 1152 t, 1153 vm, 1154 ctx, 1155 snapshotTree, 1156 address, 1157 test.apiVersion) 1158 } 1159 1160 before, err := vm.GetAccount(ctx, address, snapshotTree) 1161 require.NoError(t, err) 1162 assert.Len(t, before.Keys, keyCount) 1163 1164 txBody := flow.NewTransactionBody(). 1165 SetScript([]byte(test.source)). 1166 AddAuthorizer(address) 1167 1168 for i := 0; i < keyCount; i++ { 1169 keyIndexArg, err := jsoncdc.Encode(cadence.NewInt(i)) 1170 require.NoError(t, err) 1171 1172 txBody.AddArgument(keyIndexArg) 1173 } 1174 1175 executionSnapshot, output, err := vm.Run( 1176 ctx, 1177 fvm.Transaction(txBody, 0), 1178 snapshotTree) 1179 require.NoError(t, err) 1180 assert.NoError(t, output.Err) 1181 1182 snapshotTree = snapshotTree.Append(executionSnapshot) 1183 1184 after, err := vm.GetAccount(ctx, address, snapshotTree) 1185 require.NoError(t, err) 1186 assert.Len(t, after.Keys, keyCount) 1187 1188 for _, publicKey := range after.Keys { 1189 assert.True(t, publicKey.Revoked) 1190 } 1191 }), 1192 ) 1193 } 1194 } 1195 1196 func TestGetAccountKey(t *testing.T) { 1197 1198 options := []fvm.Option{ 1199 fvm.WithAuthorizationChecksEnabled(false), 1200 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 1201 fvm.WithCadenceLogging(true), 1202 } 1203 1204 t.Run("Non-existent key", 1205 newVMTest().withContextOptions(options...). 1206 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1207 snapshotTree, address := createAccount( 1208 t, 1209 vm, 1210 chain, 1211 ctx, 1212 snapshotTree) 1213 1214 const keyCount = 2 1215 1216 for i := 0; i < keyCount; i++ { 1217 snapshotTree, _ = addAccountKey( 1218 t, 1219 vm, 1220 ctx, 1221 snapshotTree, 1222 address, 1223 accountKeyAPIVersionV2) 1224 } 1225 1226 before, err := vm.GetAccount(ctx, address, snapshotTree) 1227 require.NoError(t, err) 1228 assert.Len(t, before.Keys, keyCount) 1229 1230 for _, keyIndex := range []int{-1, keyCount, keyCount + 1} { 1231 keyIndexArg, err := jsoncdc.Encode(cadence.NewInt(keyIndex)) 1232 require.NoError(t, err) 1233 1234 txBody := flow.NewTransactionBody(). 1235 SetScript([]byte(getAccountKeyTransaction)). 1236 AddArgument(keyIndexArg). 1237 AddAuthorizer(address) 1238 1239 executionSnapshot, output, err := vm.Run( 1240 ctx, 1241 fvm.Transaction(txBody, 0), 1242 snapshotTree) 1243 require.NoError(t, err) 1244 require.NoError(t, output.Err) 1245 1246 snapshotTree = snapshotTree.Append(executionSnapshot) 1247 1248 require.Len(t, output.Logs, 1) 1249 assert.Equal(t, "nil", output.Logs[0]) 1250 } 1251 }), 1252 ) 1253 1254 t.Run("Existing key", 1255 newVMTest().withContextOptions(options...). 1256 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1257 snapshotTree, address := createAccount( 1258 t, 1259 vm, 1260 chain, 1261 ctx, 1262 snapshotTree) 1263 1264 const keyCount = 2 1265 const keyIndex = keyCount - 1 1266 1267 keys := make([]flow.AccountPublicKey, keyCount) 1268 for i := 0; i < keyCount; i++ { 1269 snapshotTree, keys[i] = addAccountKey( 1270 t, 1271 vm, 1272 ctx, 1273 snapshotTree, 1274 address, 1275 accountKeyAPIVersionV2) 1276 } 1277 1278 before, err := vm.GetAccount(ctx, address, snapshotTree) 1279 require.NoError(t, err) 1280 assert.Len(t, before.Keys, keyCount) 1281 1282 keyIndexArg, err := jsoncdc.Encode(cadence.NewInt(keyIndex)) 1283 require.NoError(t, err) 1284 1285 txBody := flow.NewTransactionBody(). 1286 SetScript([]byte(getAccountKeyTransaction)). 1287 AddArgument(keyIndexArg). 1288 AddAuthorizer(address) 1289 1290 _, output, err := vm.Run( 1291 ctx, 1292 fvm.Transaction(txBody, 0), 1293 snapshotTree) 1294 require.NoError(t, err) 1295 require.NoError(t, output.Err) 1296 1297 require.Len(t, output.Logs, 1) 1298 1299 key := keys[keyIndex] 1300 1301 expected := fmt.Sprintf( 1302 "AccountKey("+ 1303 "keyIndex: %d, "+ 1304 "publicKey: PublicKey(publicKey: %s, signatureAlgorithm: SignatureAlgorithm(rawValue: 1)), "+ 1305 "hashAlgorithm: HashAlgorithm(rawValue: 3), "+ 1306 "weight: 1000.00000000, "+ 1307 "isRevoked: false)", 1308 keyIndex, 1309 byteSliceToCadenceArrayLiteral(key.PublicKey.Encode()), 1310 ) 1311 1312 assert.Equal(t, expected, output.Logs[0]) 1313 }), 1314 ) 1315 1316 t.Run("Key added by a different api version", 1317 newVMTest().withContextOptions(options...). 1318 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1319 snapshotTree, address := createAccount( 1320 t, 1321 vm, 1322 chain, 1323 ctx, 1324 snapshotTree) 1325 1326 const keyCount = 2 1327 const keyIndex = keyCount - 1 1328 1329 keys := make([]flow.AccountPublicKey, keyCount) 1330 for i := 0; i < keyCount; i++ { 1331 1332 // Use the old version of API to add the key 1333 snapshotTree, keys[i] = addAccountKey( 1334 t, 1335 vm, 1336 ctx, 1337 snapshotTree, 1338 address, 1339 accountKeyAPIVersionV1) 1340 } 1341 1342 before, err := vm.GetAccount(ctx, address, snapshotTree) 1343 require.NoError(t, err) 1344 assert.Len(t, before.Keys, keyCount) 1345 1346 keyIndexArg, err := jsoncdc.Encode(cadence.NewInt(keyIndex)) 1347 require.NoError(t, err) 1348 1349 txBody := flow.NewTransactionBody(). 1350 SetScript([]byte(getAccountKeyTransaction)). 1351 AddArgument(keyIndexArg). 1352 AddAuthorizer(address) 1353 1354 _, output, err := vm.Run( 1355 ctx, 1356 fvm.Transaction(txBody, 0), 1357 snapshotTree) 1358 require.NoError(t, err) 1359 require.NoError(t, output.Err) 1360 1361 require.Len(t, output.Logs, 1) 1362 1363 key := keys[keyIndex] 1364 1365 expected := fmt.Sprintf( 1366 "AccountKey("+ 1367 "keyIndex: %d, "+ 1368 "publicKey: PublicKey(publicKey: %s, signatureAlgorithm: SignatureAlgorithm(rawValue: 1)), "+ 1369 "hashAlgorithm: HashAlgorithm(rawValue: 3), "+ 1370 "weight: 1000.00000000, "+ 1371 "isRevoked: false)", 1372 keyIndex, 1373 byteSliceToCadenceArrayLiteral(key.PublicKey.Encode()), 1374 ) 1375 1376 assert.Equal(t, expected, output.Logs[0]) 1377 }), 1378 ) 1379 1380 t.Run("Multiple keys", 1381 newVMTest().withContextOptions(options...). 1382 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1383 snapshotTree, address := createAccount( 1384 t, 1385 vm, 1386 chain, 1387 ctx, 1388 snapshotTree) 1389 1390 const keyCount = 2 1391 1392 keys := make([]flow.AccountPublicKey, keyCount) 1393 for i := 0; i < keyCount; i++ { 1394 1395 snapshotTree, keys[i] = addAccountKey( 1396 t, 1397 vm, 1398 ctx, 1399 snapshotTree, 1400 address, 1401 accountKeyAPIVersionV2) 1402 } 1403 1404 before, err := vm.GetAccount(ctx, address, snapshotTree) 1405 require.NoError(t, err) 1406 assert.Len(t, before.Keys, keyCount) 1407 1408 txBody := flow.NewTransactionBody(). 1409 SetScript([]byte(getMultipleAccountKeysTransaction)). 1410 AddAuthorizer(address) 1411 1412 for i := 0; i < keyCount; i++ { 1413 keyIndexArg, err := jsoncdc.Encode(cadence.NewInt(i)) 1414 require.NoError(t, err) 1415 1416 txBody.AddArgument(keyIndexArg) 1417 } 1418 1419 _, output, err := vm.Run( 1420 ctx, 1421 fvm.Transaction(txBody, 0), 1422 snapshotTree) 1423 require.NoError(t, err) 1424 require.NoError(t, output.Err) 1425 1426 assert.Len(t, output.Logs, 2) 1427 1428 for i := 0; i < keyCount; i++ { 1429 expected := fmt.Sprintf( 1430 "AccountKey("+ 1431 "keyIndex: %d, "+ 1432 "publicKey: PublicKey(publicKey: %s, signatureAlgorithm: SignatureAlgorithm(rawValue: 1)), "+ 1433 "hashAlgorithm: HashAlgorithm(rawValue: 3), "+ 1434 "weight: 1000.00000000, "+ 1435 "isRevoked: false)", 1436 i, 1437 byteSliceToCadenceArrayLiteral(keys[i].PublicKey.Encode()), 1438 ) 1439 1440 assert.Equal(t, expected, output.Logs[i]) 1441 } 1442 }), 1443 ) 1444 } 1445 1446 func byteSliceToCadenceArrayLiteral(bytes []byte) string { 1447 elements := make([]string, 0, len(bytes)) 1448 1449 for _, b := range bytes { 1450 elements = append(elements, strconv.Itoa(int(b))) 1451 } 1452 1453 return format.Array(elements) 1454 } 1455 1456 func TestAccountBalanceFields(t *testing.T) { 1457 t.Run("Get balance works", 1458 newVMTest().withContextOptions( 1459 fvm.WithAuthorizationChecksEnabled(false), 1460 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 1461 fvm.WithCadenceLogging(true), 1462 ). 1463 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1464 snapshotTree, account := createAccount( 1465 t, 1466 vm, 1467 chain, 1468 ctx, 1469 snapshotTree) 1470 1471 txBody := transferTokensTx(chain). 1472 AddArgument(jsoncdc.MustEncode(cadence.UFix64(100_000_000))). 1473 AddArgument(jsoncdc.MustEncode(cadence.Address(account))). 1474 AddAuthorizer(chain.ServiceAddress()) 1475 1476 executionSnapshot, output, err := vm.Run( 1477 ctx, 1478 fvm.Transaction(txBody, 0), 1479 snapshotTree) 1480 require.NoError(t, err) 1481 require.NoError(t, output.Err) 1482 1483 snapshotTree = snapshotTree.Append(executionSnapshot) 1484 1485 script := fvm.Script([]byte(fmt.Sprintf(` 1486 pub fun main(): UFix64 { 1487 let acc = getAccount(0x%s) 1488 return acc.balance 1489 } 1490 `, account.Hex()))) 1491 1492 _, output, err = vm.Run(ctx, script, snapshotTree) 1493 require.NoError(t, err) 1494 require.NoError(t, output.Err) 1495 1496 assert.NoError(t, err) 1497 1498 assert.Equal(t, cadence.UFix64(100_000_000), output.Value) 1499 }), 1500 ) 1501 1502 // TODO - this is because get account + borrow returns 1503 // empty values instead of failing for an account that doesnt exist 1504 // this behavior needs to addressed on Cadence side 1505 t.Run("Get balance returns 0 for accounts that don't exist", 1506 newVMTest().withContextOptions( 1507 fvm.WithAuthorizationChecksEnabled(false), 1508 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 1509 fvm.WithCadenceLogging(true), 1510 ). 1511 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1512 nonExistentAddress, err := chain.AddressAtIndex(100) 1513 require.NoError(t, err) 1514 1515 script := fvm.Script([]byte(fmt.Sprintf(` 1516 pub fun main(): UFix64 { 1517 let acc = getAccount(0x%s) 1518 return acc.balance 1519 } 1520 `, nonExistentAddress))) 1521 1522 _, output, err := vm.Run(ctx, script, snapshotTree) 1523 require.NoError(t, err) 1524 require.NoError(t, output.Err) 1525 1526 require.NoError(t, err) 1527 require.NoError(t, output.Err) 1528 require.Equal(t, cadence.UFix64(0), output.Value) 1529 }), 1530 ) 1531 1532 t.Run("Get balance fails if snapshotTree returns an error", 1533 newVMTest().withContextOptions( 1534 fvm.WithAuthorizationChecksEnabled(false), 1535 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 1536 fvm.WithCadenceLogging(true), 1537 ). 1538 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1539 snapshotTree, address := createAccount( 1540 t, 1541 vm, 1542 chain, 1543 ctx, 1544 snapshotTree) 1545 1546 script := fvm.Script([]byte(fmt.Sprintf(` 1547 pub fun main(): UFix64 { 1548 let acc = getAccount(0x%s) 1549 return acc.balance 1550 } 1551 `, address))) 1552 1553 snapshot := errorOnAddressSnapshotWrapper{ 1554 snapshotTree: snapshotTree, 1555 owner: address, 1556 } 1557 1558 _, _, err := vm.Run(ctx, script, snapshot) 1559 require.ErrorContains( 1560 t, 1561 err, 1562 fmt.Sprintf( 1563 "error getting register %s", 1564 address.Hex())) 1565 }), 1566 ) 1567 1568 t.Run("Get available balance works", 1569 newVMTest().withContextOptions( 1570 fvm.WithAuthorizationChecksEnabled(false), 1571 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 1572 fvm.WithCadenceLogging(true), 1573 fvm.WithAccountStorageLimit(false), 1574 ).withBootstrapProcedureOptions( 1575 fvm.WithStorageMBPerFLOW(1000_000_000), 1576 ). 1577 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1578 snapshotTree, account := createAccount( 1579 t, 1580 vm, 1581 chain, 1582 ctx, 1583 snapshotTree) 1584 1585 txBody := transferTokensTx(chain). 1586 AddArgument(jsoncdc.MustEncode(cadence.UFix64(100_000_000))). 1587 AddArgument(jsoncdc.MustEncode(cadence.Address(account))). 1588 AddAuthorizer(chain.ServiceAddress()) 1589 1590 executionSnapshot, output, err := vm.Run( 1591 ctx, 1592 fvm.Transaction(txBody, 0), 1593 snapshotTree) 1594 require.NoError(t, err) 1595 require.NoError(t, output.Err) 1596 1597 snapshotTree = snapshotTree.Append(executionSnapshot) 1598 1599 script := fvm.Script([]byte(fmt.Sprintf(` 1600 pub fun main(): UFix64 { 1601 let acc = getAccount(0x%s) 1602 return acc.availableBalance 1603 } 1604 `, account.Hex()))) 1605 1606 _, output, err = vm.Run(ctx, script, snapshotTree) 1607 assert.NoError(t, err) 1608 assert.NoError(t, output.Err) 1609 assert.Equal(t, cadence.UFix64(99_993_040), output.Value) 1610 }), 1611 ) 1612 1613 t.Run("Get available balance fails for accounts that don't exist", 1614 newVMTest().withContextOptions( 1615 fvm.WithAuthorizationChecksEnabled(false), 1616 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 1617 fvm.WithCadenceLogging(true), 1618 fvm.WithAccountStorageLimit(false), 1619 ).withBootstrapProcedureOptions( 1620 fvm.WithStorageMBPerFLOW(1_000_000_000), 1621 ). 1622 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1623 nonExistentAddress, err := chain.AddressAtIndex(100) 1624 require.NoError(t, err) 1625 1626 script := fvm.Script([]byte(fmt.Sprintf(` 1627 pub fun main(): UFix64 { 1628 let acc = getAccount(0x%s) 1629 return acc.availableBalance 1630 } 1631 `, nonExistentAddress))) 1632 1633 _, output, err := vm.Run(ctx, script, snapshotTree) 1634 assert.NoError(t, err) 1635 assert.Error(t, output.Err) 1636 }), 1637 ) 1638 1639 t.Run("Get available balance works with minimum balance", 1640 newVMTest().withContextOptions( 1641 fvm.WithAuthorizationChecksEnabled(false), 1642 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 1643 fvm.WithCadenceLogging(true), 1644 fvm.WithAccountStorageLimit(false), 1645 ).withBootstrapProcedureOptions( 1646 fvm.WithStorageMBPerFLOW(1000_000_000), 1647 fvm.WithAccountCreationFee(100_000), 1648 fvm.WithMinimumStorageReservation(100_000), 1649 ). 1650 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1651 snapshotTree, account := createAccount( 1652 t, 1653 vm, 1654 chain, 1655 ctx, 1656 snapshotTree) 1657 1658 txBody := transferTokensTx(chain). 1659 AddArgument(jsoncdc.MustEncode(cadence.UFix64(100_000_000))). 1660 AddArgument(jsoncdc.MustEncode(cadence.Address(account))). 1661 AddAuthorizer(chain.ServiceAddress()) 1662 1663 executionSnapshot, output, err := vm.Run( 1664 ctx, 1665 fvm.Transaction(txBody, 0), 1666 snapshotTree) 1667 require.NoError(t, err) 1668 require.NoError(t, output.Err) 1669 1670 snapshotTree = snapshotTree.Append(executionSnapshot) 1671 1672 script := fvm.Script([]byte(fmt.Sprintf(` 1673 pub fun main(): UFix64 { 1674 let acc = getAccount(0x%s) 1675 return acc.availableBalance 1676 } 1677 `, account.Hex()))) 1678 1679 _, output, err = vm.Run(ctx, script, snapshotTree) 1680 assert.NoError(t, err) 1681 assert.NoError(t, output.Err) 1682 1683 // Should be 100_000_000 because 100_000 was given to it during account creation and is now locked up 1684 assert.Equal(t, cadence.UFix64(100_000_000), output.Value) 1685 }), 1686 ) 1687 } 1688 1689 func TestGetStorageCapacity(t *testing.T) { 1690 t.Run("Get storage capacity", 1691 newVMTest().withContextOptions( 1692 fvm.WithAuthorizationChecksEnabled(false), 1693 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 1694 fvm.WithCadenceLogging(true), 1695 fvm.WithAccountStorageLimit(false), 1696 ).withBootstrapProcedureOptions( 1697 fvm.WithStorageMBPerFLOW(1_000_000_000), 1698 fvm.WithAccountCreationFee(100_000), 1699 fvm.WithMinimumStorageReservation(100_000), 1700 ). 1701 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1702 snapshotTree, account := createAccount( 1703 t, 1704 vm, 1705 chain, 1706 ctx, 1707 snapshotTree) 1708 1709 txBody := transferTokensTx(chain). 1710 AddArgument(jsoncdc.MustEncode(cadence.UFix64(100_000_000))). 1711 AddArgument(jsoncdc.MustEncode(cadence.Address(account))). 1712 AddAuthorizer(chain.ServiceAddress()) 1713 1714 executionSnapshot, output, err := vm.Run( 1715 ctx, 1716 fvm.Transaction(txBody, 0), 1717 snapshotTree) 1718 require.NoError(t, err) 1719 require.NoError(t, output.Err) 1720 1721 snapshotTree = snapshotTree.Append(executionSnapshot) 1722 1723 script := fvm.Script([]byte(fmt.Sprintf(` 1724 pub fun main(): UInt64 { 1725 let acc = getAccount(0x%s) 1726 return acc.storageCapacity 1727 } 1728 `, account))) 1729 1730 _, output, err = vm.Run(ctx, script, snapshotTree) 1731 require.NoError(t, err) 1732 require.NoError(t, output.Err) 1733 1734 require.Equal(t, cadence.UInt64(10_010_000), output.Value) 1735 }), 1736 ) 1737 t.Run("Get storage capacity returns 0 for accounts that don't exist", 1738 newVMTest().withContextOptions( 1739 fvm.WithAuthorizationChecksEnabled(false), 1740 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 1741 fvm.WithCadenceLogging(true), 1742 fvm.WithAccountStorageLimit(false), 1743 ).withBootstrapProcedureOptions( 1744 fvm.WithStorageMBPerFLOW(1_000_000_000), 1745 fvm.WithAccountCreationFee(100_000), 1746 fvm.WithMinimumStorageReservation(100_000), 1747 ). 1748 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1749 nonExistentAddress, err := chain.AddressAtIndex(100) 1750 require.NoError(t, err) 1751 1752 script := fvm.Script([]byte(fmt.Sprintf(` 1753 pub fun main(): UInt64 { 1754 let acc = getAccount(0x%s) 1755 return acc.storageCapacity 1756 } 1757 `, nonExistentAddress))) 1758 1759 _, output, err := vm.Run(ctx, script, snapshotTree) 1760 1761 require.NoError(t, err) 1762 require.NoError(t, output.Err) 1763 require.Equal(t, cadence.UInt64(0), output.Value) 1764 }), 1765 ) 1766 t.Run("Get storage capacity fails if snapshotTree returns an error", 1767 newVMTest().withContextOptions( 1768 fvm.WithAuthorizationChecksEnabled(false), 1769 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 1770 fvm.WithCadenceLogging(true), 1771 fvm.WithAccountStorageLimit(false), 1772 ).withBootstrapProcedureOptions( 1773 fvm.WithStorageMBPerFLOW(1_000_000_000), 1774 fvm.WithAccountCreationFee(100_000), 1775 fvm.WithMinimumStorageReservation(100_000), 1776 ). 1777 run(func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1778 address := chain.ServiceAddress() 1779 1780 script := fvm.Script([]byte(fmt.Sprintf(` 1781 pub fun main(): UInt64 { 1782 let acc = getAccount(0x%s) 1783 return acc.storageCapacity 1784 } 1785 `, address))) 1786 1787 storageSnapshot := errorOnAddressSnapshotWrapper{ 1788 owner: address, 1789 snapshotTree: snapshotTree, 1790 } 1791 1792 _, _, err := vm.Run(ctx, script, storageSnapshot) 1793 require.ErrorContains( 1794 t, 1795 err, 1796 fmt.Sprintf( 1797 "error getting register %s", 1798 address.Hex())) 1799 }), 1800 ) 1801 }