feat: ethrpc: implement call, estimateGas, getTransactionCount (#9306)

This commit is contained in:
Kevin Li 2022-09-28 10:58:24 -04:00 committed by vyzo
parent e5bb5b7430
commit b53d5924a1
7 changed files with 352 additions and 90 deletions

View File

@ -774,7 +774,7 @@ type FullNode interface {
EthGetBlockByNumber(ctx context.Context, blkNum EthInt, fullTxInfo bool) (EthBlock, error) //perm:read EthGetBlockByNumber(ctx context.Context, blkNum EthInt, fullTxInfo bool) (EthBlock, error) //perm:read
EthGetTransactionByHash(ctx context.Context, txHash EthHash) (EthTx, error) //perm:read EthGetTransactionByHash(ctx context.Context, txHash EthHash) (EthTx, error) //perm:read
EthGetTransactionCount(ctx context.Context, sender EthAddress, blkOpt string) (EthInt, error) //perm:read EthGetTransactionCount(ctx context.Context, sender EthAddress, blkOpt string) (EthInt, error) //perm:read
EthGetTransactionReceipt(ctx context.Context, blkHash EthHash) (EthTxReceipt, error) //perm:read EthGetTransactionReceipt(ctx context.Context, txHash EthHash) (EthTxReceipt, error) //perm:read
EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash EthHash, txIndex EthInt) (EthTx, error) //perm:read EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash EthHash, txIndex EthInt) (EthTx, error) //perm:read
EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum EthInt, txIndex EthInt) (EthTx, error) //perm:read EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum EthInt, txIndex EthInt) (EthTx, error) //perm:read
@ -785,10 +785,13 @@ type FullNode interface {
NetVersion(ctx context.Context) (string, error) //perm:read NetVersion(ctx context.Context) (string, error) //perm:read
NetListening(ctx context.Context) (bool, error) //perm:read NetListening(ctx context.Context) (bool, error) //perm:read
EthProtocolVersion(ctx context.Context) (EthInt, error) //perm:read EthProtocolVersion(ctx context.Context) (EthInt, error) //perm:read
EthGasPrice(ctx context.Context) (EthInt, error) //perm:read EthGasPrice(ctx context.Context) (EthBigInt, error) //perm:read
EthMaxPriorityFeePerGas(ctx context.Context) (EthInt, error) //perm:read
EthMaxPriorityFeePerGas(ctx context.Context) (EthBigInt, error) //perm:read
EthEstimateGas(ctx context.Context, tx EthCall, blkParam string) (EthInt, error) //perm:read EthEstimateGas(ctx context.Context, tx EthCall, blkParam string) (EthInt, error) //perm:read
EthCall(ctx context.Context, tx EthCall, blkParam string) (string, error) //perm:read EthCall(ctx context.Context, tx EthCall, blkParam string) (EthBytes, error) //perm:read
EthSendRawTransaction(ctx context.Context, rawTx EthBytes) (EthHash, error) //perm:read
// CreateBackup creates node backup onder the specified file name. The // CreateBackup creates node backup onder the specified file name. The
// method requires that the lotus daemon is running with the // method requires that the lotus daemon is running with the

View File

@ -1,6 +1,7 @@
package api package api
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
@ -15,6 +16,7 @@ import (
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/big"
init8 "github.com/filecoin-project/go-state-types/builtin/v8/init"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
) )
@ -70,6 +72,33 @@ func (e *EthBigInt) UnmarshalJSON(b []byte) error {
return nil return nil
} }
type EthBytes []byte
func (e EthBytes) MarshalJSON() ([]byte, error) {
encoded := "0x" + hex.EncodeToString(e)
return json.Marshal(encoded)
}
func (e *EthBytes) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
s = strings.Replace(s, "0x", "", -1)
if len(s)%2 == 1 {
s = "0" + s
}
decoded, err := hex.DecodeString(s)
if err != nil {
return err
}
*e = decoded
return nil
}
type EthBlock struct { type EthBlock struct {
ParentHash EthHash `json:"parentHash"` ParentHash EthHash `json:"parentHash"`
Sha3Uncles EthHash `json:"sha3Uncles"` Sha3Uncles EthHash `json:"sha3Uncles"`
@ -123,10 +152,10 @@ type EthTx struct {
BlockNumber EthInt `json:"blockNumber"` BlockNumber EthInt `json:"blockNumber"`
TransactionIndex EthInt `json:"transacionIndex"` TransactionIndex EthInt `json:"transacionIndex"`
From EthAddress `json:"from"` From EthAddress `json:"from"`
To EthAddress `json:"to"` To *EthAddress `json:"to"`
Value EthBigInt `json:"value"` Value EthBigInt `json:"value"`
Type EthInt `json:"type"` Type EthInt `json:"type"`
Input []byte `json:"input"` Input EthBytes `json:"input"`
Gas EthInt `json:"gas"` Gas EthInt `json:"gas"`
MaxFeePerGas EthBigInt `json:"maxFeePerGas"` MaxFeePerGas EthBigInt `json:"maxFeePerGas"`
MaxPriorityFeePerGas EthBigInt `json:"maxPriorityFeePerGas"` MaxPriorityFeePerGas EthBigInt `json:"maxPriorityFeePerGas"`
@ -141,7 +170,7 @@ type EthCall struct {
Gas EthInt `json:"gas"` Gas EthInt `json:"gas"`
GasPrice EthBigInt `json:"gasPrice"` GasPrice EthBigInt `json:"gasPrice"`
Value EthBigInt `json:"value"` Value EthBigInt `json:"value"`
Data []byte `json:"data"` Data EthBytes `json:"data"`
} }
func (c *EthCall) UnmarshalJSON(b []byte) error { func (c *EthCall) UnmarshalJSON(b []byte) error {
@ -159,19 +188,68 @@ type EthTxReceipt struct {
TransactionHash EthHash `json:"transactionHash"` TransactionHash EthHash `json:"transactionHash"`
TransactionIndex EthInt `json:"transacionIndex"` TransactionIndex EthInt `json:"transacionIndex"`
BlockHash EthHash `json:"blockHash"` BlockHash EthHash `json:"blockHash"`
BlockNumber EthHash `json:"blockNumber"` BlockNumber EthInt `json:"blockNumber"`
From EthAddress `json:"from"` From EthAddress `json:"from"`
To EthAddress `json:"to"` To *EthAddress `json:"to"`
// Logs // Logs
// LogsBloom // LogsBloom
StateRoot EthHash `json:"root"` StateRoot EthHash `json:"root"`
Status EthInt `json:"status"` Status EthInt `json:"status"`
ContractAddress *EthAddress `json:"contractAddress"` ContractAddress *EthAddress `json:"contractAddress"`
CumulativeGasUsed EthBigInt `json:"cumulativeGasUsed"` CumulativeGasUsed EthInt `json:"cumulativeGasUsed"`
GasUsed EthBigInt `json:"gasUsed"` GasUsed EthInt `json:"gasUsed"`
EffectiveGasPrice EthBigInt `json:"effectiveGasPrice"` EffectiveGasPrice EthBigInt `json:"effectiveGasPrice"`
} }
func NewEthTxReceipt(tx EthTx, lookup *MsgLookup, replay *InvocResult) (EthTxReceipt, error) {
receipt := EthTxReceipt{
TransactionHash: tx.Hash,
TransactionIndex: tx.TransactionIndex,
BlockHash: tx.BlockHash,
BlockNumber: tx.BlockNumber,
From: tx.From,
To: tx.To,
StateRoot: EmptyEthHash,
}
contractAddr, err := CheckContractCreation(lookup)
if err == nil {
receipt.To = nil
receipt.ContractAddress = contractAddr
}
if lookup.Receipt.ExitCode.IsSuccess() {
receipt.Status = 1
}
if lookup.Receipt.ExitCode.IsError() {
receipt.Status = 0
}
receipt.GasUsed = EthInt(lookup.Receipt.GasUsed)
// TODO: handle CumulativeGasUsed
receipt.CumulativeGasUsed = EmptyEthInt
effectiveGasPrice := big.Div(replay.GasCost.TotalCost, big.NewInt(lookup.Receipt.GasUsed))
receipt.EffectiveGasPrice = EthBigInt(effectiveGasPrice)
return receipt, nil
}
func CheckContractCreation(lookup *MsgLookup) (*EthAddress, error) {
if lookup.Receipt.ExitCode.IsError() {
return nil, xerrors.Errorf("message execution was not successful")
}
var result init8.ExecReturn
ret := bytes.NewReader(lookup.Receipt.Return)
if err := result.UnmarshalCBOR(ret); err == nil {
contractAddr, err := EthAddressFromFilecoinIDAddress(result.IDAddress)
if err == nil {
return &contractAddr, nil
}
}
return nil, xerrors.Errorf("not a contract creation tx")
}
const ( const (
ETH_ADDRESS_LENGTH = 20 ETH_ADDRESS_LENGTH = 20
ETH_HASH_LENGTH = 32 ETH_HASH_LENGTH = 32

View File

@ -136,3 +136,22 @@ func TestUnmarshalEthCall(t *testing.T) {
err := c.UnmarshalJSON([]byte(data)) err := c.UnmarshalJSON([]byte(data))
require.Nil(t, err) require.Nil(t, err)
} }
func TestUnmarshalEthBytes(t *testing.T) {
testcases := []string{
`"0x00"`,
strings.ToLower(`"0xd4c5fb16488Aa48081296299d54b0c648C9333dA"`),
strings.ToLower(`"0x2C2EC67e3e1FeA8e4A39601cB3A3Cd44f5fa830d"`),
strings.ToLower(`"0x01184F793982104363F9a8a5845743f452dE0586"`),
}
for _, tc := range testcases {
var s EthBytes
err := s.UnmarshalJSON([]byte(tc))
require.Nil(t, err)
data, err := s.MarshalJSON()
require.Nil(t, err)
require.Equal(t, string(data), tc)
}
}

View File

@ -952,10 +952,10 @@ func (mr *MockFullNodeMockRecorder) EthBlockNumber(arg0 interface{}) *gomock.Cal
} }
// EthCall mocks base method. // EthCall mocks base method.
func (m *MockFullNode) EthCall(arg0 context.Context, arg1 api.EthCall, arg2 string) (string, error) { func (m *MockFullNode) EthCall(arg0 context.Context, arg1 api.EthCall, arg2 string) (api.EthBytes, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EthCall", arg0, arg1, arg2) ret := m.ctrl.Call(m, "EthCall", arg0, arg1, arg2)
ret0, _ := ret[0].(string) ret0, _ := ret[0].(api.EthBytes)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
@ -997,10 +997,10 @@ func (mr *MockFullNodeMockRecorder) EthEstimateGas(arg0, arg1, arg2 interface{})
} }
// EthGasPrice mocks base method. // EthGasPrice mocks base method.
func (m *MockFullNode) EthGasPrice(arg0 context.Context) (api.EthInt, error) { func (m *MockFullNode) EthGasPrice(arg0 context.Context) (api.EthBigInt, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EthGasPrice", arg0) ret := m.ctrl.Call(m, "EthGasPrice", arg0)
ret0, _ := ret[0].(api.EthInt) ret0, _ := ret[0].(api.EthBigInt)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
@ -1192,10 +1192,10 @@ func (mr *MockFullNodeMockRecorder) EthGetTransactionReceipt(arg0, arg1 interfac
} }
// EthMaxPriorityFeePerGas mocks base method. // EthMaxPriorityFeePerGas mocks base method.
func (m *MockFullNode) EthMaxPriorityFeePerGas(arg0 context.Context) (api.EthInt, error) { func (m *MockFullNode) EthMaxPriorityFeePerGas(arg0 context.Context) (api.EthBigInt, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EthMaxPriorityFeePerGas", arg0) ret := m.ctrl.Call(m, "EthMaxPriorityFeePerGas", arg0)
ret0, _ := ret[0].(api.EthInt) ret0, _ := ret[0].(api.EthBigInt)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
@ -1221,6 +1221,21 @@ func (mr *MockFullNodeMockRecorder) EthProtocolVersion(arg0 interface{}) *gomock
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthProtocolVersion", reflect.TypeOf((*MockFullNode)(nil).EthProtocolVersion), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthProtocolVersion", reflect.TypeOf((*MockFullNode)(nil).EthProtocolVersion), arg0)
} }
// EthSendRawTransaction mocks base method.
func (m *MockFullNode) EthSendRawTransaction(arg0 context.Context, arg1 api.EthBytes) (api.EthHash, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EthSendRawTransaction", arg0, arg1)
ret0, _ := ret[0].(api.EthHash)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// EthSendRawTransaction indicates an expected call of EthSendRawTransaction.
func (mr *MockFullNodeMockRecorder) EthSendRawTransaction(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthSendRawTransaction", reflect.TypeOf((*MockFullNode)(nil).EthSendRawTransaction), arg0, arg1)
}
// GasEstimateFeeCap mocks base method. // GasEstimateFeeCap mocks base method.
func (m *MockFullNode) GasEstimateFeeCap(arg0 context.Context, arg1 *types.Message, arg2 int64, arg3 types.TipSetKey) (big.Int, error) { func (m *MockFullNode) GasEstimateFeeCap(arg0 context.Context, arg1 *types.Message, arg2 int64, arg3 types.TipSetKey) (big.Int, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@ -222,13 +222,13 @@ type FullNodeStruct struct {
EthBlockNumber func(p0 context.Context) (EthInt, error) `perm:"read"` EthBlockNumber func(p0 context.Context) (EthInt, error) `perm:"read"`
EthCall func(p0 context.Context, p1 EthCall, p2 string) (string, error) `perm:"read"` EthCall func(p0 context.Context, p1 EthCall, p2 string) (EthBytes, error) `perm:"read"`
EthChainId func(p0 context.Context) (EthInt, error) `perm:"read"` EthChainId func(p0 context.Context) (EthInt, error) `perm:"read"`
EthEstimateGas func(p0 context.Context, p1 EthCall, p2 string) (EthInt, error) `perm:"read"` EthEstimateGas func(p0 context.Context, p1 EthCall, p2 string) (EthInt, error) `perm:"read"`
EthGasPrice func(p0 context.Context) (EthInt, error) `perm:"read"` EthGasPrice func(p0 context.Context) (EthBigInt, error) `perm:"read"`
EthGetBalance func(p0 context.Context, p1 EthAddress, p2 string) (EthBigInt, error) `perm:"read"` EthGetBalance func(p0 context.Context, p1 EthAddress, p2 string) (EthBigInt, error) `perm:"read"`
@ -254,10 +254,12 @@ type FullNodeStruct struct {
EthGetTransactionReceipt func(p0 context.Context, p1 EthHash) (EthTxReceipt, error) `perm:"read"` EthGetTransactionReceipt func(p0 context.Context, p1 EthHash) (EthTxReceipt, error) `perm:"read"`
EthMaxPriorityFeePerGas func(p0 context.Context) (EthInt, error) `perm:"read"` EthMaxPriorityFeePerGas func(p0 context.Context) (EthBigInt, error) `perm:"read"`
EthProtocolVersion func(p0 context.Context) (EthInt, error) `perm:"read"` EthProtocolVersion func(p0 context.Context) (EthInt, error) `perm:"read"`
EthSendRawTransaction func(p0 context.Context, p1 EthBytes) (EthHash, error) `perm:"read"`
GasEstimateFeeCap func(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` GasEstimateFeeCap func(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"`
GasEstimateGasLimit func(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (int64, error) `perm:"read"` GasEstimateGasLimit func(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (int64, error) `perm:"read"`
@ -1857,15 +1859,15 @@ func (s *FullNodeStub) EthBlockNumber(p0 context.Context) (EthInt, error) {
return *new(EthInt), ErrNotSupported return *new(EthInt), ErrNotSupported
} }
func (s *FullNodeStruct) EthCall(p0 context.Context, p1 EthCall, p2 string) (string, error) { func (s *FullNodeStruct) EthCall(p0 context.Context, p1 EthCall, p2 string) (EthBytes, error) {
if s.Internal.EthCall == nil { if s.Internal.EthCall == nil {
return "", ErrNotSupported return *new(EthBytes), ErrNotSupported
} }
return s.Internal.EthCall(p0, p1, p2) return s.Internal.EthCall(p0, p1, p2)
} }
func (s *FullNodeStub) EthCall(p0 context.Context, p1 EthCall, p2 string) (string, error) { func (s *FullNodeStub) EthCall(p0 context.Context, p1 EthCall, p2 string) (EthBytes, error) {
return "", ErrNotSupported return *new(EthBytes), ErrNotSupported
} }
func (s *FullNodeStruct) EthChainId(p0 context.Context) (EthInt, error) { func (s *FullNodeStruct) EthChainId(p0 context.Context) (EthInt, error) {
@ -1890,15 +1892,15 @@ func (s *FullNodeStub) EthEstimateGas(p0 context.Context, p1 EthCall, p2 string)
return *new(EthInt), ErrNotSupported return *new(EthInt), ErrNotSupported
} }
func (s *FullNodeStruct) EthGasPrice(p0 context.Context) (EthInt, error) { func (s *FullNodeStruct) EthGasPrice(p0 context.Context) (EthBigInt, error) {
if s.Internal.EthGasPrice == nil { if s.Internal.EthGasPrice == nil {
return *new(EthInt), ErrNotSupported return *new(EthBigInt), ErrNotSupported
} }
return s.Internal.EthGasPrice(p0) return s.Internal.EthGasPrice(p0)
} }
func (s *FullNodeStub) EthGasPrice(p0 context.Context) (EthInt, error) { func (s *FullNodeStub) EthGasPrice(p0 context.Context) (EthBigInt, error) {
return *new(EthInt), ErrNotSupported return *new(EthBigInt), ErrNotSupported
} }
func (s *FullNodeStruct) EthGetBalance(p0 context.Context, p1 EthAddress, p2 string) (EthBigInt, error) { func (s *FullNodeStruct) EthGetBalance(p0 context.Context, p1 EthAddress, p2 string) (EthBigInt, error) {
@ -2033,15 +2035,15 @@ func (s *FullNodeStub) EthGetTransactionReceipt(p0 context.Context, p1 EthHash)
return *new(EthTxReceipt), ErrNotSupported return *new(EthTxReceipt), ErrNotSupported
} }
func (s *FullNodeStruct) EthMaxPriorityFeePerGas(p0 context.Context) (EthInt, error) { func (s *FullNodeStruct) EthMaxPriorityFeePerGas(p0 context.Context) (EthBigInt, error) {
if s.Internal.EthMaxPriorityFeePerGas == nil { if s.Internal.EthMaxPriorityFeePerGas == nil {
return *new(EthInt), ErrNotSupported return *new(EthBigInt), ErrNotSupported
} }
return s.Internal.EthMaxPriorityFeePerGas(p0) return s.Internal.EthMaxPriorityFeePerGas(p0)
} }
func (s *FullNodeStub) EthMaxPriorityFeePerGas(p0 context.Context) (EthInt, error) { func (s *FullNodeStub) EthMaxPriorityFeePerGas(p0 context.Context) (EthBigInt, error) {
return *new(EthInt), ErrNotSupported return *new(EthBigInt), ErrNotSupported
} }
func (s *FullNodeStruct) EthProtocolVersion(p0 context.Context) (EthInt, error) { func (s *FullNodeStruct) EthProtocolVersion(p0 context.Context) (EthInt, error) {
@ -2055,6 +2057,17 @@ func (s *FullNodeStub) EthProtocolVersion(p0 context.Context) (EthInt, error) {
return *new(EthInt), ErrNotSupported return *new(EthInt), ErrNotSupported
} }
func (s *FullNodeStruct) EthSendRawTransaction(p0 context.Context, p1 EthBytes) (EthHash, error) {
if s.Internal.EthSendRawTransaction == nil {
return *new(EthHash), ErrNotSupported
}
return s.Internal.EthSendRawTransaction(p0, p1)
}
func (s *FullNodeStub) EthSendRawTransaction(p0 context.Context, p1 EthBytes) (EthHash, error) {
return *new(EthHash), ErrNotSupported
}
func (s *FullNodeStruct) GasEstimateFeeCap(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) { func (s *FullNodeStruct) GasEstimateFeeCap(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) {
if s.Internal.GasEstimateFeeCap == nil { if s.Internal.GasEstimateFeeCap == nil {
return *new(types.BigInt), ErrNotSupported return *new(types.BigInt), ErrNotSupported

View File

@ -86,6 +86,7 @@
* [EthGetTransactionReceipt](#EthGetTransactionReceipt) * [EthGetTransactionReceipt](#EthGetTransactionReceipt)
* [EthMaxPriorityFeePerGas](#EthMaxPriorityFeePerGas) * [EthMaxPriorityFeePerGas](#EthMaxPriorityFeePerGas)
* [EthProtocolVersion](#EthProtocolVersion) * [EthProtocolVersion](#EthProtocolVersion)
* [EthSendRawTransaction](#EthSendRawTransaction)
* [Gas](#Gas) * [Gas](#Gas)
* [GasEstimateFeeCap](#GasEstimateFeeCap) * [GasEstimateFeeCap](#GasEstimateFeeCap)
* [GasEstimateGasLimit](#GasEstimateGasLimit) * [GasEstimateGasLimit](#GasEstimateGasLimit)
@ -2189,13 +2190,13 @@ Inputs:
"gas": "0x5", "gas": "0x5",
"gasPrice": "0x0", "gasPrice": "0x0",
"value": "0x0", "value": "0x0",
"data": "Ynl0ZSBhcnJheQ==" "data": "0x07"
}, },
"string value" "string value"
] ]
``` ```
Response: `"string value"` Response: `"0x07"`
### EthChainId ### EthChainId
@ -2220,7 +2221,7 @@ Inputs:
"gas": "0x5", "gas": "0x5",
"gasPrice": "0x0", "gasPrice": "0x0",
"value": "0x0", "value": "0x0",
"data": "Ynl0ZSBhcnJheQ==" "data": "0x07"
}, },
"string value" "string value"
] ]
@ -2235,7 +2236,7 @@ Perms: read
Inputs: `null` Inputs: `null`
Response: `"0x5"` Response: `"0x0"`
### EthGetBalance ### EthGetBalance
@ -2417,10 +2418,10 @@ Response:
"blockNumber": "0x5", "blockNumber": "0x5",
"transacionIndex": "0x5", "transacionIndex": "0x5",
"from": "0x0707070707070707070707070707070707070707", "from": "0x0707070707070707070707070707070707070707",
"to": "0x0707070707070707070707070707070707070707", "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031",
"value": "0x0", "value": "0x0",
"type": "0x5", "type": "0x5",
"input": "Ynl0ZSBhcnJheQ==", "input": "0x07",
"gas": "0x5", "gas": "0x5",
"maxFeePerGas": "0x0", "maxFeePerGas": "0x0",
"maxPriorityFeePerGas": "0x0", "maxPriorityFeePerGas": "0x0",
@ -2453,10 +2454,10 @@ Response:
"blockNumber": "0x5", "blockNumber": "0x5",
"transacionIndex": "0x5", "transacionIndex": "0x5",
"from": "0x0707070707070707070707070707070707070707", "from": "0x0707070707070707070707070707070707070707",
"to": "0x0707070707070707070707070707070707070707", "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031",
"value": "0x0", "value": "0x0",
"type": "0x5", "type": "0x5",
"input": "Ynl0ZSBhcnJheQ==", "input": "0x07",
"gas": "0x5", "gas": "0x5",
"maxFeePerGas": "0x0", "maxFeePerGas": "0x0",
"maxPriorityFeePerGas": "0x0", "maxPriorityFeePerGas": "0x0",
@ -2488,10 +2489,10 @@ Response:
"blockNumber": "0x5", "blockNumber": "0x5",
"transacionIndex": "0x5", "transacionIndex": "0x5",
"from": "0x0707070707070707070707070707070707070707", "from": "0x0707070707070707070707070707070707070707",
"to": "0x0707070707070707070707070707070707070707", "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031",
"value": "0x0", "value": "0x0",
"type": "0x5", "type": "0x5",
"input": "Ynl0ZSBhcnJheQ==", "input": "0x07",
"gas": "0x5", "gas": "0x5",
"maxFeePerGas": "0x0", "maxFeePerGas": "0x0",
"maxPriorityFeePerGas": "0x0", "maxPriorityFeePerGas": "0x0",
@ -2534,14 +2535,14 @@ Response:
"transactionHash": "0x0707070707070707070707070707070707070707070707070707070707070707", "transactionHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"transacionIndex": "0x5", "transacionIndex": "0x5",
"blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707", "blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockNumber": "0x0707070707070707070707070707070707070707070707070707070707070707", "blockNumber": "0x5",
"from": "0x0707070707070707070707070707070707070707", "from": "0x0707070707070707070707070707070707070707",
"to": "0x0707070707070707070707070707070707070707", "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031",
"root": "0x0707070707070707070707070707070707070707070707070707070707070707", "root": "0x0707070707070707070707070707070707070707070707070707070707070707",
"status": "0x5", "status": "0x5",
"contractAddress": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", "contractAddress": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031",
"cumulativeGasUsed": "0x0", "cumulativeGasUsed": "0x5",
"gasUsed": "0x0", "gasUsed": "0x5",
"effectiveGasPrice": "0x0" "effectiveGasPrice": "0x0"
} }
``` ```
@ -2553,7 +2554,7 @@ Perms: read
Inputs: `null` Inputs: `null`
Response: `"0x5"` Response: `"0x0"`
### EthProtocolVersion ### EthProtocolVersion
@ -2564,6 +2565,20 @@ Inputs: `null`
Response: `"0x5"` Response: `"0x5"`
### EthSendRawTransaction
Perms: read
Inputs:
```json
[
"0x07"
]
```
Response: `"0x0707070707070707070707070707070707070707070707070707070707070707"`
## Gas ## Gas

View File

@ -8,11 +8,17 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/modules/dtypes"
) )
type EthModuleAPI interface { type EthModuleAPI interface {
@ -24,7 +30,7 @@ type EthModuleAPI interface {
EthGetBlockByNumber(ctx context.Context, blkNum api.EthInt, fullTxInfo bool) (api.EthBlock, error) EthGetBlockByNumber(ctx context.Context, blkNum api.EthInt, fullTxInfo bool) (api.EthBlock, error)
EthGetTransactionByHash(ctx context.Context, txHash api.EthHash) (api.EthTx, error) EthGetTransactionByHash(ctx context.Context, txHash api.EthHash) (api.EthTx, error)
EthGetTransactionCount(ctx context.Context, sender api.EthAddress, blkOpt string) (api.EthInt, error) EthGetTransactionCount(ctx context.Context, sender api.EthAddress, blkOpt string) (api.EthInt, error)
EthGetTransactionReceipt(ctx context.Context, blkHash api.EthHash) (api.EthTxReceipt, error) EthGetTransactionReceipt(ctx context.Context, txHash api.EthHash) (api.EthTxReceipt, error)
EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash api.EthHash, txIndex api.EthInt) (api.EthTx, error) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash api.EthHash, txIndex api.EthInt) (api.EthTx, error)
EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum api.EthInt, txIndex api.EthInt) (api.EthTx, error) EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum api.EthInt, txIndex api.EthInt) (api.EthTx, error)
EthGetCode(ctx context.Context, address api.EthAddress) (string, error) EthGetCode(ctx context.Context, address api.EthAddress) (string, error)
@ -34,11 +40,11 @@ type EthModuleAPI interface {
NetVersion(ctx context.Context) (string, error) NetVersion(ctx context.Context) (string, error)
NetListening(ctx context.Context) (bool, error) NetListening(ctx context.Context) (bool, error)
EthProtocolVersion(ctx context.Context) (api.EthInt, error) EthProtocolVersion(ctx context.Context) (api.EthInt, error)
EthGasPrice(ctx context.Context) (api.EthInt, error) EthGasPrice(ctx context.Context) (api.EthBigInt, error)
EthEstimateGas(ctx context.Context, tx api.EthCall, blkParam string) (api.EthInt, error) EthEstimateGas(ctx context.Context, tx api.EthCall, blkParam string) (api.EthInt, error)
EthCall(ctx context.Context, tx api.EthCall, blkParam string) (string, error) EthCall(ctx context.Context, tx api.EthCall, blkParam string) (api.EthBytes, error)
EthMaxPriorityFeePerGas(ctx context.Context) (api.EthInt, error) EthMaxPriorityFeePerGas(ctx context.Context) (api.EthBigInt, error)
// EthSendRawTransaction(ctx context.Context, tx api.EthTx) (api.EthHash, error) EthSendRawTransaction(ctx context.Context, rawTx api.EthBytes) (api.EthHash, error)
} }
var _ EthModuleAPI = *new(api.FullNode) var _ EthModuleAPI = *new(api.FullNode)
@ -50,8 +56,11 @@ type EthModule struct {
fx.In fx.In
Chain *store.ChainStore Chain *store.ChainStore
Mpool *messagepool.MessagePool
StateManager *stmgr.StateManager
ChainAPI ChainAPI
MpoolAPI
StateAPI StateAPI
} }
@ -65,6 +74,10 @@ type EthAPI struct {
EthModuleAPI EthModuleAPI
} }
func (a *EthModule) StateNetworkName(ctx context.Context) (dtypes.NetworkName, error) {
return stmgr.GetNetworkName(ctx, a.StateManager, a.Chain.GetHeaviestTipSet().ParentState())
}
func (a *EthModule) EthBlockNumber(context.Context) (api.EthInt, error) { func (a *EthModule) EthBlockNumber(context.Context) (api.EthInt, error) {
height := a.Chain.GetHeaviestTipSet().Height() height := a.Chain.GetHeaviestTipSet().Height()
return api.EthInt(height), nil return api.EthInt(height), nil
@ -140,11 +153,40 @@ func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash api.EthH
} }
func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender api.EthAddress, blkParam string) (api.EthInt, error) { func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender api.EthAddress, blkParam string) (api.EthInt, error) {
return api.EthInt(0), nil addr, err := sender.ToFilecoinAddress()
if err != nil {
return api.EthInt(0), err
}
nonce, err := a.Mpool.GetNonce(ctx, addr, types.EmptyTSK)
if err != nil {
return api.EthInt(0), err
}
return api.EthInt(nonce), nil
} }
func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, blkHash api.EthHash) (api.EthTxReceipt, error) { func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, txHash api.EthHash) (api.EthTxReceipt, error) {
return api.EthTxReceipt{}, nil cid := txHash.ToCid()
msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, cid, api.LookbackNoLimit, true)
if err != nil {
return api.EthTxReceipt{}, err
}
tx, err := a.ethTxFromFilecoinMessageLookup(ctx, msgLookup)
if err != nil {
return api.EthTxReceipt{}, err
}
replay, err := a.StateAPI.StateReplay(ctx, types.EmptyTSK, cid)
if err != nil {
return api.EthTxReceipt{}, err
}
receipt, err := api.NewEthTxReceipt(tx, msgLookup, replay)
if err != nil {
return api.EthTxReceipt{}, err
}
return receipt, nil
} }
func (a *EthModule) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash api.EthHash, txIndex api.EthInt) (api.EthTx, error) { func (a *EthModule) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash api.EthHash, txIndex api.EthInt) (api.EthTx, error) {
@ -196,27 +238,98 @@ func (a *EthModule) NetListening(ctx context.Context) (bool, error) {
} }
func (a *EthModule) EthProtocolVersion(ctx context.Context) (api.EthInt, error) { func (a *EthModule) EthProtocolVersion(ctx context.Context) (api.EthInt, error) {
return api.EthInt(0), nil height := a.Chain.GetHeaviestTipSet().Height()
return api.EthInt(a.StateManager.GetNetworkVersion(ctx, height)), nil
} }
func (a *EthModule) EthMaxPriorityFeePerGas(ctx context.Context) (api.EthInt, error) { func (a *EthModule) EthMaxPriorityFeePerGas(ctx context.Context) (api.EthBigInt, error) {
return api.EthInt(0), nil gasPremium, err := a.GasAPI.GasEstimateGasPremium(ctx, 0, builtin.SystemActorAddr, 10000, types.EmptyTSK)
if err != nil {
return api.EthBigInt(big.Zero()), err
}
return api.EthBigInt(gasPremium), nil
} }
func (a *EthModule) EthGasPrice(ctx context.Context) (api.EthInt, error) { func (a *EthModule) EthGasPrice(ctx context.Context) (api.EthBigInt, error) {
return api.EthInt(0), nil // According to Geth's implementation, eth_gasPrice should return base + tip
// Ref: https://github.com/ethereum/pm/issues/328#issuecomment-853234014
ts := a.Chain.GetHeaviestTipSet()
baseFee := ts.Blocks()[0].ParentBaseFee
premium, err := a.EthMaxPriorityFeePerGas(ctx)
if err != nil {
return api.EthBigInt(big.Zero()), nil
}
gasPrice := big.Add(baseFee, big.Int(premium))
return api.EthBigInt(gasPrice), nil
} }
// func (a *EthModule) EthSendRawTransaction(ctx context.Context tx api.EthTx) (api.EthHash, error) { func (a *EthModule) EthSendRawTransaction(ctx context.Context, rawTx api.EthBytes) (api.EthHash, error) {
// return api.EthHash{}, nil return api.EthHash{}, nil
// } }
func (a *EthModule) applyEvmMsg(ctx context.Context, tx api.EthCall) (*api.InvocResult, error) {
from, err := tx.From.ToFilecoinAddress()
if err != nil {
return nil, err
}
to, err := tx.To.ToFilecoinAddress()
if err != nil {
return nil, xerrors.Errorf("cannot get Filecoin address: %w", err)
}
msg := &types.Message{
From: from,
To: to,
Value: big.Int(tx.Value),
Method: abi.MethodNum(2),
Params: tx.Data,
GasLimit: build.BlockGasLimit,
GasFeeCap: big.Zero(),
GasPremium: big.Zero(),
}
ts := a.Chain.GetHeaviestTipSet()
// Try calling until we find a height with no migration.
var res *api.InvocResult
for {
res, err = a.StateManager.CallWithGas(ctx, msg, []types.ChainMsg{}, ts)
if err != stmgr.ErrExpensiveFork {
break
}
ts, err = a.Chain.GetTipSetFromKey(ctx, ts.Parents())
if err != nil {
return nil, xerrors.Errorf("getting parent tipset: %w", err)
}
}
if err != nil {
return nil, xerrors.Errorf("CallWithGas failed: %w", err)
}
if res.MsgRct.ExitCode != exitcode.Ok {
return nil, xerrors.Errorf("message execution failed: exit %s, reason: %s", res.MsgRct.ExitCode, res.Error)
}
return res, nil
}
func (a *EthModule) EthEstimateGas(ctx context.Context, tx api.EthCall, blkParam string) (api.EthInt, error) { func (a *EthModule) EthEstimateGas(ctx context.Context, tx api.EthCall, blkParam string) (api.EthInt, error) {
return api.EthInt(0), nil invokeResult, err := a.applyEvmMsg(ctx, tx)
if err != nil {
return api.EthInt(0), err
}
ret := invokeResult.MsgRct.GasUsed
return api.EthInt(ret), nil
} }
func (a *EthModule) EthCall(ctx context.Context, tx api.EthCall, blkParam string) (string, error) { func (a *EthModule) EthCall(ctx context.Context, tx api.EthCall, blkParam string) (api.EthBytes, error) {
return "", nil invokeResult, err := a.applyEvmMsg(ctx, tx)
if err != nil {
return nil, err
}
if len(invokeResult.MsgRct.Return) > 0 {
return api.EthBytes(invokeResult.MsgRct.Return), nil
}
return api.EthBytes{}, nil
} }
func (a *EthModule) ethBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTxInfo bool) (api.EthBlock, error) { func (a *EthModule) ethBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTxInfo bool) (api.EthBlock, error) {
@ -306,7 +419,7 @@ func (a *EthModule) ethTxFromFilecoinMessageLookup(ctx context.Context, msgLooku
return api.EthTx{}, err return api.EthTx{}, err
} }
toFilAddr, err := a.StateAPI.StateLookupID(ctx, msg.From, types.EmptyTSK) toFilAddr, err := a.StateAPI.StateLookupID(ctx, msg.To, types.EmptyTSK)
if err != nil { if err != nil {
return api.EthTx{}, err return api.EthTx{}, err
} }
@ -316,13 +429,19 @@ func (a *EthModule) ethTxFromFilecoinMessageLookup(ctx context.Context, msgLooku
return api.EthTx{}, err return api.EthTx{}, err
} }
toAddr := &toEthAddr
_, err = api.CheckContractCreation(msgLookup)
if err == nil {
toAddr = nil
}
tx := api.EthTx{ tx := api.EthTx{
ChainID: api.EthInt(build.Eip155ChainId), ChainID: api.EthInt(build.Eip155ChainId),
Hash: txHash, Hash: txHash,
BlockHash: blkHash, BlockHash: blkHash,
BlockNumber: api.EthInt(msgLookup.Height), BlockNumber: api.EthInt(msgLookup.Height),
From: fromEthAddr, From: fromEthAddr,
To: toEthAddr, To: toAddr,
Value: api.EthBigInt(msg.Value), Value: api.EthBigInt(msg.Value),
Type: api.EthInt(2), Type: api.EthInt(2),
Gas: api.EthInt(msg.GasLimit), Gas: api.EthInt(msg.GasLimit),
@ -331,7 +450,7 @@ func (a *EthModule) ethTxFromFilecoinMessageLookup(ctx context.Context, msgLooku
V: api.EthBigIntZero, V: api.EthBigIntZero,
R: api.EthBigIntZero, R: api.EthBigIntZero,
S: api.EthBigIntZero, S: api.EthBigIntZero,
// TODO: Input: Input: msg.Params,
} }
return tx, nil return tx, nil
} }