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:
+44
-34
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user