feat: ethrpc: implement call, estimateGas, getTransactionCount (#9306)
This commit is contained in:
parent
e5bb5b7430
commit
b53d5924a1
@ -774,7 +774,7 @@ type FullNode interface {
|
||||
EthGetBlockByNumber(ctx context.Context, blkNum EthInt, fullTxInfo bool) (EthBlock, 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
|
||||
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
|
||||
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
|
||||
NetListening(ctx context.Context) (bool, error) //perm:read
|
||||
EthProtocolVersion(ctx context.Context) (EthInt, error) //perm:read
|
||||
EthGasPrice(ctx context.Context) (EthInt, error) //perm:read
|
||||
EthMaxPriorityFeePerGas(ctx context.Context) (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
|
||||
EthGasPrice(ctx context.Context) (EthBigInt, error) //perm:read
|
||||
|
||||
EthMaxPriorityFeePerGas(ctx context.Context) (EthBigInt, error) //perm:read
|
||||
EthEstimateGas(ctx context.Context, tx EthCall, blkParam string) (EthInt, 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
|
||||
// method requires that the lotus daemon is running with the
|
||||
|
130
api/eth_types.go
130
api/eth_types.go
@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
@ -15,6 +16,7 @@ import (
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"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"
|
||||
)
|
||||
@ -70,6 +72,33 @@ func (e *EthBigInt) UnmarshalJSON(b []byte) error {
|
||||
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 {
|
||||
ParentHash EthHash `json:"parentHash"`
|
||||
Sha3Uncles EthHash `json:"sha3Uncles"`
|
||||
@ -116,23 +145,23 @@ func NewEthBlock() EthBlock {
|
||||
}
|
||||
|
||||
type EthTx struct {
|
||||
ChainID EthInt `json:"chainId"`
|
||||
Nonce uint64 `json:"nonce"`
|
||||
Hash EthHash `json:"hash"`
|
||||
BlockHash EthHash `json:"blockHash"`
|
||||
BlockNumber EthInt `json:"blockNumber"`
|
||||
TransactionIndex EthInt `json:"transacionIndex"`
|
||||
From EthAddress `json:"from"`
|
||||
To EthAddress `json:"to"`
|
||||
Value EthBigInt `json:"value"`
|
||||
Type EthInt `json:"type"`
|
||||
Input []byte `json:"input"`
|
||||
Gas EthInt `json:"gas"`
|
||||
MaxFeePerGas EthBigInt `json:"maxFeePerGas"`
|
||||
MaxPriorityFeePerGas EthBigInt `json:"maxPriorityFeePerGas"`
|
||||
V EthBigInt `json:"v"`
|
||||
R EthBigInt `json:"r"`
|
||||
S EthBigInt `json:"s"`
|
||||
ChainID EthInt `json:"chainId"`
|
||||
Nonce uint64 `json:"nonce"`
|
||||
Hash EthHash `json:"hash"`
|
||||
BlockHash EthHash `json:"blockHash"`
|
||||
BlockNumber EthInt `json:"blockNumber"`
|
||||
TransactionIndex EthInt `json:"transacionIndex"`
|
||||
From EthAddress `json:"from"`
|
||||
To *EthAddress `json:"to"`
|
||||
Value EthBigInt `json:"value"`
|
||||
Type EthInt `json:"type"`
|
||||
Input EthBytes `json:"input"`
|
||||
Gas EthInt `json:"gas"`
|
||||
MaxFeePerGas EthBigInt `json:"maxFeePerGas"`
|
||||
MaxPriorityFeePerGas EthBigInt `json:"maxPriorityFeePerGas"`
|
||||
V EthBigInt `json:"v"`
|
||||
R EthBigInt `json:"r"`
|
||||
S EthBigInt `json:"s"`
|
||||
}
|
||||
|
||||
type EthCall struct {
|
||||
@ -141,7 +170,7 @@ type EthCall struct {
|
||||
Gas EthInt `json:"gas"`
|
||||
GasPrice EthBigInt `json:"gasPrice"`
|
||||
Value EthBigInt `json:"value"`
|
||||
Data []byte `json:"data"`
|
||||
Data EthBytes `json:"data"`
|
||||
}
|
||||
|
||||
func (c *EthCall) UnmarshalJSON(b []byte) error {
|
||||
@ -156,22 +185,71 @@ func (c *EthCall) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
|
||||
type EthTxReceipt struct {
|
||||
TransactionHash EthHash `json:"transactionHash"`
|
||||
TransactionIndex EthInt `json:"transacionIndex"`
|
||||
BlockHash EthHash `json:"blockHash"`
|
||||
BlockNumber EthHash `json:"blockNumber"`
|
||||
From EthAddress `json:"from"`
|
||||
To EthAddress `json:"to"`
|
||||
TransactionHash EthHash `json:"transactionHash"`
|
||||
TransactionIndex EthInt `json:"transacionIndex"`
|
||||
BlockHash EthHash `json:"blockHash"`
|
||||
BlockNumber EthInt `json:"blockNumber"`
|
||||
From EthAddress `json:"from"`
|
||||
To *EthAddress `json:"to"`
|
||||
// Logs
|
||||
// LogsBloom
|
||||
StateRoot EthHash `json:"root"`
|
||||
Status EthInt `json:"status"`
|
||||
ContractAddress *EthAddress `json:"contractAddress"`
|
||||
CumulativeGasUsed EthBigInt `json:"cumulativeGasUsed"`
|
||||
GasUsed EthBigInt `json:"gasUsed"`
|
||||
CumulativeGasUsed EthInt `json:"cumulativeGasUsed"`
|
||||
GasUsed EthInt `json:"gasUsed"`
|
||||
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 (
|
||||
ETH_ADDRESS_LENGTH = 20
|
||||
ETH_HASH_LENGTH = 32
|
||||
|
@ -136,3 +136,22 @@ func TestUnmarshalEthCall(t *testing.T) {
|
||||
err := c.UnmarshalJSON([]byte(data))
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -952,10 +952,10 @@ func (mr *MockFullNodeMockRecorder) EthBlockNumber(arg0 interface{}) *gomock.Cal
|
||||
}
|
||||
|
||||
// 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()
|
||||
ret := m.ctrl.Call(m, "EthCall", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret0, _ := ret[0].(api.EthBytes)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@ -997,10 +997,10 @@ func (mr *MockFullNodeMockRecorder) EthEstimateGas(arg0, arg1, arg2 interface{})
|
||||
}
|
||||
|
||||
// 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()
|
||||
ret := m.ctrl.Call(m, "EthGasPrice", arg0)
|
||||
ret0, _ := ret[0].(api.EthInt)
|
||||
ret0, _ := ret[0].(api.EthBigInt)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@ -1192,10 +1192,10 @@ func (mr *MockFullNodeMockRecorder) EthGetTransactionReceipt(arg0, arg1 interfac
|
||||
}
|
||||
|
||||
// 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()
|
||||
ret := m.ctrl.Call(m, "EthMaxPriorityFeePerGas", arg0)
|
||||
ret0, _ := ret[0].(api.EthInt)
|
||||
ret0, _ := ret[0].(api.EthBigInt)
|
||||
ret1, _ := ret[1].(error)
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (m *MockFullNode) GasEstimateFeeCap(arg0 context.Context, arg1 *types.Message, arg2 int64, arg3 types.TipSetKey) (big.Int, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -222,13 +222,13 @@ type FullNodeStruct struct {
|
||||
|
||||
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"`
|
||||
|
||||
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"`
|
||||
|
||||
@ -254,10 +254,12 @@ type FullNodeStruct struct {
|
||||
|
||||
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"`
|
||||
|
||||
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"`
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
return "", ErrNotSupported
|
||||
return *new(EthBytes), ErrNotSupported
|
||||
}
|
||||
return s.Internal.EthCall(p0, p1, p2)
|
||||
}
|
||||
|
||||
func (s *FullNodeStub) EthCall(p0 context.Context, p1 EthCall, p2 string) (string, error) {
|
||||
return "", ErrNotSupported
|
||||
func (s *FullNodeStub) EthCall(p0 context.Context, p1 EthCall, p2 string) (EthBytes, error) {
|
||||
return *new(EthBytes), ErrNotSupported
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) EthGasPrice(p0 context.Context) (EthInt, error) {
|
||||
func (s *FullNodeStruct) EthGasPrice(p0 context.Context) (EthBigInt, error) {
|
||||
if s.Internal.EthGasPrice == nil {
|
||||
return *new(EthInt), ErrNotSupported
|
||||
return *new(EthBigInt), ErrNotSupported
|
||||
}
|
||||
return s.Internal.EthGasPrice(p0)
|
||||
}
|
||||
|
||||
func (s *FullNodeStub) EthGasPrice(p0 context.Context) (EthInt, error) {
|
||||
return *new(EthInt), ErrNotSupported
|
||||
func (s *FullNodeStub) EthGasPrice(p0 context.Context) (EthBigInt, error) {
|
||||
return *new(EthBigInt), ErrNotSupported
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) EthMaxPriorityFeePerGas(p0 context.Context) (EthInt, error) {
|
||||
func (s *FullNodeStruct) EthMaxPriorityFeePerGas(p0 context.Context) (EthBigInt, error) {
|
||||
if s.Internal.EthMaxPriorityFeePerGas == nil {
|
||||
return *new(EthInt), ErrNotSupported
|
||||
return *new(EthBigInt), ErrNotSupported
|
||||
}
|
||||
return s.Internal.EthMaxPriorityFeePerGas(p0)
|
||||
}
|
||||
|
||||
func (s *FullNodeStub) EthMaxPriorityFeePerGas(p0 context.Context) (EthInt, error) {
|
||||
return *new(EthInt), ErrNotSupported
|
||||
func (s *FullNodeStub) EthMaxPriorityFeePerGas(p0 context.Context) (EthBigInt, error) {
|
||||
return *new(EthBigInt), ErrNotSupported
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
if s.Internal.GasEstimateFeeCap == nil {
|
||||
return *new(types.BigInt), ErrNotSupported
|
||||
|
@ -86,6 +86,7 @@
|
||||
* [EthGetTransactionReceipt](#EthGetTransactionReceipt)
|
||||
* [EthMaxPriorityFeePerGas](#EthMaxPriorityFeePerGas)
|
||||
* [EthProtocolVersion](#EthProtocolVersion)
|
||||
* [EthSendRawTransaction](#EthSendRawTransaction)
|
||||
* [Gas](#Gas)
|
||||
* [GasEstimateFeeCap](#GasEstimateFeeCap)
|
||||
* [GasEstimateGasLimit](#GasEstimateGasLimit)
|
||||
@ -2189,13 +2190,13 @@ Inputs:
|
||||
"gas": "0x5",
|
||||
"gasPrice": "0x0",
|
||||
"value": "0x0",
|
||||
"data": "Ynl0ZSBhcnJheQ=="
|
||||
"data": "0x07"
|
||||
},
|
||||
"string value"
|
||||
]
|
||||
```
|
||||
|
||||
Response: `"string value"`
|
||||
Response: `"0x07"`
|
||||
|
||||
### EthChainId
|
||||
|
||||
@ -2220,7 +2221,7 @@ Inputs:
|
||||
"gas": "0x5",
|
||||
"gasPrice": "0x0",
|
||||
"value": "0x0",
|
||||
"data": "Ynl0ZSBhcnJheQ=="
|
||||
"data": "0x07"
|
||||
},
|
||||
"string value"
|
||||
]
|
||||
@ -2235,7 +2236,7 @@ Perms: read
|
||||
|
||||
Inputs: `null`
|
||||
|
||||
Response: `"0x5"`
|
||||
Response: `"0x0"`
|
||||
|
||||
### EthGetBalance
|
||||
|
||||
@ -2417,10 +2418,10 @@ Response:
|
||||
"blockNumber": "0x5",
|
||||
"transacionIndex": "0x5",
|
||||
"from": "0x0707070707070707070707070707070707070707",
|
||||
"to": "0x0707070707070707070707070707070707070707",
|
||||
"to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031",
|
||||
"value": "0x0",
|
||||
"type": "0x5",
|
||||
"input": "Ynl0ZSBhcnJheQ==",
|
||||
"input": "0x07",
|
||||
"gas": "0x5",
|
||||
"maxFeePerGas": "0x0",
|
||||
"maxPriorityFeePerGas": "0x0",
|
||||
@ -2453,10 +2454,10 @@ Response:
|
||||
"blockNumber": "0x5",
|
||||
"transacionIndex": "0x5",
|
||||
"from": "0x0707070707070707070707070707070707070707",
|
||||
"to": "0x0707070707070707070707070707070707070707",
|
||||
"to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031",
|
||||
"value": "0x0",
|
||||
"type": "0x5",
|
||||
"input": "Ynl0ZSBhcnJheQ==",
|
||||
"input": "0x07",
|
||||
"gas": "0x5",
|
||||
"maxFeePerGas": "0x0",
|
||||
"maxPriorityFeePerGas": "0x0",
|
||||
@ -2488,10 +2489,10 @@ Response:
|
||||
"blockNumber": "0x5",
|
||||
"transacionIndex": "0x5",
|
||||
"from": "0x0707070707070707070707070707070707070707",
|
||||
"to": "0x0707070707070707070707070707070707070707",
|
||||
"to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031",
|
||||
"value": "0x0",
|
||||
"type": "0x5",
|
||||
"input": "Ynl0ZSBhcnJheQ==",
|
||||
"input": "0x07",
|
||||
"gas": "0x5",
|
||||
"maxFeePerGas": "0x0",
|
||||
"maxPriorityFeePerGas": "0x0",
|
||||
@ -2534,14 +2535,14 @@ Response:
|
||||
"transactionHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
|
||||
"transacionIndex": "0x5",
|
||||
"blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
|
||||
"blockNumber": "0x0707070707070707070707070707070707070707070707070707070707070707",
|
||||
"blockNumber": "0x5",
|
||||
"from": "0x0707070707070707070707070707070707070707",
|
||||
"to": "0x0707070707070707070707070707070707070707",
|
||||
"to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031",
|
||||
"root": "0x0707070707070707070707070707070707070707070707070707070707070707",
|
||||
"status": "0x5",
|
||||
"contractAddress": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031",
|
||||
"cumulativeGasUsed": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"cumulativeGasUsed": "0x5",
|
||||
"gasUsed": "0x5",
|
||||
"effectiveGasPrice": "0x0"
|
||||
}
|
||||
```
|
||||
@ -2553,7 +2554,7 @@ Perms: read
|
||||
|
||||
Inputs: `null`
|
||||
|
||||
Response: `"0x5"`
|
||||
Response: `"0x0"`
|
||||
|
||||
### EthProtocolVersion
|
||||
|
||||
@ -2564,6 +2565,20 @@ Inputs: `null`
|
||||
|
||||
Response: `"0x5"`
|
||||
|
||||
### EthSendRawTransaction
|
||||
|
||||
|
||||
Perms: read
|
||||
|
||||
Inputs:
|
||||
```json
|
||||
[
|
||||
"0x07"
|
||||
]
|
||||
```
|
||||
|
||||
Response: `"0x0707070707070707070707070707070707070707070707070707070707070707"`
|
||||
|
||||
## Gas
|
||||
|
||||
|
||||
|
@ -8,11 +8,17 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"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/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/types"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
)
|
||||
|
||||
type EthModuleAPI interface {
|
||||
@ -24,7 +30,7 @@ type EthModuleAPI interface {
|
||||
EthGetBlockByNumber(ctx context.Context, blkNum api.EthInt, fullTxInfo bool) (api.EthBlock, error)
|
||||
EthGetTransactionByHash(ctx context.Context, txHash api.EthHash) (api.EthTx, 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)
|
||||
EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum api.EthInt, txIndex api.EthInt) (api.EthTx, error)
|
||||
EthGetCode(ctx context.Context, address api.EthAddress) (string, error)
|
||||
@ -34,11 +40,11 @@ type EthModuleAPI interface {
|
||||
NetVersion(ctx context.Context) (string, error)
|
||||
NetListening(ctx context.Context) (bool, 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)
|
||||
EthCall(ctx context.Context, tx api.EthCall, blkParam string) (string, error)
|
||||
EthMaxPriorityFeePerGas(ctx context.Context) (api.EthInt, error)
|
||||
// EthSendRawTransaction(ctx context.Context, tx api.EthTx) (api.EthHash, error)
|
||||
EthCall(ctx context.Context, tx api.EthCall, blkParam string) (api.EthBytes, error)
|
||||
EthMaxPriorityFeePerGas(ctx context.Context) (api.EthBigInt, error)
|
||||
EthSendRawTransaction(ctx context.Context, rawTx api.EthBytes) (api.EthHash, error)
|
||||
}
|
||||
|
||||
var _ EthModuleAPI = *new(api.FullNode)
|
||||
@ -49,9 +55,12 @@ var _ EthModuleAPI = *new(api.FullNode)
|
||||
type EthModule struct {
|
||||
fx.In
|
||||
|
||||
Chain *store.ChainStore
|
||||
Chain *store.ChainStore
|
||||
Mpool *messagepool.MessagePool
|
||||
StateManager *stmgr.StateManager
|
||||
|
||||
ChainAPI
|
||||
MpoolAPI
|
||||
StateAPI
|
||||
}
|
||||
|
||||
@ -65,6 +74,10 @@ type EthAPI struct {
|
||||
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) {
|
||||
height := a.Chain.GetHeaviestTipSet().Height()
|
||||
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) {
|
||||
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) {
|
||||
return api.EthTxReceipt{}, nil
|
||||
func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, txHash api.EthHash) (api.EthTxReceipt, error) {
|
||||
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) {
|
||||
@ -196,27 +238,98 @@ func (a *EthModule) NetListening(ctx context.Context) (bool, 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) {
|
||||
return api.EthInt(0), nil
|
||||
func (a *EthModule) EthMaxPriorityFeePerGas(ctx context.Context) (api.EthBigInt, error) {
|
||||
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) {
|
||||
return api.EthInt(0), nil
|
||||
func (a *EthModule) EthGasPrice(ctx context.Context) (api.EthBigInt, error) {
|
||||
// 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) {
|
||||
// return api.EthHash{}, nil
|
||||
// }
|
||||
func (a *EthModule) EthSendRawTransaction(ctx context.Context, rawTx api.EthBytes) (api.EthHash, error) {
|
||||
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) {
|
||||
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) {
|
||||
return "", nil
|
||||
func (a *EthModule) EthCall(ctx context.Context, tx api.EthCall, blkParam string) (api.EthBytes, error) {
|
||||
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) {
|
||||
@ -306,7 +419,7 @@ func (a *EthModule) ethTxFromFilecoinMessageLookup(ctx context.Context, msgLooku
|
||||
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 {
|
||||
return api.EthTx{}, err
|
||||
}
|
||||
@ -316,13 +429,19 @@ func (a *EthModule) ethTxFromFilecoinMessageLookup(ctx context.Context, msgLooku
|
||||
return api.EthTx{}, err
|
||||
}
|
||||
|
||||
toAddr := &toEthAddr
|
||||
_, err = api.CheckContractCreation(msgLookup)
|
||||
if err == nil {
|
||||
toAddr = nil
|
||||
}
|
||||
|
||||
tx := api.EthTx{
|
||||
ChainID: api.EthInt(build.Eip155ChainId),
|
||||
Hash: txHash,
|
||||
BlockHash: blkHash,
|
||||
BlockNumber: api.EthInt(msgLookup.Height),
|
||||
From: fromEthAddr,
|
||||
To: toEthAddr,
|
||||
To: toAddr,
|
||||
Value: api.EthBigInt(msg.Value),
|
||||
Type: api.EthInt(2),
|
||||
Gas: api.EthInt(msg.GasLimit),
|
||||
@ -331,7 +450,7 @@ func (a *EthModule) ethTxFromFilecoinMessageLookup(ctx context.Context, msgLooku
|
||||
V: api.EthBigIntZero,
|
||||
R: api.EthBigIntZero,
|
||||
S: api.EthBigIntZero,
|
||||
// TODO: Input:
|
||||
Input: msg.Params,
|
||||
}
|
||||
return tx, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user