diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index df5b7dd95..c3b955b3d 100644 Binary files a/build/openrpc/full.json.gz and b/build/openrpc/full.json.gz differ diff --git a/chain/types/ethtypes/eth_transactions.go b/chain/types/ethtypes/eth_transactions.go index 4f5f865e4..0d20381ed 100644 --- a/chain/types/ethtypes/eth_transactions.go +++ b/chain/types/ethtypes/eth_transactions.go @@ -39,9 +39,9 @@ type EthTx struct { Gas EthUint64 `json:"gas"` MaxFeePerGas EthBigInt `json:"maxFeePerGas"` MaxPriorityFeePerGas EthBigInt `json:"maxPriorityFeePerGas"` - V EthBytes `json:"v"` - R EthBytes `json:"r"` - S EthBytes `json:"s"` + V EthBigInt `json:"v"` + R EthBigInt `json:"r"` + S EthBigInt `json:"s"` } type EthTxArgs struct { @@ -53,9 +53,9 @@ type EthTxArgs struct { MaxPriorityFeePerGas big.Int `json:"maxPriorityFeePerGas"` GasLimit int `json:"gasLimit"` Input []byte `json:"input"` - V []byte `json:"v"` - R []byte `json:"r"` - S []byte `json:"s"` + V big.Int `json:"v"` + R big.Int `json:"r"` + S big.Int `json:"s"` } func NewEthTxArgsFromMessage(msg *types.Message) (EthTxArgs, error) { @@ -246,9 +246,17 @@ func (tx *EthTxArgs) OriginalRlpMsg() ([]byte, error) { } func (tx *EthTxArgs) Signature() (*typescrypto.Signature, error) { - sig := append([]byte{}, tx.R...) - sig = append(sig, tx.S...) - sig = append(sig, tx.V...) + r := tx.R.Int.Bytes() + s := tx.S.Int.Bytes() + v := tx.V.Int.Bytes() + + sig := append([]byte{}, padLeadingZeros(r, 32)...) + sig = append(sig, padLeadingZeros(s, 32)...) + if len(v) == 0 { + sig = append(sig, 0) + } else { + sig = append(sig, v[0]) + } if len(sig) != 65 { return nil, fmt.Errorf("signature is not 65 bytes") @@ -292,6 +300,33 @@ func (tx *EthTxArgs) Sender() (address.Address, error) { return address.NewDelegatedAddress(builtintypes.EthereumAddressManagerActorID, ethAddr) } +func RecoverSignature(sig typescrypto.Signature) (r, s, v EthBigInt, err error) { + if sig.Type != typescrypto.SigTypeDelegated { + return EthBigIntZero, EthBigIntZero, EthBigIntZero, fmt.Errorf("RecoverSignature only supports Delegated signature") + } + + if len(sig.Data) != 65 { + return EthBigIntZero, EthBigIntZero, EthBigIntZero, fmt.Errorf("signature should be 65 bytes long, but got %d bytes", len(sig.Data)) + } + + r_, err := parseBigInt(sig.Data[0:32]) + if err != nil { + return EthBigIntZero, EthBigIntZero, EthBigIntZero, fmt.Errorf("cannot parse r into EthBigInt") + } + + s_, err := parseBigInt(sig.Data[32:64]) + if err != nil { + return EthBigIntZero, EthBigIntZero, EthBigIntZero, fmt.Errorf("cannot parse s into EthBigInt") + } + + v_, err := parseBigInt([]byte{sig.Data[64]}) + if err != nil { + return EthBigIntZero, EthBigIntZero, EthBigIntZero, fmt.Errorf("cannot parse v into EthBigInt") + } + + return EthBigInt(r_), EthBigInt(s_), EthBigInt(v_), nil +} + func parseEip1559Tx(data []byte) (*EthTxArgs, error) { if data[0] != 2 { return nil, fmt.Errorf("not an EIP-1559 transaction: first byte is not 2") @@ -355,21 +390,17 @@ func parseEip1559Tx(data []byte) (*EthTxArgs, error) { return nil, fmt.Errorf("access list should be an empty list") } - V, err := parseBytes(decoded[9]) + r, err := parseBigInt(decoded[10]) if err != nil { return nil, err } - if len(V) == 0 { - V = []byte{0} - } - - R, err := parseBytes(decoded[10]) + s, err := parseBigInt(decoded[11]) if err != nil { return nil, err } - S, err := parseBytes(decoded[11]) + v, err := parseBigInt(decoded[9]) if err != nil { return nil, err } @@ -383,9 +414,9 @@ func parseEip1559Tx(data []byte) (*EthTxArgs, error) { GasLimit: gasLimit, Value: value, Input: input, - R: padLeadingZeros(R, 32), - S: padLeadingZeros(S, 32), - V: V, + R: r, + S: s, + V: v, } return &args, nil } diff --git a/chain/types/ethtypes/eth_transactions_test.go b/chain/types/ethtypes/eth_transactions_test.go index b94692a3f..cb37f02c7 100644 --- a/chain/types/ethtypes/eth_transactions_test.go +++ b/chain/types/ethtypes/eth_transactions_test.go @@ -63,6 +63,66 @@ func TestTxArgs(t *testing.T) { } } +func TestSignatures(t *testing.T) { + testcases := []struct { + RawTx string + ExpectedR string + ExpectedS string + ExpectedV string + ExpectErr bool + }{ + { + "0x02f8598401df5e76028301d69083086a5e835532dd808080c080a0457e33227ac7ceee2ef121755e26b872b6fb04221993f9939349bb7b0a3e1595a02d8ef379e1d2a9e30fa61c92623cc9ed72d80cf6a48cfea341cb916bcc0a81bc", + `"0x457e33227ac7ceee2ef121755e26b872b6fb04221993f9939349bb7b0a3e1595"`, + `"0x2d8ef379e1d2a9e30fa61c92623cc9ed72d80cf6a48cfea341cb916bcc0a81bc"`, + `"0x0"`, + false, + }, + { + "0x02f8598401df5e76038301d69083086a5e835532dd808080c001a012a232866dcb0671eb0ddc01fb9c01d6ef384ec892bb29691ed0d2d293052ddfa052a6ae38c6139930db21a00eee2a4caced9a6500991b823d64ec664d003bc4b1", + `"0x12a232866dcb0671eb0ddc01fb9c01d6ef384ec892bb29691ed0d2d293052ddf"`, + `"0x52a6ae38c6139930db21a00eee2a4caced9a6500991b823d64ec664d003bc4b1"`, + `"0x1"`, + false, + }, + { + "0x00", + `""`, + `""`, + `""`, + true, + }, + } + + for _, tc := range testcases { + tx, err := ParseEthTxArgs(mustDecodeHex(tc.RawTx)) + if tc.ExpectErr { + require.Error(t, err) + continue + } + require.Nil(t, err) + + sig, err := tx.Signature() + require.Nil(t, err) + + r, s, v, err := RecoverSignature(*sig) + require.Nil(t, err) + + marshaledR, err := r.MarshalJSON() + require.Nil(t, err) + + marshaledS, err := s.MarshalJSON() + require.Nil(t, err) + + marshaledV, err := v.MarshalJSON() + require.Nil(t, err) + + require.Equal(t, tc.ExpectedR, string(marshaledR)) + require.Equal(t, tc.ExpectedS, string(marshaledS)) + require.Equal(t, tc.ExpectedV, string(marshaledV)) + } +} + func TestTransformParams(t *testing.T) { constructorParams, err := actors.SerializeParams(&evm.ConstructorParams{ Initcode: mustDecodeHex("0x1122334455"), diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index cb629f8d5..b5ab878ba 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -61,12 +61,13 @@ func EthUint64FromHex(s string) (EthUint64, error) { return EthUint64(parsedInt), nil } +// EthBigInt represents a large integer whose zero value serializes to "0x0". type EthBigInt big.Int var EthBigIntZero = EthBigInt{Int: big.Zero().Int} func (e EthBigInt) MarshalJSON() ([]byte, error) { - if e.Int == nil { + if e.Int == nil || e.Int.BitLen() == 0 { return json.Marshal("0x0") } return json.Marshal(fmt.Sprintf("0x%x", e.Int)) @@ -90,6 +91,7 @@ func (e *EthBigInt) UnmarshalJSON(b []byte) error { return nil } +// EthBytes represent arbitrary bytes. A nil or empty slice serializes to "0x". type EthBytes []byte func (e EthBytes) MarshalJSON() ([]byte, error) { @@ -97,9 +99,6 @@ func (e EthBytes) MarshalJSON() ([]byte, error) { return json.Marshal("0x") } s := hex.EncodeToString(e) - if len(s)%2 == 1 { - s = "0" + s - } return json.Marshal("0x" + s) } diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 2f9ccf0d0..025c4356c 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -2330,9 +2330,9 @@ Response: "gas": "0x5", "maxFeePerGas": "0x0", "maxPriorityFeePerGas": "0x0", - "v": "0x07", - "r": "0x07", - "s": "0x07" + "v": "0x0", + "r": "0x0", + "s": "0x0" } ``` @@ -2366,9 +2366,9 @@ Response: "gas": "0x5", "maxFeePerGas": "0x0", "maxPriorityFeePerGas": "0x0", - "v": "0x07", - "r": "0x07", - "s": "0x07" + "v": "0x0", + "r": "0x0", + "s": "0x0" } ``` @@ -2401,9 +2401,9 @@ Response: "gas": "0x5", "maxFeePerGas": "0x0", "maxPriorityFeePerGas": "0x0", - "v": "0x07", - "r": "0x07", - "s": "0x07" + "v": "0x0", + "r": "0x0", + "s": "0x0" } ``` diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index d6ec05875..13660c4b3 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -122,7 +122,15 @@ func (a *EthModule) StateNetworkName(ctx context.Context) (dtypes.NetworkName, e } func (a *EthModule) EthBlockNumber(context.Context) (ethtypes.EthUint64, error) { - height := a.Chain.GetHeaviestTipSet().Height() + // eth_blockNumber needs to return the height of the latest committed tipset. + // Ethereum clients expect all transactions included in this block to have execution outputs. + // This is the parent of the head tipset. The head tipset is speculative, has not been + // recognized by the network, and its messages are only included, not executed. + // See https://github.com/filecoin-project/ref-fvm/issues/1135. + height := a.Chain.GetHeaviestTipSet().Height() - 1 + if height < 0 { + height = 0 // genesis is the first ever committed tipset. + } return ethtypes.EthUint64(height), nil } @@ -172,22 +180,39 @@ func (a *EthModule) EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthH return newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.Chain, a.ChainAPI, a.StateAPI) } -func (a *EthModule) EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error) { - typ, num, err := ethtypes.ParseBlkNumOption(blkNum) - if err != nil { - return ethtypes.EthBlock{}, fmt.Errorf("cannot parse block number: %v", err) +func (a *EthModule) parseBlkParam(ctx context.Context, blkParam string) (tipset *types.TipSet, err error) { + if blkParam == "earliest" { + return nil, fmt.Errorf("block param \"earliest\" is not supported") } - switch typ { - case ethtypes.BlkNumLatest: - num = ethtypes.EthUint64(a.Chain.GetHeaviestTipSet().Height()) - 1 - case ethtypes.BlkNumPending: - num = ethtypes.EthUint64(a.Chain.GetHeaviestTipSet().Height()) + head := a.Chain.GetHeaviestTipSet() + switch blkParam { + case "pending": + return head, nil + case "latest": + parent, err := a.Chain.GetTipSetFromKey(ctx, head.Parents()) + if err != nil { + return nil, fmt.Errorf("cannot get parent tipset") + } + return parent, nil + default: + var num ethtypes.EthUint64 + err := num.UnmarshalJSON([]byte(`"` + blkParam + `"`)) + if err != nil { + return nil, fmt.Errorf("cannot parse block number: %v", err) + } + ts, err := a.Chain.GetTipsetByHeight(ctx, abi.ChainEpoch(num), nil, false) + if err != nil { + return nil, fmt.Errorf("cannot get tipset at height: %v", num) + } + return ts, nil } +} - ts, err := a.Chain.GetTipsetByHeight(ctx, abi.ChainEpoch(num), nil, false) +func (a *EthModule) EthGetBlockByNumber(ctx context.Context, blkParam string, fullTxInfo bool) (ethtypes.EthBlock, error) { + ts, err := a.parseBlkParam(ctx, blkParam) if err != nil { - return ethtypes.EthBlock{}, xerrors.Errorf("error loading tipset %s: %w", ts, err) + return ethtypes.EthBlock{}, err } return newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.Chain, a.ChainAPI, a.StateAPI) } @@ -200,16 +225,31 @@ func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtype cid := txHash.ToCid() + // first, try to get the cid from mined transactions msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, cid, api.LookbackNoLimit, true) - if err != nil { - return nil, nil + if err == nil { + tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, -1, a.Chain, a.ChainAPI, a.StateAPI) + if err == nil { + return &tx, nil + } } - tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, a.Chain, a.ChainAPI, a.StateAPI) + // if not found, try to get it from the mempool + pending, err := a.MpoolAPI.MpoolPending(ctx, types.EmptyTSK) if err != nil { - return nil, nil + return nil, fmt.Errorf("cannot get pending txs from mpool: %v", err) } - return &tx, nil + + for _, p := range pending { + if p.Cid() == cid { + tx, err := newEthTxFromFilecoinMessage(ctx, p, a.StateAPI) + if err != nil { + return nil, fmt.Errorf("cannot get parse message into tx: %v", err) + } + return &tx, nil + } + } + return nil, fmt.Errorf("cannot find cid %v from the mpool", cid) } func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkParam string) (ethtypes.EthUint64, error) { @@ -217,7 +257,13 @@ func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender ethtypes. if err != nil { return ethtypes.EthUint64(0), nil } - nonce, err := a.Mpool.GetNonce(ctx, addr, types.EmptyTSK) + + ts, err := a.parseBlkParam(ctx, blkParam) + if err != nil { + return ethtypes.EthUint64(0), xerrors.Errorf("cannot parse block param: %s", blkParam) + } + + nonce, err := a.Mpool.GetNonce(ctx, addr, ts.Key()) if err != nil { return ethtypes.EthUint64(0), nil } @@ -232,7 +278,7 @@ func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, txHash ethtype return nil, nil } - tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, a.Chain, a.ChainAPI, a.StateAPI) + tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, -1, a.Chain, a.ChainAPI, a.StateAPI) if err != nil { return nil, nil } @@ -267,7 +313,7 @@ func (a *EthModule) EthGetTransactionByBlockNumberAndIndex(ctx context.Context, } // EthGetCode returns string value of the compiled bytecode -func (a *EthModule) EthGetCode(ctx context.Context, ethAddr ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) { +func (a *EthModule) EthGetCode(ctx context.Context, ethAddr ethtypes.EthAddress, blkParam string) (ethtypes.EthBytes, error) { to, err := ethAddr.ToFilecoinAddress() if err != nil { return nil, xerrors.Errorf("cannot get Filecoin address: %w", err) @@ -289,7 +335,10 @@ func (a *EthModule) EthGetCode(ctx context.Context, ethAddr ethtypes.EthAddress, GasPremium: big.Zero(), } - ts := a.Chain.GetHeaviestTipSet() + ts, err := a.parseBlkParam(ctx, blkParam) + if err != nil { + return nil, xerrors.Errorf("cannot parse block param: %s", blkParam) + } // Try calling until we find a height with no migration. var res *api.InvocResult @@ -411,7 +460,12 @@ func (a *EthModule) EthGetBalance(ctx context.Context, address ethtypes.EthAddre return ethtypes.EthBigInt{}, err } - actor, err := a.StateGetActor(ctx, filAddr, types.EmptyTSK) + ts, err := a.parseBlkParam(ctx, blkParam) + if err != nil { + return ethtypes.EthBigInt{}, xerrors.Errorf("cannot parse block param: %s", blkParam) + } + + actor, err := a.StateGetActor(ctx, filAddr, ts.Key()) if xerrors.Is(err, types.ErrActorNotFound) { return ethtypes.EthBigIntZero, nil } else if err != nil { @@ -631,8 +685,11 @@ func (a *EthModule) ethCallToFilecoinMessage(ctx context.Context, tx ethtypes.Et }, nil } -func (a *EthModule) applyMessage(ctx context.Context, msg *types.Message) (res *api.InvocResult, err error) { - ts := a.Chain.GetHeaviestTipSet() +func (a *EthModule) applyMessage(ctx context.Context, msg *types.Message, tsk types.TipSetKey) (res *api.InvocResult, err error) { + ts, err := a.Chain.GetTipSetFromKey(ctx, tsk) + if err != nil { + return nil, xerrors.Errorf("cannot get tipset: %w", err) + } // Try calling until we find a height with no migration. for { @@ -677,8 +734,12 @@ func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam s if err != nil { return nil, err } + ts, err := a.parseBlkParam(ctx, blkParam) + if err != nil { + return nil, xerrors.Errorf("cannot parse block param: %s", blkParam) + } - invokeResult, err := a.applyMessage(ctx, msg) + invokeResult, err := a.applyMessage(ctx, msg, ts.Key()) if err != nil { return nil, err } @@ -1286,7 +1347,7 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx return ethtypes.EthBlock{}, err } - blkMsgs, err := cs.BlockMsgsForTipset(ctx, ts) + msgs, err := cs.MessagesForTipset(ctx, ts) if err != nil { return ethtypes.EthBlock{}, xerrors.Errorf("error loading messages for tipset: %v: %w", ts, err) } @@ -1295,27 +1356,25 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx // this seems to be a very expensive way to get gasUsed of the block. may need to find an efficient way to do it gasUsed := int64(0) - for _, blkMsg := range blkMsgs { - for _, msg := range append(blkMsg.BlsMessages, blkMsg.SecpkMessages...) { - msgLookup, err := sa.StateSearchMsg(ctx, types.EmptyTSK, msg.Cid(), api.LookbackNoLimit, true) - if err != nil || msgLookup == nil { + for txIdx, msg := range msgs { + msgLookup, err := sa.StateSearchMsg(ctx, types.EmptyTSK, msg.Cid(), api.LookbackNoLimit, false) + if err != nil || msgLookup == nil { + return ethtypes.EthBlock{}, nil + } + gasUsed += msgLookup.Receipt.GasUsed + + if fullTxInfo { + tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, txIdx, cs, ca, sa) + if err != nil { return ethtypes.EthBlock{}, nil } - gasUsed += msgLookup.Receipt.GasUsed - - if fullTxInfo { - tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, cs, ca, sa) - if err != nil { - return ethtypes.EthBlock{}, nil - } - block.Transactions = append(block.Transactions, tx) - } else { - hash, err := ethtypes.NewEthHashFromCid(msg.Cid()) - if err != nil { - return ethtypes.EthBlock{}, err - } - block.Transactions = append(block.Transactions, hash.String()) + block.Transactions = append(block.Transactions, tx) + } else { + hash, err := ethtypes.NewEthHashFromCid(msg.Cid()) + if err != nil { + return ethtypes.EthBlock{}, err } + block.Transactions = append(block.Transactions, hash.String()) } } @@ -1372,7 +1431,74 @@ func lookupEthAddress(ctx context.Context, addr address.Address, sa StateAPI) (e return ethtypes.EthAddressFromFilecoinAddress(idAddr) } -func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, cs *store.ChainStore, ca ChainAPI, sa StateAPI) (ethtypes.EthTx, error) { +func newEthTxFromFilecoinMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthTx, error) { + fromEthAddr, err := lookupEthAddress(ctx, smsg.Message.From, sa) + if err != nil { + return ethtypes.EthTx{}, err + } + + toEthAddr, err := lookupEthAddress(ctx, smsg.Message.To, sa) + if err != nil { + return ethtypes.EthTx{}, err + } + + toAddr := &toEthAddr + input := smsg.Message.Params + // Check to see if we need to decode as contract deployment. + // We don't need to resolve the to address, because there's only one form (an ID). + if smsg.Message.To == builtintypes.EthereumAddressManagerActorAddr { + switch smsg.Message.Method { + case builtintypes.MethodsEAM.Create: + toAddr = nil + var params eam.CreateParams + err = params.UnmarshalCBOR(bytes.NewReader(smsg.Message.Params)) + input = params.Initcode + case builtintypes.MethodsEAM.Create2: + toAddr = nil + var params eam.Create2Params + err = params.UnmarshalCBOR(bytes.NewReader(smsg.Message.Params)) + input = params.Initcode + } + if err != nil { + return ethtypes.EthTx{}, err + } + } + // Otherwise, try to decode as a cbor byte array. + // TODO: Actually check if this is an ethereum call. This code will work for demo purposes, but is not correct. + if toAddr != nil { + if decodedParams, err := cbg.ReadByteArray(bytes.NewReader(smsg.Message.Params), uint64(len(smsg.Message.Params))); err == nil { + input = decodedParams + } + } + + r, s, v, err := ethtypes.RecoverSignature(smsg.Signature) + if err != nil { + // we don't want to return error if the message is not an Eth tx + r, s, v = ethtypes.EthBigIntZero, ethtypes.EthBigIntZero, ethtypes.EthBigIntZero + } + + tx := ethtypes.EthTx{ + ChainID: ethtypes.EthUint64(build.Eip155ChainId), + From: fromEthAddr, + To: toAddr, + Value: ethtypes.EthBigInt(smsg.Message.Value), + Type: ethtypes.EthUint64(2), + Gas: ethtypes.EthUint64(smsg.Message.GasLimit), + MaxFeePerGas: ethtypes.EthBigInt(smsg.Message.GasFeeCap), + MaxPriorityFeePerGas: ethtypes.EthBigInt(smsg.Message.GasPremium), + V: v, + R: r, + S: s, + Input: input, + } + + return tx, nil +} + +// newEthTxFromFilecoinMessageLookup creates an ethereum transaction from filecoin message lookup. If a negative txIdx is passed +// into the function, it looksup the transaction index of the message in the tipset, otherwise it uses the txIdx passed into the +// function +func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, txIdx int, cs *store.ChainStore, ca ChainAPI, sa StateAPI) (ethtypes.EthTx, error) { if msgLookup == nil { return ethtypes.EthTx{}, fmt.Errorf("msg does not exist") } @@ -1399,18 +1525,19 @@ func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLo } // lookup the transactionIndex - txIdx := -1 - msgs, err := cs.MessagesForTipset(ctx, parentTs) - if err != nil { - return ethtypes.EthTx{}, err - } - for i, msg := range msgs { - if msg.Cid() == msgLookup.Message { - txIdx = i + if txIdx < 0 { + msgs, err := cs.MessagesForTipset(ctx, parentTs) + if err != nil { + return ethtypes.EthTx{}, err + } + for i, msg := range msgs { + if msg.Cid() == msgLookup.Message { + txIdx = i + } + } + if txIdx < 0 { + return ethtypes.EthTx{}, fmt.Errorf("cannot find the msg in the tipset") } - } - if txIdx == -1 { - return ethtypes.EthTx{}, fmt.Errorf("cannot find the msg in the tipset") } blkHash, err := ethtypes.NewEthHashFromCid(parentTsCid) @@ -1418,68 +1545,21 @@ func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLo return ethtypes.EthTx{}, err } - msg, err := ca.ChainGetMessage(ctx, msgLookup.Message) + smsg, err := cs.GetSignedMessage(ctx, msgLookup.Message) if err != nil { return ethtypes.EthTx{}, err } - fromEthAddr, err := lookupEthAddress(ctx, msg.From, sa) + tx, err := newEthTxFromFilecoinMessage(ctx, smsg, sa) if err != nil { return ethtypes.EthTx{}, err } - toEthAddr, err := lookupEthAddress(ctx, msg.To, sa) - if err != nil { - return ethtypes.EthTx{}, err - } - - toAddr := &toEthAddr - input := msg.Params - // Check to see if we need to decode as contract deployment. - // We don't need to resolve the to address, because there's only one form (an ID). - if msg.To == builtintypes.EthereumAddressManagerActorAddr { - switch msg.Method { - case builtintypes.MethodsEAM.Create: - toAddr = nil - var params eam.CreateParams - err = params.UnmarshalCBOR(bytes.NewReader(msg.Params)) - input = params.Initcode - case builtintypes.MethodsEAM.Create2: - toAddr = nil - var params eam.Create2Params - err = params.UnmarshalCBOR(bytes.NewReader(msg.Params)) - input = params.Initcode - } - if err != nil { - return ethtypes.EthTx{}, err - } - } - // Otherwise, try to decode as a cbor byte array. - // TODO: Actually check if this is an ethereum call. This code will work for demo purposes, but is not correct. - if toAddr != nil { - if decodedParams, err := cbg.ReadByteArray(bytes.NewReader(msg.Params), uint64(len(msg.Params))); err == nil { - input = decodedParams - } - } - - tx := ethtypes.EthTx{ - ChainID: ethtypes.EthUint64(build.Eip155ChainId), - Hash: txHash, - BlockHash: blkHash, - BlockNumber: ethtypes.EthUint64(parentTs.Height()), - From: fromEthAddr, - To: toAddr, - Value: ethtypes.EthBigInt(msg.Value), - Type: ethtypes.EthUint64(2), - TransactionIndex: ethtypes.EthUint64(txIdx), - Gas: ethtypes.EthUint64(msg.GasLimit), - MaxFeePerGas: ethtypes.EthBigInt(msg.GasFeeCap), - MaxPriorityFeePerGas: ethtypes.EthBigInt(msg.GasPremium), - V: ethtypes.EthBytes{}, - R: ethtypes.EthBytes{}, - S: ethtypes.EthBytes{}, - Input: input, - } + tx.ChainID = ethtypes.EthUint64(build.Eip155ChainId) + tx.Hash = txHash + tx.BlockHash = blkHash + tx.BlockNumber = ethtypes.EthUint64(parentTs.Height()) + tx.TransactionIndex = ethtypes.EthUint64(txIdx) return tx, nil }