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  }