2023-01-13 19:11:13 +00:00
package itests
import (
2023-02-01 05:13:13 +00:00
"bytes"
2023-01-13 19:11:13 +00:00
"context"
2023-01-17 16:38:40 +00:00
"encoding/binary"
2023-01-13 19:11:13 +00:00
"encoding/hex"
2023-02-01 05:13:13 +00:00
"fmt"
2023-01-13 19:11:13 +00:00
"testing"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-address"
2023-02-01 05:13:13 +00:00
"github.com/filecoin-project/go-state-types/big"
2023-01-13 19:11:13 +00:00
builtintypes "github.com/filecoin-project/go-state-types/builtin"
2023-01-17 16:38:40 +00:00
"github.com/filecoin-project/go-state-types/exitcode"
2023-01-13 19:11:13 +00:00
"github.com/filecoin-project/go-state-types/manifest"
"github.com/filecoin-project/lotus/chain/types"
2023-01-12 04:29:56 +00:00
"github.com/filecoin-project/lotus/chain/types/ethtypes"
2023-01-13 19:11:13 +00:00
"github.com/filecoin-project/lotus/itests/kit"
)
2023-01-17 16:53:28 +00:00
// convert a simple byte array into input data which is a left padded 32 byte array
2023-01-12 04:29:56 +00:00
func inputDataFromArray ( input [ ] byte ) [ ] byte {
inputData := make ( [ ] byte , 32 )
copy ( inputData [ 32 - len ( input ) : ] , input [ : ] )
return inputData
}
2023-01-13 19:11:13 +00:00
2023-01-17 16:53:28 +00:00
// 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 {
2023-01-12 04:29:56 +00:00
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
}
2023-02-01 05:13:13 +00:00
func decodeOutputToUint64 ( output [ ] byte ) ( uint64 , error ) {
var result uint64
buf := bytes . NewReader ( output [ len ( output ) - 8 : ] )
err := binary . Read ( buf , binary . BigEndian , & result )
return result , err
}
func buildInputFromuint64 ( number uint64 ) [ ] byte {
// Convert the number to a binary uint64 array
binaryNumber := make ( [ ] byte , 8 )
binary . BigEndian . PutUint64 ( binaryNumber , number )
return inputDataFromArray ( binaryNumber )
}
// recursive delegate calls that fail due to gas limits are currently getting to 229 iterations
// before running out of gas
func recursiveDelegatecallFail ( ctx context . Context , t * testing . T , client * kit . TestFullNode , filename string , count uint64 ) {
expectedIterationsBeforeFailing := int ( 229 )
fromAddr , idAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filename )
t . Log ( "recursion count - " , count )
inputData := buildInputFromuint64 ( count )
_ , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , idAddr , "recursiveCall(uint256)" , inputData )
require . NoError ( t , err )
result , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , idAddr , "totalCalls()" , [ ] byte { } )
require . NoError ( t , err )
resultUint , err := decodeOutputToUint64 ( result )
require . NoError ( t , err )
require . NotEqual ( t , int ( resultUint ) , int ( count ) )
require . Equal ( t , expectedIterationsBeforeFailing , int ( resultUint ) )
}
func recursiveDelegatecallSuccess ( ctx context . Context , t * testing . T , client * kit . TestFullNode , filename string , count uint64 ) {
t . Log ( "Count - " , count )
fromAddr , idAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filename )
inputData := buildInputFromuint64 ( count )
_ , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , idAddr , "recursiveCall(uint256)" , inputData )
require . NoError ( t , err )
result , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , idAddr , "totalCalls()" , [ ] byte { } )
require . NoError ( t , err )
resultUint , err := decodeOutputToUint64 ( result )
require . NoError ( t , err )
require . Equal ( t , int ( count ) , int ( resultUint ) )
}
// TestFEVMRecursive does a basic fevm contract installation and invocation
func TestFEVMRecursive ( t * testing . T ) {
callCounts := [ ] uint64 { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 100 , 230 , 330 }
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
filename := "contracts/Recursive.hex"
fromAddr , idAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filename )
// Successful calls
for _ , callCount := range callCounts {
callCount := callCount // linter unhappy unless callCount is local to loop
t . Run ( fmt . Sprintf ( "TestFEVMRecursive%d" , callCount ) , func ( t * testing . T ) {
_ , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , idAddr , "recursiveCall(uint256)" , buildInputFromuint64 ( callCount ) )
require . NoError ( t , err )
} )
}
}
func TestFEVMRecursiveFail ( t * testing . T ) {
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
filename := "contracts/Recursive.hex"
fromAddr , idAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filename )
// Unsuccessful calls
failCallCounts := [ ] uint64 { 340 , 400 , 600 , 850 , 1000 }
for _ , failCallCount := range failCallCounts {
failCallCount := failCallCount // linter unhappy unless callCount is local to loop
t . Run ( fmt . Sprintf ( "TestFEVMRecursiveFail%d" , failCallCount ) , func ( t * testing . T ) {
_ , wait , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , idAddr , "recursiveCall(uint256)" , buildInputFromuint64 ( failCallCount ) )
require . Error ( t , err )
require . Equal ( t , exitcode . ExitCode ( 23 ) , wait . Receipt . ExitCode )
} )
}
}
func TestFEVMRecursive1 ( t * testing . T ) {
callCount := 1
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
filename := "contracts/Recursive.hex"
fromAddr , idAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filename )
_ , ret , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , idAddr , "recursive1()" , [ ] byte { } )
require . NoError ( t , err )
events := client . EVM ( ) . LoadEvents ( ctx , * ret . Receipt . EventsRoot )
require . Equal ( t , callCount , len ( events ) )
}
func TestFEVMRecursive2 ( t * testing . T ) {
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
filename := "contracts/Recursive.hex"
fromAddr , idAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filename )
_ , ret , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , idAddr , "recursive2()" , [ ] byte { } )
require . NoError ( t , err )
events := client . EVM ( ) . LoadEvents ( ctx , * ret . Receipt . EventsRoot )
require . Equal ( t , 2 , len ( events ) )
}
// TestFEVMBasic does a basic fevm contract installation and invocation
// recursive delegate call succeeds up to 238 times
func TestFEVMRecursiveDelegatecall ( t * testing . T ) {
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
filename := "contracts/RecursiveDelegeatecall.hex"
//success with 238 or fewer calls
for i := uint64 ( 1 ) ; i <= 238 ; i += 30 {
recursiveDelegatecallSuccess ( ctx , t , client , filename , i )
}
recursiveDelegatecallSuccess ( ctx , t , client , filename , uint64 ( 238 ) )
for i := uint64 ( 239 ) ; i <= 800 ; i += 40 {
recursiveDelegatecallFail ( ctx , t , client , filename , i )
}
2023-01-12 04:29:56 +00:00
}
2023-01-13 19:11:13 +00:00
2023-01-12 04:29:56 +00:00
// TestFEVMBasic does a basic fevm contract installation and invocation
func TestFEVMBasic ( t * testing . T ) {
2023-01-13 19:11:13 +00:00
2023-02-01 05:13:13 +00:00
ctx , cancel , client := kit . SetupFEVMTest ( t )
2023-01-12 04:29:56 +00:00
defer cancel ( )
2023-01-13 19:11:13 +00:00
2023-01-12 04:29:56 +00:00
filename := "contracts/SimpleCoin.hex"
// install contract
fromAddr , idAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filename )
2023-01-13 19:11:13 +00:00
// invoke the contract with owner
{
2023-01-17 16:53:28 +00:00
inputData := inputDataFromFrom ( ctx , t , client , fromAddr )
2023-02-01 05:13:13 +00:00
result , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , idAddr , "getBalance(address)" , inputData )
require . NoError ( t , err )
2023-01-13 19:11:13 +00:00
expectedResult , err := hex . DecodeString ( "0000000000000000000000000000000000000000000000000000000000002710" )
require . NoError ( t , err )
require . Equal ( t , result , expectedResult )
}
// invoke the contract with non owner
{
2023-01-17 16:53:28 +00:00
inputData := inputDataFromFrom ( ctx , t , client , fromAddr )
2023-02-01 05:13:13 +00:00
inputData [ 31 ] ++ // change the pub address to one that has 0 balance by modifying the last byte of the address
result , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , idAddr , "getBalance(address)" , inputData )
require . NoError ( t , err )
2023-01-13 19:11:13 +00:00
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 ) {
2023-02-01 05:13:13 +00:00
ctx , cancel , client := kit . SetupFEVMTest ( t )
2023-01-13 19:11:13 +00:00
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 )
}
2023-01-12 04:29:56 +00:00
// TestFEVMDelegateCall deploys two contracts and makes a delegate call transaction
func TestFEVMDelegateCall ( t * testing . T ) {
2023-02-01 05:13:13 +00:00
ctx , cancel , client := kit . SetupFEVMTest ( t )
2023-01-12 04:29:56 +00:00
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
2023-01-17 16:53:28 +00:00
inputDataContract := inputDataFromFrom ( ctx , t , client , actorAddr )
2023-01-12 04:29:56 +00:00
inputDataValue := inputDataFromArray ( [ ] byte { 7 } )
inputData := append ( inputDataContract , inputDataValue ... )
//verify that the returned value of the call to setvars is 7
2023-02-01 05:13:13 +00:00
result , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , storageAddr , "setVars(address,uint256)" , inputData )
require . NoError ( t , err )
2023-01-12 04:29:56 +00:00
expectedResult , err := hex . DecodeString ( "0000000000000000000000000000000000000000000000000000000000000007" )
require . NoError ( t , err )
require . Equal ( t , result , expectedResult )
2023-02-01 05:13:13 +00:00
//test the value is 7 a second way by calling the getter
result , _ , err = client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , storageAddr , "getCounter()" , [ ] byte { } )
require . NoError ( t , err )
2023-01-12 04:29:56 +00:00
require . Equal ( t , result , expectedResult )
//test the value is 0 via calling the getter on the Actor contract
2023-02-01 05:13:13 +00:00
result , _ , err = client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , actorAddr , "getCounter()" , [ ] byte { } )
require . NoError ( t , err )
2023-01-12 04:29:56 +00:00
expectedResultActor , err := hex . DecodeString ( "0000000000000000000000000000000000000000000000000000000000000000" )
require . NoError ( t , err )
require . Equal ( t , result , expectedResultActor )
}
2023-01-19 18:00:52 +00:00
2023-02-01 05:13:13 +00:00
// TestFEVMDelegateCallRevert makes a delegatecall action and then calls revert.
// the state should not have changed because of the revert
func TestFEVMDelegateCallRevert ( t * testing . T ) {
ctx , cancel , client := kit . 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
_ , wait , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , storageAddr , "setVarsRevert(address,uint256)" , inputData )
require . Error ( t , err )
require . Equal ( t , exitcode . ExitCode ( 33 ) , wait . Receipt . ExitCode )
//test the value is 0 via calling the getter and was not set to 7
expectedResult , err := hex . DecodeString ( "0000000000000000000000000000000000000000000000000000000000000000" )
require . NoError ( t , err )
result , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , storageAddr , "getCounter()" , [ ] byte { } )
require . NoError ( t , err )
require . Equal ( t , result , expectedResult )
//test the value is 0 via calling the getter on the Actor contract
result , _ , err = client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , actorAddr , "getCounter()" , [ ] byte { } )
require . NoError ( t , err )
require . Equal ( t , result , expectedResult )
}
// TestFEVMSimpleRevert makes a call that is a simple revert
func TestFEVMSimpleRevert ( t * testing . T ) {
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
//install contract Actor
filenameStorage := "contracts/DelegatecallStorage.hex"
fromAddr , contractAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filenameStorage )
//call revert
_ , wait , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , contractAddr , "revert()" , [ ] byte { } )
require . Equal ( t , wait . Receipt . ExitCode , exitcode . ExitCode ( 33 ) )
require . Error ( t , err )
}
// TestFEVMSelfDestruct creates a contract that just has a self destruct feature and calls it
func TestFEVMSelfDestruct ( t * testing . T ) {
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
//install contract Actor
filenameStorage := "contracts/SelfDestruct.hex"
fromAddr , contractAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filenameStorage )
//call destroy
_ , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , contractAddr , "destroy()" , [ ] byte { } )
require . NoError ( t , err )
//call destroy a second time and also no error
_ , _ , err = client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , contractAddr , "destroy()" , [ ] byte { } )
require . NoError ( t , err )
}
// TestFEVMTestApp deploys a fairly complex app contract and confirms it works as expected
func TestFEVMTestApp ( t * testing . T ) {
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
//install contract Actor
filenameStorage := "contracts/TestApp.hex"
fromAddr , contractAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filenameStorage )
inputData , err := hex . DecodeString ( "0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000066162636465660000000000000000000000000000000000000000000000000000" ) // sending string "abcdef" and int 7 - constructed using remix
require . NoError ( t , err )
_ , _ , err = client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , contractAddr , "new_Test(string,uint256)" , inputData )
require . NoError ( t , err )
inputData , err = hex . DecodeString ( "0000000000000000000000000000000000000000000000000000000000000000" )
require . NoError ( t , err )
_ , _ , err = client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , contractAddr , "get_Test_N(uint256)" , inputData )
require . NoError ( t , err )
}
// TestFEVMTestApp creates a contract that just has a self destruct feature and calls it
func TestFEVMTestConstructor ( t * testing . T ) {
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
//install contract Actor
filenameStorage := "contracts/Constructor.hex"
fromAddr , contractAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filenameStorage )
//input = uint256{7}. set value and confirm tx success
inputData , err := hex . DecodeString ( "0000000000000000000000000000000000000000000000000000000000000007" )
require . NoError ( t , err )
_ , _ , err = client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , contractAddr , "new_Test(uint256)" , inputData )
require . NoError ( t , err )
}
// TestFEVMAutoSelfDestruct creates a contract that just has a self destruct feature and calls it
func TestFEVMAutoSelfDestruct ( t * testing . T ) {
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
//install contract Actor
filenameStorage := "contracts/AutoSelfDestruct.hex"
fromAddr , contractAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filenameStorage )
//call destroy
_ , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , contractAddr , "destroy()" , [ ] byte { } )
require . NoError ( t , err )
}
// TestFEVMTestApp creates a contract that just has a self destruct feature and calls it
func TestFEVMTestSendToContract ( t * testing . T ) {
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
bal , err := client . WalletBalance ( ctx , client . DefaultKey . Address )
require . NoError ( t , err )
//install contract TestApp
filenameStorage := "contracts/SelfDestruct.hex"
fromAddr , contractAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filenameStorage )
//transfer half balance to contract
sendAmount := big . Div ( bal , big . NewInt ( 2 ) )
client . EVM ( ) . TransferValueOrFail ( ctx , fromAddr , contractAddr , sendAmount )
//call self destruct which should return balance
_ , _ , err = client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , contractAddr , "destroy()" , [ ] byte { } )
require . NoError ( t , err )
finalBalanceMinimum := types . FromFil ( uint64 ( 99_999_999 ) ) // 100 million FIL - 1 FIL for gas upper bounds
finalBal , err := client . WalletBalance ( ctx , client . DefaultKey . Address )
require . NoError ( t , err )
require . Equal ( t , true , finalBal . GreaterThan ( finalBalanceMinimum ) )
}
// creates a contract that would fail when tx are sent to it
// on eth but on fevm it succeeds
// example failing on testnet https://goerli.etherscan.io/address/0x2ff1525e060169dbf97b9461758c8f701f107cd2
func TestFEVMTestNotPayable ( t * testing . T ) {
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
fromAddr := client . DefaultKey . Address
t . Log ( "from - " , fromAddr )
//create contract A
filenameStorage := "contracts/NotPayable.hex"
fromAddr , contractAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filenameStorage )
sendAmount := big . NewInt ( 10_000_000 )
client . EVM ( ) . TransferValueOrFail ( ctx , fromAddr , contractAddr , sendAmount )
}
// tx to non function succeeds
func TestFEVMSendCall ( t * testing . T ) {
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
//install contract
filenameActor := "contracts/GasSendTest.hex"
fromAddr , contractAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filenameActor )
_ , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , contractAddr , "x()" , [ ] byte { } )
require . NoError ( t , err )
}
// creates a contract that would fail when tx are sent to it
// on eth but on fevm it succeeds
// example on goerli of tx failing https://goerli.etherscan.io/address/0xec037bdc9a79420985a53a49fdae3ccf8989909b
func TestFEVMSendGasLimit ( t * testing . T ) {
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
//install contract
filenameActor := "contracts/GasLimitSend.hex"
fromAddr , contractAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filenameActor )
//send $ to contract
//transfer 1 attoFIL to contract
sendAmount := big . MustFromString ( "1" )
client . EVM ( ) . TransferValueOrFail ( ctx , fromAddr , contractAddr , sendAmount )
_ , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , contractAddr , "getDataLength()" , [ ] byte { } )
require . NoError ( t , err )
}
// TestFEVMDelegateCall deploys the two contracts in TestFEVMDelegateCall but instead of A calling B, A calls A which should cause A to cause A in an infinite loop and should give a reasonable error
// XXX should not be fatal errors
func TestFEVMDelegateCallRecursiveFail ( t * testing . T ) {
//TODO change the gas limit of this invocation and confirm that the number of errors is different
//also TODO should we not have fatal error show up here?
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
//install contract Actor
filenameActor := "contracts/DelegatecallStorage.hex"
fromAddr , actorAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filenameActor )
//any data will do for this test that fails
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
_ , wait , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , actorAddr , "setVarsSelf(address,uint256)" , inputData )
require . Error ( t , err )
require . Equal ( t , exitcode . SysErrorIllegalArgument , wait . Receipt . ExitCode )
//assert no fatal errors but still there are errors::
errorAny := "fatal error"
require . NotContains ( t , err . Error ( ) , errorAny )
}
// XXX Currently fails as self destruct has a bug
// TestFEVMTestSendValueThroughContracts creates A and B contract and exchanges value
// and self destructs and accounts for value sent
func TestFEVMTestSendValueThroughContractsAndDestroy ( t * testing . T ) {
ctx , cancel , client := kit . SetupFEVMTest ( t )
defer cancel ( )
fromAddr := client . DefaultKey . Address
t . Log ( "from - " , fromAddr )
//create contract A
filenameStorage := "contracts/ValueSender.hex"
fromAddr , contractAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filenameStorage )
//create contract B
ret , _ , err := client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , contractAddr , "createB()" , [ ] byte { } )
require . NoError ( t , err )
ethAddr , err := ethtypes . CastEthAddress ( ret [ 12 : ] )
require . NoError ( t , err )
contractBAddress , err := ethAddr . ToFilecoinAddress ( )
require . NoError ( t , err )
t . Log ( "contractBAddress - " , contractBAddress )
//self destruct contract B
_ , _ , err = client . EVM ( ) . InvokeContractByFuncName ( ctx , fromAddr , contractBAddress , "selfDestruct()" , [ ] byte { } )
require . NoError ( t , err )
}
2023-01-19 18:00:52 +00:00
func TestEVMRpcDisable ( t * testing . T ) {
client , _ , _ := kit . EnsembleMinimal ( t , kit . MockProofs ( ) , kit . ThroughRPC ( ) , kit . DisableEthRPC ( ) )
_ , err := client . EthBlockNumber ( context . Background ( ) )
2023-01-19 20:35:19 +00:00
require . ErrorContains ( t , err , "module disabled, enable with Fevm.EnableEthRPC" )
2023-01-19 18:00:52 +00:00
}
2023-01-17 16:38:40 +00:00
// TestFEVMRecursiveFuncCall deploys a contract and makes a recursive function calls
func TestFEVMRecursiveFuncCall ( t * testing . T ) {
2023-02-01 05:13:13 +00:00
ctx , cancel , client := kit . SetupFEVMTest ( t )
2023-01-17 16:38:40 +00:00
defer cancel ( )
//install contract Actor
filenameActor := "contracts/StackFunc.hex"
fromAddr , actorAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filenameActor )
2023-01-17 16:38:40 +00:00
testN := func ( n int , ex exitcode . ExitCode ) func ( t * testing . T ) {
2023-01-17 16:38:40 +00:00
return func ( t * testing . T ) {
inputData := make ( [ ] byte , 32 )
binary . BigEndian . PutUint64 ( inputData [ 24 : ] , uint64 ( n ) )
2023-01-17 16:38:40 +00:00
client . EVM ( ) . InvokeContractByFuncNameExpectExit ( ctx , fromAddr , actorAddr , "exec1(uint256)" , inputData , ex )
2023-01-17 16:38:40 +00:00
}
}
2023-01-17 16:38:40 +00:00
t . Run ( "n=0" , testN ( 0 , exitcode . Ok ) )
t . Run ( "n=1" , testN ( 1 , exitcode . Ok ) )
t . Run ( "n=20" , testN ( 20 , exitcode . Ok ) )
t . Run ( "n=200" , testN ( 200 , exitcode . Ok ) )
t . Run ( "n=507" , testN ( 507 , exitcode . Ok ) )
2023-02-03 03:10:30 +00:00
t . Run ( "n=508" , testN ( 508 , exitcode . ExitCode ( 37 ) ) ) // 37 means stack overflow
2023-01-17 16:38:40 +00:00
}
// TestFEVMRecursiveActorCall deploys a contract and makes a recursive actor calls
func TestFEVMRecursiveActorCall ( t * testing . T ) {
2023-02-01 05:13:13 +00:00
ctx , cancel , client := kit . SetupFEVMTest ( t )
2023-01-17 16:38:40 +00:00
defer cancel ( )
//install contract Actor
filenameActor := "contracts/RecCall.hex"
fromAddr , actorAddr := client . EVM ( ) . DeployContractFromFilename ( ctx , filenameActor )
testN := func ( n , r int , ex exitcode . ExitCode ) func ( t * testing . T ) {
return func ( t * testing . T ) {
inputData := make ( [ ] byte , 32 * 3 )
binary . BigEndian . PutUint64 ( inputData [ 24 : ] , uint64 ( n ) )
binary . BigEndian . PutUint64 ( inputData [ 32 + 24 : ] , uint64 ( n ) )
binary . BigEndian . PutUint64 ( inputData [ 32 + 32 + 24 : ] , uint64 ( r ) )
client . EVM ( ) . InvokeContractByFuncNameExpectExit ( ctx , fromAddr , actorAddr , "exec1(uint256,uint256,uint256)" , inputData , ex )
}
}
t . Run ( "n=0,r=1" , testN ( 0 , 1 , exitcode . Ok ) )
t . Run ( "n=1,r=1" , testN ( 1 , 1 , exitcode . Ok ) )
t . Run ( "n=20,r=1" , testN ( 20 , 1 , exitcode . Ok ) )
t . Run ( "n=200,r=1" , testN ( 200 , 1 , exitcode . Ok ) )
t . Run ( "n=251,r=1" , testN ( 251 , 1 , exitcode . Ok ) )
2023-02-03 03:10:30 +00:00
t . Run ( "n=252,r=1-fails" , testN ( 252 , 1 , exitcode . ExitCode ( 37 ) ) ) // 37 means stack overflow
2023-01-17 16:38:40 +00:00
t . Run ( "n=0,r=10" , testN ( 0 , 10 , exitcode . Ok ) )
t . Run ( "n=1,r=10" , testN ( 1 , 10 , exitcode . Ok ) )
t . Run ( "n=20,r=10" , testN ( 20 , 10 , exitcode . Ok ) )
t . Run ( "n=200,r=10" , testN ( 200 , 10 , exitcode . Ok ) )
t . Run ( "n=251,r=10" , testN ( 251 , 10 , exitcode . Ok ) )
2023-02-03 03:10:30 +00:00
t . Run ( "n=252,r=10-fails" , testN ( 252 , 10 , exitcode . ExitCode ( 37 ) ) )
2023-01-17 16:38:40 +00:00
t . Run ( "n=0,r=32" , testN ( 0 , 32 , exitcode . Ok ) )
t . Run ( "n=1,r=32" , testN ( 1 , 32 , exitcode . Ok ) )
t . Run ( "n=20,r=32" , testN ( 20 , 32 , exitcode . Ok ) )
t . Run ( "n=200,r=32" , testN ( 200 , 32 , exitcode . Ok ) )
t . Run ( "n=251,r=32" , testN ( 251 , 32 , exitcode . Ok ) )
t . Run ( "n=0,r=254" , testN ( 0 , 254 , exitcode . Ok ) )
2023-02-03 03:10:30 +00:00
t . Run ( "n=251,r=166" , testN ( 251 , 166 , exitcode . Ok ) )
2023-01-17 16:38:40 +00:00
2023-02-03 03:10:30 +00:00
t . Run ( "n=0,r=256-fails" , testN ( 0 , 256 , exitcode . ExitCode ( 33 ) ) ) // 33 means transaction reverted
t . Run ( "n=251,r=167-fails" , testN ( 251 , 167 , exitcode . ExitCode ( 33 ) ) )
2023-01-17 16:38:40 +00:00
}