api: ethrpc: implement a few ethereum JSON-RPC API methods (#9208)
This commit is contained in:
@@ -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))),
|
||||
|
||||
@@ -34,6 +34,7 @@ type FullNodeAPI struct {
|
||||
full.MsigAPI
|
||||
full.WalletAPI
|
||||
full.SyncAPI
|
||||
full.EthAPI
|
||||
|
||||
DS dtypes.MetadataDS
|
||||
NetworkName dtypes.NetworkName
|
||||
|
||||
@@ -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
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user