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

[FVM] beyond EVM part 6.2 - Implement EVM.BridgedAccount.withdraw #4890

Merged
merged 8 commits into from
Nov 14, 2023
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
5 changes: 4 additions & 1 deletion fvm/evm/emulator/emulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,11 @@ func (proc *procedure) withdrawFrom(address types.Address, amount *big.Int) (*ty
// while this method is only called from bridged accounts
// it might be the case that someone creates a bridged account
// and never transfer tokens to and call for withdraw
// TODO: we might revisit this apporach and
// return res, types.ErrAccountDoesNotExist
// instead
if !proc.state.Exist(addr) {
return res, types.ErrAccountDoesNotExist
proc.state.CreateAccount(addr)
}

// check the source account balance
Expand Down
4 changes: 3 additions & 1 deletion fvm/evm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package evm

import (
"github.com/onflow/cadence/runtime"
"github.com/onflow/cadence/runtime/common"

"github.com/onflow/flow-go/fvm/environment"
evm "github.com/onflow/flow-go/fvm/evm/emulator"
Expand All @@ -21,6 +22,7 @@ func SetupEnvironment(
backend types.Backend,
env runtime.Environment,
service flow.Address,
flowToken flow.Address,
) error {
// TODO: setup proper root address based on chainID
evmRootAddress, err := RootAccountAddress(chainID)
Expand All @@ -45,7 +47,7 @@ func SetupEnvironment(
return err
}

contractHandler := handler.NewContractHandler(bs, aa, backend, em)
contractHandler := handler.NewContractHandler(common.Address(flowToken), bs, aa, backend, em)

stdlib.SetupEnvironment(env, contractHandler, service)

Expand Down
51 changes: 50 additions & 1 deletion fvm/evm/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func RunWithNewTestVM(t *testing.T, chain flow.Chain, f func(fvm.Context, fvm.VM
f(fvm.NewContextFromParent(ctx, fvm.WithEVMEnabled(true)), vm, snapshotTree)
}

// TODO: test with actual amount
// TODO: deposit non-zero amount
func TestEVMAddressDeposit(t *testing.T) {

t.Parallel()
Expand Down Expand Up @@ -156,3 +156,52 @@ func TestEVMAddressDeposit(t *testing.T) {
})
})
}

// TODO: deposit non-zero amount
func TestBridgedAccountWithdraw(t *testing.T) {

t.Parallel()

RunWithTestBackend(t, func(backend types.Backend) {
RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
RunWithDeployedContract(t, backend, rootAddr, func(testContract *TestContract) {
RunWithEOATestAccount(t, backend, rootAddr, func(testAccount *EOATestAccount) {
chain := flow.Emulator.Chain()
RunWithNewTestVM(t, chain, func(ctx fvm.Context, vm fvm.VM, snapshot snapshot.SnapshotTree) {

code := []byte(fmt.Sprintf(
`
import EVM from %s
import FlowToken from %s

access(all)
fun main(): UFix64 {
let bridgedAccount <- EVM.createBridgedAccount()
let vault <- bridgedAccount.withdraw(balance: EVM.Balance(flow: 0.0))
let balance = vault.balance
destroy bridgedAccount
destroy vault
return balance
}
`,
chain.ServiceAddress().HexWithPrefix(),
fvm.FlowTokenAddress(chain).HexWithPrefix(),
))

script := fvm.Script(code)

executionSnapshot, output, err := vm.Run(
ctx,
script,
snapshot)
require.NoError(t, err)
require.NoError(t, output.Err)

// TODO:
_ = executionSnapshot
})
})
})
})
})
}
11 changes: 10 additions & 1 deletion fvm/evm/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/onflow/cadence/runtime/common"

"github.com/onflow/flow-go/fvm/environment"
"github.com/onflow/flow-go/fvm/errors"
Expand All @@ -22,21 +23,28 @@ import (
// in the future we might benefit from a view style of access to db passed as
// a param to the emulator.
type ContractHandler struct {
flowTokenAddress common.Address
blockstore types.BlockStore
addressAllocator types.AddressAllocator
backend types.Backend
emulator types.Emulator
addressAllocator types.AddressAllocator
}

func (h *ContractHandler) FlowTokenAddress() common.Address {
return h.flowTokenAddress
}

var _ types.ContractHandler = &ContractHandler{}

func NewContractHandler(
flowTokenAddress common.Address,
blockstore types.BlockStore,
addressAllocator types.AddressAllocator,
backend types.Backend,
emulator types.Emulator,
) *ContractHandler {
return &ContractHandler{
flowTokenAddress: flowTokenAddress,
blockstore: blockstore,
addressAllocator: addressAllocator,
backend: backend,
Expand Down Expand Up @@ -131,6 +139,7 @@ func (h *ContractHandler) emitEvent(event *types.Event) {
// TODO add extra metering for rlp encoding
encoded, err := event.Payload.Encode()
handleError(err)

err = h.backend.EmitFlowEvent(event.Etype, encoded)
handleError(err)
}
Expand Down
64 changes: 50 additions & 14 deletions fvm/evm/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import (
gethTypes "github.com/ethereum/go-ethereum/core/types"
gethParams "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/onflow/cadence/runtime/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/onflow/flow-go/fvm"
"github.com/onflow/flow-go/fvm/errors"
"github.com/onflow/flow-go/fvm/evm/emulator"
"github.com/onflow/flow-go/fvm/evm/emulator/database"
Expand All @@ -25,10 +27,14 @@ import (

// TODO add test for fatal errors

var flowTokenAddress = common.Address(fvm.FlowTokenAddress(flow.Emulator.Chain()))

func TestHandler_TransactionRun(t *testing.T) {
t.Parallel()

t.Run("test - transaction run (happy case)", func(t *testing.T) {
t.Parallel()

testutils.RunWithTestBackend(t, func(backend types.Backend) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
Expand All @@ -55,8 +61,7 @@ func TestHandler_TransactionRun(t *testing.T) {
return result, nil
},
}

handler := handler.NewContractHandler(bs, aa, backend, em)
handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)

coinbase := types.NewAddress(gethCommon.Address{})

Expand Down Expand Up @@ -104,6 +109,8 @@ func TestHandler_TransactionRun(t *testing.T) {
})

t.Run("test - transaction run (unhappy cases)", func(t *testing.T) {
t.Parallel()

testutils.RunWithTestBackend(t, func(backend types.Backend) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
Expand All @@ -119,7 +126,7 @@ func TestHandler_TransactionRun(t *testing.T) {
return &types.Result{}, types.NewEVMExecutionError(fmt.Errorf("some sort of error"))
},
}
handler := handler.NewContractHandler(bs, aa, backend, em)
handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)

coinbase := types.NewAddress(gethCommon.Address{})

Expand Down Expand Up @@ -164,6 +171,8 @@ func TestHandler_TransactionRun(t *testing.T) {
})

t.Run("test running transaction (with integrated emulator)", func(t *testing.T) {
t.Parallel()

testutils.RunWithTestBackend(t, func(backend types.Backend) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {

Expand All @@ -178,7 +187,7 @@ func TestHandler_TransactionRun(t *testing.T) {

emulator := emulator.NewEmulator(db)

handler := handler.NewContractHandler(bs, aa, backend, emulator)
handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, emulator)

eoa := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex)

Expand Down Expand Up @@ -230,6 +239,8 @@ func TestHandler_OpsWithoutEmulator(t *testing.T) {
t.Parallel()

t.Run("test last executed block call", func(t *testing.T) {
t.Parallel()

testutils.RunWithTestBackend(t, func(backend types.Backend) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
bs, err := handler.NewBlockStore(backend, rootAddr)
Expand All @@ -242,7 +253,8 @@ func TestHandler_OpsWithoutEmulator(t *testing.T) {
require.NoError(t, err)
emulator := emulator.NewEmulator(db)

handler := handler.NewContractHandler(bs, aa, backend, emulator)
handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, emulator)

// test call last executed block without initialization
b := handler.LastExecutedBlock()
require.Equal(t, types.GenesisBlock, b)
Expand All @@ -262,6 +274,8 @@ func TestHandler_OpsWithoutEmulator(t *testing.T) {
})

t.Run("test address allocation", func(t *testing.T) {
t.Parallel()

testutils.RunWithTestBackend(t, func(backend types.Backend) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
blockchain, err := handler.NewBlockStore(backend, rootAddr)
Expand All @@ -270,7 +284,8 @@ func TestHandler_OpsWithoutEmulator(t *testing.T) {
aa, err := handler.NewAddressAllocator(backend, rootAddr)
require.NoError(t, err)

handler := handler.NewContractHandler(blockchain, aa, backend, nil)
handler := handler.NewContractHandler(flowTokenAddress, blockchain, aa, backend, nil)

foa := handler.AllocateAddress()
require.NotNil(t, foa)

Expand All @@ -284,6 +299,8 @@ func TestHandler_OpsWithoutEmulator(t *testing.T) {
func TestHandler_BridgedAccount(t *testing.T) {

t.Run("test deposit/withdraw (with integrated emulator)", func(t *testing.T) {
t.Parallel()

testutils.RunWithTestBackend(t, func(backend types.Backend) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
bs, err := handler.NewBlockStore(backend, rootAddr)
Expand All @@ -297,7 +314,8 @@ func TestHandler_BridgedAccount(t *testing.T) {

emulator := emulator.NewEmulator(db)

handler := handler.NewContractHandler(bs, aa, backend, emulator)
handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, emulator)

foa := handler.AccountByAddress(handler.AllocateAddress(), true)
require.NotNil(t, foa)

Expand Down Expand Up @@ -360,6 +378,8 @@ func TestHandler_BridgedAccount(t *testing.T) {
})

t.Run("test withdraw (unhappy case)", func(t *testing.T) {
t.Parallel()

testutils.RunWithTestBackend(t, func(backend types.Backend) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
Expand All @@ -372,7 +392,8 @@ func TestHandler_BridgedAccount(t *testing.T) {
// Withdraw calls are only possible within FOA accounts
assertPanic(t, types.IsAUnAuthroizedMethodCallError, func() {
em := &testutils.TestEmulator{}
handler := handler.NewContractHandler(bs, aa, backend, em)

handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)

account := handler.AccountByAddress(testutils.RandomAddress(t), false)
account.Withdraw(types.Balance(1))
Expand All @@ -385,8 +406,10 @@ func TestHandler_BridgedAccount(t *testing.T) {
return &types.Result{}, types.NewEVMExecutionError(fmt.Errorf("some sort of error"))
},
}
handler := handler.NewContractHandler(bs, aa, backend, em)

handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
account := handler.AccountByAddress(testutils.RandomAddress(t), true)

account.Withdraw(types.Balance(1))
})

Expand All @@ -397,8 +420,10 @@ func TestHandler_BridgedAccount(t *testing.T) {
return &types.Result{}, types.NewEVMExecutionError(fmt.Errorf("some sort of error"))
},
}
handler := handler.NewContractHandler(bs, aa, backend, em)

handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
account := handler.AccountByAddress(testutils.RandomAddress(t), true)

account.Withdraw(types.Balance(0))
})

Expand All @@ -409,8 +434,10 @@ func TestHandler_BridgedAccount(t *testing.T) {
return &types.Result{}, types.NewFatalError(fmt.Errorf("some sort of fatal error"))
},
}
handler := handler.NewContractHandler(bs, aa, backend, em)

handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
account := handler.AccountByAddress(testutils.RandomAddress(t), true)

account.Withdraw(types.Balance(0))
})
})
Expand All @@ -419,6 +446,8 @@ func TestHandler_BridgedAccount(t *testing.T) {
})

t.Run("test deposit (unhappy case)", func(t *testing.T) {
t.Parallel()

testutils.RunWithTestBackend(t, func(backend types.Backend) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {
Expand All @@ -435,8 +464,10 @@ func TestHandler_BridgedAccount(t *testing.T) {
return &types.Result{}, types.NewEVMExecutionError(fmt.Errorf("some sort of error"))
},
}
handler := handler.NewContractHandler(bs, aa, backend, em)

handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
account := handler.AccountByAddress(testutils.RandomAddress(t), true)

account.Deposit(types.NewFlowTokenVault(1))
})

Expand All @@ -447,8 +478,10 @@ func TestHandler_BridgedAccount(t *testing.T) {
return &types.Result{}, types.NewFatalError(fmt.Errorf("some sort of fatal error"))
},
}
handler := handler.NewContractHandler(bs, aa, backend, em)

handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
account := handler.AccountByAddress(testutils.RandomAddress(t), true)

account.Deposit(types.NewFlowTokenVault(1))
})
})
Expand All @@ -457,6 +490,8 @@ func TestHandler_BridgedAccount(t *testing.T) {
})

t.Run("test deploy/call (with integrated emulator)", func(t *testing.T) {
t.Parallel()

// TODO update this test with events, gas metering, etc
testutils.RunWithTestBackend(t, func(backend types.Backend) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
Expand All @@ -471,7 +506,8 @@ func TestHandler_BridgedAccount(t *testing.T) {

emulator := emulator.NewEmulator(db)

handler := handler.NewContractHandler(bs, aa, backend, emulator)
handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, emulator)

foa := handler.AccountByAddress(handler.AllocateAddress(), true)
require.NotNil(t, foa)

Expand Down
Loading
Loading