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

This commit is contained in:
Kevin Li
2022-11-09 19:25:40 +02:00
committed by vyzo
parent e5bb5b7430
commit b53d5924a1
7 changed files with 352 additions and 90 deletions
+8 -5
View File
@@ -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
View File
@@ -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
+19
View File
@@ -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
View File
@@ -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
View File
@@ -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