eth: rpc: various Ethereum JSON-RPC API fixes (#9837)
Co-authored-by: Raúl Kripalani <raul@protocol.ai> 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
This commit is contained in:
parent
5f4ccfd790
commit
20f27af97e
Binary file not shown.
@ -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
|
||||
}
|
||||
|
@ -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"),
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user