From 20f27af97ef721f1782bd0b4b9a9576c39dcebe8 Mon Sep 17 00:00:00 2001 From: ychiao Date: Tue, 20 Dec 2022 00:45:32 +0800 Subject: [PATCH] eth: rpc: various Ethereum JSON-RPC API fixes (#9837) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Raúl Kripalani Fixes https://github.com/filecoin-project/ref-fvm/issues/1016 Fixes https://github.com/filecoin-project/ref-fvm/issues/1158 Fixes https://github.com/filecoin-project/ref-fvm/issues/1196 Fixes https://github.com/filecoin-project/ref-fvm/issues/1269 Fixes https://github.com/filecoin-project/lotus/issues/9820 --- build/openrpc/full.json.gz | Bin 32232 -> 32207 bytes chain/types/ethtypes/eth_transactions.go | 69 ++-- chain/types/ethtypes/eth_transactions_test.go | 60 ++++ chain/types/ethtypes/eth_types.go | 7 +- documentation/en/api-v1-unstable-methods.md | 18 +- node/impl/full/eth.go | 300 +++++++++++------- 6 files changed, 312 insertions(+), 142 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index df5b7dd956b7f47c829a59696ed0c3fa3e7e778e..c3b955b3de593a0f8d988be5b8d085cb828fcf50 100644 GIT binary patch delta 30072 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 W&c&7N-~R^y0RR8)x2ZR^Uj_i2zjinP 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 }