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-11-09 19:23:49 +02:00
committed by vyzo
co-authored by Raúl Kripalani
parent 4652d8559f
commit e5bb5b7430
16 changed files with 455 additions and 87 deletions
+3 -2
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
+86 -16
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
}
+51 -9
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)
}
+30
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()
+27 -1
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