feat: ethrpc: implement call, estimateGas, getTransactionCount (#9306)
This commit is contained in:
+8
-5
@@ -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
|
||||
|
||||
+104
-26
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+21
-6
@@ -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()
|
||||
|
||||
+28
-15
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user