From 4d2bb5ff1d9cd8915d284819bdb6421eba17d581 Mon Sep 17 00:00:00 2001 From: Freddy Caceres Date: Tue, 9 Aug 2022 17:44:01 -0400 Subject: [PATCH 1/6] first pass --- server/config/config.go | 33 ++++++++++++++++++++------------- server/config/toml.go | 4 ++++ server/flags/flags.go | 1 + server/json_rpc.go | 16 +++++++++++++++- server/start.go | 1 + 5 files changed, 41 insertions(+), 14 deletions(-) diff --git a/server/config/config.go b/server/config/config.go index a4ba48a99f..a1ebfc9134 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -49,6 +49,8 @@ const ( DefaultHTTPIdleTimeout = 120 * time.Second // DefaultAllowUnprotectedTxs value is false DefaultAllowUnprotectedTxs = false + // 0 Max open connections represents unlimited amount of connections + DefaultMaxOpenConnections = 0 ) var evmTracers = []string{"json", "markdown", "struct", "access_list"} @@ -103,6 +105,9 @@ type JSONRPCConfig struct { // AllowUnprotectedTxs restricts unprotected (non EIP155 signed) transactions to be submitted via // the node's RPC when global parameter is disabled. AllowUnprotectedTxs bool `mapstructure:"allow-unprotected-txs"` + // MaxOpenConnections sets the maximum number of simultaneous connections + // for the server listener. + MaxOpenConnections int `mapstructure:"max-open-connections"` } // TLSConfig defines the certificate and matching private key for the server. @@ -202,6 +207,7 @@ func DefaultJSONRPCConfig() *JSONRPCConfig { HTTPTimeout: DefaultHTTPTimeout, HTTPIdleTimeout: DefaultHTTPIdleTimeout, AllowUnprotectedTxs: DefaultAllowUnprotectedTxs, + MaxOpenConnections: DefaultMaxOpenConnections, } } @@ -292,19 +298,20 @@ func GetConfig(v *viper.Viper) Config { MaxTxGasWanted: v.GetUint64("evm.max-tx-gas-wanted"), }, JSONRPC: JSONRPCConfig{ - Enable: v.GetBool("json-rpc.enable"), - API: v.GetStringSlice("json-rpc.api"), - Address: v.GetString("json-rpc.address"), - WsAddress: v.GetString("json-rpc.ws-address"), - GasCap: v.GetUint64("json-rpc.gas-cap"), - FilterCap: v.GetInt32("json-rpc.filter-cap"), - FeeHistoryCap: v.GetInt32("json-rpc.feehistory-cap"), - TxFeeCap: v.GetFloat64("json-rpc.txfee-cap"), - EVMTimeout: v.GetDuration("json-rpc.evm-timeout"), - LogsCap: v.GetInt32("json-rpc.logs-cap"), - BlockRangeCap: v.GetInt32("json-rpc.block-range-cap"), - HTTPTimeout: v.GetDuration("json-rpc.http-timeout"), - HTTPIdleTimeout: v.GetDuration("json-rpc.http-idle-timeout"), + Enable: v.GetBool("json-rpc.enable"), + API: v.GetStringSlice("json-rpc.api"), + Address: v.GetString("json-rpc.address"), + WsAddress: v.GetString("json-rpc.ws-address"), + GasCap: v.GetUint64("json-rpc.gas-cap"), + FilterCap: v.GetInt32("json-rpc.filter-cap"), + FeeHistoryCap: v.GetInt32("json-rpc.feehistory-cap"), + TxFeeCap: v.GetFloat64("json-rpc.txfee-cap"), + EVMTimeout: v.GetDuration("json-rpc.evm-timeout"), + LogsCap: v.GetInt32("json-rpc.logs-cap"), + BlockRangeCap: v.GetInt32("json-rpc.block-range-cap"), + HTTPTimeout: v.GetDuration("json-rpc.http-timeout"), + HTTPIdleTimeout: v.GetDuration("json-rpc.http-idle-timeout"), + MaxOpenConnections: v.GetInt("json-rpc.max-open-connections"), }, TLS: TLSConfig{ CertificatePath: v.GetString("tls.certificate-path"), diff --git a/server/config/toml.go b/server/config/toml.go index 93d3285add..d8861c83c8 100644 --- a/server/config/toml.go +++ b/server/config/toml.go @@ -66,6 +66,10 @@ http-idle-timeout = "{{ .JSONRPC.HTTPIdleTimeout }}" # the node's RPC when the global parameter is disabled. allow-unprotected-txs = {{ .JSONRPC.AllowUnprotectedTxs }} +# MaxOpenConnections sets the maximum number of simultaneous connections +# for the server listener. +max-open-connections = {{ .JSONRPC.MaxOpenConnections }} + ############################################################################### ### TLS Configuration ### ############################################################################### diff --git a/server/flags/flags.go b/server/flags/flags.go index 67fb0cc791..5422988e3e 100644 --- a/server/flags/flags.go +++ b/server/flags/flags.go @@ -47,6 +47,7 @@ const ( JSONRPCHTTPTimeout = "json-rpc.http-timeout" JSONRPCHTTPIdleTimeout = "json-rpc.http-idle-timeout" JSONRPCAllowUnprotectedTxs = "json-rpc.allow-unprotected-txs" + JSONRPCMaxOpenConnections = "json-rpc.max-open-connections" ) // EVM flags diff --git a/server/json_rpc.go b/server/json_rpc.go index c27399b88c..3a69b44a21 100644 --- a/server/json_rpc.go +++ b/server/json_rpc.go @@ -1,11 +1,13 @@ package server import ( + "net" "net/http" "time" "github.com/gorilla/mux" "github.com/rs/cors" + "golang.org/x/net/netutil" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/server" @@ -70,10 +72,22 @@ func StartJSONRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr, tmEn } httpSrvDone := make(chan struct{}, 1) + if httpSrv.Addr == "" { + httpSrv.Addr = ":http" + } + ln, err := net.Listen("tcp", httpSrv.Addr) + if err != nil { + return nil, nil, err + } + + if config.JSONRPC.MaxOpenConnections > 0 { + ln = netutil.LimitListener(ln, config.JSONRPC.MaxOpenConnections) + } + errCh := make(chan error) go func() { ctx.Logger.Info("Starting JSON-RPC server", "address", config.JSONRPC.Address) - if err := httpSrv.ListenAndServe(); err != nil { + if err := httpSrv.Serve(ln); err != nil { if err == http.ErrServerClosed { close(httpSrvDone) return diff --git a/server/start.go b/server/start.go index c4e979f309..acdd4c9366 100644 --- a/server/start.go +++ b/server/start.go @@ -164,6 +164,7 @@ which accepts a path for the resulting pprof file. cmd.Flags().Bool(srvflags.JSONRPCAllowUnprotectedTxs, config.DefaultAllowUnprotectedTxs, "Allow for unprotected (non EIP155 signed) transactions to be submitted via the node's RPC when the global parameter is disabled") cmd.Flags().Int32(srvflags.JSONRPCLogsCap, config.DefaultLogsCap, "Sets the max number of results can be returned from single `eth_getLogs` query") cmd.Flags().Int32(srvflags.JSONRPCBlockRangeCap, config.DefaultBlockRangeCap, "Sets the max block range allowed for `eth_getLogs` query") + cmd.Flags().Int(srvflags.JSONRPCMaxOpenConnections, config.DefaultMaxOpenConnections, "Sets the maximum number of simultaneous connections for the server listener") cmd.Flags().String(srvflags.EVMTracer, config.DefaultEVMTracer, "the EVM tracer type to collect execution traces from the EVM transaction execution (json|struct|access_list|markdown)") cmd.Flags().Uint64(srvflags.EVMMaxTxGasWanted, config.DefaultMaxTxGasWanted, "the gas wanted for each eth tx returned in ante handler in check tx mode") From 381237091c361dad08d9d6c1e5c82f1ae1047707 Mon Sep 17 00:00:00 2001 From: Freddy Caceres Date: Tue, 9 Aug 2022 18:03:19 -0400 Subject: [PATCH 2/6] refractor --- rpc/websockets.go | 2 +- server/json_rpc.go | 13 ++----------- server/start.go | 2 +- server/util.go | 17 +++++++++++++++++ 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/rpc/websockets.go b/rpc/websockets.go index 97a57acd9a..c501ace3a8 100644 --- a/rpc/websockets.go +++ b/rpc/websockets.go @@ -74,7 +74,7 @@ type websocketsServer struct { logger log.Logger } -func NewWebsocketsServer(clientCtx client.Context, logger log.Logger, tmWSClient *rpcclient.WSClient, cfg config.Config) WebsocketsServer { +func NewWebsocketsServer(clientCtx client.Context, logger log.Logger, tmWSClient *rpcclient.WSClient, cfg *config.Config) WebsocketsServer { logger = logger.With("api", "websocket-server") _, port, _ := net.SplitHostPort(cfg.JSONRPC.Address) diff --git a/server/json_rpc.go b/server/json_rpc.go index 3a69b44a21..bf6b8306c9 100644 --- a/server/json_rpc.go +++ b/server/json_rpc.go @@ -1,13 +1,11 @@ package server import ( - "net" "net/http" "time" "github.com/gorilla/mux" "github.com/rs/cors" - "golang.org/x/net/netutil" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/server" @@ -20,7 +18,7 @@ import ( ) // StartJSONRPC starts the JSON-RPC server -func StartJSONRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr, tmEndpoint string, config config.Config) (*http.Server, chan struct{}, error) { +func StartJSONRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr, tmEndpoint string, config *config.Config) (*http.Server, chan struct{}, error) { tmWsClient := ConnectTmWS(tmRPCAddr, tmEndpoint, ctx.Logger) logger := ctx.Logger.With("module", "geth") @@ -72,18 +70,11 @@ func StartJSONRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr, tmEn } httpSrvDone := make(chan struct{}, 1) - if httpSrv.Addr == "" { - httpSrv.Addr = ":http" - } - ln, err := net.Listen("tcp", httpSrv.Addr) + ln, err := Listen(httpSrv.Addr, config) if err != nil { return nil, nil, err } - if config.JSONRPC.MaxOpenConnections > 0 { - ln = netutil.LimitListener(ln, config.JSONRPC.MaxOpenConnections) - } - errCh := make(chan error) go func() { ctx.Logger.Info("Starting JSON-RPC server", "address", config.JSONRPC.Address) diff --git a/server/start.go b/server/start.go index acdd4c9366..6957d5be36 100644 --- a/server/start.go +++ b/server/start.go @@ -427,7 +427,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty tmEndpoint := "/websocket" tmRPCAddr := cfg.RPC.ListenAddress - httpSrv, httpSrvDone, err = StartJSONRPC(ctx, clientCtx, tmRPCAddr, tmEndpoint, config) + httpSrv, httpSrvDone, err = StartJSONRPC(ctx, clientCtx, tmRPCAddr, tmEndpoint, &config) if err != nil { return err } diff --git a/server/util.go b/server/util.go index 885f657d9e..25742830e3 100644 --- a/server/util.go +++ b/server/util.go @@ -1,12 +1,15 @@ package server import ( + "net" "net/http" "time" + "github.com/evmos/ethermint/server/config" "github.com/gorilla/mux" "github.com/improbable-eng/grpc-web/go/grpcweb" "github.com/spf13/cobra" + "golang.org/x/net/netutil" sdkserver "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/types" @@ -97,3 +100,17 @@ func MountGRPCWebServices( }) } } + +func Listen(addr string, config *config.Config) (net.Listener, error) { + if addr == "" { + addr = ":http" + } + ln, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + if config.JSONRPC.MaxOpenConnections > 0 { + ln = netutil.LimitListener(ln, config.JSONRPC.MaxOpenConnections) + } + return ln, err +} From 3b51839244c00c09d254e8f706bc6b47be6703fe Mon Sep 17 00:00:00 2001 From: Freddy Caceres Date: Tue, 9 Aug 2022 18:08:12 -0400 Subject: [PATCH 3/6] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e62e5b4e8f..6a1efc8d89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## Unreleased +### Improvments +* (rpc) [#1229](https://github.com/evmos/ethermint/pull/1229) Add support for configuring RPC `MaxOpenConnections` + ### State Machine Breaking * (deps) [\#1159](https://github.com/evmos/ethermint/pull/1159) Bump Geth version to `v1.10.19`. From 87bdf54f0d0acb95eb2f63be2c46c68cac52a1e7 Mon Sep 17 00:00:00 2001 From: Freddy Caceres Date: Tue, 9 Aug 2022 18:13:57 -0400 Subject: [PATCH 4/6] fix test build --- testutil/network/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testutil/network/util.go b/testutil/network/util.go index 05dcbb5f31..487a6bdf2d 100644 --- a/testutil/network/util.go +++ b/testutil/network/util.go @@ -131,7 +131,7 @@ func startInProcess(cfg Config, val *Validator) error { tmEndpoint := "/websocket" tmRPCAddr := val.RPCAddress - val.jsonrpc, val.jsonrpcDone, err = server.StartJSONRPC(val.Ctx, val.ClientCtx, tmRPCAddr, tmEndpoint, *val.AppConfig) + val.jsonrpc, val.jsonrpcDone, err = server.StartJSONRPC(val.Ctx, val.ClientCtx, tmRPCAddr, tmEndpoint, val.AppConfig) if err != nil { return err } From b3a9aab7d6aef8e2b4c4383098353a964e7bc00b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Wed, 10 Aug 2022 10:32:05 +0200 Subject: [PATCH 5/6] Apply suggestions from code review --- CHANGELOG.md | 3 ++- server/config/config.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a1efc8d89..3f21be0bed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## Unreleased -### Improvments +### Improvements + * (rpc) [#1229](https://github.com/evmos/ethermint/pull/1229) Add support for configuring RPC `MaxOpenConnections` ### State Machine Breaking diff --git a/server/config/config.go b/server/config/config.go index a1ebfc9134..407b2d0b74 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -49,7 +49,7 @@ const ( DefaultHTTPIdleTimeout = 120 * time.Second // DefaultAllowUnprotectedTxs value is false DefaultAllowUnprotectedTxs = false - // 0 Max open connections represents unlimited amount of connections + // DefaultMaxOpenConnections represents the amount of open connections (unlimited = 0) DefaultMaxOpenConnections = 0 ) From 09abf485c4a17c2e9311c416b3d7f9e396e4fa31 Mon Sep 17 00:00:00 2001 From: Freddy Caceres Date: Wed, 10 Aug 2022 07:38:09 -0400 Subject: [PATCH 6/6] add comments --- server/util.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/util.go b/server/util.go index 25742830e3..b608e7b6ce 100644 --- a/server/util.go +++ b/server/util.go @@ -20,7 +20,7 @@ import ( rpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client" ) -// add server commands +// AddCommands adds server commands func AddCommands(rootCmd *cobra.Command, defaultNodeHome string, appCreator types.AppCreator, appExport types.AppExporter, addStartFlags types.ModuleInitFlags) { tendermintCmd := &cobra.Command{ Use: "tendermint", @@ -101,6 +101,8 @@ func MountGRPCWebServices( } } +// Listen starts a net.Listener on the tcp network on the given address. +// If there is a specified MaxOpenConnections in the config, it will also set the limitListener. func Listen(addr string, config *config.Config) (net.Listener, error) { if addr == "" { addr = ":http"