Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new script that checks if payer has balance to pay for tx #435

23 changes: 23 additions & 0 deletions lib/go/templates/internal/assets/assets.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions lib/go/templates/service_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const (
getExecutionMemoryLimit = "FlowServiceAccount/scripts/get_execution_memory_limit.cdc"
setExecutionMemoryLimit = "FlowServiceAccount/set_execution_memory_limit.cdc"

verifyPayerBalanceForTxExecution = "FlowServiceAccount/scripts/verify_payer_balance_for_tx_execution.cdc"

// Account templates
createAccountFilename = "accounts/create_new_account.cdc"
addKeyFilename = "accounts/add_key.cdc"
Expand Down Expand Up @@ -243,3 +245,9 @@ func GenerateGetExecutionMemoryLimit(env Environment) []byte {

return []byte(ReplaceAddresses(code, env))
}

func GenerateVerifyPayerBalanceForTxExecution(env Environment) []byte {
code := assets.MustAssetString(verifyPayerBalanceForTxExecution)

return []byte(ReplaceAddresses(code, env))
}
102 changes: 101 additions & 1 deletion lib/go/test/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package test

import (
"context"
"fmt"
"github.com/onflow/flow-emulator/emulator"
"testing"

"github.com/onflow/cadence"
Expand Down Expand Up @@ -401,6 +403,105 @@ func TestContracts(t *testing.T) {

})

t.Run("Should check if payer has sufficient balance to execute tx", func(t *testing.T) {
illia-malachyn marked this conversation as resolved.
Show resolved Hide resolved
// create blockchain with tx fees enabled
blockchain, adapter := newBlockchain(
emulator.WithStorageLimitEnabled(true),
emulator.WithTransactionFeesEnabled(true),
emulator.WithStorageLimitEnabled(true),
emulator.WithMinimumStorageReservation(cadence.UFix64(15000)))

// create SmallBalanceContract contract
code := []byte(`
access(all) contract SmallBalanceContract {
access(all) var value: Int32

init() {
self.value = 42
}

access(all) fun SetValue(new_value: Int32) {
self.value = new_value
}
}
`)
keys := test.AccountKeyGenerator()
accKey, accSigner := keys.NewWithSigner()
accAddress, err := adapter.CreateAccount(context.Background(), []*flow.AccountKey{accKey}, []sdktemplates.Contract{
{
Name: "SmallBalanceContract",
Source: string(code),
},
})
assert.NoError(t, err)
_, err = blockchain.CommitBlock()
assert.NoError(t, err)

// we want to execute some tx so that the payer has less balance than default
txCode := []byte(fmt.Sprintf(`
import SmallBalanceContract from 0x%s

transaction(value: Int32) {
prepare(signer: auth(Storage) &Account) {}

execute {
if value > SmallBalanceContract.value {
SmallBalanceContract.SetValue(new_value: value + 1)
} else {
SmallBalanceContract.SetValue(new_value: value - 1)
}
}
}
`, accAddress))

tx := flow.NewTransaction().
SetScript(txCode).
SetComputeLimit(9999).
SetProposalKey(accAddress, 0, 0).
SetPayer(accAddress).
AddAuthorizer(accAddress)

err = tx.AddArgument(cadence.Int32(15))
require.NoError(t, err)

err = tx.SignEnvelope(accAddress, 0, accSigner)
assert.NoError(t, err)

// this transaction should fail and be reverted, but the fees will still be paid
// which will push the balance below the minimum account balance
// calling VerifyPayerBalanceForTxExecution after this will return false.
txRes := Submit(t, blockchain, tx, true)
require.True(t, txRes.Reverted())

// set up args
cadenceAddress := cadence.NewAddress(accAddress)
inclusionEffort := cadence.UFix64(100_000_000)
gasLimit := cadence.UFix64(9999)
args := [][]byte{jsoncdc.MustEncode(cadenceAddress), jsoncdc.MustEncode(inclusionEffort), jsoncdc.MustEncode(gasLimit)}

result = executeScriptAndCheck(t, blockchain, templates.GenerateVerifyPayerBalanceForTxExecution(env), args)
require.NotNil(t, result)

// we want to get account balance later for comparison
acc, err := adapter.GetAccount(context.Background(), accAddress)
require.NoError(t, err)

// parse VerifyPayerBalanceResult
resultStruct := result.(cadence.Struct)
fields := cadence.FieldsMappedByName(resultStruct)

// actual balance should be less than required
requiredBalance := uint64(fields["requiredBalance"].(cadence.UFix64))
require.NotNil(t, requiredBalance)

actualBalance := acc.Balance
require.Less(t, actualBalance, requiredBalance)

// user cannot execute tx as he does not have sufficient balance
canExecuteTransaction := bool(fields["canExecuteTransaction"].(cadence.Bool))
require.False(t, canExecuteTransaction)
})

// deploy the ServiceAccount contract
serviceAccountCode := contracts.FlowServiceAccount(
env,
Expand All @@ -414,5 +515,4 @@ func TestContracts(t *testing.T) {
assert.NoError(t, err)
_, err = b.CommitBlock()
assert.NoError(t, err)

}
1 change: 0 additions & 1 deletion lib/go/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ func newBlockchain(opts ...emulator.Option) (emulator.Emulator, *adapters.SDKAda
b, err := emulator.New(
append(
[]emulator.Option{
// No storage limit
emulator.WithStorageLimitEnabled(false),
},
opts...,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import FlowFees from "FlowFees"

access(all) fun main(payerAcct: Address, inclusionEffort: UFix64, maxExecutionEffort: UFix64): FlowFees.VerifyPayerBalanceResult {
let authAcct = getAuthAccount<auth(BorrowValue) &Account>(payerAcct)
return FlowFees.verifyPayersBalanceForTransactionExecution(authAcct, inclusionEffort: inclusionEffort,
maxExecutionEffort: maxExecutionEffort)
}
Loading