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:
parent
06d288e92b
commit
9b4df6a4d0
@ -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))
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user