feat: eth: encode eth tx input as solidity ABI (#11402)
When translating "native" messages to Ethereum transactions, correctly handle parameters: 1. If the message looks like a valid "create external", treat it as a contract creation. 2. If it looks like a valid EVM invocation, decode it as such. 3. Otherwise, ABI-encode the parameters to make them look like a "handle_filecoin_method" call. This will help chain explorers recognize these messages. Part of #11355
This commit is contained in:
parent
9b4df6a4d0
commit
d5fd4cdcc0
@ -13,6 +13,9 @@
|
|||||||
- feat: Added instructions on how to setup Prometheus/Grafana for monitoring a local Lotus node [filecoin-project/lotus#11276](https://github.com/filecoin-project/lotus/pull/11276)
|
- feat: Added instructions on how to setup Prometheus/Grafana for monitoring a local Lotus node [filecoin-project/lotus#11276](https://github.com/filecoin-project/lotus/pull/11276)
|
||||||
- fix: Exclude reverted events in `eth_getLogs` results [filecoin-project/lotus#11318](https://github.com/filecoin-project/lotus/pull/11318)
|
- fix: Exclude reverted events in `eth_getLogs` results [filecoin-project/lotus#11318](https://github.com/filecoin-project/lotus/pull/11318)
|
||||||
- fix: The Ethereum API will now use the correct state-tree when resolving "native" addresses into masked ID addresses. Additionally, pending messages from native account types won't be visible in the Ethereum API because there is no "correct" state-tree to pick in this case. However, pending _Ethereum_ transactions and native messages that have landed on-chain will still be visible through the Ethereum API.
|
- fix: The Ethereum API will now use the correct state-tree when resolving "native" addresses into masked ID addresses. Additionally, pending messages from native account types won't be visible in the Ethereum API because there is no "correct" state-tree to pick in this case. However, pending _Ethereum_ transactions and native messages that have landed on-chain will still be visible through the Ethereum API.
|
||||||
|
- feat: Unambiguously translate native messages to Ethereum transactions by:
|
||||||
|
- Detecting native messages that "look" like Ethereum transactions (creating smart contracts, invoking a smart contract, etc.), and decoding them as such.
|
||||||
|
- Otherwise, ABI-encoding the inputs as if they were calls to a `handle_filecoin_method` Solidity method.
|
||||||
|
|
||||||
## New features
|
## New features
|
||||||
- feat: Add move-partition command ([filecoin-project/lotus#11290](https://github.com/filecoin-project/lotus/pull/11290))
|
- feat: Add move-partition command ([filecoin-project/lotus#11290](https://github.com/filecoin-project/lotus/pull/11290))
|
||||||
|
@ -187,6 +187,18 @@ func TestTransactionHashLookupBlsFilecoinMessage(t *testing.T) {
|
|||||||
toEth, err := client.FilecoinAddressToEthAddress(ctx, toId)
|
toEth, err := client.FilecoinAddressToEthAddress(ctx, toId)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, &toEth, chainTx.To)
|
require.Equal(t, &toEth, chainTx.To)
|
||||||
|
|
||||||
|
const expectedHex = "868e10c4" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000060" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
|
||||||
|
// verify that the params are correctly encoded.
|
||||||
|
expected, err := hex.DecodeString(expectedHex)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, ethtypes.EthBytes(expected), chainTx.Input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTransactionHashLookupSecpFilecoinMessage tests to see if lotus can find a Secp Filecoin Message using the transaction hash
|
// TestTransactionHashLookupSecpFilecoinMessage tests to see if lotus can find a Secp Filecoin Message using the transaction hash
|
||||||
@ -266,6 +278,18 @@ func TestTransactionHashLookupSecpFilecoinMessage(t *testing.T) {
|
|||||||
toEth, err := client.FilecoinAddressToEthAddress(ctx, toId)
|
toEth, err := client.FilecoinAddressToEthAddress(ctx, toId)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, &toEth, chainTx.To)
|
require.Equal(t, &toEth, chainTx.To)
|
||||||
|
|
||||||
|
const expectedHex = "868e10c4" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000060" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
|
||||||
|
// verify that the params are correctly encoded.
|
||||||
|
expected, err := hex.DecodeString(expectedHex)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, ethtypes.EthBytes(expected), chainTx.Input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTransactionHashLookupSecpFilecoinMessage tests to see if lotus can find a Secp Filecoin Message using the transaction hash
|
// TestTransactionHashLookupSecpFilecoinMessage tests to see if lotus can find a Secp Filecoin Message using the transaction hash
|
||||||
|
@ -9,11 +9,13 @@ import (
|
|||||||
|
|
||||||
"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/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/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/actors"
|
||||||
"github.com/filecoin-project/lotus/chain/store"
|
"github.com/filecoin-project/lotus/chain/store"
|
||||||
"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"
|
||||||
@ -78,7 +80,7 @@ func TestValueTransferValidSignature(t *testing.T) {
|
|||||||
client.EVM().SignTransaction(&tx, key.PrivateKey)
|
client.EVM().SignTransaction(&tx, key.PrivateKey)
|
||||||
hash := client.EVM().SubmitTransaction(ctx, &tx)
|
hash := client.EVM().SubmitTransaction(ctx, &tx)
|
||||||
|
|
||||||
receipt, err := waitForEthTxReceipt(ctx, client, hash)
|
receipt, err := client.EVM().WaitTransaction(ctx, hash)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, receipt)
|
require.NotNil(t, receipt)
|
||||||
require.EqualValues(t, ethAddr, receipt.From)
|
require.EqualValues(t, ethAddr, receipt.From)
|
||||||
@ -166,7 +168,7 @@ func TestContractDeploymentValidSignature(t *testing.T) {
|
|||||||
client.EVM().SignTransaction(tx, key.PrivateKey)
|
client.EVM().SignTransaction(tx, key.PrivateKey)
|
||||||
hash := client.EVM().SubmitTransaction(ctx, tx)
|
hash := client.EVM().SubmitTransaction(ctx, tx)
|
||||||
|
|
||||||
receipt, err := waitForEthTxReceipt(ctx, client, hash)
|
receipt, err := client.EVM().WaitTransaction(ctx, hash)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, receipt)
|
require.NotNil(t, receipt)
|
||||||
|
|
||||||
@ -213,7 +215,7 @@ func TestContractInvocation(t *testing.T) {
|
|||||||
client.EVM().SignTransaction(tx, key.PrivateKey)
|
client.EVM().SignTransaction(tx, key.PrivateKey)
|
||||||
hash := client.EVM().SubmitTransaction(ctx, tx)
|
hash := client.EVM().SubmitTransaction(ctx, tx)
|
||||||
|
|
||||||
receipt, err := waitForEthTxReceipt(ctx, client, hash)
|
receipt, err := client.EVM().WaitTransaction(ctx, hash)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, receipt)
|
require.NotNil(t, receipt)
|
||||||
require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status)
|
require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status)
|
||||||
@ -267,7 +269,7 @@ func TestContractInvocation(t *testing.T) {
|
|||||||
client.EVM().SignTransaction(&invokeTx, key.PrivateKey)
|
client.EVM().SignTransaction(&invokeTx, key.PrivateKey)
|
||||||
hash = client.EVM().SubmitTransaction(ctx, &invokeTx)
|
hash = client.EVM().SubmitTransaction(ctx, &invokeTx)
|
||||||
|
|
||||||
receipt, err = waitForEthTxReceipt(ctx, client, hash)
|
receipt, err = client.EVM().WaitTransaction(ctx, hash)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, receipt)
|
require.NotNil(t, receipt)
|
||||||
|
|
||||||
@ -376,16 +378,108 @@ func deployContractTx(ctx context.Context, client *kit.TestFullNode, ethAddr eth
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForEthTxReceipt(ctx context.Context, client *kit.TestFullNode, hash ethtypes.EthHash) (*api.EthTxReceipt, error) {
|
func TestEthTxFromNativeAccount(t *testing.T) {
|
||||||
var receipt *api.EthTxReceipt
|
blockTime := 10 * time.Millisecond
|
||||||
var err error
|
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
|
||||||
for i := 0; i < 10000000000; i++ {
|
|
||||||
receipt, err = client.EthGetTransactionReceipt(ctx, hash)
|
ens.InterconnectAll().BeginMining(blockTime)
|
||||||
if err != nil || receipt == nil {
|
|
||||||
time.Sleep(500 * time.Millisecond)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
continue
|
defer cancel()
|
||||||
|
|
||||||
|
msg := &types.Message{
|
||||||
|
From: client.DefaultKey.Address,
|
||||||
|
To: client.DefaultKey.Address,
|
||||||
|
Value: abi.TokenAmount(types.MustParseFIL("100")),
|
||||||
|
Method: builtin2.MethodsEVM.InvokeContract,
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
// Send a message with no input.
|
||||||
return receipt, err
|
|
||||||
|
sMsg, err := client.MpoolPushMessage(ctx, msg, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
client.WaitMsg(ctx, sMsg.Cid())
|
||||||
|
|
||||||
|
hash, err := client.EthGetTransactionHashByCid(ctx, sMsg.Cid())
|
||||||
|
require.NoError(t, err)
|
||||||
|
tx, err := client.EthGetTransactionByHash(ctx, hash)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Expect empty input params given that we "invoked" the contract (well, invoked ourselves).
|
||||||
|
require.Equal(t, ethtypes.EthBytes{}, tx.Input)
|
||||||
|
|
||||||
|
// Send a message with some input.
|
||||||
|
|
||||||
|
input := abi.CborBytes([]byte{0x1, 0x2, 0x3, 0x4})
|
||||||
|
msg.Params, err = actors.SerializeParams(&input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
sMsg, err = client.MpoolPushMessage(ctx, msg, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
client.WaitMsg(ctx, sMsg.Cid())
|
||||||
|
hash, err = client.EthGetTransactionHashByCid(ctx, sMsg.Cid())
|
||||||
|
require.NoError(t, err)
|
||||||
|
tx, err = client.EthGetTransactionByHash(ctx, hash)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Expect the decoded input.
|
||||||
|
require.EqualValues(t, input, tx.Input)
|
||||||
|
|
||||||
|
// Invoke the contract, but with incorrectly encoded input. We expect this to be abi-encoded
|
||||||
|
// as if it were any other method call.
|
||||||
|
|
||||||
|
msg.Params = input
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
sMsg, err = client.MpoolPushMessage(ctx, msg, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
client.WaitMsg(ctx, sMsg.Cid())
|
||||||
|
hash, err = client.EthGetTransactionHashByCid(ctx, sMsg.Cid())
|
||||||
|
require.NoError(t, err)
|
||||||
|
tx, err = client.EthGetTransactionByHash(ctx, hash)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
const expectedHex1 = "868e10c4" + // "handle filecoin method" function selector
|
||||||
|
// InvokeEVM method number
|
||||||
|
"00000000000000000000000000000000000000000000000000000000e525aa15" +
|
||||||
|
// CBOR multicodec (0x51)
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000051" +
|
||||||
|
// Offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000060" +
|
||||||
|
// Number of bytes in the input (4)
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000004" +
|
||||||
|
// Input: 1, 2, 3, 4
|
||||||
|
"0102030400000000000000000000000000000000000000000000000000000000"
|
||||||
|
|
||||||
|
input, err = hex.DecodeString(expectedHex1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, input, tx.Input)
|
||||||
|
|
||||||
|
// Invoke a random method with the same input. We expect the same result as above, but with
|
||||||
|
// a different method number.
|
||||||
|
|
||||||
|
msg.Method++
|
||||||
|
|
||||||
|
sMsg, err = client.MpoolPushMessage(ctx, msg, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
client.WaitMsg(ctx, sMsg.Cid())
|
||||||
|
hash, err = client.EthGetTransactionHashByCid(ctx, sMsg.Cid())
|
||||||
|
require.NoError(t, err)
|
||||||
|
tx, err = client.EthGetTransactionByHash(ctx, hash)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
const expectedHex2 = "868e10c4" + // "handle filecoin method" function selector
|
||||||
|
// InvokeEVM+1
|
||||||
|
"00000000000000000000000000000000000000000000000000000000e525aa16" +
|
||||||
|
// CBOR multicodec (0x51)
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000051" +
|
||||||
|
// Offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000060" +
|
||||||
|
// Number of bytes in the input (4)
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000004" +
|
||||||
|
// Input: 1, 2, 3, 4
|
||||||
|
"0102030400000000000000000000000000000000000000000000000000000000"
|
||||||
|
input, err = hex.DecodeString(expectedHex2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, input, tx.Input)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package itests
|
package itests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"os"
|
"os"
|
||||||
@ -14,8 +13,6 @@ 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"
|
||||||
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/exitcode"
|
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
"github.com/filecoin-project/lotus/chain/actors"
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
@ -43,7 +40,7 @@ func effectiveEthAddressForCreate(t *testing.T, sender address.Address) ethtypes
|
|||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
func createAndDeploy(ctx context.Context, t *testing.T, client *kit.TestFullNode, fromAddr address.Address, contract []byte) *api.MsgLookup {
|
func createAndDeploy(ctx context.Context, t *testing.T, client *kit.TestFullNode, fromAddr address.Address, contract []byte) *api.EthTxReceipt {
|
||||||
// Create and deploy evm actor
|
// Create and deploy evm actor
|
||||||
|
|
||||||
method := builtintypes.MethodsEAM.CreateExternal
|
method := builtintypes.MethodsEAM.CreateExternal
|
||||||
@ -61,21 +58,13 @@ func createAndDeploy(ctx context.Context, t *testing.T, client *kit.TestFullNode
|
|||||||
smsg, err := client.MpoolPushMessage(ctx, createMsg, nil)
|
smsg, err := client.MpoolPushMessage(ctx, createMsg, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
wait, err := client.StateWaitMsg(ctx, smsg.Cid(), 0, 0, false)
|
txHash, err := client.EthGetTransactionHashByCid(ctx, smsg.Cid())
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, exitcode.Ok, wait.Receipt.ExitCode)
|
|
||||||
return wait
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEthAddressTX(ctx context.Context, t *testing.T, client *kit.TestFullNode, wait *api.MsgLookup, ethAddr ethtypes.EthAddress) ethtypes.EthAddress {
|
|
||||||
// Check if eth address returned from CreateExternal is the same as eth address predicted at the start
|
|
||||||
var createExternalReturn eam.CreateExternalReturn
|
|
||||||
err := createExternalReturn.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return))
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
createdEthAddr, err := ethtypes.CastEthAddress(createExternalReturn.EthAddress[:])
|
receipt, err := client.EVM().WaitTransaction(ctx, *txHash)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return createdEthAddr
|
require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status)
|
||||||
|
return receipt
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddressCreationBeforeDeploy(t *testing.T) {
|
func TestAddressCreationBeforeDeploy(t *testing.T) {
|
||||||
@ -112,11 +101,11 @@ func TestAddressCreationBeforeDeploy(t *testing.T) {
|
|||||||
require.True(t, builtin.IsPlaceholderActor(actor.Code))
|
require.True(t, builtin.IsPlaceholderActor(actor.Code))
|
||||||
|
|
||||||
// Create and deploy evm actor
|
// Create and deploy evm actor
|
||||||
wait := createAndDeploy(ctx, t, client, fromAddr, contract)
|
receipt := createAndDeploy(ctx, t, client, fromAddr, contract)
|
||||||
|
|
||||||
// Check if eth address returned from CreateExternal is the same as eth address predicted at the start
|
// Check if eth address returned from CreateExternal is the same as eth address predicted at
|
||||||
createdEthAddr := getEthAddressTX(ctx, t, client, wait, ethAddr)
|
// the start
|
||||||
require.Equal(t, ethAddr, createdEthAddr)
|
require.Equal(t, ðAddr, receipt.ContractAddress)
|
||||||
|
|
||||||
// Check if newly deployed actor still has funds
|
// Check if newly deployed actor still has funds
|
||||||
actorPostCreate, err := client.StateGetActor(ctx, contractFilAddr, types.EmptyTSK)
|
actorPostCreate, err := client.StateGetActor(ctx, contractFilAddr, types.EmptyTSK)
|
||||||
@ -158,11 +147,11 @@ func TestDeployAddressMultipleTimes(t *testing.T) {
|
|||||||
require.True(t, builtin.IsPlaceholderActor(actor.Code))
|
require.True(t, builtin.IsPlaceholderActor(actor.Code))
|
||||||
|
|
||||||
// Create and deploy evm actor
|
// Create and deploy evm actor
|
||||||
wait := createAndDeploy(ctx, t, client, fromAddr, contract)
|
receipt := createAndDeploy(ctx, t, client, fromAddr, contract)
|
||||||
|
|
||||||
// Check if eth address returned from CreateExternal is the same as eth address predicted at the start
|
// Check if eth address returned from CreateExternal is the same as eth address predicted at
|
||||||
createdEthAddr := getEthAddressTX(ctx, t, client, wait, ethAddr)
|
// the start
|
||||||
require.Equal(t, ethAddr, createdEthAddr)
|
require.Equal(t, ðAddr, receipt.ContractAddress)
|
||||||
|
|
||||||
// Check if newly deployed actor still has funds
|
// Check if newly deployed actor still has funds
|
||||||
actorPostCreate, err := client.StateGetActor(ctx, contractFilAddr, types.EmptyTSK)
|
actorPostCreate, err := client.StateGetActor(ctx, contractFilAddr, types.EmptyTSK)
|
||||||
@ -171,10 +160,9 @@ func TestDeployAddressMultipleTimes(t *testing.T) {
|
|||||||
require.True(t, builtin.IsEvmActor(actorPostCreate.Code))
|
require.True(t, builtin.IsEvmActor(actorPostCreate.Code))
|
||||||
|
|
||||||
// Create and deploy evm actor
|
// Create and deploy evm actor
|
||||||
wait = createAndDeploy(ctx, t, client, fromAddr, contract)
|
receipt = createAndDeploy(ctx, t, client, fromAddr, contract)
|
||||||
|
|
||||||
// Check that this time eth address returned from CreateExternal is not the same as eth address predicted at the start
|
// Check that this time eth address returned from CreateExternal is not the same as eth address predicted at the start
|
||||||
createdEthAddr = getEthAddressTX(ctx, t, client, wait, ethAddr)
|
require.NotEqual(t, ðAddr, receipt.ContractAddress)
|
||||||
require.NotEqual(t, ethAddr, createdEthAddr)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
cbg "github.com/whyrusleeping/cbor-gen"
|
cbg "github.com/whyrusleeping/cbor-gen"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
amt4 "github.com/filecoin-project/go-amt-ipld/v4"
|
amt4 "github.com/filecoin-project/go-amt-ipld/v4"
|
||||||
@ -291,6 +292,17 @@ func (e *EVM) InvokeContractByFuncNameExpectExit(ctx context.Context, fromAddr a
|
|||||||
require.Equal(e.t, exit, wait.Receipt.ExitCode)
|
require.Equal(e.t, exit, wait.Receipt.ExitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *EVM) WaitTransaction(ctx context.Context, hash ethtypes.EthHash) (*api.EthTxReceipt, error) {
|
||||||
|
if mcid, err := e.EthGetMessageCidByTransactionHash(ctx, &hash); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if mcid == nil {
|
||||||
|
return nil, xerrors.Errorf("couldn't find message CID for txn hash: %s", hash)
|
||||||
|
} else {
|
||||||
|
e.WaitMsg(ctx, *mcid)
|
||||||
|
return e.EthGetTransactionReceipt(ctx, hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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()
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/multiformats/go-multicodec"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
@ -465,19 +466,23 @@ func newEthTxFromSignedMessage(smsg *types.SignedMessage, st *state.StateTree) (
|
|||||||
tx = ethTxFromNativeMessage(smsg.VMMessage(), st)
|
tx = ethTxFromNativeMessage(smsg.VMMessage(), st)
|
||||||
tx.Hash, err = ethtypes.EthHashFromCid(smsg.Cid())
|
tx.Hash, err = ethtypes.EthHashFromCid(smsg.Cid())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tx, err
|
return ethtypes.EthTx{}, err
|
||||||
}
|
}
|
||||||
} else { // BLS Filecoin message
|
} else { // BLS Filecoin message
|
||||||
tx = ethTxFromNativeMessage(smsg.VMMessage(), st)
|
tx = ethTxFromNativeMessage(smsg.VMMessage(), st)
|
||||||
tx.Hash, err = ethtypes.EthHashFromCid(smsg.Message.Cid())
|
tx.Hash, err = ethtypes.EthHashFromCid(smsg.Message.Cid())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tx, err
|
return ethtypes.EthTx{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx, nil
|
return tx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert a native message to an eth transaction.
|
||||||
|
//
|
||||||
|
// - The state-tree must be from after the message was applied (ideally the following tipset).
|
||||||
|
//
|
||||||
// ethTxFromNativeMessage does NOT populate:
|
// ethTxFromNativeMessage does NOT populate:
|
||||||
// - BlockHash
|
// - BlockHash
|
||||||
// - BlockNumber
|
// - BlockNumber
|
||||||
@ -487,9 +492,42 @@ func ethTxFromNativeMessage(msg *types.Message, st *state.StateTree) ethtypes.Et
|
|||||||
// We don't care if we error here, conversion is best effort for non-eth transactions
|
// We don't care if we error here, conversion is best effort for non-eth transactions
|
||||||
from, _ := lookupEthAddress(msg.From, st)
|
from, _ := lookupEthAddress(msg.From, st)
|
||||||
to, _ := lookupEthAddress(msg.To, st)
|
to, _ := lookupEthAddress(msg.To, st)
|
||||||
|
toPtr := &to
|
||||||
|
|
||||||
|
// Convert the input parameters to "solidity ABI".
|
||||||
|
|
||||||
|
// For empty, we use "0" as the codec. Otherwise, we use CBOR for message
|
||||||
|
// parameters.
|
||||||
|
var codec uint64
|
||||||
|
if len(msg.Params) > 0 {
|
||||||
|
codec = uint64(multicodec.Cbor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We try to decode the input as an EVM method invocation and/or a contract creation. If
|
||||||
|
// that fails, we encode the "native" parameters as Solidity ABI.
|
||||||
|
var input []byte
|
||||||
|
switch msg.Method {
|
||||||
|
case builtintypes.MethodsEVM.InvokeContract, builtintypes.MethodsEAM.CreateExternal:
|
||||||
|
inp, err := decodePayload(msg.Params, codec)
|
||||||
|
if err == nil {
|
||||||
|
// If this is a valid "create external", unset the "to" address.
|
||||||
|
if msg.Method == builtintypes.MethodsEAM.CreateExternal {
|
||||||
|
toPtr = nil
|
||||||
|
}
|
||||||
|
input = []byte(inp)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Yeah, we're going to ignore errors here because the user can send whatever they
|
||||||
|
// want and may send garbage.
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
input = encodeFilecoinParamsAsABI(msg.Method, codec, msg.Params)
|
||||||
|
}
|
||||||
|
|
||||||
return ethtypes.EthTx{
|
return ethtypes.EthTx{
|
||||||
To: &to,
|
To: toPtr,
|
||||||
From: from,
|
From: from,
|
||||||
|
Input: input,
|
||||||
Nonce: ethtypes.EthUint64(msg.Nonce),
|
Nonce: ethtypes.EthUint64(msg.Nonce),
|
||||||
ChainID: ethtypes.EthUint64(build.Eip155ChainId),
|
ChainID: ethtypes.EthUint64(build.Eip155ChainId),
|
||||||
Value: ethtypes.EthBigInt(msg.Value),
|
Value: ethtypes.EthBigInt(msg.Value),
|
||||||
|
Loading…
Reference in New Issue
Block a user