diff --git a/fvm/fvm_test.go b/fvm/fvm_test.go index 030d8765f5f..92245acf949 100644 --- a/fvm/fvm_test.go +++ b/fvm/fvm_test.go @@ -1478,6 +1478,117 @@ func TestSettingExecutionWeights(t *testing.T) { unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID()) }, )) + + t.Run("transaction with more accounts touched uses more computation", newVMTest().withBootstrapProcedureOptions( + fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), + fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), + fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), + fvm.WithTransactionFee(fvm.DefaultTransactionFees), + fvm.WithExecutionEffortWeights( + meter.ExecutionEffortWeights{ + common.ComputationKindStatement: 0, + // only count loops + // the storage check has a loop + common.ComputationKindLoop: 1 << meter.MeterExecutionInternalPrecisionBytes, + common.ComputationKindFunctionInvocation: 0, + }, + ), + ).withContextOptions( + fvm.WithAccountStorageLimit(true), + fvm.WithTransactionFeesEnabled(true), + fvm.WithMemoryLimit(math.MaxUint64), + ).run( + func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { + // Create an account private key. + privateKeys, err := testutil.GenerateAccountPrivateKeys(5) + require.NoError(t, err) + + // Bootstrap a ledger, creating accounts with the provided + // private keys and the root account. + snapshotTree, accounts, err := testutil.CreateAccounts( + vm, + snapshotTree, + privateKeys, + chain) + require.NoError(t, err) + + sc := systemcontracts.SystemContractsForChain(chain.ChainID()) + + // create a transaction without loops so only the looping in the storage check is counted. + txBody := flow.NewTransactionBody(). + SetScript([]byte(fmt.Sprintf(` + import FungibleToken from 0x%s + import FlowToken from 0x%s + + transaction() { + let sentVault: @FungibleToken.Vault + + prepare(signer: AuthAccount) { + let vaultRef = signer.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault) + ?? panic("Could not borrow reference to the owner's Vault!") + + self.sentVault <- vaultRef.withdraw(amount: 5.0) + } + + execute { + let recipient1 = getAccount(%s) + let recipient2 = getAccount(%s) + let recipient3 = getAccount(%s) + let recipient4 = getAccount(%s) + let recipient5 = getAccount(%s) + + let receiverRef1 = recipient1.getCapability(/public/flowTokenReceiver) + .borrow<&{FungibleToken.Receiver}>() + ?? panic("Could not borrow receiver reference to the recipient's Vault") + let receiverRef2 = recipient2.getCapability(/public/flowTokenReceiver) + .borrow<&{FungibleToken.Receiver}>() + ?? panic("Could not borrow receiver reference to the recipient's Vault") + let receiverRef3 = recipient3.getCapability(/public/flowTokenReceiver) + .borrow<&{FungibleToken.Receiver}>() + ?? panic("Could not borrow receiver reference to the recipient's Vault") + let receiverRef4 = recipient4.getCapability(/public/flowTokenReceiver) + .borrow<&{FungibleToken.Receiver}>() + ?? panic("Could not borrow receiver reference to the recipient's Vault") + let receiverRef5 = recipient5.getCapability(/public/flowTokenReceiver) + .borrow<&{FungibleToken.Receiver}>() + ?? panic("Could not borrow receiver reference to the recipient's Vault") + + receiverRef1.deposit(from: <-self.sentVault.withdraw(amount: 1.0)) + receiverRef2.deposit(from: <-self.sentVault.withdraw(amount: 1.0)) + receiverRef3.deposit(from: <-self.sentVault.withdraw(amount: 1.0)) + receiverRef4.deposit(from: <-self.sentVault.withdraw(amount: 1.0)) + receiverRef5.deposit(from: <-self.sentVault.withdraw(amount: 1.0)) + + destroy self.sentVault + } + }`, + sc.FungibleToken.Address, + sc.FlowToken.Address, + accounts[0].HexWithPrefix(), + accounts[1].HexWithPrefix(), + accounts[2].HexWithPrefix(), + accounts[3].HexWithPrefix(), + accounts[4].HexWithPrefix(), + ))). + SetProposalKey(chain.ServiceAddress(), 0, 0). + AddAuthorizer(chain.ServiceAddress()). + SetPayer(chain.ServiceAddress()) + + err = testutil.SignTransactionAsServiceAccount(txBody, 0, chain) + require.NoError(t, err) + + _, output, err := vm.Run( + ctx, + fvm.Transaction(txBody, 0), + snapshotTree) + require.NoError(t, err) + require.NoError(t, output.Err) + + // The storage check should loop once for each of the five accounts created + + // once for the service account + require.Equal(t, uint64(5+1), output.ComputationUsed) + }, + )) } func TestStorageUsed(t *testing.T) {