Skip to content

Commit

Permalink
Merge pull request onflow#6252 from onflow/ramtin/evm-code-cleanup-an…
Browse files Browse the repository at this point in the history
…d-doc-improvement-part2

[Flow EVM] code and doc clean up
  • Loading branch information
ramtinms authored Aug 2, 2024
2 parents e66fe24 + 0324a9f commit 8c89495
Show file tree
Hide file tree
Showing 18 changed files with 420 additions and 318 deletions.
15 changes: 15 additions & 0 deletions fvm/evm/emulator/emulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ func (bl *BlockView) RunTransaction(
func (bl *BlockView) BatchRunTransactions(txs []*gethTypes.Transaction) ([]*types.Result, error) {
batchResults := make([]*types.Result, len(txs))

// create a new procedure
proc, err := bl.newProcedure()
if err != nil {
return nil, err
Expand Down Expand Up @@ -233,13 +234,15 @@ func (bl *BlockView) BatchRunTransactions(txs []*gethTypes.Transaction) ([]*type
if err != nil {
return nil, err
}

// all commit errors (StateDB errors) has to be returned
if err := proc.commit(false); err != nil {
return nil, err
}

// this clears state for any subsequent transaction runs
proc.state.Reset()

// collect result
batchResults[i] = res
}
Expand All @@ -258,10 +261,14 @@ func (bl *BlockView) DryRunTransaction(
from gethCommon.Address,
) (*types.Result, error) {
var txResult *types.Result

// create a new procedure
proc, err := bl.newProcedure()
if err != nil {
return nil, err
}

// convert tx into message
msg, err := gethCore.TransactionToMessage(
tx,
GetSigner(bl.config),
Expand Down Expand Up @@ -398,6 +405,13 @@ func (proc *procedure) withdrawFrom(
types.ErrInvalidBalance), nil
}

// check balance is not prone to rounding error
if types.BalanceConversionToUFix64ProneToRoundingError(call.Value) {
return types.NewInvalidResult(
call.Transaction(),
types.ErrWithdrawBalanceRounding), nil
}

// create bridge account if not exist
bridge := call.To.ToCommon()
if !proc.state.Exist(bridge) {
Expand Down Expand Up @@ -619,6 +633,7 @@ func (proc *procedure) run(
// if the block gas limit is set to anything than max
// we need to update this code.
gasPool := (*gethCore.GasPool)(&proc.config.BlockContext.GasLimit)

// transit the state
execResult, err := gethCore.NewStateTransition(
proc.evm,
Expand Down
137 changes: 93 additions & 44 deletions fvm/evm/emulator/emulator_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package emulator_test

import (
"encoding/hex"
"encoding/json"
"fmt"
"math"
Expand Down Expand Up @@ -45,10 +46,16 @@ func RunWithNewReadOnlyBlockView(t testing.TB, em *emulator.Emulator, f func(blk
f(blk)
}

func requireSuccessfulExecution(t testing.TB, err error, res *types.Result) {
require.NoError(t, err)
require.NoError(t, res.VMError)
require.NoError(t, res.ValidationError)
}

func TestNativeTokenBridging(t *testing.T) {
testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
originalBalance := big.NewInt(10000)
originalBalance := types.MakeBigIntInFlow(3)
testAccount := types.NewAddressFromString("test")
bridgeAccount := types.NewAddressFromString("bridge")
testAccountNonce := uint64(0)
Expand All @@ -58,7 +65,7 @@ func TestNativeTokenBridging(t *testing.T) {
RunWithNewBlockView(t, env, func(blk types.BlockView) {
call := types.NewDepositCall(bridgeAccount, testAccount, originalBalance, 0)
res, err := blk.DirectCall(call)
require.NoError(t, err)
requireSuccessfulExecution(t, err, res)
require.Equal(t, defaultCtx.DirectCallBaseGasUsage, res.GasConsumed)
require.Equal(t, call.Hash(), res.TxHash)
})
Expand All @@ -68,16 +75,46 @@ func TestNativeTokenBridging(t *testing.T) {
retBalance, err := blk.BalanceOf(testAccount)
require.NoError(t, err)
require.Equal(t, originalBalance, retBalance)
// check balance of bridgeAccount to be zero

// check balance of bridgeAccount to be zero
retBalance, err = blk.BalanceOf(bridgeAccount)
require.NoError(t, err)
require.Equal(t, big.NewInt(0).Uint64(), retBalance.Uint64())
})
})
})
t.Run("tokens deposit to an smart contract that doesn't accept native token", func(t *testing.T) {
var testContract types.Address
// deploy contract
RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
RunWithNewBlockView(t, env, func(blk types.BlockView) {
emptyContractByteCode, err := hex.DecodeString("6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea2646970667358221220093c3754c634ed147652afc2e8c4a2336be5c37cbc733839668aa5a11e713e6e64736f6c634300081a0033")
require.NoError(t, err)
call := types.NewDeployCall(
bridgeAccount,
emptyContractByteCode,
100_000,
big.NewInt(0),
1)
res, err := blk.DirectCall(call)
requireSuccessfulExecution(t, err, res)
testContract = *res.DeployedContractAddress
})
})

RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
RunWithNewBlockView(t, env, func(blk types.BlockView) {
call := types.NewDepositCall(bridgeAccount, testContract, types.MakeBigIntInFlow(1), 0)
res, err := blk.DirectCall(call)
require.NoError(t, err)
require.NoError(t, res.ValidationError)
require.Equal(t, res.VMError, gethVM.ErrExecutionReverted)
})
})
})

t.Run("tokens withdraw", func(t *testing.T) {
amount := big.NewInt(1000)
amount := types.OneFlow()
RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) {
retBalance, err := blk.BalanceOf(testAccount)
Expand All @@ -92,7 +129,7 @@ func TestNativeTokenBridging(t *testing.T) {
RunWithNewBlockView(t, env, func(blk types.BlockView) {
call := types.NewWithdrawCall(bridgeAccount, testAccount, amount, testAccountNonce)
res, err := blk.DirectCall(call)
require.NoError(t, err)
requireSuccessfulExecution(t, err, res)
require.Equal(t, defaultCtx.DirectCallBaseGasUsage, res.GasConsumed)
require.Equal(t, call.Hash(), res.TxHash)
testAccountNonce += 1
Expand All @@ -115,8 +152,33 @@ func TestNativeTokenBridging(t *testing.T) {
})
})
})
// TODO: add test for deploying to an empty contract (error on deposit call)
// and a test case for when not enough balance on account for withdraw
t.Run("tokens withdraw that results in rounding error", func(t *testing.T) {
RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
RunWithNewBlockView(t, env, func(blk types.BlockView) {
call := types.NewWithdrawCall(bridgeAccount, testAccount, big.NewInt(1000), testAccountNonce)
res, err := blk.DirectCall(call)
require.NoError(t, err)
require.Equal(t, res.ValidationError, types.ErrWithdrawBalanceRounding)
testAccountNonce += 1
})
})
})

t.Run("tokens withdraw not having enough balance", func(t *testing.T) {
RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
RunWithNewBlockView(t, env, func(blk types.BlockView) {
call := types.NewWithdrawCall(bridgeAccount, testAccount, types.MakeBigIntInFlow(3), testAccountNonce)
res, err := blk.DirectCall(call)
require.NoError(t, err)
require.True(t,
strings.Contains(
res.ValidationError.Error(),
"insufficient funds",
),
)
})
})
})
})
})
}
Expand Down Expand Up @@ -155,7 +217,7 @@ func TestContractInteraction(t *testing.T) {
amountToBeTransfered,
testAccountNonce)
res, err := blk.DirectCall(call)
require.NoError(t, err)
requireSuccessfulExecution(t, err, res)
require.NotNil(t, res.DeployedContractAddress)
contractAddr = *res.DeployedContractAddress
require.Equal(t, call.Hash(), res.TxHash)
Expand Down Expand Up @@ -196,7 +258,7 @@ func TestContractInteraction(t *testing.T) {
testAccountNonce,
),
)
require.NoError(t, err)
requireSuccessfulExecution(t, err, res)
require.GreaterOrEqual(t, res.GasConsumed, uint64(40_000))
testAccountNonce += 1
require.Empty(t, res.PrecompiledCalls)
Expand All @@ -215,7 +277,7 @@ func TestContractInteraction(t *testing.T) {
testAccountNonce,
),
)
require.NoError(t, err)
requireSuccessfulExecution(t, err, res)
testAccountNonce += 1

ret := new(big.Int).SetBytes(res.ReturnedData)
Expand All @@ -236,7 +298,7 @@ func TestContractInteraction(t *testing.T) {
testAccountNonce,
),
)
require.NoError(t, err)
requireSuccessfulExecution(t, err, res)
testAccountNonce += 1

ret := new(big.Int).SetBytes(res.ReturnedData)
Expand Down Expand Up @@ -277,6 +339,7 @@ func TestContractInteraction(t *testing.T) {
),
)
require.NoError(t, err)
require.NoError(t, res.ValidationError)
testAccountNonce += 1
require.Error(t, res.VMError)
strings.Contains(string(res.ReturnedData), "Value is too low")
Expand All @@ -297,7 +360,7 @@ func TestContractInteraction(t *testing.T) {
testAccountNonce,
),
)
require.NoError(t, err)
requireSuccessfulExecution(t, err, res)
testAccountNonce += 1

ret := new(big.Int).SetBytes(res.ReturnedData)
Expand Down Expand Up @@ -337,9 +400,7 @@ func TestContractInteraction(t *testing.T) {

)
res, err := blk.RunTransaction(tx)
require.NoError(t, err)
require.NoError(t, res.VMError)
require.NoError(t, res.ValidationError)
requireSuccessfulExecution(t, err, res)
require.Greater(t, res.GasConsumed, uint64(0))

// check the balance of coinbase
Expand Down Expand Up @@ -397,8 +458,7 @@ func TestContractInteraction(t *testing.T) {
results, err := blk.BatchRunTransactions(txs)
require.NoError(t, err)
for _, res := range results {
require.NoError(t, res.VMError)
require.NoError(t, res.ValidationError)
requireSuccessfulExecution(t, nil, res)
require.Greater(t, res.GasConsumed, uint64(0))
}

Expand All @@ -416,7 +476,7 @@ func TestContractInteraction(t *testing.T) {
})
})

t.Run("test runing transactions with dynamic fees (happy case)", func(t *testing.T) {
t.Run("test running transactions with dynamic fees (happy case)", func(t *testing.T) {
account := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex)
fAddr := account.Address()
RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
Expand Down Expand Up @@ -454,9 +514,7 @@ func TestContractInteraction(t *testing.T) {
account.SetNonce(account.Nonce() + 1)

res, err := blk.RunTransaction(tx)
require.NoError(t, err)
require.NoError(t, res.VMError)
require.NoError(t, res.ValidationError)
requireSuccessfulExecution(t, err, res)
require.Greater(t, res.GasConsumed, uint64(0))
})
})
Expand Down Expand Up @@ -621,8 +679,9 @@ func TestSelfdestruct(t *testing.T) {
// setup the test with funded account and deploying a selfdestruct contract.
RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) {
RunWithNewBlockView(t, env, func(blk types.BlockView) {
_, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, testAddress, startBalance, 0))
res, err := blk.DirectCall(types.NewDepositCall(bridgeAccount, testAddress, startBalance, 0))
require.NoError(t, err)
requireSuccessfulExecution(t, err, res)
})

RunWithNewBlockView(t, env, func(blk types.BlockView) {
Expand All @@ -634,7 +693,7 @@ func TestSelfdestruct(t *testing.T) {
deployBalance,
0),
)
require.NoError(t, err)
requireSuccessfulExecution(t, err, res)
require.NotNil(t, res.DeployedContractAddress)
contractAddr = *res.DeployedContractAddress
})
Expand All @@ -659,8 +718,7 @@ func TestSelfdestruct(t *testing.T) {
Value: big.NewInt(0),
GasLimit: 100_000,
})
require.NoError(t, err)
require.False(t, res.Failed())
requireSuccessfulExecution(t, err, res)
})

// after calling selfdestruct the balance should be returned to the caller and
Expand Down Expand Up @@ -944,9 +1002,7 @@ func TestTransactionTracing(t *testing.T) {
testAccount.Nonce(),
),
)
require.NoError(t, err)
require.NoError(t, res.ValidationError)
require.NoError(t, res.VMError)
requireSuccessfulExecution(t, err, res)
txID = res.TxHash
trace, err = tracer.TxTracer().GetResult()
require.NoError(t, err)
Expand Down Expand Up @@ -991,9 +1047,7 @@ func TestTransactionTracing(t *testing.T) {

// interact and record trace
res, err := blk.RunTransaction(tx)
require.NoError(t, err)
require.NoError(t, res.ValidationError)
require.NoError(t, res.VMError)
requireSuccessfulExecution(t, err, res)
txID = res.TxHash
trace, err = tracer.TxTracer().GetResult()
require.NoError(t, err)
Expand Down Expand Up @@ -1112,8 +1166,8 @@ func TestTxIndex(t *testing.T) {
ctx := types.NewDefaultBlockContext(blockNumber.Uint64())
expectedTxIndex := uint16(1)
ctx.TxCountSoFar = 1
testAccount1 := types.NewAddressFromString("test")
testAccount2 := types.NewAddressFromString("test")
testAccount1 := testutils.RandomAddress(t)
testAccount2 := testutils.RandomAddress(t)

blk, err := em.NewBlockView(ctx)
require.NoError(t, err)
Expand Down Expand Up @@ -1144,12 +1198,10 @@ func TestTxIndex(t *testing.T) {
types.NewDepositCall(
types.EmptyAddress,
fAddr,
types.OneFlow,
types.OneFlow(),
account.Nonce(),
))
require.NoError(t, err)
require.NoError(t, res.VMError)
require.NoError(t, res.ValidationError)
requireSuccessfulExecution(t, err, res)
require.Equal(t, expectedTxIndex, res.Index)
expectedTxIndex += 1
ctx.TxCountSoFar = 3
Expand All @@ -1161,15 +1213,13 @@ func TestTxIndex(t *testing.T) {
t,
testAccount1.ToCommon(), // to
nil, // data
big.NewInt(1000), // amount
big.NewInt(0), // amount
gethParams.TxGas, // gas limit
big.NewInt(0),
)

res, err = blk.RunTransaction(tx)
require.NoError(t, err)
require.NoError(t, res.VMError)
require.NoError(t, res.ValidationError)
requireSuccessfulExecution(t, err, res)
require.Equal(t, expectedTxIndex, res.Index)
expectedTxIndex += 1
ctx.TxCountSoFar = 4
Expand All @@ -1184,16 +1234,15 @@ func TestTxIndex(t *testing.T) {
t,
testAccount1.ToCommon(), // to
nil, // data
big.NewInt(1000), // amount
big.NewInt(0), // amount
gethParams.TxGas, // gas limit
big.NewInt(0),
)
}
results, err := blk.BatchRunTransactions(txs)
require.NoError(t, err)
for i, res := range results {
require.NoError(t, res.VMError)
require.NoError(t, res.ValidationError)
requireSuccessfulExecution(t, err, res)
require.Equal(t, expectedTxIndex+uint16(i), res.Index)
}
})
Expand Down
Loading

0 comments on commit 8c89495

Please sign in to comment.