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