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