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 all 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
63 changes: 58 additions & 5 deletions emulator/script_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,19 +157,72 @@ 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() {
main()
}
`
result, err := b.ExecuteScript([]byte(code), nil)
require.NoError(t, err)

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

func TestScriptExecutionLimit(t *testing.T) {

t.Parallel()

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
}
}
`

t.Run("ExceedingLimit", func(t *testing.T) {

t.Parallel()

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

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

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

t.Run("SufficientLimit", func(t *testing.T) {

t.Parallel()

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

result, err := b.ExecuteScript([]byte(code), nil)
require.NoError(t, err)
require.NoError(t, result.Error)
})
}
129 changes: 119 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,16 @@ 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

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

func TestTransactionExecutionLimit(t *testing.T) {

t.Parallel()

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
}
}
}
`

t.Run("ExceedingLimit", func(t *testing.T) {

t.Parallel()

const limit = 2000

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

// 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))
})

t.Run("SufficientLimit", func(t *testing.T) {

t.Parallel()

const limit = 19000

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

// 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