From 7a1f18cc8f03457a79666a14038d92d51060ed01 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 18 Jan 2024 10:23:13 -0800 Subject: [PATCH 01/38] add deploy at functionality --- fvm/evm/emulator/emulator.go | 92 ++++++++++++++++++++++++++++++++---- fvm/evm/types/call.go | 43 +++++++++++++++-- 2 files changed, 124 insertions(+), 11 deletions(-) diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index 69ad180d043..f63d65cfcb6 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -3,11 +3,13 @@ package emulator import ( "math/big" + "github.com/ethereum/go-ethereum/common" gethCommon "github.com/ethereum/go-ethereum/common" gethCore "github.com/ethereum/go-ethereum/core" gethTypes "github.com/ethereum/go-ethereum/core/types" gethVM "github.com/ethereum/go-ethereum/core/vm" gethCrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "github.com/onflow/atree" "github.com/onflow/flow-go/fvm/evm/emulator/state" @@ -97,16 +99,19 @@ func (bl *BlockView) DirectCall(call *types.DirectCall) (*types.Result, error) { if err != nil { return nil, err } - var res *types.Result switch call.SubType { case types.DepositCallSubType: - res, err = proc.mintTo(call.To, call.Value) + return proc.mintTo(call.To, call.Value) case types.WithdrawCallSubType: - res, err = proc.withdrawFrom(call.From, call.Value) + return proc.withdrawFrom(call.From, call.Value) + case types.DeployCallSubType: + if call.HasNonEmptyToField() { + return proc.deployAt(call.From, call.To, call.Data, call.GasLimit) + } + fallthrough default: - res, err = proc.run(call.Message(), types.DirectCallTxType) + return proc.run(call.Message(), types.DirectCallTxType) } - return res, err } // RunTransaction runs an evm transaction @@ -206,9 +211,6 @@ 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) { proc.state.CreateAccount(addr) } @@ -230,6 +232,80 @@ func (proc *procedure) withdrawFrom(address types.Address, amount *big.Int) (*ty return res, proc.commit() } +// deployAt deploys a contract at the given target address +// behaviour should be similar to what evm.create internal method does with +// less checks on the configs (no call to this is possible on the old forks). +func (proc *procedure) deployAt( + from types.Address, + to types.Address, + data types.Code, + gasLimit uint64, +) (*types.Result, error) { + addr := to.ToCommon() + res := &types.Result{ + TxType: types.DirectCallTxType, + } + + // ensure there's no existing contract + contractHash := proc.state.GetCodeHash(addr) + if proc.state.GetNonce(addr) != 0 || (contractHash != (common.Hash{}) && contractHash != gethTypes.EmptyCodeHash) { + return res, gethVM.ErrContractAddressCollision + } + + proc.state.CreateAccount(addr) + // set the nonce to 1 (EIP-158) + proc.state.SetNonce(addr, 1) + + var err error + inter := gethVM.NewEVMInterpreter(proc.evm) + contract := gethVM.NewContract(gethVM.AccountRef(from), gethVM.AccountRef(addr), new(big.Int), gasLimit) + contract.Code = data + contract.CodeHash = gethCrypto.Keccak256Hash(data) + contract.CodeAddr = &addr + + // update access list (Berlin) + proc.state.AddAddressToAccessList(addr) + + // run through interpreter to catch error and compute gas + ret, err := inter.Run(contract, nil, false) + gasCost := uint64(len(ret)) * params.CreateDataGas + res.GasConsumed = gasCost + + // handle errors + if err != nil { + // for all errors except this one consume all the remaining gas (Homestead) + if err != gethVM.ErrExecutionReverted { + res.GasConsumed = gasLimit + } + return res, err + } + + // check gas usage + if gasCost > gasLimit { + // consume all the remaining gas (Homestead) + res.GasConsumed = gasLimit + return res, gethVM.ErrCodeStoreOutOfGas + } + + // check max code size (EIP-158) + if len(ret) > params.MaxCodeSize { + // consume all the remaining gas (Homestead) + res.GasConsumed = gasLimit + return res, gethVM.ErrMaxCodeSizeExceeded + } + + // reject code starting with 0xEF (EIP-3541) + if len(ret) >= 1 && ret[0] == 0xEF { + // consume all the remaining gas (Homestead) + res.GasConsumed = gasLimit + return res, gethVM.ErrInvalidCode + } + + proc.state.SetCode(addr, ret) + res.DeployedContractAddress = to + return res, proc.commit() +} + func (proc *procedure) run(msg *gethCore.Message, txType uint8) (*types.Result, error) { res := types.Result{ TxType: txType, diff --git a/fvm/evm/types/call.go b/fvm/evm/types/call.go index 31562fe9ccd..b1701c24359 100644 --- a/fvm/evm/types/call.go +++ b/fvm/evm/types/call.go @@ -52,7 +52,7 @@ func (dc *DirectCall) Hash() (gethCommon.Hash, error) { // Message constructs a core.Message from the direct call func (dc *DirectCall) Message() *gethCore.Message { var to *gethCommon.Address - if dc.To != EmptyAddress { + if dc.HasNonEmptyToField() { ct := dc.To.ToCommon() to = &ct } @@ -70,6 +70,11 @@ func (dc *DirectCall) Message() *gethCore.Message { } } +// HasNonEmptyTo returns true if to has a non empty value +func (dc *DirectCall) HasNonEmptyToField() bool { + return dc.To != EmptyAddress +} + func NewDepositCall(address Address, amount *big.Int) *DirectCall { return &DirectCall{ Type: DirectCallTxType, @@ -106,7 +111,12 @@ func NewTransferCall(from Address, to Address, amount *big.Int) *DirectCall { } } -func NewDeployCall(caller Address, code Code, gasLimit uint64, value *big.Int) *DirectCall { +func NewDeployCall( + caller Address, + code Code, + gasLimit uint64, + value *big.Int, +) *DirectCall { return &DirectCall{ Type: DirectCallTxType, SubType: DeployCallSubType, @@ -118,7 +128,34 @@ func NewDeployCall(caller Address, code Code, gasLimit uint64, value *big.Int) * } } -func NewContractCall(caller Address, to Address, data Data, gasLimit uint64, value *big.Int) *DirectCall { +// this subtype should only be used internally for +// deploying contracts at given addresses (e.g. COA account init setup) +// should not be used for other means. +func NewDeployCallWithTargetAddress( + caller Address, + to Address, + code Code, + gasLimit uint64, + value *big.Int, +) *DirectCall { + return &DirectCall{ + Type: DirectCallTxType, + SubType: DeployCallSubType, + From: caller, + To: to, + Data: code, + Value: value, + GasLimit: gasLimit, + } +} + +func NewContractCall( + caller Address, + to Address, + data Data, + gasLimit uint64, + value *big.Int, +) *DirectCall { return &DirectCall{ Type: DirectCallTxType, SubType: ContractCallSubType, From 2129f81c065e819a15c24fce14bf0e74373bb563 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 18 Jan 2024 11:12:45 -0800 Subject: [PATCH 02/38] update deployedAt --- fvm/evm/emulator/emulator.go | 45 ++++++++++++++++++++----------- fvm/evm/emulator/emulator_test.go | 31 +++++++++++++++++++++ 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index f63d65cfcb6..f6771b528b8 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -3,13 +3,12 @@ package emulator import ( "math/big" - "github.com/ethereum/go-ethereum/common" gethCommon "github.com/ethereum/go-ethereum/common" gethCore "github.com/ethereum/go-ethereum/core" gethTypes "github.com/ethereum/go-ethereum/core/types" gethVM "github.com/ethereum/go-ethereum/core/vm" gethCrypto "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/params" + gethParams "github.com/ethereum/go-ethereum/params" "github.com/onflow/atree" "github.com/onflow/flow-go/fvm/evm/emulator/state" @@ -106,7 +105,7 @@ func (bl *BlockView) DirectCall(call *types.DirectCall) (*types.Result, error) { return proc.withdrawFrom(call.From, call.Value) case types.DeployCallSubType: if call.HasNonEmptyToField() { - return proc.deployAt(call.From, call.To, call.Data, call.GasLimit) + return proc.deployAt(call.From, call.To, call.Data, call.GasLimit, call.Value) } fallthrough default: @@ -236,39 +235,51 @@ func (proc *procedure) withdrawFrom(address types.Address, amount *big.Int) (*ty // behaviour should be similar to what evm.create internal method does with // less checks on the configs (no call to this is possible on the old forks). func (proc *procedure) deployAt( - from types.Address, + caller types.Address, to types.Address, data types.Code, gasLimit uint64, + value *big.Int, ) (*types.Result, error) { - addr := to.ToCommon() res := &types.Result{ TxType: types.DirectCallTxType, } + addr := to.ToCommon() - // ensure there's no existing contract + // precheck 1 - check balance of the source + if value.Sign() != 0 && !proc.evm.Context.CanTransfer(proc.state, caller.ToCommon(), value) { + return res, gethVM.ErrInsufficientBalance + } + + // precheck 2 - ensure there's no existing contract is deployed at the address contractHash := proc.state.GetCodeHash(addr) - if proc.state.GetNonce(addr) != 0 || (contractHash != (common.Hash{}) && contractHash != gethTypes.EmptyCodeHash) { + if proc.state.GetNonce(addr) != 0 || (contractHash != (gethCommon.Hash{}) && contractHash != gethTypes.EmptyCodeHash) { return res, gethVM.ErrContractAddressCollision } + // setup account proc.state.CreateAccount(addr) - // set the nonce to 1 (EIP-158) - proc.state.SetNonce(addr, 1) + proc.state.SetNonce(addr, 1) // (EIP-158) + proc.evm.Context.Transfer( // transfer value + proc.state, + caller.ToCommon(), + addr, + value, + ) + // run code through interpreter + // this would check for errors and computes the final bytes to be stored under account var err error inter := gethVM.NewEVMInterpreter(proc.evm) - contract := gethVM.NewContract(gethVM.AccountRef(from), gethVM.AccountRef(addr), new(big.Int), gasLimit) + contract := gethVM.NewContract(gethVM.AccountRef(caller.ToCommon()), gethVM.AccountRef(addr), value, gasLimit) contract.Code = data contract.CodeHash = gethCrypto.Keccak256Hash(data) contract.CodeAddr = &addr - // update access list (Berlin) proc.state.AddAddressToAccessList(addr) - // run through interpreter to catch error and compute gas ret, err := inter.Run(contract, nil, false) - gasCost := uint64(len(ret)) * params.CreateDataGas + gasCost := uint64(len(ret)) * gethParams.CreateDataGas res.GasConsumed = gasCost // handle errors @@ -277,20 +288,23 @@ func (proc *procedure) deployAt( if err != gethVM.ErrExecutionReverted { res.GasConsumed = gasLimit } + res.Failed = true return res, err } - // check gas usage + // update gas usage if gasCost > gasLimit { // consume all the remaining gas (Homestead) res.GasConsumed = gasLimit + res.Failed = true return res, gethVM.ErrCodeStoreOutOfGas } // check max code size (EIP-158) - if len(ret) > params.MaxCodeSize { + if len(ret) > gethParams.MaxCodeSize { // consume all the remaining gas (Homestead) res.GasConsumed = gasLimit + res.Failed = true return res, gethVM.ErrMaxCodeSizeExceeded } @@ -298,6 +312,7 @@ func (proc *procedure) deployAt( if len(ret) >= 1 && ret[0] == 0xEF { // consume all the remaining gas (Homestead) res.GasConsumed = gasLimit + res.Failed = true return res, gethVM.ErrInvalidCode } diff --git a/fvm/evm/emulator/emulator_test.go b/fvm/evm/emulator/emulator_test.go index 3cca27b0906..bdbfb641ac2 100644 --- a/fvm/evm/emulator/emulator_test.go +++ b/fvm/evm/emulator/emulator_test.go @@ -285,6 +285,37 @@ func TestContractInteraction(t *testing.T) { require.True(t, types.IsEVMValidationError(err)) }) }) + t.Run("deploy contract at target address", func(t *testing.T) { + RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { + target := types.Address{1, 2, 3} + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall( + types.NewDeployCallWithTargetAddress( + testAccount, + target, + testContract.ByteCode, + math.MaxUint64, + amountToBeTransfered), + ) + require.NoError(t, err) + require.Equal(t, target, res.DeployedContractAddress) + }) + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + require.NotNil(t, target) + retCode, err := blk.CodeOf(target) + require.NoError(t, err) + require.NotEmpty(t, retCode) + + retBalance, err := blk.BalanceOf(target) + require.NoError(t, err) + require.Equal(t, amountToBeTransfered, retBalance) + + retBalance, err = blk.BalanceOf(testAccount) + require.NoError(t, err) + require.Equal(t, amount.Sub(amount, amountToBeTransfered), retBalance) + }) + }) + }) }) }) From 09f3e2239fff82106fd151eda7084fb8c9df53f1 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 18 Jan 2024 11:28:58 -0800 Subject: [PATCH 03/38] add more tests --- fvm/evm/emulator/emulator.go | 16 ++++++++--- fvm/evm/emulator/emulator_test.go | 45 ++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index f6771b528b8..ccfc5950696 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -233,7 +233,9 @@ func (proc *procedure) withdrawFrom(address types.Address, amount *big.Int) (*ty // deployAt deploys a contract at the given target address // behaviour should be similar to what evm.create internal method does with -// less checks on the configs (no call to this is possible on the old forks). +// a few diffrences, don't need to check for previous forks given this +// functionality was not available to anyone, we don't need to +// follow snapshoting, given we do commit/revert style in this code base. func (proc *procedure) deployAt( caller types.Address, to types.Address, @@ -247,13 +249,15 @@ func (proc *procedure) deployAt( addr := to.ToCommon() // precheck 1 - check balance of the source - if value.Sign() != 0 && !proc.evm.Context.CanTransfer(proc.state, caller.ToCommon(), value) { + if value.Sign() != 0 && + !proc.evm.Context.CanTransfer(proc.state, caller.ToCommon(), value) { return res, gethVM.ErrInsufficientBalance } // precheck 2 - ensure there's no existing contract is deployed at the address contractHash := proc.state.GetCodeHash(addr) - if proc.state.GetNonce(addr) != 0 || (contractHash != (gethCommon.Hash{}) && contractHash != gethTypes.EmptyCodeHash) { + if proc.state.GetNonce(addr) != 0 || + (contractHash != (gethCommon.Hash{}) && contractHash != gethTypes.EmptyCodeHash) { return res, gethVM.ErrContractAddressCollision } @@ -271,7 +275,11 @@ func (proc *procedure) deployAt( // this would check for errors and computes the final bytes to be stored under account var err error inter := gethVM.NewEVMInterpreter(proc.evm) - contract := gethVM.NewContract(gethVM.AccountRef(caller.ToCommon()), gethVM.AccountRef(addr), value, gasLimit) + contract := gethVM.NewContract( + gethVM.AccountRef(caller.ToCommon()), + gethVM.AccountRef(addr), + value, + gasLimit) contract.Code = data contract.CodeHash = gethCrypto.Keccak256Hash(data) contract.CodeAddr = &addr diff --git a/fvm/evm/emulator/emulator_test.go b/fvm/evm/emulator/emulator_test.go index bdbfb641ac2..7d6198e2d3b 100644 --- a/fvm/evm/emulator/emulator_test.go +++ b/fvm/evm/emulator/emulator_test.go @@ -285,6 +285,26 @@ func TestContractInteraction(t *testing.T) { require.True(t, types.IsEVMValidationError(err)) }) }) + }) + }) +} + +func TestDeployAtFunctionality(t *testing.T) { + testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { + testContract := testutils.GetStorageTestContract(t) + testAccount := types.NewAddressFromString("test") + amount := big.NewInt(0).Mul(big.NewInt(1337), big.NewInt(gethParams.Ether)) + amountToBeTransfered := big.NewInt(0).Mul(big.NewInt(100), big.NewInt(gethParams.Ether)) + + // fund test account + RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { + RunWithNewBlockView(t, env, func(blk types.BlockView) { + _, err := blk.DirectCall(types.NewDepositCall(testAccount, amount)) + require.NoError(t, err) + }) + }) + t.Run("deploy contract at target address", func(t *testing.T) { RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { target := types.Address{1, 2, 3} @@ -314,9 +334,32 @@ func TestContractInteraction(t *testing.T) { require.NoError(t, err) require.Equal(t, amount.Sub(amount, amountToBeTransfered), retBalance) }) + // test deployment to an address that is already exist + RunWithNewBlockView(t, env, func(blk types.BlockView) { + _, err := blk.DirectCall( + types.NewDeployCallWithTargetAddress( + testAccount, + target, + testContract.ByteCode, + math.MaxUint64, + amountToBeTransfered), + ) + require.Error(t, err) + }) + // test deployment with not enough gas + RunWithNewBlockView(t, env, func(blk types.BlockView) { + _, err := blk.DirectCall( + types.NewDeployCallWithTargetAddress( + testAccount, + types.Address{3, 4, 5}, + testContract.ByteCode, + 100, + new(big.Int)), + ) + require.Error(t, err) + }) }) }) - }) }) } From 7e0ad0e0322dcb1e67edd268b712a695f956e93d Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 18 Jan 2024 12:48:26 -0800 Subject: [PATCH 04/38] add DeployACOAAccount method --- fvm/evm/emulator/emulator.go | 11 +- fvm/evm/handler/contracts.go | 110 +++++++++++++++ fvm/evm/handler/handler.go | 164 ++++++++++++++-------- fvm/evm/handler/handler_benchmark_test.go | 2 +- fvm/evm/handler/handler_test.go | 27 ++-- fvm/evm/stdlib/contract.go | 2 +- fvm/evm/stdlib/contract_test.go | 20 ++- fvm/evm/testutils/emulator.go | 11 +- fvm/evm/types/account.go | 8 +- fvm/evm/types/emulator.go | 4 +- fvm/evm/types/handler.go | 4 +- 11 files changed, 275 insertions(+), 88 deletions(-) create mode 100644 fvm/evm/handler/contracts.go diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index ccfc5950696..0da024a6a5d 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -72,14 +72,19 @@ func (bv *ReadOnlyBlockView) BalanceOf(address types.Address) (*big.Int, error) return bv.state.GetBalance(address.ToCommon()), nil } +// NonceOf returns the nonce of the given address +func (bv *ReadOnlyBlockView) NonceOf(address types.Address) (uint64, error) { + return bv.state.GetNonce(address.ToCommon()), nil +} + // CodeOf returns the code of the given address func (bv *ReadOnlyBlockView) CodeOf(address types.Address) (types.Code, error) { return bv.state.GetCode(address.ToCommon()), nil } -// NonceOf returns the nonce of the given address -func (bv *ReadOnlyBlockView) NonceOf(address types.Address) (uint64, error) { - return bv.state.GetNonce(address.ToCommon()), nil +// CodeHashOf returns the code hash of the given address +func (bv *ReadOnlyBlockView) CodeHashOf(address types.Address) ([]byte, error) { + return bv.state.GetCodeHash(address.ToCommon()).Bytes(), nil } // BlockView allows mutation of the evm state as part of a block diff --git a/fvm/evm/handler/contracts.go b/fvm/evm/handler/contracts.go new file mode 100644 index 00000000000..4b16fc94f49 --- /dev/null +++ b/fvm/evm/handler/contracts.go @@ -0,0 +1,110 @@ +package handler + +// contract code: +// +// // SPDX-License-Identifier: UNLICENSED +// pragma solidity >=0.7.0 <0.9.0; +// interface IERC165 { +// function supportsInterface(bytes4 interfaceId) external view returns (bool); +// } +// interface ERC721TokenReceiver { +// function onERC721Received( +// address _operator, +// address _from, +// uint256 _tokenId, +// bytes calldata _data +// ) external returns (bytes4); +// } +// interface ERC777TokensRecipient { +// function tokensReceived( +// address operator, +// address from, +// address to, +// uint256 amount, +// bytes calldata data, +// bytes calldata operatorData +// ) external; +// } +// interface ERC1155TokenReceiver { +// function onERC1155Received( +// address _operator, +// address _from, +// uint256 _id, +// uint256 _value, +// bytes calldata _data +// ) external returns (bytes4); +// function onERC1155BatchReceived( +// address _operator, +// address _from, +// uint256[] calldata _ids, +// uint256[] calldata _values, +// bytes calldata _data +// ) external returns (bytes4); +// } +// contract COA is ERC1155TokenReceiver, ERC777TokensRecipient, ERC721TokenReceiver, IERC165 { +// address constant public cadenceArch = 0x0000000000000000000000010000000000000001; +// event FlowReceived(address indexed sender, uint256 value); +// receive() external payable { +// emit FlowReceived(msg.sender, msg.value); +// } +// function supportsInterface(bytes4 id) external view virtual override returns (bool) { +// return +// id == type(ERC1155TokenReceiver).interfaceId || +// id == type(ERC721TokenReceiver).interfaceId || +// id == type(ERC777TokensRecipient).interfaceId || +// id == type(IERC165).interfaceId; +// } +// +// function onERC1155Received( +// address, +// address, +// uint256, +// uint256, +// bytes calldata +// ) external pure override returns (bytes4) { +// return 0xf23a6e61; +// } +// +// function onERC1155BatchReceived( +// address, +// address, +// uint256[] calldata, +// uint256[] calldata, +// bytes calldata +// ) external pure override returns (bytes4) { +// return 0xbc197c81; +// } +// +// function onERC721Received( +// address, +// address, +// uint256, +// bytes calldata +// ) external pure override returns (bytes4) { +// return 0x150b7a02; +// } +// +// function tokensReceived( +// address, +// address, +// address, +// uint256, +// bytes calldata, +// bytes calldata +// ) external pure override {} +// +// function isValidSignature( +// bytes32 _hash, +// bytes memory _sig +// ) external view virtual returns (bytes4){ +// (bool ok, bytes memory data) = cadenceArch.staticcall(abi.encodeWithSignature("verifyCOAOwnershipProof(bytes32, bytes)", _hash, _sig)); +// require(ok); +// bool output = abi.decode(data, (bool)); +// if (output) { +// return 0x1626ba7e; +// } +// return 0xffffffff; +// } +// } + +var COAContractBytes = []byte("608060405234801561000f575f80fd5b50610e1e8061001d5f395ff3fe608060405260043610610072575f3560e01c80631626ba7e1161004d5780631626ba7e1461016b578063bc197c81146101a7578063d0d250bd146101e3578063f23a6e611461020d576100c7565b806223de29146100cb57806301ffc9a7146100f3578063150b7a021461012f576100c7565b366100c7573373ffffffffffffffffffffffffffffffffffffffff167fb496ce2863a72c02f90deb616c638c76948f3aff803140881c602775c05fdf3a346040516100bd91906105a5565b60405180910390a2005b5f80fd5b3480156100d6575f80fd5b506100f160048036038101906100ec91906106b4565b610249565b005b3480156100fe575f80fd5b50610119600480360381019061011491906107d3565b610253565b6040516101269190610818565b60405180910390f35b34801561013a575f80fd5b5061015560048036038101906101509190610831565b6103f3565b60405161016291906108c4565b60405180910390f35b348015610176575f80fd5b50610191600480360381019061018c9190610a48565b610407565b60405161019e91906108c4565b60405180910390f35b3480156101b2575f80fd5b506101cd60048036038101906101c89190610af7565b610554565b6040516101da91906108c4565b60405180910390f35b3480156101ee575f80fd5b506101f761056b565b6040516102049190610bdd565b60405180910390f35b348015610218575f80fd5b50610233600480360381019061022e9190610bf6565b610578565b60405161024091906108c4565b60405180910390f35b5050505050505050565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061031d57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061038457507e23de29000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806103ec57507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b5f63150b7a0260e01b905095945050505050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff16858560405160240161043d929190610d15565b6040516020818303038152906040527eb997bd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516104c69190610d7d565b5f60405180830381855afa9150503d805f81146104fe576040519150601f19603f3d011682016040523d82523d5f602084013e610503565b606091505b509150915081610511575f80fd5b5f818060200190518101906105269190610dbd565b9050801561054057631626ba7e60e01b935050505061054e565b63ffffffff60e01b93505050505b92915050565b5f63bc197c8160e01b905098975050505050505050565b6801000000000000000181565b5f63f23a6e6160e01b90509695505050505050565b5f819050919050565b61059f8161058d565b82525050565b5f6020820190506105b85f830184610596565b92915050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6105f8826105cf565b9050919050565b610608816105ee565b8114610612575f80fd5b50565b5f81359050610623816105ff565b92915050565b6106328161058d565b811461063c575f80fd5b50565b5f8135905061064d81610629565b92915050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261067457610673610653565b5b8235905067ffffffffffffffff81111561069157610690610657565b5b6020830191508360018202830111156106ad576106ac61065b565b5b9250929050565b5f805f805f805f8060c0898b0312156106d0576106cf6105c7565b5b5f6106dd8b828c01610615565b98505060206106ee8b828c01610615565b97505060406106ff8b828c01610615565b96505060606107108b828c0161063f565b955050608089013567ffffffffffffffff811115610731576107306105cb565b5b61073d8b828c0161065f565b945094505060a089013567ffffffffffffffff8111156107605761075f6105cb565b5b61076c8b828c0161065f565b92509250509295985092959890939650565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6107b28161077e565b81146107bc575f80fd5b50565b5f813590506107cd816107a9565b92915050565b5f602082840312156107e8576107e76105c7565b5b5f6107f5848285016107bf565b91505092915050565b5f8115159050919050565b610812816107fe565b82525050565b5f60208201905061082b5f830184610809565b92915050565b5f805f805f6080868803121561084a576108496105c7565b5b5f61085788828901610615565b955050602061086888828901610615565b94505060406108798882890161063f565b935050606086013567ffffffffffffffff81111561089a576108996105cb565b5b6108a68882890161065f565b92509250509295509295909350565b6108be8161077e565b82525050565b5f6020820190506108d75f8301846108b5565b92915050565b5f819050919050565b6108ef816108dd565b81146108f9575f80fd5b50565b5f8135905061090a816108e6565b92915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61095a82610914565b810181811067ffffffffffffffff8211171561097957610978610924565b5b80604052505050565b5f61098b6105be565b90506109978282610951565b919050565b5f67ffffffffffffffff8211156109b6576109b5610924565b5b6109bf82610914565b9050602081019050919050565b828183375f83830152505050565b5f6109ec6109e78461099c565b610982565b905082815260208101848484011115610a0857610a07610910565b5b610a138482856109cc565b509392505050565b5f82601f830112610a2f57610a2e610653565b5b8135610a3f8482602086016109da565b91505092915050565b5f8060408385031215610a5e57610a5d6105c7565b5b5f610a6b858286016108fc565b925050602083013567ffffffffffffffff811115610a8c57610a8b6105cb565b5b610a9885828601610a1b565b9150509250929050565b5f8083601f840112610ab757610ab6610653565b5b8235905067ffffffffffffffff811115610ad457610ad3610657565b5b602083019150836020820283011115610af057610aef61065b565b5b9250929050565b5f805f805f805f8060a0898b031215610b1357610b126105c7565b5b5f610b208b828c01610615565b9850506020610b318b828c01610615565b975050604089013567ffffffffffffffff811115610b5257610b516105cb565b5b610b5e8b828c01610aa2565b9650965050606089013567ffffffffffffffff811115610b8157610b806105cb565b5b610b8d8b828c01610aa2565b9450945050608089013567ffffffffffffffff811115610bb057610baf6105cb565b5b610bbc8b828c0161065f565b92509250509295985092959890939650565b610bd7816105ee565b82525050565b5f602082019050610bf05f830184610bce565b92915050565b5f805f805f8060a08789031215610c1057610c0f6105c7565b5b5f610c1d89828a01610615565b9650506020610c2e89828a01610615565b9550506040610c3f89828a0161063f565b9450506060610c5089828a0161063f565b935050608087013567ffffffffffffffff811115610c7157610c706105cb565b5b610c7d89828a0161065f565b92509250509295509295509295565b610c95816108dd565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610cd2578082015181840152602081019050610cb7565b5f8484015250505050565b5f610ce782610c9b565b610cf18185610ca5565b9350610d01818560208601610cb5565b610d0a81610914565b840191505092915050565b5f604082019050610d285f830185610c8c565b8181036020830152610d3a8184610cdd565b90509392505050565b5f81905092915050565b5f610d5782610c9b565b610d618185610d43565b9350610d71818560208601610cb5565b80840191505092915050565b5f610d888284610d4d565b915081905092915050565b610d9c816107fe565b8114610da6575f80fd5b50565b5f81519050610db781610d93565b92915050565b5f60208284031215610dd257610dd16105c7565b5b5f610ddf84828501610da9565b9150509291505056fea2646970667358221220c5a50c64c66e3bfb048a9c796b1fd390d35974be1136aed5e4c00cf2fc62c55164736f6c63430008160033") diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index acbfc1c173e..d2ebd1015af 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -2,6 +2,7 @@ package handler import ( "bytes" + "math/big" gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" @@ -52,10 +53,25 @@ func NewContractHandler( } } -// AllocateAddress allocates an address to be used by the bridged accounts -func (h *ContractHandler) AllocateAddress() types.Address { +// DeployACOAAccount deploys a cadence-owned-account and returns the address +func (h *ContractHandler) DeployACOAAccount() types.Address { target, err := h.addressAllocator.AllocateAddress() + gaslimit := types.GasLimit(30_000_000) // TODO figure out me handleError(err) + h.checkGasLimit(gaslimit) + + // TODO replace the caller + factory := types.Address{0, 0, 0, 0, 0, 1, 0, 0} + + call := types.NewDeployCallWithTargetAddress( + factory, + target, + COAContractBytes, + uint64(gaslimit), + new(big.Int), + ) + // TODO use the returned result + h.executeAndHandleCall(h.getBlockContext(), call, 0, false) return target } @@ -151,6 +167,58 @@ func (h *ContractHandler) getBlockContext() types.BlockContext { } } +func (h *ContractHandler) executeAndHandleCall( + ctx types.BlockContext, + call *types.DirectCall, + totalSupplyDiff uint64, + deductSupplyDiff bool, +) *types.Result { + // execute the call + blk, err := h.emulator.NewBlockView(ctx) + handleError(err) + + res, err := blk.DirectCall(call) + h.meterGasUsage(res) + handleError(err) + + // update block proposal + callHash, err := call.Hash() + if err != nil { + err = types.NewFatalError(err) + handleError(err) + } + + bp, err := h.blockstore.BlockProposal() + handleError(err) + bp.AppendTxHash(callHash) + if deductSupplyDiff { + bp.TotalSupply -= totalSupplyDiff + } else { + // TODO: add overflow errors (even though we might never get there) + bp.TotalSupply += totalSupplyDiff + } + + // emit events + encoded, err := call.Encode() + handleError(err) + + h.emitEvent( + types.NewTransactionExecutedEvent( + bp.Height, + encoded, + callHash, + res, + ), + ) + h.emitEvent(types.NewBlockExecutedEvent(bp)) + + // commit block proposal + err = h.blockstore.CommitBlockProposal() + handleError(err) + + return res +} + type Account struct { isAuthorized bool address types.Address @@ -166,12 +234,12 @@ func newAccount(fch *ContractHandler, addr types.Address, isAuthorized bool) *Ac } } -// Address returns the address associated with the bridged account +// Address returns the address associated with the account func (a *Account) Address() types.Address { return a.address } -// Balance returns the balance of this bridged account +// Balance returns the balance of this account // // TODO: we might need to meter computation for read only operations as well // currently the storage limits is enforced @@ -184,11 +252,37 @@ func (a *Account) Balance() types.Balance { bl, err := blk.BalanceOf(a.address) handleError(err) + // TODO: this might cause issues, we might need to update the + // way we represent balance balance, err := types.NewBalanceFromAttoFlow(bl) handleError(err) return balance } +// Code returns the code of this account +func (a *Account) Code() types.Code { + ctx := a.fch.getBlockContext() + + blk, err := a.fch.emulator.NewReadOnlyBlockView(ctx) + handleError(err) + + code, err := blk.CodeOf(a.address) + handleError(err) + return code +} + +// CodeHash returns the code hash of this account +func (a *Account) CodeHash() []byte { + ctx := a.fch.getBlockContext() + + blk, err := a.fch.emulator.NewReadOnlyBlockView(ctx) + handleError(err) + + code, err := blk.CodeHashOf(a.address) + handleError(err) + return code +} + // Deposit deposits the token from the given vault into the flow evm main vault // and update the account balance with the new amount func (a *Account) Deposit(v *types.FLOWTokenVault) { @@ -199,7 +293,7 @@ func (a *Account) Deposit(v *types.FLOWTokenVault) { a.address, v.Balance().ToAttoFlow(), ) - a.executeAndHandleCall(a.fch.getBlockContext(), call, v.Balance().ToAttoFlow().Uint64(), false) + a.fch.executeAndHandleCall(a.fch.getBlockContext(), call, v.Balance().ToAttoFlow().Uint64(), false) } // Withdraw deducts the balance from the account and @@ -221,7 +315,7 @@ func (a *Account) Withdraw(b types.Balance) *types.FLOWTokenVault { a.address, b.ToAttoFlow(), ) - a.executeAndHandleCall(a.fch.getBlockContext(), call, b.ToAttoFlow().Uint64(), true) + a.fch.executeAndHandleCall(a.fch.getBlockContext(), call, b.ToAttoFlow().Uint64(), true) return types.NewFlowTokenVault(b) } @@ -238,7 +332,7 @@ func (a *Account) Transfer(to types.Address, balance types.Balance) { to, balance.ToAttoFlow(), ) - a.executeAndHandleCall(ctx, call, 0, false) + a.fch.executeAndHandleCall(ctx, call, 0, false) } // Deploy deploys a contract to the EVM environment @@ -254,7 +348,7 @@ func (a *Account) Deploy(code types.Code, gaslimit types.GasLimit, balance types uint64(gaslimit), balance.ToAttoFlow(), ) - res := a.executeAndHandleCall(a.fch.getBlockContext(), call, 0, false) + res := a.fch.executeAndHandleCall(a.fch.getBlockContext(), call, 0, false) return types.Address(res.DeployedContractAddress) } @@ -272,62 +366,10 @@ func (a *Account) Call(to types.Address, data types.Data, gaslimit types.GasLimi uint64(gaslimit), balance.ToAttoFlow(), ) - res := a.executeAndHandleCall(a.fch.getBlockContext(), call, 0, false) + res := a.fch.executeAndHandleCall(a.fch.getBlockContext(), call, 0, false) return res.ReturnedValue } -func (a *Account) executeAndHandleCall( - ctx types.BlockContext, - call *types.DirectCall, - totalSupplyDiff uint64, - deductSupplyDiff bool, -) *types.Result { - // execute the call - blk, err := a.fch.emulator.NewBlockView(ctx) - handleError(err) - - res, err := blk.DirectCall(call) - a.fch.meterGasUsage(res) - handleError(err) - - // update block proposal - callHash, err := call.Hash() - if err != nil { - err = types.NewFatalError(err) - handleError(err) - } - - bp, err := a.fch.blockstore.BlockProposal() - handleError(err) - bp.AppendTxHash(callHash) - if deductSupplyDiff { - bp.TotalSupply -= totalSupplyDiff - } else { - // TODO: add overflow errors (even though we might never get there) - bp.TotalSupply += totalSupplyDiff - } - - // emit events - encoded, err := call.Encode() - handleError(err) - - a.fch.emitEvent( - types.NewTransactionExecutedEvent( - bp.Height, - encoded, - callHash, - res, - ), - ) - a.fch.emitEvent(types.NewBlockExecutedEvent(bp)) - - // commit block proposal - err = a.fch.blockstore.CommitBlockProposal() - handleError(err) - - return res -} - func (a *Account) checkAuthorized() { // check if account is authorized (i.e. is a bridged account) if !a.isAuthorized { diff --git a/fvm/evm/handler/handler_benchmark_test.go b/fvm/evm/handler/handler_benchmark_test.go index 6165b26c29d..c6e824217da 100644 --- a/fvm/evm/handler/handler_benchmark_test.go +++ b/fvm/evm/handler/handler_benchmark_test.go @@ -26,7 +26,7 @@ func benchmarkStorageGrowth(b *testing.B, accountCount, setupKittyCount int) { // setup several of accounts // note that trie growth is the function of number of accounts for i := 0; i < accountCount; i++ { - account := handler.AccountByAddress(handler.AllocateAddress(), true) + account := handler.AccountByAddress(handler.DeployACOAAccount(), true) account.Deposit(types.NewFlowTokenVault(types.Balance(100))) accounts[i] = account } diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 0acf3d3ceff..23715514580 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -197,7 +197,7 @@ func TestHandler_TransactionRun(t *testing.T) { eoa := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex) // deposit 1 Flow to the foa account - addr := handler.AllocateAddress() + addr := handler.DeployACOAAccount() orgBalance, err := types.NewBalanceFromAttoFlow(types.OneFlowInAttoFlow) require.NoError(t, err) vault := types.NewFlowTokenVault(orgBalance) @@ -224,7 +224,7 @@ func TestHandler_TransactionRun(t *testing.T) { ) // setup coinbase - foa2 := handler.AllocateAddress() + foa2 := handler.DeployACOAAccount() account2 := handler.AccountByAddress(foa2, true) require.Equal(t, types.Balance(0), account2.Balance()) @@ -268,24 +268,21 @@ func TestHandler_OpsWithoutEmulator(t *testing.T) { }) }) - t.Run("test address allocation", func(t *testing.T) { + t.Run("test coa deployment", func(t *testing.T) { t.Parallel() testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { - blockchain, err := handler.NewBlockStore(backend, rootAddr) - require.NoError(t, err) - - aa, err := handler.NewAddressAllocator(backend, rootAddr) - require.NoError(t, err) + h := SetupHandler(t, backend, rootAddr) - handler := handler.NewContractHandler(flowTokenAddress, blockchain, aa, backend, nil) - - foa := handler.AllocateAddress() - require.NotNil(t, foa) + coa := h.DeployACOAAccount() + require.NotNil(t, coa) expectedAddress := types.NewAddress(gethCommon.HexToAddress("0x00000000000000000001")) - require.Equal(t, expectedAddress, foa) + require.Equal(t, expectedAddress, coa) + + acc := h.AccountByAddress(coa, true) + require.Equal(t, handler.COAContractBytes, acc.Code()) }) }) }) @@ -300,7 +297,7 @@ func TestHandler_BridgedAccount(t *testing.T) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { handler := SetupHandler(t, backend, rootAddr) - foa := handler.AccountByAddress(handler.AllocateAddress(), true) + foa := handler.AccountByAddress(handler.DeployACOAAccount(), true) require.NotNil(t, foa) zeroBalance, err := types.NewBalanceFromAttoFlow(big.NewInt(0)) @@ -474,7 +471,7 @@ func TestHandler_BridgedAccount(t *testing.T) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { handler := SetupHandler(t, backend, rootAddr) - foa := handler.AccountByAddress(handler.AllocateAddress(), true) + foa := handler.AccountByAddress(handler.DeployACOAAccount(), true) require.NotNil(t, foa) // deposit 10000 flow diff --git a/fvm/evm/stdlib/contract.go b/fvm/evm/stdlib/contract.go index d7fe8401d59..7f796e7ac1e 100644 --- a/fvm/evm/stdlib/contract.go +++ b/fvm/evm/stdlib/contract.go @@ -1146,7 +1146,7 @@ func newInternalEVMTypeCreateBridgedAccountFunction( internalEVMTypeCreateBridgedAccountFunctionType, func(invocation interpreter.Invocation) interpreter.Value { inter := invocation.Interpreter - address := handler.AllocateAddress() + address := handler.DeployACOAAccount() return EVMAddressToAddressBytesArrayValue(inter, address) }, ) diff --git a/fvm/evm/stdlib/contract_test.go b/fvm/evm/stdlib/contract_test.go index 54a68488b6d..af955ca5a04 100644 --- a/fvm/evm/stdlib/contract_test.go +++ b/fvm/evm/stdlib/contract_test.go @@ -37,7 +37,7 @@ func (t *testContractHandler) FlowTokenAddress() common.Address { var _ types.ContractHandler = &testContractHandler{} -func (t *testContractHandler) AllocateAddress() types.Address { +func (t *testContractHandler) DeployACOAAccount() types.Address { if t.allocateAddress == nil { t.addressIndex++ var address types.Address @@ -71,6 +71,8 @@ func (t *testContractHandler) Run(tx []byte, coinbase types.Address) { type testFlowAccount struct { address types.Address balance func() types.Balance + code func() types.Code + codeHash func() []byte transfer func(address types.Address, balance types.Balance) deposit func(vault *types.FLOWTokenVault) withdraw func(balance types.Balance) *types.FLOWTokenVault @@ -91,6 +93,20 @@ func (t *testFlowAccount) Balance() types.Balance { return t.balance() } +func (t *testFlowAccount) Code() types.Code { + if t.balance == nil { + return types.Code{} + } + return t.code() +} + +func (t *testFlowAccount) CodeHash() []byte { + if t.codeHash == nil { + return nil + } + return t.codeHash() +} + func (t *testFlowAccount) Transfer(address types.Address, balance types.Balance) { if t.transfer == nil { panic("unexpected Transfer") @@ -3304,7 +3320,7 @@ func TestBridgedAccountDeploy(t *testing.T) { assert.Equal(t, types.GasLimit(9999), limit) assert.Equal(t, types.Balance(expectedBalance), balance) - return handler.AllocateAddress() + return handler.DeployACOAAccount() }, } }, diff --git a/fvm/evm/testutils/emulator.go b/fvm/evm/testutils/emulator.go index 5f7f2ce3068..8f52deef095 100644 --- a/fvm/evm/testutils/emulator.go +++ b/fvm/evm/testutils/emulator.go @@ -17,6 +17,7 @@ type TestEmulator struct { BalanceOfFunc func(address types.Address) (*big.Int, error) NonceOfFunc func(address types.Address) (uint64, error) CodeOfFunc func(address types.Address) (types.Code, error) + CodeHashOfFunc func(address types.Address) ([]byte, error) DirectCallFunc func(call *types.DirectCall) (*types.Result, error) RunTransactionFunc func(tx *gethTypes.Transaction) (*types.Result, error) } @@ -49,7 +50,7 @@ func (em *TestEmulator) NonceOf(address types.Address) (uint64, error) { return em.NonceOfFunc(address) } -// CodeOf returns the code for this address (if smart contract is deployed at this address) +// CodeOf returns the code for this address func (em *TestEmulator) CodeOf(address types.Address) (types.Code, error) { if em.CodeOfFunc == nil { panic("method not set") @@ -57,6 +58,14 @@ func (em *TestEmulator) CodeOf(address types.Address) (types.Code, error) { return em.CodeOfFunc(address) } +// CodeHashOf returns the code hash for this address +func (em *TestEmulator) CodeHashOf(address types.Address) ([]byte, error) { + if em.CodeHashOfFunc == nil { + panic("method not set") + } + return em.CodeHashOfFunc(address) +} + // DirectCall executes a direct call func (em *TestEmulator) DirectCall(call *types.DirectCall) (*types.Result, error) { if em.DirectCallFunc == nil { diff --git a/fvm/evm/types/account.go b/fvm/evm/types/account.go index 9e247fe1992..fc63786b986 100644 --- a/fvm/evm/types/account.go +++ b/fvm/evm/types/account.go @@ -15,9 +15,15 @@ type Account interface { // Returns the address of this account Address() Address - // Returns balance of this account + // Returns the balance of this account Balance() Balance + // Returns the code of this account + Code() Code + + // Returns the code hash of this account + CodeHash() []byte + // Deposit deposits the token from the given vault into this account Deposit(*FLOWTokenVault) diff --git a/fvm/evm/types/emulator.go b/fvm/evm/types/emulator.go index 73eef076a5b..177deba26e8 100644 --- a/fvm/evm/types/emulator.go +++ b/fvm/evm/types/emulator.go @@ -37,8 +37,10 @@ type ReadOnlyBlockView interface { BalanceOf(address Address) (*big.Int, error) // NonceOf returns the nonce of this address NonceOf(address Address) (uint64, error) - // CodeOf returns the code for this address (if smart contract is deployed at this address) + // CodeOf returns the code for this address CodeOf(address Address) (Code, error) + // CodeHashOf returns the code hash for this address + CodeHashOf(address Address) ([]byte, error) } // BlockView facilitates execution of a transaction or a direct evm call in the context of a block diff --git a/fvm/evm/types/handler.go b/fvm/evm/types/handler.go index 3badb5c6175..fb515433752 100644 --- a/fvm/evm/types/handler.go +++ b/fvm/evm/types/handler.go @@ -24,8 +24,8 @@ import ( // ContractHandler handles operations on the evm environment type ContractHandler interface { - // AllocateAddress allocates an address to be used by a bridged account resource - AllocateAddress() Address + // Deploys a cadence-owned-account + DeployACOAAccount() Address // AccountByAddress returns an account by address // if isAuthorized is set, it allows for functionality like `call`, `deploy` From 86c66035a77d9b88eb9d97c80676643554ac75cb Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 18 Jan 2024 12:52:07 -0800 Subject: [PATCH 05/38] add COA contract ABI --- fvm/evm/handler/contracts.go | 449 ++++++++++++++++++++++++++--------- 1 file changed, 342 insertions(+), 107 deletions(-) diff --git a/fvm/evm/handler/contracts.go b/fvm/evm/handler/contracts.go index 4b16fc94f49..a8113854685 100644 --- a/fvm/evm/handler/contracts.go +++ b/fvm/evm/handler/contracts.go @@ -1,110 +1,345 @@ package handler -// contract code: -// -// // SPDX-License-Identifier: UNLICENSED -// pragma solidity >=0.7.0 <0.9.0; -// interface IERC165 { -// function supportsInterface(bytes4 interfaceId) external view returns (bool); -// } -// interface ERC721TokenReceiver { -// function onERC721Received( -// address _operator, -// address _from, -// uint256 _tokenId, -// bytes calldata _data -// ) external returns (bytes4); -// } -// interface ERC777TokensRecipient { -// function tokensReceived( -// address operator, -// address from, -// address to, -// uint256 amount, -// bytes calldata data, -// bytes calldata operatorData -// ) external; -// } -// interface ERC1155TokenReceiver { -// function onERC1155Received( -// address _operator, -// address _from, -// uint256 _id, -// uint256 _value, -// bytes calldata _data -// ) external returns (bytes4); -// function onERC1155BatchReceived( -// address _operator, -// address _from, -// uint256[] calldata _ids, -// uint256[] calldata _values, -// bytes calldata _data -// ) external returns (bytes4); -// } -// contract COA is ERC1155TokenReceiver, ERC777TokensRecipient, ERC721TokenReceiver, IERC165 { -// address constant public cadenceArch = 0x0000000000000000000000010000000000000001; -// event FlowReceived(address indexed sender, uint256 value); -// receive() external payable { -// emit FlowReceived(msg.sender, msg.value); -// } -// function supportsInterface(bytes4 id) external view virtual override returns (bool) { -// return -// id == type(ERC1155TokenReceiver).interfaceId || -// id == type(ERC721TokenReceiver).interfaceId || -// id == type(ERC777TokensRecipient).interfaceId || -// id == type(IERC165).interfaceId; -// } -// -// function onERC1155Received( -// address, -// address, -// uint256, -// uint256, -// bytes calldata -// ) external pure override returns (bytes4) { -// return 0xf23a6e61; -// } -// -// function onERC1155BatchReceived( -// address, -// address, -// uint256[] calldata, -// uint256[] calldata, -// bytes calldata -// ) external pure override returns (bytes4) { -// return 0xbc197c81; -// } -// -// function onERC721Received( -// address, -// address, -// uint256, -// bytes calldata -// ) external pure override returns (bytes4) { -// return 0x150b7a02; -// } -// -// function tokensReceived( -// address, -// address, -// address, -// uint256, -// bytes calldata, -// bytes calldata -// ) external pure override {} -// -// function isValidSignature( -// bytes32 _hash, -// bytes memory _sig -// ) external view virtual returns (bytes4){ -// (bool ok, bytes memory data) = cadenceArch.staticcall(abi.encodeWithSignature("verifyCOAOwnershipProof(bytes32, bytes)", _hash, _sig)); -// require(ok); -// bool output = abi.decode(data, (bool)); -// if (output) { -// return 0x1626ba7e; -// } -// return 0xffffffff; -// } -// } - var COAContractBytes = []byte("608060405234801561000f575f80fd5b50610e1e8061001d5f395ff3fe608060405260043610610072575f3560e01c80631626ba7e1161004d5780631626ba7e1461016b578063bc197c81146101a7578063d0d250bd146101e3578063f23a6e611461020d576100c7565b806223de29146100cb57806301ffc9a7146100f3578063150b7a021461012f576100c7565b366100c7573373ffffffffffffffffffffffffffffffffffffffff167fb496ce2863a72c02f90deb616c638c76948f3aff803140881c602775c05fdf3a346040516100bd91906105a5565b60405180910390a2005b5f80fd5b3480156100d6575f80fd5b506100f160048036038101906100ec91906106b4565b610249565b005b3480156100fe575f80fd5b50610119600480360381019061011491906107d3565b610253565b6040516101269190610818565b60405180910390f35b34801561013a575f80fd5b5061015560048036038101906101509190610831565b6103f3565b60405161016291906108c4565b60405180910390f35b348015610176575f80fd5b50610191600480360381019061018c9190610a48565b610407565b60405161019e91906108c4565b60405180910390f35b3480156101b2575f80fd5b506101cd60048036038101906101c89190610af7565b610554565b6040516101da91906108c4565b60405180910390f35b3480156101ee575f80fd5b506101f761056b565b6040516102049190610bdd565b60405180910390f35b348015610218575f80fd5b50610233600480360381019061022e9190610bf6565b610578565b60405161024091906108c4565b60405180910390f35b5050505050505050565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061031d57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061038457507e23de29000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806103ec57507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b5f63150b7a0260e01b905095945050505050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff16858560405160240161043d929190610d15565b6040516020818303038152906040527eb997bd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516104c69190610d7d565b5f60405180830381855afa9150503d805f81146104fe576040519150601f19603f3d011682016040523d82523d5f602084013e610503565b606091505b509150915081610511575f80fd5b5f818060200190518101906105269190610dbd565b9050801561054057631626ba7e60e01b935050505061054e565b63ffffffff60e01b93505050505b92915050565b5f63bc197c8160e01b905098975050505050505050565b6801000000000000000181565b5f63f23a6e6160e01b90509695505050505050565b5f819050919050565b61059f8161058d565b82525050565b5f6020820190506105b85f830184610596565b92915050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6105f8826105cf565b9050919050565b610608816105ee565b8114610612575f80fd5b50565b5f81359050610623816105ff565b92915050565b6106328161058d565b811461063c575f80fd5b50565b5f8135905061064d81610629565b92915050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261067457610673610653565b5b8235905067ffffffffffffffff81111561069157610690610657565b5b6020830191508360018202830111156106ad576106ac61065b565b5b9250929050565b5f805f805f805f8060c0898b0312156106d0576106cf6105c7565b5b5f6106dd8b828c01610615565b98505060206106ee8b828c01610615565b97505060406106ff8b828c01610615565b96505060606107108b828c0161063f565b955050608089013567ffffffffffffffff811115610731576107306105cb565b5b61073d8b828c0161065f565b945094505060a089013567ffffffffffffffff8111156107605761075f6105cb565b5b61076c8b828c0161065f565b92509250509295985092959890939650565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6107b28161077e565b81146107bc575f80fd5b50565b5f813590506107cd816107a9565b92915050565b5f602082840312156107e8576107e76105c7565b5b5f6107f5848285016107bf565b91505092915050565b5f8115159050919050565b610812816107fe565b82525050565b5f60208201905061082b5f830184610809565b92915050565b5f805f805f6080868803121561084a576108496105c7565b5b5f61085788828901610615565b955050602061086888828901610615565b94505060406108798882890161063f565b935050606086013567ffffffffffffffff81111561089a576108996105cb565b5b6108a68882890161065f565b92509250509295509295909350565b6108be8161077e565b82525050565b5f6020820190506108d75f8301846108b5565b92915050565b5f819050919050565b6108ef816108dd565b81146108f9575f80fd5b50565b5f8135905061090a816108e6565b92915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61095a82610914565b810181811067ffffffffffffffff8211171561097957610978610924565b5b80604052505050565b5f61098b6105be565b90506109978282610951565b919050565b5f67ffffffffffffffff8211156109b6576109b5610924565b5b6109bf82610914565b9050602081019050919050565b828183375f83830152505050565b5f6109ec6109e78461099c565b610982565b905082815260208101848484011115610a0857610a07610910565b5b610a138482856109cc565b509392505050565b5f82601f830112610a2f57610a2e610653565b5b8135610a3f8482602086016109da565b91505092915050565b5f8060408385031215610a5e57610a5d6105c7565b5b5f610a6b858286016108fc565b925050602083013567ffffffffffffffff811115610a8c57610a8b6105cb565b5b610a9885828601610a1b565b9150509250929050565b5f8083601f840112610ab757610ab6610653565b5b8235905067ffffffffffffffff811115610ad457610ad3610657565b5b602083019150836020820283011115610af057610aef61065b565b5b9250929050565b5f805f805f805f8060a0898b031215610b1357610b126105c7565b5b5f610b208b828c01610615565b9850506020610b318b828c01610615565b975050604089013567ffffffffffffffff811115610b5257610b516105cb565b5b610b5e8b828c01610aa2565b9650965050606089013567ffffffffffffffff811115610b8157610b806105cb565b5b610b8d8b828c01610aa2565b9450945050608089013567ffffffffffffffff811115610bb057610baf6105cb565b5b610bbc8b828c0161065f565b92509250509295985092959890939650565b610bd7816105ee565b82525050565b5f602082019050610bf05f830184610bce565b92915050565b5f805f805f8060a08789031215610c1057610c0f6105c7565b5b5f610c1d89828a01610615565b9650506020610c2e89828a01610615565b9550506040610c3f89828a0161063f565b9450506060610c5089828a0161063f565b935050608087013567ffffffffffffffff811115610c7157610c706105cb565b5b610c7d89828a0161065f565b92509250509295509295509295565b610c95816108dd565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610cd2578082015181840152602081019050610cb7565b5f8484015250505050565b5f610ce782610c9b565b610cf18185610ca5565b9350610d01818560208601610cb5565b610d0a81610914565b840191505092915050565b5f604082019050610d285f830185610c8c565b8181036020830152610d3a8184610cdd565b90509392505050565b5f81905092915050565b5f610d5782610c9b565b610d618185610d43565b9350610d71818560208601610cb5565b80840191505092915050565b5f610d888284610d4d565b915081905092915050565b610d9c816107fe565b8114610da6575f80fd5b50565b5f81519050610db781610d93565b92915050565b5f60208284031215610dd257610dd16105c7565b5b5f610ddf84828501610da9565b9150509291505056fea2646970667358221220c5a50c64c66e3bfb048a9c796b1fd390d35974be1136aed5e4c00cf2fc62c55164736f6c63430008160033") + +var COAContractCode = ` + // SPDX-License-Identifier: UNLICENSED + pragma solidity >=0.7.0 <0.9.0; + interface IERC165 { + function supportsInterface(bytes4 interfaceId) external view returns (bool); + } + interface ERC721TokenReceiver { + function onERC721Received( + address _operator, + address _from, + uint256 _tokenId, + bytes calldata _data + ) external returns (bytes4); + } + interface ERC777TokensRecipient { + function tokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes calldata data, + bytes calldata operatorData + ) external; + } + interface ERC1155TokenReceiver { + function onERC1155Received( + address _operator, + address _from, + uint256 _id, + uint256 _value, + bytes calldata _data + ) external returns (bytes4); + function onERC1155BatchReceived( + address _operator, + address _from, + uint256[] calldata _ids, + uint256[] calldata _values, + bytes calldata _data + ) external returns (bytes4); + } + contract COA is ERC1155TokenReceiver, ERC777TokensRecipient, ERC721TokenReceiver, IERC165 { + address constant public cadenceArch = 0x0000000000000000000000010000000000000001; + event FlowReceived(address indexed sender, uint256 value); + receive() external payable { + emit FlowReceived(msg.sender, msg.value); + } + function supportsInterface(bytes4 id) external view virtual override returns (bool) { + return + id == type(ERC1155TokenReceiver).interfaceId || + id == type(ERC721TokenReceiver).interfaceId || + id == type(ERC777TokensRecipient).interfaceId || + id == type(IERC165).interfaceId; + } + + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes calldata + ) external pure override returns (bytes4) { + return 0xf23a6e61; + } + + function onERC1155BatchReceived( + address, + address, + uint256[] calldata, + uint256[] calldata, + bytes calldata + ) external pure override returns (bytes4) { + return 0xbc197c81; + } + + function onERC721Received( + address, + address, + uint256, + bytes calldata + ) external pure override returns (bytes4) { + return 0x150b7a02; + } + + function tokensReceived( + address, + address, + address, + uint256, + bytes calldata, + bytes calldata + ) external pure override {} + + function isValidSignature( + bytes32 _hash, + bytes memory _sig + ) external view virtual returns (bytes4){ + (bool ok, bytes memory data) = cadenceArch.staticcall(abi.encodeWithSignature("verifyCOAOwnershipProof(bytes32, bytes)", _hash, _sig)); + require(ok); + bool output = abi.decode(data, (bool)); + if (output) { + return 0x1626ba7e; + } + return 0xffffffff; + } + } +` + +var COAContractABI = ` +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "FlowReceived", + "type": "event" + }, + { + "inputs": [], + "name": "cadenceArch", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_sig", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "id", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "tokensReceived", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] + +` From 6e8b064e9790d89bd146fd2e0b8c3798347cabdb Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 18 Jan 2024 18:38:35 -0800 Subject: [PATCH 06/38] add test --- fvm/evm/handler/addressAllocator.go | 2 +- fvm/evm/handler/contracts.go | 5 +- fvm/evm/handler/handler_test.go | 91 ++++++++++++++++++++--------- fvm/evm/testutils/contract.go | 8 ++- fvm/evm/types/call.go | 4 +- 5 files changed, 78 insertions(+), 32 deletions(-) diff --git a/fvm/evm/handler/addressAllocator.go b/fvm/evm/handler/addressAllocator.go index 28e476d9427..84aa1436a0c 100644 --- a/fvm/evm/handler/addressAllocator.go +++ b/fvm/evm/handler/addressAllocator.go @@ -33,7 +33,7 @@ func (aa *AddressAllocator) AllocateAddress() (types.Address, error) { return types.Address{}, err } // default value for uuid is 1 - uuid := uint64(1) + uuid := uint64(100) if len(data) > 0 { uuid = binary.BigEndian.Uint64(data) } diff --git a/fvm/evm/handler/contracts.go b/fvm/evm/handler/contracts.go index a8113854685..7a925d9d46c 100644 --- a/fvm/evm/handler/contracts.go +++ b/fvm/evm/handler/contracts.go @@ -1,6 +1,8 @@ package handler -var COAContractBytes = []byte("608060405234801561000f575f80fd5b50610e1e8061001d5f395ff3fe608060405260043610610072575f3560e01c80631626ba7e1161004d5780631626ba7e1461016b578063bc197c81146101a7578063d0d250bd146101e3578063f23a6e611461020d576100c7565b806223de29146100cb57806301ffc9a7146100f3578063150b7a021461012f576100c7565b366100c7573373ffffffffffffffffffffffffffffffffffffffff167fb496ce2863a72c02f90deb616c638c76948f3aff803140881c602775c05fdf3a346040516100bd91906105a5565b60405180910390a2005b5f80fd5b3480156100d6575f80fd5b506100f160048036038101906100ec91906106b4565b610249565b005b3480156100fe575f80fd5b50610119600480360381019061011491906107d3565b610253565b6040516101269190610818565b60405180910390f35b34801561013a575f80fd5b5061015560048036038101906101509190610831565b6103f3565b60405161016291906108c4565b60405180910390f35b348015610176575f80fd5b50610191600480360381019061018c9190610a48565b610407565b60405161019e91906108c4565b60405180910390f35b3480156101b2575f80fd5b506101cd60048036038101906101c89190610af7565b610554565b6040516101da91906108c4565b60405180910390f35b3480156101ee575f80fd5b506101f761056b565b6040516102049190610bdd565b60405180910390f35b348015610218575f80fd5b50610233600480360381019061022e9190610bf6565b610578565b60405161024091906108c4565b60405180910390f35b5050505050505050565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061031d57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061038457507e23de29000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806103ec57507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b5f63150b7a0260e01b905095945050505050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff16858560405160240161043d929190610d15565b6040516020818303038152906040527eb997bd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516104c69190610d7d565b5f60405180830381855afa9150503d805f81146104fe576040519150601f19603f3d011682016040523d82523d5f602084013e610503565b606091505b509150915081610511575f80fd5b5f818060200190518101906105269190610dbd565b9050801561054057631626ba7e60e01b935050505061054e565b63ffffffff60e01b93505050505b92915050565b5f63bc197c8160e01b905098975050505050505050565b6801000000000000000181565b5f63f23a6e6160e01b90509695505050505050565b5f819050919050565b61059f8161058d565b82525050565b5f6020820190506105b85f830184610596565b92915050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6105f8826105cf565b9050919050565b610608816105ee565b8114610612575f80fd5b50565b5f81359050610623816105ff565b92915050565b6106328161058d565b811461063c575f80fd5b50565b5f8135905061064d81610629565b92915050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261067457610673610653565b5b8235905067ffffffffffffffff81111561069157610690610657565b5b6020830191508360018202830111156106ad576106ac61065b565b5b9250929050565b5f805f805f805f8060c0898b0312156106d0576106cf6105c7565b5b5f6106dd8b828c01610615565b98505060206106ee8b828c01610615565b97505060406106ff8b828c01610615565b96505060606107108b828c0161063f565b955050608089013567ffffffffffffffff811115610731576107306105cb565b5b61073d8b828c0161065f565b945094505060a089013567ffffffffffffffff8111156107605761075f6105cb565b5b61076c8b828c0161065f565b92509250509295985092959890939650565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6107b28161077e565b81146107bc575f80fd5b50565b5f813590506107cd816107a9565b92915050565b5f602082840312156107e8576107e76105c7565b5b5f6107f5848285016107bf565b91505092915050565b5f8115159050919050565b610812816107fe565b82525050565b5f60208201905061082b5f830184610809565b92915050565b5f805f805f6080868803121561084a576108496105c7565b5b5f61085788828901610615565b955050602061086888828901610615565b94505060406108798882890161063f565b935050606086013567ffffffffffffffff81111561089a576108996105cb565b5b6108a68882890161065f565b92509250509295509295909350565b6108be8161077e565b82525050565b5f6020820190506108d75f8301846108b5565b92915050565b5f819050919050565b6108ef816108dd565b81146108f9575f80fd5b50565b5f8135905061090a816108e6565b92915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61095a82610914565b810181811067ffffffffffffffff8211171561097957610978610924565b5b80604052505050565b5f61098b6105be565b90506109978282610951565b919050565b5f67ffffffffffffffff8211156109b6576109b5610924565b5b6109bf82610914565b9050602081019050919050565b828183375f83830152505050565b5f6109ec6109e78461099c565b610982565b905082815260208101848484011115610a0857610a07610910565b5b610a138482856109cc565b509392505050565b5f82601f830112610a2f57610a2e610653565b5b8135610a3f8482602086016109da565b91505092915050565b5f8060408385031215610a5e57610a5d6105c7565b5b5f610a6b858286016108fc565b925050602083013567ffffffffffffffff811115610a8c57610a8b6105cb565b5b610a9885828601610a1b565b9150509250929050565b5f8083601f840112610ab757610ab6610653565b5b8235905067ffffffffffffffff811115610ad457610ad3610657565b5b602083019150836020820283011115610af057610aef61065b565b5b9250929050565b5f805f805f805f8060a0898b031215610b1357610b126105c7565b5b5f610b208b828c01610615565b9850506020610b318b828c01610615565b975050604089013567ffffffffffffffff811115610b5257610b516105cb565b5b610b5e8b828c01610aa2565b9650965050606089013567ffffffffffffffff811115610b8157610b806105cb565b5b610b8d8b828c01610aa2565b9450945050608089013567ffffffffffffffff811115610bb057610baf6105cb565b5b610bbc8b828c0161065f565b92509250509295985092959890939650565b610bd7816105ee565b82525050565b5f602082019050610bf05f830184610bce565b92915050565b5f805f805f8060a08789031215610c1057610c0f6105c7565b5b5f610c1d89828a01610615565b9650506020610c2e89828a01610615565b9550506040610c3f89828a0161063f565b9450506060610c5089828a0161063f565b935050608087013567ffffffffffffffff811115610c7157610c706105cb565b5b610c7d89828a0161065f565b92509250509295509295509295565b610c95816108dd565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610cd2578082015181840152602081019050610cb7565b5f8484015250505050565b5f610ce782610c9b565b610cf18185610ca5565b9350610d01818560208601610cb5565b610d0a81610914565b840191505092915050565b5f604082019050610d285f830185610c8c565b8181036020830152610d3a8184610cdd565b90509392505050565b5f81905092915050565b5f610d5782610c9b565b610d618185610d43565b9350610d71818560208601610cb5565b80840191505092915050565b5f610d888284610d4d565b915081905092915050565b610d9c816107fe565b8114610da6575f80fd5b50565b5f81519050610db781610d93565b92915050565b5f60208284031215610dd257610dd16105c7565b5b5f610ddf84828501610da9565b9150509291505056fea2646970667358221220c5a50c64c66e3bfb048a9c796b1fd390d35974be1136aed5e4c00cf2fc62c55164736f6c63430008160033") +import "encoding/hex" + +var COAContractBytes, _ = hex.DecodeString("608060405234801561000f575f80fd5b50610e1e8061001d5f395ff3fe608060405260043610610072575f3560e01c80631626ba7e1161004d5780631626ba7e1461016b578063bc197c81146101a7578063d0d250bd146101e3578063f23a6e611461020d576100c7565b806223de29146100cb57806301ffc9a7146100f3578063150b7a021461012f576100c7565b366100c7573373ffffffffffffffffffffffffffffffffffffffff167fb496ce2863a72c02f90deb616c638c76948f3aff803140881c602775c05fdf3a346040516100bd91906105a5565b60405180910390a2005b5f80fd5b3480156100d6575f80fd5b506100f160048036038101906100ec91906106b4565b610249565b005b3480156100fe575f80fd5b50610119600480360381019061011491906107d3565b610253565b6040516101269190610818565b60405180910390f35b34801561013a575f80fd5b5061015560048036038101906101509190610831565b6103f3565b60405161016291906108c4565b60405180910390f35b348015610176575f80fd5b50610191600480360381019061018c9190610a48565b610407565b60405161019e91906108c4565b60405180910390f35b3480156101b2575f80fd5b506101cd60048036038101906101c89190610af7565b610554565b6040516101da91906108c4565b60405180910390f35b3480156101ee575f80fd5b506101f761056b565b6040516102049190610bdd565b60405180910390f35b348015610218575f80fd5b50610233600480360381019061022e9190610bf6565b610578565b60405161024091906108c4565b60405180910390f35b5050505050505050565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061031d57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061038457507e23de29000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806103ec57507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b5f63150b7a0260e01b905095945050505050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff16858560405160240161043d929190610d15565b6040516020818303038152906040527eb997bd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516104c69190610d7d565b5f60405180830381855afa9150503d805f81146104fe576040519150601f19603f3d011682016040523d82523d5f602084013e610503565b606091505b509150915081610511575f80fd5b5f818060200190518101906105269190610dbd565b9050801561054057631626ba7e60e01b935050505061054e565b63ffffffff60e01b93505050505b92915050565b5f63bc197c8160e01b905098975050505050505050565b6801000000000000000181565b5f63f23a6e6160e01b90509695505050505050565b5f819050919050565b61059f8161058d565b82525050565b5f6020820190506105b85f830184610596565b92915050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6105f8826105cf565b9050919050565b610608816105ee565b8114610612575f80fd5b50565b5f81359050610623816105ff565b92915050565b6106328161058d565b811461063c575f80fd5b50565b5f8135905061064d81610629565b92915050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261067457610673610653565b5b8235905067ffffffffffffffff81111561069157610690610657565b5b6020830191508360018202830111156106ad576106ac61065b565b5b9250929050565b5f805f805f805f8060c0898b0312156106d0576106cf6105c7565b5b5f6106dd8b828c01610615565b98505060206106ee8b828c01610615565b97505060406106ff8b828c01610615565b96505060606107108b828c0161063f565b955050608089013567ffffffffffffffff811115610731576107306105cb565b5b61073d8b828c0161065f565b945094505060a089013567ffffffffffffffff8111156107605761075f6105cb565b5b61076c8b828c0161065f565b92509250509295985092959890939650565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6107b28161077e565b81146107bc575f80fd5b50565b5f813590506107cd816107a9565b92915050565b5f602082840312156107e8576107e76105c7565b5b5f6107f5848285016107bf565b91505092915050565b5f8115159050919050565b610812816107fe565b82525050565b5f60208201905061082b5f830184610809565b92915050565b5f805f805f6080868803121561084a576108496105c7565b5b5f61085788828901610615565b955050602061086888828901610615565b94505060406108798882890161063f565b935050606086013567ffffffffffffffff81111561089a576108996105cb565b5b6108a68882890161065f565b92509250509295509295909350565b6108be8161077e565b82525050565b5f6020820190506108d75f8301846108b5565b92915050565b5f819050919050565b6108ef816108dd565b81146108f9575f80fd5b50565b5f8135905061090a816108e6565b92915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61095a82610914565b810181811067ffffffffffffffff8211171561097957610978610924565b5b80604052505050565b5f61098b6105be565b90506109978282610951565b919050565b5f67ffffffffffffffff8211156109b6576109b5610924565b5b6109bf82610914565b9050602081019050919050565b828183375f83830152505050565b5f6109ec6109e78461099c565b610982565b905082815260208101848484011115610a0857610a07610910565b5b610a138482856109cc565b509392505050565b5f82601f830112610a2f57610a2e610653565b5b8135610a3f8482602086016109da565b91505092915050565b5f8060408385031215610a5e57610a5d6105c7565b5b5f610a6b858286016108fc565b925050602083013567ffffffffffffffff811115610a8c57610a8b6105cb565b5b610a9885828601610a1b565b9150509250929050565b5f8083601f840112610ab757610ab6610653565b5b8235905067ffffffffffffffff811115610ad457610ad3610657565b5b602083019150836020820283011115610af057610aef61065b565b5b9250929050565b5f805f805f805f8060a0898b031215610b1357610b126105c7565b5b5f610b208b828c01610615565b9850506020610b318b828c01610615565b975050604089013567ffffffffffffffff811115610b5257610b516105cb565b5b610b5e8b828c01610aa2565b9650965050606089013567ffffffffffffffff811115610b8157610b806105cb565b5b610b8d8b828c01610aa2565b9450945050608089013567ffffffffffffffff811115610bb057610baf6105cb565b5b610bbc8b828c0161065f565b92509250509295985092959890939650565b610bd7816105ee565b82525050565b5f602082019050610bf05f830184610bce565b92915050565b5f805f805f8060a08789031215610c1057610c0f6105c7565b5b5f610c1d89828a01610615565b9650506020610c2e89828a01610615565b9550506040610c3f89828a0161063f565b9450506060610c5089828a0161063f565b935050608087013567ffffffffffffffff811115610c7157610c706105cb565b5b610c7d89828a0161065f565b92509250509295509295509295565b610c95816108dd565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610cd2578082015181840152602081019050610cb7565b5f8484015250505050565b5f610ce782610c9b565b610cf18185610ca5565b9350610d01818560208601610cb5565b610d0a81610914565b840191505092915050565b5f604082019050610d285f830185610c8c565b8181036020830152610d3a8184610cdd565b90509392505050565b5f81905092915050565b5f610d5782610c9b565b610d618185610d43565b9350610d71818560208601610cb5565b80840191505092915050565b5f610d888284610d4d565b915081905092915050565b610d9c816107fe565b8114610da6575f80fd5b50565b5f81519050610db781610d93565b92915050565b5f60208284031215610dd257610dd16105c7565b5b5f610ddf84828501610da9565b9150509291505056fea2646970667358221220c5a50c64c66e3bfb048a9c796b1fd390d35974be1136aed5e4c00cf2fc62c55164736f6c63430008160033") var COAContractCode = ` // SPDX-License-Identifier: UNLICENSED @@ -341,5 +343,4 @@ var COAContractABI = ` "type": "receive" } ] - ` diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 23715514580..39a5aa4a971 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -34,6 +34,11 @@ import ( var flowTokenAddress = common.MustBytesToAddress(systemcontracts.SystemContractsForChain(flow.Emulator).FlowToken.Address.Bytes()) +// makeABalanceInFlow makes a balance with `count` Flow in it +func makeABalanceInFlow(count uint64) types.Balance { + return types.Balance(uint64(100_000_000) * count) +} + func TestHandler_TransactionRun(t *testing.T) { t.Parallel() @@ -268,31 +273,11 @@ func TestHandler_OpsWithoutEmulator(t *testing.T) { }) }) - t.Run("test coa deployment", func(t *testing.T) { - t.Parallel() - - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { - testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { - h := SetupHandler(t, backend, rootAddr) - - coa := h.DeployACOAAccount() - require.NotNil(t, coa) - - expectedAddress := types.NewAddress(gethCommon.HexToAddress("0x00000000000000000001")) - require.Equal(t, expectedAddress, coa) - - acc := h.AccountByAddress(coa, true) - require.Equal(t, handler.COAContractBytes, acc.Code()) - }) - }) - }) } func TestHandler_BridgedAccount(t *testing.T) { - + t.Parallel() t.Run("test deposit/withdraw (with integrated emulator)", func(t *testing.T) { - t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { handler := SetupHandler(t, backend, rootAddr) @@ -320,18 +305,20 @@ func TestHandler_BridgedAccount(t *testing.T) { require.Equal(t, zeroBalance, foa.Balance()) events := backend.Events() - require.Len(t, events, 4) + require.Len(t, events, 6) + + // first two transactions are for COA setup // transaction event - event := events[0] + event := events[2] assert.Equal(t, event.Type, types.EventTypeTransactionExecuted) // block event - event = events[1] + event = events[3] assert.Equal(t, event.Type, types.EventTypeBlockExecuted) // transaction event - event = events[2] + event = events[4] assert.Equal(t, event.Type, types.EventTypeTransactionExecuted) _, err = jsoncdc.Decode(nil, event.Payload) require.NoError(t, err) @@ -340,7 +327,7 @@ func TestHandler_BridgedAccount(t *testing.T) { // assert.Equal(t, balance, ret.Amount) // block event - event = events[3] + event = events[5] assert.Equal(t, event.Type, types.EventTypeBlockExecuted) // check gas usage @@ -351,6 +338,58 @@ func TestHandler_BridgedAccount(t *testing.T) { }) }) + t.Run("test coa deployment", func(t *testing.T) { + testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { + h := SetupHandler(t, backend, rootAddr) + + coa := h.DeployACOAAccount() + require.NotNil(t, coa) + + // expectedAddress := types.NewAddress(gethCommon.HexToAddress("0x00000000000000000001")) + // require.Equal(t, expectedAddress, coa) + + acc := h.AccountByAddress(coa, true) + require.NotEmpty(t, acc.Code()) + + // make a second account with some money + coa2 := h.DeployACOAAccount() + acc2 := h.AccountByAddress(coa2, true) + acc2.Deposit(types.NewFlowTokenVault(makeABalanceInFlow(100))) + + // transfer money + acc2.Transfer( + coa, + types.DefaultTransferGasUsageToCOAs, + makeABalanceInFlow(1), + ) + + // make a call to the contract + ret := acc2.Call( + coa, + testutils.MakeCallData(t, + handler.COAContractABI, + "onERC721Received", + gethCommon.Address{1}, + gethCommon.Address{1}, + big.NewInt(0), + []byte{'A'}, + ), + types.GasLimit(3_000_000), + types.Balance(0)) + + // 0x150b7a02 + expected := types.Data([]byte{ + 21, 11, 122, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }) + require.Equal(t, expected, ret) + }) + }) + }) + t.Run("test withdraw (unhappy case)", func(t *testing.T) { t.Parallel() diff --git a/fvm/evm/testutils/contract.go b/fvm/evm/testutils/contract.go index f67cced4c94..2e61ad497bd 100644 --- a/fvm/evm/testutils/contract.go +++ b/fvm/evm/testutils/contract.go @@ -25,14 +25,18 @@ type TestContract struct { DeployedAt types.Address } -func (tc *TestContract) MakeCallData(t testing.TB, name string, args ...interface{}) []byte { - abi, err := gethABI.JSON(strings.NewReader(tc.ABI)) +func MakeCallData(t testing.TB, abiString string, name string, args ...interface{}) []byte { + abi, err := gethABI.JSON(strings.NewReader(abiString)) require.NoError(t, err) call, err := abi.Pack(name, args...) require.NoError(t, err) return call } +func (tc *TestContract) MakeCallData(t testing.TB, name string, args ...interface{}) []byte { + return MakeCallData(t, tc.ABI, name, args...) +} + func (tc *TestContract) SetDeployedAt(deployedAt types.Address) { tc.DeployedAt = deployedAt } diff --git a/fvm/evm/types/call.go b/fvm/evm/types/call.go index b1701c24359..c73901f57d0 100644 --- a/fvm/evm/types/call.go +++ b/fvm/evm/types/call.go @@ -21,6 +21,8 @@ const ( ContractCallSubType = byte(5) TransferGasUsage = 21_000 + // 21_000 for transaction + gas limit for receive and fallback + DefaultGasLimitForTokenTransfer = 21_000 + 2_300 ) // DirectCall captures all the data related to a direct call to evm @@ -107,7 +109,7 @@ func NewTransferCall(from Address, to Address, amount *big.Int) *DirectCall { To: to, Data: nil, Value: amount, - GasLimit: TransferGasUsage, + GasLimit: DefaultGasLimitForTokenTransfer, } } From 72e557e2c07819284f1f60240305b220774b1c22 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 18 Jan 2024 18:40:58 -0800 Subject: [PATCH 07/38] . --- fvm/evm/handler/handler_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 39a5aa4a971..2cb7c26d177 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -360,7 +360,6 @@ func TestHandler_BridgedAccount(t *testing.T) { // transfer money acc2.Transfer( coa, - types.DefaultTransferGasUsageToCOAs, makeABalanceInFlow(1), ) From 5a662b25a8630beff35993912bf1027df1ae96bf Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 23 Jan 2024 11:29:50 -0800 Subject: [PATCH 08/38] update address allocation to shuffle --- fvm/evm/handler/addressAllocator.go | 17 +++++++++++++++-- fvm/evm/handler/addressAllocator_test.go | 8 +++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/fvm/evm/handler/addressAllocator.go b/fvm/evm/handler/addressAllocator.go index d1dc8299130..1a729ce80c8 100644 --- a/fvm/evm/handler/addressAllocator.go +++ b/fvm/evm/handler/addressAllocator.go @@ -1,6 +1,7 @@ package handler import ( + "bytes" "encoding/binary" "github.com/onflow/atree" @@ -21,6 +22,9 @@ var ( // leading zeros helps with storage and all zero is reserved for the EVM precompiles FlowEVMPrecompileAddressPrefix = [addressPrefixLen]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} FlowEVMCOAAddressPrefix = [addressPrefixLen]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2} + // this seed is used for shuffling address index + // shuffling index is used to make address postfixes look random + addressIndexShuffleSeed = uint64(0xFFEEDDCCBBAA9987) ) type AddressAllocator struct { @@ -44,7 +48,7 @@ func (aa *AddressAllocator) AllocateCOAAddress() (types.Address, error) { if err != nil { return types.Address{}, err } - // default value for uuid is 1 + // default value for uuid is 1, zero is not good for shuffling uuid := uint64(1) if len(data) > 0 { uuid = binary.BigEndian.Uint64(data) @@ -64,7 +68,7 @@ func (aa *AddressAllocator) AllocateCOAAddress() (types.Address, error) { } func MakeCOAAddress(index uint64) types.Address { - return makePrefixedAddress(index, FlowEVMCOAAddressPrefix) + return makePrefixedAddress(shuffleAddressIndex(index), FlowEVMCOAAddressPrefix) } func (aa *AddressAllocator) AllocatePrecompileAddress(index uint64) types.Address { @@ -83,3 +87,12 @@ func makePrefixedAddress(index uint64, prefix [addressPrefixLen]byte) types.Addr binary.BigEndian.PutUint64(addr[prefixIndex:], index) return addr } + +func shuffleAddressIndex(preShuffleIndex uint64) uint64 { + return uint64(preShuffleIndex * addressIndexShuffleSeed) +} + +// IsACOAAddress returns true if the address is a COA address +func IsACOAAddress(addr types.Address) bool { + return bytes.HasPrefix(addr[:], FlowEVMCOAAddressPrefix[:]) +} diff --git a/fvm/evm/handler/addressAllocator_test.go b/fvm/evm/handler/addressAllocator_test.go index 03794baea9a..04cf2244202 100644 --- a/fvm/evm/handler/addressAllocator_test.go +++ b/fvm/evm/handler/addressAllocator_test.go @@ -22,20 +22,22 @@ func TestAddressAllocator(t *testing.T) { adr := aa.AllocatePrecompileAddress(3) expectedAddress := types.NewAddress(gethCommon.HexToAddress("0x0000000000000000000000010000000000000003")) require.Equal(t, expectedAddress, adr) + require.False(t, handler.IsACOAAddress(adr)) // test default value fall back adr, err = aa.AllocateCOAAddress() require.NoError(t, err) - expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x0000000000000000000000020000000000000001")) + expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x000000000000000000000002ffeeddccbbaa9987")) require.Equal(t, expectedAddress, adr) + require.True(t, handler.IsACOAAddress(adr)) // continous allocation logic adr, err = aa.AllocateCOAAddress() require.NoError(t, err) - expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x0000000000000000000000020000000000000002")) + expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x000000000000000000000002ffddbb997755330e")) require.Equal(t, expectedAddress, adr) + require.True(t, handler.IsACOAAddress(adr)) }) - }) } From 45583ca08b521c6910626c93701e6cfece06d715 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 23 Jan 2024 11:39:00 -0800 Subject: [PATCH 09/38] move things around --- fvm/evm/handler/addressAllocator.go | 26 +++++----------- fvm/evm/handler/addressAllocator_test.go | 9 ++++-- fvm/evm/types/address.go | 38 ++++++++++++++++-------- fvm/evm/types/call.go | 11 +++++++ 4 files changed, 50 insertions(+), 34 deletions(-) diff --git a/fvm/evm/handler/addressAllocator.go b/fvm/evm/handler/addressAllocator.go index 1a729ce80c8..a9fba1f9e14 100644 --- a/fvm/evm/handler/addressAllocator.go +++ b/fvm/evm/handler/addressAllocator.go @@ -1,7 +1,6 @@ package handler import ( - "bytes" "encoding/binary" "github.com/onflow/atree" @@ -13,16 +12,7 @@ import ( const ( ledgerAddressAllocatorKey = "AddressAllocator" uint64ByteSize = 8 - addressPrefixLen = 12 -) - -var ( - // prefixes: - // the first 12 bytes of addresses allocation - // leading zeros helps with storage and all zero is reserved for the EVM precompiles - FlowEVMPrecompileAddressPrefix = [addressPrefixLen]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} - FlowEVMCOAAddressPrefix = [addressPrefixLen]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2} - // this seed is used for shuffling address index + // addressIndexShuffleSeed is used for shuffling address index // shuffling index is used to make address postfixes look random addressIndexShuffleSeed = uint64(0xFFEEDDCCBBAA9987) ) @@ -68,7 +58,7 @@ func (aa *AddressAllocator) AllocateCOAAddress() (types.Address, error) { } func MakeCOAAddress(index uint64) types.Address { - return makePrefixedAddress(shuffleAddressIndex(index), FlowEVMCOAAddressPrefix) + return makePrefixedAddress(shuffleAddressIndex(index), types.FlowEVMCOAAddressPrefix) } func (aa *AddressAllocator) AllocatePrecompileAddress(index uint64) types.Address { @@ -77,10 +67,13 @@ func (aa *AddressAllocator) AllocatePrecompileAddress(index uint64) types.Addres } func MakePrecompileAddress(index uint64) types.Address { - return makePrefixedAddress(index, FlowEVMPrecompileAddressPrefix) + return makePrefixedAddress(index, types.FlowEVMExtendedPrecompileAddressPrefix) } -func makePrefixedAddress(index uint64, prefix [addressPrefixLen]byte) types.Address { +func makePrefixedAddress( + index uint64, + prefix [types.FlowEVMSpecialAddressPrefixLen]byte, +) types.Address { var addr types.Address prefixIndex := types.AddressLength - uint64ByteSize copy(addr[:prefixIndex], prefix[:]) @@ -91,8 +84,3 @@ func makePrefixedAddress(index uint64, prefix [addressPrefixLen]byte) types.Addr func shuffleAddressIndex(preShuffleIndex uint64) uint64 { return uint64(preShuffleIndex * addressIndexShuffleSeed) } - -// IsACOAAddress returns true if the address is a COA address -func IsACOAAddress(addr types.Address) bool { - return bytes.HasPrefix(addr[:], FlowEVMCOAAddressPrefix[:]) -} diff --git a/fvm/evm/handler/addressAllocator_test.go b/fvm/evm/handler/addressAllocator_test.go index 04cf2244202..df259cfbf3f 100644 --- a/fvm/evm/handler/addressAllocator_test.go +++ b/fvm/evm/handler/addressAllocator_test.go @@ -22,21 +22,24 @@ func TestAddressAllocator(t *testing.T) { adr := aa.AllocatePrecompileAddress(3) expectedAddress := types.NewAddress(gethCommon.HexToAddress("0x0000000000000000000000010000000000000003")) require.Equal(t, expectedAddress, adr) - require.False(t, handler.IsACOAAddress(adr)) + // check conforming to types + require.False(t, types.IsACOAAddress(adr)) // test default value fall back adr, err = aa.AllocateCOAAddress() require.NoError(t, err) expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x000000000000000000000002ffeeddccbbaa9987")) require.Equal(t, expectedAddress, adr) - require.True(t, handler.IsACOAAddress(adr)) + // check conforming to types + require.True(t, types.IsACOAAddress(adr)) // continous allocation logic adr, err = aa.AllocateCOAAddress() require.NoError(t, err) expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x000000000000000000000002ffddbb997755330e")) require.Equal(t, expectedAddress, adr) - require.True(t, handler.IsACOAAddress(adr)) + // check conforming to types + require.True(t, types.IsACOAAddress(adr)) }) }) diff --git a/fvm/evm/types/address.go b/fvm/evm/types/address.go index 134ae6c6cf8..ed1568dcc0f 100644 --- a/fvm/evm/types/address.go +++ b/fvm/evm/types/address.go @@ -1,11 +1,36 @@ package types import ( - "math/big" + "bytes" gethCommon "github.com/ethereum/go-ethereum/common" ) +const ( + // number of prefix bytes with specific values for special accounts (extended precompiles and COAs) + // using leading zeros for prefix helps with the storage compactness + FlowEVMSpecialAddressPrefixLen = 12 +) + +var ( + // Prefix for the built-in EVM precompiles + FlowEVMNativePrecompileAddressPrefix = [FlowEVMSpecialAddressPrefixLen]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + // Prefix for the extended precompiles + FlowEVMExtendedPrecompileAddressPrefix = [FlowEVMSpecialAddressPrefixLen]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} + // Prefix for the COA addresses + FlowEVMCOAAddressPrefix = [FlowEVMSpecialAddressPrefixLen]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2} +) + +// IsACOAAddress returns true if the address is a COA address +func IsACOAAddress(addr Address) bool { + return bytes.HasPrefix(addr[:], FlowEVMCOAAddressPrefix[:]) +} + +// IsACOAAddress returns true if the address is a COA address +func IsAnExtendedPrecompileAddress(addr Address) bool { + return bytes.HasPrefix(addr[:], FlowEVMExtendedPrecompileAddressPrefix[:]) +} + // Address is an EVM-compatible address type Address gethCommon.Address @@ -39,14 +64,3 @@ func NewAddressFromBytes(inp []byte) Address { func NewAddressFromString(str string) Address { return NewAddressFromBytes([]byte(str)) } - -type GasLimit uint64 - -type Code []byte - -type Data []byte - -// AsBigInt process the data and return it as a big integer -func (d Data) AsBigInt() *big.Int { - return new(big.Int).SetBytes(d) -} diff --git a/fvm/evm/types/call.go b/fvm/evm/types/call.go index 31562fe9ccd..ea20e764b7a 100644 --- a/fvm/evm/types/call.go +++ b/fvm/evm/types/call.go @@ -129,3 +129,14 @@ func NewContractCall(caller Address, to Address, data Data, gasLimit uint64, val GasLimit: gasLimit, } } + +type GasLimit uint64 + +type Code []byte + +type Data []byte + +// AsBigInt process the data and return it as a big integer +func (d Data) AsBigInt() *big.Int { + return new(big.Int).SetBytes(d) +} From b9d7a8ee4ba4315f3fc213525ee3a429c5917e85 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 23 Jan 2024 11:40:04 -0800 Subject: [PATCH 10/38] clean up --- fvm/evm/types/address.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/fvm/evm/types/address.go b/fvm/evm/types/address.go index ed1568dcc0f..8fe7dc18114 100644 --- a/fvm/evm/types/address.go +++ b/fvm/evm/types/address.go @@ -21,16 +21,6 @@ var ( FlowEVMCOAAddressPrefix = [FlowEVMSpecialAddressPrefixLen]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2} ) -// IsACOAAddress returns true if the address is a COA address -func IsACOAAddress(addr Address) bool { - return bytes.HasPrefix(addr[:], FlowEVMCOAAddressPrefix[:]) -} - -// IsACOAAddress returns true if the address is a COA address -func IsAnExtendedPrecompileAddress(addr Address) bool { - return bytes.HasPrefix(addr[:], FlowEVMExtendedPrecompileAddressPrefix[:]) -} - // Address is an EVM-compatible address type Address gethCommon.Address @@ -64,3 +54,13 @@ func NewAddressFromBytes(inp []byte) Address { func NewAddressFromString(str string) Address { return NewAddressFromBytes([]byte(str)) } + +// IsACOAAddress returns true if the address is a COA address +func IsACOAAddress(addr Address) bool { + return bytes.HasPrefix(addr[:], FlowEVMCOAAddressPrefix[:]) +} + +// IsAnExtendedPrecompileAddress returns true if the address is a extended precompile address +func IsAnExtendedPrecompileAddress(addr Address) bool { + return bytes.HasPrefix(addr[:], FlowEVMExtendedPrecompileAddressPrefix[:]) +} From b8e6017fb0ab0d749bb56d98c39229256a628c4f Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 23 Jan 2024 15:00:18 -0800 Subject: [PATCH 11/38] cleanups --- fvm/evm/emulator/emulator.go | 4 +- fvm/evm/handler/addressAllocator.go | 2 +- fvm/evm/handler/{contracts.go => coa.go} | 113 +---------------------- fvm/evm/handler/coa.sol | 104 +++++++++++++++++++++ fvm/evm/handler/handler.go | 12 ++- fvm/evm/handler/handler_test.go | 2 +- 6 files changed, 122 insertions(+), 115 deletions(-) rename fvm/evm/handler/{contracts.go => coa.go} (80%) create mode 100644 fvm/evm/handler/coa.sol diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index 0da024a6a5d..70c393e7fcc 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -238,9 +238,11 @@ func (proc *procedure) withdrawFrom(address types.Address, amount *big.Int) (*ty // deployAt deploys a contract at the given target address // behaviour should be similar to what evm.create internal method does with -// a few diffrences, don't need to check for previous forks given this +// a few differences, don't need to check for previous forks given this // functionality was not available to anyone, we don't need to // follow snapshoting, given we do commit/revert style in this code base. +// in the future we might optimize this method accepting deploy-ready byte codes +// and skip interpreter call, gas calculations and many checks. func (proc *procedure) deployAt( caller types.Address, to types.Address, diff --git a/fvm/evm/handler/addressAllocator.go b/fvm/evm/handler/addressAllocator.go index 84aa1436a0c..28e476d9427 100644 --- a/fvm/evm/handler/addressAllocator.go +++ b/fvm/evm/handler/addressAllocator.go @@ -33,7 +33,7 @@ func (aa *AddressAllocator) AllocateAddress() (types.Address, error) { return types.Address{}, err } // default value for uuid is 1 - uuid := uint64(100) + uuid := uint64(1) if len(data) > 0 { uuid = binary.BigEndian.Uint64(data) } diff --git a/fvm/evm/handler/contracts.go b/fvm/evm/handler/coa.go similarity index 80% rename from fvm/evm/handler/contracts.go rename to fvm/evm/handler/coa.go index 7a925d9d46c..0e81142ffa5 100644 --- a/fvm/evm/handler/contracts.go +++ b/fvm/evm/handler/coa.go @@ -2,116 +2,13 @@ package handler import "encoding/hex" -var COAContractBytes, _ = hex.DecodeString("608060405234801561000f575f80fd5b50610e1e8061001d5f395ff3fe608060405260043610610072575f3560e01c80631626ba7e1161004d5780631626ba7e1461016b578063bc197c81146101a7578063d0d250bd146101e3578063f23a6e611461020d576100c7565b806223de29146100cb57806301ffc9a7146100f3578063150b7a021461012f576100c7565b366100c7573373ffffffffffffffffffffffffffffffffffffffff167fb496ce2863a72c02f90deb616c638c76948f3aff803140881c602775c05fdf3a346040516100bd91906105a5565b60405180910390a2005b5f80fd5b3480156100d6575f80fd5b506100f160048036038101906100ec91906106b4565b610249565b005b3480156100fe575f80fd5b50610119600480360381019061011491906107d3565b610253565b6040516101269190610818565b60405180910390f35b34801561013a575f80fd5b5061015560048036038101906101509190610831565b6103f3565b60405161016291906108c4565b60405180910390f35b348015610176575f80fd5b50610191600480360381019061018c9190610a48565b610407565b60405161019e91906108c4565b60405180910390f35b3480156101b2575f80fd5b506101cd60048036038101906101c89190610af7565b610554565b6040516101da91906108c4565b60405180910390f35b3480156101ee575f80fd5b506101f761056b565b6040516102049190610bdd565b60405180910390f35b348015610218575f80fd5b50610233600480360381019061022e9190610bf6565b610578565b60405161024091906108c4565b60405180910390f35b5050505050505050565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061031d57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061038457507e23de29000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806103ec57507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b5f63150b7a0260e01b905095945050505050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff16858560405160240161043d929190610d15565b6040516020818303038152906040527eb997bd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516104c69190610d7d565b5f60405180830381855afa9150503d805f81146104fe576040519150601f19603f3d011682016040523d82523d5f602084013e610503565b606091505b509150915081610511575f80fd5b5f818060200190518101906105269190610dbd565b9050801561054057631626ba7e60e01b935050505061054e565b63ffffffff60e01b93505050505b92915050565b5f63bc197c8160e01b905098975050505050505050565b6801000000000000000181565b5f63f23a6e6160e01b90509695505050505050565b5f819050919050565b61059f8161058d565b82525050565b5f6020820190506105b85f830184610596565b92915050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6105f8826105cf565b9050919050565b610608816105ee565b8114610612575f80fd5b50565b5f81359050610623816105ff565b92915050565b6106328161058d565b811461063c575f80fd5b50565b5f8135905061064d81610629565b92915050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261067457610673610653565b5b8235905067ffffffffffffffff81111561069157610690610657565b5b6020830191508360018202830111156106ad576106ac61065b565b5b9250929050565b5f805f805f805f8060c0898b0312156106d0576106cf6105c7565b5b5f6106dd8b828c01610615565b98505060206106ee8b828c01610615565b97505060406106ff8b828c01610615565b96505060606107108b828c0161063f565b955050608089013567ffffffffffffffff811115610731576107306105cb565b5b61073d8b828c0161065f565b945094505060a089013567ffffffffffffffff8111156107605761075f6105cb565b5b61076c8b828c0161065f565b92509250509295985092959890939650565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6107b28161077e565b81146107bc575f80fd5b50565b5f813590506107cd816107a9565b92915050565b5f602082840312156107e8576107e76105c7565b5b5f6107f5848285016107bf565b91505092915050565b5f8115159050919050565b610812816107fe565b82525050565b5f60208201905061082b5f830184610809565b92915050565b5f805f805f6080868803121561084a576108496105c7565b5b5f61085788828901610615565b955050602061086888828901610615565b94505060406108798882890161063f565b935050606086013567ffffffffffffffff81111561089a576108996105cb565b5b6108a68882890161065f565b92509250509295509295909350565b6108be8161077e565b82525050565b5f6020820190506108d75f8301846108b5565b92915050565b5f819050919050565b6108ef816108dd565b81146108f9575f80fd5b50565b5f8135905061090a816108e6565b92915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61095a82610914565b810181811067ffffffffffffffff8211171561097957610978610924565b5b80604052505050565b5f61098b6105be565b90506109978282610951565b919050565b5f67ffffffffffffffff8211156109b6576109b5610924565b5b6109bf82610914565b9050602081019050919050565b828183375f83830152505050565b5f6109ec6109e78461099c565b610982565b905082815260208101848484011115610a0857610a07610910565b5b610a138482856109cc565b509392505050565b5f82601f830112610a2f57610a2e610653565b5b8135610a3f8482602086016109da565b91505092915050565b5f8060408385031215610a5e57610a5d6105c7565b5b5f610a6b858286016108fc565b925050602083013567ffffffffffffffff811115610a8c57610a8b6105cb565b5b610a9885828601610a1b565b9150509250929050565b5f8083601f840112610ab757610ab6610653565b5b8235905067ffffffffffffffff811115610ad457610ad3610657565b5b602083019150836020820283011115610af057610aef61065b565b5b9250929050565b5f805f805f805f8060a0898b031215610b1357610b126105c7565b5b5f610b208b828c01610615565b9850506020610b318b828c01610615565b975050604089013567ffffffffffffffff811115610b5257610b516105cb565b5b610b5e8b828c01610aa2565b9650965050606089013567ffffffffffffffff811115610b8157610b806105cb565b5b610b8d8b828c01610aa2565b9450945050608089013567ffffffffffffffff811115610bb057610baf6105cb565b5b610bbc8b828c0161065f565b92509250509295985092959890939650565b610bd7816105ee565b82525050565b5f602082019050610bf05f830184610bce565b92915050565b5f805f805f8060a08789031215610c1057610c0f6105c7565b5b5f610c1d89828a01610615565b9650506020610c2e89828a01610615565b9550506040610c3f89828a0161063f565b9450506060610c5089828a0161063f565b935050608087013567ffffffffffffffff811115610c7157610c706105cb565b5b610c7d89828a0161065f565b92509250509295509295509295565b610c95816108dd565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610cd2578082015181840152602081019050610cb7565b5f8484015250505050565b5f610ce782610c9b565b610cf18185610ca5565b9350610d01818560208601610cb5565b610d0a81610914565b840191505092915050565b5f604082019050610d285f830185610c8c565b8181036020830152610d3a8184610cdd565b90509392505050565b5f81905092915050565b5f610d5782610c9b565b610d618185610d43565b9350610d71818560208601610cb5565b80840191505092915050565b5f610d888284610d4d565b915081905092915050565b610d9c816107fe565b8114610da6575f80fd5b50565b5f81519050610db781610d93565b92915050565b5f60208284031215610dd257610dd16105c7565b5b5f610ddf84828501610da9565b9150509291505056fea2646970667358221220c5a50c64c66e3bfb048a9c796b1fd390d35974be1136aed5e4c00cf2fc62c55164736f6c63430008160033") - -var COAContractCode = ` - // SPDX-License-Identifier: UNLICENSED - pragma solidity >=0.7.0 <0.9.0; - interface IERC165 { - function supportsInterface(bytes4 interfaceId) external view returns (bool); - } - interface ERC721TokenReceiver { - function onERC721Received( - address _operator, - address _from, - uint256 _tokenId, - bytes calldata _data - ) external returns (bytes4); - } - interface ERC777TokensRecipient { - function tokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes calldata data, - bytes calldata operatorData - ) external; - } - interface ERC1155TokenReceiver { - function onERC1155Received( - address _operator, - address _from, - uint256 _id, - uint256 _value, - bytes calldata _data - ) external returns (bytes4); - function onERC1155BatchReceived( - address _operator, - address _from, - uint256[] calldata _ids, - uint256[] calldata _values, - bytes calldata _data - ) external returns (bytes4); - } - contract COA is ERC1155TokenReceiver, ERC777TokensRecipient, ERC721TokenReceiver, IERC165 { - address constant public cadenceArch = 0x0000000000000000000000010000000000000001; - event FlowReceived(address indexed sender, uint256 value); - receive() external payable { - emit FlowReceived(msg.sender, msg.value); - } - function supportsInterface(bytes4 id) external view virtual override returns (bool) { - return - id == type(ERC1155TokenReceiver).interfaceId || - id == type(ERC721TokenReceiver).interfaceId || - id == type(ERC777TokensRecipient).interfaceId || - id == type(IERC165).interfaceId; - } - - function onERC1155Received( - address, - address, - uint256, - uint256, - bytes calldata - ) external pure override returns (bytes4) { - return 0xf23a6e61; - } +var COAContractDeploymentRequiredGas = uint64(723_000) - function onERC1155BatchReceived( - address, - address, - uint256[] calldata, - uint256[] calldata, - bytes calldata - ) external pure override returns (bytes4) { - return 0xbc197c81; - } - - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) external pure override returns (bytes4) { - return 0x150b7a02; - } - - function tokensReceived( - address, - address, - address, - uint256, - bytes calldata, - bytes calldata - ) external pure override {} - - function isValidSignature( - bytes32 _hash, - bytes memory _sig - ) external view virtual returns (bytes4){ - (bool ok, bytes memory data) = cadenceArch.staticcall(abi.encodeWithSignature("verifyCOAOwnershipProof(bytes32, bytes)", _hash, _sig)); - require(ok); - bool output = abi.decode(data, (bool)); - if (output) { - return 0x1626ba7e; - } - return 0xffffffff; - } - } -` +// COAContractBytes is the compiled version of the coa smart contract. +var COAContractBytes, _ = hex.DecodeString("608060405234801561000f575f80fd5b50610e1e8061001d5f395ff3fe608060405260043610610072575f3560e01c80631626ba7e1161004d5780631626ba7e1461016b578063bc197c81146101a7578063d0d250bd146101e3578063f23a6e611461020d576100c7565b806223de29146100cb57806301ffc9a7146100f3578063150b7a021461012f576100c7565b366100c7573373ffffffffffffffffffffffffffffffffffffffff167fb496ce2863a72c02f90deb616c638c76948f3aff803140881c602775c05fdf3a346040516100bd91906105a5565b60405180910390a2005b5f80fd5b3480156100d6575f80fd5b506100f160048036038101906100ec91906106b4565b610249565b005b3480156100fe575f80fd5b50610119600480360381019061011491906107d3565b610253565b6040516101269190610818565b60405180910390f35b34801561013a575f80fd5b5061015560048036038101906101509190610831565b6103f3565b60405161016291906108c4565b60405180910390f35b348015610176575f80fd5b50610191600480360381019061018c9190610a48565b610407565b60405161019e91906108c4565b60405180910390f35b3480156101b2575f80fd5b506101cd60048036038101906101c89190610af7565b610554565b6040516101da91906108c4565b60405180910390f35b3480156101ee575f80fd5b506101f761056b565b6040516102049190610bdd565b60405180910390f35b348015610218575f80fd5b50610233600480360381019061022e9190610bf6565b610578565b60405161024091906108c4565b60405180910390f35b5050505050505050565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061031d57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061038457507e23de29000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806103ec57507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b5f63150b7a0260e01b905095945050505050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff16858560405160240161043d929190610d15565b6040516020818303038152906040527eb997bd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516104c69190610d7d565b5f60405180830381855afa9150503d805f81146104fe576040519150601f19603f3d011682016040523d82523d5f602084013e610503565b606091505b509150915081610511575f80fd5b5f818060200190518101906105269190610dbd565b9050801561054057631626ba7e60e01b935050505061054e565b63ffffffff60e01b93505050505b92915050565b5f63bc197c8160e01b905098975050505050505050565b6801000000000000000181565b5f63f23a6e6160e01b90509695505050505050565b5f819050919050565b61059f8161058d565b82525050565b5f6020820190506105b85f830184610596565b92915050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6105f8826105cf565b9050919050565b610608816105ee565b8114610612575f80fd5b50565b5f81359050610623816105ff565b92915050565b6106328161058d565b811461063c575f80fd5b50565b5f8135905061064d81610629565b92915050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261067457610673610653565b5b8235905067ffffffffffffffff81111561069157610690610657565b5b6020830191508360018202830111156106ad576106ac61065b565b5b9250929050565b5f805f805f805f8060c0898b0312156106d0576106cf6105c7565b5b5f6106dd8b828c01610615565b98505060206106ee8b828c01610615565b97505060406106ff8b828c01610615565b96505060606107108b828c0161063f565b955050608089013567ffffffffffffffff811115610731576107306105cb565b5b61073d8b828c0161065f565b945094505060a089013567ffffffffffffffff8111156107605761075f6105cb565b5b61076c8b828c0161065f565b92509250509295985092959890939650565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6107b28161077e565b81146107bc575f80fd5b50565b5f813590506107cd816107a9565b92915050565b5f602082840312156107e8576107e76105c7565b5b5f6107f5848285016107bf565b91505092915050565b5f8115159050919050565b610812816107fe565b82525050565b5f60208201905061082b5f830184610809565b92915050565b5f805f805f6080868803121561084a576108496105c7565b5b5f61085788828901610615565b955050602061086888828901610615565b94505060406108798882890161063f565b935050606086013567ffffffffffffffff81111561089a576108996105cb565b5b6108a68882890161065f565b92509250509295509295909350565b6108be8161077e565b82525050565b5f6020820190506108d75f8301846108b5565b92915050565b5f819050919050565b6108ef816108dd565b81146108f9575f80fd5b50565b5f8135905061090a816108e6565b92915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61095a82610914565b810181811067ffffffffffffffff8211171561097957610978610924565b5b80604052505050565b5f61098b6105be565b90506109978282610951565b919050565b5f67ffffffffffffffff8211156109b6576109b5610924565b5b6109bf82610914565b9050602081019050919050565b828183375f83830152505050565b5f6109ec6109e78461099c565b610982565b905082815260208101848484011115610a0857610a07610910565b5b610a138482856109cc565b509392505050565b5f82601f830112610a2f57610a2e610653565b5b8135610a3f8482602086016109da565b91505092915050565b5f8060408385031215610a5e57610a5d6105c7565b5b5f610a6b858286016108fc565b925050602083013567ffffffffffffffff811115610a8c57610a8b6105cb565b5b610a9885828601610a1b565b9150509250929050565b5f8083601f840112610ab757610ab6610653565b5b8235905067ffffffffffffffff811115610ad457610ad3610657565b5b602083019150836020820283011115610af057610aef61065b565b5b9250929050565b5f805f805f805f8060a0898b031215610b1357610b126105c7565b5b5f610b208b828c01610615565b9850506020610b318b828c01610615565b975050604089013567ffffffffffffffff811115610b5257610b516105cb565b5b610b5e8b828c01610aa2565b9650965050606089013567ffffffffffffffff811115610b8157610b806105cb565b5b610b8d8b828c01610aa2565b9450945050608089013567ffffffffffffffff811115610bb057610baf6105cb565b5b610bbc8b828c0161065f565b92509250509295985092959890939650565b610bd7816105ee565b82525050565b5f602082019050610bf05f830184610bce565b92915050565b5f805f805f8060a08789031215610c1057610c0f6105c7565b5b5f610c1d89828a01610615565b9650506020610c2e89828a01610615565b9550506040610c3f89828a0161063f565b9450506060610c5089828a0161063f565b935050608087013567ffffffffffffffff811115610c7157610c706105cb565b5b610c7d89828a0161065f565b92509250509295509295509295565b610c95816108dd565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610cd2578082015181840152602081019050610cb7565b5f8484015250505050565b5f610ce782610c9b565b610cf18185610ca5565b9350610d01818560208601610cb5565b610d0a81610914565b840191505092915050565b5f604082019050610d285f830185610c8c565b8181036020830152610d3a8184610cdd565b90509392505050565b5f81905092915050565b5f610d5782610c9b565b610d618185610d43565b9350610d71818560208601610cb5565b80840191505092915050565b5f610d888284610d4d565b915081905092915050565b610d9c816107fe565b8114610da6575f80fd5b50565b5f81519050610db781610d93565b92915050565b5f60208284031215610dd257610dd16105c7565b5b5f610ddf84828501610da9565b9150509291505056fea2646970667358221220c5a50c64c66e3bfb048a9c796b1fd390d35974be1136aed5e4c00cf2fc62c55164736f6c63430008160033") -var COAContractABI = ` +// COAContractABIJSON is the json string of ABI of the coa smart contract. +var COAContractABIJSON = ` [ { "anonymous": false, diff --git a/fvm/evm/handler/coa.sol b/fvm/evm/handler/coa.sol new file mode 100644 index 00000000000..f81b7840320 --- /dev/null +++ b/fvm/evm/handler/coa.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.7.0 <0.9.0; +interface IERC165 { + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} +interface ERC721TokenReceiver { + function onERC721Received( + address _operator, + address _from, + uint256 _tokenId, + bytes calldata _data + ) external returns (bytes4); +} +interface ERC777TokensRecipient { +function tokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes calldata data, + bytes calldata operatorData + ) external; +} +interface ERC1155TokenReceiver { +function onERC1155Received( + address _operator, + address _from, + uint256 _id, + uint256 _value, + bytes calldata _data + ) external returns (bytes4); +function onERC1155BatchReceived( + address _operator, + address _from, + uint256[] calldata _ids, + uint256[] calldata _values, + bytes calldata _data + ) external returns (bytes4); +} +contract COA is ERC1155TokenReceiver, ERC777TokensRecipient, ERC721TokenReceiver, IERC165 { + address constant public cadenceArch = 0x0000000000000000000000010000000000000001; + event FlowReceived(address indexed sender, uint256 value); + receive() external payable { + emit FlowReceived(msg.sender, msg.value); + } + function supportsInterface(bytes4 id) external view virtual override returns (bool) { + return + id == type(ERC1155TokenReceiver).interfaceId || + id == type(ERC721TokenReceiver).interfaceId || + id == type(ERC777TokensRecipient).interfaceId || + id == type(IERC165).interfaceId; + } + + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes calldata + ) external pure override returns (bytes4) { + return 0xf23a6e61; + } + + function onERC1155BatchReceived( + address, + address, + uint256[] calldata, + uint256[] calldata, + bytes calldata + ) external pure override returns (bytes4) { + return 0xbc197c81; + } + + function onERC721Received( + address, + address, + uint256, + bytes calldata + ) external pure override returns (bytes4) { + return 0x150b7a02; + } + + function tokensReceived( + address, + address, + address, + uint256, + bytes calldata, + bytes calldata + ) external pure override {} + + function isValidSignature( + bytes32 _hash, + bytes memory _sig + ) external view virtual returns (bytes4){ + (bool ok, bytes memory data) = cadenceArch.staticcall(abi.encodeWithSignature("verifyCOAOwnershipProof(bytes32, bytes)", _hash, _sig)); + require(ok); + bool output = abi.decode(data, (bool)); + if (output) { + return 0x1626ba7e; + } + return 0xffffffff; + } +} \ No newline at end of file diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index d2ebd1015af..403d9e73634 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -2,6 +2,7 @@ package handler import ( "bytes" + "fmt" "math/big" gethTypes "github.com/ethereum/go-ethereum/core/types" @@ -56,7 +57,7 @@ func NewContractHandler( // DeployACOAAccount deploys a cadence-owned-account and returns the address func (h *ContractHandler) DeployACOAAccount() types.Address { target, err := h.addressAllocator.AllocateAddress() - gaslimit := types.GasLimit(30_000_000) // TODO figure out me + gaslimit := types.GasLimit(COAContractDeploymentRequiredGas) handleError(err) h.checkGasLimit(gaslimit) @@ -70,9 +71,12 @@ func (h *ContractHandler) DeployACOAAccount() types.Address { uint64(gaslimit), new(big.Int), ) - // TODO use the returned result - h.executeAndHandleCall(h.getBlockContext(), call, 0, false) - return target + res := h.executeAndHandleCall(h.getBlockContext(), call, 0, false) + // check deployed contract address matches + if res.DeployedContractAddress != target { + handleError(fmt.Errorf("coa contract deployment failure for address %x", target)) + } + return res.DeployedContractAddress } // AccountByAddress returns the account for the given address, diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 2cb7c26d177..a5d95d28ef2 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -367,7 +367,7 @@ func TestHandler_BridgedAccount(t *testing.T) { ret := acc2.Call( coa, testutils.MakeCallData(t, - handler.COAContractABI, + handler.COAContractABIJSON, "onERC721Received", gethCommon.Address{1}, gethCommon.Address{1}, From 6e92abc74862cb240585dae7f61d2c0e72d32667 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 23 Jan 2024 15:06:48 -0800 Subject: [PATCH 12/38] rename bridged to COA --- fvm/evm/emulator/emulator.go | 4 ++-- fvm/evm/evm_test.go | 2 +- fvm/evm/handler/handler.go | 4 ++-- fvm/evm/handler/handler_test.go | 2 +- fvm/evm/types/account.go | 16 ++++++++-------- fvm/evm/types/call.go | 2 +- fvm/evm/types/handler.go | 9 +++++---- 7 files changed, 20 insertions(+), 19 deletions(-) diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index 70c393e7fcc..44181d876cf 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -212,8 +212,8 @@ func (proc *procedure) withdrawFrom(address types.Address, amount *big.Int) (*ty } // check if account exists - // while this method is only called from bridged accounts - // it might be the case that someone creates a bridged account + // while this method is only called for COAs + // it might be the case that someone creates a COA // and never transfer tokens to and call for withdraw if !proc.state.Exist(addr) { proc.state.CreateAccount(addr) diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index 6108b57aaaf..4797500f930 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -165,7 +165,7 @@ func TestEVMAddressDeposit(t *testing.T) { }) } -func TestBridgedAccountWithdraw(t *testing.T) { +func TestCOAWithdraw(t *testing.T) { t.Parallel() diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index 403d9e73634..448a2ad89bd 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -80,7 +80,7 @@ func (h *ContractHandler) DeployACOAAccount() types.Address { } // AccountByAddress returns the account for the given address, -// if isAuthorized is set, account is controlled by the FVM (bridged accounts) +// if isAuthorized is set, account is controlled by the FVM (COAs) func (h *ContractHandler) AccountByAddress(addr types.Address, isAuthorized bool) types.Account { return newAccount(h, addr, isAuthorized) } @@ -375,7 +375,7 @@ func (a *Account) Call(to types.Address, data types.Data, gaslimit types.GasLimi } func (a *Account) checkAuthorized() { - // check if account is authorized (i.e. is a bridged account) + // check if account is authorized (i.e. is a COA) if !a.isAuthorized { handleError(types.ErrUnAuthroizedMethodCall) } diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index a5d95d28ef2..1f97306aac0 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -275,7 +275,7 @@ func TestHandler_OpsWithoutEmulator(t *testing.T) { } -func TestHandler_BridgedAccount(t *testing.T) { +func TestHandler_COA(t *testing.T) { t.Parallel() t.Run("test deposit/withdraw (with integrated emulator)", func(t *testing.T) { testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { diff --git a/fvm/evm/types/account.go b/fvm/evm/types/account.go index fc63786b986..54edad8f0b3 100644 --- a/fvm/evm/types/account.go +++ b/fvm/evm/types/account.go @@ -2,13 +2,13 @@ package types // Account is an EVM account, currently // three types of accounts are supported on Flow EVM, -// externally owned accounts (EOAs), smart contract accounts and bridged accounts -// BridgedAccount is a new type of account in the environment, +// externally owned accounts (EOAs), smart contract accounts and cadence owned accounts +// Cadence-owned-account (COA) is a new type of account in the environment, // that instead of being managed by public key, // it is managed by a resource owned by a Flow account. // // In other words, the FVM account who owns the FOA resource -// can bridge native tokens to and from the account associated with the bridged account, +// can bridge native tokens to and from the account associated with the COA, // deploy contracts to the environment, // or call methods on contracts without the need to sign a transaction. type Account interface { @@ -29,7 +29,7 @@ type Account interface { // Withdraw withdraws the balance from account and // return it as a FlowTokenVault - // works only for bridged accounts + // works only for COAs Withdraw(Balance) *FLOWTokenVault // Transfer is a utility method on top of call for transfering tokens to another account @@ -37,17 +37,17 @@ type Account interface { // Deploy deploys a contract to the environment // the new deployed contract would be at the returned address and - // the contract data is not controlled by the bridge account - // works only for bridged accounts + // the contract data is not controlled by the COA + // works only for COAs Deploy(Code, GasLimit, Balance) Address // Call calls a smart contract function with the given data. // The gas usage is limited by the given gas limit, // and the Flow transaction's computation limit. - // The fees are deducted from the bridged account + // The fees are deducted from the COA // and are transferred to the target address. // if no data is provided it would behave as transfering tokens to the // target address - // works only for bridged accounts + // works only for COAs Call(Address, Data, GasLimit, Balance) Data } diff --git a/fvm/evm/types/call.go b/fvm/evm/types/call.go index c73901f57d0..3979e8cafb1 100644 --- a/fvm/evm/types/call.go +++ b/fvm/evm/types/call.go @@ -10,7 +10,7 @@ import ( ) const ( - // tx type 255 is used for direct calls from bridged accounts + // tx type 255 is used for direct calls from COAs DirectCallTxType = byte(255) UnknownCallSubType = byte(0) diff --git a/fvm/evm/types/handler.go b/fvm/evm/types/handler.go index fb515433752..e8541f0e388 100644 --- a/fvm/evm/types/handler.go +++ b/fvm/evm/types/handler.go @@ -15,12 +15,12 @@ import ( // First, passing a signed transaction (EOA account) to the `EVM.run` Cadence function // creates a new block, updates the internal merkle tree, and emits a new root hash. // -// The Second way is through a new form of account called bridged accounts, +// The Second way is through a new form of account called cadence-owned-accounts (COAs), // which is represented and controlled through a resource, owned by a Flow account. -// The owner of the bridged account resource can interact with the evm environment on behalf of the address stored on the resource. +// The owner of the COA resource can interact with the evm environment on behalf of the address stored on the resource. // // The evm environment shares the same native token as Flow, there are no new tokens minted. -// Other ERC-20 fungible tokens can be bridged between bridged account resources and Flow accounts. +// Other ERC-20 fungible tokens can be bridged between COA resources and Flow accounts. // ContractHandler handles operations on the evm environment type ContractHandler interface { @@ -39,6 +39,7 @@ type ContractHandler interface { // collects the gas fees, and transfers the gas fees to the given coinbase account. Run(tx []byte, coinbase Address) + // FlowTokenAddress returns the address where FLOW token is deployed FlowTokenAddress() common.Address } @@ -51,7 +52,7 @@ type Backend interface { // AddressAllocator allocates addresses, used by the handler type AddressAllocator interface { - // AllocateAddress allocates an address to be used by a bridged account resource + // AllocateAddress allocates an address to be used by a COA resource AllocateAddress() (Address, error) } From 32a624f9a6bd5411618107b2237a025b00a7f027 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 23 Jan 2024 15:23:28 -0800 Subject: [PATCH 13/38] cleanup --- fvm/evm/handler/handler.go | 2 -- fvm/evm/handler/handler_test.go | 7 +------ fvm/evm/types/call.go | 10 ++++++---- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index cb5d6efe402..ca8d80c1c11 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -264,8 +264,6 @@ func (a *Account) Balance() types.Balance { bl, err := blk.BalanceOf(a.address) handleError(err) - // TODO: this might cause issues, we might need to update the - // way we represent balance balance, err := types.NewBalanceFromAttoFlow(bl) handleError(err) return balance diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 4274a126fdb..e7861bb314f 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -366,11 +366,6 @@ func TestHandler_COA(t *testing.T) { h := SetupHandler(t, backend, rootAddr) coa := h.DeployACOAAccount() - require.NotNil(t, coa) - - // expectedAddress := types.NewAddress(gethCommon.HexToAddress("0x00000000000000000001")) - // require.Equal(t, expectedAddress, coa) - acc := h.AccountByAddress(coa, true) require.NotEmpty(t, acc.Code()) @@ -379,7 +374,7 @@ func TestHandler_COA(t *testing.T) { acc2 := h.AccountByAddress(coa2, true) acc2.Deposit(types.NewFlowTokenVault(makeABalanceInFlow(100))) - // transfer money + // transfer money to COA acc2.Transfer( coa, makeABalanceInFlow(1), diff --git a/fvm/evm/types/call.go b/fvm/evm/types/call.go index 3979e8cafb1..75ceb25971c 100644 --- a/fvm/evm/types/call.go +++ b/fvm/evm/types/call.go @@ -20,8 +20,10 @@ const ( DeployCallSubType = byte(4) ContractCallSubType = byte(5) - TransferGasUsage = 21_000 - // 21_000 for transaction + gas limit for receive and fallback + DepositCallGasLimit = 21_000 + WithdrawCallGasLimit = 21_000 + + // 21_000 is the minimum for a transaction + max gas allowed for receive/fallback methods DefaultGasLimitForTokenTransfer = 21_000 + 2_300 ) @@ -85,7 +87,7 @@ func NewDepositCall(address Address, amount *big.Int) *DirectCall { To: address, Data: nil, Value: amount, - GasLimit: TransferGasUsage, + GasLimit: DepositCallGasLimit, } } @@ -97,7 +99,7 @@ func NewWithdrawCall(address Address, amount *big.Int) *DirectCall { To: EmptyAddress, Data: nil, Value: amount, - GasLimit: TransferGasUsage, + GasLimit: WithdrawCallGasLimit, } } From 5321f97a14fb6823021b33a71158ba562da940a5 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 23 Jan 2024 18:14:01 -0800 Subject: [PATCH 14/38] fix tests --- fvm/evm/handler/handler.go | 54 --------------------------------- fvm/evm/handler/handler_test.go | 27 ++--------------- 2 files changed, 3 insertions(+), 78 deletions(-) diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index ca8d80c1c11..34112179260 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -381,60 +381,6 @@ func (a *Account) Call(to types.Address, data types.Data, gaslimit types.GasLimi return res.ReturnedValue } -func (a *Account) executeAndHandleCall( - ctx types.BlockContext, - call *types.DirectCall, - totalSupplyDiff *big.Int, - deductSupplyDiff bool, -) *types.Result { - // execute the call - blk, err := a.fch.emulator.NewBlockView(ctx) - handleError(err) - - res, err := blk.DirectCall(call) - a.fch.meterGasUsage(res) - handleError(err) - - // update block proposal - callHash, err := call.Hash() - if err != nil { - err = types.NewFatalError(err) - handleError(err) - } - - bp, err := a.fch.blockstore.BlockProposal() - handleError(err) - bp.AppendTxHash(callHash) - if totalSupplyDiff != nil { - if deductSupplyDiff { - bp.TotalSupply = new(big.Int).Sub(bp.TotalSupply, totalSupplyDiff) - } else { - bp.TotalSupply = new(big.Int).Add(bp.TotalSupply, totalSupplyDiff) - } - - } - - // emit events - encoded, err := call.Encode() - handleError(err) - - a.fch.emitEvent( - types.NewTransactionExecutedEvent( - bp.Height, - encoded, - callHash, - res, - ), - ) - a.fch.emitEvent(types.NewBlockExecutedEvent(bp)) - - // commit block proposal - err = a.fch.blockstore.CommitBlockProposal() - handleError(err) - - return res -} - func (a *Account) checkAuthorized() { // check if account is authorized (i.e. is a COA) if !a.isAuthorized { diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index e7861bb314f..41ad385e68b 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -238,8 +238,9 @@ func TestHandler_TransactionRun(t *testing.T) { handler.Run(tx, account2.Address()) require.Equal(t, orgBalance.Sub(deduction).Add(addition), foa.Balance()) + // TODO: unlock me when balance changes are merged // fees has been collected to the coinbase - require.NotEqual(t, types.Balance(0), account2.Balance()) + // require.NotEqual(t, types.Balance(0), account2.Balance()) }) }) @@ -274,27 +275,6 @@ func TestHandler_OpsWithoutEmulator(t *testing.T) { }) }) - t.Run("test address allocation", func(t *testing.T) { - t.Parallel() - - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { - testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { - blockchain, err := handler.NewBlockStore(backend, rootAddr) - require.NoError(t, err) - - aa, err := handler.NewAddressAllocator(backend, rootAddr) - require.NoError(t, err) - - h := handler.NewContractHandler(flowTokenAddress, blockchain, aa, backend, nil) - - foa := h.DeployACOAAccount() - require.NotNil(t, foa) - - expectedAddress := handler.MakeCOAAddress(1) - require.Equal(t, expectedAddress, foa) - }) - }) - }) } func TestHandler_COA(t *testing.T) { @@ -355,7 +335,7 @@ func TestHandler_COA(t *testing.T) { // check gas usage computationUsed, err := backend.ComputationUsed() require.NoError(t, err) - require.Equal(t, types.DefaultDirectCallBaseGasUsage*2, computationUsed) + require.Greater(t, computationUsed, types.DefaultDirectCallBaseGasUsage*3) }) }) }) @@ -533,7 +513,6 @@ func TestHandler_COA(t *testing.T) { testContract := testutils.GetStorageTestContract(t) addr := foa.Deploy(testContract.ByteCode, math.MaxUint64, types.Balance(0)) - require.NotNil(t, addr) num := big.NewInt(22) From a89f0bdb26dc369d71c8e8b954f15efda86f301d Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 23 Jan 2024 18:47:18 -0800 Subject: [PATCH 15/38] bug fix --- fvm/evm/emulator/emulator.go | 5 ++++- fvm/evm/handler/handler_test.go | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index e4bfb569a3e..8b8f4fb084d 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -116,7 +116,10 @@ func (bl *BlockView) DirectCall(call *types.DirectCall) (*types.Result, error) { } fallthrough default: - return proc.run(call.Message(), types.DirectCallTxType) + // set message nonce (needed for deploy) + msg := call.Message() + msg.Nonce = proc.state.GetNonce(call.From.ToCommon()) + return proc.run(msg, types.DirectCallTxType) } } diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 41ad385e68b..299f260030b 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -508,14 +508,15 @@ func TestHandler_COA(t *testing.T) { require.NotNil(t, foa) // deposit 10000 flow - vault := types.NewFlowTokenVault(testutils.MakeABalanceInFlow(10000)) + bal := testutils.MakeABalanceInFlow(10000) + vault := types.NewFlowTokenVault(bal) foa.Deposit(vault) + require.Equal(t, bal, foa.Balance()) testContract := testutils.GetStorageTestContract(t) addr := foa.Deploy(testContract.ByteCode, math.MaxUint64, types.Balance(0)) num := big.NewInt(22) - _ = foa.Call( addr, testContract.MakeCallData(t, "store", num), From 9170c64b855e32d84de0ebbf5e66744b629300f6 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 23 Jan 2024 20:19:35 -0800 Subject: [PATCH 16/38] update factory address --- fvm/evm/emulator/emulator.go | 8 ++++++++ fvm/evm/handler/addressAllocator.go | 4 ++++ fvm/evm/handler/addressAllocator_test.go | 8 +++++--- fvm/evm/handler/handler.go | 4 +--- fvm/evm/types/handler.go | 3 +++ 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index 8b8f4fb084d..3f32bfe090c 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -273,6 +273,14 @@ func (proc *procedure) deployAt( return res, gethVM.ErrContractAddressCollision } + callerCommon := caller.ToCommon() + // setup caller if doesn't exist + if !proc.state.Exist(callerCommon) { + proc.state.CreateAccount(callerCommon) + } + // increment the nonce for the caller + proc.state.SetNonce(callerCommon, proc.state.GetNonce(callerCommon)+1) + // setup account proc.state.CreateAccount(addr) proc.state.SetNonce(addr, 1) // (EIP-158) diff --git a/fvm/evm/handler/addressAllocator.go b/fvm/evm/handler/addressAllocator.go index d1dc8299130..55a20527acf 100644 --- a/fvm/evm/handler/addressAllocator.go +++ b/fvm/evm/handler/addressAllocator.go @@ -38,6 +38,10 @@ func NewAddressAllocator(led atree.Ledger, flexAddress flow.Address) (*AddressAl }, nil } +func (aa *AddressAllocator) COAFactoryAddress() types.Address { + return MakeCOAAddress(0) +} + // AllocateCOAAddress allocates an address for COA func (aa *AddressAllocator) AllocateCOAAddress() (types.Address, error) { data, err := aa.led.GetValue(aa.flexAddress[:], []byte(ledgerAddressAllocatorKey)) diff --git a/fvm/evm/handler/addressAllocator_test.go b/fvm/evm/handler/addressAllocator_test.go index 03794baea9a..a2729ba4028 100644 --- a/fvm/evm/handler/addressAllocator_test.go +++ b/fvm/evm/handler/addressAllocator_test.go @@ -13,7 +13,6 @@ import ( ) func TestAddressAllocator(t *testing.T) { - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(root flow.Address) { aa, err := handler.NewAddressAllocator(backend, root) @@ -34,8 +33,11 @@ func TestAddressAllocator(t *testing.T) { require.NoError(t, err) expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x0000000000000000000000020000000000000002")) require.Equal(t, expectedAddress, adr) - }) + // factory + factory := aa.COAFactoryAddress() + expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x0000000000000000000000020000000000000000")) + require.Equal(t, expectedAddress, factory) + }) }) - } diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index 34112179260..7960a98d392 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -68,9 +68,7 @@ func (h *ContractHandler) DeployACOAAccount() types.Address { handleError(err) h.checkGasLimit(gaslimit) - // TODO replace the caller - factory := types.Address{0, 0, 0, 0, 0, 1, 0, 0} - + factory := h.addressAllocator.COAFactoryAddress() call := types.NewDeployCallWithTargetAddress( factory, target, diff --git a/fvm/evm/types/handler.go b/fvm/evm/types/handler.go index 081994edd65..1ca94a61e4d 100644 --- a/fvm/evm/types/handler.go +++ b/fvm/evm/types/handler.go @@ -56,6 +56,9 @@ type AddressAllocator interface { // AllocateAddress allocates an address to be used by a COA resource AllocateCOAAddress() (Address, error) + // COAFactoryAddress returns the address for the COA factory + COAFactoryAddress() Address + // AllocateAddress allocates an address by index to be used by a precompile contract AllocatePrecompileAddress(index uint64) Address } From 98aad4de4ab7231b2e3e634a7f4f918a1ff4ce51 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 23 Jan 2024 22:10:56 -0800 Subject: [PATCH 17/38] update test --- fvm/evm/handler/handler.go | 5 ----- fvm/fvm_test.go | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index 7960a98d392..438caca8f4e 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -2,7 +2,6 @@ package handler import ( "bytes" - "fmt" "math/big" gethTypes "github.com/ethereum/go-ethereum/core/types" @@ -77,10 +76,6 @@ func (h *ContractHandler) DeployACOAAccount() types.Address { new(big.Int), ) res := h.executeAndHandleCall(h.getBlockContext(), call, nil, false) - // check deployed contract address matches - if res.DeployedContractAddress != target { - handleError(fmt.Errorf("coa contract deployment failure for address %x", target)) - } return res.DeployedContractAddress } diff --git a/fvm/fvm_test.go b/fvm/fvm_test.go index 0d377c1b445..a95f3bfd692 100644 --- a/fvm/fvm_test.go +++ b/fvm/fvm_test.go @@ -3208,10 +3208,10 @@ func TestEVM(t *testing.T) { require.NoError(t, err) require.NoError(t, output.Err) - require.Len(t, output.Events, 3) + require.Len(t, output.Events, 5) evmLocation := types.EVMLocation{} - txExe, blockExe := output.Events[1], output.Events[2] + txExe, blockExe := output.Events[3], output.Events[4] assert.Equal(t, evmLocation.TypeID(nil, string(types.EventTypeTransactionExecuted)), common.TypeID(txExe.Type)) assert.Equal(t, evmLocation.TypeID(nil, string(types.EventTypeBlockExecuted)), common.TypeID(blockExe.Type)) }), From 76b8b383c9b6a611562897ec8a5dfa2ccaee0e5b Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 24 Jan 2024 11:28:42 -0800 Subject: [PATCH 18/38] apply pr feedback --- fvm/evm/emulator/emulator.go | 10 +++++----- fvm/evm/emulator/emulator_test.go | 6 ++++-- fvm/evm/handler/handler.go | 4 ++-- fvm/evm/handler/handler_benchmark_test.go | 2 +- fvm/evm/handler/handler_test.go | 14 +++++++------- fvm/evm/stdlib/contract.go | 2 +- fvm/evm/stdlib/contract_test.go | 4 ++-- fvm/evm/types/call.go | 8 ++++---- fvm/evm/types/handler.go | 4 ++-- 9 files changed, 28 insertions(+), 26 deletions(-) diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index 3f32bfe090c..333ea74e594 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -111,15 +111,12 @@ func (bl *BlockView) DirectCall(call *types.DirectCall) (*types.Result, error) { case types.WithdrawCallSubType: return proc.withdrawFrom(call.From, call.Value) case types.DeployCallSubType: - if call.HasNonEmptyToField() { + if !call.EmptyToField() { return proc.deployAt(call.From, call.To, call.Data, call.GasLimit, call.Value) } fallthrough default: - // set message nonce (needed for deploy) - msg := call.Message() - msg.Nonce = proc.state.GetNonce(call.From.ToCommon()) - return proc.run(msg, types.DirectCallTxType) + return proc.run(call.Message(), types.DirectCallTxType) } } @@ -354,6 +351,9 @@ func (proc *procedure) run(msg *gethCore.Message, txType uint8) (*types.Result, TxType: txType, } + // set the nonce for the message (needed for some opeartions like deployment) + msg.Nonce = proc.state.GetNonce(msg.From) + gasPool := (*gethCore.GasPool)(&proc.config.BlockContext.GasLimit) execResult, err := gethCore.NewStateTransition( proc.evm, diff --git a/fvm/evm/emulator/emulator_test.go b/fvm/evm/emulator/emulator_test.go index 7e950369fce..8d8aa6721a0 100644 --- a/fvm/evm/emulator/emulator_test.go +++ b/fvm/evm/emulator/emulator_test.go @@ -1,12 +1,14 @@ package emulator_test import ( + "fmt" "math" "math/big" "testing" gethCommon "github.com/ethereum/go-ethereum/common" gethTypes "github.com/ethereum/go-ethereum/core/types" + gethVM "github.com/ethereum/go-ethereum/core/vm" gethParams "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/require" @@ -344,7 +346,7 @@ func TestDeployAtFunctionality(t *testing.T) { math.MaxUint64, amountToBeTransfered), ) - require.Error(t, err) + require.Equal(t, gethVM.ErrContractAddressCollision, err) }) // test deployment with not enough gas RunWithNewBlockView(t, env, func(blk types.BlockView) { @@ -356,7 +358,7 @@ func TestDeployAtFunctionality(t *testing.T) { 100, new(big.Int)), ) - require.Error(t, err) + require.Equal(t, fmt.Errorf("out of gas"), err) }) }) }) diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index 438caca8f4e..13573799cdc 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -60,8 +60,8 @@ func getPrecompiles( return []types.Precompile{archContract} } -// DeployACOAAccount deploys a cadence-owned-account and returns the address -func (h *ContractHandler) DeployACOAAccount() types.Address { +// DeployCOA deploys a cadence-owned-account and returns the address +func (h *ContractHandler) DeployCOA() types.Address { target, err := h.addressAllocator.AllocateCOAAddress() gaslimit := types.GasLimit(COAContractDeploymentRequiredGas) handleError(err) diff --git a/fvm/evm/handler/handler_benchmark_test.go b/fvm/evm/handler/handler_benchmark_test.go index c6e824217da..7d2ef22f009 100644 --- a/fvm/evm/handler/handler_benchmark_test.go +++ b/fvm/evm/handler/handler_benchmark_test.go @@ -26,7 +26,7 @@ func benchmarkStorageGrowth(b *testing.B, accountCount, setupKittyCount int) { // setup several of accounts // note that trie growth is the function of number of accounts for i := 0; i < accountCount; i++ { - account := handler.AccountByAddress(handler.DeployACOAAccount(), true) + account := handler.AccountByAddress(handler.DeployCOA(), true) account.Deposit(types.NewFlowTokenVault(types.Balance(100))) accounts[i] = account } diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 299f260030b..5539af9707f 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -203,7 +203,7 @@ func TestHandler_TransactionRun(t *testing.T) { eoa := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex) // deposit 1 Flow to the foa account - addr := handler.DeployACOAAccount() + addr := handler.DeployCOA() orgBalance, err := types.NewBalanceFromAttoFlow(types.OneFlowInAttoFlow) require.NoError(t, err) vault := types.NewFlowTokenVault(orgBalance) @@ -230,7 +230,7 @@ func TestHandler_TransactionRun(t *testing.T) { ) // setup coinbase - foa2 := handler.DeployACOAAccount() + foa2 := handler.DeployCOA() account2 := handler.AccountByAddress(foa2, true) require.Equal(t, types.Balance(0), account2.Balance()) @@ -284,7 +284,7 @@ func TestHandler_COA(t *testing.T) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { handler := SetupHandler(t, backend, rootAddr) - foa := handler.AccountByAddress(handler.DeployACOAAccount(), true) + foa := handler.AccountByAddress(handler.DeployCOA(), true) require.NotNil(t, foa) zeroBalance, err := types.NewBalanceFromAttoFlow(big.NewInt(0)) @@ -345,12 +345,12 @@ func TestHandler_COA(t *testing.T) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { h := SetupHandler(t, backend, rootAddr) - coa := h.DeployACOAAccount() + coa := h.DeployCOA() acc := h.AccountByAddress(coa, true) require.NotEmpty(t, acc.Code()) // make a second account with some money - coa2 := h.DeployACOAAccount() + coa2 := h.DeployCOA() acc2 := h.AccountByAddress(coa2, true) acc2.Deposit(types.NewFlowTokenVault(makeABalanceInFlow(100))) @@ -504,7 +504,7 @@ func TestHandler_COA(t *testing.T) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { handler := SetupHandler(t, backend, rootAddr) - foa := handler.AccountByAddress(handler.DeployACOAAccount(), true) + foa := handler.AccountByAddress(handler.DeployCOA(), true) require.NotNil(t, foa) // deposit 10000 flow @@ -545,7 +545,7 @@ func TestHandler_COA(t *testing.T) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { h := SetupHandler(t, backend, rootAddr) - foa := h.AccountByAddress(h.DeployACOAAccount(), true) + foa := h.AccountByAddress(h.DeployCOA(), true) require.NotNil(t, foa) vault := types.NewFlowTokenVault(testutils.MakeABalanceInFlow(10000)) diff --git a/fvm/evm/stdlib/contract.go b/fvm/evm/stdlib/contract.go index 63a90c1f3ba..ff9d0391ea6 100644 --- a/fvm/evm/stdlib/contract.go +++ b/fvm/evm/stdlib/contract.go @@ -1153,7 +1153,7 @@ func newInternalEVMTypeCreateBridgedAccountFunction( internalEVMTypeCreateBridgedAccountFunctionType, func(invocation interpreter.Invocation) interpreter.Value { inter := invocation.Interpreter - address := handler.DeployACOAAccount() + address := handler.DeployCOA() return EVMAddressToAddressBytesArrayValue(inter, address) }, ) diff --git a/fvm/evm/stdlib/contract_test.go b/fvm/evm/stdlib/contract_test.go index 7114b71cb85..1eb0902e9b2 100644 --- a/fvm/evm/stdlib/contract_test.go +++ b/fvm/evm/stdlib/contract_test.go @@ -37,7 +37,7 @@ func (t *testContractHandler) FlowTokenAddress() common.Address { var _ types.ContractHandler = &testContractHandler{} -func (t *testContractHandler) DeployACOAAccount() types.Address { +func (t *testContractHandler) DeployCOA() types.Address { if t.allocateAddress == nil { t.addressIndex++ var address types.Address @@ -3349,7 +3349,7 @@ func TestBridgedAccountDeploy(t *testing.T) { assert.Equal(t, types.GasLimit(9999), limit) assert.Equal(t, types.Balance(expectedBalance), balance) - return handler.DeployACOAAccount() + return handler.DeployCOA() }, } }, diff --git a/fvm/evm/types/call.go b/fvm/evm/types/call.go index 75ceb25971c..9401bcf54b8 100644 --- a/fvm/evm/types/call.go +++ b/fvm/evm/types/call.go @@ -56,7 +56,7 @@ func (dc *DirectCall) Hash() (gethCommon.Hash, error) { // Message constructs a core.Message from the direct call func (dc *DirectCall) Message() *gethCore.Message { var to *gethCommon.Address - if dc.HasNonEmptyToField() { + if !dc.EmptyToField() { ct := dc.To.ToCommon() to = &ct } @@ -74,9 +74,9 @@ func (dc *DirectCall) Message() *gethCore.Message { } } -// HasNonEmptyTo returns true if to has a non empty value -func (dc *DirectCall) HasNonEmptyToField() bool { - return dc.To != EmptyAddress +// EmptyToField returns true if `to` field contains an empty address +func (dc *DirectCall) EmptyToField() bool { + return dc.To == EmptyAddress } func NewDepositCall(address Address, amount *big.Int) *DirectCall { diff --git a/fvm/evm/types/handler.go b/fvm/evm/types/handler.go index 1ca94a61e4d..5032ffa2a3f 100644 --- a/fvm/evm/types/handler.go +++ b/fvm/evm/types/handler.go @@ -24,8 +24,8 @@ import ( // ContractHandler handles operations on the evm environment type ContractHandler interface { - // Deploys a cadence-owned-account - DeployACOAAccount() Address + // DeployCOA deploys a Cadence owned account and return the address + DeployCOA() Address // AccountByAddress returns an account by address // if isAuthorized is set, it allows for functionality like `call`, `deploy` From 06cc7a5cba5a019e77b3180c5275045a038857be Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 24 Jan 2024 12:23:06 -0800 Subject: [PATCH 19/38] set nonce only for direct calls --- fvm/evm/emulator/emulator.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index 333ea74e594..eb922d5d132 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -116,7 +116,7 @@ func (bl *BlockView) DirectCall(call *types.DirectCall) (*types.Result, error) { } fallthrough default: - return proc.run(call.Message(), types.DirectCallTxType) + return proc.runDirect(call.Message(), types.DirectCallTxType) } } @@ -346,14 +346,17 @@ func (proc *procedure) deployAt( return res, proc.commit() } +func (proc *procedure) runDirect(msg *gethCore.Message, txType uint8) (*types.Result, error) { + // set the nonce for the message (needed for some opeartions like deployment) + msg.Nonce = proc.state.GetNonce(msg.From) + return proc.run(msg, types.DirectCallTxType) +} + func (proc *procedure) run(msg *gethCore.Message, txType uint8) (*types.Result, error) { res := types.Result{ TxType: txType, } - // set the nonce for the message (needed for some opeartions like deployment) - msg.Nonce = proc.state.GetNonce(msg.From) - gasPool := (*gethCore.GasPool)(&proc.config.BlockContext.GasLimit) execResult, err := gethCore.NewStateTransition( proc.evm, From e1442cb08b975baf26bcc752f0c62fd405d8ae43 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 24 Jan 2024 14:01:36 -0800 Subject: [PATCH 20/38] update runDirect to add origin --- fvm/evm/emulator/emulator.go | 1 + 1 file changed, 1 insertion(+) diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index eb922d5d132..829f7269f1c 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -349,6 +349,7 @@ func (proc *procedure) deployAt( func (proc *procedure) runDirect(msg *gethCore.Message, txType uint8) (*types.Result, error) { // set the nonce for the message (needed for some opeartions like deployment) msg.Nonce = proc.state.GetNonce(msg.From) + proc.evm.TxContext.Origin = msg.From return proc.run(msg, types.DirectCallTxType) } From 3a71c54ec8972cade33f6c6e52ecffec38a9ccb5 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Mon, 29 Jan 2024 11:51:39 -0800 Subject: [PATCH 21/38] fix bugs due to merge --- fvm/evm/handler/handler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 820b8c02f3b..de71fa4f460 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -569,7 +569,7 @@ func TestHandler_COA(t *testing.T) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { handler := SetupHandler(t, backend, rootAddr) - foa := handler.AccountByAddress(handler.AllocateAddress(), true) + foa := handler.AccountByAddress(handler.DeployCOA(), true) require.NotNil(t, foa) vault := types.NewFlowTokenVault(types.MakeABalanceInFlow(100)) From 72bf78cafef5107d4c6fba7be6a406d3c5621ea4 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Mon, 29 Jan 2024 13:05:25 -0800 Subject: [PATCH 22/38] use resource id as the seed for address allocation --- fvm/evm/handler/addressAllocator.go | 24 +----------- fvm/evm/handler/addressAllocator_test.go | 6 +-- fvm/evm/handler/handler.go | 5 +-- fvm/evm/handler/handler_benchmark_test.go | 2 +- fvm/evm/handler/handler_test.go | 16 ++++---- fvm/evm/stdlib/contract.cdc | 16 +++++--- fvm/evm/stdlib/contract.go | 12 +++++- fvm/evm/stdlib/contract_test.go | 46 +++++++++++++---------- fvm/evm/types/handler.go | 4 +- 9 files changed, 65 insertions(+), 66 deletions(-) diff --git a/fvm/evm/handler/addressAllocator.go b/fvm/evm/handler/addressAllocator.go index 06466811ed3..316c2fa0d9f 100644 --- a/fvm/evm/handler/addressAllocator.go +++ b/fvm/evm/handler/addressAllocator.go @@ -37,28 +37,8 @@ func (aa *AddressAllocator) COAFactoryAddress() types.Address { } // AllocateCOAAddress allocates an address for COA -func (aa *AddressAllocator) AllocateCOAAddress() (types.Address, error) { - data, err := aa.led.GetValue(aa.flexAddress[:], []byte(ledgerAddressAllocatorKey)) - if err != nil { - return types.Address{}, err - } - // default value for uuid is 1, zero is not good for shuffling - uuid := uint64(1) - if len(data) > 0 { - uuid = binary.BigEndian.Uint64(data) - } - - target := MakeCOAAddress(uuid) - - // store new uuid - newData := make([]byte, 8) - binary.BigEndian.PutUint64(newData, uuid+1) - err = aa.led.SetValue(aa.flexAddress[:], []byte(ledgerAddressAllocatorKey), newData) - if err != nil { - return types.Address{}, err - } - - return target, nil +func (aa *AddressAllocator) AllocateCOAAddress(uuid uint64) types.Address { + return MakeCOAAddress(uuid) } func MakeCOAAddress(index uint64) types.Address { diff --git a/fvm/evm/handler/addressAllocator_test.go b/fvm/evm/handler/addressAllocator_test.go index fa75328f29f..b2471da358c 100644 --- a/fvm/evm/handler/addressAllocator_test.go +++ b/fvm/evm/handler/addressAllocator_test.go @@ -25,16 +25,14 @@ func TestAddressAllocator(t *testing.T) { require.False(t, types.IsACOAAddress(adr)) // test default value fall back - adr, err = aa.AllocateCOAAddress() - require.NoError(t, err) + adr = aa.AllocateCOAAddress(1) expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x000000000000000000000002ffeeddccbbaa9987")) require.Equal(t, expectedAddress, adr) // check conforming to types require.True(t, types.IsACOAAddress(adr)) // continous allocation logic - adr, err = aa.AllocateCOAAddress() - require.NoError(t, err) + adr = aa.AllocateCOAAddress(2) expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x000000000000000000000002ffddbb997755330e")) require.Equal(t, expectedAddress, adr) // check conforming to types diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index f055e2914c4..c5805004ff1 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -62,10 +62,9 @@ func getPrecompiles( } // DeployCOA deploys a cadence-owned-account and returns the address -func (h *ContractHandler) DeployCOA() types.Address { - target, err := h.addressAllocator.AllocateCOAAddress() +func (h *ContractHandler) DeployCOA(uuid uint64) types.Address { + target := h.addressAllocator.AllocateCOAAddress(uuid) gaslimit := types.GasLimit(COAContractDeploymentRequiredGas) - handleError(err) h.checkGasLimit(gaslimit) factory := h.addressAllocator.COAFactoryAddress() diff --git a/fvm/evm/handler/handler_benchmark_test.go b/fvm/evm/handler/handler_benchmark_test.go index a44108b620b..8c7ff388706 100644 --- a/fvm/evm/handler/handler_benchmark_test.go +++ b/fvm/evm/handler/handler_benchmark_test.go @@ -26,7 +26,7 @@ func benchmarkStorageGrowth(b *testing.B, accountCount, setupKittyCount int) { // setup several of accounts // note that trie growth is the function of number of accounts for i := 0; i < accountCount; i++ { - account := handler.AccountByAddress(handler.DeployCOA(), true) + account := handler.AccountByAddress(handler.DeployCOA(uint64(i+1)), true) account.Deposit(types.NewFlowTokenVault(types.NewBalanceFromUFix64(100))) accounts[i] = account } diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index de71fa4f460..2cef07199e8 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -198,7 +198,7 @@ func TestHandler_TransactionRun(t *testing.T) { eoa := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex) // deposit 1 Flow to the foa account - addr := handler.DeployCOA() + addr := handler.DeployCOA(1) orgBalance := types.NewBalanceFromUFix64(types.OneFlowInUFix64) vault := types.NewFlowTokenVault(orgBalance) foa := handler.AccountByAddress(addr, true) @@ -224,7 +224,7 @@ func TestHandler_TransactionRun(t *testing.T) { ) // setup coinbase - foa2 := handler.DeployCOA() + foa2 := handler.DeployCOA(2) account2 := handler.AccountByAddress(foa2, true) require.Equal(t, types.NewBalanceFromUFix64(0), account2.Balance()) @@ -278,7 +278,7 @@ func TestHandler_COA(t *testing.T) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { handler := SetupHandler(t, backend, rootAddr) - foa := handler.AccountByAddress(handler.DeployCOA(), true) + foa := handler.AccountByAddress(handler.DeployCOA(1), true) require.NotNil(t, foa) zeroBalance := types.NewBalance(big.NewInt(0)) @@ -342,12 +342,12 @@ func TestHandler_COA(t *testing.T) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { h := SetupHandler(t, backend, rootAddr) - coa := h.DeployCOA() + coa := h.DeployCOA(1) acc := h.AccountByAddress(coa, true) require.NotEmpty(t, acc.Code()) // make a second account with some money - coa2 := h.DeployCOA() + coa2 := h.DeployCOA(2) acc2 := h.AccountByAddress(coa2, true) acc2.Deposit(types.NewFlowTokenVault(types.MakeABalanceInFlow(100))) @@ -501,7 +501,7 @@ func TestHandler_COA(t *testing.T) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { handler := SetupHandler(t, backend, rootAddr) - foa := handler.AccountByAddress(handler.DeployCOA(), true) + foa := handler.AccountByAddress(handler.DeployCOA(1), true) require.NotNil(t, foa) // deposit 10000 flow @@ -543,7 +543,7 @@ func TestHandler_COA(t *testing.T) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { h := SetupHandler(t, backend, rootAddr) - foa := h.AccountByAddress(h.DeployCOA(), true) + foa := h.AccountByAddress(h.DeployCOA(1), true) require.NotNil(t, foa) vault := types.NewFlowTokenVault(types.MakeABalanceInFlow(10000)) @@ -569,7 +569,7 @@ func TestHandler_COA(t *testing.T) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { handler := SetupHandler(t, backend, rootAddr) - foa := handler.AccountByAddress(handler.DeployCOA(), true) + foa := handler.AccountByAddress(handler.DeployCOA(1), true) require.NotNil(t, foa) vault := types.NewFlowTokenVault(types.MakeABalanceInFlow(100)) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 3151542131b..bc902b26bdc 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -50,9 +50,14 @@ contract EVM { resource BridgedAccount { access(self) - let addressBytes: [UInt8; 20] + var addressBytes: [UInt8; 20] - init(addressBytes: [UInt8; 20]) { + init() { + self.addressBytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + } + + access(contract) + fun setAddress(addressBytes: [UInt8; 20]) { self.addressBytes = addressBytes } @@ -127,9 +132,10 @@ contract EVM { /// Creates a new bridged account access(all) fun createBridgedAccount(): @BridgedAccount { - return <-create BridgedAccount( - addressBytes: InternalEVM.createBridgedAccount() - ) + let acc <-create BridgedAccount() + let addr = InternalEVM.createBridgedAccount(uuid: acc.uuid) + acc.setAddress(addressBytes: addr) + return <-acc } /// Runs an a RLP-encoded EVM transaction, deducts the gas fees, diff --git a/fvm/evm/stdlib/contract.go b/fvm/evm/stdlib/contract.go index 64b17d1d08f..9fc790937da 100644 --- a/fvm/evm/stdlib/contract.go +++ b/fvm/evm/stdlib/contract.go @@ -1140,6 +1140,12 @@ func newInternalEVMTypeCallFunction( const internalEVMTypeCreateBridgedAccountFunctionName = "createBridgedAccount" var internalEVMTypeCreateBridgedAccountFunctionType = &sema.FunctionType{ + Parameters: []sema.Parameter{ + { + Label: "uuid", + TypeAnnotation: sema.NewTypeAnnotation(sema.UInt64Type), + }, + }, ReturnTypeAnnotation: sema.NewTypeAnnotation(evmAddressBytesType), } @@ -1152,7 +1158,11 @@ func newInternalEVMTypeCreateBridgedAccountFunction( internalEVMTypeCreateBridgedAccountFunctionType, func(invocation interpreter.Invocation) interpreter.Value { inter := invocation.Interpreter - address := handler.DeployCOA() + uuid, ok := invocation.Arguments[0].(interpreter.UInt64Value) + if !ok { + panic(errors.NewUnreachableError()) + } + address := handler.DeployCOA(uint64(uuid)) return EVMAddressToAddressBytesArrayValue(inter, address) }, ) diff --git a/fvm/evm/stdlib/contract_test.go b/fvm/evm/stdlib/contract_test.go index 22679b5a504..ca196ad6482 100644 --- a/fvm/evm/stdlib/contract_test.go +++ b/fvm/evm/stdlib/contract_test.go @@ -24,8 +24,7 @@ import ( type testContractHandler struct { flowTokenAddress common.Address - allocateAddress func() types.Address - addressIndex uint64 + deployCOA func(uint64) types.Address accountByAddress func(types.Address, bool) types.Account lastExecutedBlock func() *types.Block run func(tx []byte, coinbase types.Address) @@ -37,14 +36,13 @@ func (t *testContractHandler) FlowTokenAddress() common.Address { var _ types.ContractHandler = &testContractHandler{} -func (t *testContractHandler) DeployCOA() types.Address { - if t.allocateAddress == nil { - t.addressIndex++ +func (t *testContractHandler) DeployCOA(uuid uint64) types.Address { + if t.deployCOA == nil { var address types.Address - binary.LittleEndian.PutUint64(address[:], t.addressIndex) + binary.LittleEndian.PutUint64(address[:], uuid) return address } - return t.allocateAddress() + return t.deployCOA(uuid) } func (t *testContractHandler) AccountByAddress(addr types.Address, isAuthorized bool) types.Account { @@ -2859,13 +2857,18 @@ func TestEVMCreateBridgedAccount(t *testing.T) { t.Parallel() - handler := &testContractHandler{} + uuidCounter := uint64(0) + handler := &testContractHandler{ + deployCOA: func(uuid uint64) types.Address { + require.Equal(t, uuidCounter, uuid) + return types.Address{uint8(uuidCounter)} + }, + } contractsAddress := flow.BytesToAddress([]byte{0x1}) transactionEnvironment := newEVMTransactionEnvironment(handler, contractsAddress) scriptEnvironment := newEVMScriptEnvironment(handler, contractsAddress) - rt := runtime.NewInterpreterRuntime(runtime.Config{}) script := []byte(` @@ -2908,6 +2911,10 @@ func TestEVMCreateBridgedAccount(t *testing.T) { OnDecodeArgument: func(b []byte, t cadence.Type) (cadence.Value, error) { return json.Decode(nil, b) }, + OnGenerateUUID: func() (uint64, error) { + uuidCounter++ + return uuidCounter, nil + }, } nextTransactionLocation := NewTransactionLocationGenerator() @@ -2940,7 +2947,7 @@ func TestEVMCreateBridgedAccount(t *testing.T) { require.NoError(t, err) expected := cadence.NewArray([]cadence.Value{ - cadence.UInt8(2), cadence.UInt8(0), + cadence.UInt8(4), cadence.UInt8(0), cadence.UInt8(0), cadence.UInt8(0), cadence.UInt8(0), cadence.UInt8(0), cadence.UInt8(0), cadence.UInt8(0), @@ -2967,7 +2974,7 @@ func TestBridgedAccountCall(t *testing.T) { handler := &testContractHandler{ accountByAddress: func(fromAddress types.Address, isAuthorized bool) types.Account { - assert.Equal(t, types.Address{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) + assert.Equal(t, types.Address{3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) assert.True(t, isAuthorized) return &testFlowAccount{ @@ -3091,7 +3098,7 @@ func TestEVMAddressDeposit(t *testing.T) { handler := &testContractHandler{ accountByAddress: func(fromAddress types.Address, isAuthorized bool) types.Account { - assert.Equal(t, types.Address{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) + assert.Equal(t, types.Address{5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) assert.False(t, isAuthorized) return &testFlowAccount{ @@ -3209,7 +3216,7 @@ func TestBridgedAccountWithdraw(t *testing.T) { handler := &testContractHandler{ flowTokenAddress: common.Address(contractsAddress), accountByAddress: func(fromAddress types.Address, isAuthorized bool) types.Account { - assert.Equal(t, types.Address{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) + assert.Equal(t, types.Address{5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) assert.Equal(t, deposited, isAuthorized) return &testFlowAccount{ @@ -3333,11 +3340,10 @@ func TestBridgedAccountDeploy(t *testing.T) { expectedBalance, err := cadence.NewUFix64FromParts(1, 23000000) require.NoError(t, err) - var handler *testContractHandler - handler = &testContractHandler{ + handler := &testContractHandler{ flowTokenAddress: common.Address(contractsAddress), accountByAddress: func(fromAddress types.Address, isAuthorized bool) types.Account { - assert.Equal(t, types.Address{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) + assert.Equal(t, types.Address{3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) assert.True(t, isAuthorized) return &testFlowAccount{ @@ -3348,7 +3354,7 @@ func TestBridgedAccountDeploy(t *testing.T) { assert.Equal(t, types.GasLimit(9999), limit) assert.Equal(t, types.NewBalanceFromUFix64(expectedBalance), balance) - return handler.DeployCOA() + return types.Address{4} }, } }, @@ -3432,7 +3438,7 @@ func TestBridgedAccountDeploy(t *testing.T) { require.NoError(t, err) expected := cadence.NewArray([]cadence.Value{ - cadence.UInt8(2), cadence.UInt8(0), + cadence.UInt8(4), cadence.UInt8(0), cadence.UInt8(0), cadence.UInt8(0), cadence.UInt8(0), cadence.UInt8(0), cadence.UInt8(0), cadence.UInt8(0), @@ -3468,7 +3474,7 @@ func TestEVMAccountBalance(t *testing.T) { handler := &testContractHandler{ flowTokenAddress: common.Address(contractsAddress), accountByAddress: func(fromAddress types.Address, isAuthorized bool) types.Account { - assert.Equal(t, types.Address{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) + assert.Equal(t, types.Address{3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) assert.False(t, isAuthorized) return &testFlowAccount{ @@ -3568,7 +3574,7 @@ func TestEVMAccountBalanceForABIOnlyContract(t *testing.T) { handler := &testContractHandler{ flowTokenAddress: common.Address(contractsAddress), accountByAddress: func(fromAddress types.Address, isAuthorized bool) types.Account { - assert.Equal(t, types.Address{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) + assert.Equal(t, types.Address{5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fromAddress) assert.False(t, isAuthorized) return &testFlowAccount{ diff --git a/fvm/evm/types/handler.go b/fvm/evm/types/handler.go index afe9255a90e..d49b02bfd43 100644 --- a/fvm/evm/types/handler.go +++ b/fvm/evm/types/handler.go @@ -25,7 +25,7 @@ import ( // ContractHandler handles operations on the evm environment type ContractHandler interface { // DeployCOA deploys a Cadence owned account and return the address - DeployCOA() Address + DeployCOA(uuid uint64) Address // AccountByAddress returns an account by address // if isAuthorized is set, it allows for functionality like `call`, `deploy` @@ -55,7 +55,7 @@ type Backend interface { // AddressAllocator allocates addresses, used by the handler type AddressAllocator interface { // AllocateAddress allocates an address to be used by a COA resource - AllocateCOAAddress() (Address, error) + AllocateCOAAddress(uuid uint64) Address // COAFactoryAddress returns the address for the COA factory COAFactoryAddress() Address From a0761f7c66bda8be2674732888d6cd482c6db289 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Mon, 29 Jan 2024 21:11:44 -0800 Subject: [PATCH 23/38] improve coa contract --- fvm/evm/handler/coa.go | 21 +-------------------- fvm/evm/handler/coa.sol | 4 +--- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/fvm/evm/handler/coa.go b/fvm/evm/handler/coa.go index 0e81142ffa5..c5a95d2af9f 100644 --- a/fvm/evm/handler/coa.go +++ b/fvm/evm/handler/coa.go @@ -5,30 +5,11 @@ import "encoding/hex" var COAContractDeploymentRequiredGas = uint64(723_000) // COAContractBytes is the compiled version of the coa smart contract. -var COAContractBytes, _ = hex.DecodeString("608060405234801561000f575f80fd5b50610e1e8061001d5f395ff3fe608060405260043610610072575f3560e01c80631626ba7e1161004d5780631626ba7e1461016b578063bc197c81146101a7578063d0d250bd146101e3578063f23a6e611461020d576100c7565b806223de29146100cb57806301ffc9a7146100f3578063150b7a021461012f576100c7565b366100c7573373ffffffffffffffffffffffffffffffffffffffff167fb496ce2863a72c02f90deb616c638c76948f3aff803140881c602775c05fdf3a346040516100bd91906105a5565b60405180910390a2005b5f80fd5b3480156100d6575f80fd5b506100f160048036038101906100ec91906106b4565b610249565b005b3480156100fe575f80fd5b50610119600480360381019061011491906107d3565b610253565b6040516101269190610818565b60405180910390f35b34801561013a575f80fd5b5061015560048036038101906101509190610831565b6103f3565b60405161016291906108c4565b60405180910390f35b348015610176575f80fd5b50610191600480360381019061018c9190610a48565b610407565b60405161019e91906108c4565b60405180910390f35b3480156101b2575f80fd5b506101cd60048036038101906101c89190610af7565b610554565b6040516101da91906108c4565b60405180910390f35b3480156101ee575f80fd5b506101f761056b565b6040516102049190610bdd565b60405180910390f35b348015610218575f80fd5b50610233600480360381019061022e9190610bf6565b610578565b60405161024091906108c4565b60405180910390f35b5050505050505050565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061031d57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061038457507e23de29000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806103ec57507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b5f63150b7a0260e01b905095945050505050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff16858560405160240161043d929190610d15565b6040516020818303038152906040527eb997bd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516104c69190610d7d565b5f60405180830381855afa9150503d805f81146104fe576040519150601f19603f3d011682016040523d82523d5f602084013e610503565b606091505b509150915081610511575f80fd5b5f818060200190518101906105269190610dbd565b9050801561054057631626ba7e60e01b935050505061054e565b63ffffffff60e01b93505050505b92915050565b5f63bc197c8160e01b905098975050505050505050565b6801000000000000000181565b5f63f23a6e6160e01b90509695505050505050565b5f819050919050565b61059f8161058d565b82525050565b5f6020820190506105b85f830184610596565b92915050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6105f8826105cf565b9050919050565b610608816105ee565b8114610612575f80fd5b50565b5f81359050610623816105ff565b92915050565b6106328161058d565b811461063c575f80fd5b50565b5f8135905061064d81610629565b92915050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261067457610673610653565b5b8235905067ffffffffffffffff81111561069157610690610657565b5b6020830191508360018202830111156106ad576106ac61065b565b5b9250929050565b5f805f805f805f8060c0898b0312156106d0576106cf6105c7565b5b5f6106dd8b828c01610615565b98505060206106ee8b828c01610615565b97505060406106ff8b828c01610615565b96505060606107108b828c0161063f565b955050608089013567ffffffffffffffff811115610731576107306105cb565b5b61073d8b828c0161065f565b945094505060a089013567ffffffffffffffff8111156107605761075f6105cb565b5b61076c8b828c0161065f565b92509250509295985092959890939650565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6107b28161077e565b81146107bc575f80fd5b50565b5f813590506107cd816107a9565b92915050565b5f602082840312156107e8576107e76105c7565b5b5f6107f5848285016107bf565b91505092915050565b5f8115159050919050565b610812816107fe565b82525050565b5f60208201905061082b5f830184610809565b92915050565b5f805f805f6080868803121561084a576108496105c7565b5b5f61085788828901610615565b955050602061086888828901610615565b94505060406108798882890161063f565b935050606086013567ffffffffffffffff81111561089a576108996105cb565b5b6108a68882890161065f565b92509250509295509295909350565b6108be8161077e565b82525050565b5f6020820190506108d75f8301846108b5565b92915050565b5f819050919050565b6108ef816108dd565b81146108f9575f80fd5b50565b5f8135905061090a816108e6565b92915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61095a82610914565b810181811067ffffffffffffffff8211171561097957610978610924565b5b80604052505050565b5f61098b6105be565b90506109978282610951565b919050565b5f67ffffffffffffffff8211156109b6576109b5610924565b5b6109bf82610914565b9050602081019050919050565b828183375f83830152505050565b5f6109ec6109e78461099c565b610982565b905082815260208101848484011115610a0857610a07610910565b5b610a138482856109cc565b509392505050565b5f82601f830112610a2f57610a2e610653565b5b8135610a3f8482602086016109da565b91505092915050565b5f8060408385031215610a5e57610a5d6105c7565b5b5f610a6b858286016108fc565b925050602083013567ffffffffffffffff811115610a8c57610a8b6105cb565b5b610a9885828601610a1b565b9150509250929050565b5f8083601f840112610ab757610ab6610653565b5b8235905067ffffffffffffffff811115610ad457610ad3610657565b5b602083019150836020820283011115610af057610aef61065b565b5b9250929050565b5f805f805f805f8060a0898b031215610b1357610b126105c7565b5b5f610b208b828c01610615565b9850506020610b318b828c01610615565b975050604089013567ffffffffffffffff811115610b5257610b516105cb565b5b610b5e8b828c01610aa2565b9650965050606089013567ffffffffffffffff811115610b8157610b806105cb565b5b610b8d8b828c01610aa2565b9450945050608089013567ffffffffffffffff811115610bb057610baf6105cb565b5b610bbc8b828c0161065f565b92509250509295985092959890939650565b610bd7816105ee565b82525050565b5f602082019050610bf05f830184610bce565b92915050565b5f805f805f8060a08789031215610c1057610c0f6105c7565b5b5f610c1d89828a01610615565b9650506020610c2e89828a01610615565b9550506040610c3f89828a0161063f565b9450506060610c5089828a0161063f565b935050608087013567ffffffffffffffff811115610c7157610c706105cb565b5b610c7d89828a0161065f565b92509250509295509295509295565b610c95816108dd565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610cd2578082015181840152602081019050610cb7565b5f8484015250505050565b5f610ce782610c9b565b610cf18185610ca5565b9350610d01818560208601610cb5565b610d0a81610914565b840191505092915050565b5f604082019050610d285f830185610c8c565b8181036020830152610d3a8184610cdd565b90509392505050565b5f81905092915050565b5f610d5782610c9b565b610d618185610d43565b9350610d71818560208601610cb5565b80840191505092915050565b5f610d888284610d4d565b915081905092915050565b610d9c816107fe565b8114610da6575f80fd5b50565b5f81519050610db781610d93565b92915050565b5f60208284031215610dd257610dd16105c7565b5b5f610ddf84828501610da9565b9150509291505056fea2646970667358221220c5a50c64c66e3bfb048a9c796b1fd390d35974be1136aed5e4c00cf2fc62c55164736f6c63430008160033") +var COAContractBytes, _ = hex.DecodeString("608060405234801561000f575f80fd5b50610db98061001d5f395ff3fe608060405260043610610072575f3560e01c80631626ba7e1161004d5780631626ba7e1461011d578063bc197c8114610159578063d0d250bd14610195578063f23a6e61146101bf57610079565b806223de291461007d57806301ffc9a7146100a5578063150b7a02146100e157610079565b3661007957005b5f80fd5b348015610088575f80fd5b506100a3600480360381019061009e9190610641565b6101fb565b005b3480156100b0575f80fd5b506100cb60048036038101906100c69190610760565b610205565b6040516100d891906107a5565b60405180910390f35b3480156100ec575f80fd5b50610107600480360381019061010291906107be565b6103a5565b6040516101149190610851565b60405180910390f35b348015610128575f80fd5b50610143600480360381019061013e91906109d5565b6103b9565b6040516101509190610851565b60405180910390f35b348015610164575f80fd5b5061017f600480360381019061017a9190610a84565b610509565b60405161018c9190610851565b60405180910390f35b3480156101a0575f80fd5b506101a9610520565b6040516101b69190610b6a565b60405180910390f35b3480156101ca575f80fd5b506101e560048036038101906101e09190610b83565b61052d565b6040516101f29190610851565b60405180910390f35b5050505050505050565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806102cf57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061033657507e23de29000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061039e57507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b5f63150b7a0260e01b905095945050505050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff163086866040516024016103f193929190610ca2565b6040516020818303038152906040527f07bb4ecc000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161047b9190610d18565b5f60405180830381855afa9150503d805f81146104b3576040519150601f19603f3d011682016040523d82523d5f602084013e6104b8565b606091505b5091509150816104c6575f80fd5b5f818060200190518101906104db9190610d58565b905080156104f557631626ba7e60e01b9350505050610503565b63ffffffff60e01b93505050505b92915050565b5f63bc197c8160e01b905098975050505050505050565b6801000000000000000181565b5f63f23a6e6160e01b90509695505050505050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61057c82610553565b9050919050565b61058c81610572565b8114610596575f80fd5b50565b5f813590506105a781610583565b92915050565b5f819050919050565b6105bf816105ad565b81146105c9575f80fd5b50565b5f813590506105da816105b6565b92915050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f840112610601576106006105e0565b5b8235905067ffffffffffffffff81111561061e5761061d6105e4565b5b60208301915083600182028301111561063a576106396105e8565b5b9250929050565b5f805f805f805f8060c0898b03121561065d5761065c61054b565b5b5f61066a8b828c01610599565b985050602061067b8b828c01610599565b975050604061068c8b828c01610599565b965050606061069d8b828c016105cc565b955050608089013567ffffffffffffffff8111156106be576106bd61054f565b5b6106ca8b828c016105ec565b945094505060a089013567ffffffffffffffff8111156106ed576106ec61054f565b5b6106f98b828c016105ec565b92509250509295985092959890939650565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61073f8161070b565b8114610749575f80fd5b50565b5f8135905061075a81610736565b92915050565b5f602082840312156107755761077461054b565b5b5f6107828482850161074c565b91505092915050565b5f8115159050919050565b61079f8161078b565b82525050565b5f6020820190506107b85f830184610796565b92915050565b5f805f805f608086880312156107d7576107d661054b565b5b5f6107e488828901610599565b95505060206107f588828901610599565b9450506040610806888289016105cc565b935050606086013567ffffffffffffffff8111156108275761082661054f565b5b610833888289016105ec565b92509250509295509295909350565b61084b8161070b565b82525050565b5f6020820190506108645f830184610842565b92915050565b5f819050919050565b61087c8161086a565b8114610886575f80fd5b50565b5f8135905061089781610873565b92915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6108e7826108a1565b810181811067ffffffffffffffff82111715610906576109056108b1565b5b80604052505050565b5f610918610542565b905061092482826108de565b919050565b5f67ffffffffffffffff821115610943576109426108b1565b5b61094c826108a1565b9050602081019050919050565b828183375f83830152505050565b5f61097961097484610929565b61090f565b9050828152602081018484840111156109955761099461089d565b5b6109a0848285610959565b509392505050565b5f82601f8301126109bc576109bb6105e0565b5b81356109cc848260208601610967565b91505092915050565b5f80604083850312156109eb576109ea61054b565b5b5f6109f885828601610889565b925050602083013567ffffffffffffffff811115610a1957610a1861054f565b5b610a25858286016109a8565b9150509250929050565b5f8083601f840112610a4457610a436105e0565b5b8235905067ffffffffffffffff811115610a6157610a606105e4565b5b602083019150836020820283011115610a7d57610a7c6105e8565b5b9250929050565b5f805f805f805f8060a0898b031215610aa057610a9f61054b565b5b5f610aad8b828c01610599565b9850506020610abe8b828c01610599565b975050604089013567ffffffffffffffff811115610adf57610ade61054f565b5b610aeb8b828c01610a2f565b9650965050606089013567ffffffffffffffff811115610b0e57610b0d61054f565b5b610b1a8b828c01610a2f565b9450945050608089013567ffffffffffffffff811115610b3d57610b3c61054f565b5b610b498b828c016105ec565b92509250509295985092959890939650565b610b6481610572565b82525050565b5f602082019050610b7d5f830184610b5b565b92915050565b5f805f805f8060a08789031215610b9d57610b9c61054b565b5b5f610baa89828a01610599565b9650506020610bbb89828a01610599565b9550506040610bcc89828a016105cc565b9450506060610bdd89828a016105cc565b935050608087013567ffffffffffffffff811115610bfe57610bfd61054f565b5b610c0a89828a016105ec565b92509250509295509295509295565b610c228161086a565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610c5f578082015181840152602081019050610c44565b5f8484015250505050565b5f610c7482610c28565b610c7e8185610c32565b9350610c8e818560208601610c42565b610c97816108a1565b840191505092915050565b5f606082019050610cb55f830186610b5b565b610cc26020830185610c19565b8181036040830152610cd48184610c6a565b9050949350505050565b5f81905092915050565b5f610cf282610c28565b610cfc8185610cde565b9350610d0c818560208601610c42565b80840191505092915050565b5f610d238284610ce8565b915081905092915050565b610d378161078b565b8114610d41575f80fd5b50565b5f81519050610d5281610d2e565b92915050565b5f60208284031215610d6d57610d6c61054b565b5b5f610d7a84828501610d44565b9150509291505056fea264697066735822122076a1ae2a0143dcafcdc7f7ea5bdba7fb314b92d8e74573311cbc152ce364d8a564736f6c63430008160033") // COAContractABIJSON is the json string of ABI of the coa smart contract. var COAContractABIJSON = ` [ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "FlowReceived", - "type": "event" - }, { "inputs": [], "name": "cadenceArch", diff --git a/fvm/evm/handler/coa.sol b/fvm/evm/handler/coa.sol index f81b7840320..7f6451ca9fd 100644 --- a/fvm/evm/handler/coa.sol +++ b/fvm/evm/handler/coa.sol @@ -39,9 +39,7 @@ function onERC1155BatchReceived( } contract COA is ERC1155TokenReceiver, ERC777TokensRecipient, ERC721TokenReceiver, IERC165 { address constant public cadenceArch = 0x0000000000000000000000010000000000000001; - event FlowReceived(address indexed sender, uint256 value); receive() external payable { - emit FlowReceived(msg.sender, msg.value); } function supportsInterface(bytes4 id) external view virtual override returns (bool) { return @@ -93,7 +91,7 @@ contract COA is ERC1155TokenReceiver, ERC777TokensRecipient, ERC721TokenReceiver bytes32 _hash, bytes memory _sig ) external view virtual returns (bytes4){ - (bool ok, bytes memory data) = cadenceArch.staticcall(abi.encodeWithSignature("verifyCOAOwnershipProof(bytes32, bytes)", _hash, _sig)); + (bool ok, bytes memory data) = cadenceArch.staticcall(abi.encodeWithSignature("verifyCOAOwnershipProof(address, bytes32, bytes)", address(this), _hash, _sig)); require(ok); bool output = abi.decode(data, (bool)); if (output) { From 4591f8f93c91d792701f2321ba3a51cbbbaa9701 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Mon, 29 Jan 2024 21:13:18 -0800 Subject: [PATCH 24/38] clean up --- fvm/evm/handler/coa.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fvm/evm/handler/coa.sol b/fvm/evm/handler/coa.sol index 7f6451ca9fd..1c161e232f6 100644 --- a/fvm/evm/handler/coa.sol +++ b/fvm/evm/handler/coa.sol @@ -91,7 +91,7 @@ contract COA is ERC1155TokenReceiver, ERC777TokensRecipient, ERC721TokenReceiver bytes32 _hash, bytes memory _sig ) external view virtual returns (bytes4){ - (bool ok, bytes memory data) = cadenceArch.staticcall(abi.encodeWithSignature("verifyCOAOwnershipProof(address, bytes32, bytes)", address(this), _hash, _sig)); + (bool ok, bytes memory data) = cadenceArch.staticcall(abi.encodeWithSignature("verifyCOAOwnershipProof(address,bytes32,bytes)", address(this), _hash, _sig)); require(ok); bool output = abi.decode(data, (bool)); if (output) { From 38d833babbf50581a665f083540ff12f47cf6488 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 1 Feb 2024 11:02:47 -0800 Subject: [PATCH 25/38] apply PR feedback --- fvm/evm/emulator/emulator.go | 25 ++++++++++++++++--------- fvm/evm/types/call.go | 5 +++-- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index 702ed640c94..7b164cc9d87 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -1,6 +1,7 @@ package emulator import ( + "errors" "math/big" gethCommon "github.com/ethereum/go-ethereum/common" @@ -16,6 +17,8 @@ import ( "github.com/onflow/flow-go/model/flow" ) +var ErrInvalidBalance = errors.New("invalid balance for transfer") + // Emulator handles operations against evm runtime type Emulator struct { rootAddr flow.Address @@ -279,15 +282,20 @@ func (proc *procedure) deployAt( // increment the nonce for the caller proc.state.SetNonce(callerCommon, proc.state.GetNonce(callerCommon)+1) + if value.Sign() < 0 { + return res, ErrInvalidBalance + } // setup account proc.state.CreateAccount(addr) proc.state.SetNonce(addr, 1) // (EIP-158) - proc.evm.Context.Transfer( // transfer value - proc.state, - caller.ToCommon(), - addr, - value, - ) + if value.Sign() > 0 { + proc.evm.Context.Transfer( // transfer value + proc.state, + caller.ToCommon(), + addr, + value, + ) + } // run code through interpreter // this would check for errors and computes the final bytes to be stored under account @@ -298,9 +306,8 @@ func (proc *procedure) deployAt( gethVM.AccountRef(addr), value, gasLimit) - contract.Code = data - contract.CodeHash = gethCrypto.Keccak256Hash(data) - contract.CodeAddr = &addr + + contract.SetCallCode(&addr, gethCrypto.Keccak256Hash(data), data) // update access list (Berlin) proc.state.AddAddressToAccessList(addr) diff --git a/fvm/evm/types/call.go b/fvm/evm/types/call.go index ec8c7d34820..3f9837cc9eb 100644 --- a/fvm/evm/types/call.go +++ b/fvm/evm/types/call.go @@ -6,6 +6,7 @@ import ( gethCommon "github.com/ethereum/go-ethereum/common" gethCore "github.com/ethereum/go-ethereum/core" gethCrypto "github.com/ethereum/go-ethereum/crypto" + gethParams "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -20,8 +21,8 @@ const ( DeployCallSubType = byte(4) ContractCallSubType = byte(5) - DepositCallGasLimit = 21_000 - WithdrawCallGasLimit = 21_000 + DepositCallGasLimit = gethParams.TxGas + WithdrawCallGasLimit = gethParams.TxGas // 21_000 is the minimum for a transaction + max gas allowed for receive/fallback methods DefaultGasLimitForTokenTransfer = 21_000 + 2_300 From 0a9e40bd8fe0cf80f2985889d9f780147099ac15 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Thu, 1 Feb 2024 23:17:00 +0100 Subject: [PATCH 26/38] clarify security consideration about prefix length --- fvm/evm/types/address.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fvm/evm/types/address.go b/fvm/evm/types/address.go index 8fe7dc18114..0d707aea6f1 100644 --- a/fvm/evm/types/address.go +++ b/fvm/evm/types/address.go @@ -7,8 +7,16 @@ import ( ) const ( - // number of prefix bytes with specific values for special accounts (extended precompiles and COAs) - // using leading zeros for prefix helps with the storage compactness + // number of prefix bytes with constant values for special accounts (extended precompiles and COAs) + // using leading zeros for prefix helps with the storage compactness. + // + // The prefix length should insure a high-enough level of security against finding a preimage using the hash + // function used for EVM addresses generation (Keccak256). This is required to avoid finding an EVM address + // that can also be a valid FlowEVM address. + // The target (minimal) security in this case is the security level provided by EVM addresses. + // Since EVM addresses are 160-bits long, EVM addresses offer only 80 bits of security (collision resistance + // offers the lowest level). + // A preimage resistance of 80 bits requires the prefix to be at least 80-bits long (i.e 10 bytes) FlowEVMSpecialAddressPrefixLen = 12 ) @@ -56,6 +64,12 @@ func NewAddressFromString(str string) Address { } // IsACOAAddress returns true if the address is a COA address +// +// This test insure `addr` has been generated as a COA address with high probability. +// Brute forcing an EVM address `addr` to pass the `IsACOAAddress` test is as hard as the bit-length +// of `FlowEVMCOAAddressPrefix` (here 96 bits). +// Although this is lower than the protocol-wide security level in Flow (128 bits), it remains +// higher than the EVM addresses security (80 bits when considering collision attacks) func IsACOAAddress(addr Address) bool { return bytes.HasPrefix(addr[:], FlowEVMCOAAddressPrefix[:]) } From c81674d582ced043a70d5a12aabad9f83055c1c6 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Thu, 1 Feb 2024 23:24:58 +0100 Subject: [PATCH 27/38] simplify makePrefixedAddress --- fvm/evm/handler/addressAllocator.go | 7 +++---- fvm/evm/types/address.go | 4 +++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/fvm/evm/handler/addressAllocator.go b/fvm/evm/handler/addressAllocator.go index 316c2fa0d9f..ee7838f5eb4 100644 --- a/fvm/evm/handler/addressAllocator.go +++ b/fvm/evm/handler/addressAllocator.go @@ -11,7 +11,6 @@ import ( const ( ledgerAddressAllocatorKey = "AddressAllocator" - uint64ByteSize = 8 // addressIndexShuffleSeed is used for shuffling address index // shuffling index is used to make address postfixes look random addressIndexShuffleSeed = uint64(0xFFEEDDCCBBAA9987) @@ -59,9 +58,9 @@ func makePrefixedAddress( prefix [types.FlowEVMSpecialAddressPrefixLen]byte, ) types.Address { var addr types.Address - prefixIndex := types.AddressLength - uint64ByteSize - copy(addr[:prefixIndex], prefix[:]) - binary.BigEndian.PutUint64(addr[prefixIndex:], index) + copy(addr[:], prefix[:]) + // only works if `len(addr) - len(prefix)` is exactly 8 bytes + binary.BigEndian.PutUint64(addr[len(prefix):], index) return addr } diff --git a/fvm/evm/types/address.go b/fvm/evm/types/address.go index 0d707aea6f1..2c50398ec2b 100644 --- a/fvm/evm/types/address.go +++ b/fvm/evm/types/address.go @@ -17,6 +17,8 @@ const ( // Since EVM addresses are 160-bits long, EVM addresses offer only 80 bits of security (collision resistance // offers the lowest level). // A preimage resistance of 80 bits requires the prefix to be at least 80-bits long (i.e 10 bytes) + // + // When used as a prefix in EVM addresses (20-bytes long), it leaves a variable part of 8 bytes (64 bits). FlowEVMSpecialAddressPrefixLen = 12 ) @@ -65,7 +67,7 @@ func NewAddressFromString(str string) Address { // IsACOAAddress returns true if the address is a COA address // -// This test insure `addr` has been generated as a COA address with high probability. +// This test insures `addr` has been generated as a COA address with high probability. // Brute forcing an EVM address `addr` to pass the `IsACOAAddress` test is as hard as the bit-length // of `FlowEVMCOAAddressPrefix` (here 96 bits). // Although this is lower than the protocol-wide security level in Flow (128 bits), it remains From c8bd1da07ca4fc25013cd6278e54d8aab083c465 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Thu, 1 Feb 2024 23:39:33 +0100 Subject: [PATCH 28/38] explain mapAddressIndex logic --- fvm/evm/handler/addressAllocator.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/fvm/evm/handler/addressAllocator.go b/fvm/evm/handler/addressAllocator.go index ee7838f5eb4..a4f9d3019c6 100644 --- a/fvm/evm/handler/addressAllocator.go +++ b/fvm/evm/handler/addressAllocator.go @@ -11,9 +11,10 @@ import ( const ( ledgerAddressAllocatorKey = "AddressAllocator" - // addressIndexShuffleSeed is used for shuffling address index - // shuffling index is used to make address postfixes look random - addressIndexShuffleSeed = uint64(0xFFEEDDCCBBAA9987) + // addressIndexMultiplierConstant is used for mapping address indices + // into a deterministic random-looking address postfixes. + // look at `mapAddressIndex` for more details. + addressIndexMultiplierConstant = uint64(0xFFEEDDCCBBAA9977) ) type AddressAllocator struct { @@ -41,7 +42,7 @@ func (aa *AddressAllocator) AllocateCOAAddress(uuid uint64) types.Address { } func MakeCOAAddress(index uint64) types.Address { - return makePrefixedAddress(shuffleAddressIndex(index), types.FlowEVMCOAAddressPrefix) + return makePrefixedAddress(mapAddressIndex(index), types.FlowEVMCOAAddressPrefix) } func (aa *AddressAllocator) AllocatePrecompileAddress(index uint64) types.Address { @@ -64,6 +65,18 @@ func makePrefixedAddress( return addr } -func shuffleAddressIndex(preShuffleIndex uint64) uint64 { - return uint64(preShuffleIndex * addressIndexShuffleSeed) +// `mapAddressIndex` maps an index of 64 bits to a deterministic random-looking 64 bits. +// +// The mapping function must be an injective mapping (in this case bijective) +// where every two indices always map to two different results. Multiple injective +// mappings are possible. +// +// The current implementation uses a simple modular multiplication by a constant modulo 2^64. +// The multiplier constant can be any odd number. Since odd numbers are co-prime with 2^64, they +// have an multiplicative inverse modulo 2^64. +// This makes multiplying by an odd number an injective function (and therefore bijective). +// +// Multiplying modulo 2^64 is implicitly implemented as a uint64 multiplication with a uin64 result. +func mapAddressIndex(index uint64) uint64 { + return uint64(index * addressIndexMultiplierConstant) } From 47d5b2c5a25b0c17a856997847d69b14ec6e0f34 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Thu, 1 Feb 2024 23:43:04 +0100 Subject: [PATCH 29/38] add requirement on constant --- fvm/evm/handler/addressAllocator.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fvm/evm/handler/addressAllocator.go b/fvm/evm/handler/addressAllocator.go index a4f9d3019c6..157e3e97876 100644 --- a/fvm/evm/handler/addressAllocator.go +++ b/fvm/evm/handler/addressAllocator.go @@ -11,9 +11,10 @@ import ( const ( ledgerAddressAllocatorKey = "AddressAllocator" - // addressIndexMultiplierConstant is used for mapping address indices - // into a deterministic random-looking address postfixes. - // look at `mapAddressIndex` for more details. + // `addressIndexMultiplierConstant` is used for mapping address indices + // into deterministic random-looking address postfixes. + // The constant must be an ODD number. It is a "nothing-up-my-sleeves" constant. + // Look at `mapAddressIndex` for more details. addressIndexMultiplierConstant = uint64(0xFFEEDDCCBBAA9977) ) From 88de16715ffd1e22f4260671bfe13c4fed3790dd Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Fri, 2 Feb 2024 00:03:59 +0100 Subject: [PATCH 30/38] typos and formulations --- fvm/evm/handler/addressAllocator.go | 2 +- fvm/evm/types/address.go | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/fvm/evm/handler/addressAllocator.go b/fvm/evm/handler/addressAllocator.go index 157e3e97876..7e80e63db4d 100644 --- a/fvm/evm/handler/addressAllocator.go +++ b/fvm/evm/handler/addressAllocator.go @@ -74,7 +74,7 @@ func makePrefixedAddress( // // The current implementation uses a simple modular multiplication by a constant modulo 2^64. // The multiplier constant can be any odd number. Since odd numbers are co-prime with 2^64, they -// have an multiplicative inverse modulo 2^64. +// have a multiplicative inverse modulo 2^64. // This makes multiplying by an odd number an injective function (and therefore bijective). // // Multiplying modulo 2^64 is implicitly implemented as a uint64 multiplication with a uin64 result. diff --git a/fvm/evm/types/address.go b/fvm/evm/types/address.go index 2c50398ec2b..a7ef173a111 100644 --- a/fvm/evm/types/address.go +++ b/fvm/evm/types/address.go @@ -7,22 +7,24 @@ import ( ) const ( - // number of prefix bytes with constant values for special accounts (extended precompiles and COAs) - // using leading zeros for prefix helps with the storage compactness. + // number of prefix bytes with constant values for special accounts (extended precompiles and COAs). // // The prefix length should insure a high-enough level of security against finding a preimage using the hash // function used for EVM addresses generation (Keccak256). This is required to avoid finding an EVM address - // that can also be a valid FlowEVM address. + // that is also a valid FlowEVM address. // The target (minimal) security in this case is the security level provided by EVM addresses. - // Since EVM addresses are 160-bits long, EVM addresses offer only 80 bits of security (collision resistance + // Since EVM addresses are 160-bits long, they offer only 80 bits of security (collision resistance // offers the lowest level). - // A preimage resistance of 80 bits requires the prefix to be at least 80-bits long (i.e 10 bytes) + // A preimage resistance of 80 bits requires the prefix to be at least 80-bits long (i.e 10 bytes). // - // When used as a prefix in EVM addresses (20-bytes long), it leaves a variable part of 8 bytes (64 bits). + // When used as a prefix in EVM addresses (20-bytes long), a prefix length of 12 bytes + // leaves a variable part of 8 bytes (64 bits). FlowEVMSpecialAddressPrefixLen = 12 ) var ( + // Using leading zeros for prefix helps with the storage compactness. + // // Prefix for the built-in EVM precompiles FlowEVMNativePrecompileAddressPrefix = [FlowEVMSpecialAddressPrefixLen]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // Prefix for the extended precompiles From 4fa6fdaee518c124646a098a2aef5e922413dc40 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Fri, 2 Feb 2024 09:33:20 +0100 Subject: [PATCH 31/38] more clarity about the choice of the multplpier constant --- fvm/evm/handler/addressAllocator.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fvm/evm/handler/addressAllocator.go b/fvm/evm/handler/addressAllocator.go index 7e80e63db4d..9f24a5946db 100644 --- a/fvm/evm/handler/addressAllocator.go +++ b/fvm/evm/handler/addressAllocator.go @@ -13,7 +13,11 @@ const ( ledgerAddressAllocatorKey = "AddressAllocator" // `addressIndexMultiplierConstant` is used for mapping address indices // into deterministic random-looking address postfixes. - // The constant must be an ODD number. It is a "nothing-up-my-sleeves" constant. + // The constant must be an ODD number. + // It is a "nothing-up-my-sleeves" constant, chosen to be big enough so that + // the index and its corresponding address look less "related". + // Note that the least significant byte was set to "77" instead of "88" to force + // the odd parity. // Look at `mapAddressIndex` for more details. addressIndexMultiplierConstant = uint64(0xFFEEDDCCBBAA9977) ) From 78f2cbb4be99f9983aa75b421ab16ca219ea9fd5 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Fri, 2 Feb 2024 10:40:01 -0800 Subject: [PATCH 32/38] apply pr feedback --- fvm/evm/handler/addressAllocator.go | 13 +- fvm/evm/handler/coa.go | 224 ---------------------------- fvm/evm/handler/coa/coa.go | 19 +++ fvm/evm/handler/{ => coa}/coa.sol | 0 fvm/evm/handler/coa/coa_abi.json | 212 ++++++++++++++++++++++++++ fvm/evm/handler/coa/coa_bytes.hex | 1 + fvm/evm/handler/handler.go | 5 +- fvm/evm/handler/handler_test.go | 11 +- 8 files changed, 247 insertions(+), 238 deletions(-) delete mode 100644 fvm/evm/handler/coa.go create mode 100644 fvm/evm/handler/coa/coa.go rename fvm/evm/handler/{ => coa}/coa.sol (100%) create mode 100644 fvm/evm/handler/coa/coa_abi.json create mode 100644 fvm/evm/handler/coa/coa_bytes.hex diff --git a/fvm/evm/handler/addressAllocator.go b/fvm/evm/handler/addressAllocator.go index 9f24a5946db..21ac367e7ce 100644 --- a/fvm/evm/handler/addressAllocator.go +++ b/fvm/evm/handler/addressAllocator.go @@ -10,12 +10,11 @@ import ( ) const ( - ledgerAddressAllocatorKey = "AddressAllocator" // `addressIndexMultiplierConstant` is used for mapping address indices - // into deterministic random-looking address postfixes. + // into deterministic random-looking address postfixes. // The constant must be an ODD number. // It is a "nothing-up-my-sleeves" constant, chosen to be big enough so that - // the index and its corresponding address look less "related". + // the index and its corresponding address look less "related". // Note that the least significant byte was set to "77" instead of "88" to force // the odd parity. // Look at `mapAddressIndex` for more details. @@ -72,13 +71,13 @@ func makePrefixedAddress( // `mapAddressIndex` maps an index of 64 bits to a deterministic random-looking 64 bits. // -// The mapping function must be an injective mapping (in this case bijective) -// where every two indices always map to two different results. Multiple injective +// The mapping function must be an injective mapping (in this case bijective) +// where every two indices always map to two different results. Multiple injective // mappings are possible. -// +// // The current implementation uses a simple modular multiplication by a constant modulo 2^64. // The multiplier constant can be any odd number. Since odd numbers are co-prime with 2^64, they -// have a multiplicative inverse modulo 2^64. +// have a multiplicative inverse modulo 2^64. // This makes multiplying by an odd number an injective function (and therefore bijective). // // Multiplying modulo 2^64 is implicitly implemented as a uint64 multiplication with a uin64 result. diff --git a/fvm/evm/handler/coa.go b/fvm/evm/handler/coa.go deleted file mode 100644 index c5a95d2af9f..00000000000 --- a/fvm/evm/handler/coa.go +++ /dev/null @@ -1,224 +0,0 @@ -package handler - -import "encoding/hex" - -var COAContractDeploymentRequiredGas = uint64(723_000) - -// COAContractBytes is the compiled version of the coa smart contract. -var COAContractBytes, _ = hex.DecodeString("608060405234801561000f575f80fd5b50610db98061001d5f395ff3fe608060405260043610610072575f3560e01c80631626ba7e1161004d5780631626ba7e1461011d578063bc197c8114610159578063d0d250bd14610195578063f23a6e61146101bf57610079565b806223de291461007d57806301ffc9a7146100a5578063150b7a02146100e157610079565b3661007957005b5f80fd5b348015610088575f80fd5b506100a3600480360381019061009e9190610641565b6101fb565b005b3480156100b0575f80fd5b506100cb60048036038101906100c69190610760565b610205565b6040516100d891906107a5565b60405180910390f35b3480156100ec575f80fd5b50610107600480360381019061010291906107be565b6103a5565b6040516101149190610851565b60405180910390f35b348015610128575f80fd5b50610143600480360381019061013e91906109d5565b6103b9565b6040516101509190610851565b60405180910390f35b348015610164575f80fd5b5061017f600480360381019061017a9190610a84565b610509565b60405161018c9190610851565b60405180910390f35b3480156101a0575f80fd5b506101a9610520565b6040516101b69190610b6a565b60405180910390f35b3480156101ca575f80fd5b506101e560048036038101906101e09190610b83565b61052d565b6040516101f29190610851565b60405180910390f35b5050505050505050565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806102cf57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061033657507e23de29000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061039e57507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b5f63150b7a0260e01b905095945050505050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff163086866040516024016103f193929190610ca2565b6040516020818303038152906040527f07bb4ecc000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161047b9190610d18565b5f60405180830381855afa9150503d805f81146104b3576040519150601f19603f3d011682016040523d82523d5f602084013e6104b8565b606091505b5091509150816104c6575f80fd5b5f818060200190518101906104db9190610d58565b905080156104f557631626ba7e60e01b9350505050610503565b63ffffffff60e01b93505050505b92915050565b5f63bc197c8160e01b905098975050505050505050565b6801000000000000000181565b5f63f23a6e6160e01b90509695505050505050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61057c82610553565b9050919050565b61058c81610572565b8114610596575f80fd5b50565b5f813590506105a781610583565b92915050565b5f819050919050565b6105bf816105ad565b81146105c9575f80fd5b50565b5f813590506105da816105b6565b92915050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f840112610601576106006105e0565b5b8235905067ffffffffffffffff81111561061e5761061d6105e4565b5b60208301915083600182028301111561063a576106396105e8565b5b9250929050565b5f805f805f805f8060c0898b03121561065d5761065c61054b565b5b5f61066a8b828c01610599565b985050602061067b8b828c01610599565b975050604061068c8b828c01610599565b965050606061069d8b828c016105cc565b955050608089013567ffffffffffffffff8111156106be576106bd61054f565b5b6106ca8b828c016105ec565b945094505060a089013567ffffffffffffffff8111156106ed576106ec61054f565b5b6106f98b828c016105ec565b92509250509295985092959890939650565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61073f8161070b565b8114610749575f80fd5b50565b5f8135905061075a81610736565b92915050565b5f602082840312156107755761077461054b565b5b5f6107828482850161074c565b91505092915050565b5f8115159050919050565b61079f8161078b565b82525050565b5f6020820190506107b85f830184610796565b92915050565b5f805f805f608086880312156107d7576107d661054b565b5b5f6107e488828901610599565b95505060206107f588828901610599565b9450506040610806888289016105cc565b935050606086013567ffffffffffffffff8111156108275761082661054f565b5b610833888289016105ec565b92509250509295509295909350565b61084b8161070b565b82525050565b5f6020820190506108645f830184610842565b92915050565b5f819050919050565b61087c8161086a565b8114610886575f80fd5b50565b5f8135905061089781610873565b92915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6108e7826108a1565b810181811067ffffffffffffffff82111715610906576109056108b1565b5b80604052505050565b5f610918610542565b905061092482826108de565b919050565b5f67ffffffffffffffff821115610943576109426108b1565b5b61094c826108a1565b9050602081019050919050565b828183375f83830152505050565b5f61097961097484610929565b61090f565b9050828152602081018484840111156109955761099461089d565b5b6109a0848285610959565b509392505050565b5f82601f8301126109bc576109bb6105e0565b5b81356109cc848260208601610967565b91505092915050565b5f80604083850312156109eb576109ea61054b565b5b5f6109f885828601610889565b925050602083013567ffffffffffffffff811115610a1957610a1861054f565b5b610a25858286016109a8565b9150509250929050565b5f8083601f840112610a4457610a436105e0565b5b8235905067ffffffffffffffff811115610a6157610a606105e4565b5b602083019150836020820283011115610a7d57610a7c6105e8565b5b9250929050565b5f805f805f805f8060a0898b031215610aa057610a9f61054b565b5b5f610aad8b828c01610599565b9850506020610abe8b828c01610599565b975050604089013567ffffffffffffffff811115610adf57610ade61054f565b5b610aeb8b828c01610a2f565b9650965050606089013567ffffffffffffffff811115610b0e57610b0d61054f565b5b610b1a8b828c01610a2f565b9450945050608089013567ffffffffffffffff811115610b3d57610b3c61054f565b5b610b498b828c016105ec565b92509250509295985092959890939650565b610b6481610572565b82525050565b5f602082019050610b7d5f830184610b5b565b92915050565b5f805f805f8060a08789031215610b9d57610b9c61054b565b5b5f610baa89828a01610599565b9650506020610bbb89828a01610599565b9550506040610bcc89828a016105cc565b9450506060610bdd89828a016105cc565b935050608087013567ffffffffffffffff811115610bfe57610bfd61054f565b5b610c0a89828a016105ec565b92509250509295509295509295565b610c228161086a565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610c5f578082015181840152602081019050610c44565b5f8484015250505050565b5f610c7482610c28565b610c7e8185610c32565b9350610c8e818560208601610c42565b610c97816108a1565b840191505092915050565b5f606082019050610cb55f830186610b5b565b610cc26020830185610c19565b8181036040830152610cd48184610c6a565b9050949350505050565b5f81905092915050565b5f610cf282610c28565b610cfc8185610cde565b9350610d0c818560208601610c42565b80840191505092915050565b5f610d238284610ce8565b915081905092915050565b610d378161078b565b8114610d41575f80fd5b50565b5f81519050610d5281610d2e565b92915050565b5f60208284031215610d6d57610d6c61054b565b5b5f610d7a84828501610d44565b9150509291505056fea264697066735822122076a1ae2a0143dcafcdc7f7ea5bdba7fb314b92d8e74573311cbc152ce364d8a564736f6c63430008160033") - -// COAContractABIJSON is the json string of ABI of the coa smart contract. -var COAContractABIJSON = ` -[ - { - "inputs": [], - "name": "cadenceArch", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_hash", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "_sig", - "type": "bytes" - } - ], - "name": "isValidSignature", - "outputs": [ - { - "internalType": "bytes4", - "name": "", - "type": "bytes4" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - }, - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "name": "onERC1155BatchReceived", - "outputs": [ - { - "internalType": "bytes4", - "name": "", - "type": "bytes4" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "name": "onERC1155Received", - "outputs": [ - { - "internalType": "bytes4", - "name": "", - "type": "bytes4" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "name": "onERC721Received", - "outputs": [ - { - "internalType": "bytes4", - "name": "", - "type": "bytes4" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes4", - "name": "id", - "type": "bytes4" - } - ], - "name": "supportsInterface", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "name": "tokensReceived", - "outputs": [], - "stateMutability": "pure", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } -] -` diff --git a/fvm/evm/handler/coa/coa.go b/fvm/evm/handler/coa/coa.go new file mode 100644 index 00000000000..8be5394cff5 --- /dev/null +++ b/fvm/evm/handler/coa/coa.go @@ -0,0 +1,19 @@ +package coa + +import ( + _ "embed" + "encoding/hex" +) + +var ContractDeploymentRequiredGas = uint64(723_000) + +//go:embed coa_bytes.hex +var contractBytesInHex string + +// ContractBytes is the compiled version of the coa smart contract. +var ContractBytes, _ = hex.DecodeString(contractBytesInHex) + +// ContractABIJSON is the json string of ABI of the coa smart contract. +// +//go:embed coa_abi.json +var ContractABIJSON string diff --git a/fvm/evm/handler/coa.sol b/fvm/evm/handler/coa/coa.sol similarity index 100% rename from fvm/evm/handler/coa.sol rename to fvm/evm/handler/coa/coa.sol diff --git a/fvm/evm/handler/coa/coa_abi.json b/fvm/evm/handler/coa/coa_abi.json new file mode 100644 index 00000000000..3f46c1f4b8f --- /dev/null +++ b/fvm/evm/handler/coa/coa_abi.json @@ -0,0 +1,212 @@ +[ + { + "inputs": [], + "name": "cadenceArch", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_sig", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "id", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "tokensReceived", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/fvm/evm/handler/coa/coa_bytes.hex b/fvm/evm/handler/coa/coa_bytes.hex new file mode 100644 index 00000000000..3fe140b33c3 --- /dev/null +++ b/fvm/evm/handler/coa/coa_bytes.hex @@ -0,0 +1 @@ +608060405234801561000f575f80fd5b50610db98061001d5f395ff3fe608060405260043610610072575f3560e01c80631626ba7e1161004d5780631626ba7e1461011d578063bc197c8114610159578063d0d250bd14610195578063f23a6e61146101bf57610079565b806223de291461007d57806301ffc9a7146100a5578063150b7a02146100e157610079565b3661007957005b5f80fd5b348015610088575f80fd5b506100a3600480360381019061009e9190610641565b6101fb565b005b3480156100b0575f80fd5b506100cb60048036038101906100c69190610760565b610205565b6040516100d891906107a5565b60405180910390f35b3480156100ec575f80fd5b50610107600480360381019061010291906107be565b6103a5565b6040516101149190610851565b60405180910390f35b348015610128575f80fd5b50610143600480360381019061013e91906109d5565b6103b9565b6040516101509190610851565b60405180910390f35b348015610164575f80fd5b5061017f600480360381019061017a9190610a84565b610509565b60405161018c9190610851565b60405180910390f35b3480156101a0575f80fd5b506101a9610520565b6040516101b69190610b6a565b60405180910390f35b3480156101ca575f80fd5b506101e560048036038101906101e09190610b83565b61052d565b6040516101f29190610851565b60405180910390f35b5050505050505050565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806102cf57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061033657507e23de29000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061039e57507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b5f63150b7a0260e01b905095945050505050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff163086866040516024016103f193929190610ca2565b6040516020818303038152906040527f07bb4ecc000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161047b9190610d18565b5f60405180830381855afa9150503d805f81146104b3576040519150601f19603f3d011682016040523d82523d5f602084013e6104b8565b606091505b5091509150816104c6575f80fd5b5f818060200190518101906104db9190610d58565b905080156104f557631626ba7e60e01b9350505050610503565b63ffffffff60e01b93505050505b92915050565b5f63bc197c8160e01b905098975050505050505050565b6801000000000000000181565b5f63f23a6e6160e01b90509695505050505050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61057c82610553565b9050919050565b61058c81610572565b8114610596575f80fd5b50565b5f813590506105a781610583565b92915050565b5f819050919050565b6105bf816105ad565b81146105c9575f80fd5b50565b5f813590506105da816105b6565b92915050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f840112610601576106006105e0565b5b8235905067ffffffffffffffff81111561061e5761061d6105e4565b5b60208301915083600182028301111561063a576106396105e8565b5b9250929050565b5f805f805f805f8060c0898b03121561065d5761065c61054b565b5b5f61066a8b828c01610599565b985050602061067b8b828c01610599565b975050604061068c8b828c01610599565b965050606061069d8b828c016105cc565b955050608089013567ffffffffffffffff8111156106be576106bd61054f565b5b6106ca8b828c016105ec565b945094505060a089013567ffffffffffffffff8111156106ed576106ec61054f565b5b6106f98b828c016105ec565b92509250509295985092959890939650565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61073f8161070b565b8114610749575f80fd5b50565b5f8135905061075a81610736565b92915050565b5f602082840312156107755761077461054b565b5b5f6107828482850161074c565b91505092915050565b5f8115159050919050565b61079f8161078b565b82525050565b5f6020820190506107b85f830184610796565b92915050565b5f805f805f608086880312156107d7576107d661054b565b5b5f6107e488828901610599565b95505060206107f588828901610599565b9450506040610806888289016105cc565b935050606086013567ffffffffffffffff8111156108275761082661054f565b5b610833888289016105ec565b92509250509295509295909350565b61084b8161070b565b82525050565b5f6020820190506108645f830184610842565b92915050565b5f819050919050565b61087c8161086a565b8114610886575f80fd5b50565b5f8135905061089781610873565b92915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6108e7826108a1565b810181811067ffffffffffffffff82111715610906576109056108b1565b5b80604052505050565b5f610918610542565b905061092482826108de565b919050565b5f67ffffffffffffffff821115610943576109426108b1565b5b61094c826108a1565b9050602081019050919050565b828183375f83830152505050565b5f61097961097484610929565b61090f565b9050828152602081018484840111156109955761099461089d565b5b6109a0848285610959565b509392505050565b5f82601f8301126109bc576109bb6105e0565b5b81356109cc848260208601610967565b91505092915050565b5f80604083850312156109eb576109ea61054b565b5b5f6109f885828601610889565b925050602083013567ffffffffffffffff811115610a1957610a1861054f565b5b610a25858286016109a8565b9150509250929050565b5f8083601f840112610a4457610a436105e0565b5b8235905067ffffffffffffffff811115610a6157610a606105e4565b5b602083019150836020820283011115610a7d57610a7c6105e8565b5b9250929050565b5f805f805f805f8060a0898b031215610aa057610a9f61054b565b5b5f610aad8b828c01610599565b9850506020610abe8b828c01610599565b975050604089013567ffffffffffffffff811115610adf57610ade61054f565b5b610aeb8b828c01610a2f565b9650965050606089013567ffffffffffffffff811115610b0e57610b0d61054f565b5b610b1a8b828c01610a2f565b9450945050608089013567ffffffffffffffff811115610b3d57610b3c61054f565b5b610b498b828c016105ec565b92509250509295985092959890939650565b610b6481610572565b82525050565b5f602082019050610b7d5f830184610b5b565b92915050565b5f805f805f8060a08789031215610b9d57610b9c61054b565b5b5f610baa89828a01610599565b9650506020610bbb89828a01610599565b9550506040610bcc89828a016105cc565b9450506060610bdd89828a016105cc565b935050608087013567ffffffffffffffff811115610bfe57610bfd61054f565b5b610c0a89828a016105ec565b92509250509295509295509295565b610c228161086a565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610c5f578082015181840152602081019050610c44565b5f8484015250505050565b5f610c7482610c28565b610c7e8185610c32565b9350610c8e818560208601610c42565b610c97816108a1565b840191505092915050565b5f606082019050610cb55f830186610b5b565b610cc26020830185610c19565b8181036040830152610cd48184610c6a565b9050949350505050565b5f81905092915050565b5f610cf282610c28565b610cfc8185610cde565b9350610d0c818560208601610c42565b80840191505092915050565b5f610d238284610ce8565b915081905092915050565b610d378161078b565b8114610d41575f80fd5b50565b5f81519050610d5281610d2e565b92915050565b5f60208284031215610d6d57610d6c61054b565b5b5f610d7a84828501610d44565b9150509291505056fea264697066735822122076a1ae2a0143dcafcdc7f7ea5bdba7fb314b92d8e74573311cbc152ce364d8a564736f6c63430008160033 \ No newline at end of file diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index c5805004ff1..2bf1ae44491 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -11,6 +11,7 @@ import ( "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/errors" + "github.com/onflow/flow-go/fvm/evm/handler/coa" "github.com/onflow/flow-go/fvm/evm/precompiles" "github.com/onflow/flow-go/fvm/evm/types" ) @@ -64,14 +65,14 @@ func getPrecompiles( // DeployCOA deploys a cadence-owned-account and returns the address func (h *ContractHandler) DeployCOA(uuid uint64) types.Address { target := h.addressAllocator.AllocateCOAAddress(uuid) - gaslimit := types.GasLimit(COAContractDeploymentRequiredGas) + gaslimit := types.GasLimit(coa.ContractDeploymentRequiredGas) h.checkGasLimit(gaslimit) factory := h.addressAllocator.COAFactoryAddress() call := types.NewDeployCallWithTargetAddress( factory, target, - COAContractBytes, + coa.ContractBytes, uint64(gaslimit), new(big.Int), ) diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 2cef07199e8..0664fd0d047 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -24,6 +24,7 @@ import ( "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/fvm/evm/emulator" "github.com/onflow/flow-go/fvm/evm/handler" + "github.com/onflow/flow-go/fvm/evm/handler/coa" "github.com/onflow/flow-go/fvm/evm/precompiles" "github.com/onflow/flow-go/fvm/evm/testutils" "github.com/onflow/flow-go/fvm/evm/types" @@ -342,8 +343,8 @@ func TestHandler_COA(t *testing.T) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { h := SetupHandler(t, backend, rootAddr) - coa := h.DeployCOA(1) - acc := h.AccountByAddress(coa, true) + coa1 := h.DeployCOA(1) + acc := h.AccountByAddress(coa1, true) require.NotEmpty(t, acc.Code()) // make a second account with some money @@ -353,15 +354,15 @@ func TestHandler_COA(t *testing.T) { // transfer money to COA acc2.Transfer( - coa, + coa1, types.MakeABalanceInFlow(1), ) // make a call to the contract ret := acc2.Call( - coa, + coa1, testutils.MakeCallData(t, - handler.COAContractABIJSON, + coa.ContractABIJSON, "onERC721Received", gethCommon.Address{1}, gethCommon.Address{1}, From 326471d00ac66d1a80c013c5dff6a48a3534be9f Mon Sep 17 00:00:00 2001 From: ramtinms Date: Fri, 2 Feb 2024 12:42:02 -0800 Subject: [PATCH 33/38] improve coa solidity code doc --- fvm/evm/handler/coa/coa.sol | 112 +++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 47 deletions(-) diff --git a/fvm/evm/handler/coa/coa.sol b/fvm/evm/handler/coa/coa.sol index 1c161e232f6..4fba8857015 100644 --- a/fvm/evm/handler/coa/coa.sol +++ b/fvm/evm/handler/coa/coa.sol @@ -5,40 +5,57 @@ interface IERC165 { } interface ERC721TokenReceiver { function onERC721Received( - address _operator, - address _from, - uint256 _tokenId, - bytes calldata _data - ) external returns (bytes4); + address _operator, + address _from, + uint256 _tokenId, + bytes calldata _data + ) external returns (bytes4); } interface ERC777TokensRecipient { -function tokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes calldata data, - bytes calldata operatorData - ) external; + function tokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes calldata data, + bytes calldata operatorData + ) external; } interface ERC1155TokenReceiver { -function onERC1155Received( - address _operator, - address _from, - uint256 _id, - uint256 _value, - bytes calldata _data - ) external returns (bytes4); -function onERC1155BatchReceived( - address _operator, - address _from, - uint256[] calldata _ids, - uint256[] calldata _values, - bytes calldata _data - ) external returns (bytes4); + + function onERC1155Received( + address _operator, + address _from, + uint256 _id, + uint256 _value, + bytes calldata _data + ) external returns (bytes4); + + function onERC1155BatchReceived( + address _operator, + address _from, + uint256[] calldata _ids, + uint256[] calldata _values, + bytes calldata _data + ) external returns (bytes4); + } contract COA is ERC1155TokenReceiver, ERC777TokensRecipient, ERC721TokenReceiver, IERC165 { address constant public cadenceArch = 0x0000000000000000000000010000000000000001; + + // bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")) + bytes4 constant internal ERC721ReceivedIsSupported = 0x150b7a02; + + // bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) + bytes4 constant internal ERC1155ReceivedIsSupported = 0xf23a6e61; + + // bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)")) + bytes4 constant internal ERC1155BatchReceivedIsSupported = 0xbc197c81; + + // bytes4(keccak256("isValidSignature(bytes32,bytes)") + bytes4 constant internal ValidERC1271Signature = 0x1626ba7e; + bytes4 constant internal InvalidERC1271Signature = 0xffffffff; + receive() external payable { } function supportsInterface(bytes4 id) external view virtual override returns (bool) { @@ -49,54 +66,55 @@ contract COA is ERC1155TokenReceiver, ERC777TokensRecipient, ERC721TokenReceiver id == type(IERC165).interfaceId; } - function onERC1155Received( + function tokensReceived( + address, address, address, uint256, - uint256, + bytes calldata, bytes calldata - ) external pure override returns (bytes4) { - return 0xf23a6e61; - } + ) external pure override {} - function onERC1155BatchReceived( + function onERC721Received( address, address, - uint256[] calldata, - uint256[] calldata, + uint256, bytes calldata ) external pure override returns (bytes4) { - return 0xbc197c81; + return ERC721ReceivedIsSupported; } - function onERC721Received( + function onERC1155Received( address, address, uint256, + uint256, bytes calldata ) external pure override returns (bytes4) { - return 0x150b7a02; + return ERC1155ReceivedIsSupported; } - function tokensReceived( - address, + function onERC1155BatchReceived( address, address, - uint256, - bytes calldata, + uint256[] calldata, + uint256[] calldata, bytes calldata - ) external pure override {} + ) external pure override returns (bytes4) { + return ERC1155BatchReceivedIsSupported; + } + // ERC1271 requirement function isValidSignature( - bytes32 _hash, - bytes memory _sig + bytes32 _hash, + bytes memory _sig ) external view virtual returns (bytes4){ (bool ok, bytes memory data) = cadenceArch.staticcall(abi.encodeWithSignature("verifyCOAOwnershipProof(address,bytes32,bytes)", address(this), _hash, _sig)); require(ok); bool output = abi.decode(data, (bool)); if (output) { - return 0x1626ba7e; + return ValidERC1271Signature; } - return 0xffffffff; + return InvalidERC1271Signature; } } \ No newline at end of file From 62027437d6c28554f79a2384c1b3cffda3d240bc Mon Sep 17 00:00:00 2001 From: ramtinms Date: Fri, 2 Feb 2024 12:44:18 -0800 Subject: [PATCH 34/38] small improvements --- fvm/evm/handler/coa/coa.sol | 6 ++++++ fvm/evm/handler/coa/coa_bytes.hex | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/fvm/evm/handler/coa/coa.sol b/fvm/evm/handler/coa/coa.sol index 4fba8857015..7c35f35c3cf 100644 --- a/fvm/evm/handler/coa/coa.sol +++ b/fvm/evm/handler/coa/coa.sol @@ -1,8 +1,11 @@ // SPDX-License-Identifier: UNLICENSED + pragma solidity >=0.7.0 <0.9.0; + interface IERC165 { function supportsInterface(bytes4 interfaceId) external view returns (bool); } + interface ERC721TokenReceiver { function onERC721Received( address _operator, @@ -11,6 +14,7 @@ interface ERC721TokenReceiver { bytes calldata _data ) external returns (bytes4); } + interface ERC777TokensRecipient { function tokensReceived( address operator, @@ -21,6 +25,7 @@ interface ERC777TokensRecipient { bytes calldata operatorData ) external; } + interface ERC1155TokenReceiver { function onERC1155Received( @@ -40,6 +45,7 @@ interface ERC1155TokenReceiver { ) external returns (bytes4); } + contract COA is ERC1155TokenReceiver, ERC777TokensRecipient, ERC721TokenReceiver, IERC165 { address constant public cadenceArch = 0x0000000000000000000000010000000000000001; diff --git a/fvm/evm/handler/coa/coa_bytes.hex b/fvm/evm/handler/coa/coa_bytes.hex index 3fe140b33c3..7d63c2389f1 100644 --- a/fvm/evm/handler/coa/coa_bytes.hex +++ b/fvm/evm/handler/coa/coa_bytes.hex @@ -1 +1 @@ -608060405234801561000f575f80fd5b50610db98061001d5f395ff3fe608060405260043610610072575f3560e01c80631626ba7e1161004d5780631626ba7e1461011d578063bc197c8114610159578063d0d250bd14610195578063f23a6e61146101bf57610079565b806223de291461007d57806301ffc9a7146100a5578063150b7a02146100e157610079565b3661007957005b5f80fd5b348015610088575f80fd5b506100a3600480360381019061009e9190610641565b6101fb565b005b3480156100b0575f80fd5b506100cb60048036038101906100c69190610760565b610205565b6040516100d891906107a5565b60405180910390f35b3480156100ec575f80fd5b50610107600480360381019061010291906107be565b6103a5565b6040516101149190610851565b60405180910390f35b348015610128575f80fd5b50610143600480360381019061013e91906109d5565b6103b9565b6040516101509190610851565b60405180910390f35b348015610164575f80fd5b5061017f600480360381019061017a9190610a84565b610509565b60405161018c9190610851565b60405180910390f35b3480156101a0575f80fd5b506101a9610520565b6040516101b69190610b6a565b60405180910390f35b3480156101ca575f80fd5b506101e560048036038101906101e09190610b83565b61052d565b6040516101f29190610851565b60405180910390f35b5050505050505050565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806102cf57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061033657507e23de29000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061039e57507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b5f63150b7a0260e01b905095945050505050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff163086866040516024016103f193929190610ca2565b6040516020818303038152906040527f07bb4ecc000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161047b9190610d18565b5f60405180830381855afa9150503d805f81146104b3576040519150601f19603f3d011682016040523d82523d5f602084013e6104b8565b606091505b5091509150816104c6575f80fd5b5f818060200190518101906104db9190610d58565b905080156104f557631626ba7e60e01b9350505050610503565b63ffffffff60e01b93505050505b92915050565b5f63bc197c8160e01b905098975050505050505050565b6801000000000000000181565b5f63f23a6e6160e01b90509695505050505050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61057c82610553565b9050919050565b61058c81610572565b8114610596575f80fd5b50565b5f813590506105a781610583565b92915050565b5f819050919050565b6105bf816105ad565b81146105c9575f80fd5b50565b5f813590506105da816105b6565b92915050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f840112610601576106006105e0565b5b8235905067ffffffffffffffff81111561061e5761061d6105e4565b5b60208301915083600182028301111561063a576106396105e8565b5b9250929050565b5f805f805f805f8060c0898b03121561065d5761065c61054b565b5b5f61066a8b828c01610599565b985050602061067b8b828c01610599565b975050604061068c8b828c01610599565b965050606061069d8b828c016105cc565b955050608089013567ffffffffffffffff8111156106be576106bd61054f565b5b6106ca8b828c016105ec565b945094505060a089013567ffffffffffffffff8111156106ed576106ec61054f565b5b6106f98b828c016105ec565b92509250509295985092959890939650565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61073f8161070b565b8114610749575f80fd5b50565b5f8135905061075a81610736565b92915050565b5f602082840312156107755761077461054b565b5b5f6107828482850161074c565b91505092915050565b5f8115159050919050565b61079f8161078b565b82525050565b5f6020820190506107b85f830184610796565b92915050565b5f805f805f608086880312156107d7576107d661054b565b5b5f6107e488828901610599565b95505060206107f588828901610599565b9450506040610806888289016105cc565b935050606086013567ffffffffffffffff8111156108275761082661054f565b5b610833888289016105ec565b92509250509295509295909350565b61084b8161070b565b82525050565b5f6020820190506108645f830184610842565b92915050565b5f819050919050565b61087c8161086a565b8114610886575f80fd5b50565b5f8135905061089781610873565b92915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6108e7826108a1565b810181811067ffffffffffffffff82111715610906576109056108b1565b5b80604052505050565b5f610918610542565b905061092482826108de565b919050565b5f67ffffffffffffffff821115610943576109426108b1565b5b61094c826108a1565b9050602081019050919050565b828183375f83830152505050565b5f61097961097484610929565b61090f565b9050828152602081018484840111156109955761099461089d565b5b6109a0848285610959565b509392505050565b5f82601f8301126109bc576109bb6105e0565b5b81356109cc848260208601610967565b91505092915050565b5f80604083850312156109eb576109ea61054b565b5b5f6109f885828601610889565b925050602083013567ffffffffffffffff811115610a1957610a1861054f565b5b610a25858286016109a8565b9150509250929050565b5f8083601f840112610a4457610a436105e0565b5b8235905067ffffffffffffffff811115610a6157610a606105e4565b5b602083019150836020820283011115610a7d57610a7c6105e8565b5b9250929050565b5f805f805f805f8060a0898b031215610aa057610a9f61054b565b5b5f610aad8b828c01610599565b9850506020610abe8b828c01610599565b975050604089013567ffffffffffffffff811115610adf57610ade61054f565b5b610aeb8b828c01610a2f565b9650965050606089013567ffffffffffffffff811115610b0e57610b0d61054f565b5b610b1a8b828c01610a2f565b9450945050608089013567ffffffffffffffff811115610b3d57610b3c61054f565b5b610b498b828c016105ec565b92509250509295985092959890939650565b610b6481610572565b82525050565b5f602082019050610b7d5f830184610b5b565b92915050565b5f805f805f8060a08789031215610b9d57610b9c61054b565b5b5f610baa89828a01610599565b9650506020610bbb89828a01610599565b9550506040610bcc89828a016105cc565b9450506060610bdd89828a016105cc565b935050608087013567ffffffffffffffff811115610bfe57610bfd61054f565b5b610c0a89828a016105ec565b92509250509295509295509295565b610c228161086a565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610c5f578082015181840152602081019050610c44565b5f8484015250505050565b5f610c7482610c28565b610c7e8185610c32565b9350610c8e818560208601610c42565b610c97816108a1565b840191505092915050565b5f606082019050610cb55f830186610b5b565b610cc26020830185610c19565b8181036040830152610cd48184610c6a565b9050949350505050565b5f81905092915050565b5f610cf282610c28565b610cfc8185610cde565b9350610d0c818560208601610c42565b80840191505092915050565b5f610d238284610ce8565b915081905092915050565b610d378161078b565b8114610d41575f80fd5b50565b5f81519050610d5281610d2e565b92915050565b5f60208284031215610d6d57610d6c61054b565b5b5f610d7a84828501610d44565b9150509291505056fea264697066735822122076a1ae2a0143dcafcdc7f7ea5bdba7fb314b92d8e74573311cbc152ce364d8a564736f6c63430008160033 \ No newline at end of file +608060405234801561000f575f80fd5b50610db98061001d5f395ff3fe608060405260043610610072575f3560e01c80631626ba7e1161004d5780631626ba7e1461011d578063bc197c8114610159578063d0d250bd14610195578063f23a6e61146101bf57610079565b806223de291461007d57806301ffc9a7146100a5578063150b7a02146100e157610079565b3661007957005b5f80fd5b348015610088575f80fd5b506100a3600480360381019061009e9190610641565b6101fb565b005b3480156100b0575f80fd5b506100cb60048036038101906100c69190610760565b610205565b6040516100d891906107a5565b60405180910390f35b3480156100ec575f80fd5b50610107600480360381019061010291906107be565b6103a5565b6040516101149190610851565b60405180910390f35b348015610128575f80fd5b50610143600480360381019061013e91906109d5565b6103b9565b6040516101509190610851565b60405180910390f35b348015610164575f80fd5b5061017f600480360381019061017a9190610a84565b610509565b60405161018c9190610851565b60405180910390f35b3480156101a0575f80fd5b506101a9610520565b6040516101b69190610b6a565b60405180910390f35b3480156101ca575f80fd5b506101e560048036038101906101e09190610b83565b61052d565b6040516101f29190610851565b60405180910390f35b5050505050505050565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806102cf57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061033657507e23de29000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061039e57507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b5f63150b7a0260e01b905095945050505050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff163086866040516024016103f193929190610ca2565b6040516020818303038152906040527f5ee837e7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161047b9190610d18565b5f60405180830381855afa9150503d805f81146104b3576040519150601f19603f3d011682016040523d82523d5f602084013e6104b8565b606091505b5091509150816104c6575f80fd5b5f818060200190518101906104db9190610d58565b905080156104f557631626ba7e60e01b9350505050610503565b63ffffffff60e01b93505050505b92915050565b5f63bc197c8160e01b905098975050505050505050565b6801000000000000000181565b5f63f23a6e6160e01b90509695505050505050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61057c82610553565b9050919050565b61058c81610572565b8114610596575f80fd5b50565b5f813590506105a781610583565b92915050565b5f819050919050565b6105bf816105ad565b81146105c9575f80fd5b50565b5f813590506105da816105b6565b92915050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f840112610601576106006105e0565b5b8235905067ffffffffffffffff81111561061e5761061d6105e4565b5b60208301915083600182028301111561063a576106396105e8565b5b9250929050565b5f805f805f805f8060c0898b03121561065d5761065c61054b565b5b5f61066a8b828c01610599565b985050602061067b8b828c01610599565b975050604061068c8b828c01610599565b965050606061069d8b828c016105cc565b955050608089013567ffffffffffffffff8111156106be576106bd61054f565b5b6106ca8b828c016105ec565b945094505060a089013567ffffffffffffffff8111156106ed576106ec61054f565b5b6106f98b828c016105ec565b92509250509295985092959890939650565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61073f8161070b565b8114610749575f80fd5b50565b5f8135905061075a81610736565b92915050565b5f602082840312156107755761077461054b565b5b5f6107828482850161074c565b91505092915050565b5f8115159050919050565b61079f8161078b565b82525050565b5f6020820190506107b85f830184610796565b92915050565b5f805f805f608086880312156107d7576107d661054b565b5b5f6107e488828901610599565b95505060206107f588828901610599565b9450506040610806888289016105cc565b935050606086013567ffffffffffffffff8111156108275761082661054f565b5b610833888289016105ec565b92509250509295509295909350565b61084b8161070b565b82525050565b5f6020820190506108645f830184610842565b92915050565b5f819050919050565b61087c8161086a565b8114610886575f80fd5b50565b5f8135905061089781610873565b92915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6108e7826108a1565b810181811067ffffffffffffffff82111715610906576109056108b1565b5b80604052505050565b5f610918610542565b905061092482826108de565b919050565b5f67ffffffffffffffff821115610943576109426108b1565b5b61094c826108a1565b9050602081019050919050565b828183375f83830152505050565b5f61097961097484610929565b61090f565b9050828152602081018484840111156109955761099461089d565b5b6109a0848285610959565b509392505050565b5f82601f8301126109bc576109bb6105e0565b5b81356109cc848260208601610967565b91505092915050565b5f80604083850312156109eb576109ea61054b565b5b5f6109f885828601610889565b925050602083013567ffffffffffffffff811115610a1957610a1861054f565b5b610a25858286016109a8565b9150509250929050565b5f8083601f840112610a4457610a436105e0565b5b8235905067ffffffffffffffff811115610a6157610a606105e4565b5b602083019150836020820283011115610a7d57610a7c6105e8565b5b9250929050565b5f805f805f805f8060a0898b031215610aa057610a9f61054b565b5b5f610aad8b828c01610599565b9850506020610abe8b828c01610599565b975050604089013567ffffffffffffffff811115610adf57610ade61054f565b5b610aeb8b828c01610a2f565b9650965050606089013567ffffffffffffffff811115610b0e57610b0d61054f565b5b610b1a8b828c01610a2f565b9450945050608089013567ffffffffffffffff811115610b3d57610b3c61054f565b5b610b498b828c016105ec565b92509250509295985092959890939650565b610b6481610572565b82525050565b5f602082019050610b7d5f830184610b5b565b92915050565b5f805f805f8060a08789031215610b9d57610b9c61054b565b5b5f610baa89828a01610599565b9650506020610bbb89828a01610599565b9550506040610bcc89828a016105cc565b9450506060610bdd89828a016105cc565b935050608087013567ffffffffffffffff811115610bfe57610bfd61054f565b5b610c0a89828a016105ec565b92509250509295509295509295565b610c228161086a565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610c5f578082015181840152602081019050610c44565b5f8484015250505050565b5f610c7482610c28565b610c7e8185610c32565b9350610c8e818560208601610c42565b610c97816108a1565b840191505092915050565b5f606082019050610cb55f830186610b5b565b610cc26020830185610c19565b8181036040830152610cd48184610c6a565b9050949350505050565b5f81905092915050565b5f610cf282610c28565b610cfc8185610cde565b9350610d0c818560208601610c42565b80840191505092915050565b5f610d238284610ce8565b915081905092915050565b610d378161078b565b8114610d41575f80fd5b50565b5f81519050610d5281610d2e565b92915050565b5f60208284031215610d6d57610d6c61054b565b5b5f610d7a84828501610d44565b9150509291505056fea264697066735822122079a2b495dc3da197ff64bc2f601bc2ea89b1704c035aaebb9e4a19d8e71f691064736f6c63430008160033 \ No newline at end of file From 6d7e364d66d2d881e991444c4e5ecf0c8c92b294 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Fri, 2 Feb 2024 12:59:20 -0800 Subject: [PATCH 35/38] clean up --- fvm/evm/types/address.go | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/fvm/evm/types/address.go b/fvm/evm/types/address.go index a7ef173a111..640ec525276 100644 --- a/fvm/evm/types/address.go +++ b/fvm/evm/types/address.go @@ -6,21 +6,18 @@ import ( gethCommon "github.com/ethereum/go-ethereum/common" ) -const ( - // number of prefix bytes with constant values for special accounts (extended precompiles and COAs). - // - // The prefix length should insure a high-enough level of security against finding a preimage using the hash - // function used for EVM addresses generation (Keccak256). This is required to avoid finding an EVM address - // that is also a valid FlowEVM address. - // The target (minimal) security in this case is the security level provided by EVM addresses. - // Since EVM addresses are 160-bits long, they offer only 80 bits of security (collision resistance - // offers the lowest level). - // A preimage resistance of 80 bits requires the prefix to be at least 80-bits long (i.e 10 bytes). - // - // When used as a prefix in EVM addresses (20-bytes long), a prefix length of 12 bytes - // leaves a variable part of 8 bytes (64 bits). - FlowEVMSpecialAddressPrefixLen = 12 -) +// FlowEVMSpecialAddressPrefixLen captures the number of prefix bytes with constant values for special accounts (extended precompiles and COAs). +// +// The prefix length should insure a high-enough level of security against finding a preimage using the hash +// function used for EVM addresses generation (Keccak256). This is required to avoid finding an EVM address +// that is also a valid FlowEVM address. +// The target (minimal) security in this case is the security level provided by EVM addresses. +// Since EVM addresses are 160-bits long, they offer only 80 bits of security (collision resistance +// offers the lowest level). +// A preimage resistance of 80 bits requires the prefix to be at least 80-bits long (i.e 10 bytes). +// When used as a prefix in EVM addresses (20-bytes long), a prefix length of 12 bytes +// leaves a variable part of 8 bytes (64 bits). +const FlowEVMSpecialAddressPrefixLen = 12 var ( // Using leading zeros for prefix helps with the storage compactness. From 99747b1f99f0f6c23f979ebd3cac794be7a48e2a Mon Sep 17 00:00:00 2001 From: ramtinms Date: Fri, 2 Feb 2024 13:19:30 -0800 Subject: [PATCH 36/38] improve bridge account address allocation --- fvm/evm/stdlib/contract.cdc | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index b5cad4fb24e..ab9e848feca 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -72,11 +72,20 @@ contract EVM { var addressBytes: [UInt8; 20] init() { - self.addressBytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + // address is initially set to zero + // but updated through initAddress later + // we have to do this since we need resource id (uuid) + // to calculate the EVM address for this bridge account + self.addressBytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] } access(contract) - fun setAddress(addressBytes: [UInt8; 20]) { + fun initAddress(addressBytes: [UInt8; 20]) { + // only allow set address for the first time + // check address is empty + for item in self.addressBytes { + assert(item == 0, message: "address byte is not empty") + } self.addressBytes = addressBytes } @@ -157,7 +166,7 @@ contract EVM { fun createBridgedAccount(): @BridgedAccount { let acc <-create BridgedAccount() let addr = InternalEVM.createBridgedAccount(uuid: acc.uuid) - acc.setAddress(addressBytes: addr) + acc.initAddress(addressBytes: addr) return <-acc } From bb58529423f4253bb81efcd6c484682c1bc1c3d1 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Fri, 2 Feb 2024 14:24:33 -0800 Subject: [PATCH 37/38] fix test --- fvm/evm/handler/addressAllocator_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fvm/evm/handler/addressAllocator_test.go b/fvm/evm/handler/addressAllocator_test.go index b2471da358c..94e51fcdbea 100644 --- a/fvm/evm/handler/addressAllocator_test.go +++ b/fvm/evm/handler/addressAllocator_test.go @@ -26,14 +26,14 @@ func TestAddressAllocator(t *testing.T) { // test default value fall back adr = aa.AllocateCOAAddress(1) - expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x000000000000000000000002ffeeddccbbaa9987")) + expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x000000000000000000000002ffeeddccbbaa9977")) require.Equal(t, expectedAddress, adr) // check conforming to types require.True(t, types.IsACOAAddress(adr)) // continous allocation logic adr = aa.AllocateCOAAddress(2) - expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x000000000000000000000002ffddbb997755330e")) + expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x000000000000000000000002ffddbb99775532ee")) require.Equal(t, expectedAddress, adr) // check conforming to types require.True(t, types.IsACOAAddress(adr)) From 75081ff3d020b360725b579d9e446d37d5913b76 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Fri, 2 Feb 2024 15:27:05 -0800 Subject: [PATCH 38/38] fix test --- fvm/fvm_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fvm/fvm_test.go b/fvm/fvm_test.go index 06eab811ff6..81627ad0ba6 100644 --- a/fvm/fvm_test.go +++ b/fvm/fvm_test.go @@ -3110,7 +3110,10 @@ func TestEVM(t *testing.T) { // we have implemented a snapshot wrapper to return an error from the EVM t.Run("internal evm error handling", newVMTest(). withBootstrapProcedureOptions(fvm.WithSetupEVMEnabled(true)). - withContextOptions(fvm.WithEVMEnabled(true)). + withContextOptions( + fvm.WithChain(flow.Emulator.Chain()), + fvm.WithEVMEnabled(true), + ). run(func( t *testing.T, vm fvm.VM, @@ -3138,7 +3141,7 @@ func TestEVM(t *testing.T) { errStorage. On("Get", mockery.AnythingOfType("flow.RegisterID")). Return(func(id flow.RegisterID) (flow.RegisterValue, error) { - if id.Key == "AddressAllocator" { + if id.Key == "LatestBlock" { return nil, e.err } return snapshotTree.Get(id)