diff --git a/fvm/evm/emulator/emulator_test.go b/fvm/evm/emulator/emulator_test.go index 34623024589..65c92ec28a7 100644 --- a/fvm/evm/emulator/emulator_test.go +++ b/fvm/evm/emulator/emulator_test.go @@ -19,6 +19,7 @@ import ( "github.com/onflow/flow-go/fvm/evm/debug" "github.com/onflow/flow-go/fvm/evm/emulator" "github.com/onflow/flow-go/fvm/evm/testutils" + "github.com/onflow/flow-go/fvm/evm/testutils/contracts" "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/model/flow" @@ -45,6 +46,12 @@ func RunWithNewReadOnlyBlockView(t testing.TB, em *emulator.Emulator, f func(blk f(blk) } +func requireSuccessfulExecution(t testing.TB, err error, res *types.Result) { + require.NoError(t, err) + require.NoError(t, res.VMError) + require.NoError(t, res.ValidationError) +} + func TestNativeTokenBridging(t *testing.T) { testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { @@ -688,6 +695,266 @@ func TestSelfdestruct(t *testing.T) { }) } +// test factory patterns +func TestFactoryPatterns(t *testing.T) { + testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { + + var factoryAddress types.Address + factoryContract := testutils.GetFactoryTestContract(t) + factoryDeployer := types.NewAddressFromString("factoryDeployer") + factoryDeployerBalance := big.NewInt(0).Mul(big.NewInt(1000), big.NewInt(gethParams.Ether)) + factoryBalance := big.NewInt(0).Mul(big.NewInt(100), big.NewInt(gethParams.Ether)) + + // setup the test with funded account and deploying a factory contract. + RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { + t.Run("test deploying factory", func(t *testing.T) { + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall(types.NewDepositCall(types.EmptyAddress, factoryDeployer, factoryDeployerBalance, 0)) + require.NoError(t, err) + requireSuccessfulExecution(t, err, res) + }) + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall( + types.NewDeployCall( + factoryDeployer, + factoryContract.ByteCode, + math.MaxUint64, + factoryBalance, + 0), + ) + requireSuccessfulExecution(t, err, res) + require.NotNil(t, res.DeployedContractAddress) + factoryAddress = *res.DeployedContractAddress + }) + }) + + t.Run("test self-destruct to a contract that is already deployed", + func(t *testing.T) { + // first test call deploy and try self destruct later + var deployed types.Address + RunWithNewBlockView(t, env, func(blk types.BlockView) { + salt := [32]byte{1} + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + factoryAddress, + factoryContract.MakeCallData(t, "deploy", salt), + 250_000, + big.NewInt(0), + 0, + ), + ) + requireSuccessfulExecution(t, err, res) + + // decode address, data is left padded + deployed = types.Address(gethCommon.BytesToAddress(res.ReturnedData[12:])) + }) + + // deposit money into the contract + depositedBalance := big.NewInt(200) + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall(types.NewDepositCall( + types.EmptyAddress, + deployed, + depositedBalance, 1)) + require.NoError(t, err) + requireSuccessfulExecution(t, err, res) + }) + // check balance of contract + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + bal, err := blk.BalanceOf(deployed) + require.NoError(t, err) + require.Equal(t, depositedBalance, bal) + }) + + // set storage on deployed contract + storedValue := big.NewInt(12) + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + types.Address(deployed), + testutils.MakeCallData(t, + contracts.FactoryDeployableContractABIJSON, + "set", + storedValue), + 120_000, + big.NewInt(0), + 0, + ), + ) + requireSuccessfulExecution(t, err, res) + }) + + // call self-destruct on the deployed + refundAddress := testutils.RandomAddress(t) + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + types.Address(deployed), + testutils.MakeCallData(t, + contracts.FactoryDeployableContractABIJSON, + "destroy", + refundAddress), + 120_000, + big.NewInt(0), + 0, + ), + ) + requireSuccessfulExecution(t, err, res) + }) + + // check balance of the refund address and the contract + // balance should be transferred to the refund address + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + bal, err := blk.BalanceOf(refundAddress) + require.NoError(t, err) + require.Equal(t, depositedBalance, bal) + + bal, err = blk.BalanceOf(deployed) + require.NoError(t, err) + require.True(t, types.BalancesAreEqual(big.NewInt(0), bal)) + }) + + // data should still be there + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + types.Address(deployed), + testutils.MakeCallData(t, + contracts.FactoryDeployableContractABIJSON, + "get"), + 120_000, + big.NewInt(0), + 0, + ), + ) + requireSuccessfulExecution(t, err, res) + require.Equal(t, storedValue, new(big.Int).SetBytes(res.ReturnedData)) + }) + }) + + t.Run("test deploy and destroy in a single call", + func(t *testing.T) { + var originalFactoryBalance types.Balance + var err error + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + originalFactoryBalance, err = blk.BalanceOf(factoryAddress) + require.NoError(t, err) + }) + + storedValue := big.NewInt(100) + RunWithNewBlockView(t, env, func(blk types.BlockView) { + salt := [32]byte{2} + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + factoryAddress, + factoryContract.MakeCallData(t, + "deployAndDestroy", + salt, + storedValue), + 400_000, + big.NewInt(0), + 0, + ), + ) + requireSuccessfulExecution(t, err, res) + }) + + // no balance change on the caller + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + ret, err := blk.BalanceOf(factoryAddress) + require.NoError(t, err) + require.True(t, types.BalancesAreEqual(originalFactoryBalance, ret)) + }) + }) + t.Run("test deposit first to an address and then deploy in a single call", + func(t *testing.T) { + storedValue := big.NewInt(120) + balance := big.NewInt(80) + var deployed types.Address + RunWithNewBlockView(t, env, func(blk types.BlockView) { + salt := [32]byte{3} + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + factoryAddress, + factoryContract.MakeCallData(t, "depositAndDeploy", salt, balance, storedValue), + 250_000, + big.NewInt(0), + 1, + ), + ) + requireSuccessfulExecution(t, err, res) + // decode address, data is left padded + deployed = types.Address(gethCommon.BytesToAddress(res.ReturnedData[12:])) + }) + + // no balance change on the caller + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + ret, err := blk.BalanceOf(deployed) + require.NoError(t, err) + require.True(t, types.BalancesAreEqual(balance, ret)) + }) + + // check stored data + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + types.Address(deployed), + testutils.MakeCallData(t, + contracts.FactoryDeployableContractABIJSON, + "get"), + 120_000, + big.NewInt(0), + 0, + ), + ) + requireSuccessfulExecution(t, err, res) + require.Equal(t, storedValue, new(big.Int).SetBytes(res.ReturnedData)) + }) + }) + + t.Run("test deposit, deploy, destroy in a single call", + func(t *testing.T) { + var originalFactoryBalance types.Balance + var err error + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + originalFactoryBalance, err = blk.BalanceOf(factoryAddress) + require.NoError(t, err) + }) + + RunWithNewBlockView(t, env, func(blk types.BlockView) { + salt := [32]byte{4} + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + factoryAddress, + factoryContract.MakeCallData(t, "depositDeployAndDestroy", salt, big.NewInt(100), big.NewInt(10)), + 250_000, + big.NewInt(0), + 1, + ), + ) + requireSuccessfulExecution(t, err, res) + }) + // no balance change on the caller + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + ret, err := blk.BalanceOf(factoryAddress) + require.NoError(t, err) + require.True(t, types.BalancesAreEqual(originalFactoryBalance, ret)) + }) + }) + }) + }) + }) +} + func TestTransfers(t *testing.T) { testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { @@ -1009,6 +1276,84 @@ func TestTransactionTracing(t *testing.T) { }) + t.Run("contract interaction using run transaction with proxy", func(t *testing.T) { + runWithDeployedContract(t, func(testContract *testutils.TestContract, testAccount *testutils.EOATestAccount, emu *emulator.Emulator) { + + // deploy proxy + proxyContract := testutils.GetProxyContract(t) + RunWithNewBlockView(t, emu, func(blk types.BlockView) { + call := types.NewDeployCall( + testAccount.Address(), + proxyContract.ByteCode, + math.MaxUint64, + big.NewInt(0), + testAccount.Nonce()) + + res, err := blk.DirectCall(call) + require.NoError(t, err) + require.NotNil(t, res.DeployedContractAddress) + testAccount.SetNonce(testAccount.Nonce() + 1) + proxyContract.DeployedAt = *res.DeployedContractAddress + }) + + RunWithNewBlockView(t, emu, func(blk types.BlockView) { + // set proxy contract reference the test contract + tx := testAccount.PrepareAndSignTx( + t, + proxyContract.DeployedAt.ToCommon(), + proxyContract.MakeCallData(t, "setImplementation", testContract.DeployedAt.ToCommon()), + big.NewInt(0), + 1_000_000, + big.NewInt(0), + ) + res, err := blk.RunTransaction(tx) + requireSuccessfulExecution(t, err, res) + }) + + blk, uploader, tracer := blockWithTracer(t, emu) + + var txID gethCommon.Hash + var trace json.RawMessage + + blockID := flow.Identifier{0x02} + uploaded := make(chan struct{}) + + uploader.UploadFunc = func(id string, message json.RawMessage) error { + uploaded <- struct{}{} + require.Equal(t, debug.TraceID(txID, blockID), id) + require.Equal(t, trace, message) + require.Greater(t, len(message), 0) + return nil + } + + // make call to the proxy + tx := testAccount.PrepareAndSignTx( + t, + proxyContract.DeployedAt.ToCommon(), + testContract.MakeCallData(t, "store", big.NewInt(2)), + big.NewInt(0), + 1_000_000, + big.NewInt(0), + ) + + // interact and record trace + res, err := blk.RunTransaction(tx) + requireSuccessfulExecution(t, err, res) + txID = res.TxHash + trace, err = tracer.TxTracer().GetResult() + require.NoError(t, err) + require.NotEmpty(t, trace) + tracer.WithBlockID(blockID) + + tracer.Collect(txID) + testAccount.SetNonce(testAccount.Nonce() + 1) + require.Eventuallyf(t, func() bool { + <-uploaded + return true + }, time.Second, time.Millisecond*100, "upload did not execute") + }) + + }) t.Run("contract interaction run failed transaction", func(t *testing.T) { runWithDeployedContract(t, func(testContract *testutils.TestContract, testAccount *testutils.EOATestAccount, emu *emulator.Emulator) { blk, _, tracer := blockWithTracer(t, emu) diff --git a/fvm/evm/emulator/state/base.go b/fvm/evm/emulator/state/base.go index d66483c86d5..6adc37bae8e 100644 --- a/fvm/evm/emulator/state/base.go +++ b/fvm/evm/emulator/state/base.go @@ -187,7 +187,7 @@ func (v *BaseView) GetStorageRoot(addr common.Address) (common.Hash, error) { } // account is EOA - if account.CollectionID == nil { + if len(account.CollectionID) == 0 { return gethTypes.EmptyRootHash, nil } diff --git a/fvm/evm/emulator/state/delta.go b/fvm/evm/emulator/state/delta.go index ed1750fdbf1..ef26f60dbfa 100644 --- a/fvm/evm/emulator/state/delta.go +++ b/fvm/evm/emulator/state/delta.go @@ -104,6 +104,11 @@ func (d *DeltaView) Exist(addr gethCommon.Address) (bool, error) { if found { return true, nil } + // if is address is dirty it exists + _, found = d.dirtyAddresses[addr] + if found { + return true, nil + } return d.parent.Exist(addr) } @@ -407,6 +412,20 @@ func (d *DeltaView) SetState(sk types.SlotAddress, value gethCommon.Hash) error // the way back to the base view. This means that the state root that is returned // ignores the updates to slots during the transaction. func (d *DeltaView) GetStorageRoot(addr gethCommon.Address) (gethCommon.Hash, error) { + exist, err := d.Exist(addr) + if err != nil { + return gethCommon.Hash{}, err + } + if exist { + code, err := d.GetCode(addr) + if err != nil { + return gethCommon.Hash{}, err + } + if len(code) == 0 { + return gethTypes.EmptyRootHash, nil + } + // else go back to the parent + } // go back to parents (until we reach the base view) // Note that if storage is updated in deltas but not // committed the expected behavior is to return the root in the base view. diff --git a/fvm/evm/emulator/state/stateDB_test.go b/fvm/evm/emulator/state/stateDB_test.go index 048de6f5ee1..62f3d3d7efc 100644 --- a/fvm/evm/emulator/state/stateDB_test.go +++ b/fvm/evm/emulator/state/stateDB_test.go @@ -328,6 +328,15 @@ func TestStateDB(t *testing.T) { require.NoError(t, db.Error()) require.Equal(t, gethTypes.EmptyRootHash, root) + db.AddBalance(addr1, uint256.NewInt(100), tracing.BalanceChangeTouchAccount) + require.NoError(t, db.Error()) + err = db.Commit(true) + require.NoError(t, err) + + root = db.GetStorageRoot(addr1) + require.NoError(t, db.Error()) + require.Equal(t, gethTypes.EmptyRootHash, root) + // add slots to the account key := testutils.RandomCommonHash(t) value := testutils.RandomCommonHash(t) diff --git a/fvm/evm/testutils/contract.go b/fvm/evm/testutils/contract.go index 1c084a4262d..424877c9ce2 100644 --- a/fvm/evm/testutils/contract.go +++ b/fvm/evm/testutils/contract.go @@ -52,6 +52,20 @@ func GetDummyKittyTestContract(t testing.TB) *TestContract { } } +func GetProxyContract(t testing.TB) *TestContract { + return &TestContract{ + ABI: contracts.ProxyContractABIJSON, + ByteCode: contracts.ProxyContractBytes, + } +} + +func GetFactoryTestContract(t testing.TB) *TestContract { + return &TestContract{ + ABI: contracts.FactoryContractABIJSON, + ByteCode: contracts.FactoryContractBytes, + } +} + func RunWithDeployedContract(t testing.TB, tc *TestContract, led atree.Ledger, flowEVMRootAddress flow.Address, f func(*TestContract)) { DeployContract(t, RandomAddress(t), tc, led, flowEVMRootAddress) f(tc) diff --git a/fvm/evm/testutils/contracts/contracts.go b/fvm/evm/testutils/contracts/contracts.go index adce36b692e..cb7b9704079 100644 --- a/fvm/evm/testutils/contracts/contracts.go +++ b/fvm/evm/testutils/contracts/contracts.go @@ -20,3 +20,22 @@ var DummyKittyContractBytes, _ = hex.DecodeString(dummyKittyContractBytesInHex) //go:embed dummy_kitty_abi.json var DummyKittyContractABIJSON string + +//go:embed factory_bytes.hex +var factoryContractBytesInHex string + +var FactoryContractBytes, _ = hex.DecodeString(factoryContractBytesInHex) + +//go:embed factory_abi.json +var FactoryContractABIJSON string + +//go:embed factory_deployable_abi.json +var FactoryDeployableContractABIJSON string + +//go:embed proxy_bytes.hex +var proxyContractBytesInHex string + +var ProxyContractBytes, _ = hex.DecodeString(proxyContractBytesInHex) + +//go:embed proxy_abi.json +var ProxyContractABIJSON string diff --git a/fvm/evm/testutils/contracts/factory.sol b/fvm/evm/testutils/contracts/factory.sol new file mode 100644 index 00000000000..39ffea4e7be --- /dev/null +++ b/fvm/evm/testutils/contracts/factory.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.7.0 <0.9.0; + +contract Factory { + constructor() payable {} + + function deploy(bytes32 salt) public returns (address) { + new Deployable{salt: salt}(); + return _getCreate2Address(salt, keccak256(abi.encodePacked(type(Deployable).creationCode))); + } + + function deployAndDestroy(bytes32 salt, uint256 value) public { + Deployable dep = new Deployable{salt: salt}(); + dep.set(value); + dep.destroy(address(this)); + } + + function depositAndDeploy(bytes32 salt, uint256 amount, uint256 stored) public returns (address) { + address addr = _getCreate2Address(salt, keccak256(abi.encodePacked(type(Deployable).creationCode))); + bool success; + assembly { + success := call(gas(), addr, amount, 0, 0, 0, 0) + } + require(success); + Deployable dep = new Deployable{salt: salt}(); + dep.set(stored); + return _getCreate2Address(salt, keccak256(abi.encodePacked(type(Deployable).creationCode))); + } + + function depositDeployAndDestroy(bytes32 salt, uint256 amount, uint256 stored) public { + address addr = _getCreate2Address(salt, keccak256(abi.encodePacked(type(Deployable).creationCode))); + bool success; + assembly { + success := call(gas(), addr, amount, 0, 0, 0, 0) + } + require(success); + Deployable dep = new Deployable{salt: salt}(); + dep.set(stored); + dep.destroy(address(this)); + } + + function _getCreate2Address(bytes32 salt, bytes32 codeHash) internal view returns (address) { + return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, codeHash))))); + } +} + +contract Deployable { + uint256 number; + constructor() payable {} + function set(uint256 num) public { + number = num; + } + function get() public view returns (uint256){ + return number; + } + function destroy(address etherDestination) external { + selfdestruct(payable(etherDestination)); + } + + receive() external payable { + } +} \ No newline at end of file diff --git a/fvm/evm/testutils/contracts/factory_abi.json b/fvm/evm/testutils/contracts/factory_abi.json new file mode 100644 index 00000000000..924407ab38a --- /dev/null +++ b/fvm/evm/testutils/contracts/factory_abi.json @@ -0,0 +1,96 @@ +[ + { + "inputs": [], + "stateMutability": "payable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "deploy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "deployAndDestroy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stored", + "type": "uint256" + } + ], + "name": "depositAndDeploy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stored", + "type": "uint256" + } + ], + "name": "depositDeployAndDestroy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/fvm/evm/testutils/contracts/factory_bytes.hex b/fvm/evm/testutils/contracts/factory_bytes.hex new file mode 100644 index 00000000000..ebe208d2232 --- /dev/null +++ b/fvm/evm/testutils/contracts/factory_bytes.hex @@ -0,0 +1 @@ +6080604052610af6806100115f395ff3fe608060405234801561000f575f80fd5b506004361061004a575f3560e01c80632b85ba381461004e578063624ff5c41461007e5780639e3bb99e1461009a578063cf4fe092146100b6575b5f80fd5b610068600480360381019061006391906105b4565b6100e6565b604051610075919061061e565b60405180910390f35b6100986004803603810190610093919061066a565b61016d565b005b6100b460048036038101906100af91906106ba565b6102d8565b005b6100d060048036038101906100cb919061066a565b6103d6565b6040516100dd919061061e565b60405180910390f35b5f816040516100f490610570565b8190604051809103905ff5905080158015610111573d5f803e3d5ffd5b5050610166826040518060200161012790610570565b6020820181038252601f19601f8201166040525060405160200161014b919061074a565b60405160208183030381529060405280519060200120610534565b9050919050565b5f6101c1846040518060200161018290610570565b6020820181038252601f19601f820116604052506040516020016101a6919061074a565b60405160208183030381529060405280519060200120610534565b90505f805f805f87865af19050806101d7575f80fd5b5f856040516101e590610570565b8190604051809103905ff5905080158015610202573d5f803e3d5ffd5b5090508073ffffffffffffffffffffffffffffffffffffffff166360fe47b1856040518263ffffffff1660e01b815260040161023e919061076f565b5f604051808303815f87803b158015610255575f80fd5b505af1158015610267573d5f803e3d5ffd5b505050508073ffffffffffffffffffffffffffffffffffffffff1662f55d9d306040518263ffffffff1660e01b81526004016102a3919061061e565b5f604051808303815f87803b1580156102ba575f80fd5b505af11580156102cc573d5f803e3d5ffd5b50505050505050505050565b5f826040516102e690610570565b8190604051809103905ff5905080158015610303573d5f803e3d5ffd5b5090508073ffffffffffffffffffffffffffffffffffffffff166360fe47b1836040518263ffffffff1660e01b815260040161033f919061076f565b5f604051808303815f87803b158015610356575f80fd5b505af1158015610368573d5f803e3d5ffd5b505050508073ffffffffffffffffffffffffffffffffffffffff1662f55d9d306040518263ffffffff1660e01b81526004016103a4919061061e565b5f604051808303815f87803b1580156103bb575f80fd5b505af11580156103cd573d5f803e3d5ffd5b50505050505050565b5f8061042b85604051806020016103ec90610570565b6020820181038252601f19601f82011660405250604051602001610410919061074a565b60405160208183030381529060405280519060200120610534565b90505f805f805f88865af1905080610441575f80fd5b5f8660405161044f90610570565b8190604051809103905ff590508015801561046c573d5f803e3d5ffd5b5090508073ffffffffffffffffffffffffffffffffffffffff166360fe47b1866040518263ffffffff1660e01b81526004016104a8919061076f565b5f604051808303815f87803b1580156104bf575f80fd5b505af11580156104d1573d5f803e3d5ffd5b5050505061052887604051806020016104e990610570565b6020820181038252601f19601f8201166040525060405160200161050d919061074a565b60405160208183030381529060405280519060200120610534565b93505050509392505050565b5f60ff60f81b3084846040516020016105509493929190610838565b604051602081830303815290604052805190602001205f1c905092915050565b61023b8061088683390190565b5f80fd5b5f819050919050565b61059381610581565b811461059d575f80fd5b50565b5f813590506105ae8161058a565b92915050565b5f602082840312156105c9576105c861057d565b5b5f6105d6848285016105a0565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610608826105df565b9050919050565b610618816105fe565b82525050565b5f6020820190506106315f83018461060f565b92915050565b5f819050919050565b61064981610637565b8114610653575f80fd5b50565b5f8135905061066481610640565b92915050565b5f805f606084860312156106815761068061057d565b5b5f61068e868287016105a0565b935050602061069f86828701610656565b92505060406106b086828701610656565b9150509250925092565b5f80604083850312156106d0576106cf61057d565b5b5f6106dd858286016105a0565b92505060206106ee85828601610656565b9150509250929050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f610724826106f8565b61072e8185610702565b935061073e81856020860161070c565b80840191505092915050565b5f610755828461071a565b915081905092915050565b61076981610637565b82525050565b5f6020820190506107825f830184610760565b92915050565b5f7fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b5f819050919050565b6107cd6107c882610788565b6107b3565b82525050565b5f8160601b9050919050565b5f6107e9826107d3565b9050919050565b5f6107fa826107df565b9050919050565b61081261080d826105fe565b6107f0565b82525050565b5f819050919050565b61083261082d82610581565b610818565b82525050565b5f61084382876107bc565b6001820191506108538286610801565b6014820191506108638285610821565b6020820191506108738284610821565b6020820191508190509594505050505056fe608060405261022a806100115f395ff3fe608060405260043610610036575f3560e01c8062f55d9d1461004157806360fe47b1146100695780636d4ce63c146100915761003d565b3661003d57005b5f80fd5b34801561004c575f80fd5b5061006760048036038101906100629190610143565b6100bb565b005b348015610074575f80fd5b5061008f600480360381019061008a91906101a1565b6100d4565b005b34801561009c575f80fd5b506100a56100dd565b6040516100b291906101db565b60405180910390f35b8073ffffffffffffffffffffffffffffffffffffffff16ff5b805f8190555050565b5f8054905090565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610112826100e9565b9050919050565b61012281610108565b811461012c575f80fd5b50565b5f8135905061013d81610119565b92915050565b5f60208284031215610158576101576100e5565b5b5f6101658482850161012f565b91505092915050565b5f819050919050565b6101808161016e565b811461018a575f80fd5b50565b5f8135905061019b81610177565b92915050565b5f602082840312156101b6576101b56100e5565b5b5f6101c38482850161018d565b91505092915050565b6101d58161016e565b82525050565b5f6020820190506101ee5f8301846101cc565b9291505056fea2646970667358221220cbb07f58eed2fb91caf5b496fe4f528d45cf25085898c4f2735c5dcf5bd2fc9f64736f6c634300081a0033a2646970667358221220d1e0a588152bd25c8a5276df79dde8e504c854e590f1dbb00ec9808aee242b4164736f6c634300081a0033 \ No newline at end of file diff --git a/fvm/evm/testutils/contracts/factory_deployable_abi.json b/fvm/evm/testutils/contracts/factory_deployable_abi.json new file mode 100644 index 00000000000..f33f2acc2fa --- /dev/null +++ b/fvm/evm/testutils/contracts/factory_deployable_abi.json @@ -0,0 +1,50 @@ +[ + { + "inputs": [], + "stateMutability": "payable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "etherDestination", + "type": "address" + } + ], + "name": "destroy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "get", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "num", + "type": "uint256" + } + ], + "name": "set", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/fvm/evm/testutils/contracts/proxy.sol b/fvm/evm/testutils/contracts/proxy.sol new file mode 100644 index 00000000000..5dc3f80107b --- /dev/null +++ b/fvm/evm/testutils/contracts/proxy.sol @@ -0,0 +1,25 @@ +pragma solidity ^0.8.0; + +contract Proxy { + address private _implementation; + + function setImplementation(address implementation) external { + _implementation = implementation; + } + + fallback() external payable { + address impl = _implementation; + assembly { + calldatacopy(0, 0, calldatasize()) + let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0) + returndatacopy(0, 0, returndatasize()) + switch result + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } +} \ No newline at end of file diff --git a/fvm/evm/testutils/contracts/proxy_abi.json b/fvm/evm/testutils/contracts/proxy_abi.json new file mode 100644 index 00000000000..aaba81ba703 --- /dev/null +++ b/fvm/evm/testutils/contracts/proxy_abi.json @@ -0,0 +1,19 @@ +[ + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "setImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/fvm/evm/testutils/contracts/proxy_bytes.hex b/fvm/evm/testutils/contracts/proxy_bytes.hex new file mode 100644 index 00000000000..700cc41f67d --- /dev/null +++ b/fvm/evm/testutils/contracts/proxy_bytes.hex @@ -0,0 +1 @@ +6080604052348015600e575f80fd5b5061018e8061001c5f395ff3fe608060405260043610610021575f3560e01c8063d784d4261461006557610022565b5b5f805f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050365f80375f80365f845af43d5f803e805f8114610061573d5ff35b3d5ffd5b348015610070575f80fd5b5061008b6004803603810190610086919061012d565b61008d565b005b805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6100fc826100d3565b9050919050565b61010c816100f2565b8114610116575f80fd5b50565b5f8135905061012781610103565b92915050565b5f60208284031215610142576101416100cf565b5b5f61014f84828501610119565b9150509291505056fea2646970667358221220dc83726b5fd29997fb44f2a424f0959fe05c9736efc3bf92f405182d14cdb5fb64736f6c634300081a0033 \ No newline at end of file