Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eth: FIP-0055: implement final version of transitory delegated signature. #10239

Merged
merged 6 commits into from
Feb 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests shouldn't really be called "account abstraction" because we do not have account abstraction yet. We should rename them in a separate cleanup patch.

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 @@ -761,10 +761,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