api: ethrpc: implement a few ethereum JSON-RPC API methods (#9208)

This commit is contained in:
Kevin Li
2022-11-09 19:23:11 +02:00
committed by vyzo
parent ef90ba7cef
commit 4652d8559f
12 changed files with 1631 additions and 0 deletions
+1
View File
@@ -153,6 +153,7 @@ var ChainNode = Options(
Override(new(messagepool.Provider), messagepool.NewProvider),
Override(new(messagesigner.MpoolNonceAPI), From(new(*messagepool.MessagePool))),
Override(new(full.ChainModuleAPI), From(new(full.ChainModule))),
Override(new(full.EthModuleAPI), From(new(full.EthModule))),
Override(new(full.GasModuleAPI), From(new(full.GasModule))),
Override(new(full.MpoolModuleAPI), From(new(full.MpoolModule))),
Override(new(full.StateModuleAPI), From(new(full.StateModule))),
+1
View File
@@ -34,6 +34,7 @@ type FullNodeAPI struct {
full.MsigAPI
full.WalletAPI
full.SyncAPI
full.EthAPI
DS dtypes.MetadataDS
NetworkName dtypes.NetworkName
+197
View File
@@ -0,0 +1,197 @@
package full
import (
"context"
"strconv"
"go.uber.org/fx"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
)
type EthModuleAPI interface {
EthBlockNumber(ctx context.Context) (api.EthInt, error)
EthAccounts(ctx context.Context) ([]api.EthAddress, error)
EthGetBlockTransactionCountByNumber(ctx context.Context, blkNum api.EthInt) (api.EthInt, error)
EthGetBlockTransactionCountByHash(ctx context.Context, blkHash api.EthHash) (api.EthInt, error)
EthGetBlockByHash(ctx context.Context, blkHash api.EthHash, fullTxInfo bool) (api.EthBlock, error)
EthGetBlockByNumber(ctx context.Context, blkNum api.EthInt, fullTxInfo bool) (api.EthBlock, error)
EthGetTransactionByHash(ctx context.Context, txHash api.EthHash) (api.EthTx, error)
EthGetTransactionCount(ctx context.Context, sender api.EthAddress, blkOpt string) (api.EthInt, error)
EthGetTransactionReceipt(ctx context.Context, blkHash api.EthHash) (api.EthTxReceipt, error)
EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash api.EthHash, txIndex api.EthInt) (api.EthTx, error)
EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum api.EthInt, txIndex api.EthInt) (api.EthTx, error)
EthGetCode(ctx context.Context, address api.EthAddress) (string, error)
EthGetStorageAt(ctx context.Context, address api.EthAddress, position api.EthInt, blkParam string) (string, error)
EthGetBalance(ctx context.Context, address api.EthAddress, blkParam string) (api.EthBigInt, error)
EthChainId(ctx context.Context) (api.EthInt, error)
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)
// EthSendRawTransaction(ctx context.Context, tx api.EthTx) (api.EthHash, error)
}
var _ EthModuleAPI = *new(api.FullNode)
// EthModule provides a default implementation of EthModuleAPI.
// It can be swapped out with another implementation through Dependency
// Injection (for example with a thin RPC client).
type EthModule struct {
fx.In
Chain *store.ChainStore
StateAPI
}
var _ EthModuleAPI = (*EthModule)(nil)
type EthAPI struct {
fx.In
Chain *store.ChainStore
EthModuleAPI
}
func (a *EthModule) EthBlockNumber(context.Context) (api.EthInt, error) {
height := a.Chain.GetHeaviestTipSet().Height()
return api.EthInt(height), nil
}
func (a *EthModule) EthAccounts(context.Context) ([]api.EthAddress, error) {
// The lotus node is not expected to hold manage accounts, so we'll always return an empty array
return []api.EthAddress{}, nil
}
func (a *EthModule) countTipsetMsgs(ctx context.Context, ts *types.TipSet) (int, error) {
blkMsgs, err := a.Chain.BlockMsgsForTipset(ctx, ts)
if err != nil {
return 0, xerrors.Errorf("error loading messages for tipset: %v: %w", ts, err)
}
count := 0
for _, blkMsg := range blkMsgs {
// TODO: may need to run canonical ordering and deduplication here
count += len(blkMsg.BlsMessages) + len(blkMsg.SecpkMessages)
}
return count, nil
}
func (a *EthModule) EthGetBlockTransactionCountByNumber(ctx context.Context, blkNum api.EthInt) (api.EthInt, error) {
ts, err := a.Chain.GetTipsetByHeight(ctx, abi.ChainEpoch(blkNum), nil, false)
if err != nil {
return api.EthInt(0), xerrors.Errorf("error loading tipset %s: %w", ts, err)
}
count, err := a.countTipsetMsgs(ctx, ts)
return api.EthInt(count), err
}
func (a *EthModule) EthGetBlockTransactionCountByHash(ctx context.Context, blkHash api.EthHash) (api.EthInt, error) {
ts, err := a.Chain.GetTipSetByCid(ctx, blkHash.ToCid())
if err != nil {
return api.EthInt(0), xerrors.Errorf("error loading tipset %s: %w", ts, err)
}
count, err := a.countTipsetMsgs(ctx, ts)
return api.EthInt(count), err
}
func (a *EthModule) EthGetBlockByHash(ctx context.Context, blkHash api.EthHash, fullTxInfo bool) (api.EthBlock, error) {
return api.EthBlock{}, nil
}
func (a *EthModule) EthGetBlockByNumber(ctx context.Context, blkNum api.EthInt, fullTxInfo bool) (api.EthBlock, error) {
return api.EthBlock{}, nil
}
func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash api.EthHash) (api.EthTx, error) {
return api.EthTx{}, nil
}
func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender api.EthAddress, blkParam string) (api.EthInt, error) {
return api.EthInt(0), nil
}
func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, blkHash api.EthHash) (api.EthTxReceipt, error) {
return api.EthTxReceipt{}, nil
}
func (a *EthModule) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash api.EthHash, txIndex api.EthInt) (api.EthTx, error) {
return api.EthTx{}, nil
}
func (a *EthModule) EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum api.EthInt, txIndex api.EthInt) (api.EthTx, error) {
return api.EthTx{}, nil
}
// EthGetCode returns string value of the compiled bytecode
func (a *EthModule) EthGetCode(ctx context.Context, address api.EthAddress) (string, error) {
return "", nil
}
func (a *EthModule) EthGetStorageAt(ctx context.Context, address api.EthAddress, position api.EthInt, blkParam string) (string, error) {
return "", nil
}
func (a *EthModule) EthGetBalance(ctx context.Context, address api.EthAddress, blkParam string) (api.EthBigInt, error) {
filAddr, err := address.ToFilecoinAddress()
if err != nil {
return api.EthBigInt{}, err
}
actor, err := a.StateGetActor(ctx, filAddr, types.EmptyTSK)
if err != nil {
return api.EthBigInt{}, err
}
return api.EthBigInt{Int: actor.Balance.Int}, nil
}
func (a *EthModule) EthChainId(ctx context.Context) (api.EthInt, error) {
return api.EthInt(0), nil
}
func (a *EthModule) NetVersion(ctx context.Context) (string, error) {
// Note that networkId is not encoded in hex
nv, err := a.StateNetworkVersion(ctx, types.EmptyTSK)
if err != nil {
return "", err
}
return strconv.FormatUint(uint64(nv), 10), nil
}
func (a *EthModule) NetListening(ctx context.Context) (bool, error) {
return true, nil
}
func (a *EthModule) EthProtocolVersion(ctx context.Context) (api.EthInt, error) {
return api.EthInt(0), nil
}
func (a *EthModule) EthMaxPriorityFeePerGas(ctx context.Context) (api.EthInt, error) {
return api.EthInt(0), nil
}
func (a *EthModule) EthGasPrice(ctx context.Context) (api.EthInt, error) {
return api.EthInt(0), nil
}
// func (a *EthModule) EthSendRawTransaction(ctx context.Context tx api.EthTx) (api.EthHash, 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
// }
//
+28
View File
@@ -73,6 +73,34 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server
rpcServer.Register("Filecoin", hnd)
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")
// TODO: use reflect to automatically register all the eth aliases
rpcServer.AliasMethod("eth_accounts", "Filecoin.EthAccounts")
rpcServer.AliasMethod("eth_blockNumber", "Filecoin.EthBlockNumber")
rpcServer.AliasMethod("eth_getBlockTransactionCountByNumber", "Filecoin.EthGetBlockTransactionCountByNumber")
rpcServer.AliasMethod("eth_getBlockTransactionCountByHash", "Filecoin.EthGetBlockTransactionCountByHash")
rpcServer.AliasMethod("eth_getBlockByHash", "Filecoin.EthGetBlockByHash")
rpcServer.AliasMethod("eth_getBlockByNumber", "Filecoin.EthGetBlockByNumber")
rpcServer.AliasMethod("eth_getTransactionByHash", "Filecoin.EthGetTransactionByHash")
rpcServer.AliasMethod("eth_getTransactionCount", "Filecoin.EthGetTransactionCount")
rpcServer.AliasMethod("eth_getTransactionReceipt", "Filecoin.EthGetTransactionReceipt")
rpcServer.AliasMethod("eth_getTransactionByBlockHashAndIndex", "Filecoin.EthGetTransactionByBlockHashAndIndex")
rpcServer.AliasMethod("eth_getTransactionByBlockNumberAndIndex", "Filecoin.EthGetTransactionByBlockNumberAndIndex")
rpcServer.AliasMethod("eth_getCode", "Filecoin.EthGetCode")
rpcServer.AliasMethod("eth_getStorageAt", "Filecoin.EthGetStorageAt")
rpcServer.AliasMethod("eth_getBalance", "Filecoin.EthGetBalance")
rpcServer.AliasMethod("eth_chainId", "Filecoin.EthChainId")
rpcServer.AliasMethod("eth_protocolVersion", "Filecoin.EthProtocolVersion")
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("net_version", "Filecoin.NetVersion")
rpcServer.AliasMethod("net_listening", "Filecoin.NetListening")
var handler http.Handler = rpcServer
if permissioned {
handler = &auth.Handler{Verify: a.AuthVerify, Next: rpcServer.ServeHTTP}