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) - 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) - 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.
## 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))

View File

@ -62,9 +62,14 @@ type EthTxArgs struct {
// - BlockHash // - BlockHash
// - BlockNumber // - BlockNumber
// - TransactionIndex // - TransactionIndex
// - From
// - Hash // - Hash
func EthTxFromSignedEthMessage(smsg *types.SignedMessage) (EthTx, error) { 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 { if smsg.Signature.Type != typescrypto.SigTypeDelegated {
return EthTx{}, xerrors.Errorf("signature is not delegated type, is type: %d", smsg.Signature.Type) 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) 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{ return EthTx{
Nonce: EthUint64(txArgs.Nonce), Nonce: EthUint64(txArgs.Nonce),
ChainID: EthUint64(txArgs.ChainID), ChainID: EthUint64(txArgs.ChainID),
To: txArgs.To, To: txArgs.To,
From: from,
Value: EthBigInt(txArgs.Value), Value: EthBigInt(txArgs.Value),
Type: Eip1559TxType, Type: Eip1559TxType,
Gas: EthUint64(txArgs.GasLimit), Gas: EthUint64(txArgs.GasLimit),

View File

@ -120,7 +120,6 @@ func TestTransactionHashLookupBlsFilecoinMessage(t *testing.T) {
kit.MockProofs(), kit.MockProofs(),
kit.ThroughRPC(), kit.ThroughRPC(),
) )
ens.InterconnectAll().BeginMining(blocktime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute) ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel() defer cancel()
@ -146,9 +145,13 @@ func TestTransactionHashLookupBlsFilecoinMessage(t *testing.T) {
hash, err := ethtypes.EthHashFromCid(sm.Message.Cid()) hash, err := ethtypes.EthHashFromCid(sm.Message.Cid())
require.NoError(t, err) require.NoError(t, err)
mpoolTx, err := client.EthGetTransactionByHash(ctx, &hash) // Assert that BLS messages cannot be retrieved from the message pool until it lands
require.NoError(t, err) // on-chain via the eth API.
require.Equal(t, hash, mpoolTx.Hash) _, err = client.EthGetTransactionByHash(ctx, &hash)
require.Error(t, err)
// Now start mining.
ens.InterconnectAll().BeginMining(blocktime)
// Wait for message to land on chain // Wait for message to land on chain
var receipt *api.EthTxReceipt var receipt *api.EthTxReceipt
@ -177,6 +180,13 @@ func TestTransactionHashLookupBlsFilecoinMessage(t *testing.T) {
require.NotEmpty(t, *chainTx.BlockHash) require.NotEmpty(t, *chainTx.BlockHash)
require.NotNil(t, chainTx.TransactionIndex) require.NotNil(t, chainTx.TransactionIndex)
require.Equal(t, uint64(*chainTx.TransactionIndex), uint64(0)) // only transaction 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 // 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()) hash, err := ethtypes.EthHashFromCid(secpSmsg.Cid())
require.NoError(t, err) 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) _, err = client.StateWaitMsg(ctx, secpSmsg.Cid(), 3, api.LookbackNoLimit, true)
require.NoError(t, err) require.NoError(t, err)
@ -253,6 +259,13 @@ func TestTransactionHashLookupSecpFilecoinMessage(t *testing.T) {
require.NotEmpty(t, *chainTx.BlockHash) require.NotEmpty(t, *chainTx.BlockHash)
require.NotNil(t, chainTx.TransactionIndex) require.NotNil(t, chainTx.TransactionIndex)
require.Equal(t, uint64(*chainTx.TransactionIndex), uint64(0)) // only transaction 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 // 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 { for _, p := range pending {
if p.Cid() == c { 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 { 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 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 { 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 { if err != nil {
return ethtypes.EthFeeHistory{}, xerrors.Errorf("failed to retrieve messages and receipts for height %d: %w", ts.Height(), err) 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) 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 { if err != nil {
return nil, xerrors.Errorf("failed when calling ExecutionTrace: %w", err) 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() cid, err := ts.Key().Cid()
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to get tipset key cid: %w", err) 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{} 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 { if err != nil {
return nil, xerrors.Errorf("failed building traces: %w", err) 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) 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 { if err != nil {
return nil, xerrors.Errorf("failed when calling ExecutionTrace: %w", err) 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)) allTraces := make([]*ethtypes.EthTraceReplayBlockTransaction, 0, len(trace))
for _, ir := range trace { for _, ir := range trace {
// ignore messages from system actor // ignore messages from system actor
@ -943,7 +964,7 @@ func (a *EthModule) EthTraceReplayBlockTransactions(ctx context.Context, blkNum
VmTrace: nil, 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 { if err != nil {
return nil, xerrors.Errorf("failed building traces: %w", err) 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) _ = 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) { 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) { switch fc := f.(type) {
case filterEventCollector: case filterEventCollector:
return ethFilterResultFromEvents(fc.TakeCollectedEvents(ctx), e.SubManager.StateAPI) return ethFilterResultFromEvents(ctx, fc.TakeCollectedEvents(ctx), e.SubManager.StateAPI)
case filterTipSetCollector: case filterTipSetCollector:
return ethFilterResultFromTipSets(fc.TakeCollectedTipSets(ctx)) return ethFilterResultFromTipSets(fc.TakeCollectedTipSets(ctx))
case filterMessageCollector: case filterMessageCollector:
return ethFilterResultFromMessages(fc.TakeCollectedMessages(ctx), e.SubManager.StateAPI) return ethFilterResultFromMessages(fc.TakeCollectedMessages(ctx))
} }
return nil, xerrors.Errorf("unknown filter type") 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) { switch fc := f.(type) {
case filterEventCollector: 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") 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 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{} res := &ethtypes.EthFilterResult{}
for _, ev := range evs { for _, ev := range evs {
log := ethtypes.EthLog{ log := ethtypes.EthLog{
@ -117,7 +117,7 @@ func ethFilterResultFromEvents(evs []*filter.CollectedEvent, sa StateAPI) (*etht
return nil, err return nil, err
} }
log.TransactionHash, err = ethTxHashFromMessageCid(context.TODO(), ev.MsgCid, sa) log.TransactionHash, err = ethTxHashFromMessageCid(ctx, ev.MsgCid, sa)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -155,11 +155,11 @@ func ethFilterResultFromTipSets(tsks []types.TipSetKey) (*ethtypes.EthFilterResu
return res, nil return res, nil
} }
func ethFilterResultFromMessages(cs []*types.SignedMessage, sa StateAPI) (*ethtypes.EthFilterResult, error) { func ethFilterResultFromMessages(cs []*types.SignedMessage) (*ethtypes.EthFilterResult, error) {
res := &ethtypes.EthFilterResult{} res := &ethtypes.EthFilterResult{}
for _, c := range cs { for _, c := range cs {
hash, err := ethTxHashFromSignedMessage(context.TODO(), c, sa) hash, err := ethTxHashFromSignedMessage(c)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -321,14 +321,14 @@ func (e *ethSubscription) send(ctx context.Context, v interface{}) {
} }
func (e *ethSubscription) start(ctx context.Context) { func (e *ethSubscription) start(ctx context.Context) {
for { for ctx.Err() == nil {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return return
case v := <-e.in: case v := <-e.in:
switch vt := v.(type) { switch vt := v.(type) {
case *filter.CollectedEvent: case *filter.CollectedEvent:
evs, err := ethFilterResultFromEvents([]*filter.CollectedEvent{vt}, e.StateAPI) evs, err := ethFilterResultFromEvents(ctx, []*filter.CollectedEvent{vt}, e.StateAPI)
if err != nil { if err != nil {
continue continue
} }
@ -344,7 +344,7 @@ func (e *ethSubscription) start(ctx context.Context) {
e.send(ctx, ev) e.send(ctx, ev)
case *types.SignedMessage: // mpool txid case *types.SignedMessage: // mpool txid
evs, err := ethFilterResultFromMessages([]*types.SignedMessage{vt}, e.StateAPI) evs, err := ethFilterResultFromMessages([]*types.SignedMessage{vt})
if err != nil { if err != nil {
continue continue
} }

View File

@ -2,7 +2,6 @@ package full
import ( import (
"bytes" "bytes"
"context"
"github.com/multiformats/go-multicodec" "github.com/multiformats/go-multicodec"
cbg "github.com/whyrusleeping/cbor-gen" cbg "github.com/whyrusleeping/cbor-gen"
@ -12,6 +11,7 @@ import (
"github.com/filecoin-project/go-state-types/builtin/v10/evm" "github.com/filecoin-project/go-state-types/builtin/v10/evm"
builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin" 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"
"github.com/filecoin-project/lotus/chain/types/ethtypes" "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 // 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 // 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 we need to include the ActorID in the trace. For now, just log a warning and skip
// this trace. // this trace.
// //
// TODO: Add ActorID in trace, see https://github.com/filecoin-project/lotus/pull/11100#discussion_r1302442288 // 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 { if err != nil {
log.Warnf("buildTraces: failed to lookup from address %s: %v", et.Msg.From, err) log.Warnf("buildTraces: failed to lookup from address %s: %v", et.Msg.From, err)
return nil return nil
} }
to, err := lookupEthAddress(ctx, et.Msg.To, sa) to, err := lookupEthAddress(et.Msg.To, st)
if err != nil { if err != nil {
log.Warnf("buildTraces: failed to lookup to address %s: %w", et.Msg.To, err) log.Warnf("buildTraces: failed to lookup to address %s: %w", et.Msg.To, err)
return nil return nil
@ -239,7 +239,7 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
*traces = append(*traces, trace) *traces = append(*traces, trace)
for i, call := range et.Subcalls { 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 { if err != nil {
return err return err
} }

View File

@ -21,6 +21,7 @@ import (
"github.com/filecoin-project/lotus/api" "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/actors"
"github.com/filecoin-project/lotus/chain/state"
"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"
@ -190,7 +191,8 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx
bn := ethtypes.EthUint64(ts.Height()) bn := ethtypes.EthUint64(ts.Height())
blkCid, err := ts.Key().Cid() tsk := ts.Key()
blkCid, err := tsk.Cid()
if err != nil { if err != nil {
return ethtypes.EthBlock{}, err return ethtypes.EthBlock{}, err
} }
@ -199,11 +201,16 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx
return ethtypes.EthBlock{}, err return ethtypes.EthBlock{}, err
} }
msgs, rcpts, err := messagesAndReceipts(ctx, ts, cs, sa) stRoot, msgs, rcpts, err := executeTipset(ctx, ts, cs, sa)
if err != nil { if err != nil {
return ethtypes.EthBlock{}, xerrors.Errorf("failed to retrieve messages and receipts: %w", err) 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) block := ethtypes.NewEthBlock(len(msgs) > 0)
gasUsed := int64(0) gasUsed := int64(0)
@ -225,7 +232,7 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx
default: default:
return ethtypes.EthBlock{}, xerrors.Errorf("failed to get signed msg %s: %w", msg.Cid(), err) 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 { if err != nil {
return ethtypes.EthBlock{}, xerrors.Errorf("failed to convert msg to ethTx: %w", err) 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 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) msgs, err := cs.MessagesForTipset(ctx, ts)
if err != nil { 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 { 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) rcpts, err := cs.ReadReceipts(ctx, rcptRoot)
if err != nil { 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) { 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) 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 // 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. // 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. // 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. // 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. // Attempt to convert directly, if it's an f4 address.
ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(addr) 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. // 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 return ethtypes.EthAddress{}, err
} else if actor.Address != nil { } else if actor.Address != nil {
if ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(*actor.Address); err == nil && !ethAddr.IsMaskedID() { 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. // Otherwise, resolve the ID addr.
idAddr, err := sa.StateLookupID(ctx, addr, types.EmptyTSK) idAddr, err := st.LookupID(addr)
if err != nil { if err != nil {
return ethtypes.EthAddress{}, err 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) smsg, err := sa.Chain.GetSignedMessage(ctx, c)
if err == nil { if err == nil {
// This is an Eth Tx, Secp message, Or BLS message in the mpool // 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) _, 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 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 { if smsg.Signature.Type == crypto.SigTypeDelegated {
ethTx, err := newEthTxFromSignedMessage(ctx, smsg, sa) tx, err := ethtypes.EthTxFromSignedEthMessage(smsg)
if err != nil { 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 { } else if smsg.Signature.Type == crypto.SigTypeSecp256k1 {
return ethtypes.EthHashFromCid(smsg.Cid()) return ethtypes.EthHashFromCid(smsg.Cid())
} else { // BLS message } 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 tx ethtypes.EthTx
var err error var err error
@ -453,21 +461,14 @@ func newEthTxFromSignedMessage(ctx context.Context, smsg *types.SignedMessage, s
if err != nil { if err != nil {
return ethtypes.EthTx{}, xerrors.Errorf("failed to calculate hash for ethTx: %w", err) 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 } 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()) tx.Hash, err = ethtypes.EthHashFromCid(smsg.Cid())
if err != nil { if err != nil {
return tx, err return tx, err
} }
} else { // BLS Filecoin message } else { // BLS Filecoin message
tx = ethTxFromNativeMessage(ctx, smsg.VMMessage(), sa) 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 tx, err
@ -482,10 +483,10 @@ func newEthTxFromSignedMessage(ctx context.Context, smsg *types.SignedMessage, s
// - BlockNumber // - BlockNumber
// - TransactionIndex // - TransactionIndex
// - Hash // - 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 // We don't care if we error here, conversion is best effort for non-eth transactions
from, _ := lookupEthAddress(ctx, msg.From, sa) from, _ := lookupEthAddress(msg.From, st)
to, _ := lookupEthAddress(ctx, msg.To, sa) to, _ := lookupEthAddress(msg.To, st)
return ethtypes.EthTx{ return ethtypes.EthTx{
To: &to, To: &to,
From: from, 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) 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 { if err != nil {
return ethtypes.EthTx{}, err return ethtypes.EthTx{}, err
} }
@ -576,7 +582,6 @@ func newEthTxFromMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, tx
ti = ethtypes.EthUint64(txIdx) ti = ethtypes.EthUint64(txIdx)
) )
tx.ChainID = ethtypes.EthUint64(build.Eip155ChainId)
tx.BlockHash = &blkHash tx.BlockHash = &blkHash
tx.BlockNumber = &bn tx.BlockNumber = &bn
tx.TransactionIndex = &ti 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) 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 // The tx is located in the parent tipset
parentTs, err := cs.LoadTipSet(ctx, ts.Parents()) parentTs, err := cs.LoadTipSet(ctx, ts.Parents())
if err != nil { 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) 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 { if err != nil {
return api.EthTxReceipt{}, xerrors.Errorf("failed to resolve Ethereum address: %w", err) 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/build"
"github.com/filecoin-project/lotus/chain/ethhashlookup" "github.com/filecoin-project/lotus/chain/ethhashlookup"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
) )
type EthTxHashManager struct { type EthTxHashManager struct {
@ -64,7 +65,7 @@ func (m *EthTxHashManager) Apply(ctx context.Context, from, to *types.TipSet) er
continue continue
} }
hash, err := ethTxHashFromSignedMessage(ctx, smsg, m.StateAPI) hash, err := ethTxHashFromSignedMessage(smsg)
if err != nil { if err != nil {
return err return err
} }
@ -84,13 +85,18 @@ func (m *EthTxHashManager) ProcessSignedMessage(ctx context.Context, msg *types.
return return
} }
ethTx, err := newEthTxFromSignedMessage(ctx, msg, m.StateAPI) ethTx, err := ethtypes.EthTxFromSignedEthMessage(msg)
if err != nil { if err != nil {
log.Errorf("error converting filecoin message to eth tx: %s", err) log.Errorf("error converting filecoin message to eth tx: %s", err)
return 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 { if err != nil {
log.Errorf("error inserting tx mapping to db: %s", err) log.Errorf("error inserting tx mapping to db: %s", err)
return return