add a basic FEVM integration test. (#9922)

* add a basic FEVM integration test.

Exercises f4 addresses, placeholder transitions, Ethereum Account.

* remove unused parameter from newEthTxFromFilecoinMessageLookup.

* break when found in newEthTxFromFilecoinMessageLookup.

* fixup test.

* lint and gen.

* move test to itests root package.

Co-authored-by: Shrenuj Bansal <shrenuj.bansal@protocol.ai>
This commit is contained in:
raulk 2022-12-21 20:12:07 +00:00 committed by GitHub
parent 7073b33d9b
commit e7aa7cb04f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 395 additions and 41 deletions

View File

@ -769,6 +769,11 @@ workflows:
suite: itest-eth_account_abstraction suite: itest-eth_account_abstraction
target: "./itests/eth_account_abstraction_test.go" target: "./itests/eth_account_abstraction_test.go"
- test:
name: test-itest-eth_deploy
suite: itest-eth_deploy
target: "./itests/eth_deploy_test.go"
- test: - test:
name: test-itest-eth_filter name: test-itest-eth_filter
suite: itest-eth_filter suite: itest-eth_filter

View File

@ -581,7 +581,7 @@ func (filec *FilecoinEC) checkBlockMessages(ctx context.Context, b *types.FullBl
if err != nil { if err != nil {
return err return err
} }
msg, err := txArgs.OriginalRlpMsg() msg, err := txArgs.ToRlpUnsignedMsg()
if err != nil { if err != nil {
return err return err
} }

View File

@ -796,7 +796,7 @@ func (mp *MessagePool) VerifyMsgSig(m *types.SignedMessage) error {
if err != nil { if err != nil {
return xerrors.Errorf("failed to convert to eth tx args: %w", err) return xerrors.Errorf("failed to convert to eth tx args: %w", err)
} }
msg, err := txArgs.OriginalRlpMsg() msg, err := txArgs.ToRlpUnsignedMsg()
if err != nil { if err != nil {
return err return err
} }

View File

@ -20,6 +20,7 @@ import (
"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/actors"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/sigs/delegated"
) )
const Eip1559TxType = 2 const Eip1559TxType = 2
@ -183,7 +184,50 @@ func (tx *EthTxArgs) ToSignedMessage() (*types.SignedMessage, error) {
} }
func (tx *EthTxArgs) OriginalRlpMsg() ([]byte, error) { func (tx *EthTxArgs) HashedOriginalRlpMsg() ([]byte, error) {
msg, err := tx.ToRlpUnsignedMsg()
if err != nil {
return nil, err
}
hasher := sha3.NewLegacyKeccak256()
hasher.Write(msg)
hash := hasher.Sum(nil)
return hash, nil
}
func (tx *EthTxArgs) ToRlpUnsignedMsg() ([]byte, error) {
packed, err := tx.packTxFields()
if err != nil {
return nil, err
}
encoded, err := EncodeRLP(packed)
if err != nil {
return nil, err
}
return append([]byte{0x02}, encoded...), nil
}
func (tx *EthTxArgs) ToRlpSignedMsg() ([]byte, error) {
packed1, err := tx.packTxFields()
if err != nil {
return nil, err
}
packed2, err := tx.packSigFields()
if err != nil {
return nil, err
}
encoded, err := EncodeRLP(append(packed1, packed2...))
if err != nil {
return nil, err
}
return append([]byte{0x02}, encoded...), nil
}
func (tx *EthTxArgs) packTxFields() ([]interface{}, error) {
chainId, err := formatInt(tx.ChainID) chainId, err := formatInt(tx.ChainID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -225,12 +269,27 @@ func (tx *EthTxArgs) OriginalRlpMsg() ([]byte, error) {
tx.Input, tx.Input,
[]interface{}{}, // access list []interface{}{}, // access list
} }
return res, nil
}
encoded, err := EncodeRLP(res) func (tx *EthTxArgs) packSigFields() ([]interface{}, error) {
r, err := formatBigInt(tx.R)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return append([]byte{0x02}, encoded...), nil
s, err := formatBigInt(tx.S)
if err != nil {
return nil, err
}
v, err := formatBigInt(tx.V)
if err != nil {
return nil, err
}
res := []interface{}{v, r, s}
return res, nil
} }
func (tx *EthTxArgs) Signature() (*typescrypto.Signature, error) { func (tx *EthTxArgs) Signature() (*typescrypto.Signature, error) {
@ -255,7 +314,7 @@ func (tx *EthTxArgs) Signature() (*typescrypto.Signature, error) {
} }
func (tx *EthTxArgs) Sender() (address.Address, error) { func (tx *EthTxArgs) Sender() (address.Address, error) {
msg, err := tx.OriginalRlpMsg() msg, err := tx.ToRlpUnsignedMsg()
if err != nil { if err != nil {
return address.Undef, err return address.Undef, err
} }
@ -274,17 +333,11 @@ func (tx *EthTxArgs) Sender() (address.Address, error) {
return address.Undef, err return address.Undef, err
} }
// if we get an uncompressed public key (that's what we get from the library, ethAddr, err := delegated.EthAddressFromPubKey(pubk)
// but putting this check here for defensiveness), strip the prefix if err != nil {
if pubk[0] == 0x04 { return address.Undef, err
pubk = pubk[1:]
} }
// Calculate the f4 address based on the keccak hash of the pubkey.
hasher.Reset()
hasher.Write(pubk)
ethAddr := hasher.Sum(nil)[12:]
return address.NewDelegatedAddress(builtintypes.EthereumAddressManagerActorID, ethAddr) return address.NewDelegatedAddress(builtintypes.EthereumAddressManagerActorID, ethAddr)
} }
@ -402,9 +455,9 @@ func parseEip1559Tx(data []byte) (*EthTxArgs, error) {
GasLimit: gasLimit, GasLimit: gasLimit,
Value: value, Value: value,
Input: input, Input: input,
V: v,
R: r, R: r,
S: s, S: s,
V: v,
} }
return &args, nil return &args, nil
} }

View File

@ -20,6 +20,7 @@ import (
"github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/lib/sigs" "github.com/filecoin-project/lotus/lib/sigs"
"github.com/filecoin-project/lotus/lib/sigs/delegated"
_ "github.com/filecoin-project/lotus/lib/sigs/delegated" _ "github.com/filecoin-project/lotus/lib/sigs/delegated"
) )
@ -42,7 +43,7 @@ func TestTxArgs(t *testing.T) {
txArgs, err := ParseEthTxArgs(tc.Input) txArgs, err := ParseEthTxArgs(tc.Input)
require.NoError(t, err) require.NoError(t, err)
msgRecovered, err := txArgs.OriginalRlpMsg() msgRecovered, err := txArgs.ToRlpUnsignedMsg()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, tc.NosigTx, "0x"+hex.EncodeToString(msgRecovered), comment) require.Equal(t, tc.NosigTx, "0x"+hex.EncodeToString(msgRecovered), comment)
@ -198,14 +199,8 @@ func TestDelegatedSigner(t *testing.T) {
r := mustDecodeHex(rHex) r := mustDecodeHex(rHex)
s := mustDecodeHex(sHex) s := mustDecodeHex(sHex)
if pubk[0] == 0x04 { addrHash, err := delegated.EthAddressFromPubKey(pubk)
pubk = pubk[1:] require.NoError(t, err)
}
hasher := sha3.NewLegacyKeccak256()
hasher.Reset()
hasher.Write(pubk)
addrHash := hasher.Sum(nil)
from, err := address.NewDelegatedAddress(builtintypes.EthereumAddressManagerActorID, addrHash[12:]) from, err := address.NewDelegatedAddress(builtintypes.EthereumAddressManagerActorID, addrHash[12:])
require.NoError(t, err) require.NoError(t, err)

View File

@ -1,7 +1,6 @@
package key package key
import ( import (
"golang.org/x/crypto/sha3"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
@ -10,6 +9,7 @@ import (
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/sigs" "github.com/filecoin-project/lotus/lib/sigs"
"github.com/filecoin-project/lotus/lib/sigs/delegated"
) )
func GenerateKey(typ types.KeyType) (*Key, error) { func GenerateKey(typ types.KeyType) (*Key, error) {
@ -54,17 +54,12 @@ func NewKey(keyinfo types.KeyInfo) (*Key, error) {
} }
case types.KTDelegated: case types.KTDelegated:
// Assume eth for now // Assume eth for now
hasher := sha3.NewLegacyKeccak256() ethAddr, err := delegated.EthAddressFromPubKey(k.PublicKey)
pubk := k.PublicKey if err != nil {
// if we get an uncompressed public key (that's what we get from the library, return nil, xerrors.Errorf("failed to calculate Eth address from public key: %w", err)
// but putting this check here for defensiveness), strip the prefix
if pubk[0] == 0x04 {
pubk = pubk[1:]
} }
hasher.Write(pubk) k.Address, err = address.NewDelegatedAddress(builtin.EthereumAddressManagerActorID, ethAddr)
k.Address, err = address.NewDelegatedAddress(builtin.EthereumAddressManagerActorID, hasher.Sum(nil)[12:])
if err != nil { if err != nil {
return nil, xerrors.Errorf("converting Delegated to address: %w", err) return nil, xerrors.Errorf("converting Delegated to address: %w", err)
} }

View File

@ -71,7 +71,7 @@ func TestEthAccountAbstraction(t *testing.T) {
txArgs, err := ethtypes.NewEthTxArgsFromMessage(msgFromEmbryo) txArgs, err := ethtypes.NewEthTxArgsFromMessage(msgFromEmbryo)
require.NoError(t, err) require.NoError(t, err)
digest, err := txArgs.OriginalRlpMsg() digest, err := txArgs.ToRlpUnsignedMsg()
require.NoError(t, err) require.NoError(t, err)
siggy, err := client.WalletSign(ctx, embryoAddress, digest) siggy, err := client.WalletSign(ctx, embryoAddress, digest)
@ -107,7 +107,7 @@ func TestEthAccountAbstraction(t *testing.T) {
txArgs, err = ethtypes.NewEthTxArgsFromMessage(msgFromEmbryo) txArgs, err = ethtypes.NewEthTxArgsFromMessage(msgFromEmbryo)
require.NoError(t, err) require.NoError(t, err)
digest, err = txArgs.OriginalRlpMsg() digest, err = txArgs.ToRlpUnsignedMsg()
require.NoError(t, err) require.NoError(t, err)
siggy, err = client.WalletSign(ctx, embryoAddress, digest) siggy, err = client.WalletSign(ctx, embryoAddress, digest)

136
itests/eth_deploy_test.go Normal file
View File

@ -0,0 +1,136 @@
package itests
import (
"context"
"encoding/hex"
"fmt"
"os"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-state-types/big"
"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"
"github.com/filecoin-project/lotus/itests/kit"
"github.com/filecoin-project/lotus/node/config"
)
// TestDeployment smoke tests the deployment of a contract via the
// Ethereum JSON-RPC endpoint, from an EEOA.
func TestDeployment(t *testing.T) {
// TODO the contract installation and invocation can be lifted into utility methods
// He who writes the second test, shall do that.
// kit.QuietMiningLogs()
blockTime := 100 * time.Millisecond
client, _, ens := kit.EnsembleMinimal(
t,
kit.MockProofs(),
kit.ThroughRPC(),
kit.WithCfgOpt(func(cfg *config.FullNode) error {
cfg.ActorEvent.EnableRealTimeFilterAPI = true
return nil
}),
)
ens.InterconnectAll().BeginMining(blockTime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
// install contract
contractHex, err := os.ReadFile("./contracts/SimpleCoin.bin")
require.NoError(t, err)
contract, err := hex.DecodeString(string(contractHex))
require.NoError(t, err)
// 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))
// verify balances.
bal := client.EVM().AssertAddressBalanceConsistent(ctx, deployer)
require.Equal(t, types.FromFil(10), bal)
// verify the deployer address is an embryo.
client.AssertActorType(ctx, deployer, manifest.EmbryoKey)
gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{
From: &ethAddr,
Data: contract,
})
require.NoError(t, err)
maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx)
require.NoError(t, err)
// now deploy a contract from the embryo, and validate it went well
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)
pendingFilter, err := client.EthNewPendingTransactionFilter(ctx)
require.NoError(t, err)
hash := client.EVM().SubmitTransaction(ctx, &tx)
fmt.Println(hash)
changes, err := client.EthGetFilterChanges(ctx, pendingFilter)
require.NoError(t, err)
require.Len(t, changes.Results, 1)
require.Equal(t, hash.String(), changes.Results[0])
time.Sleep(5 * time.Second)
var receipt *api.EthTxReceipt
for i := 0; i < 10000000000; i++ {
receipt, err = client.EthGetTransactionReceipt(ctx, hash)
fmt.Println(receipt, err)
if err != nil || receipt == nil {
time.Sleep(500 * time.Millisecond)
continue
}
break
}
require.NoError(t, err)
require.NotNil(t, receipt)
// Success.
require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status)
// Verify that the deployer is now an account.
client.AssertActorType(ctx, deployer, manifest.EthAccountKey)
// Verify that the nonce was incremented.
nonce, err := client.MpoolGetNonce(ctx, deployer)
require.NoError(t, err)
require.EqualValues(t, 1, nonce)
// Verify that the deployer is now an account.
client.AssertActorType(ctx, deployer, manifest.EthAccountKey)
// Get contract address.
contractAddr, err := client.EVM().ComputeContractAddress(ethAddr, 0).ToFilecoinAddress()
require.NoError(t, err)
client.AssertActorType(ctx, contractAddr, "evm")
}

View File

@ -7,8 +7,10 @@ import (
"fmt" "fmt"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/multiformats/go-varint"
"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"
"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"
@ -16,10 +18,15 @@ import (
"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/builtin/v10/eam"
"github.com/filecoin-project/go-state-types/crypto"
"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"
"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/wallet/key"
"github.com/filecoin-project/lotus/lib/sigs"
"github.com/filecoin-project/lotus/lib/sigs/delegated"
) )
// EVM groups EVM-related actions. // EVM groups EVM-related actions.
@ -122,6 +129,89 @@ func (e *EVM) LoadEvents(ctx context.Context, eventsRoot cid.Cid) []types.Event
return ret return ret
} }
func (e *EVM) NewAccount() (*key.Key, ethtypes.EthAddress, address.Address) {
// Generate a secp256k1 key; this will back the Ethereum identity.
key, err := key.GenerateKey(types.KTSecp256k1)
require.NoError(e.t, err)
ethAddr, err := delegated.EthAddressFromPubKey(key.PublicKey)
require.NoError(e.t, err)
addr, err := address.NewDelegatedAddress(builtintypes.EthereumAddressManagerActorID, ethAddr)
require.NoError(e.t, err)
return key, *(*ethtypes.EthAddress)(ethAddr), addr
}
// AssertAddressBalanceConsistent checks that the balance reported via the
// Filecoin and Ethereum operations for an f410 address is identical, returning
// the balance.
func (e *EVM) AssertAddressBalanceConsistent(ctx context.Context, addr address.Address) big.Int {
// Validate the arg is an f410 address.
require.Equal(e.t, address.Delegated, addr.Protocol())
payload := addr.Payload()
namespace, _, err := varint.FromUvarint(payload)
require.NoError(e.t, err)
require.Equal(e.t, builtintypes.EthereumAddressManagerActorID, namespace)
fbal, err := e.WalletBalance(ctx, addr)
require.NoError(e.t, err)
ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(addr)
require.NoError(e.t, err)
ebal, err := e.EthGetBalance(ctx, ethAddr, "latest")
require.NoError(e.t, err)
require.Equal(e.t, fbal, types.BigInt(ebal))
return fbal
}
// SignTransaction signs an Ethereum transaction in place with the supplied private key.
func (e *EVM) SignTransaction(tx *ethtypes.EthTxArgs, privKey []byte) {
preimage, err := tx.ToRlpUnsignedMsg()
require.NoError(e.t, err)
// sign the RLP payload
signature, err := sigs.Sign(crypto.SigTypeDelegated, privKey, preimage)
require.NoError(e.t, err)
r, s, v, err := ethtypes.RecoverSignature(*signature)
require.NoError(e.t, err)
tx.V = big.Int(v)
tx.R = big.Int(r)
tx.S = big.Int(s)
}
// SubmitTransaction submits the transaction via the Eth endpoint.
func (e *EVM) SubmitTransaction(ctx context.Context, tx *ethtypes.EthTxArgs) ethtypes.EthHash {
signed, err := tx.ToRlpSignedMsg()
require.NoError(e.t, err)
hash, err := e.EthSendRawTransaction(ctx, signed)
require.NoError(e.t, err)
return hash
}
// ComputeContractAddress computes the address of a contract deployed by the
// specified address with the specified nonce.
func (e *EVM) ComputeContractAddress(deployer ethtypes.EthAddress, nonce uint64) ethtypes.EthAddress {
nonceRlp, err := formatInt(int(nonce))
require.NoError(e.t, err)
encoded, err := ethtypes.EncodeRLP([]interface{}{
deployer[:],
nonceRlp,
})
require.NoError(e.t, err)
hasher := sha3.NewLegacyKeccak256()
hasher.Write(encoded)
return *(*ethtypes.EthAddress)(hasher.Sum(nil)[12:])
}
// TODO: cleanup and put somewhere reusable. // TODO: cleanup and put somewhere reusable.
type apiIpldStore struct { type apiIpldStore struct {
ctx context.Context ctx context.Context
@ -152,3 +242,23 @@ func (ht *apiIpldStore) Get(ctx context.Context, c cid.Cid, out interface{}) err
func (ht *apiIpldStore) Put(ctx context.Context, v interface{}) (cid.Cid, error) { func (ht *apiIpldStore) Put(ctx context.Context, v interface{}) (cid.Cid, error) {
panic("No mutations allowed") panic("No mutations allowed")
} }
func formatInt(val int) ([]byte, error) {
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.BigEndian, int64(val))
if err != nil {
return nil, err
}
return removeLeadingZeros(buf.Bytes()), nil
}
func removeLeadingZeros(data []byte) []byte {
firstNonZeroIndex := len(data)
for i, b := range data {
if b > 0 {
firstNonZeroIndex = i
break
}
}
return data[firstNonZeroIndex:]
}

33
itests/kit/state.go Normal file
View File

@ -0,0 +1,33 @@
package kit
import (
"context"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-address"
actorstypes "github.com/filecoin-project/go-state-types/actors"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types"
)
// AssertActorType verifies that the supplied address is an actor of the
// specified type (as per its manifest key).
func (f *TestFullNode) AssertActorType(ctx context.Context, addr address.Address, actorType string) {
// validate that an embryo was created
act, err := f.StateGetActor(ctx, addr, types.EmptyTSK)
require.NoError(f.t, err)
nv, err := f.StateNetworkVersion(ctx, types.EmptyTSK)
require.NoError(f.t, err)
av, err := actorstypes.VersionForNetwork(nv)
require.NoError(f.t, err)
codecid, exists := actors.GetActorCodeID(av, actorType)
require.True(f.t, exists)
// check the code CID
require.Equal(f.t, codecid, act.Code)
}

26
lib/sigs/delegated/eth.go Normal file
View File

@ -0,0 +1,26 @@
package delegated
import (
"fmt"
"golang.org/x/crypto/sha3"
)
// EthAddressFromPubKey returns the Ethereum address corresponding to an
// uncompressed secp256k1 public key.
//
// TODO move somewhere else, this likely doesn't belong here.
func EthAddressFromPubKey(pubk []byte) ([]byte, error) {
// if we get an uncompressed public key (that's what we get from the library,
// but putting this check here for defensiveness), strip the prefix
if pubk[0] != 0x04 {
return nil, fmt.Errorf("expected first byte of secp256k1 to be 0x04 (uncompressed)")
}
pubk = pubk[1:]
// Calculate the Ethereum address based on the keccak hash of the pubkey.
hasher := sha3.NewLegacyKeccak256()
hasher.Write(pubk)
ethAddr := hasher.Sum(nil)[12:]
return ethAddr, nil
}

View File

@ -228,7 +228,7 @@ func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtype
// first, try to get the cid from mined transactions // first, try to get the cid from mined transactions
msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, cid, api.LookbackNoLimit, true) msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, cid, api.LookbackNoLimit, true)
if err == nil { if err == nil {
tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, -1, a.Chain, a.ChainAPI, a.StateAPI) tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI)
if err == nil { if err == nil {
return &tx, nil return &tx, nil
} }
@ -278,7 +278,7 @@ func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, txHash ethtype
return nil, nil return nil, nil
} }
tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, -1, a.Chain, a.ChainAPI, a.StateAPI) tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI)
if err != nil { if err != nil {
return nil, nil return nil, nil
} }
@ -1364,7 +1364,7 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx
gasUsed += msgLookup.Receipt.GasUsed gasUsed += msgLookup.Receipt.GasUsed
if fullTxInfo { if fullTxInfo {
tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, txIdx, cs, ca, sa) tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, txIdx, cs, sa)
if err != nil { if err != nil {
return ethtypes.EthBlock{}, nil return ethtypes.EthBlock{}, nil
} }
@ -1498,7 +1498,7 @@ func newEthTxFromFilecoinMessage(ctx context.Context, smsg *types.SignedMessage,
// newEthTxFromFilecoinMessageLookup creates an ethereum transaction from filecoin message lookup. If a negative txIdx is passed // newEthTxFromFilecoinMessageLookup creates an ethereum transaction from filecoin message lookup. If a negative txIdx is passed
// into the function, it looksup the transaction index of the message in the tipset, otherwise it uses the txIdx passed into the // into the function, it looksup the transaction index of the message in the tipset, otherwise it uses the txIdx passed into the
// function // function
func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, txIdx int, cs *store.ChainStore, ca ChainAPI, sa StateAPI) (ethtypes.EthTx, error) { func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, txIdx int, cs *store.ChainStore, sa StateAPI) (ethtypes.EthTx, error) {
if msgLookup == nil { if msgLookup == nil {
return ethtypes.EthTx{}, fmt.Errorf("msg does not exist") return ethtypes.EthTx{}, fmt.Errorf("msg does not exist")
} }
@ -1533,6 +1533,7 @@ func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLo
for i, msg := range msgs { for i, msg := range msgs {
if msg.Cid() == msgLookup.Message { if msg.Cid() == msgLookup.Message {
txIdx = i txIdx = i
break
} }
} }
if txIdx < 0 { if txIdx < 0 {