From 0543a289412158606cbb30c1717f9e6a68f639f9 Mon Sep 17 00:00:00 2001 From: yihuang Date: Tue, 19 Apr 2022 20:49:05 +0800 Subject: [PATCH] rpc: make json-rpc API namespaces extensible (#1056) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * make jsonrpc api namespaces extensible Closes: #1052, #1037 * Apply suggestions from code review Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> --- CHANGELOG.md | 1 + rpc/apis.go | 117 +++++++++++++++++++++++----------------- server/config/config.go | 2 +- 3 files changed, 70 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 731d032a..40059c2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements * (deps) [tharsis#1046](https://github.com/tharsis/ethermint/pull/1046) Bump Cosmos SDK version to [`v0.45.3`](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.45.3) +* (rpc) [tharsis#1056](https://github.com/tharsis/ethermint/pull/1056) Make json-rpc namespaces extensible ### Bug Fixes diff --git a/rpc/apis.go b/rpc/apis.go index 29c431a9..e437c431 100644 --- a/rpc/apis.go +++ b/rpc/apis.go @@ -3,6 +3,8 @@ package rpc import ( + "fmt" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/server" @@ -35,102 +37,119 @@ const ( apiVersion = "1.0" ) -// GetRPCAPIs returns the list of all APIs -func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient, selectedAPIs []string) []rpc.API { - nonceLock := new(types.AddrLocker) - evmBackend := backend.NewEVMBackend(ctx, ctx.Logger, clientCtx) +// APICreator creates the json-rpc api implementations. +type APICreator = func(*server.Context, client.Context, *rpcclient.WSClient) []rpc.API - var apis []rpc.API - // remove duplicates - selectedAPIs = unique(selectedAPIs) +// apiCreators defines the json-rpc api namespaces. +var apiCreators map[string]APICreator - for index := range selectedAPIs { - switch selectedAPIs[index] { - case EthNamespace: - apis = append(apis, - rpc.API{ +func init() { + apiCreators = map[string]APICreator{ + EthNamespace: func(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient) []rpc.API { + nonceLock := new(types.AddrLocker) + evmBackend := backend.NewEVMBackend(ctx, ctx.Logger, clientCtx) + return []rpc.API{ + { Namespace: EthNamespace, Version: apiVersion, Service: eth.NewPublicAPI(ctx.Logger, clientCtx, evmBackend, nonceLock), Public: true, }, - rpc.API{ + { Namespace: EthNamespace, Version: apiVersion, Service: filters.NewPublicAPI(ctx.Logger, clientCtx, tmWSClient, evmBackend), Public: true, }, - ) - case Web3Namespace: - apis = append(apis, - rpc.API{ + } + }, + Web3Namespace: func(*server.Context, client.Context, *rpcclient.WSClient) []rpc.API { + return []rpc.API{ + { Namespace: Web3Namespace, Version: apiVersion, Service: web3.NewPublicAPI(), Public: true, }, - ) - case NetNamespace: - apis = append(apis, - rpc.API{ + } + }, + NetNamespace: func(_ *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API { + return []rpc.API{ + { Namespace: NetNamespace, Version: apiVersion, Service: net.NewPublicAPI(clientCtx), Public: true, }, - ) - case PersonalNamespace: - apis = append(apis, - rpc.API{ + } + }, + PersonalNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API { + evmBackend := backend.NewEVMBackend(ctx, ctx.Logger, clientCtx) + return []rpc.API{ + { Namespace: PersonalNamespace, Version: apiVersion, Service: personal.NewAPI(ctx.Logger, clientCtx, evmBackend), Public: false, }, - ) - case TxPoolNamespace: - apis = append(apis, - rpc.API{ + } + }, + TxPoolNamespace: func(ctx *server.Context, _ client.Context, _ *rpcclient.WSClient) []rpc.API { + return []rpc.API{ + { Namespace: TxPoolNamespace, Version: apiVersion, Service: txpool.NewPublicAPI(ctx.Logger), Public: true, }, - ) - case DebugNamespace: - apis = append(apis, - rpc.API{ + } + }, + DebugNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API { + evmBackend := backend.NewEVMBackend(ctx, ctx.Logger, clientCtx) + return []rpc.API{ + { Namespace: DebugNamespace, Version: apiVersion, Service: debug.NewAPI(ctx, evmBackend, clientCtx), Public: true, }, - ) - case MinerNamespace: - apis = append(apis, - rpc.API{ + } + }, + MinerNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API { + evmBackend := backend.NewEVMBackend(ctx, ctx.Logger, clientCtx) + return []rpc.API{ + { Namespace: MinerNamespace, Version: apiVersion, Service: miner.NewPrivateAPI(ctx, clientCtx, evmBackend), Public: false, }, - ) - default: - ctx.Logger.Error("invalid namespace value", "namespace", selectedAPIs[index]) + } + }, + } +} + +// GetRPCAPIs returns the list of all APIs +func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient, selectedAPIs []string) []rpc.API { + var apis []rpc.API + + for _, ns := range selectedAPIs { + if creator, ok := apiCreators[ns]; ok { + apis = append(apis, creator(ctx, clientCtx, tmWSClient)...) + } else { + ctx.Logger.Error("invalid namespace value", "namespace", ns) } } 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) - } +// RegisterAPINamespace registers a new API namespace with the API creator. +// This function fails if the namespace is already registered. +func RegisterAPINamespace(ns string, creator APICreator) error { + if _, ok := apiCreators[ns]; ok { + return fmt.Errorf("duplicated api namespace %s", ns) } - return list + apiCreators[ns] = creator + return nil } diff --git a/server/config/config.go b/server/config/config.go index 023e5a62..ed0f463b 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -237,7 +237,7 @@ func (c JSONRPCConfig) Validate() error { return errors.New("JSON-RPC HTTP idle timeout duration cannot be negative") } - // TODO: validate APIs + // check for duplicates seenAPIs := make(map[string]bool) for _, api := range c.API { if seenAPIs[api] {