feat: configurable JSON-RPC APIs (#349)

* fix confusing name

* feat: Enable configurable grpc apis

* docs: Update docs and changelog

* Organized flags order

* fix linter

* fix linter

* fix maligned struct

* fix typo in docs

* fix unnecesary duplicate

* Update cmd/ethermintd/config/config.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Update cmd/ethermintd/config/config.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Update cmd/ethermintd/config/config.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Update docs/basics/json_rpc.md

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* fix eth to be manage as default

* Update init.sh

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Update tests/solidity/init-test-node.sh

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* add default case

* add default enable api namespaces

* update changelog

* fix namespaces array  handler

* remove duplicated changelog

* fix typo

* remove duplicates namespaces and fix eth namespace issue

* fix variable name

* break line in docs

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
crypto-facs 2021-07-26 13:15:55 +02:00 committed by GitHub
parent 54581269b8
commit d1212725f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 130 additions and 60 deletions

View File

@ -74,6 +74,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (rpc) [tharsis#176](https://github.com/tharsis/ethermint/issues/176) Support fetching pending nonce * (rpc) [tharsis#176](https://github.com/tharsis/ethermint/issues/176) Support fetching pending nonce
* (rpc) [tharsis#272](https://github.com/tharsis/ethermint/pull/272) do binary search to estimate gas accurately * (rpc) [tharsis#272](https://github.com/tharsis/ethermint/pull/272) do binary search to estimate gas accurately
* (rpc) [#313](https://github.com/tharsis/ethermint/pull/313) Implement internal debug namespace (Not including logger functions nor traces). * (rpc) [#313](https://github.com/tharsis/ethermint/pull/313) Implement internal debug namespace (Not including logger functions nor traces).
* (rpc) [#349](https://github.com/tharsis/ethermint/pull/349) Implement configurable JSON-RPC APIs to manage enabled namespaces.
### Bug Fixes ### Bug Fixes

View File

@ -18,6 +18,11 @@ const (
DefaultEVMWSAddress = "0.0.0.0:8546" DefaultEVMWSAddress = "0.0.0.0:8546"
) )
// GetDefaultAPINamespaces returns the default list of JSON-RPC namespaces that should be enabled
func GetDefaultAPINamespaces() []string {
return []string{"eth"}
}
// AppConfig helps to override default appConfig template and configs. // AppConfig helps to override default appConfig template and configs.
// return "", nil if no custom configuration is required for the application. // return "", nil if no custom configuration is required for the application.
func AppConfig() (string, interface{}) { func AppConfig() (string, interface{}) {
@ -57,27 +62,30 @@ func DefaultConfig() *Config {
} }
} }
// DefaultEVMConfig returns an EVM config with the JSON-RPC API enabled by default
func DefaultEVMConfig() *EVMRPCConfig {
return &EVMRPCConfig{
Enable: true,
RPCAddress: DefaultEVMAddress,
WsAddress: DefaultEVMWSAddress,
}
}
// EVMRPCConfig defines configuration for the EVM RPC server. // EVMRPCConfig defines configuration for the EVM RPC server.
type EVMRPCConfig struct { type EVMRPCConfig struct {
// RPCAddress defines the HTTP server to listen on // RPCAddress defines the HTTP server to listen on
RPCAddress string `mapstructure:"address"` RPCAddress string `mapstructure:"address"`
// WsAddress defines the WebSocket server to listen on // WsAddress defines the WebSocket server to listen on
WsAddress string `mapstructure:"ws-address"` WsAddress string `mapstructure:"ws-address"`
// API defines a list of JSON-RPC namespaces that should be enabled
API []string `mapstructure:"api"`
// Enable defines if the EVM RPC server should be enabled. // Enable defines if the EVM RPC server should be enabled.
Enable bool `mapstructure:"enable"` Enable bool `mapstructure:"enable"`
// EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk) // EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk)
EnableUnsafeCORS bool `mapstructure:"enable-unsafe-cors"` EnableUnsafeCORS bool `mapstructure:"enable-unsafe-cors"`
} }
// DefaultEVMConfig returns an EVM config with the JSON-RPC API enabled by default
func DefaultEVMConfig() *EVMRPCConfig {
return &EVMRPCConfig{
Enable: true,
API: GetDefaultAPINamespaces(),
RPCAddress: DefaultEVMAddress,
WsAddress: DefaultEVMWSAddress,
}
}
// Config defines the server's top level configuration. It includes the default app config // Config defines the server's top level configuration. It includes the default app config
// from the SDK as well as the EVM configuration to enable the JSON-RPC APIs. // from the SDK as well as the EVM configuration to enable the JSON-RPC APIs.
type Config struct { type Config struct {
@ -95,6 +103,7 @@ func GetConfig(v *viper.Viper) Config {
Config: cfg, Config: cfg,
EVMRPC: EVMRPCConfig{ EVMRPC: EVMRPCConfig{
Enable: v.GetBool("evm-rpc.enable"), Enable: v.GetBool("evm-rpc.enable"),
API: v.GetStringSlice("evm-rpc.api"),
RPCAddress: v.GetString("evm-rpc.address"), RPCAddress: v.GetString("evm-rpc.address"),
WsAddress: v.GetString("evm-rpc.ws-address"), WsAddress: v.GetString("evm-rpc.ws-address"),
}, },

View File

@ -11,6 +11,20 @@ Check the JSON-RPC methods and namespaces supported on Ethermint. {synopsis}
- [Ethereum JSON-RPC](https://eth.wiki/json-rpc/API) {prereq} - [Ethereum JSON-RPC](https://eth.wiki/json-rpc/API) {prereq}
- [Geth JSON-RPC APIs](https://geth.ethereum.org/docs/rpc/server) {prereq} - [Geth JSON-RPC APIs](https://geth.ethereum.org/docs/rpc/server) {prereq}
## JSON-RPC Server
To enable RPC server use the following flag (set to true by default).
```
ethermintd start --evm-rpc.enable
```
By default, only `eth` namespace is enabled. In order to enable other namespaces use flag `--evm-rpc.api`.
```
ethermintd start --evm-rpc.api eth,txpool,personal,net,debug,web3
```
## JSON-RPC Methods ## JSON-RPC Methods
| Method | Namespace | Implemented | Notes | | Method | Namespace | Implemented | Notes |

View File

@ -5,7 +5,6 @@ package rpc
import ( import (
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/tharsis/ethermint/ethereum/rpc/backend" "github.com/tharsis/ethermint/ethereum/rpc/backend"
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/debug" "github.com/tharsis/ethermint/ethereum/rpc/namespaces/debug"
@ -33,53 +32,94 @@ const (
) )
// GetRPCAPIs returns the list of all APIs // GetRPCAPIs returns the list of all APIs
func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient) []rpc.API { func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient, selectedAPIs []string) []rpc.API {
nonceLock := new(types.AddrLocker) nonceLock := new(types.AddrLocker)
backend := backend.NewEVMBackend(ctx.Logger, clientCtx) evmBackend := backend.NewEVMBackend(ctx.Logger, clientCtx)
ethAPI := eth.NewPublicAPI(ctx.Logger, clientCtx, backend, nonceLock) ethAPI := eth.NewPublicAPI(ctx.Logger, clientCtx, evmBackend, nonceLock)
return []rpc.API{ var apis []rpc.API
{
Namespace: Web3Namespace, // remove duplicates
Version: apiVersion, selectedAPIs = unique(selectedAPIs)
Service: web3.NewPublicAPI(),
Public: true, for index := range selectedAPIs {
}, switch selectedAPIs[index] {
{ case EthNamespace:
apis = append(apis,
rpc.API{
Namespace: EthNamespace, Namespace: EthNamespace,
Version: apiVersion, Version: apiVersion,
Service: ethAPI, Service: ethAPI,
Public: true, Public: true,
}, },
{ rpc.API{
Namespace: EthNamespace, Namespace: EthNamespace,
Version: apiVersion, Version: apiVersion,
Service: filters.NewPublicAPI(ctx.Logger, tmWSClient, backend), Service: filters.NewPublicAPI(ctx.Logger, tmWSClient, evmBackend),
Public: true, Public: true,
}, },
{ )
case Web3Namespace:
apis = append(apis,
rpc.API{
Namespace: Web3Namespace,
Version: apiVersion,
Service: web3.NewPublicAPI(),
Public: true,
},
)
case NetNamespace:
apis = append(apis,
rpc.API{
Namespace: NetNamespace, Namespace: NetNamespace,
Version: apiVersion, Version: apiVersion,
Service: net.NewPublicAPI(clientCtx), Service: net.NewPublicAPI(clientCtx),
Public: true, Public: true,
}, },
{ )
case PersonalNamespace:
apis = append(apis,
rpc.API{
Namespace: PersonalNamespace, Namespace: PersonalNamespace,
Version: apiVersion, Version: apiVersion,
Service: personal.NewAPI(ctx.Logger, ethAPI), Service: personal.NewAPI(ctx.Logger, ethAPI),
Public: true, Public: true,
}, },
{ )
case TxPoolNamespace:
apis = append(apis,
rpc.API{
Namespace: TxPoolNamespace, Namespace: TxPoolNamespace,
Version: apiVersion, Version: apiVersion,
Service: txpool.NewPublicAPI(ctx.Logger), Service: txpool.NewPublicAPI(ctx.Logger),
Public: true, Public: true,
}, },
{ )
case DebugNamespace:
apis = append(apis,
rpc.API{
Namespace: DebugNamespace, Namespace: DebugNamespace,
Version: apiVersion, Version: apiVersion,
Service: debug.NewInternalAPI(ctx), Service: debug.NewInternalAPI(ctx),
Public: true, Public: true,
}, },
)
default:
ctx.Logger.Error("invalid namespace value", "namespace", selectedAPIs[index])
} }
} }
return apis
}
func unique(intSlice []string) []string {
keys := make(map[string]bool)
var list []string
for _, entry := range intSlice {
if _, value := keys[entry]; !value {
keys[entry] = true
list = append(list, entry)
}
}
return list
}

View File

@ -84,4 +84,4 @@ if [[ $1 == "pending" ]]; then
fi fi
# Start the node (remove the --pruning=nothing flag if historical queries are not needed) # Start the node (remove the --pruning=nothing flag if historical queries are not needed)
ethermintd start --pruning=nothing $TRACE --log_level $LOGLEVEL --minimum-gas-prices=0.0001aphoton ethermintd start --pruning=nothing $TRACE --log_level $LOGLEVEL --minimum-gas-prices=0.0001aphoton --evm-rpc.api eth,txpool,personal,net,debug,web3

View File

@ -21,7 +21,9 @@ func StartEVMRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr string
tmWsClient := ConnectTmWS(tmRPCAddr, tmEndpoint) tmWsClient := ConnectTmWS(tmRPCAddr, tmEndpoint)
rpcServer := ethrpc.NewServer() rpcServer := ethrpc.NewServer()
apis := rpc.GetRPCAPIs(ctx, clientCtx, tmWsClient)
rpcAPIArr := config.EVMRPC.API
apis := rpc.GetRPCAPIs(ctx, clientCtx, tmWsClient, rpcAPIArr)
for _, api := range apis { for _, api := range apis {
if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil { if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil {

View File

@ -72,6 +72,7 @@ const (
flagGRPCEnable = "grpc.enable" flagGRPCEnable = "grpc.enable"
flagGRPCAddress = "grpc.address" flagGRPCAddress = "grpc.address"
flagEVMRPCEnable = "evm-rpc.enable" flagEVMRPCEnable = "evm-rpc.enable"
flagEVMRPCAPI = "evm-rpc.api"
flagEVMRPCAddress = "evm-rpc.address" flagEVMRPCAddress = "evm-rpc.address"
flagEVMWSAddress = "evm-rpc.ws-address" flagEVMWSAddress = "evm-rpc.ws-address"
flagEVMEnableUnsafeCORS = "evm-rpc.enable-unsafe-cors" flagEVMEnableUnsafeCORS = "evm-rpc.enable-unsafe-cors"
@ -179,6 +180,7 @@ which accepts a path for the resulting pprof file.
cmd.Flags().String(flagGRPCWebAddress, serverconfig.DefaultGRPCWebAddress, "The gRPC-Web server address to listen on") cmd.Flags().String(flagGRPCWebAddress, serverconfig.DefaultGRPCWebAddress, "The gRPC-Web server address to listen on")
cmd.Flags().Bool(flagEVMRPCEnable, true, "Define if the gRPC server should be enabled") cmd.Flags().Bool(flagEVMRPCEnable, true, "Define if the gRPC server should be enabled")
cmd.Flags().StringSlice(flagEVMRPCAPI, config.GetDefaultAPINamespaces(), "Defines a list of JSON-RPC namespaces that should be enabled")
cmd.Flags().String(flagEVMRPCAddress, config.DefaultEVMAddress, "the EVM RPC server address to listen on") cmd.Flags().String(flagEVMRPCAddress, config.DefaultEVMAddress, "the EVM RPC server address to listen on")
cmd.Flags().String(flagEVMWSAddress, config.DefaultEVMWSAddress, "the EVM WS server address to listen on") cmd.Flags().String(flagEVMWSAddress, config.DefaultEVMWSAddress, "the EVM WS server address to listen on")
cmd.Flags().Bool(flagEVMEnableUnsafeCORS, false, "Define if the EVM RPC server should enabled CORS (unsafe - use it at your own risk)") cmd.Flags().Bool(flagEVMEnableUnsafeCORS, false, "Define if the EVM RPC server should enabled CORS (unsafe - use it at your own risk)")

View File

@ -43,4 +43,4 @@ ethermintd collect-gentxs
ethermintd validate-genesis ethermintd validate-genesis
# Start the node (remove the --pruning=nothing flag if historical queries are not needed) # Start the node (remove the --pruning=nothing flag if historical queries are not needed)
ethermintd start --pruning=nothing --rpc.unsafe --keyring-backend test --trace --log_level info ethermintd start --pruning=nothing --rpc.unsafe --keyring-backend test --trace --log_level info --evm-rpc.api eth,txpool,personal,net,debug,web3

View File

@ -121,7 +121,9 @@ func startInProcess(cfg Config, val *Validator) error {
val.jsonRPC = jsonrpc.NewServer() val.jsonRPC = jsonrpc.NewServer()
apis := rpc.GetRPCAPIs(val.Ctx, val.ClientCtx, tmWsClient) rpcAPIArr := val.AppConfig.EVMRPC.API
apis := rpc.GetRPCAPIs(val.Ctx, val.ClientCtx, tmWsClient, rpcAPIArr)
for _, api := range apis { for _, api := range apis {
if err := val.jsonRPC.RegisterName(api.Namespace, api.Service); err != nil { if err := val.jsonRPC.RegisterName(api.Namespace, api.Service); err != nil {
return fmt.Errorf("failed to register JSON-RPC namespace %s: %w", api.Namespace, err) return fmt.Errorf("failed to register JSON-RPC namespace %s: %w", api.Namespace, err)