From d1212725f85727032f87dfb26f659966541e2023 Mon Sep 17 00:00:00 2001 From: crypto-facs <84574577+crypto-facs@users.noreply.github.com> Date: Mon, 26 Jul 2021 13:15:55 +0200 Subject: [PATCH] feat: configurable JSON-RPC APIs (#349) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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> --- CHANGELOG.md | 1 + cmd/ethermintd/config/config.go | 27 ++++--- docs/basics/json_rpc.md | 14 ++++ ethereum/rpc/apis.go | 134 ++++++++++++++++++++----------- init.sh | 2 +- server/evmrpc.go | 4 +- server/start.go | 2 + tests/solidity/init-test-node.sh | 2 +- testutil/network/util.go | 4 +- 9 files changed, 130 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7ae2374..19e80d18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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#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) [#349](https://github.com/tharsis/ethermint/pull/349) Implement configurable JSON-RPC APIs to manage enabled namespaces. ### Bug Fixes diff --git a/cmd/ethermintd/config/config.go b/cmd/ethermintd/config/config.go index be3f2b30..88ee7cbb 100644 --- a/cmd/ethermintd/config/config.go +++ b/cmd/ethermintd/config/config.go @@ -18,6 +18,11 @@ const ( 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. // return "", nil if no custom configuration is required for the application. 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. type EVMRPCConfig struct { // RPCAddress defines the HTTP server to listen on RPCAddress string `mapstructure:"address"` // WsAddress defines the WebSocket server to listen on 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 bool `mapstructure:"enable"` // EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk) 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 // from the SDK as well as the EVM configuration to enable the JSON-RPC APIs. type Config struct { @@ -95,6 +103,7 @@ func GetConfig(v *viper.Viper) Config { Config: cfg, EVMRPC: EVMRPCConfig{ Enable: v.GetBool("evm-rpc.enable"), + API: v.GetStringSlice("evm-rpc.api"), RPCAddress: v.GetString("evm-rpc.address"), WsAddress: v.GetString("evm-rpc.ws-address"), }, diff --git a/docs/basics/json_rpc.md b/docs/basics/json_rpc.md index aee7afb8..315e4994 100644 --- a/docs/basics/json_rpc.md +++ b/docs/basics/json_rpc.md @@ -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} - [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 | Method | Namespace | Implemented | Notes | diff --git a/ethereum/rpc/apis.go b/ethereum/rpc/apis.go index 898d7a8a..0b93bc5e 100644 --- a/ethereum/rpc/apis.go +++ b/ethereum/rpc/apis.go @@ -5,7 +5,6 @@ package rpc import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/server" - "github.com/ethereum/go-ethereum/rpc" "github.com/tharsis/ethermint/ethereum/rpc/backend" "github.com/tharsis/ethermint/ethereum/rpc/namespaces/debug" @@ -33,53 +32,94 @@ const ( ) // 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) - backend := backend.NewEVMBackend(ctx.Logger, clientCtx) - ethAPI := eth.NewPublicAPI(ctx.Logger, clientCtx, backend, nonceLock) + evmBackend := backend.NewEVMBackend(ctx.Logger, clientCtx) + ethAPI := eth.NewPublicAPI(ctx.Logger, clientCtx, evmBackend, nonceLock) - return []rpc.API{ - { - Namespace: Web3Namespace, - Version: apiVersion, - Service: web3.NewPublicAPI(), - Public: true, - }, - { - Namespace: EthNamespace, - Version: apiVersion, - Service: ethAPI, - Public: true, - }, - { - Namespace: EthNamespace, - Version: apiVersion, - Service: filters.NewPublicAPI(ctx.Logger, tmWSClient, backend), - Public: true, - }, - { - Namespace: NetNamespace, - Version: apiVersion, - Service: net.NewPublicAPI(clientCtx), - Public: true, - }, - { - Namespace: PersonalNamespace, - Version: apiVersion, - Service: personal.NewAPI(ctx.Logger, ethAPI), - Public: true, - }, - { - Namespace: TxPoolNamespace, - Version: apiVersion, - Service: txpool.NewPublicAPI(ctx.Logger), - Public: true, - }, - { - Namespace: DebugNamespace, - Version: apiVersion, - Service: debug.NewInternalAPI(ctx), - Public: true, - }, + var apis []rpc.API + + // remove duplicates + selectedAPIs = unique(selectedAPIs) + + for index := range selectedAPIs { + switch selectedAPIs[index] { + case EthNamespace: + apis = append(apis, + rpc.API{ + Namespace: EthNamespace, + Version: apiVersion, + Service: ethAPI, + Public: true, + }, + rpc.API{ + Namespace: EthNamespace, + Version: apiVersion, + Service: filters.NewPublicAPI(ctx.Logger, tmWSClient, evmBackend), + 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, + Version: apiVersion, + Service: net.NewPublicAPI(clientCtx), + Public: true, + }, + ) + case PersonalNamespace: + apis = append(apis, + rpc.API{ + Namespace: PersonalNamespace, + Version: apiVersion, + Service: personal.NewAPI(ctx.Logger, ethAPI), + Public: true, + }, + ) + case TxPoolNamespace: + apis = append(apis, + rpc.API{ + Namespace: TxPoolNamespace, + Version: apiVersion, + Service: txpool.NewPublicAPI(ctx.Logger), + Public: true, + }, + ) + case DebugNamespace: + apis = append(apis, + rpc.API{ + Namespace: DebugNamespace, + Version: apiVersion, + Service: debug.NewInternalAPI(ctx), + 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 } diff --git a/init.sh b/init.sh index 9af30fac..d9f6bfd4 100755 --- a/init.sh +++ b/init.sh @@ -84,4 +84,4 @@ if [[ $1 == "pending" ]]; then fi # 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 diff --git a/server/evmrpc.go b/server/evmrpc.go index c5023804..6c60e660 100644 --- a/server/evmrpc.go +++ b/server/evmrpc.go @@ -21,7 +21,9 @@ func StartEVMRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr string tmWsClient := ConnectTmWS(tmRPCAddr, tmEndpoint) rpcServer := ethrpc.NewServer() - apis := rpc.GetRPCAPIs(ctx, clientCtx, tmWsClient) + + rpcAPIArr := config.EVMRPC.API + apis := rpc.GetRPCAPIs(ctx, clientCtx, tmWsClient, rpcAPIArr) for _, api := range apis { if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil { diff --git a/server/start.go b/server/start.go index e9746a1a..6fb06522 100644 --- a/server/start.go +++ b/server/start.go @@ -72,6 +72,7 @@ const ( flagGRPCEnable = "grpc.enable" flagGRPCAddress = "grpc.address" flagEVMRPCEnable = "evm-rpc.enable" + flagEVMRPCAPI = "evm-rpc.api" flagEVMRPCAddress = "evm-rpc.address" flagEVMWSAddress = "evm-rpc.ws-address" 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().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(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)") diff --git a/tests/solidity/init-test-node.sh b/tests/solidity/init-test-node.sh index fe9a8c29..280620c9 100755 --- a/tests/solidity/init-test-node.sh +++ b/tests/solidity/init-test-node.sh @@ -43,4 +43,4 @@ ethermintd collect-gentxs ethermintd validate-genesis # 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 diff --git a/testutil/network/util.go b/testutil/network/util.go index b49e3671..736291ef 100644 --- a/testutil/network/util.go +++ b/testutil/network/util.go @@ -121,7 +121,9 @@ func startInProcess(cfg Config, val *Validator) error { 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 { 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)