From bc3aec4ef6b9a093db90e3189be7d126bcc74877 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Thu, 23 Feb 2023 06:19:04 +0000 Subject: [PATCH] rpc: add configurable http and `eth_call` timeout --- cmd/harmony/config_migrations.go | 17 +++++ cmd/harmony/default.go | 6 +- cmd/harmony/flags.go | 39 +++++++++++- cmd/harmony/flags_test.go | 58 +++++++++++++++++ internal/configs/harmony/harmony.go | 64 +++++++++++++++++++ internal/configs/harmony/harmony_test.go | 81 ++++++++++++++++++++++++ internal/configs/node/config.go | 7 ++ internal/configs/node/network.go | 6 ++ rosetta/rosetta.go | 5 +- rosetta/services/call_service.go | 10 ++- rpc/contract.go | 22 +++++-- rpc/rpc.go | 25 +++++--- 12 files changed, 320 insertions(+), 20 deletions(-) create mode 100644 internal/configs/harmony/harmony_test.go diff --git a/cmd/harmony/config_migrations.go b/cmd/harmony/config_migrations.go index 7c9420a0b..b3da9ec2b 100644 --- a/cmd/harmony/config_migrations.go +++ b/cmd/harmony/config_migrations.go @@ -340,6 +340,23 @@ func init() { return confTree } + migrations["2.5.12"] = func(confTree *toml.Tree) *toml.Tree { + if confTree.Get("HTTP.ReadTimeout") == nil { + confTree.Set("HTTP.ReadTimeout", defaultConfig.HTTP.ReadTimeout) + } + if confTree.Get("HTTP.WriteTimeout") == nil { + confTree.Set("HTTP.WriteTimeout", defaultConfig.HTTP.WriteTimeout) + } + if confTree.Get("HTTP.IdleTimeout") == nil { + confTree.Set("HTTP.IdleTimeout", defaultConfig.HTTP.IdleTimeout) + } + if confTree.Get("RPCOpt.EvmCallTimeout") == nil { + confTree.Set("RPCOpt.EvmCallTimeout", defaultConfig.RPCOpt.EvmCallTimeout) + } + confTree.Set("Version", "2.5.13") + return confTree + } + // check that the latest version here is the same as in default.go largestKey := getNextVersion(migrations) if largestKey != tomlConfigVersion { diff --git a/cmd/harmony/default.go b/cmd/harmony/default.go index 3835c4921..95e05b29c 100644 --- a/cmd/harmony/default.go +++ b/cmd/harmony/default.go @@ -5,7 +5,7 @@ import ( nodeconfig "github.com/harmony-one/harmony/internal/configs/node" ) -const tomlConfigVersion = "2.5.12" +const tomlConfigVersion = "2.5.13" const ( defNetworkType = nodeconfig.Mainnet @@ -44,6 +44,9 @@ var defaultConfig = harmonyconfig.HarmonyConfig{ Port: nodeconfig.DefaultRPCPort, AuthPort: nodeconfig.DefaultAuthRPCPort, RosettaPort: nodeconfig.DefaultRosettaPort, + ReadTimeout: nodeconfig.DefaultHTTPTimeoutRead, + WriteTimeout: nodeconfig.DefaultHTTPTimeoutWrite, + IdleTimeout: nodeconfig.DefaultHTTPTimeoutIdle, }, WS: harmonyconfig.WsConfig{ Enabled: true, @@ -59,6 +62,7 @@ var defaultConfig = harmonyconfig.HarmonyConfig{ RpcFilterFile: "./.hmy/rpc_filter.txt", RateLimterEnabled: true, RequestsPerSecond: nodeconfig.DefaultRPCRateLimit, + EvmCallTimeout: nodeconfig.DefaultEvmCallTimeout, }, BLSKeys: harmonyconfig.BlsConfig{ KeyDir: "./.hmy/blskeys", diff --git a/cmd/harmony/flags.go b/cmd/harmony/flags.go index 2ffceb6e5..8a2799ce2 100644 --- a/cmd/harmony/flags.go +++ b/cmd/harmony/flags.go @@ -75,6 +75,9 @@ var ( httpPortFlag, httpAuthPortFlag, httpRosettaPortFlag, + httpReadTimeoutFlag, + httpWriteTimeoutFlag, + httpIdleTimeoutFlag, } wsFlags = []cli.Flag{ @@ -92,6 +95,7 @@ var ( rpcFilterFileFlag, rpcRateLimiterEnabledFlag, rpcRateLimitFlag, + rpcEvmCallTimeoutFlag, } blsFlags = append(newBLSFlags, legacyBLSFlags...) @@ -695,6 +699,21 @@ var ( Usage: "rosetta port to listen for HTTP requests", DefValue: defaultConfig.HTTP.RosettaPort, } + httpReadTimeoutFlag = cli.StringFlag{ + Name: "http.timeout.read", + Usage: "maximum duration to read the entire request, including the body", + DefValue: defaultConfig.HTTP.ReadTimeout, + } + httpWriteTimeoutFlag = cli.StringFlag{ + Name: "http.timeout.write", + Usage: "maximum duration before timing out writes of the response", + DefValue: defaultConfig.HTTP.WriteTimeout, + } + httpIdleTimeoutFlag = cli.StringFlag{ + Name: "http.timeout.idle", + Usage: "maximum amount of time to wait for the next request when keep-alives are enabled", + DefValue: defaultConfig.HTTP.IdleTimeout, + } ) func applyHTTPFlags(cmd *cobra.Command, config *harmonyconfig.HarmonyConfig) { @@ -732,6 +751,16 @@ func applyHTTPFlags(cmd *cobra.Command, config *harmonyconfig.HarmonyConfig) { config.HTTP.Enabled = true } + if cli.IsFlagChanged(cmd, httpReadTimeoutFlag) { + config.HTTP.ReadTimeout = cli.GetStringFlagValue(cmd, httpReadTimeoutFlag) + } + if cli.IsFlagChanged(cmd, httpWriteTimeoutFlag) { + config.HTTP.WriteTimeout = cli.GetStringFlagValue(cmd, httpWriteTimeoutFlag) + } + if cli.IsFlagChanged(cmd, httpIdleTimeoutFlag) { + config.HTTP.IdleTimeout = cli.GetStringFlagValue(cmd, httpIdleTimeoutFlag) + } + } // ws flags @@ -821,6 +850,12 @@ var ( Usage: "the number of requests per second for RPCs", DefValue: defaultConfig.RPCOpt.RequestsPerSecond, } + + rpcEvmCallTimeoutFlag = cli.StringFlag{ + Name: "rpc.evm-call-timeout", + Usage: "timeout for evm execution (eth_call); 0 means infinite timeout", + DefValue: defaultConfig.RPCOpt.EvmCallTimeout, + } ) func applyRPCOptFlags(cmd *cobra.Command, config *harmonyconfig.HarmonyConfig) { @@ -845,7 +880,9 @@ func applyRPCOptFlags(cmd *cobra.Command, config *harmonyconfig.HarmonyConfig) { if cli.IsFlagChanged(cmd, rpcRateLimitFlag) { config.RPCOpt.RequestsPerSecond = cli.GetIntFlagValue(cmd, rpcRateLimitFlag) } - + if cli.IsFlagChanged(cmd, rpcEvmCallTimeoutFlag) { + config.RPCOpt.EvmCallTimeout = cli.GetStringFlagValue(cmd, rpcEvmCallTimeoutFlag) + } } // bls flags diff --git a/cmd/harmony/flags_test.go b/cmd/harmony/flags_test.go index 15ba9aaf0..2015188ed 100644 --- a/cmd/harmony/flags_test.go +++ b/cmd/harmony/flags_test.go @@ -77,6 +77,9 @@ func TestHarmonyFlags(t *testing.T) { AuthPort: 9501, RosettaEnabled: false, RosettaPort: 9700, + ReadTimeout: defaultConfig.HTTP.ReadTimeout, + WriteTimeout: defaultConfig.HTTP.WriteTimeout, + IdleTimeout: defaultConfig.HTTP.IdleTimeout, }, RPCOpt: harmonyconfig.RpcOptConfig{ DebugEnabled: false, @@ -86,6 +89,7 @@ func TestHarmonyFlags(t *testing.T) { RpcFilterFile: "./.hmy/rpc_filter.txt", RateLimterEnabled: true, RequestsPerSecond: 1000, + EvmCallTimeout: defaultConfig.RPCOpt.EvmCallTimeout, }, WS: harmonyconfig.WsConfig{ Enabled: true, @@ -531,6 +535,9 @@ func TestRPCFlags(t *testing.T) { Port: defaultConfig.HTTP.Port, AuthPort: defaultConfig.HTTP.AuthPort, RosettaPort: defaultConfig.HTTP.RosettaPort, + ReadTimeout: defaultConfig.HTTP.ReadTimeout, + WriteTimeout: defaultConfig.HTTP.WriteTimeout, + IdleTimeout: defaultConfig.HTTP.IdleTimeout, }, }, { @@ -542,6 +549,9 @@ func TestRPCFlags(t *testing.T) { Port: 9001, AuthPort: defaultConfig.HTTP.AuthPort, RosettaPort: defaultConfig.HTTP.RosettaPort, + ReadTimeout: defaultConfig.HTTP.ReadTimeout, + WriteTimeout: defaultConfig.HTTP.WriteTimeout, + IdleTimeout: defaultConfig.HTTP.IdleTimeout, }, }, { @@ -553,6 +563,9 @@ func TestRPCFlags(t *testing.T) { Port: defaultConfig.HTTP.Port, AuthPort: 9001, RosettaPort: defaultConfig.HTTP.RosettaPort, + ReadTimeout: defaultConfig.HTTP.ReadTimeout, + WriteTimeout: defaultConfig.HTTP.WriteTimeout, + IdleTimeout: defaultConfig.HTTP.IdleTimeout, }, }, { @@ -564,6 +577,9 @@ func TestRPCFlags(t *testing.T) { Port: 9001, AuthPort: defaultConfig.HTTP.AuthPort, RosettaPort: 10001, + ReadTimeout: defaultConfig.HTTP.ReadTimeout, + WriteTimeout: defaultConfig.HTTP.WriteTimeout, + IdleTimeout: defaultConfig.HTTP.IdleTimeout, }, }, { @@ -575,6 +591,9 @@ func TestRPCFlags(t *testing.T) { Port: defaultConfig.HTTP.Port, AuthPort: defaultConfig.HTTP.AuthPort, RosettaPort: 10001, + ReadTimeout: defaultConfig.HTTP.ReadTimeout, + WriteTimeout: defaultConfig.HTTP.WriteTimeout, + IdleTimeout: defaultConfig.HTTP.IdleTimeout, }, }, { @@ -586,6 +605,23 @@ func TestRPCFlags(t *testing.T) { Port: 9501, AuthPort: 9502, RosettaPort: 9701, + ReadTimeout: defaultConfig.HTTP.ReadTimeout, + WriteTimeout: defaultConfig.HTTP.WriteTimeout, + IdleTimeout: defaultConfig.HTTP.IdleTimeout, + }, + }, + { + args: []string{"--http.timeout.read", "10s", "--http.timeout.write", "20s", "--http.timeout.idle", "30s"}, + expConfig: harmonyconfig.HttpConfig{ + Enabled: true, + RosettaEnabled: false, + IP: defaultConfig.HTTP.IP, + Port: defaultConfig.HTTP.Port, + AuthPort: defaultConfig.HTTP.AuthPort, + RosettaPort: defaultConfig.HTTP.RosettaPort, + ReadTimeout: "10s", + WriteTimeout: "20s", + IdleTimeout: "30s", }, }, } @@ -699,6 +735,7 @@ func TestRPCOptFlags(t *testing.T) { RpcFilterFile: "./.hmy/rpc_filter.txt", RateLimterEnabled: true, RequestsPerSecond: 1000, + EvmCallTimeout: defaultConfig.RPCOpt.EvmCallTimeout, }, }, @@ -712,6 +749,7 @@ func TestRPCOptFlags(t *testing.T) { RpcFilterFile: "./.hmy/rpc_filter.txt", RateLimterEnabled: true, RequestsPerSecond: 1000, + EvmCallTimeout: defaultConfig.RPCOpt.EvmCallTimeout, }, }, @@ -725,6 +763,7 @@ func TestRPCOptFlags(t *testing.T) { RpcFilterFile: "./.hmy/rpc_filter.txt", RateLimterEnabled: true, RequestsPerSecond: 1000, + EvmCallTimeout: defaultConfig.RPCOpt.EvmCallTimeout, }, }, @@ -738,6 +777,7 @@ func TestRPCOptFlags(t *testing.T) { RpcFilterFile: "./.hmy/rpc_filter.txt", RateLimterEnabled: true, RequestsPerSecond: 1000, + EvmCallTimeout: defaultConfig.RPCOpt.EvmCallTimeout, }, }, @@ -751,6 +791,7 @@ func TestRPCOptFlags(t *testing.T) { RpcFilterFile: "./rmf.toml", RateLimterEnabled: true, RequestsPerSecond: 1000, + EvmCallTimeout: defaultConfig.RPCOpt.EvmCallTimeout, }, }, @@ -764,6 +805,7 @@ func TestRPCOptFlags(t *testing.T) { RpcFilterFile: "./.hmy/rpc_filter.txt", RateLimterEnabled: true, RequestsPerSecond: 1000, + EvmCallTimeout: defaultConfig.RPCOpt.EvmCallTimeout, }, }, @@ -777,6 +819,7 @@ func TestRPCOptFlags(t *testing.T) { RpcFilterFile: "./.hmy/rpc_filter.txt", RateLimterEnabled: true, RequestsPerSecond: 2000, + EvmCallTimeout: defaultConfig.RPCOpt.EvmCallTimeout, }, }, @@ -790,6 +833,21 @@ func TestRPCOptFlags(t *testing.T) { RpcFilterFile: "./.hmy/rpc_filter.txt", RateLimterEnabled: false, RequestsPerSecond: 2000, + EvmCallTimeout: defaultConfig.RPCOpt.EvmCallTimeout, + }, + }, + + { + args: []string{"--rpc.evm-call-timeout", "10s"}, + expConfig: harmonyconfig.RpcOptConfig{ + DebugEnabled: false, + EthRPCsEnabled: true, + StakingRPCsEnabled: true, + LegacyRPCsEnabled: true, + RpcFilterFile: "./.hmy/rpc_filter.txt", + RateLimterEnabled: true, + RequestsPerSecond: 1000, + EvmCallTimeout: "10s", }, }, } diff --git a/internal/configs/harmony/harmony.go b/internal/configs/harmony/harmony.go index 67c29f820..d4e8df4b0 100644 --- a/internal/configs/harmony/harmony.go +++ b/internal/configs/harmony/harmony.go @@ -3,6 +3,10 @@ package harmony import ( "reflect" "strings" + "time" + + nodeconfig "github.com/harmony-one/harmony/internal/configs/node" + "github.com/harmony-one/harmony/internal/utils" ) // HarmonyConfig contains all the configs user can set for running harmony binary. Served as the bridge @@ -32,6 +36,62 @@ type HarmonyConfig struct { ShardData ShardDataConfig } +func (hc HarmonyConfig) ToRPCServerConfig() nodeconfig.RPCServerConfig { + readTimeout, err := time.ParseDuration(hc.HTTP.ReadTimeout) + if err != nil { + readTimeout, _ = time.ParseDuration(nodeconfig.DefaultHTTPTimeoutRead) + utils.Logger().Warn(). + Str("provided", hc.HTTP.ReadTimeout). + Dur("updated", readTimeout). + Msg("Sanitizing invalid http read timeout") + } + writeTimeout, err := time.ParseDuration(hc.HTTP.WriteTimeout) + if err != nil { + writeTimeout, _ = time.ParseDuration(nodeconfig.DefaultHTTPTimeoutWrite) + utils.Logger().Warn(). + Str("provided", hc.HTTP.WriteTimeout). + Dur("updated", writeTimeout). + Msg("Sanitizing invalid http write timeout") + } + idleTimeout, err := time.ParseDuration(hc.HTTP.IdleTimeout) + if err != nil { + idleTimeout, _ = time.ParseDuration(nodeconfig.DefaultHTTPTimeoutIdle) + utils.Logger().Warn(). + Str("provided", hc.HTTP.IdleTimeout). + Dur("updated", idleTimeout). + Msg("Sanitizing invalid http idle timeout") + } + evmCallTimeout, err := time.ParseDuration(hc.RPCOpt.EvmCallTimeout) + if err != nil { + evmCallTimeout, _ = time.ParseDuration(nodeconfig.DefaultEvmCallTimeout) + utils.Logger().Warn(). + Str("provided", hc.RPCOpt.EvmCallTimeout). + Dur("updated", evmCallTimeout). + Msg("Sanitizing invalid evm_call timeout") + } + return nodeconfig.RPCServerConfig{ + HTTPEnabled: hc.HTTP.Enabled, + HTTPIp: hc.HTTP.IP, + HTTPPort: hc.HTTP.Port, + HTTPAuthPort: hc.HTTP.AuthPort, + HTTPTimeoutRead: readTimeout, + HTTPTimeoutWrite: writeTimeout, + HTTPTimeoutIdle: idleTimeout, + WSEnabled: hc.WS.Enabled, + WSIp: hc.WS.IP, + WSPort: hc.WS.Port, + WSAuthPort: hc.WS.AuthPort, + DebugEnabled: hc.RPCOpt.DebugEnabled, + EthRPCsEnabled: hc.RPCOpt.EthRPCsEnabled, + StakingRPCsEnabled: hc.RPCOpt.StakingRPCsEnabled, + LegacyRPCsEnabled: hc.RPCOpt.LegacyRPCsEnabled, + RpcFilterFile: hc.RPCOpt.RpcFilterFile, + RateLimiterEnabled: hc.RPCOpt.RateLimterEnabled, + RequestsPerSecond: hc.RPCOpt.RequestsPerSecond, + EvmCallTimeout: evmCallTimeout, + } +} + type DnsSync struct { Port int // replaces: Network.DNSSyncPort Zone string // replaces: Network.DNSZone @@ -180,6 +240,9 @@ type HttpConfig struct { AuthPort int RosettaEnabled bool RosettaPort int + ReadTimeout string + WriteTimeout string + IdleTimeout string } type WsConfig struct { @@ -197,6 +260,7 @@ type RpcOptConfig struct { RpcFilterFile string // Define filters to enable/disable RPC exposure RateLimterEnabled bool // Enable Rate limiter for RPC RequestsPerSecond int // for RPC rate limiter + EvmCallTimeout string // Timeout for eth_call } type DevnetConfig struct { diff --git a/internal/configs/harmony/harmony_test.go b/internal/configs/harmony/harmony_test.go new file mode 100644 index 000000000..fef7cac9d --- /dev/null +++ b/internal/configs/harmony/harmony_test.go @@ -0,0 +1,81 @@ +package harmony + +import ( + "fmt" + "testing" + "time" + + nodeconfig "github.com/harmony-one/harmony/internal/configs/node" + "github.com/stretchr/testify/assert" +) + +func TestToRPCServerConfig(t *testing.T) { + tests := []struct { + input HarmonyConfig + output nodeconfig.RPCServerConfig + }{ + { + input: HarmonyConfig{ + HTTP: HttpConfig{ + Enabled: true, + RosettaEnabled: false, + IP: "127.0.0.1", + Port: nodeconfig.DefaultRPCPort, + AuthPort: nodeconfig.DefaultAuthRPCPort, + RosettaPort: nodeconfig.DefaultRosettaPort, + ReadTimeout: "-1", + WriteTimeout: "-2", + IdleTimeout: "-3", + }, + WS: WsConfig{ + Enabled: true, + IP: "127.0.0.1", + Port: nodeconfig.DefaultWSPort, + AuthPort: nodeconfig.DefaultAuthWSPort, + }, + RPCOpt: RpcOptConfig{ + DebugEnabled: false, + EthRPCsEnabled: true, + StakingRPCsEnabled: true, + LegacyRPCsEnabled: true, + RpcFilterFile: "./.hmy/rpc_filter.txt", + RateLimterEnabled: true, + RequestsPerSecond: nodeconfig.DefaultRPCRateLimit, + EvmCallTimeout: "-4", + }, + }, + output: nodeconfig.RPCServerConfig{ + HTTPEnabled: true, + HTTPIp: "127.0.0.1", + HTTPPort: nodeconfig.DefaultRPCPort, + HTTPAuthPort: nodeconfig.DefaultAuthRPCPort, + HTTPTimeoutRead: 30 * time.Second, + HTTPTimeoutWrite: 30 * time.Second, + HTTPTimeoutIdle: 120 * time.Second, + WSEnabled: true, + WSIp: "127.0.0.1", + WSPort: nodeconfig.DefaultWSPort, + WSAuthPort: nodeconfig.DefaultAuthWSPort, + DebugEnabled: false, + EthRPCsEnabled: true, + StakingRPCsEnabled: true, + LegacyRPCsEnabled: true, + RpcFilterFile: "./.hmy/rpc_filter.txt", + RateLimiterEnabled: true, + RequestsPerSecond: nodeconfig.DefaultRPCRateLimit, + EvmCallTimeout: 5 * time.Second, + }, + }, + } + for i, tt := range tests { + assertObject := assert.New(t) + name := fmt.Sprintf("TestToRPCServerConfig: #%d", i) + t.Run(name, func(t *testing.T) { + assertObject.Equal( + tt.input.ToRPCServerConfig(), + tt.output, + name, + ) + }) + } +} diff --git a/internal/configs/node/config.go b/internal/configs/node/config.go index 9a0e950ec..9f681fca9 100644 --- a/internal/configs/node/config.go +++ b/internal/configs/node/config.go @@ -8,6 +8,7 @@ import ( "math/big" "strings" "sync" + "time" bls_core "github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/harmony/crypto/bls" @@ -115,6 +116,10 @@ type RPCServerConfig struct { HTTPPort int HTTPAuthPort int + HTTPTimeoutRead time.Duration + HTTPTimeoutWrite time.Duration + HTTPTimeoutIdle time.Duration + WSEnabled bool WSIp string WSPort int @@ -130,6 +135,8 @@ type RPCServerConfig struct { RateLimiterEnabled bool RequestsPerSecond int + + EvmCallTimeout time.Duration } // RosettaServerConfig is the config for the rosetta server diff --git a/internal/configs/node/network.go b/internal/configs/node/network.go index 8b15d3359..03f147212 100644 --- a/internal/configs/node/network.go +++ b/internal/configs/node/network.go @@ -51,6 +51,12 @@ const ( DefaultAuthRPCPort = 9501 // DefaultRosettaPort is the default rosetta port. The actual port used is 9000+700 DefaultRosettaPort = 9700 + // DefaultHTTP timeouts - read, write, and idle + DefaultHTTPTimeoutRead = "30s" + DefaultHTTPTimeoutWrite = "30s" + DefaultHTTPTimeoutIdle = "120s" + // DefaultEvmCallTimeout is the default timeout for evm call + DefaultEvmCallTimeout = "5s" // DefaultWSPort is the default port for web socket endpoint. The actual port used is DefaultWSPort = 9800 // DefaultAuthWSPort is the default port for web socket auth endpoint. The actual port used is diff --git a/rosetta/rosetta.go b/rosetta/rosetta.go index 860c2d0d6..5ff222a65 100644 --- a/rosetta/rosetta.go +++ b/rosetta/rosetta.go @@ -84,7 +84,10 @@ func getRouter(asserter *asserter.Asserter, hmy *hmy.Harmony, limiterEnable bool server.NewMempoolAPIController(services.NewMempoolAPI(hmy), asserter), server.NewNetworkAPIController(services.NewNetworkAPI(hmy), asserter), server.NewConstructionAPIController(services.NewConstructionAPI(hmy), asserter), - server.NewCallAPIController(services.NewCallAPIService(hmy, limiterEnable, rateLimit), asserter), + server.NewCallAPIController( + services.NewCallAPIService(hmy, limiterEnable, rateLimit, hmy.NodeAPI.GetConfig().NodeConfig.RPCServer.EvmCallTimeout), + asserter, + ), server.NewEventsAPIController(services.NewEventAPI(hmy), asserter), server.NewSearchAPIController(services.NewSearchAPI(hmy), asserter), ) diff --git a/rosetta/services/call_service.go b/rosetta/services/call_service.go index 46f528d7b..9d26bab28 100644 --- a/rosetta/services/call_service.go +++ b/rosetta/services/call_service.go @@ -3,6 +3,7 @@ package services import ( "context" "encoding/json" + "time" "github.com/coinbase/rosetta-sdk-go/server" "github.com/coinbase/rosetta-sdk-go/types" @@ -82,10 +83,15 @@ func (c *CallAPIService) Call( } -func NewCallAPIService(hmy *hmy.Harmony, limiterEnable bool, rateLimit int) server.CallAPIServicer { +func NewCallAPIService( + hmy *hmy.Harmony, + limiterEnable bool, + rateLimit int, + evmCallTimeout time.Duration, +) server.CallAPIServicer { return &CallAPIService{ hmy: hmy, - publicContractAPI: rpc2.NewPublicContractAPI(hmy, rpc2.V2, limiterEnable, rateLimit), + publicContractAPI: rpc2.NewPublicContractAPI(hmy, rpc2.V2, limiterEnable, rateLimit, evmCallTimeout), publicStakingAPI: rpc2.NewPublicStakingAPI(hmy, rpc2.V2), publicBlockChainAPI: rpc2.NewPublicBlockchainAPI(hmy, rpc2.V2, limiterEnable, rateLimit), } diff --git a/rpc/contract.go b/rpc/contract.go index 337bea7cd..abcb4f941 100644 --- a/rpc/contract.go +++ b/rpc/contract.go @@ -31,11 +31,18 @@ type PublicContractService struct { hmy *hmy.Harmony version Version // TEMP SOLUTION to rpc node spamming issue - limiterCall *rate.Limiter + limiterCall *rate.Limiter + evmCallTimeout time.Duration } // NewPublicContractAPI creates a new API for the RPC interface -func NewPublicContractAPI(hmy *hmy.Harmony, version Version, limiterEnable bool, limit int) rpc.API { +func NewPublicContractAPI( + hmy *hmy.Harmony, + version Version, + limiterEnable bool, + limit int, + evmCallTimeout time.Duration, +) rpc.API { var limiter *rate.Limiter if limiterEnable { limiter = rate.NewLimiter(rate.Limit(limit), limit) @@ -44,8 +51,13 @@ func NewPublicContractAPI(hmy *hmy.Harmony, version Version, limiterEnable bool, return rpc.API{ Namespace: version.Namespace(), Version: APIVersion, - Service: &PublicContractService{hmy, version, limiter}, - Public: true, + Service: &PublicContractService{ + hmy: hmy, + version: version, + limiterCall: limiter, + evmCallTimeout: evmCallTimeout, + }, + Public: true, } } @@ -80,7 +92,7 @@ func (s *PublicContractService) Call( } // Execute call - result, err := DoEVMCall(ctx, s.hmy, args, blockNrOrHash, CallTimeout) + result, err := DoEVMCall(ctx, s.hmy, args, blockNrOrHash, s.evmCallTimeout) if err != nil { return nil, err } diff --git a/rpc/rpc.go b/rpc/rpc.go index 2c94f0a5e..1bae7367d 100644 --- a/rpc/rpc.go +++ b/rpc/rpc.go @@ -58,9 +58,9 @@ var ( wsEndpoint = "" wsAuthEndpoint = "" httpVirtualHosts = []string{"*"} - httpTimeouts = rpc.DefaultHTTPTimeouts - httpOrigins = []string{"*"} - wsOrigins = []string{"*"} + // httpTimeouts = rpc.DefaultHTTPTimeouts + httpOrigins = []string{"*"} + wsOrigins = []string{"*"} ) // Version of the RPC @@ -86,13 +86,18 @@ func StartServers(hmy *hmy.Harmony, apis []rpc.API, config nodeconfig.RPCServerC rmf.ExposeAll() } if config.HTTPEnabled { + timeouts := rpc.HTTPTimeouts{ + ReadTimeout: config.HTTPTimeoutRead, + WriteTimeout: config.HTTPTimeoutWrite, + IdleTimeout: config.HTTPTimeoutIdle, + } httpEndpoint = fmt.Sprintf("%v:%v", config.HTTPIp, config.HTTPPort) - if err := startHTTP(apis, &rmf); err != nil { + if err := startHTTP(apis, &rmf, timeouts); err != nil { return err } httpAuthEndpoint = fmt.Sprintf("%v:%v", config.HTTPIp, config.HTTPAuthPort) - if err := startAuthHTTP(authApis, &rmf); err != nil { + if err := startAuthHTTP(authApis, &rmf, timeouts); err != nil { return err } } @@ -158,8 +163,8 @@ func getAPIs(hmy *hmy.Harmony, config nodeconfig.RPCServerConfig) []rpc.API { NewPublicHarmonyAPI(hmy, V2), NewPublicBlockchainAPI(hmy, V1, config.RateLimiterEnabled, config.RequestsPerSecond), NewPublicBlockchainAPI(hmy, V2, config.RateLimiterEnabled, config.RequestsPerSecond), - NewPublicContractAPI(hmy, V1, config.RateLimiterEnabled, config.RequestsPerSecond), - NewPublicContractAPI(hmy, V2, config.RateLimiterEnabled, config.RequestsPerSecond), + NewPublicContractAPI(hmy, V1, config.RateLimiterEnabled, config.RequestsPerSecond, config.EvmCallTimeout), + NewPublicContractAPI(hmy, V2, config.RateLimiterEnabled, config.RequestsPerSecond, config.EvmCallTimeout), NewPublicTransactionAPI(hmy, V1), NewPublicTransactionAPI(hmy, V2), NewPublicPoolAPI(hmy, V1, config.RateLimiterEnabled, config.RequestsPerSecond), @@ -185,7 +190,7 @@ func getAPIs(hmy *hmy.Harmony, config nodeconfig.RPCServerConfig) []rpc.API { publicAPIs = append(publicAPIs, NewPublicHarmonyAPI(hmy, Eth), NewPublicBlockchainAPI(hmy, Eth, config.RateLimiterEnabled, config.RequestsPerSecond), - NewPublicContractAPI(hmy, Eth, config.RateLimiterEnabled, config.RequestsPerSecond), + NewPublicContractAPI(hmy, Eth, config.RateLimiterEnabled, config.RequestsPerSecond, config.EvmCallTimeout), NewPublicTransactionAPI(hmy, Eth), NewPublicPoolAPI(hmy, Eth, config.RateLimiterEnabled, config.RequestsPerSecond), eth.NewPublicEthService(hmy, "eth"), @@ -210,7 +215,7 @@ func getAPIs(hmy *hmy.Harmony, config nodeconfig.RPCServerConfig) []rpc.API { return publicAPIs } -func startHTTP(apis []rpc.API, rmf *rpc.RpcMethodFilter) (err error) { +func startHTTP(apis []rpc.API, rmf *rpc.RpcMethodFilter, httpTimeouts rpc.HTTPTimeouts) (err error) { httpListener, httpHandler, err = rpc.StartHTTPEndpoint( httpEndpoint, apis, HTTPModules, rmf, httpOrigins, httpVirtualHosts, httpTimeouts, ) @@ -227,7 +232,7 @@ func startHTTP(apis []rpc.API, rmf *rpc.RpcMethodFilter) (err error) { return nil } -func startAuthHTTP(apis []rpc.API, rmf *rpc.RpcMethodFilter) (err error) { +func startAuthHTTP(apis []rpc.API, rmf *rpc.RpcMethodFilter, httpTimeouts rpc.HTTPTimeouts) (err error) { httpListener, httpHandler, err = rpc.StartHTTPEndpoint( httpAuthEndpoint, apis, HTTPModules, rmf, httpOrigins, httpVirtualHosts, httpTimeouts, )