From 9b4df6a4d0ca41249c5237fdfb955d70122c3628 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 17 Nov 2023 18:20:31 +0100 Subject: [PATCH] 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 --- CHANGELOG.md | 1 + chain/types/ethtypes/eth_transactions.go | 15 ++++- itests/eth_hash_lookup_test.go | 29 ++++++--- node/impl/full/eth.go | 43 +++++++++---- node/impl/full/eth_event.go | 14 ++--- node/impl/full/eth_trace.go | 10 +-- node/impl/full/eth_utils.go | 78 +++++++++++++----------- node/impl/full/txhashmanager.go | 12 +++- 8 files changed, 133 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e54438eba..6cb54b5f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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)) diff --git a/chain/types/ethtypes/eth_transactions.go b/chain/types/ethtypes/eth_transactions.go index 6c13c5bf6..a3b1d0150 100644 --- a/chain/types/ethtypes/eth_transactions.go +++ b/chain/types/ethtypes/eth_transactions.go @@ -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), diff --git a/itests/eth_hash_lookup_test.go b/itests/eth_hash_lookup_test.go index 37d069796..0bf321088 100644 --- a/itests/eth_hash_lookup_test.go +++ b/itests/eth_hash_lookup_test.go @@ -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 diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index f8c5ba9b5..22f78e723 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -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") diff --git a/node/impl/full/eth_event.go b/node/impl/full/eth_event.go index 69021e08a..54dd164ac 100644 --- a/node/impl/full/eth_event.go +++ b/node/impl/full/eth_event.go @@ -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 := ðtypes.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 := ðtypes.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 } diff --git a/node/impl/full/eth_trace.go b/node/impl/full/eth_trace.go index fd5c25566..9d24394d7 100644 --- a/node/impl/full/eth_trace.go +++ b/node/impl/full/eth_trace.go @@ -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 } diff --git a/node/impl/full/eth_utils.go b/node/impl/full/eth_utils.go index 2799638dd..8bbb58b9b 100644 --- a/node/impl/full/eth_utils.go +++ b/node/impl/full/eth_utils.go @@ -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) } diff --git a/node/impl/full/txhashmanager.go b/node/impl/full/txhashmanager.go index 6757cc6dd..64c488d37 100644 --- a/node/impl/full/txhashmanager.go +++ b/node/impl/full/txhashmanager.go @@ -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