Skip to content

Commit

Permalink
eth: FIP-0055: implement final version of transitory delegated signat…
Browse files Browse the repository at this point in the history
…ure. (#10239)
  • Loading branch information
raulk authored Feb 12, 2023
1 parent 1ee9516 commit 37e1ac5
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 46 deletions.
35 changes: 8 additions & 27 deletions chain/types/ethtypes/eth_transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,41 +117,29 @@ func EthTxArgsFromUnsignedEthMessage(msg *types.Message) (EthTxArgs, error) {
default:
return EthTxArgs{}, fmt.Errorf("unsupported EAM method")
}
} else {
} else if msg.Method == builtintypes.MethodsEVM.InvokeContract {
addr, err := EthAddressFromFilecoinAddress(msg.To)
if err != nil {
return EthTxArgs{}, err
}
to = &addr

if len(msg.Params) == 0 {
if msg.Method != builtintypes.MethodSend {
return EthTxArgs{}, xerrors.Errorf("cannot invoke method %d on non-EAM actor without params", msg.Method)
}
} else {
if msg.Method != builtintypes.MethodsEVM.InvokeContract {
return EthTxArgs{},
xerrors.Errorf("invalid methodnum %d: only allowed non-send method is InvokeContract(%d)",
msg.Method,
builtintypes.MethodsEVM.InvokeContract)
}

if len(msg.Params) > 0 {
params, err = cbg.ReadByteArray(paramsReader, uint64(len(msg.Params)))
if err != nil {
return EthTxArgs{}, xerrors.Errorf("failed to read params byte array: %w", err)
}
}
} else {
return EthTxArgs{},
xerrors.Errorf("invalid methodnum %d: only allowed method is InvokeContract(%d)",
msg.Method, builtintypes.MethodsEVM.InvokeContract)
}

if paramsReader.Len() != 0 {
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{
ChainID: build.Eip155ChainId,
Nonce: int(msg.Nonce),
Expand All @@ -170,9 +158,9 @@ func (tx *EthTxArgs) ToUnsignedMessage(from address.Address) (*types.Message, er
}

var err error
method := builtintypes.MethodSend
var params []byte
var to address.Address
method := builtintypes.MethodsEVM.InvokeContract
// nil indicates the EAM, only CreateExternal is allowed
if tx.To == nil {
to = builtintypes.EthereumAddressManagerActorAddr
Expand All @@ -192,18 +180,11 @@ func (tx *EthTxArgs) ToUnsignedMessage(from address.Address) (*types.Message, er
if err != nil {
return nil, xerrors.Errorf("failed to convert To into filecoin addr: %w", err)
}
if len(tx.Input) == 0 {
// Yes, this is redundant, but let's be sure what we're doing
method = builtintypes.MethodSend
params = make([]byte, 0)
} else {
// must be InvokeContract
method = builtintypes.MethodsEVM.InvokeContract
if len(tx.Input) > 0 {
buf := new(bytes.Buffer)
if err = cbg.WriteByteArray(buf, tx.Input); err != nil {
return nil, xerrors.Errorf("failed to write input args: %w", err)
}

params = buf.Bytes()
}
}
Expand Down
35 changes: 20 additions & 15 deletions itests/eth_account_abstraction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
"github.com/filecoin-project/lotus/itests/kit"
)

// TestEthAccountAbstraction goes over the account abstraction workflow:
// TestEthAccountAbstraction goes over the placeholder creation and promotion workflow:
// - an placeholder is created when it receives a message
// - the placeholder turns into an EOA when it sends a message
func TestEthAccountAbstraction(t *testing.T) {
Expand Down Expand Up @@ -67,7 +67,8 @@ func TestEthAccountAbstraction(t *testing.T) {
msgFromPlaceholder := &types.Message{
From: placeholderAddress,
// self-send because an "eth tx payload" can't be to a filecoin address?
To: placeholderAddress,
To: placeholderAddress,
Method: builtin2.MethodsEVM.InvokeContract,
}
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
require.NoError(t, err)
Expand Down Expand Up @@ -100,9 +101,10 @@ func TestEthAccountAbstraction(t *testing.T) {
// Send another message, it should succeed without any code CID changes

msgFromPlaceholder = &types.Message{
From: placeholderAddress,
To: placeholderAddress,
Nonce: 1,
From: placeholderAddress,
To: placeholderAddress,
Method: builtin2.MethodsEVM.InvokeContract,
Nonce: 1,
}

msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
Expand Down Expand Up @@ -152,9 +154,10 @@ func TestEthAccountAbstractionFailure(t *testing.T) {

// create a placeholder actor at the target address
msgCreatePlaceholder := &types.Message{
From: client.DefaultKey.Address,
To: placeholderAddress,
Value: abi.TokenAmount(types.MustParseFIL("100")),
From: client.DefaultKey.Address,
To: placeholderAddress,
Value: abi.TokenAmount(types.MustParseFIL("100")),
Method: builtin2.MethodsEVM.InvokeContract,
}
smCreatePlaceholder, err := client.MpoolPushMessage(ctx, msgCreatePlaceholder, nil)
require.NoError(t, err)
Expand All @@ -172,9 +175,10 @@ func TestEthAccountAbstractionFailure(t *testing.T) {

// send a message from the placeholder address
msgFromPlaceholder := &types.Message{
From: placeholderAddress,
To: placeholderAddress,
Value: abi.TokenAmount(types.MustParseFIL("20")),
From: placeholderAddress,
To: placeholderAddress,
Value: abi.TokenAmount(types.MustParseFIL("20")),
Method: builtin2.MethodsEVM.InvokeContract,
}
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
require.NoError(t, err)
Expand Down Expand Up @@ -209,10 +213,11 @@ func TestEthAccountAbstractionFailure(t *testing.T) {
// Send a valid message now, it should succeed without any code CID changes

msgFromPlaceholder = &types.Message{
From: placeholderAddress,
To: placeholderAddress,
Nonce: 1,
Value: abi.NewTokenAmount(1),
From: placeholderAddress,
To: placeholderAddress,
Nonce: 1,
Value: abi.NewTokenAmount(1),
Method: builtin2.MethodsEVM.InvokeContract,
}

msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
Expand Down
57 changes: 57 additions & 0 deletions itests/fevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding/hex"
"fmt"
"testing"
"time"

"github.com/stretchr/testify/require"

Expand All @@ -17,6 +18,7 @@ import (
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/go-state-types/manifest"

"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
Expand Down Expand Up @@ -786,3 +788,58 @@ func TestFEVMDestroyCreate2(t *testing.T) {
require.Equal(t, ethFromAddr, senderSecondCall)

}

func TestFEVMBareTransferTriggersSmartContractLogic(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()

// This contract emits an event on receiving value.
filename := "contracts/ValueSender.hex"
_, contractAddr := client.EVM().DeployContractFromFilename(ctx, filename)

accctKey, accntEth, accntFil := client.EVM().NewAccount()
kit.SendFunds(ctx, t, client, accntFil, types.FromFil(10))

contractEth, err := ethtypes.EthAddressFromFilecoinAddress(contractAddr)
require.NoError(t, err)

gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{
From: &accntEth,
To: &contractEth,
Value: ethtypes.EthBigInt(big.NewInt(100)),
})
require.NoError(t, err)

maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx)
require.NoError(t, err)

tx := ethtypes.EthTxArgs{
ChainID: build.Eip155ChainId,
Value: big.NewInt(100),
Nonce: 0,
To: &contractEth,
MaxFeePerGas: types.NanoFil,
MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas),
GasLimit: int(gaslimit),
V: big.Zero(),
R: big.Zero(),
S: big.Zero(),
}

client.EVM().SignTransaction(&tx, accctKey.PrivateKey)

hash := client.EVM().SubmitTransaction(ctx, &tx)

var receipt *api.EthTxReceipt
for i := 0; i < 1000; i++ {
receipt, err = client.EthGetTransactionReceipt(ctx, hash)
require.NoError(t, err)
if receipt != nil {
break
}
time.Sleep(500 * time.Millisecond)
}

// The receive() function emits one log, that's how we know we hit it.
require.Len(t, receipt.Logs, 1)
}
3 changes: 2 additions & 1 deletion itests/kit/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ func SetupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *TestFull
return ctx, cancel, client
}

func (e *EVM) TransferValueOrFail(ctx context.Context, fromAddr address.Address, toAddr address.Address, sendAmount big.Int) {
func (e *EVM) TransferValueOrFail(ctx context.Context, fromAddr address.Address, toAddr address.Address, sendAmount big.Int) *api.MsgLookup {
sendMsg := &types.Message{
From: fromAddr,
To: toAddr,
Expand All @@ -365,6 +365,7 @@ func (e *EVM) TransferValueOrFail(ctx context.Context, fromAddr address.Address,
mLookup, err := e.StateWaitMsg(ctx, signedMsg.Cid(), 3, api.LookbackNoLimit, true)
require.NoError(e.t, err)
require.Equal(e.t, exitcode.Ok, mLookup.Receipt.ExitCode)
return mLookup
}

func NewEthFilterBuilder() *EthFilterBuilder {
Expand Down
5 changes: 2 additions & 3 deletions node/impl/full/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -765,10 +765,9 @@ func (a *EthModule) ethCallToFilecoinMessage(ctx context.Context, tx ethtypes.Et
return nil, fmt.Errorf("failed to encode tx input into a cbor byte-string")
}
params = buf.Bytes()
method = builtintypes.MethodsEVM.InvokeContract
} else {
method = builtintypes.MethodSend
}

method = builtintypes.MethodsEVM.InvokeContract
}

return &types.Message{
Expand Down

0 comments on commit 37e1ac5

Please sign in to comment.