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 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
EthMaxPriorityFeePerGas(ctx context.Context) (EthInt, error) //perm:read
EthGasPrice(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 // 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

@ -5,15 +5,18 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
mathbig "math/big"
"strconv" "strconv"
"strings" "strings"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/multiformats/go-multihash" "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-address"
"github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/lotus/build"
) )
type EthInt int64 type EthInt int64
@ -32,12 +35,16 @@ func (e *EthInt) UnmarshalJSON(b []byte) error {
return err return err
} }
eint := EthInt(parsedInt) eint := EthInt(parsedInt)
e = &eint *e = eint
return nil return nil
} }
type EthBigInt big.Int type EthBigInt big.Int
var (
EthBigIntZero = EthBigInt{Int: big.Zero().Int}
)
func (e EthBigInt) MarshalJSON() ([]byte, error) { func (e EthBigInt) MarshalJSON() ([]byte, error) {
if e.Int == nil { if e.Int == nil {
return json.Marshal("0x0") return json.Marshal("0x0")
@ -45,6 +52,24 @@ func (e EthBigInt) MarshalJSON() ([]byte, error) {
return json.Marshal(fmt.Sprintf("0x%x", e.Int)) 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 { type EthBlock struct {
ParentHash EthHash `json:"parentHash"` ParentHash EthHash `json:"parentHash"`
Sha3Uncles EthHash `json:"sha3Uncles"` Sha3Uncles EthHash `json:"sha3Uncles"`
@ -53,24 +78,49 @@ type EthBlock struct {
TransactionsRoot EthHash `json:"transactionsRoot"` TransactionsRoot EthHash `json:"transactionsRoot"`
ReceiptsRoot EthHash `json:"receiptsRoot"` ReceiptsRoot EthHash `json:"receiptsRoot"`
// TODO: include LogsBloom // TODO: include LogsBloom
Difficulty EthInt `json:"difficulty"` Difficulty EthInt `json:"difficulty"`
Number EthInt `json:"number"` Number EthInt `json:"number"`
GasLimit EthInt `json:"gasLimit"` GasLimit EthInt `json:"gasLimit"`
GasUsed EthInt `json:"gasUsed"` GasUsed EthInt `json:"gasUsed"`
Timestamp EthInt `json:"timestamp"` Timestamp EthInt `json:"timestamp"`
Extradata []byte `json:"extraData"` Extradata []byte `json:"extraData"`
MixHash EthHash `json:"mixHash"` MixHash EthHash `json:"mixHash"`
Nonce EthNonce `json:"nonce"` Nonce EthNonce `json:"nonce"`
BaseFeePerGas EthInt `json:"baseFeePerGas"` BaseFeePerGas EthBigInt `json:"baseFeePerGas"`
Transactions EthTx `json:"transactions"` 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 { type EthTx struct {
ChainID *EthInt `json:"chainId"` ChainID EthInt `json:"chainId"`
Nonce uint64 `json:"nonce"` Nonce uint64 `json:"nonce"`
Hash EthHash `json:"hash"` Hash EthHash `json:"hash"`
BlockHash EthHash `json:"blockHash"` BlockHash EthHash `json:"blockHash"`
BlockNumber EthHash `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"`
@ -85,6 +135,26 @@ type EthTx struct {
S EthBigInt `json:"s"` 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 { type EthTxReceipt struct {
TransactionHash EthHash `json:"transactionHash"` TransactionHash EthHash `json:"transactionHash"`
TransactionIndex EthInt `json:"transacionIndex"` TransactionIndex EthInt `json:"transacionIndex"`
@ -136,7 +206,7 @@ func (a *EthAddress) UnmarshalJSON(b []byte) error {
if err != nil { if err != nil {
return err return err
} }
a = &addr copy(a[:], addr[:])
return nil return nil
} }
@ -185,7 +255,7 @@ func (h *EthHash) UnmarshalJSON(b []byte) error {
if err != nil { if err != nil {
return err return err
} }
h = &hash copy(h[:], hash[:])
return nil return nil
} }

View File

@ -30,6 +30,20 @@ func TestEthIntMarshalJSON(t *testing.T) {
require.Equal(t, j, tc.Output) 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) { func TestEthBigIntMarshalJSON(t *testing.T) {
testcases := []TestCase{ 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) { func TestEthHash(t *testing.T) {
testcases := []string{ testcases := []string{
"0x013dbb9442ca9667baccc6230fcd5c1c4b2d4d2870f4bd20681d4d47cfd15184", `"0x013dbb9442ca9667baccc6230fcd5c1c4b2d4d2870f4bd20681d4d47cfd15184"`,
"0xab8653edf9f51785664a643b47605a7ba3d917b5339a0724e7642c114d0e4738", `"0xab8653edf9f51785664a643b47605a7ba3d917b5339a0724e7642c114d0e4738"`,
} }
for _, hash := range testcases { for _, hash := range testcases {
h, err := EthHashFromHex(hash) var h EthHash
err := h.UnmarshalJSON([]byte(hash))
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, h.String(), hash) require.Equal(t, h.String(), strings.Replace(hash, `"`, "", -1))
c := h.ToCid() c := h.ToCid()
h1, err := EthHashFromCid(c) h1, err := EthHashFromCid(c)
@ -65,15 +97,17 @@ func TestEthHash(t *testing.T) {
func TestEthAddr(t *testing.T) { func TestEthAddr(t *testing.T) {
testcases := []string{ testcases := []string{
strings.ToLower("0xd4c5fb16488Aa48081296299d54b0c648C9333dA"), strings.ToLower(`"0xd4c5fb16488Aa48081296299d54b0c648C9333dA"`),
strings.ToLower("0x2C2EC67e3e1FeA8e4A39601cB3A3Cd44f5fa830d"), strings.ToLower(`"0x2C2EC67e3e1FeA8e4A39601cB3A3Cd44f5fa830d"`),
strings.ToLower("0x01184F793982104363F9a8a5845743f452dE0586"), strings.ToLower(`"0x01184F793982104363F9a8a5845743f452dE0586"`),
} }
for _, addr := range testcases { for _, addr := range testcases {
a, err := EthAddressFromHex(addr) var a EthAddress
err := a.UnmarshalJSON([]byte(addr))
require.Nil(t, err) 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) 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) 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. // EthChainId mocks base method.
func (m *MockFullNode) EthChainId(arg0 context.Context) (api.EthInt, error) { func (m *MockFullNode) EthChainId(arg0 context.Context) (api.EthInt, error) {
m.ctrl.T.Helper() 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) 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. // EthGasPrice mocks base method.
func (m *MockFullNode) EthGasPrice(arg0 context.Context) (api.EthInt, error) { func (m *MockFullNode) EthGasPrice(arg0 context.Context) (api.EthInt, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@ -222,9 +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"`
EthChainId func(p0 context.Context) (EthInt, 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"` 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 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) { func (s *FullNodeStruct) EthChainId(p0 context.Context) (EthInt, error) {
if s.Internal.EthChainId == nil { if s.Internal.EthChainId == nil {
return *new(EthInt), ErrNotSupported return *new(EthInt), ErrNotSupported
@ -1864,6 +1879,17 @@ func (s *FullNodeStub) EthChainId(p0 context.Context) (EthInt, error) {
return *new(EthInt), ErrNotSupported 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) { func (s *FullNodeStruct) EthGasPrice(p0 context.Context) (EthInt, error) {
if s.Internal.EthGasPrice == nil { if s.Internal.EthGasPrice == nil {
return *new(EthInt), ErrNotSupported return *new(EthInt), ErrNotSupported

View File

@ -131,4 +131,8 @@ const InteractivePoRepConfidence = 6
const BootstrapPeerThreshold = 1 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 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 // BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start
const BootstrapPeerThreshold = 2 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 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 // BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start
const BootstrapPeerThreshold = 4 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 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 // BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start
const BootstrapPeerThreshold = 2 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 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 // BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start
const BootstrapPeerThreshold = 4 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 // we skip checks on message validity in this block to sidestep the zero-bls signature
var WhitelistedBlock = MustParseCid("bafy2bzaceapyg2uyzk7vueh3xccxkuwbz3nxewjyguoxvhx77malc2lzn2ybi") var WhitelistedBlock = MustParseCid("bafy2bzaceapyg2uyzk7vueh3xccxkuwbz3nxewjyguoxvhx77malc2lzn2ybi")

View File

@ -130,3 +130,7 @@ var (
) )
const BootstrapPeerThreshold = 1 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 // BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start
const BootstrapPeerThreshold = 2 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 var WhitelistedBlock = cid.Undef

View File

@ -99,6 +99,14 @@ func (k *TipSetKey) UnmarshalJSON(b []byte) error {
return nil 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) { func (k TipSetKey) ToStorageBlock() (block.Block, error) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
if err := k.MarshalCBOR(buf); err != nil { if err := k.MarshalCBOR(buf); err != nil {

View File

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

View File

@ -10,6 +10,7 @@ import (
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api" "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/store"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
) )
@ -33,8 +34,10 @@ 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)
EthMaxPriorityFeePerGas(ctx context.Context) (api.EthInt, error)
EthGasPrice(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) // EthSendRawTransaction(ctx context.Context, tx api.EthTx) (api.EthHash, error)
} }
@ -47,6 +50,8 @@ type EthModule struct {
fx.In fx.In
Chain *store.ChainStore Chain *store.ChainStore
ChainAPI
StateAPI 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) { 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) { 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) { 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) { 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) { 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) { 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 // return api.EthHash{}, nil
// } // }
// func (a *EthModule) EthEstimateGas(ctx context.Context, tx api.EthTx, 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 return api.EthInt(0), nil
// } }
//
// func (a *EthModule) EthCall(ctx context.Context, tx api.EthTx, blkParam string) (string, error) { func (a *EthModule) EthCall(ctx context.Context, tx api.EthCall, blkParam string) (string, error) {
// return "", nil 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_maxPriorityFeePerGas", "Filecoin.EthMaxPriorityFeePerGas")
rpcServer.AliasMethod("eth_gasPrice", "Filecoin.EthGasPrice") rpcServer.AliasMethod("eth_gasPrice", "Filecoin.EthGasPrice")
rpcServer.AliasMethod("eth_sendRawTransaction", "Filecoin.EthSendRawTransaction") rpcServer.AliasMethod("eth_sendRawTransaction", "Filecoin.EthSendRawTransaction")
// rpcServer.AliasMethod("eth_estimateGas", "Filecoin.EthEstimateGas") rpcServer.AliasMethod("eth_estimateGas", "Filecoin.EthEstimateGas")
// rpcServer.AliasMethod("eth_call", "Filecoin.EthCall") rpcServer.AliasMethod("eth_call", "Filecoin.EthCall")
rpcServer.AliasMethod("net_version", "Filecoin.NetVersion") rpcServer.AliasMethod("net_version", "Filecoin.NetVersion")
rpcServer.AliasMethod("net_listening", "Filecoin.NetListening") rpcServer.AliasMethod("net_listening", "Filecoin.NetListening")