Merge branch 'release/v1.20.0' into iand/eth-openrpc-validate

This commit is contained in:
Ian Davis 2023-01-26 11:35:33 +00:00
commit a9cd76907e
24 changed files with 313 additions and 40 deletions

View File

@ -473,7 +473,7 @@ jobs:
- run: - run:
name: Docker push name: Docker push
command: | command: |
docker push filecoin/<<parameters.image>>:<<parameters.channel>> docker push filecoin/<<parameters.image>>:<<parameters.channel>>-<<parameters.network>>
if [[ ! -z $CIRCLE_SHA ]]; then if [[ ! -z $CIRCLE_SHA ]]; then
docker image tag filecoin/<<parameters.image>>:<<parameters.channel>>-<<parameters.network>> filecoin/<<parameters.image>>:"${CIRCLE_SHA:0:7}"-<<parameters.network>> docker image tag filecoin/<<parameters.image>>:<<parameters.channel>>-<<parameters.network>> filecoin/<<parameters.image>>:"${CIRCLE_SHA:0:7}"-<<parameters.network>>
docker push filecoin/<<parameters.image>>:"${CIRCLE_SHA:0:7}"-<<parameters.network>> docker push filecoin/<<parameters.image>>:"${CIRCLE_SHA:0:7}"-<<parameters.network>>

View File

@ -473,7 +473,7 @@ jobs:
- run: - run:
name: Docker push name: Docker push
command: | command: |
docker push filecoin/<<parameters.image>>:<<parameters.channel>> docker push filecoin/<<parameters.image>>:<<parameters.channel>>-<<parameters.network>>
if [["[[ ! -z $CIRCLE_SHA ]]"]]; then if [["[[ ! -z $CIRCLE_SHA ]]"]]; then
docker image tag filecoin/<<parameters.image>>:<<parameters.channel>>-<<parameters.network>> filecoin/<<parameters.image>>:"${CIRCLE_SHA:0:7}"-<<parameters.network>> docker image tag filecoin/<<parameters.image>>:<<parameters.channel>>-<<parameters.network>> filecoin/<<parameters.image>>:"${CIRCLE_SHA:0:7}"-<<parameters.network>>
docker push filecoin/<<parameters.image>>:"${CIRCLE_SHA:0:7}"-<<parameters.network>> docker push filecoin/<<parameters.image>>:"${CIRCLE_SHA:0:7}"-<<parameters.network>>

View File

@ -836,6 +836,9 @@ type FullNode interface {
// Unsubscribe from a websocket subscription // Unsubscribe from a websocket subscription
EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) //perm:write EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) //perm:write
// Returns the client version
Web3ClientVersion(ctx context.Context) (string, error) //perm:read
// CreateBackup creates node backup onder the specified file name. The // CreateBackup creates node backup onder the specified file name. The
// method requires that the lotus daemon is running with the // method requires that the lotus daemon is running with the
// LOTUS_BACKUP_BASE_PATH environment variable set to some path, and that // LOTUS_BACKUP_BASE_PATH environment variable set to some path, and that

View File

@ -41,4 +41,6 @@ func CreateEthRPCAliases(as apitypes.Aliaser) {
as.AliasMethod("net_version", "Filecoin.NetVersion") as.AliasMethod("net_version", "Filecoin.NetVersion")
as.AliasMethod("net_listening", "Filecoin.NetListening") as.AliasMethod("net_listening", "Filecoin.NetListening")
as.AliasMethod("web3_clientVersion", "Filecoin.Web3ClientVersion")
} }

View File

@ -4096,3 +4096,18 @@ func (mr *MockFullNodeMockRecorder) WalletVerify(arg0, arg1, arg2, arg3 interfac
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WalletVerify", reflect.TypeOf((*MockFullNode)(nil).WalletVerify), arg0, arg1, arg2, arg3) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WalletVerify", reflect.TypeOf((*MockFullNode)(nil).WalletVerify), arg0, arg1, arg2, arg3)
} }
// Web3ClientVersion mocks base method.
func (m *MockFullNode) Web3ClientVersion(arg0 context.Context) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Web3ClientVersion", arg0)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Web3ClientVersion indicates an expected call of Web3ClientVersion.
func (mr *MockFullNodeMockRecorder) Web3ClientVersion(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Web3ClientVersion", reflect.TypeOf((*MockFullNode)(nil).Web3ClientVersion), arg0)
}

View File

@ -582,6 +582,8 @@ type FullNodeStruct struct {
WalletValidateAddress func(p0 context.Context, p1 string) (address.Address, error) `perm:"read"` WalletValidateAddress func(p0 context.Context, p1 string) (address.Address, error) `perm:"read"`
WalletVerify func(p0 context.Context, p1 address.Address, p2 []byte, p3 *crypto.Signature) (bool, error) `perm:"read"` WalletVerify func(p0 context.Context, p1 address.Address, p2 []byte, p3 *crypto.Signature) (bool, error) `perm:"read"`
Web3ClientVersion func(p0 context.Context) (string, error) `perm:"read"`
} }
} }
@ -3936,6 +3938,17 @@ func (s *FullNodeStub) WalletVerify(p0 context.Context, p1 address.Address, p2 [
return false, ErrNotSupported return false, ErrNotSupported
} }
func (s *FullNodeStruct) Web3ClientVersion(p0 context.Context) (string, error) {
if s.Internal.Web3ClientVersion == nil {
return "", ErrNotSupported
}
return s.Internal.Web3ClientVersion(p0)
}
func (s *FullNodeStub) Web3ClientVersion(p0 context.Context) (string, error) {
return "", ErrNotSupported
}
func (s *GatewayStruct) ChainGetBlockMessages(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) { func (s *GatewayStruct) ChainGetBlockMessages(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) {
if s.Internal.ChainGetBlockMessages == nil { if s.Internal.ChainGetBlockMessages == nil {
return nil, ErrNotSupported return nil, ErrNotSupported

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -12,13 +12,11 @@ import (
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
gocrypto "github.com/filecoin-project/go-crypto" gocrypto "github.com/filecoin-project/go-crypto"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/big"
builtintypes "github.com/filecoin-project/go-state-types/builtin" builtintypes "github.com/filecoin-project/go-state-types/builtin"
typescrypto "github.com/filecoin-project/go-state-types/crypto" typescrypto "github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
) )
@ -63,6 +61,7 @@ func EthTxArgsFromMessage(msg *types.Message) (EthTxArgs, error) {
to *EthAddress to *EthAddress
params []byte params []byte
paramsReader = bytes.NewReader(msg.Params) paramsReader = bytes.NewReader(msg.Params)
err error
) )
if msg.Version != 0 { if msg.Version != 0 {
@ -72,11 +71,10 @@ func EthTxArgsFromMessage(msg *types.Message) (EthTxArgs, error) {
if msg.To == builtintypes.EthereumAddressManagerActorAddr { if msg.To == builtintypes.EthereumAddressManagerActorAddr {
switch msg.Method { switch msg.Method {
case builtintypes.MethodsEAM.CreateExternal: case builtintypes.MethodsEAM.CreateExternal:
var create abi.CborBytes params, err = cbg.ReadByteArray(paramsReader, uint64(len(msg.Params)))
if err := create.UnmarshalCBOR(paramsReader); err != nil { if err != nil {
return EthTxArgs{}, err return EthTxArgs{}, xerrors.Errorf("failed to read params byte array: %w", err)
} }
params = create
default: default:
return EthTxArgs{}, fmt.Errorf("unsupported EAM method") return EthTxArgs{}, fmt.Errorf("unsupported EAM method")
} }
@ -103,11 +101,6 @@ func EthTxArgsFromMessage(msg *types.Message) (EthTxArgs, error) {
if err != nil { if err != nil {
return EthTxArgs{}, xerrors.Errorf("failed to read params byte array: %w", err) return EthTxArgs{}, xerrors.Errorf("failed to read params byte array: %w", err)
} }
if len(params) == 0 {
// Otherwise, we don't get a guaranteed round-trip.
return EthTxArgs{}, xerrors.Errorf("cannot invoke contracts with empty parameters from an eth-account")
}
} }
} }
@ -115,6 +108,11 @@ func EthTxArgsFromMessage(msg *types.Message) (EthTxArgs, error) {
return EthTxArgs{}, xerrors.Errorf("extra data found in params") return EthTxArgs{}, xerrors.Errorf("extra data found in params")
} }
if len(params) == 0 && msg.Method != builtintypes.MethodSend {
// Otherwise, we don't get a guaranteed round-trip.
return EthTxArgs{}, xerrors.Errorf("msgs with empty parameters from an eth-account must be Sends (MethodNum: %d)", msg.Method)
}
return EthTxArgs{ return EthTxArgs{
ChainID: build.Eip155ChainId, ChainID: build.Eip155ChainId,
Nonce: int(msg.Nonce), Nonce: int(msg.Nonce),
@ -143,12 +141,13 @@ func (tx *EthTxArgs) ToUnsignedMessage(from address.Address) (*types.Message, er
if len(tx.Input) == 0 { if len(tx.Input) == 0 {
return nil, xerrors.New("cannot call CreateExternal without params") return nil, xerrors.New("cannot call CreateExternal without params")
} }
inputParams := abi.CborBytes(tx.Input)
params, err = actors.SerializeParams(&inputParams) buf := new(bytes.Buffer)
if err != nil { if err = cbg.WriteByteArray(buf, tx.Input); err != nil {
return nil, fmt.Errorf("failed to serialize Create params: %w", err) return nil, xerrors.Errorf("failed to serialize Create params: %w", err)
} }
params = buf.Bytes()
} else { } else {
to, err = tx.To.ToFilecoinAddress() to, err = tx.To.ToFilecoinAddress()
if err != nil { if err != nil {

View File

@ -291,7 +291,10 @@ func DumpActorState(i *ActorRegistry, act *types.Actor, b []byte) (interface{},
um := actInfo.vmActor.State() um := actInfo.vmActor.State()
if um == nil { if um == nil {
// TODO::FVM @arajasek I would like to assert that we have the empty object here if act.Code != EmptyObjectCid {
return nil, xerrors.Errorf("actor with code %s should only have empty object (%s) as its Head, instead has %s", act.Code, EmptyObjectCid, act.Head)
}
return nil, nil return nil, nil
} }
if err := um.UnmarshalCBOR(bytes.NewReader(b)); err != nil { if err := um.UnmarshalCBOR(bytes.NewReader(b)); err != nil {

View File

@ -1517,6 +1517,8 @@ func GetAsks(ctx context.Context, api lapi.FullNode) ([]QueriedAsk, error) {
} }
}(miner) }(miner)
} }
wg.Wait()
}() }()
loop: loop:
@ -1590,6 +1592,8 @@ loop:
lk.Unlock() lk.Unlock()
}(miner) }(miner)
} }
wg.Wait()
}() }()
loop2: loop2:

View File

@ -291,6 +291,8 @@
* [WalletSignMessage](#WalletSignMessage) * [WalletSignMessage](#WalletSignMessage)
* [WalletValidateAddress](#WalletValidateAddress) * [WalletValidateAddress](#WalletValidateAddress)
* [WalletVerify](#WalletVerify) * [WalletVerify](#WalletVerify)
* [Web3](#Web3)
* [Web3ClientVersion](#Web3ClientVersion)
## ##
@ -9207,3 +9209,16 @@ Inputs:
Response: `true` Response: `true`
## Web3
### Web3ClientVersion
Returns the client version
Perms: read
Inputs: `null`
Response: `"string value"`

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b5061025b806100206000396000f3fe60806040526004361061001e5760003560e01c8063cb7786d714610023575b600080fd5b61003d60048036038101906100389190610129565b61003f565b005b600083036100d15760008111156100cc573073ffffffffffffffffffffffffffffffffffffffff1663cb7786d7838460018561007b91906101ab565b6040518463ffffffff1660e01b8152600401610099939291906101ee565b600060405180830381600087803b1580156100b357600080fd5b505af11580156100c7573d6000803e3d6000fd5b505050505b6100e9565b6100e86001846100e191906101ab565b838361003f565b5b505050565b600080fd5b6000819050919050565b610106816100f3565b811461011157600080fd5b50565b600081359050610123816100fd565b92915050565b600080600060608486031215610142576101416100ee565b5b600061015086828701610114565b935050602061016186828701610114565b925050604061017286828701610114565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006101b6826100f3565b91506101c1836100f3565b92508282039050818111156101d9576101d861017c565b5b92915050565b6101e8816100f3565b82525050565b600060608201905061020360008301866101df565b61021060208301856101df565b61021d60408301846101df565b94935050505056fea26469706673582212209a21ff59c642e2970917c07bf498271c2a6df8e3929677952c0c2d8031db15cc64736f6c63430008110033

View 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);
}
}

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b50610162806100206000396000f3fe60806040526004361061001e5760003560e01c8063c38e07dd14610023575b600080fd5b61003d6004803603810190610038919061009c565b61003f565b005b600081031561005e5761005d60018261005891906100f8565b61003f565b5b50565b600080fd5b6000819050919050565b61007981610066565b811461008457600080fd5b50565b60008135905061009681610070565b92915050565b6000602082840312156100b2576100b1610061565b5b60006100c084828501610087565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061010382610066565b915061010e83610066565b9250828203905081811115610126576101256100c9565b5b9291505056fea2646970667358221220ee8f18bfd33b1e0156cfe68e9071dd32960b370c7e63ec53c62dd48e28cb5d3b64736f6c63430008110033

View 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);
}
}

View File

@ -11,6 +11,8 @@ import (
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/big"
builtin2 "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/exitcode" "github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
@ -313,3 +315,51 @@ func TestEthAccountAbstractionFailsFromEvmActor(t *testing.T) {
require.Error(t, err, "expected gas estimation to fail") require.Error(t, err, "expected gas estimation to fail")
require.Contains(t, err.Error(), "SysErrSenderInvalid") require.Contains(t, err.Error(), "SysErrSenderInvalid")
} }
func TestEthAccountManagerPermissions(t *testing.T) {
kit.QuietMiningLogs()
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(10 * time.Millisecond)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
// setup f1/f3/f4 accounts
wsp, err := client.WalletNew(ctx, types.KTSecp256k1)
require.NoError(t, err)
wbl, err := client.WalletNew(ctx, types.KTBLS)
require.NoError(t, err)
wdl, err := client.WalletNew(ctx, types.KTDelegated)
require.NoError(t, err)
def := client.DefaultKey.Address
// send some funds
client.ExpectSend(ctx, def, wsp, types.FromFil(10), "")
client.ExpectSend(ctx, def, wbl, types.FromFil(10), "")
client.ExpectSend(ctx, def, wdl, types.FromFil(10), "")
require.NoError(t, err)
// make sure that EAM only allows CreateExternal to be called by accounts
client.ExpectSend(ctx, wsp, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create, &eam.CreateParams{Nonce: 0}))
client.ExpectSend(ctx, wbl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create, &eam.CreateParams{Nonce: 0}))
client.ExpectSend(ctx, wdl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create, &eam.CreateParams{Nonce: 0}))
client.ExpectSend(ctx, wsp, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create2, &eam.Create2Params{}))
client.ExpectSend(ctx, wbl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create2, &eam.Create2Params{}))
client.ExpectSend(ctx, wdl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create2, &eam.Create2Params{}))
contractHex, err := os.ReadFile("contracts/SimpleCoin.hex")
require.NoError(t, err)
contract, err := hex.DecodeString(string(contractHex))
require.NoError(t, err)
contractParams := abi.CborBytes(contract)
client.ExpectSend(ctx, wsp, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "", client.MakeSendCall(builtin2.MethodsEAM.CreateExternal, &contractParams))
client.ExpectSend(ctx, wbl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "", client.MakeSendCall(builtin2.MethodsEAM.CreateExternal, &contractParams))
client.ExpectSend(ctx, wdl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "", client.MakeSendCall(builtin2.MethodsEAM.CreateExternal, &contractParams))
}

View File

@ -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, ex exitcode.ExitCode) func(t *testing.T) {
return func(t *testing.T) {
inputData := make([]byte, 32)
binary.BigEndian.PutUint64(inputData[24:], uint64(n))
client.EVM().InvokeContractByFuncNameExpectExit(ctx, fromAddr, actorAddr, "exec1(uint256)", inputData, ex)
}
}
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))
t.Run("n=508", testN(508, exitcode.ExitCode(23))) // 23 means stack overflow
}
// 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)))
}

View File

@ -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"
@ -113,6 +115,7 @@ func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target
From: sender, From: sender,
Value: big.Zero(), Value: big.Zero(),
Method: builtintypes.MethodsEVM.InvokeContract, Method: builtintypes.MethodsEVM.InvokeContract,
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, Params: params,
} }
@ -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()

View File

@ -1,6 +1,7 @@
package kit package kit
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"testing" "testing"
@ -10,9 +11,11 @@ import (
"github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peer"
"github.com/multiformats/go-multiaddr" "github.com/multiformats/go-multiaddr"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
cbg "github.com/whyrusleeping/cbor-gen"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"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/api/v1api" "github.com/filecoin-project/lotus/api/v1api"
@ -124,6 +127,50 @@ func (f *TestFullNode) AssignPrivKey(pkey *Libp2p) {
f.Pkey = pkey f.Pkey = pkey
} }
type SendCall struct {
Method abi.MethodNum
Params []byte
}
func (f *TestFullNode) MakeSendCall(m abi.MethodNum, params cbg.CBORMarshaler) SendCall {
var b bytes.Buffer
err := params.MarshalCBOR(&b)
require.NoError(f.t, err)
return SendCall{
Method: m,
Params: b.Bytes(),
}
}
func (f *TestFullNode) ExpectSend(ctx context.Context, from, to address.Address, value types.BigInt, errContains string, sc ...SendCall) *types.SignedMessage {
msg := &types.Message{From: from, To: to, Value: value}
if len(sc) == 1 {
msg.Method = sc[0].Method
msg.Params = sc[0].Params
}
_, err := f.GasEstimateMessageGas(ctx, msg, nil, types.EmptyTSK)
if errContains != "" {
require.ErrorContains(f.t, err, errContains)
return nil
}
require.NoError(f.t, err)
if errContains == "" {
m, err := f.MpoolPushMessage(ctx, msg, nil)
require.NoError(f.t, err)
r, err := f.StateWaitMsg(ctx, m.Cid(), 1, api.LookbackNoLimit, true)
require.NoError(f.t, err)
require.Equal(f.t, exitcode.Ok, r.Receipt.ExitCode)
return m
}
return nil
}
// ChainPredicate encapsulates a chain condition. // ChainPredicate encapsulates a chain condition.
type ChainPredicate func(set *types.TipSet) bool type ChainPredicate func(set *types.TipSet) bool

View File

@ -118,4 +118,8 @@ func (e *EthModuleDummy) EthSendRawTransaction(ctx context.Context, rawTx ethtyp
return ethtypes.EthHash{}, ErrModuleDisabled return ethtypes.EthHash{}, ErrModuleDisabled
} }
func (e *EthModuleDummy) Web3ClientVersion(ctx context.Context) (string, error) {
return "", ErrModuleDisabled
}
var _ EthModuleAPI = &EthModuleDummy{} var _ EthModuleAPI = &EthModuleDummy{}

View File

@ -64,6 +64,7 @@ type EthModuleAPI interface {
EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error)
EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error)
EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error)
Web3ClientVersion(ctx context.Context) (string, error)
} }
type EthEventAPI interface { type EthEventAPI interface {
@ -500,18 +501,8 @@ func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAdd
return nil, fmt.Errorf("failed to construct system sender address: %w", err) return nil, fmt.Errorf("failed to construct system sender address: %w", err)
} }
// TODO super duper hack (raulk). The EVM runtime actor uses the U256 parameter type in
// GetStorageAtParams, which serializes as a hex-encoded string. It should serialize
// as bytes. We didn't get to fix in time for Iron, so for now we just pass
// through the hex-encoded value passed through the Eth JSON-RPC API, by remarshalling it.
// We don't fix this at origin (builtin-actors) because we are not updating the bundle
// for Iron.
tmp, err := position.MarshalJSON()
if err != nil {
panic(err)
}
params, err := actors.SerializeParams(&evm.GetStorageAtParams{ params, err := actors.SerializeParams(&evm.GetStorageAtParams{
StorageKey: tmp[1 : len(tmp)-1], // TODO strip the JSON-encoding quotes -- yuck StorageKey: position,
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to serialize parameters: %w", err) return nil, fmt.Errorf("failed to serialize parameters: %w", err)
@ -714,6 +705,10 @@ func (a *EthModule) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.Et
return ethtypes.EthHashFromTxBytes(rawTx), nil return ethtypes.EthHashFromTxBytes(rawTx), nil
} }
func (a *EthModule) Web3ClientVersion(ctx context.Context) (string, error) {
return build.UserVersion(), nil
}
func (a *EthModule) ethCallToFilecoinMessage(ctx context.Context, tx ethtypes.EthCall) (*types.Message, error) { func (a *EthModule) ethCallToFilecoinMessage(ctx context.Context, tx ethtypes.EthCall) (*types.Message, error) {
var from address.Address var from address.Address
if tx.From == nil || *tx.From == (ethtypes.EthAddress{}) { if tx.From == nil || *tx.From == (ethtypes.EthAddress{}) {
@ -827,8 +822,9 @@ func (a *EthModule) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (et
func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) { func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) {
msg, err := a.ethCallToFilecoinMessage(ctx, tx) msg, err := a.ethCallToFilecoinMessage(ctx, tx)
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to convert ethcall to filecoin message: %w", err)
} }
ts, err := a.parseBlkParam(ctx, blkParam) ts, err := a.parseBlkParam(ctx, blkParam)
if err != nil { if err != nil {
return nil, xerrors.Errorf("cannot parse block param: %s", blkParam) return nil, xerrors.Errorf("cannot parse block param: %s", blkParam)
@ -836,11 +832,17 @@ func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam s
invokeResult, err := a.applyMessage(ctx, msg, ts.Key()) invokeResult, err := a.applyMessage(ctx, msg, ts.Key())
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to apply message: %w", err)
} }
if len(invokeResult.MsgRct.Return) > 0 {
if msg.To == builtintypes.EthereumAddressManagerActorAddr {
// As far as I can tell, the Eth API always returns empty on contract deployment
return ethtypes.EthBytes{}, nil
} else if len(invokeResult.MsgRct.Return) > 0 {
return cbg.ReadByteArray(bytes.NewReader(invokeResult.MsgRct.Return), uint64(len(invokeResult.MsgRct.Return))) return cbg.ReadByteArray(bytes.NewReader(invokeResult.MsgRct.Return), uint64(len(invokeResult.MsgRct.Return)))
} }
return ethtypes.EthBytes{}, nil return ethtypes.EthBytes{}, nil
} }