feat: ethrpc: implement EthBlock and EthTx structs (#9287)

Co-authored-by: Raúl Kripalani <raul@protocol.ai>
This commit is contained in:
Kevin Li 2022-09-12 17:46:15 -04:00 committed by vyzo
parent 4652d8559f
commit e5bb5b7430
16 changed files with 455 additions and 87 deletions

View File

@ -785,9 +785,10 @@ 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
EthMaxPriorityFeePerGas(ctx context.Context) (EthInt, error) //perm:read
EthGasPrice(ctx context.Context) (EthInt, error) //perm:read
// EthSendRawTransaction(ctx context.Context, tx api.EthTx) (EthHash, error) //perm:write
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
// CreateBackup creates node backup onder the specified file name. The
// method requires that the lotus daemon is running with the

View File

@ -5,15 +5,18 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
mathbig "math/big"
"strconv"
"strings"
"github.com/ipfs/go-cid"
"github.com/multiformats/go-multihash"
xerrors "golang.org/x/xerrors"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/lotus/build"
)
type EthInt int64
@ -32,12 +35,16 @@ func (e *EthInt) UnmarshalJSON(b []byte) error {
return err
}
eint := EthInt(parsedInt)
e = &eint
*e = eint
return nil
}
type EthBigInt big.Int
var (
EthBigIntZero = EthBigInt{Int: big.Zero().Int}
)
func (e EthBigInt) MarshalJSON() ([]byte, error) {
if e.Int == nil {
return json.Marshal("0x0")
@ -45,6 +52,24 @@ func (e EthBigInt) MarshalJSON() ([]byte, error) {
return json.Marshal(fmt.Sprintf("0x%x", e.Int))
}
func (e *EthBigInt) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
replaced := strings.Replace(s, "0x", "", -1)
if len(replaced)%2 == 1 {
replaced = "0" + replaced
}
i := new(mathbig.Int)
i.SetString(replaced, 16)
*e = EthBigInt(big.NewFromGo(i))
return nil
}
type EthBlock struct {
ParentHash EthHash `json:"parentHash"`
Sha3Uncles EthHash `json:"sha3Uncles"`
@ -53,24 +78,49 @@ type EthBlock struct {
TransactionsRoot EthHash `json:"transactionsRoot"`
ReceiptsRoot EthHash `json:"receiptsRoot"`
// TODO: include LogsBloom
Difficulty EthInt `json:"difficulty"`
Number EthInt `json:"number"`
GasLimit EthInt `json:"gasLimit"`
GasUsed EthInt `json:"gasUsed"`
Timestamp EthInt `json:"timestamp"`
Extradata []byte `json:"extraData"`
MixHash EthHash `json:"mixHash"`
Nonce EthNonce `json:"nonce"`
BaseFeePerGas EthInt `json:"baseFeePerGas"`
Transactions EthTx `json:"transactions"`
Difficulty EthInt `json:"difficulty"`
Number EthInt `json:"number"`
GasLimit EthInt `json:"gasLimit"`
GasUsed EthInt `json:"gasUsed"`
Timestamp EthInt `json:"timestamp"`
Extradata []byte `json:"extraData"`
MixHash EthHash `json:"mixHash"`
Nonce EthNonce `json:"nonce"`
BaseFeePerGas EthBigInt `json:"baseFeePerGas"`
Size EthInt `json:"size"`
// can be []EthTx or []string depending on query params
Transactions []interface{} `json:"transactions"`
Uncles []EthHash `json:"uncles"`
}
var (
EmptyEthHash = EthHash{}
EmptyEthInt = EthInt(0)
EmptyEthNonce = [8]byte{0, 0, 0, 0, 0, 0, 0, 0}
)
func NewEthBlock() EthBlock {
return EthBlock{
Sha3Uncles: EmptyEthHash,
StateRoot: EmptyEthHash,
TransactionsRoot: EmptyEthHash,
ReceiptsRoot: EmptyEthHash,
Difficulty: EmptyEthInt,
Extradata: []byte{},
MixHash: EmptyEthHash,
Nonce: EmptyEthNonce,
GasLimit: EthInt(build.BlockGasLimit), // TODO we map Ethereum blocks to Filecoin tipsets; this is inconsistent.
Uncles: []EthHash{},
Transactions: []interface{}{},
}
}
type EthTx struct {
ChainID *EthInt `json:"chainId"`
ChainID EthInt `json:"chainId"`
Nonce uint64 `json:"nonce"`
Hash EthHash `json:"hash"`
BlockHash EthHash `json:"blockHash"`
BlockNumber EthHash `json:"blockNumber"`
BlockNumber EthInt `json:"blockNumber"`
TransactionIndex EthInt `json:"transacionIndex"`
From EthAddress `json:"from"`
To EthAddress `json:"to"`
@ -85,6 +135,26 @@ type EthTx struct {
S EthBigInt `json:"s"`
}
type EthCall struct {
From EthAddress `json:"from"`
To EthAddress `json:"to"`
Gas EthInt `json:"gas"`
GasPrice EthBigInt `json:"gasPrice"`
Value EthBigInt `json:"value"`
Data []byte `json:"data"`
}
func (c *EthCall) UnmarshalJSON(b []byte) error {
type TempEthCall EthCall
var params TempEthCall
if err := json.Unmarshal(b, &params); err != nil {
return err
}
*c = EthCall(params)
return nil
}
type EthTxReceipt struct {
TransactionHash EthHash `json:"transactionHash"`
TransactionIndex EthInt `json:"transacionIndex"`
@ -136,7 +206,7 @@ func (a *EthAddress) UnmarshalJSON(b []byte) error {
if err != nil {
return err
}
a = &addr
copy(a[:], addr[:])
return nil
}
@ -185,7 +255,7 @@ func (h *EthHash) UnmarshalJSON(b []byte) error {
if err != nil {
return err
}
h = &hash
copy(h[:], hash[:])
return nil
}

View File

@ -30,6 +30,20 @@ func TestEthIntMarshalJSON(t *testing.T) {
require.Equal(t, j, tc.Output)
}
}
func TestEthIntUnmarshalJSON(t *testing.T) {
testcases := []TestCase{
{[]byte("\"0x0\""), EthInt(0)},
{[]byte("\"0x41\""), EthInt(65)},
{[]byte("\"0x400\""), EthInt(1024)},
}
for _, tc := range testcases {
var i EthInt
err := i.UnmarshalJSON(tc.Input.([]byte))
require.Nil(t, err)
require.Equal(t, i, tc.Output)
}
}
func TestEthBigIntMarshalJSON(t *testing.T) {
testcases := []TestCase{
@ -45,16 +59,34 @@ func TestEthBigIntMarshalJSON(t *testing.T) {
}
}
func TestEthBigIntUnmarshalJSON(t *testing.T) {
testcases := []TestCase{
{[]byte("\"0x0\""), EthBigInt(big.MustFromString("0"))},
{[]byte("\"0x41\""), EthBigInt(big.MustFromString("65"))},
{[]byte("\"0x400\""), EthBigInt(big.MustFromString("1024"))},
{[]byte("\"0xff1000000000000000000000000\""), EthBigInt(big.MustFromString("323330131220712761719252861321216"))},
}
for _, tc := range testcases {
var i EthBigInt
err := i.UnmarshalJSON(tc.Input.([]byte))
require.Nil(t, err)
require.Equal(t, i, tc.Output)
}
}
func TestEthHash(t *testing.T) {
testcases := []string{
"0x013dbb9442ca9667baccc6230fcd5c1c4b2d4d2870f4bd20681d4d47cfd15184",
"0xab8653edf9f51785664a643b47605a7ba3d917b5339a0724e7642c114d0e4738",
`"0x013dbb9442ca9667baccc6230fcd5c1c4b2d4d2870f4bd20681d4d47cfd15184"`,
`"0xab8653edf9f51785664a643b47605a7ba3d917b5339a0724e7642c114d0e4738"`,
}
for _, hash := range testcases {
h, err := EthHashFromHex(hash)
var h EthHash
err := h.UnmarshalJSON([]byte(hash))
require.Nil(t, err)
require.Equal(t, h.String(), hash)
require.Equal(t, h.String(), strings.Replace(hash, `"`, "", -1))
c := h.ToCid()
h1, err := EthHashFromCid(c)
@ -65,15 +97,17 @@ func TestEthHash(t *testing.T) {
func TestEthAddr(t *testing.T) {
testcases := []string{
strings.ToLower("0xd4c5fb16488Aa48081296299d54b0c648C9333dA"),
strings.ToLower("0x2C2EC67e3e1FeA8e4A39601cB3A3Cd44f5fa830d"),
strings.ToLower("0x01184F793982104363F9a8a5845743f452dE0586"),
strings.ToLower(`"0xd4c5fb16488Aa48081296299d54b0c648C9333dA"`),
strings.ToLower(`"0x2C2EC67e3e1FeA8e4A39601cB3A3Cd44f5fa830d"`),
strings.ToLower(`"0x01184F793982104363F9a8a5845743f452dE0586"`),
}
for _, addr := range testcases {
a, err := EthAddressFromHex(addr)
var a EthAddress
err := a.UnmarshalJSON([]byte(addr))
require.Nil(t, err)
require.Equal(t, a.String(), addr)
require.Equal(t, a.String(), strings.Replace(addr, `"`, "", -1))
}
}
@ -94,3 +128,11 @@ func TestParseEthAddr(t *testing.T) {
require.Equal(t, addr, faddr)
}
}
func TestUnmarshalEthCall(t *testing.T) {
data := `{"from":"0x4D6D86b31a112a05A473c4aE84afaF873f632325","to":"0xFe01CC39f5Ae8553D6914DBb9dC27D219fa22D7f","gas":"0x5","gasPrice":"0x6","value":"0x123","data":""}`
var c EthCall
err := c.UnmarshalJSON([]byte(data))
require.Nil(t, err)
}

View File

@ -951,6 +951,21 @@ func (mr *MockFullNodeMockRecorder) EthBlockNumber(arg0 interface{}) *gomock.Cal
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthBlockNumber", reflect.TypeOf((*MockFullNode)(nil).EthBlockNumber), arg0)
}
// EthCall mocks base method.
func (m *MockFullNode) EthCall(arg0 context.Context, arg1 api.EthCall, arg2 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EthCall", arg0, arg1, arg2)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// EthCall indicates an expected call of EthCall.
func (mr *MockFullNodeMockRecorder) EthCall(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthCall", reflect.TypeOf((*MockFullNode)(nil).EthCall), arg0, arg1, arg2)
}
// EthChainId mocks base method.
func (m *MockFullNode) EthChainId(arg0 context.Context) (api.EthInt, error) {
m.ctrl.T.Helper()
@ -966,6 +981,21 @@ func (mr *MockFullNodeMockRecorder) EthChainId(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthChainId", reflect.TypeOf((*MockFullNode)(nil).EthChainId), arg0)
}
// EthEstimateGas mocks base method.
func (m *MockFullNode) EthEstimateGas(arg0 context.Context, arg1 api.EthCall, arg2 string) (api.EthInt, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EthEstimateGas", arg0, arg1, arg2)
ret0, _ := ret[0].(api.EthInt)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// EthEstimateGas indicates an expected call of EthEstimateGas.
func (mr *MockFullNodeMockRecorder) EthEstimateGas(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthEstimateGas", reflect.TypeOf((*MockFullNode)(nil).EthEstimateGas), arg0, arg1, arg2)
}
// EthGasPrice mocks base method.
func (m *MockFullNode) EthGasPrice(arg0 context.Context) (api.EthInt, error) {
m.ctrl.T.Helper()

View File

@ -222,9 +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"`
EthChainId func(p0 context.Context) (EthInt, error) `perm:"read"`
EthGasPrice func(p0 context.Context) (EthInt, error) ``
EthEstimateGas func(p0 context.Context, p1 EthCall, p2 string) (EthInt, error) `perm:"read"`
EthGasPrice func(p0 context.Context) (EthInt, error) `perm:"read"`
EthGetBalance func(p0 context.Context, p1 EthAddress, p2 string) (EthBigInt, error) `perm:"read"`
@ -1853,6 +1857,17 @@ 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) {
if s.Internal.EthCall == nil {
return "", 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 *FullNodeStruct) EthChainId(p0 context.Context) (EthInt, error) {
if s.Internal.EthChainId == nil {
return *new(EthInt), ErrNotSupported
@ -1864,6 +1879,17 @@ func (s *FullNodeStub) EthChainId(p0 context.Context) (EthInt, error) {
return *new(EthInt), ErrNotSupported
}
func (s *FullNodeStruct) EthEstimateGas(p0 context.Context, p1 EthCall, p2 string) (EthInt, error) {
if s.Internal.EthEstimateGas == nil {
return *new(EthInt), ErrNotSupported
}
return s.Internal.EthEstimateGas(p0, p1, p2)
}
func (s *FullNodeStub) EthEstimateGas(p0 context.Context, p1 EthCall, p2 string) (EthInt, error) {
return *new(EthInt), ErrNotSupported
}
func (s *FullNodeStruct) EthGasPrice(p0 context.Context) (EthInt, error) {
if s.Internal.EthGasPrice == nil {
return *new(EthInt), ErrNotSupported

View File

@ -131,4 +131,8 @@ const InteractivePoRepConfidence = 6
const BootstrapPeerThreshold = 1
// ChainId defines the chain ID used in the Ethereum JSON-RPC endpoint.
// As per https://github.com/ethereum-lists/chains
const Eip155ChainId = 31415926
var WhitelistedBlock = cid.Undef

View File

@ -81,4 +81,8 @@ const PropagationDelaySecs = uint64(6)
// BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start
const BootstrapPeerThreshold = 2
// ChainId defines the chain ID used in the Ethereum JSON-RPC endpoint.
// As per https://github.com/ethereum-lists/chains
const Eip155ChainId = 3141592
var WhitelistedBlock = cid.Undef

View File

@ -114,4 +114,8 @@ var PropagationDelaySecs = uint64(10)
// BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start
const BootstrapPeerThreshold = 4
// ChainId defines the chain ID used in the Ethereum JSON-RPC endpoint.
// As per https://github.com/ethereum-lists/chains
const Eip155ChainId = 314159
var WhitelistedBlock = cid.Undef

View File

@ -119,4 +119,9 @@ const PropagationDelaySecs = uint64(6)
// BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start
const BootstrapPeerThreshold = 2
// ChainId defines the chain ID used in the Ethereum JSON-RPC endpoint.
// As per https://github.com/ethereum-lists/chains
// TODO same as butterfly for now, as we didn't submit an assignment for interopnet.
const Eip155ChainId = 3141592
var WhitelistedBlock = cid.Undef

View File

@ -127,5 +127,9 @@ const BlockDelaySecs = uint64(builtin2.EpochDurationSeconds)
// BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start
const BootstrapPeerThreshold = 4
// ChainId defines the chain ID used in the Ethereum JSON-RPC endpoint.
// As per https://github.com/ethereum-lists/chains
const Eip155ChainId = 314
// we skip checks on message validity in this block to sidestep the zero-bls signature
var WhitelistedBlock = MustParseCid("bafy2bzaceapyg2uyzk7vueh3xccxkuwbz3nxewjyguoxvhx77malc2lzn2ybi")

View File

@ -130,3 +130,7 @@ var (
)
const BootstrapPeerThreshold = 1
// ChainId defines the chain ID used in the Ethereum JSON-RPC endpoint.
// As per https://github.com/ethereum-lists/chains
const Eip155ChainId = 31415926

View File

@ -85,4 +85,8 @@ const PropagationDelaySecs = uint64(6)
// BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start
const BootstrapPeerThreshold = 2
// ChainId defines the chain ID used in the Ethereum JSON-RPC endpoint.
// As per https://github.com/ethereum-lists/chains
const Eip155ChainId = 31415
var WhitelistedBlock = cid.Undef

View File

@ -99,6 +99,14 @@ func (k *TipSetKey) UnmarshalJSON(b []byte) error {
return nil
}
func (k TipSetKey) Cid() (cid.Cid, error) {
blk, err := k.ToStorageBlock()
if err != nil {
return cid.Cid{}, err
}
return blk.Cid(), nil
}
func (k TipSetKey) ToStorageBlock() (block.Block, error) {
buf := new(bytes.Buffer)
if err := k.MarshalCBOR(buf); err != nil {

View File

@ -68,7 +68,9 @@
* [Eth](#Eth)
* [EthAccounts](#EthAccounts)
* [EthBlockNumber](#EthBlockNumber)
* [EthCall](#EthCall)
* [EthChainId](#EthChainId)
* [EthEstimateGas](#EthEstimateGas)
* [EthGasPrice](#EthGasPrice)
* [EthGetBalance](#EthGetBalance)
* [EthGetBlockByHash](#EthGetBlockByHash)
@ -2173,6 +2175,28 @@ Inputs: `null`
Response: `"0x5"`
### EthCall
Perms: read
Inputs:
```json
[
{
"from": "0x0707070707070707070707070707070707070707",
"to": "0x0707070707070707070707070707070707070707",
"gas": "0x5",
"gasPrice": "0x0",
"value": "0x0",
"data": "Ynl0ZSBhcnJheQ=="
},
"string value"
]
```
Response: `"string value"`
### EthChainId
@ -2182,10 +2206,32 @@ Inputs: `null`
Response: `"0x5"`
### EthEstimateGas
Perms: read
Inputs:
```json
[
{
"from": "0x0707070707070707070707070707070707070707",
"to": "0x0707070707070707070707070707070707070707",
"gas": "0x5",
"gasPrice": "0x0",
"value": "0x0",
"data": "Ynl0ZSBhcnJheQ=="
},
"string value"
]
```
Response: `"0x5"`
### EthGasPrice
Perms:
Perms: read
Inputs: `null`
@ -2236,26 +2282,14 @@ Response:
"extraData": "Ynl0ZSBhcnJheQ==",
"mixHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"nonce": "0x0707070707070707",
"baseFeePerGas": "0x5",
"transactions": {
"chainId": "0x5",
"nonce": 42,
"hash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockNumber": "0x0707070707070707070707070707070707070707070707070707070707070707",
"transacionIndex": "0x5",
"from": "0x0707070707070707070707070707070707070707",
"to": "0x0707070707070707070707070707070707070707",
"value": "0x0",
"type": "0x5",
"input": "Ynl0ZSBhcnJheQ==",
"gas": "0x5",
"maxFeePerGas": "0x0",
"maxPriorityFeePerGas": "0x0",
"v": "0x0",
"r": "0x0",
"s": "0x0"
}
"baseFeePerGas": "0x0",
"size": "0x5",
"transactions": [
{}
],
"uncles": [
"0x0707070707070707070707070707070707070707070707070707070707070707"
]
}
```
@ -2289,26 +2323,14 @@ Response:
"extraData": "Ynl0ZSBhcnJheQ==",
"mixHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"nonce": "0x0707070707070707",
"baseFeePerGas": "0x5",
"transactions": {
"chainId": "0x5",
"nonce": 42,
"hash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockNumber": "0x0707070707070707070707070707070707070707070707070707070707070707",
"transacionIndex": "0x5",
"from": "0x0707070707070707070707070707070707070707",
"to": "0x0707070707070707070707070707070707070707",
"value": "0x0",
"type": "0x5",
"input": "Ynl0ZSBhcnJheQ==",
"gas": "0x5",
"maxFeePerGas": "0x0",
"maxPriorityFeePerGas": "0x0",
"v": "0x0",
"r": "0x0",
"s": "0x0"
}
"baseFeePerGas": "0x0",
"size": "0x5",
"transactions": [
{}
],
"uncles": [
"0x0707070707070707070707070707070707070707070707070707070707070707"
]
}
```
@ -2392,7 +2414,7 @@ Response:
"nonce": 42,
"hash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockNumber": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockNumber": "0x5",
"transacionIndex": "0x5",
"from": "0x0707070707070707070707070707070707070707",
"to": "0x0707070707070707070707070707070707070707",
@ -2428,7 +2450,7 @@ Response:
"nonce": 42,
"hash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockNumber": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockNumber": "0x5",
"transacionIndex": "0x5",
"from": "0x0707070707070707070707070707070707070707",
"to": "0x0707070707070707070707070707070707070707",
@ -2463,7 +2485,7 @@ Response:
"nonce": 42,
"hash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockNumber": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockNumber": "0x5",
"transacionIndex": "0x5",
"from": "0x0707070707070707070707070707070707070707",
"to": "0x0707070707070707070707070707070707070707",

View File

@ -10,6 +10,7 @@ import (
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
)
@ -33,8 +34,10 @@ type EthModuleAPI interface {
NetVersion(ctx context.Context) (string, error)
NetListening(ctx context.Context) (bool, error)
EthProtocolVersion(ctx context.Context) (api.EthInt, error)
EthMaxPriorityFeePerGas(ctx context.Context) (api.EthInt, error)
EthGasPrice(ctx context.Context) (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)
EthMaxPriorityFeePerGas(ctx context.Context) (api.EthInt, error)
// EthSendRawTransaction(ctx context.Context, tx api.EthTx) (api.EthHash, error)
}
@ -47,6 +50,8 @@ type EthModule struct {
fx.In
Chain *store.ChainStore
ChainAPI
StateAPI
}
@ -104,15 +109,34 @@ func (a *EthModule) EthGetBlockTransactionCountByHash(ctx context.Context, blkHa
}
func (a *EthModule) EthGetBlockByHash(ctx context.Context, blkHash api.EthHash, fullTxInfo bool) (api.EthBlock, error) {
return api.EthBlock{}, nil
ts, err := a.Chain.GetTipSetByCid(ctx, blkHash.ToCid())
if err != nil {
return api.EthBlock{}, xerrors.Errorf("error loading tipset %s: %w", ts, err)
}
return a.ethBlockFromFilecoinTipSet(ctx, ts, fullTxInfo)
}
func (a *EthModule) EthGetBlockByNumber(ctx context.Context, blkNum api.EthInt, fullTxInfo bool) (api.EthBlock, error) {
return api.EthBlock{}, nil
ts, err := a.Chain.GetTipsetByHeight(ctx, abi.ChainEpoch(blkNum), nil, false)
if err != nil {
return api.EthBlock{}, xerrors.Errorf("error loading tipset %s: %w", ts, err)
}
return a.ethBlockFromFilecoinTipSet(ctx, ts, fullTxInfo)
}
func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash api.EthHash) (api.EthTx, error) {
return api.EthTx{}, nil
cid := txHash.ToCid()
msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, cid, api.LookbackNoLimit, true)
if err != nil {
return api.EthTx{}, nil
}
tx, err := a.ethTxFromFilecoinMessageLookup(ctx, msgLookup)
if err != nil {
return api.EthTx{}, err
}
return tx, nil
}
func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender api.EthAddress, blkParam string) (api.EthInt, error) {
@ -155,7 +179,7 @@ func (a *EthModule) EthGetBalance(ctx context.Context, address api.EthAddress, b
}
func (a *EthModule) EthChainId(ctx context.Context) (api.EthInt, error) {
return api.EthInt(0), nil
return api.EthInt(build.Eip155ChainId), nil
}
func (a *EthModule) NetVersion(ctx context.Context) (string, error) {
@ -187,11 +211,127 @@ func (a *EthModule) EthGasPrice(ctx context.Context) (api.EthInt, error) {
// return api.EthHash{}, nil
// }
// func (a *EthModule) EthEstimateGas(ctx context.Context, tx api.EthTx, blkParam string) (api.EthInt, error) {
// return api.EthInt(0), nil
// }
//
// func (a *EthModule) EthCall(ctx context.Context, tx api.EthTx, blkParam string) (string, error) {
// return "", nil
// }
//
func (a *EthModule) EthEstimateGas(ctx context.Context, tx api.EthCall, blkParam string) (api.EthInt, error) {
return api.EthInt(0), nil
}
func (a *EthModule) EthCall(ctx context.Context, tx api.EthCall, blkParam string) (string, error) {
return "", nil
}
func (a *EthModule) ethBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTxInfo bool) (api.EthBlock, error) {
parent, err := a.Chain.LoadTipSet(ctx, ts.Parents())
if err != nil {
return api.EthBlock{}, err
}
parentKeyCid, err := parent.Key().Cid()
if err != nil {
return api.EthBlock{}, err
}
parentBlkHash, err := api.EthHashFromCid(parentKeyCid)
if err != nil {
return api.EthBlock{}, err
}
blkMsgs, err := a.Chain.BlockMsgsForTipset(ctx, ts)
if err != nil {
return api.EthBlock{}, xerrors.Errorf("error loading messages for tipset: %v: %w", ts, err)
}
block := api.NewEthBlock()
// this seems to be a very expensive way to get gasUsed of the block. may need to find an efficient way to do it
gasUsed := int64(0)
for _, blkMsg := range blkMsgs {
for _, msg := range append(blkMsg.BlsMessages, blkMsg.SecpkMessages...) {
msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, msg.Cid(), api.LookbackNoLimit, true)
if err != nil {
return api.EthBlock{}, nil
}
gasUsed += msgLookup.Receipt.GasUsed
if fullTxInfo {
tx, err := a.ethTxFromFilecoinMessageLookup(ctx, msgLookup)
if err != nil {
return api.EthBlock{}, nil
}
block.Transactions = append(block.Transactions, tx)
} else {
hash, err := api.EthHashFromCid(msg.Cid())
if err != nil {
return api.EthBlock{}, err
}
block.Transactions = append(block.Transactions, hash.String())
}
}
}
block.Number = api.EthInt(ts.Height())
block.ParentHash = parentBlkHash
block.Timestamp = api.EthInt(ts.Blocks()[0].Timestamp)
block.BaseFeePerGas = api.EthBigInt{Int: ts.Blocks()[0].ParentBaseFee.Int}
block.GasUsed = api.EthInt(gasUsed)
return block, nil
}
func (a *EthModule) ethTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLookup) (api.EthTx, error) {
cid := msgLookup.Message
txHash, err := api.EthHashFromCid(cid)
if err != nil {
return api.EthTx{}, err
}
tsCid, err := msgLookup.TipSet.Cid()
if err != nil {
return api.EthTx{}, err
}
blkHash, err := api.EthHashFromCid(tsCid)
if err != nil {
return api.EthTx{}, err
}
msg, err := a.ChainAPI.ChainGetMessage(ctx, msgLookup.Message)
if err != nil {
return api.EthTx{}, err
}
fromFilIdAddr, err := a.StateAPI.StateLookupID(ctx, msg.From, types.EmptyTSK)
if err != nil {
return api.EthTx{}, err
}
fromEthAddr, err := api.EthAddressFromFilecoinIDAddress(fromFilIdAddr)
if err != nil {
return api.EthTx{}, err
}
toFilAddr, err := a.StateAPI.StateLookupID(ctx, msg.From, types.EmptyTSK)
if err != nil {
return api.EthTx{}, err
}
toEthAddr, err := api.EthAddressFromFilecoinIDAddress(toFilAddr)
if err != nil {
return api.EthTx{}, err
}
tx := api.EthTx{
ChainID: api.EthInt(build.Eip155ChainId),
Hash: txHash,
BlockHash: blkHash,
BlockNumber: api.EthInt(msgLookup.Height),
From: fromEthAddr,
To: toEthAddr,
Value: api.EthBigInt(msg.Value),
Type: api.EthInt(2),
Gas: api.EthInt(msg.GasLimit),
MaxFeePerGas: api.EthBigInt(msg.GasFeeCap),
MaxPriorityFeePerGas: api.EthBigInt(msg.GasPremium),
V: api.EthBigIntZero,
R: api.EthBigIntZero,
S: api.EthBigIntZero,
// TODO: Input:
}
return tx, nil
}

View File

@ -95,8 +95,8 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server
rpcServer.AliasMethod("eth_maxPriorityFeePerGas", "Filecoin.EthMaxPriorityFeePerGas")
rpcServer.AliasMethod("eth_gasPrice", "Filecoin.EthGasPrice")
rpcServer.AliasMethod("eth_sendRawTransaction", "Filecoin.EthSendRawTransaction")
// rpcServer.AliasMethod("eth_estimateGas", "Filecoin.EthEstimateGas")
// rpcServer.AliasMethod("eth_call", "Filecoin.EthCall")
rpcServer.AliasMethod("eth_estimateGas", "Filecoin.EthEstimateGas")
rpcServer.AliasMethod("eth_call", "Filecoin.EthCall")
rpcServer.AliasMethod("net_version", "Filecoin.NetVersion")
rpcServer.AliasMethod("net_listening", "Filecoin.NetListening")