address review
This commit is contained in:
parent
9b7c506de0
commit
eadf8b680c
@ -2,7 +2,7 @@ package api
|
|||||||
|
|
||||||
import apitypes "github.com/filecoin-project/lotus/api/types"
|
import apitypes "github.com/filecoin-project/lotus/api/types"
|
||||||
|
|
||||||
func CrateEthRPCAliases(as apitypes.Aliaser) {
|
func CreateEthRPCAliases(as apitypes.Aliaser) {
|
||||||
// TODO: maybe use reflect to automatically register all the eth aliases
|
// TODO: maybe use reflect to automatically register all the eth aliases
|
||||||
as.AliasMethod("eth_accounts", "Filecoin.EthAccounts")
|
as.AliasMethod("eth_accounts", "Filecoin.EthAccounts")
|
||||||
as.AliasMethod("eth_blockNumber", "Filecoin.EthBlockNumber")
|
as.AliasMethod("eth_blockNumber", "Filecoin.EthBlockNumber")
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -38,7 +38,7 @@ func Handler(gwapi lapi.Gateway, api lapi.FullNode, rateLimit int64, connPerMinu
|
|||||||
rpcServer.Register("Filecoin", hnd)
|
rpcServer.Register("Filecoin", hnd)
|
||||||
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")
|
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")
|
||||||
|
|
||||||
lapi.CrateEthRPCAliases(rpcServer)
|
lapi.CreateEthRPCAliases(rpcServer)
|
||||||
|
|
||||||
m.Handle(path, rpcServer)
|
m.Handle(path, rpcServer)
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,19 @@ package itests
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"encoding/hex"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"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/exitcode"
|
"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/builtin"
|
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||||
"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"
|
||||||
@ -25,9 +28,8 @@ import (
|
|||||||
func TestEthAccountAbstraction(t *testing.T) {
|
func TestEthAccountAbstraction(t *testing.T) {
|
||||||
kit.QuietMiningLogs()
|
kit.QuietMiningLogs()
|
||||||
|
|
||||||
blockTime := 100 * 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().BeginMining(10 * time.Millisecond)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@ -38,8 +40,6 @@ func TestEthAccountAbstraction(t *testing.T) {
|
|||||||
placeholderAddress, err := client.WalletImport(ctx, &secpKey.KeyInfo)
|
placeholderAddress, err := client.WalletImport(ctx, &secpKey.KeyInfo)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
fmt.Println(placeholderAddress)
|
|
||||||
|
|
||||||
// create an placeholder actor at the target address
|
// create an placeholder actor at the target address
|
||||||
msgCreatePlaceholder := &types.Message{
|
msgCreatePlaceholder := &types.Message{
|
||||||
From: client.DefaultKey.Address,
|
From: client.DefaultKey.Address,
|
||||||
@ -50,7 +50,8 @@ func TestEthAccountAbstraction(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
mLookup, err := client.StateWaitMsg(ctx, smCreatePlaceholder.Cid(), 3, api.LookbackNoLimit, true)
|
mLookup, err := client.StateWaitMsg(ctx, smCreatePlaceholder.Cid(), 3, api.LookbackNoLimit, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, exitcode.Ok, mLookup.Receipt.ExitCode)
|
|
||||||
|
require.True(t, mLookup.Receipt.ExitCode.IsSuccess())
|
||||||
|
|
||||||
// confirm the placeholder is an placeholder
|
// confirm the placeholder is an placeholder
|
||||||
placeholderActor, err := client.StateGetActor(ctx, placeholderAddress, types.EmptyTSK)
|
placeholderActor, err := client.StateGetActor(ctx, placeholderAddress, types.EmptyTSK)
|
||||||
@ -58,6 +59,7 @@ func TestEthAccountAbstraction(t *testing.T) {
|
|||||||
|
|
||||||
require.Equal(t, uint64(0), placeholderActor.Nonce)
|
require.Equal(t, uint64(0), placeholderActor.Nonce)
|
||||||
require.True(t, builtin.IsPlaceholderActor(placeholderActor.Code))
|
require.True(t, builtin.IsPlaceholderActor(placeholderActor.Code))
|
||||||
|
require.Equal(t, msgCreatePlaceholder.Value, placeholderActor.Balance)
|
||||||
|
|
||||||
// send a message from the placeholder address
|
// send a message from the placeholder address
|
||||||
msgFromPlaceholder := &types.Message{
|
msgFromPlaceholder := &types.Message{
|
||||||
@ -82,7 +84,7 @@ func TestEthAccountAbstraction(t *testing.T) {
|
|||||||
|
|
||||||
mLookup, err = client.StateWaitMsg(ctx, smFromPlaceholderCid, 3, api.LookbackNoLimit, true)
|
mLookup, err = client.StateWaitMsg(ctx, smFromPlaceholderCid, 3, api.LookbackNoLimit, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, exitcode.Ok, mLookup.Receipt.ExitCode)
|
require.True(t, mLookup.Receipt.ExitCode.IsSuccess())
|
||||||
|
|
||||||
// confirm ugly Placeholder duckling has turned into a beautiful EthAccount swan
|
// confirm ugly Placeholder duckling has turned into a beautiful EthAccount swan
|
||||||
|
|
||||||
@ -118,7 +120,7 @@ func TestEthAccountAbstraction(t *testing.T) {
|
|||||||
|
|
||||||
mLookup, err = client.StateWaitMsg(ctx, smFromPlaceholderCid, 3, api.LookbackNoLimit, true)
|
mLookup, err = client.StateWaitMsg(ctx, smFromPlaceholderCid, 3, api.LookbackNoLimit, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, exitcode.Ok, mLookup.Receipt.ExitCode)
|
require.True(t, mLookup.Receipt.ExitCode.IsSuccess())
|
||||||
|
|
||||||
// confirm no changes in code CID
|
// confirm no changes in code CID
|
||||||
|
|
||||||
@ -129,3 +131,185 @@ func TestEthAccountAbstraction(t *testing.T) {
|
|||||||
require.False(t, builtin.IsPlaceholderActor(eoaActor.Code))
|
require.False(t, builtin.IsPlaceholderActor(eoaActor.Code))
|
||||||
require.True(t, builtin.IsEthAccountActor(eoaActor.Code))
|
require.True(t, builtin.IsEthAccountActor(eoaActor.Code))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that an placeholder turns into an EthAccout even if the message fails
|
||||||
|
func TestEthAccountAbstractionFailure(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()
|
||||||
|
|
||||||
|
secpKey, err := key.GenerateKey(types.KTDelegated)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
placeholderAddress, err := client.WalletImport(ctx, &secpKey.KeyInfo)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// create a placeholder actor at the target address
|
||||||
|
msgCreatePlaceholder := &types.Message{
|
||||||
|
From: client.DefaultKey.Address,
|
||||||
|
To: placeholderAddress,
|
||||||
|
Value: abi.TokenAmount(types.MustParseFIL("100")),
|
||||||
|
}
|
||||||
|
smCreatePlaceholder, err := client.MpoolPushMessage(ctx, msgCreatePlaceholder, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
mLookup, err := client.StateWaitMsg(ctx, smCreatePlaceholder.Cid(), 3, api.LookbackNoLimit, true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, mLookup.Receipt.ExitCode.IsSuccess())
|
||||||
|
|
||||||
|
// confirm the placeholder is an placeholder
|
||||||
|
placeholderActor, err := client.StateGetActor(ctx, placeholderAddress, types.EmptyTSK)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, uint64(0), placeholderActor.Nonce)
|
||||||
|
require.True(t, builtin.IsPlaceholderActor(placeholderActor.Code))
|
||||||
|
require.Equal(t, msgCreatePlaceholder.Value, placeholderActor.Balance)
|
||||||
|
|
||||||
|
// send a message from the placeholder address
|
||||||
|
msgFromPlaceholder := &types.Message{
|
||||||
|
From: placeholderAddress,
|
||||||
|
To: placeholderAddress,
|
||||||
|
Value: abi.TokenAmount(types.MustParseFIL("20")),
|
||||||
|
}
|
||||||
|
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
msgFromPlaceholder.Value = abi.TokenAmount(types.MustParseFIL("1000"))
|
||||||
|
txArgs, err := ethtypes.NewEthTxArgsFromMessage(msgFromPlaceholder)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
digest, err := txArgs.ToRlpUnsignedMsg()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
siggy, err := client.WalletSign(ctx, placeholderAddress, digest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
smFromPlaceholderCid, err := client.MpoolPush(ctx, &types.SignedMessage{Message: *msgFromPlaceholder, Signature: *siggy})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mLookup, err = client.StateWaitMsg(ctx, smFromPlaceholderCid, 3, api.LookbackNoLimit, true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
// message should have failed because we didn't have enough $$$
|
||||||
|
require.Equal(t, exitcode.SysErrInsufficientFunds, mLookup.Receipt.ExitCode)
|
||||||
|
|
||||||
|
// BUT, ugly Placeholder duckling should have turned into a beautiful EthAccount swan anyway
|
||||||
|
|
||||||
|
eoaActor, err := client.StateGetActor(ctx, placeholderAddress, types.EmptyTSK)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.False(t, builtin.IsPlaceholderActor(eoaActor.Code))
|
||||||
|
require.True(t, builtin.IsEthAccountActor(eoaActor.Code))
|
||||||
|
require.Equal(t, uint64(1), eoaActor.Nonce)
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
}
|
||||||
|
|
||||||
|
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
txArgs, err = ethtypes.NewEthTxArgsFromMessage(msgFromPlaceholder)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
digest, err = txArgs.ToRlpUnsignedMsg()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
siggy, err = client.WalletSign(ctx, placeholderAddress, digest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
smFromPlaceholderCid, err = client.MpoolPush(ctx, &types.SignedMessage{Message: *msgFromPlaceholder, Signature: *siggy})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mLookup, err = client.StateWaitMsg(ctx, smFromPlaceholderCid, 3, api.LookbackNoLimit, true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, mLookup.Receipt.ExitCode.IsSuccess())
|
||||||
|
|
||||||
|
// confirm no changes in code CID
|
||||||
|
|
||||||
|
eoaActor, err = client.StateGetActor(ctx, placeholderAddress, types.EmptyTSK)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, uint64(2), eoaActor.Nonce)
|
||||||
|
|
||||||
|
require.False(t, builtin.IsPlaceholderActor(eoaActor.Code))
|
||||||
|
require.True(t, builtin.IsEthAccountActor(eoaActor.Code))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that f4 addresess that aren't placeholders/ethaccounts can't be top-level senders
|
||||||
|
func TestEthAccountAbstractionFailsFromEvmActor(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()
|
||||||
|
|
||||||
|
// create a new Ethereum account
|
||||||
|
key, ethAddr, deployer := client.EVM().NewAccount()
|
||||||
|
|
||||||
|
// send some funds to the f410 address
|
||||||
|
kit.SendFunds(ctx, t, client, deployer, types.FromFil(10))
|
||||||
|
|
||||||
|
// install a contract from the placeholder
|
||||||
|
contractHex, err := os.ReadFile("./contracts/SimpleCoin.hex")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
contract, err := hex.DecodeString(string(contractHex))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{
|
||||||
|
From: ðAddr,
|
||||||
|
Data: contract,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tx := ethtypes.EthTxArgs{
|
||||||
|
ChainID: build.Eip155ChainId,
|
||||||
|
Value: big.Zero(),
|
||||||
|
Nonce: 0,
|
||||||
|
MaxFeePerGas: types.NanoFil,
|
||||||
|
MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas),
|
||||||
|
GasLimit: int(gaslimit),
|
||||||
|
Input: contract,
|
||||||
|
V: big.Zero(),
|
||||||
|
R: big.Zero(),
|
||||||
|
S: big.Zero(),
|
||||||
|
}
|
||||||
|
|
||||||
|
client.EVM().SignTransaction(&tx, key.PrivateKey)
|
||||||
|
|
||||||
|
client.EVM().SubmitTransaction(ctx, &tx)
|
||||||
|
|
||||||
|
smsg, err := tx.ToSignedMessage()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ml, err := client.StateWaitMsg(ctx, smsg.Cid(), 1, api.LookbackNoLimit, true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, ml.Receipt.ExitCode.IsSuccess())
|
||||||
|
|
||||||
|
// Get contract address, assert it's an EVM actor
|
||||||
|
contractAddr, err := client.EVM().ComputeContractAddress(ethAddr, 0).ToFilecoinAddress()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
client.AssertActorType(ctx, contractAddr, "evm")
|
||||||
|
|
||||||
|
msgFromContract := &types.Message{
|
||||||
|
From: contractAddr,
|
||||||
|
To: contractAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.GasEstimateMessageGas(ctx, msgFromContract, nil, types.EmptyTSK)
|
||||||
|
require.Error(t, err, "expected gas estimation to fail")
|
||||||
|
require.Contains(t, err.Error(), "SysErrSenderInvalid")
|
||||||
|
}
|
||||||
|
@ -79,7 +79,7 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server
|
|||||||
rpcServer.Register("Filecoin", hnd)
|
rpcServer.Register("Filecoin", hnd)
|
||||||
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")
|
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")
|
||||||
|
|
||||||
api.CrateEthRPCAliases(rpcServer)
|
api.CreateEthRPCAliases(rpcServer)
|
||||||
|
|
||||||
var handler http.Handler = rpcServer
|
var handler http.Handler = rpcServer
|
||||||
if permissioned {
|
if permissioned {
|
||||||
|
Loading…
Reference in New Issue
Block a user