fix: eth: use the correct state-tree when resolving addresses (#11387)

We need to always use the state-tree from the tipset _after_ the message
executed. If we use any other state-tree, we might not find the address
we're trying to resolve.

This change also has some implication for pending messages: there's no
guarantee we'll be able to generate a 0x-style address for a pending
native message. So, instead of trying, I've removed support for pending
native messages from the Eth API. Messages from EthAccounts will still
work, and native messages will still show up in blocks/traces, they just
won't show up as "pending". Which should affect exactly nobody.

I'm also taking this opportunity to cleanup some edge-cases:

1. Pass contexts where appropriate.
2. Remove all state access from `ethTxHashFromSignedMessage`.

Part of #11355
This commit is contained in:
Steven Allen 2023-11-17 18:20:31 +01:00 committed by GitHub
parent 06d288e92b
commit 9b4df6a4d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 133 additions and 69 deletions

View File

@ -12,6 +12,7 @@
- fix: Add time slicing to splitstore purging step during compaction to reduce lock congestion [filecoin-project/lotus#11269](https://github.com/filecoin-project/lotus/pull/11269)
- 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: 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.
## New features
- feat: Add move-partition command ([filecoin-project/lotus#11290](https://github.com/filecoin-project/lotus/pull/11290))

View File

@ -62,9 +62,14 @@ type EthTxArgs struct {
// - BlockHash
// - BlockNumber
// - TransactionIndex
// - From
// - Hash
func EthTxFromSignedEthMessage(smsg *types.SignedMessage) (EthTx, error) {
// The from address is always an f410f address, never an ID or other address.
if !IsEthAddress(smsg.Message.From) {
return EthTx{}, xerrors.Errorf("sender must be an eth account, was %s", smsg.Message.From)
}
// Probably redundant, but we might as well check.
if smsg.Signature.Type != typescrypto.SigTypeDelegated {
return EthTx{}, xerrors.Errorf("signature is not delegated type, is type: %d", smsg.Signature.Type)
}
@ -79,10 +84,18 @@ func EthTxFromSignedEthMessage(smsg *types.SignedMessage) (EthTx, error) {
return EthTx{}, xerrors.Errorf("failed to recover signature: %w", err)
}
from, err := EthAddressFromFilecoinAddress(smsg.Message.From)
if err != nil {
// This should be impossible as we've already asserted that we have an EthAddress
// sender...
return EthTx{}, xerrors.Errorf("sender was not an eth account")
}
return EthTx{
Nonce: EthUint64(txArgs.Nonce),
ChainID: EthUint64(txArgs.ChainID),
To: txArgs.To,
From: from,
Value: EthBigInt(txArgs.Value),
Type: Eip1559TxType,
Gas: EthUint64(txArgs.GasLimit),

View File

@ -120,7 +120,6 @@ func TestTransactionHashLookupBlsFilecoinMessage(t *testing.T) {
kit.MockProofs(),
kit.ThroughRPC(),
)
ens.InterconnectAll().BeginMining(blocktime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
@ -146,9 +145,13 @@ func TestTransactionHashLookupBlsFilecoinMessage(t *testing.T) {
hash, err := ethtypes.EthHashFromCid(sm.Message.Cid())
require.NoError(t, err)
mpoolTx, err := client.EthGetTransactionByHash(ctx, &hash)
require.NoError(t, err)
require.Equal(t, hash, mpoolTx.Hash)
// Assert that BLS messages cannot be retrieved from the message pool until it lands
// on-chain via the eth API.
_, err = client.EthGetTransactionByHash(ctx, &hash)
require.Error(t, err)
// Now start mining.
ens.InterconnectAll().BeginMining(blocktime)
// Wait for message to land on chain
var receipt *api.EthTxReceipt
@ -177,6 +180,13 @@ func TestTransactionHashLookupBlsFilecoinMessage(t *testing.T) {
require.NotEmpty(t, *chainTx.BlockHash)
require.NotNil(t, chainTx.TransactionIndex)
require.Equal(t, uint64(*chainTx.TransactionIndex), uint64(0)) // only transaction
// verify that we correctly reported the to address.
toId, err := client.StateLookupID(ctx, addr, types.EmptyTSK)
require.NoError(t, err)
toEth, err := client.FilecoinAddressToEthAddress(ctx, toId)
require.NoError(t, err)
require.Equal(t, &toEth, chainTx.To)
}
// TestTransactionHashLookupSecpFilecoinMessage tests to see if lotus can find a Secp Filecoin Message using the transaction hash
@ -228,10 +238,6 @@ func TestTransactionHashLookupSecpFilecoinMessage(t *testing.T) {
hash, err := ethtypes.EthHashFromCid(secpSmsg.Cid())
require.NoError(t, err)
mpoolTx, err := client.EthGetTransactionByHash(ctx, &hash)
require.NoError(t, err)
require.Equal(t, hash, mpoolTx.Hash)
_, err = client.StateWaitMsg(ctx, secpSmsg.Cid(), 3, api.LookbackNoLimit, true)
require.NoError(t, err)
@ -253,6 +259,13 @@ func TestTransactionHashLookupSecpFilecoinMessage(t *testing.T) {
require.NotEmpty(t, *chainTx.BlockHash)
require.NotNil(t, chainTx.TransactionIndex)
require.Equal(t, uint64(*chainTx.TransactionIndex), uint64(0)) // only transaction
// verify that we correctly reported the to address.
toId, err := client.StateLookupID(ctx, client.DefaultKey.Address, types.EmptyTSK)
require.NoError(t, err)
toEth, err := client.FilecoinAddressToEthAddress(ctx, toId)
require.NoError(t, err)
require.Equal(t, &toEth, chainTx.To)
}
// TestTransactionHashLookupSecpFilecoinMessage tests to see if lotus can find a Secp Filecoin Message using the transaction hash

View File

@ -285,9 +285,20 @@ func (a *EthModule) EthGetTransactionByHashLimited(ctx context.Context, txHash *
for _, p := range pending {
if p.Cid() == c {
tx, err := newEthTxFromSignedMessage(ctx, p, a.StateAPI)
// We only return pending eth-account messages because we can't guarantee
// that the from/to addresses of other messages are conversable to 0x-style
// addresses. So we just ignore them.
//
// This should be "fine" as anyone using an "Ethereum-centric" block
// explorer shouldn't care about seeing pending messages from native
// accounts.
tx, err := ethtypes.EthTxFromSignedEthMessage(p)
if err != nil {
return nil, fmt.Errorf("could not convert Filecoin message into tx: %s", err)
return nil, fmt.Errorf("could not convert Filecoin message into tx: %w", err)
}
tx.Hash, err = tx.TxHash()
if err != nil {
return nil, fmt.Errorf("could not compute tx hash for eth txn: %w", err)
}
return &tx, nil
}
@ -718,7 +729,7 @@ func (a *EthModule) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (eth
)
for blocksIncluded < int(params.BlkCount) && ts.Height() > 0 {
msgs, rcpts, err := messagesAndReceipts(ctx, ts, a.Chain, a.StateAPI)
_, msgs, rcpts, err := executeTipset(ctx, ts, a.Chain, a.StateAPI)
if err != nil {
return ethtypes.EthFeeHistory{}, xerrors.Errorf("failed to retrieve messages and receipts for height %d: %w", ts.Height(), err)
}
@ -837,11 +848,16 @@ func (a *EthModule) EthTraceBlock(ctx context.Context, blkNum string) ([]*ethtyp
return nil, xerrors.Errorf("failed to get tipset: %w", err)
}
_, trace, err := a.StateManager.ExecutionTrace(ctx, ts)
stRoot, trace, err := a.StateManager.ExecutionTrace(ctx, ts)
if err != nil {
return nil, xerrors.Errorf("failed when calling ExecutionTrace: %w", err)
}
st, err := a.StateManager.StateTree(stRoot)
if err != nil {
return nil, xerrors.Errorf("failed load computed state-tree: %w", err)
}
cid, err := ts.Key().Cid()
if err != nil {
return nil, xerrors.Errorf("failed to get tipset key cid: %w", err)
@ -872,7 +888,7 @@ func (a *EthModule) EthTraceBlock(ctx context.Context, blkNum string) ([]*ethtyp
}
traces := []*ethtypes.EthTrace{}
err = buildTraces(ctx, &traces, nil, []int{}, ir.ExecutionTrace, int64(ts.Height()), a.StateAPI)
err = buildTraces(&traces, nil, []int{}, ir.ExecutionTrace, int64(ts.Height()), st)
if err != nil {
return nil, xerrors.Errorf("failed building traces: %w", err)
}
@ -904,11 +920,16 @@ func (a *EthModule) EthTraceReplayBlockTransactions(ctx context.Context, blkNum
return nil, xerrors.Errorf("failed to get tipset: %w", err)
}
_, trace, err := a.StateManager.ExecutionTrace(ctx, ts)
stRoot, trace, err := a.StateManager.ExecutionTrace(ctx, ts)
if err != nil {
return nil, xerrors.Errorf("failed when calling ExecutionTrace: %w", err)
}
st, err := a.StateManager.StateTree(stRoot)
if err != nil {
return nil, xerrors.Errorf("failed load computed state-tree: %w", err)
}
allTraces := make([]*ethtypes.EthTraceReplayBlockTransaction, 0, len(trace))
for _, ir := range trace {
// ignore messages from system actor
@ -943,7 +964,7 @@ func (a *EthModule) EthTraceReplayBlockTransactions(ctx context.Context, blkNum
VmTrace: nil,
}
err = buildTraces(ctx, &t.Trace, nil, []int{}, ir.ExecutionTrace, int64(ts.Height()), a.StateAPI)
err = buildTraces(&t.Trace, nil, []int{}, ir.ExecutionTrace, int64(ts.Height()), st)
if err != nil {
return nil, xerrors.Errorf("failed building traces: %w", err)
}
@ -1184,7 +1205,7 @@ func (e *EthEvent) EthGetLogs(ctx context.Context, filterSpec *ethtypes.EthFilte
_ = e.uninstallFilter(ctx, f)
return ethFilterResultFromEvents(ces, e.SubManager.StateAPI)
return ethFilterResultFromEvents(ctx, ces, e.SubManager.StateAPI)
}
func (e *EthEvent) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) {
@ -1199,11 +1220,11 @@ func (e *EthEvent) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilte
switch fc := f.(type) {
case filterEventCollector:
return ethFilterResultFromEvents(fc.TakeCollectedEvents(ctx), e.SubManager.StateAPI)
return ethFilterResultFromEvents(ctx, fc.TakeCollectedEvents(ctx), e.SubManager.StateAPI)
case filterTipSetCollector:
return ethFilterResultFromTipSets(fc.TakeCollectedTipSets(ctx))
case filterMessageCollector:
return ethFilterResultFromMessages(fc.TakeCollectedMessages(ctx), e.SubManager.StateAPI)
return ethFilterResultFromMessages(fc.TakeCollectedMessages(ctx))
}
return nil, xerrors.Errorf("unknown filter type")
@ -1221,7 +1242,7 @@ func (e *EthEvent) EthGetFilterLogs(ctx context.Context, id ethtypes.EthFilterID
switch fc := f.(type) {
case filterEventCollector:
return ethFilterResultFromEvents(fc.TakeCollectedEvents(ctx), e.SubManager.StateAPI)
return ethFilterResultFromEvents(ctx, fc.TakeCollectedEvents(ctx), e.SubManager.StateAPI)
}
return nil, xerrors.Errorf("wrong filter type")

View File

@ -93,7 +93,7 @@ func ethLogFromEvent(entries []types.EventEntry) (data []byte, topics []ethtypes
return data, topics, true
}
func ethFilterResultFromEvents(evs []*filter.CollectedEvent, sa StateAPI) (*ethtypes.EthFilterResult, error) {
func ethFilterResultFromEvents(ctx context.Context, evs []*filter.CollectedEvent, sa StateAPI) (*ethtypes.EthFilterResult, error) {
res := &ethtypes.EthFilterResult{}
for _, ev := range evs {
log := ethtypes.EthLog{
@ -117,7 +117,7 @@ func ethFilterResultFromEvents(evs []*filter.CollectedEvent, sa StateAPI) (*etht
return nil, err
}
log.TransactionHash, err = ethTxHashFromMessageCid(context.TODO(), ev.MsgCid, sa)
log.TransactionHash, err = ethTxHashFromMessageCid(ctx, ev.MsgCid, sa)
if err != nil {
return nil, err
}
@ -155,11 +155,11 @@ func ethFilterResultFromTipSets(tsks []types.TipSetKey) (*ethtypes.EthFilterResu
return res, nil
}
func ethFilterResultFromMessages(cs []*types.SignedMessage, sa StateAPI) (*ethtypes.EthFilterResult, error) {
func ethFilterResultFromMessages(cs []*types.SignedMessage) (*ethtypes.EthFilterResult, error) {
res := &ethtypes.EthFilterResult{}
for _, c := range cs {
hash, err := ethTxHashFromSignedMessage(context.TODO(), c, sa)
hash, err := ethTxHashFromSignedMessage(c)
if err != nil {
return nil, err
}
@ -321,14 +321,14 @@ func (e *ethSubscription) send(ctx context.Context, v interface{}) {
}
func (e *ethSubscription) start(ctx context.Context) {
for {
for ctx.Err() == nil {
select {
case <-ctx.Done():
return
case v := <-e.in:
switch vt := v.(type) {
case *filter.CollectedEvent:
evs, err := ethFilterResultFromEvents([]*filter.CollectedEvent{vt}, e.StateAPI)
evs, err := ethFilterResultFromEvents(ctx, []*filter.CollectedEvent{vt}, e.StateAPI)
if err != nil {
continue
}
@ -344,7 +344,7 @@ func (e *ethSubscription) start(ctx context.Context) {
e.send(ctx, ev)
case *types.SignedMessage: // mpool txid
evs, err := ethFilterResultFromMessages([]*types.SignedMessage{vt}, e.StateAPI)
evs, err := ethFilterResultFromMessages([]*types.SignedMessage{vt})
if err != nil {
continue
}

View File

@ -2,7 +2,6 @@ package full
import (
"bytes"
"context"
"github.com/multiformats/go-multicodec"
cbg "github.com/whyrusleeping/cbor-gen"
@ -12,6 +11,7 @@ import (
"github.com/filecoin-project/go-state-types/builtin/v10/evm"
builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
)
@ -39,18 +39,18 @@ func decodePayload(payload []byte, codec uint64) (ethtypes.EthBytes, error) {
}
// buildTraces recursively builds the traces for a given ExecutionTrace by walking the subcalls
func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *ethtypes.EthTrace, addr []int, et types.ExecutionTrace, height int64, sa StateAPI) error {
func buildTraces(traces *[]*ethtypes.EthTrace, parent *ethtypes.EthTrace, addr []int, et types.ExecutionTrace, height int64, st *state.StateTree) error {
// lookup the eth address from the from/to addresses. Note that this may fail but to support
// this we need to include the ActorID in the trace. For now, just log a warning and skip
// this trace.
//
// TODO: Add ActorID in trace, see https://github.com/filecoin-project/lotus/pull/11100#discussion_r1302442288
from, err := lookupEthAddress(ctx, et.Msg.From, sa)
from, err := lookupEthAddress(et.Msg.From, st)
if err != nil {
log.Warnf("buildTraces: failed to lookup from address %s: %v", et.Msg.From, err)
return nil
}
to, err := lookupEthAddress(ctx, et.Msg.To, sa)
to, err := lookupEthAddress(et.Msg.To, st)
if err != nil {
log.Warnf("buildTraces: failed to lookup to address %s: %w", et.Msg.To, err)
return nil
@ -239,7 +239,7 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
*traces = append(*traces, trace)
for i, call := range et.Subcalls {
err := buildTraces(ctx, traces, trace, append(addr, i), call, height, sa)
err := buildTraces(traces, trace, append(addr, i), call, height, st)
if err != nil {
return err
}

View File

@ -21,6 +21,7 @@ import (
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
@ -190,7 +191,8 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx
bn := ethtypes.EthUint64(ts.Height())
blkCid, err := ts.Key().Cid()
tsk := ts.Key()
blkCid, err := tsk.Cid()
if err != nil {
return ethtypes.EthBlock{}, err
}
@ -199,11 +201,16 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx
return ethtypes.EthBlock{}, err
}
msgs, rcpts, err := messagesAndReceipts(ctx, ts, cs, sa)
stRoot, msgs, rcpts, err := executeTipset(ctx, ts, cs, sa)
if err != nil {
return ethtypes.EthBlock{}, xerrors.Errorf("failed to retrieve messages and receipts: %w", err)
}
st, err := sa.StateManager.StateTree(stRoot)
if err != nil {
return ethtypes.EthBlock{}, xerrors.Errorf("failed to load state-tree root %q: %w", stRoot, err)
}
block := ethtypes.NewEthBlock(len(msgs) > 0)
gasUsed := int64(0)
@ -225,7 +232,7 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx
default:
return ethtypes.EthBlock{}, xerrors.Errorf("failed to get signed msg %s: %w", msg.Cid(), err)
}
tx, err := newEthTxFromSignedMessage(ctx, smsg, sa)
tx, err := newEthTxFromSignedMessage(smsg, st)
if err != nil {
return ethtypes.EthBlock{}, xerrors.Errorf("failed to convert msg to ethTx: %w", err)
}
@ -251,27 +258,27 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx
return block, nil
}
func messagesAndReceipts(ctx context.Context, ts *types.TipSet, cs *store.ChainStore, sa StateAPI) ([]types.ChainMsg, []types.MessageReceipt, error) {
func executeTipset(ctx context.Context, ts *types.TipSet, cs *store.ChainStore, sa StateAPI) (cid.Cid, []types.ChainMsg, []types.MessageReceipt, error) {
msgs, err := cs.MessagesForTipset(ctx, ts)
if err != nil {
return nil, nil, xerrors.Errorf("error loading messages for tipset: %v: %w", ts, err)
return cid.Undef, nil, nil, xerrors.Errorf("error loading messages for tipset: %v: %w", ts, err)
}
_, rcptRoot, err := sa.StateManager.TipSetState(ctx, ts)
stRoot, rcptRoot, err := sa.StateManager.TipSetState(ctx, ts)
if err != nil {
return nil, nil, xerrors.Errorf("failed to compute state: %w", err)
return cid.Undef, nil, nil, xerrors.Errorf("failed to compute state: %w", err)
}
rcpts, err := cs.ReadReceipts(ctx, rcptRoot)
if err != nil {
return nil, nil, xerrors.Errorf("error loading receipts for tipset: %v: %w", ts, err)
return cid.Undef, nil, nil, xerrors.Errorf("error loading receipts for tipset: %v: %w", ts, err)
}
if len(msgs) != len(rcpts) {
return nil, nil, xerrors.Errorf("receipts and message array lengths didn't match for tipset: %v: %w", ts, err)
return cid.Undef, nil, nil, xerrors.Errorf("receipts and message array lengths didn't match for tipset: %v: %w", ts, err)
}
return msgs, rcpts, nil
return stRoot, msgs, rcpts, nil
}
const errorFunctionSelector = "\x08\xc3\x79\xa0" // Error(string)
@ -361,7 +368,7 @@ func parseEthRevert(ret []byte) string {
// 3. Otherwise, we fall back to returning a masked ID Ethereum address. If the supplied address is an f0 address, we
// use that ID to form the masked ID address.
// 4. Otherwise, we fetch the actor's ID from the state tree and form the masked ID with it.
func lookupEthAddress(ctx context.Context, addr address.Address, sa StateAPI) (ethtypes.EthAddress, error) {
func lookupEthAddress(addr address.Address, st *state.StateTree) (ethtypes.EthAddress, error) {
// BLOCK A: We are trying to get an actual Ethereum address from an f410 address.
// Attempt to convert directly, if it's an f4 address.
ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(addr)
@ -370,7 +377,7 @@ func lookupEthAddress(ctx context.Context, addr address.Address, sa StateAPI) (e
}
// Lookup on the target actor and try to get an f410 address.
if actor, err := sa.StateGetActor(ctx, addr, types.EmptyTSK); err != nil {
if actor, err := st.GetActor(addr); err != nil {
return ethtypes.EthAddress{}, err
} else if actor.Address != nil {
if ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(*actor.Address); err == nil && !ethAddr.IsMaskedID() {
@ -385,7 +392,7 @@ func lookupEthAddress(ctx context.Context, addr address.Address, sa StateAPI) (e
}
// Otherwise, resolve the ID addr.
idAddr, err := sa.StateLookupID(ctx, addr, types.EmptyTSK)
idAddr, err := st.LookupID(addr)
if err != nil {
return ethtypes.EthAddress{}, err
}
@ -412,7 +419,7 @@ func ethTxHashFromMessageCid(ctx context.Context, c cid.Cid, sa StateAPI) (ethty
smsg, err := sa.Chain.GetSignedMessage(ctx, c)
if err == nil {
// This is an Eth Tx, Secp message, Or BLS message in the mpool
return ethTxHashFromSignedMessage(ctx, smsg, sa)
return ethTxHashFromSignedMessage(smsg)
}
_, err = sa.Chain.GetMessage(ctx, c)
@ -424,13 +431,14 @@ func ethTxHashFromMessageCid(ctx context.Context, c cid.Cid, sa StateAPI) (ethty
return ethtypes.EmptyEthHash, nil
}
func ethTxHashFromSignedMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthHash, error) {
func ethTxHashFromSignedMessage(smsg *types.SignedMessage) (ethtypes.EthHash, error) {
if smsg.Signature.Type == crypto.SigTypeDelegated {
ethTx, err := newEthTxFromSignedMessage(ctx, smsg, sa)
tx, err := ethtypes.EthTxFromSignedEthMessage(smsg)
if err != nil {
return ethtypes.EmptyEthHash, err
return ethtypes.EthHash{}, xerrors.Errorf("failed to convert from signed message: %w", err)
}
return ethTx.Hash, nil
return tx.TxHash()
} else if smsg.Signature.Type == crypto.SigTypeSecp256k1 {
return ethtypes.EthHashFromCid(smsg.Cid())
} else { // BLS message
@ -438,7 +446,7 @@ func ethTxHashFromSignedMessage(ctx context.Context, smsg *types.SignedMessage,
}
}
func newEthTxFromSignedMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthTx, error) {
func newEthTxFromSignedMessage(smsg *types.SignedMessage, st *state.StateTree) (ethtypes.EthTx, error) {
var tx ethtypes.EthTx
var err error
@ -453,21 +461,14 @@ func newEthTxFromSignedMessage(ctx context.Context, smsg *types.SignedMessage, s
if err != nil {
return ethtypes.EthTx{}, xerrors.Errorf("failed to calculate hash for ethTx: %w", err)
}
fromAddr, err := lookupEthAddress(ctx, smsg.Message.From, sa)
if err != nil {
return ethtypes.EthTx{}, xerrors.Errorf("failed to resolve Ethereum address: %w", err)
}
tx.From = fromAddr
} else if smsg.Signature.Type == crypto.SigTypeSecp256k1 { // Secp Filecoin Message
tx = ethTxFromNativeMessage(ctx, smsg.VMMessage(), sa)
tx = ethTxFromNativeMessage(smsg.VMMessage(), st)
tx.Hash, err = ethtypes.EthHashFromCid(smsg.Cid())
if err != nil {
return tx, err
}
} else { // BLS Filecoin message
tx = ethTxFromNativeMessage(ctx, smsg.VMMessage(), sa)
tx = ethTxFromNativeMessage(smsg.VMMessage(), st)
tx.Hash, err = ethtypes.EthHashFromCid(smsg.Message.Cid())
if err != nil {
return tx, err
@ -482,10 +483,10 @@ func newEthTxFromSignedMessage(ctx context.Context, smsg *types.SignedMessage, s
// - BlockNumber
// - TransactionIndex
// - Hash
func ethTxFromNativeMessage(ctx context.Context, msg *types.Message, sa StateAPI) ethtypes.EthTx {
func ethTxFromNativeMessage(msg *types.Message, st *state.StateTree) ethtypes.EthTx {
// We don't care if we error here, conversion is best effort for non-eth transactions
from, _ := lookupEthAddress(ctx, msg.From, sa)
to, _ := lookupEthAddress(ctx, msg.To, sa)
from, _ := lookupEthAddress(msg.From, st)
to, _ := lookupEthAddress(msg.To, st)
return ethtypes.EthTx{
To: &to,
From: from,
@ -566,7 +567,12 @@ func newEthTxFromMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, tx
return ethtypes.EthTx{}, xerrors.Errorf("failed to get signed msg: %w", err)
}
tx, err := newEthTxFromSignedMessage(ctx, smsg, sa)
st, err := sa.StateManager.StateTree(ts.ParentState())
if err != nil {
return ethtypes.EthTx{}, xerrors.Errorf("failed to load message state tree: %w", err)
}
tx, err := newEthTxFromSignedMessage(smsg, st)
if err != nil {
return ethtypes.EthTx{}, err
}
@ -576,7 +582,6 @@ func newEthTxFromMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, tx
ti = ethtypes.EthUint64(txIdx)
)
tx.ChainID = ethtypes.EthUint64(build.Eip155ChainId)
tx.BlockHash = &blkHash
tx.BlockNumber = &bn
tx.TransactionIndex = &ti
@ -629,6 +634,11 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook
return api.EthTxReceipt{}, xerrors.Errorf("failed to lookup tipset %s when constructing the eth txn receipt: %w", lookup.TipSet, err)
}
st, err := sa.StateManager.StateTree(ts.ParentState())
if err != nil {
return api.EthTxReceipt{}, xerrors.Errorf("failed to load the state %s when constructing the eth txn receipt: %w", ts.ParentState(), err)
}
// The tx is located in the parent tipset
parentTs, err := cs.LoadTipSet(ctx, ts.Parents())
if err != nil {
@ -684,7 +694,7 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook
return api.EthTxReceipt{}, xerrors.Errorf("failed to create ID address: %w", err)
}
l.Address, err = lookupEthAddress(ctx, addr, sa)
l.Address, err = lookupEthAddress(addr, st)
if err != nil {
return api.EthTxReceipt{}, xerrors.Errorf("failed to resolve Ethereum address: %w", err)
}

View File

@ -11,6 +11,7 @@ import (
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/ethhashlookup"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
)
type EthTxHashManager struct {
@ -64,7 +65,7 @@ func (m *EthTxHashManager) Apply(ctx context.Context, from, to *types.TipSet) er
continue
}
hash, err := ethTxHashFromSignedMessage(ctx, smsg, m.StateAPI)
hash, err := ethTxHashFromSignedMessage(smsg)
if err != nil {
return err
}
@ -84,13 +85,18 @@ func (m *EthTxHashManager) ProcessSignedMessage(ctx context.Context, msg *types.
return
}
ethTx, err := newEthTxFromSignedMessage(ctx, msg, m.StateAPI)
ethTx, err := ethtypes.EthTxFromSignedEthMessage(msg)
if err != nil {
log.Errorf("error converting filecoin message to eth tx: %s", err)
return
}
txHash, err := ethTx.TxHash()
if err != nil {
log.Errorf("error hashing transaction: %s", err)
return
}
err = m.TransactionHashLookup.UpsertHash(ethTx.Hash, msg.Cid())
err = m.TransactionHashLookup.UpsertHash(txHash, msg.Cid())
if err != nil {
log.Errorf("error inserting tx mapping to db: %s", err)
return