package itests import ( "context" "encoding/hex" "testing" "time" "github.com/stretchr/testify/require" "github.com/filecoin-project/go-address" builtintypes "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/manifest" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/itests/kit" ) // convert a simple byte array into input data which is a left padded 32 byte array func inputDataFromArray(input []byte) []byte { inputData := make([]byte, 32) copy(inputData[32-len(input):], input[:]) return inputData } // convert a "from" address into input data which is a left padded 32 byte array func inputDataFromFrom(ctx context.Context, t *testing.T, client *kit.TestFullNode, from address.Address) []byte { fromId, err := client.StateLookupID(ctx, from, types.EmptyTSK) require.NoError(t, err) senderEthAddr, err := ethtypes.EthAddressFromFilecoinAddress(fromId) require.NoError(t, err) inputData := make([]byte, 32) copy(inputData[32-len(senderEthAddr):], senderEthAddr[:]) return inputData } func setupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *kit.TestFullNode) { kit.QuietMiningLogs() blockTime := 100 * time.Millisecond client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) ens.InterconnectAll().BeginMining(blockTime) ctx, cancel := context.WithTimeout(context.Background(), time.Minute) return ctx, cancel, client } // TestFEVMBasic does a basic fevm contract installation and invocation func TestFEVMBasic(t *testing.T) { ctx, cancel, client := setupFEVMTest(t) defer cancel() filename := "contracts/SimpleCoin.hex" // install contract fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, filename) // invoke the contract with owner { inputData := inputDataFromFrom(ctx, t, client, fromAddr) result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "getBalance(address)", inputData) expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000002710") require.NoError(t, err) require.Equal(t, result, expectedResult) } // invoke the contract with non owner { inputData := inputDataFromFrom(ctx, t, client, fromAddr) inputData[31]++ // change the pub address to one that has 0 balance by incrementing the last byte of the address result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "getBalance(address)", inputData) expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") require.NoError(t, err) require.Equal(t, result, expectedResult) } } // TestFEVMETH0 tests that the ETH0 actor is in genesis func TestFEVMETH0(t *testing.T) { ctx, cancel, client := setupFEVMTest(t) defer cancel() eth0id, err := address.NewIDAddress(1001) require.NoError(t, err) client.AssertActorType(ctx, eth0id, manifest.EthAccountKey) act, err := client.StateGetActor(ctx, eth0id, types.EmptyTSK) require.NoError(t, err) eth0Addr, err := address.NewDelegatedAddress(builtintypes.EthereumAddressManagerActorID, make([]byte, 20)) require.NoError(t, err) require.Equal(t, *act.Address, eth0Addr) } // TestFEVMDelegateCall deploys two contracts and makes a delegate call transaction func TestFEVMDelegateCall(t *testing.T) { ctx, cancel, client := setupFEVMTest(t) defer cancel() //install contract Actor filenameActor := "contracts/DelegatecallActor.hex" fromAddr, actorAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor) //install contract Storage filenameStorage := "contracts/DelegatecallStorage.hex" fromAddrStorage, storageAddr := client.EVM().DeployContractFromFilename(ctx, filenameStorage) require.Equal(t, fromAddr, fromAddrStorage) //call Contract Storage which makes a delegatecall to contract Actor //this contract call sets the "counter" variable to 7, from default value 0 inputDataContract := inputDataFromFrom(ctx, t, client, actorAddr) inputDataValue := inputDataFromArray([]byte{7}) inputData := append(inputDataContract, inputDataValue...) //verify that the returned value of the call to setvars is 7 result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, storageAddr, "setVars(address,uint256)", inputData) expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000007") require.NoError(t, err) require.Equal(t, result, expectedResult) //test the value is 7 via calling the getter result = client.EVM().InvokeContractByFuncName(ctx, fromAddr, storageAddr, "getCounter()", []byte{}) require.Equal(t, result, expectedResult) //test the value is 0 via calling the getter on the Actor contract result = client.EVM().InvokeContractByFuncName(ctx, fromAddr, actorAddr, "getCounter()", []byte{}) expectedResultActor, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") require.NoError(t, err) require.Equal(t, result, expectedResultActor) } func TestEVMRpcDisable(t *testing.T) { client, _, _ := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.DisableEthRPC()) _, err := client.EthBlockNumber(context.Background()) require.ErrorContains(t, err, "module disabled, enable with Fevm.EnableEthRPC") }