eth: FIP-0055: implement final version of transitory delegated signature. (#10239)
This commit is contained in:
parent
1ee9516887
commit
37e1ac5d93
@ -117,41 +117,29 @@ func EthTxArgsFromUnsignedEthMessage(msg *types.Message) (EthTxArgs, error) {
|
|||||||
default:
|
default:
|
||||||
return EthTxArgs{}, fmt.Errorf("unsupported EAM method")
|
return EthTxArgs{}, fmt.Errorf("unsupported EAM method")
|
||||||
}
|
}
|
||||||
} else {
|
} else if msg.Method == builtintypes.MethodsEVM.InvokeContract {
|
||||||
addr, err := EthAddressFromFilecoinAddress(msg.To)
|
addr, err := EthAddressFromFilecoinAddress(msg.To)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return EthTxArgs{}, err
|
return EthTxArgs{}, err
|
||||||
}
|
}
|
||||||
to = &addr
|
to = &addr
|
||||||
|
|
||||||
if len(msg.Params) == 0 {
|
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
params, err = cbg.ReadByteArray(paramsReader, uint64(len(msg.Params)))
|
params, err = cbg.ReadByteArray(paramsReader, uint64(len(msg.Params)))
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return EthTxArgs{},
|
||||||
|
xerrors.Errorf("invalid methodnum %d: only allowed method is InvokeContract(%d)",
|
||||||
|
msg.Method, builtintypes.MethodsEVM.InvokeContract)
|
||||||
}
|
}
|
||||||
|
|
||||||
if paramsReader.Len() != 0 {
|
if paramsReader.Len() != 0 {
|
||||||
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),
|
||||||
@ -170,9 +158,9 @@ func (tx *EthTxArgs) ToUnsignedMessage(from address.Address) (*types.Message, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
method := builtintypes.MethodSend
|
|
||||||
var params []byte
|
var params []byte
|
||||||
var to address.Address
|
var to address.Address
|
||||||
|
method := builtintypes.MethodsEVM.InvokeContract
|
||||||
// nil indicates the EAM, only CreateExternal is allowed
|
// nil indicates the EAM, only CreateExternal is allowed
|
||||||
if tx.To == nil {
|
if tx.To == nil {
|
||||||
to = builtintypes.EthereumAddressManagerActorAddr
|
to = builtintypes.EthereumAddressManagerActorAddr
|
||||||
@ -192,18 +180,11 @@ func (tx *EthTxArgs) ToUnsignedMessage(from address.Address) (*types.Message, er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("failed to convert To into filecoin addr: %w", err)
|
return nil, xerrors.Errorf("failed to convert To into filecoin addr: %w", err)
|
||||||
}
|
}
|
||||||
if len(tx.Input) == 0 {
|
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
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
if err = cbg.WriteByteArray(buf, tx.Input); err != nil {
|
if err = cbg.WriteByteArray(buf, tx.Input); err != nil {
|
||||||
return nil, xerrors.Errorf("failed to write input args: %w", err)
|
return nil, xerrors.Errorf("failed to write input args: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
params = buf.Bytes()
|
params = buf.Bytes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
"github.com/filecoin-project/lotus/itests/kit"
|
"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
|
// - an placeholder is created when it receives a message
|
||||||
// - the placeholder turns into an EOA when it sends a message
|
// - the placeholder turns into an EOA when it sends a message
|
||||||
func TestEthAccountAbstraction(t *testing.T) {
|
func TestEthAccountAbstraction(t *testing.T) {
|
||||||
@ -68,6 +68,7 @@ func TestEthAccountAbstraction(t *testing.T) {
|
|||||||
From: placeholderAddress,
|
From: placeholderAddress,
|
||||||
// self-send because an "eth tx payload" can't be to a filecoin address?
|
// 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)
|
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -102,6 +103,7 @@ func TestEthAccountAbstraction(t *testing.T) {
|
|||||||
msgFromPlaceholder = &types.Message{
|
msgFromPlaceholder = &types.Message{
|
||||||
From: placeholderAddress,
|
From: placeholderAddress,
|
||||||
To: placeholderAddress,
|
To: placeholderAddress,
|
||||||
|
Method: builtin2.MethodsEVM.InvokeContract,
|
||||||
Nonce: 1,
|
Nonce: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +157,7 @@ func TestEthAccountAbstractionFailure(t *testing.T) {
|
|||||||
From: client.DefaultKey.Address,
|
From: client.DefaultKey.Address,
|
||||||
To: placeholderAddress,
|
To: placeholderAddress,
|
||||||
Value: abi.TokenAmount(types.MustParseFIL("100")),
|
Value: abi.TokenAmount(types.MustParseFIL("100")),
|
||||||
|
Method: builtin2.MethodsEVM.InvokeContract,
|
||||||
}
|
}
|
||||||
smCreatePlaceholder, err := client.MpoolPushMessage(ctx, msgCreatePlaceholder, nil)
|
smCreatePlaceholder, err := client.MpoolPushMessage(ctx, msgCreatePlaceholder, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -175,6 +178,7 @@ func TestEthAccountAbstractionFailure(t *testing.T) {
|
|||||||
From: placeholderAddress,
|
From: placeholderAddress,
|
||||||
To: placeholderAddress,
|
To: placeholderAddress,
|
||||||
Value: abi.TokenAmount(types.MustParseFIL("20")),
|
Value: abi.TokenAmount(types.MustParseFIL("20")),
|
||||||
|
Method: builtin2.MethodsEVM.InvokeContract,
|
||||||
}
|
}
|
||||||
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
|
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -213,6 +217,7 @@ func TestEthAccountAbstractionFailure(t *testing.T) {
|
|||||||
To: placeholderAddress,
|
To: placeholderAddress,
|
||||||
Nonce: 1,
|
Nonce: 1,
|
||||||
Value: abi.NewTokenAmount(1),
|
Value: abi.NewTokenAmount(1),
|
||||||
|
Method: builtin2.MethodsEVM.InvokeContract,
|
||||||
}
|
}
|
||||||
|
|
||||||
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
|
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
@ -17,6 +18,7 @@ import (
|
|||||||
"github.com/filecoin-project/go-state-types/exitcode"
|
"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/api"
|
||||||
"github.com/filecoin-project/lotus/build"
|
"github.com/filecoin-project/lotus/build"
|
||||||
"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"
|
||||||
@ -786,3 +788,58 @@ func TestFEVMDestroyCreate2(t *testing.T) {
|
|||||||
require.Equal(t, ethFromAddr, senderSecondCall)
|
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)
|
||||||
|
}
|
||||||
|
@ -354,7 +354,7 @@ func SetupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *TestFull
|
|||||||
return ctx, cancel, client
|
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{
|
sendMsg := &types.Message{
|
||||||
From: fromAddr,
|
From: fromAddr,
|
||||||
To: toAddr,
|
To: toAddr,
|
||||||
@ -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)
|
mLookup, err := e.StateWaitMsg(ctx, signedMsg.Cid(), 3, api.LookbackNoLimit, true)
|
||||||
require.NoError(e.t, err)
|
require.NoError(e.t, err)
|
||||||
require.Equal(e.t, exitcode.Ok, mLookup.Receipt.ExitCode)
|
require.Equal(e.t, exitcode.Ok, mLookup.Receipt.ExitCode)
|
||||||
|
return mLookup
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEthFilterBuilder() *EthFilterBuilder {
|
func NewEthFilterBuilder() *EthFilterBuilder {
|
||||||
|
@ -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")
|
return nil, fmt.Errorf("failed to encode tx input into a cbor byte-string")
|
||||||
}
|
}
|
||||||
params = buf.Bytes()
|
params = buf.Bytes()
|
||||||
method = builtintypes.MethodsEVM.InvokeContract
|
|
||||||
} else {
|
|
||||||
method = builtintypes.MethodSend
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
method = builtintypes.MethodsEVM.InvokeContract
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.Message{
|
return &types.Message{
|
||||||
|
Loading…
Reference in New Issue
Block a user