itests: Test FEVM recursive calls
This commit is contained in:
parent
1ea57740aa
commit
7632f9bbba
1
itests/contracts/RecCall.hex
Normal file
1
itests/contracts/RecCall.hex
Normal file
@ -0,0 +1 @@
|
|||||||
|
608060405234801561001057600080fd5b5061025b806100206000396000f3fe60806040526004361061001e5760003560e01c8063cb7786d714610023575b600080fd5b61003d60048036038101906100389190610129565b61003f565b005b600083036100d15760008111156100cc573073ffffffffffffffffffffffffffffffffffffffff1663cb7786d7838460018561007b91906101ab565b6040518463ffffffff1660e01b8152600401610099939291906101ee565b600060405180830381600087803b1580156100b357600080fd5b505af11580156100c7573d6000803e3d6000fd5b505050505b6100e9565b6100e86001846100e191906101ab565b838361003f565b5b505050565b600080fd5b6000819050919050565b610106816100f3565b811461011157600080fd5b50565b600081359050610123816100fd565b92915050565b600080600060608486031215610142576101416100ee565b5b600061015086828701610114565b935050602061016186828701610114565b925050604061017286828701610114565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006101b6826100f3565b91506101c1836100f3565b92508282039050818111156101d9576101d861017c565b5b92915050565b6101e8816100f3565b82525050565b600060608201905061020360008301866101df565b61021060208301856101df565b61021d60408301846101df565b94935050505056fea26469706673582212209a21ff59c642e2970917c07bf498271c2a6df8e3929677952c0c2d8031db15cc64736f6c63430008110033
|
16
itests/contracts/RecCall.sol
Normal file
16
itests/contracts/RecCall.sol
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.17;
|
||||||
|
|
||||||
|
contract StackRecCall {
|
||||||
|
function exec1(uint256 n, uint256 m, uint256 r) public payable {
|
||||||
|
if(n == 0) {
|
||||||
|
if(r > 0) {
|
||||||
|
StackRecCall(address(this)).exec1(m, m, r-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exec1(n-1, m, r);
|
||||||
|
}
|
||||||
|
}
|
1
itests/contracts/StackFunc.hex
Normal file
1
itests/contracts/StackFunc.hex
Normal file
@ -0,0 +1 @@
|
|||||||
|
608060405234801561001057600080fd5b50610162806100206000396000f3fe60806040526004361061001e5760003560e01c8063c38e07dd14610023575b600080fd5b61003d6004803603810190610038919061009c565b61003f565b005b600081031561005e5761005d60018261005891906100f8565b61003f565b5b50565b600080fd5b6000819050919050565b61007981610066565b811461008457600080fd5b50565b60008135905061009681610070565b92915050565b6000602082840312156100b2576100b1610061565b5b60006100c084828501610087565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061010382610066565b915061010e83610066565b9250828203905081811115610126576101256100c9565b5b9291505056fea2646970667358221220ee8f18bfd33b1e0156cfe68e9071dd32960b370c7e63ec53c62dd48e28cb5d3b64736f6c63430008110033
|
12
itests/contracts/StackFunc.sol
Normal file
12
itests/contracts/StackFunc.sol
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.17;
|
||||||
|
|
||||||
|
contract StackSelf {
|
||||||
|
function exec1(uint256 n) public payable {
|
||||||
|
if(n == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exec1(n-1);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ package itests
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -10,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
builtintypes "github.com/filecoin-project/go-state-types/builtin"
|
builtintypes "github.com/filecoin-project/go-state-types/builtin"
|
||||||
|
"github.com/filecoin-project/go-state-types/exitcode"
|
||||||
"github.com/filecoin-project/go-state-types/manifest"
|
"github.com/filecoin-project/go-state-types/manifest"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
@ -38,9 +40,9 @@ func inputDataFromFrom(ctx context.Context, t *testing.T, client *kit.TestFullNo
|
|||||||
|
|
||||||
func setupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *kit.TestFullNode) {
|
func setupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *kit.TestFullNode) {
|
||||||
kit.QuietMiningLogs()
|
kit.QuietMiningLogs()
|
||||||
blockTime := 100 * time.Millisecond
|
blockTime := 5 * time.Millisecond
|
||||||
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
|
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
|
||||||
ens.InterconnectAll().BeginMining(blockTime)
|
ens.InterconnectAll().BeginMiningMustPost(blockTime)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
return ctx, cancel, client
|
return ctx, cancel, client
|
||||||
}
|
}
|
||||||
@ -139,3 +141,78 @@ func TestEVMRpcDisable(t *testing.T) {
|
|||||||
_, err := client.EthBlockNumber(context.Background())
|
_, err := client.EthBlockNumber(context.Background())
|
||||||
require.ErrorContains(t, err, "module disabled, enable with Fevm.EnableEthRPC")
|
require.ErrorContains(t, err, "module disabled, enable with Fevm.EnableEthRPC")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestFEVMRecursiveFuncCall deploys a contract and makes a recursive function calls
|
||||||
|
func TestFEVMRecursiveFuncCall(t *testing.T) {
|
||||||
|
ctx, cancel, client := setupFEVMTest(t)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
//install contract Actor
|
||||||
|
filenameActor := "contracts/StackFunc.hex"
|
||||||
|
fromAddr, actorAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor)
|
||||||
|
|
||||||
|
testN := func(n int) func(t *testing.T) {
|
||||||
|
return func(t *testing.T) {
|
||||||
|
inputData := make([]byte, 32)
|
||||||
|
binary.BigEndian.PutUint64(inputData[24:], uint64(n))
|
||||||
|
|
||||||
|
result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, actorAddr, "exec1(uint256)", inputData)
|
||||||
|
require.Equal(t, result, []byte{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("n=0", testN(0))
|
||||||
|
t.Run("n=1", testN(1))
|
||||||
|
t.Run("n=20", testN(20))
|
||||||
|
t.Run("n=200", testN(200))
|
||||||
|
t.Run("n=293", testN(293))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFEVMRecursiveActorCall deploys a contract and makes a recursive actor calls
|
||||||
|
func TestFEVMRecursiveActorCall(t *testing.T) {
|
||||||
|
ctx, cancel, client := setupFEVMTest(t)
|
||||||
|
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))
|
||||||
|
|
||||||
|
t.Run("n=252,r=1-fails", testN(252, 1, exitcode.ExitCode(23))) // 23 means stack overflow
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
t.Run("n=252,r=10-fails", testN(252, 10, exitcode.ExitCode(23)))
|
||||||
|
|
||||||
|
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))
|
||||||
|
t.Run("n=251,r=170", testN(251, 170, exitcode.Ok))
|
||||||
|
|
||||||
|
t.Run("n=0,r=255-fails", testN(0, 255, exitcode.ExitCode(33))) // 33 means transaction reverted
|
||||||
|
t.Run("n=251,r=171-fails", testN(251, 171, exitcode.ExitCode(33)))
|
||||||
|
}
|
||||||
|
@ -21,8 +21,10 @@ import (
|
|||||||
builtintypes "github.com/filecoin-project/go-state-types/builtin"
|
builtintypes "github.com/filecoin-project/go-state-types/builtin"
|
||||||
"github.com/filecoin-project/go-state-types/builtin/v10/eam"
|
"github.com/filecoin-project/go-state-types/builtin/v10/eam"
|
||||||
"github.com/filecoin-project/go-state-types/crypto"
|
"github.com/filecoin-project/go-state-types/crypto"
|
||||||
|
"github.com/filecoin-project/go-state-types/exitcode"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
"github.com/filecoin-project/lotus/chain/actors"
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
||||||
@ -109,11 +111,12 @@ func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target
|
|||||||
params = buffer.Bytes()
|
params = buffer.Bytes()
|
||||||
|
|
||||||
msg := &types.Message{
|
msg := &types.Message{
|
||||||
To: target,
|
To: target,
|
||||||
From: sender,
|
From: sender,
|
||||||
Value: big.Zero(),
|
Value: big.Zero(),
|
||||||
Method: builtintypes.MethodsEVM.InvokeContract,
|
Method: builtintypes.MethodsEVM.InvokeContract,
|
||||||
Params: params,
|
GasLimit: build.BlockGasLimit, // note: we hardcode block gas limit due to slightly broken gas estimation - https://github.com/filecoin-project/lotus/issues/10041
|
||||||
|
Params: params,
|
||||||
}
|
}
|
||||||
|
|
||||||
e.t.Log("sending invoke message")
|
e.t.Log("sending invoke message")
|
||||||
@ -237,12 +240,18 @@ func (e *EVM) ComputeContractAddress(deployer ethtypes.EthAddress, nonce uint64)
|
|||||||
func (e *EVM) InvokeContractByFuncName(ctx context.Context, fromAddr address.Address, idAddr address.Address, funcSignature string, inputData []byte) []byte {
|
func (e *EVM) InvokeContractByFuncName(ctx context.Context, fromAddr address.Address, idAddr address.Address, funcSignature string, inputData []byte) []byte {
|
||||||
entryPoint := CalcFuncSignature(funcSignature)
|
entryPoint := CalcFuncSignature(funcSignature)
|
||||||
wait := e.InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData)
|
wait := e.InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData)
|
||||||
require.True(e.t, wait.Receipt.ExitCode.IsSuccess(), "contract execution failed")
|
require.True(e.t, wait.Receipt.ExitCode.IsSuccess(), "contract execution failed: %d", wait.Receipt.ExitCode)
|
||||||
result, err := cbg.ReadByteArray(bytes.NewBuffer(wait.Receipt.Return), uint64(len(wait.Receipt.Return)))
|
result, err := cbg.ReadByteArray(bytes.NewBuffer(wait.Receipt.Return), uint64(len(wait.Receipt.Return)))
|
||||||
require.NoError(e.t, err)
|
require.NoError(e.t, err)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *EVM) InvokeContractByFuncNameExpectExit(ctx context.Context, fromAddr address.Address, idAddr address.Address, funcSignature string, inputData []byte, exit exitcode.ExitCode) {
|
||||||
|
entryPoint := CalcFuncSignature(funcSignature)
|
||||||
|
wait := e.InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData)
|
||||||
|
require.Equal(e.t, exit, wait.Receipt.ExitCode)
|
||||||
|
}
|
||||||
|
|
||||||
// function signatures are the first 4 bytes of the hash of the function name and types
|
// function signatures are the first 4 bytes of the hash of the function name and types
|
||||||
func CalcFuncSignature(funcName string) []byte {
|
func CalcFuncSignature(funcName string) []byte {
|
||||||
hasher := sha3.NewLegacyKeccak256()
|
hasher := sha3.NewLegacyKeccak256()
|
||||||
|
Loading…
Reference in New Issue
Block a user