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

Computation parity with FVM #451

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 14 additions & 22 deletions emulator/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"math"
"strings"
"sync"
"time"
Expand All @@ -38,6 +39,7 @@ import (
fvmcrypto "github.com/onflow/flow-go/fvm/crypto"
"github.com/onflow/flow-go/fvm/environment"
fvmerrors "github.com/onflow/flow-go/fvm/errors"
"github.com/onflow/flow-go/fvm/meter"
reusableRuntime "github.com/onflow/flow-go/fvm/runtime"
"github.com/onflow/flow-go/fvm/storage/snapshot"
flowgo "github.com/onflow/flow-go/model/flow"
Expand Down Expand Up @@ -538,7 +540,7 @@ func configureFVM(blockchain *Blockchain, conf config, blocks *blocks) (*fvm.Vir
fvm.WithBlocks(blocks),
fvm.WithContractDeploymentRestricted(false),
fvm.WithContractRemovalRestricted(!conf.ContractRemovalEnabled),
fvm.WithGasLimit(conf.ScriptGasLimit),
fvm.WithComputationLimit(conf.ScriptGasLimit),
fvm.WithCadenceLogging(true),
fvm.WithAccountStorageLimit(conf.StorageLimitEnabled),
fvm.WithTransactionFeesEnabled(conf.TransactionFeesEnabled),
Expand Down Expand Up @@ -692,6 +694,17 @@ func configureBootstrapProcedure(conf config, flowAccountKey flowgo.AccountPubli
options = append(options,
fvm.WithInitialTokenSupply(supply),
fvm.WithRestrictedAccountCreationEnabled(false),
fvm.WithTransactionFee(fvm.DefaultTransactionFees),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@m-Peter Could you please bring back the comment describing where these magic values come from, i.e. add

		// This enables variable transaction fees AND execution effort metering
		// as described in Variable Transaction Fees: Execution Effort FLIP: https://github.com/onflow/flow/pull/753)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@turbolent Sure thing 😇

fvm.WithExecutionMemoryLimit(math.MaxUint32),
fvm.WithExecutionMemoryWeights(meter.DefaultMemoryWeights),
fvm.WithExecutionEffortWeights(map[common.ComputationKind]uint64{
common.ComputationKindStatement: 1569,
common.ComputationKindLoop: 1569,
common.ComputationKindFunctionInvocation: 1569,
environment.ComputationKindGetValue: 808,
environment.ComputationKindCreateAccount: 2837670,
environment.ComputationKindSetValue: 765,
}),
)
if conf.StorageLimitEnabled {
options = append(options,
Expand All @@ -700,27 +713,6 @@ func configureBootstrapProcedure(conf config, flowAccountKey flowgo.AccountPubli
fvm.WithStorageMBPerFLOW(conf.StorageMBPerFLOW),
)
}
if conf.TransactionFeesEnabled {
janezpodhostnik marked this conversation as resolved.
Show resolved Hide resolved
// This enables variable transaction fees AND execution effort metering
// as described in Variable Transaction Fees: Execution Effort FLIP: https://github.com/onflow/flow/pull/753)
// TODO: In the future this should be an injectable parameter. For now this is hard coded
// as this is the first iteration of variable execution fees.
options = append(options,
fvm.WithTransactionFee(fvm.BootstrapProcedureFeeParameters{
SurgeFactor: cadence.UFix64(100_000_000), // 1.0
InclusionEffortCost: cadence.UFix64(100), // 1E-6
ExecutionEffortCost: cadence.UFix64(499_000_000), // 4.99
}),
fvm.WithExecutionEffortWeights(map[common.ComputationKind]uint64{
common.ComputationKindStatement: 1569,
common.ComputationKindLoop: 1569,
common.ComputationKindFunctionInvocation: 1569,
environment.ComputationKindGetValue: 808,
environment.ComputationKindCreateAccount: 2837670,
environment.ComputationKindSetValue: 765,
}),
)
}
return fvm.Bootstrap(
flowAccountKey,
options...,
Expand Down
73 changes: 68 additions & 5 deletions emulator/script_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,19 +157,82 @@ func TestInfiniteScript(t *testing.T) {

t.Parallel()

const limit = 1000
const limit = 90
b, err := emulator.New(
emulator.WithScriptGasLimit(limit),
)
require.NoError(t, err)

const code = `
pub fun main() {
main()
}
`
pub fun main() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mixed tabs and spaces (here and in other cadence scripts)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 82b2bd6

main()
}
`
result, err := b.ExecuteScript([]byte(code), nil)
require.NoError(t, err)

require.True(t, fvmerrors.IsComputationLimitExceededError(result.Error))
}

func TestScriptWithExceeedingComputationLimit(t *testing.T) {

t.Parallel()

const limit = 2000
b, err := emulator.New(
emulator.WithScriptGasLimit(limit),
)
require.NoError(t, err)

const code = `
pub fun main() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe store the script in a const

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I restructured the tests to remove the script duplication in 82b2bd6

var s: Int256 = 1024102410241024
var i: Int256 = 0
var a: Int256 = 7
var b: Int256 = 5
var c: Int256 = 2

while i < 150000 {
s = s * a
s = s / b
s = s / c
i = i + 1
}
}
`
result, err := b.ExecuteScript([]byte(code), nil)
require.NoError(t, err)

require.True(t, fvmerrors.IsComputationLimitExceededError(result.Error))
}

func TestScriptWithSufficientComputationLimit(t *testing.T) {

t.Parallel()

const limit = 19000
b, err := emulator.New(
emulator.WithScriptGasLimit(limit),
)
require.NoError(t, err)

const code = `
pub fun main() {
var s: Int256 = 1024102410241024
var i: Int256 = 0
var a: Int256 = 7
var b: Int256 = 5
var c: Int256 = 2

while i < 150000 {
s = s * a
s = s / b
s = s / c
i = i + 1
}
}
`
result, err := b.ExecuteScript([]byte(code), nil)
require.NoError(t, err)
require.NoError(t, result.Error)
}
143 changes: 133 additions & 10 deletions emulator/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1765,7 +1765,7 @@ func TestInfiniteTransaction(t *testing.T) {

t.Parallel()

const limit = 1000
const limit = 90

b, adapter := setupTransactionTests(
t,
Expand All @@ -1774,16 +1774,78 @@ func TestInfiniteTransaction(t *testing.T) {
)

const code = `
pub fun test() {
test()
}
pub fun test() {
test()
}

transaction {
execute {
test()
}
}
`
transaction {
execute {
test()
}
}
`

// Create a new account

accountKeys := test.AccountKeyGenerator()
accountKey, signer := accountKeys.NewWithSigner()
accountAddress, err := adapter.CreateAccount(context.Background(), []*flowsdk.AccountKey{accountKey}, nil)
assert.NoError(t, err)

// Sign the transaction using the new account.
// Do not test using the service account,
// as the computation limit is disabled for it

tx := flowsdk.NewTransaction().
SetScript([]byte(code)).
SetGasLimit(limit).
SetProposalKey(accountAddress, 0, 0).
SetPayer(accountAddress)

err = tx.SignEnvelope(accountAddress, 0, signer)
assert.NoError(t, err)

// Submit tx
err = adapter.SendTransaction(context.Background(), *tx)
assert.NoError(t, err)

// Execute tx
result, err := b.ExecuteNextTransaction()
assert.NoError(t, err)

require.True(t, fvmerrors.IsComputationLimitExceededError(result.Error))
}

func TestTransactionWithExceedingComputationLimit(t *testing.T) {

t.Parallel()

const limit = 2000

b, adapter := setupTransactionTests(
t,
emulator.WithStorageLimitEnabled(false),
emulator.WithTransactionMaxGasLimit(limit),
)

const code = `
transaction {
execute {
var s: Int256 = 1024102410241024
var i: Int256 = 0
var a: Int256 = 7
var b: Int256 = 5
var c: Int256 = 2

while i < 150000 {
s = s * a
s = s / b
s = s / c
i = i + 1
}
}
}
`

// Create a new account

Expand Down Expand Up @@ -1816,6 +1878,67 @@ func TestInfiniteTransaction(t *testing.T) {
require.True(t, fvmerrors.IsComputationLimitExceededError(result.Error))
}

func TestTransactionWithSufficientComputationLimit(t *testing.T) {

t.Parallel()

const limit = 19000

b, adapter := setupTransactionTests(
t,
emulator.WithStorageLimitEnabled(false),
emulator.WithTransactionMaxGasLimit(limit),
)

const code = `
transaction {
execute {
var s: Int256 = 1024102410241024
var i: Int256 = 0
var a: Int256 = 7
var b: Int256 = 5
var c: Int256 = 2

while i < 150000 {
s = s * a
s = s / b
s = s / c
i = i + 1
}
}
}
`

// Create a new account

accountKeys := test.AccountKeyGenerator()
accountKey, signer := accountKeys.NewWithSigner()
accountAddress, err := adapter.CreateAccount(context.Background(), []*flowsdk.AccountKey{accountKey}, nil)
assert.NoError(t, err)

// Sign the transaction using the new account.
// Do not test using the service account,
// as the computation limit is disabled for it

tx := flowsdk.NewTransaction().
SetScript([]byte(code)).
SetGasLimit(limit).
SetProposalKey(accountAddress, 0, 0).
SetPayer(accountAddress)

err = tx.SignEnvelope(accountAddress, 0, signer)
assert.NoError(t, err)

// Submit tx
err = adapter.SendTransaction(context.Background(), *tx)
assert.NoError(t, err)

// Execute tx
result, err := b.ExecuteNextTransaction()
assert.NoError(t, err)
assert.NoError(t, result.Error)
}

func TestSubmitTransactionWithCustomLogger(t *testing.T) {

t.Parallel()
Expand Down
4 changes: 4 additions & 0 deletions utils/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ func PrintScriptResult(logger *zerolog.Logger, result *types.ScriptResult) {
logger.Debug().
Str("scriptID", result.ScriptID.String()).
Uint64("computationUsed", result.ComputationUsed).
Uint64("memoryEstimate", result.MemoryEstimate).
Msg("⭐ Script executed")
} else {
logger.Warn().
Str("scriptID", result.ScriptID.String()).
Uint64("computationUsed", result.ComputationUsed).
Uint64("memoryEstimate", result.MemoryEstimate).
Msg("❗ Script reverted")
}

Expand All @@ -54,11 +56,13 @@ func PrintTransactionResult(logger *zerolog.Logger, result *types.TransactionRes
logger.Debug().
Str("txID", result.TransactionID.String()).
Uint64("computationUsed", result.ComputationUsed).
Uint64("memoryEstimate", result.MemoryEstimate).
Msg("⭐ Transaction executed")
} else {
logger.Warn().
Str("txID", result.TransactionID.String()).
Uint64("computationUsed", result.ComputationUsed).
Uint64("memoryEstimate", result.MemoryEstimate).
Msg("❗ Transaction reverted")
}

Expand Down