From a2846fd1a77a42747a10377eefa581f3b22ea94d Mon Sep 17 00:00:00 2001 From: nitely Date: Thu, 27 Nov 2025 23:54:28 -0300 Subject: [PATCH] flatten metrics --- beacon_chain/conf.nim | 125 +---------------- beacon_chain/nimbus_beacon_node.nim | 6 +- beacon_chain/nimbus_binary_common.nim | 164 ++++++++++++++++++----- beacon_chain/nimbus_validator_client.nim | 4 +- vendor/nim-confutils | 2 +- 5 files changed, 140 insertions(+), 161 deletions(-) diff --git a/beacon_chain/conf.nim b/beacon_chain/conf.nim index 8fce00cfc9..59d63a94c9 100644 --- a/beacon_chain/conf.nim +++ b/beacon_chain/conf.nim @@ -45,14 +45,10 @@ export declareGauge network_name, "network name", ["name"] const - # TODO: How should we select between IPv4 and IPv6 - # Maybe there should be a config option for this. - defaultAdminListenAddress* = (static parseIpAddress("127.0.0.1")) defaultSigningNodeRequestTimeout* = 60 defaultBeaconNode* = "http://127.0.0.1:" & $defaultEth2RestPort defaultBeaconNodeUri* = parseUri(defaultBeaconNode) defaultGasLimit* = 60_000_000 - defaultAdminListenAddressDesc* = $defaultAdminListenAddress defaultBeaconNodeDesc = $defaultBeaconNode when defined(windows): @@ -408,21 +404,7 @@ type defaultValue: 0 name: "stop-at-synced-epoch" .}: uint64 - metricsEnabled* {. - desc: "Enable the metrics server" - defaultValue: false - name: "metrics" .}: bool - - metricsAddress* {. - desc: "Listening address of the metrics server" - defaultValue: defaultAdminListenAddress - defaultValueDesc: $defaultAdminListenAddressDesc - name: "metrics-address" .}: IpAddress - - metricsPort* {. - desc: "Listening HTTP port of the metrics server" - defaultValue: 8008 - name: "metrics-port" .}: Port + metrics* {.flatten.}: MetricsConf statusBarEnabled* {. posixOnly @@ -494,51 +476,9 @@ type desc: "The number of seconds to keep recently accessed states in memory" name: "rest-statecache-ttl" .}: Natural - restRequestTimeout* {. - defaultValue: 0 - defaultValueDesc: "infinite" - desc: "The number of seconds to wait until complete REST request " & - "will be received" - name: "rest-request-timeout" .}: Natural - - restMaxRequestBodySize* {. - defaultValue: 16_384 - desc: "Maximum size of REST request body (kilobytes)" - name: "rest-max-body-size" .}: Natural - - restMaxRequestHeadersSize* {. - defaultValue: 128 - desc: "Maximum size of REST request headers (kilobytes)" - name: "rest-max-headers-size" .}: Natural - ## NOTE: If you going to adjust this value please check value - ## ``ClientMaximumValidatorIds`` and comments in - ## `spec/eth2_apis/rest_types.nim`. This values depend on each other. - - keymanagerEnabled* {. - desc: "Enable the REST keymanager API" - defaultValue: false - name: "keymanager" .}: bool - - keymanagerPort* {. - desc: "Listening port for the REST keymanager API" - defaultValue: defaultEth2RestPort - defaultValueDesc: $defaultEth2RestPortDesc - name: "keymanager-port" .}: Port - - keymanagerAddress* {. - desc: "Listening port for the REST keymanager API" - defaultValue: defaultAdminListenAddress - defaultValueDesc: $defaultAdminListenAddressDesc - name: "keymanager-address" .}: IpAddress - - keymanagerAllowedOrigin* {. - desc: "Limit the access to the Keymanager API to a particular hostname " & - "(for CORS-enabled clients such as browsers)" - name: "keymanager-allow-origin" .}: Option[string] + request* {.flatten.}: RequestConf - keymanagerTokenFile* {. - desc: "A file specifying the authorization token required for accessing the keymanager API" - name: "keymanager-token-file" .}: Option[InputFile] + keymanager* {.flatten.}: KeymanagerConf lightClientDataServe* {. desc: "Serve data for enabling light clients to stay in sync with the network" @@ -957,23 +897,6 @@ type desc: "A directory containing validator keystore passwords" name: "secrets-dir" .}: Option[InputDir] - restRequestTimeout* {. - defaultValue: 0 - defaultValueDesc: "infinite" - desc: "The number of seconds to wait until complete REST request " & - "will be received" - name: "rest-request-timeout" .}: Natural - - restMaxRequestBodySize* {. - defaultValue: 16_384 - desc: "Maximum size of REST request body (kilobytes)" - name: "rest-max-body-size" .}: Natural - - restMaxRequestHeadersSize* {. - defaultValue: 64 - desc: "Maximum size of REST request headers (kilobytes)" - name: "rest-max-headers-size" .}: Natural - # Same option as appears in Lighthouse and Prysm # https://lighthouse-book.sigmaprime.io/suggested-fee-recipient.html # https://github.com/prysmaticlabs/prysm/pull/10312 @@ -986,47 +909,11 @@ type defaultValue: defaultGasLimit name: "suggested-gas-limit" .}: uint64 - keymanagerEnabled* {. - desc: "Enable the REST keymanager API" - defaultValue: false - name: "keymanager" .}: bool - - keymanagerPort* {. - desc: "Listening port for the REST keymanager API" - defaultValue: defaultEth2RestPort - defaultValueDesc: $defaultEth2RestPortDesc - name: "keymanager-port" .}: Port - - keymanagerAddress* {. - desc: "Listening port for the REST keymanager API" - defaultValue: defaultAdminListenAddress - defaultValueDesc: $defaultAdminListenAddressDesc - name: "keymanager-address" .}: IpAddress - - keymanagerAllowedOrigin* {. - desc: "Limit the access to the Keymanager API to a particular hostname " & - "(for CORS-enabled clients such as browsers)" - name: "keymanager-allow-origin" .}: Option[string] - - keymanagerTokenFile* {. - desc: "A file specifying the authorizition token required for accessing the keymanager API" - name: "keymanager-token-file" .}: Option[InputFile] + request* {.flatten.}: RequestConf - metricsEnabled* {. - desc: "Enable the metrics server (BETA)" - defaultValue: false - name: "metrics" .}: bool - - metricsAddress* {. - desc: "Listening address of the metrics server (BETA)" - defaultValue: defaultAdminListenAddress - defaultValueDesc: $defaultAdminListenAddressDesc - name: "metrics-address" .}: IpAddress + keymanager* {.flatten.}: KeymanagerConf - metricsPort* {. - desc: "Listening HTTP port of the metrics server (BETA)" - defaultValue: 8108 - name: "metrics-port" .}: Port + metrics* {.flatten.}: MetricsBetaConf graffiti* {. desc: "The graffiti value that will appear in proposed blocks. " & diff --git a/beacon_chain/nimbus_beacon_node.nim b/beacon_chain/nimbus_beacon_node.nim index 276e629cca..80443ce021 100644 --- a/beacon_chain/nimbus_beacon_node.nim +++ b/beacon_chain/nimbus_beacon_node.nim @@ -994,7 +994,7 @@ proc init*( config.restAllowedOrigin, validateBeaconApiQueries, nimbusAgentStr, - config) + config.request) else: nil @@ -1051,7 +1051,7 @@ proc init*( validatorPool = newClone(ValidatorPool.init( slashingProtectionDB, config.doppelgangerDetection)) - keymanagerInitResult = initKeymanagerServer(config, restServer) + keymanagerInitResult = initKeymanagerServer(config.keymanager, config.request, restServer) keymanagerHost = if keymanagerInitResult.server != nil: newClone KeymanagerHost.init( validatorPool, @@ -2876,7 +2876,7 @@ proc doRunBeaconNode( # we disable piggy-backing on other metrics here. setSystemMetricsAutomaticUpdate(false) - node.metricsServer = waitFor(config.initMetricsServer()).valueOr: + node.metricsServer = waitFor(initMetricsServer(config.metrics)).valueOr: return when not defined(windows): diff --git a/beacon_chain/nimbus_binary_common.nim b/beacon_chain/nimbus_binary_common.nim index 500c205f18..7060447d6d 100644 --- a/beacon_chain/nimbus_binary_common.nim +++ b/beacon_chain/nimbus_binary_common.nim @@ -14,13 +14,13 @@ import std/[cpuinfo, exitprocs, os, tables, terminal, typetraits], # Nimble packages - chronos, confutils, presto, toml_serialization, metrics, + chronos, confutils, confutils/std/net, presto, toml_serialization, metrics, chronicles, chronicles/helpers as chroniclesHelpers, chronicles/topics_registry, stew/io2, metrics/chronos_httpserver, taskpools, # Local modules - ./spec/keystore, - ./buildinfo + ./spec/[keystore, network], + ./[buildinfo, version] from ./spec/datatypes/base import SPEC_VERSION @@ -39,6 +39,12 @@ when defined(posix): export confutils, toml_serialization +const + # TODO: How should we select between IPv4 and IPv6 + # Maybe there should be a config option for this. + defaultAdminListenAddress* = (static parseIpAddress("127.0.0.1")) + defaultAdminListenAddressDesc* = $defaultAdminListenAddress + type StdoutLogKind* {.pure.} = enum Auto = "auto" @@ -283,13 +289,35 @@ proc sleepAsync*(t: TimeDiff): Future[void] {. sleepAsync(nanoseconds( if t.nanoseconds < 0: 0'i64 else: t.nanoseconds)) +type + RequestConf* = object + timeout* {. + defaultValue: 0 + defaultValueDesc: "infinite" + desc: "The number of seconds to wait until complete REST request " & + "will be received" + name: "rest-request-timeout" .}: Natural + + maxBodySize* {. + defaultValue: 16_384 + desc: "Maximum size of REST request body (kilobytes)" + name: "rest-max-body-size" .}: Natural + + maxHeadersSize* {. + defaultValue: 128 + desc: "Maximum size of REST request headers (kilobytes)" + name: "rest-max-headers-size" .}: Natural + ## NOTE: If you going to adjust this value please check value + ## ``ClientMaximumValidatorIds`` and comments in + ## `spec/eth2_apis/rest_types.nim`. This values depend on each other. + proc init*(T: type RestServerRef, ip: IpAddress, port: Port, allowedOrigin: Option[string], validateFn: PatternCallback, ident: string, - config: auto): T = + request: RequestConf): T = let address = initTAddress(ip, port) serverFlags = {HttpServerFlags.QueryCommaSeparatedArray, @@ -298,12 +326,12 @@ proc init*(T: type RestServerRef, # at least once per slot (12.seconds). let headersTimeout = - if config.restRequestTimeout == 0: + if request.timeout == 0: chronos.InfiniteDuration else: - seconds(int64(config.restRequestTimeout)) - maxHeadersSize = config.restMaxRequestHeadersSize * 1024 - maxRequestBodySize = config.restMaxRequestBodySize * 1024 + seconds(int64(request.timeout)) + maxHeadersSize = request.maxHeadersSize * 1024 + maxRequestBodySize = request.maxBodySize * 1024 let res = RestServerRef.new(RestRouter.init(validateFn, allowedOrigin), address, serverFlags = serverFlags, @@ -326,20 +354,54 @@ type server*: RestServerRef token*: string + KeymanagerConf* = object + enabled* {. + desc: "Enable the REST keymanager API" + defaultValue: false + name: "keymanager" .}: bool + + port* {. + desc: "Listening port for the REST keymanager API" + defaultValue: defaultEth2RestPort + defaultValueDesc: $defaultEth2RestPortDesc + name: "keymanager-port" .}: Port + + address* {. + desc: "Listening port for the REST keymanager API" + defaultValue: defaultAdminListenAddress + defaultValueDesc: $defaultAdminListenAddressDesc + name: "keymanager-address" .}: IpAddress + + allowedOrigin* {. + desc: "Limit the access to the Keymanager API to a particular hostname " & + "(for CORS-enabled clients such as browsers)" + name: "keymanager-allow-origin" .}: Option[string] + + tokenFile* {. + desc: "A file specifying the authorization token required for accessing the keymanager API" + name: "keymanager-token-file" .}: Option[InputFile] + +# XXX from rest_key_management_api to avoid circular dependency; +# but does not seem to be used anywhere else, maybe just move it here +func validateKeymanagerApiQueries(key: string, value: string): int = + # There are no queries to validate + return 0 + proc initKeymanagerServer*( - config: auto, - existingRestServer: RestServerRef = nil): KeymanagerInitResult - {.raises: [].} = + keymanager: KeymanagerConf, + request: RequestConf, + existingRestServer: RestServerRef = nil +): KeymanagerInitResult {.raises: [].} = var token: string - let keymanagerServer = if config.keymanagerEnabled: - if config.keymanagerTokenFile.isNone: + let keymanagerServer = if keymanager.enabled: + if keymanager.tokenFile.isNone: echo "To enable the Keymanager API, you must also specify " & "the --keymanager-token-file option." quit 1 let - tokenFilePath = config.keymanagerTokenFile.get.string + tokenFilePath = keymanager.tokenFile.get.string tokenFileReadRes = readAllChars(tokenFilePath) if tokenFileReadRes.isErr: @@ -352,41 +414,71 @@ proc initKeymanagerServer*( fatal "The keymanager token should not be empty", tokenFilePath quit 1 - when compiles(config.restPort): - if existingRestServer != nil and - config.restAddress == config.keymanagerAddress and - config.restPort == config.keymanagerPort: - existingRestServer - else: - RestServerRef.init(config.keymanagerAddress, config.keymanagerPort, - config.keymanagerAllowedOrigin, - validateKeymanagerApiQueries, - nimbusAgentStr, - config) + if existingRestServer != nil and + existingRestServer.server.address == initTAddress(keymanager.address, keymanager.port): + existingRestServer else: - RestServerRef.init(config.keymanagerAddress, config.keymanagerPort, - config.keymanagerAllowedOrigin, - validateKeymanagerApiQueries, - nimbusAgentStr, - config) + RestServerRef.init( + keymanager.address, + keymanager.port, + keymanager.allowedOrigin, + validateKeymanagerApiQueries, + nimbusAgentStr, + request + ) else: nil KeymanagerInitResult(server: keymanagerServer, token: token) +type + MetricsConf* = object + enabled* {. + desc: "Enable the metrics server" + defaultValue: false + name: "metrics" .}: bool + + address* {. + desc: "Listening address of the metrics server" + defaultValue: defaultAdminListenAddress + defaultValueDesc: $defaultAdminListenAddressDesc + name: "metrics-address" .}: IpAddress + + port* {. + desc: "Listening HTTP port of the metrics server" + defaultValue: 8008 + name: "metrics-port" .}: Port + + MetricsBetaConf* = object + enabled* {. + desc: "Enable the metrics server (BETA)" + defaultValue: false + name: "metrics" .}: bool + + address* {. + desc: "Listening address of the metrics server (BETA)" + defaultValue: defaultAdminListenAddress + defaultValueDesc: $defaultAdminListenAddressDesc + name: "metrics-address" .}: IpAddress + + port* {. + desc: "Listening HTTP port of the metrics server (BETA)" + defaultValue: 8108 + name: "metrics-port" .}: Port + proc initMetricsServer*( - config: auto + metrics: MetricsConf or MetricsBetaConf ): Future[Result[Opt[MetricsHttpServerRef], string]] {. async: (raises: [CancelledError]).} = - if config.metricsEnabled: + if metrics.enabled: let - metricsAddress = config.metricsAddress - metricsPort = config.metricsPort - url = "http://" & $metricsAddress & ":" & $metricsPort & "/metrics" + address = metrics.address + port = metrics.port + url = "http://" & $address & ":" & $port & "/metrics" info "Starting metrics HTTP server", url = url - let server = MetricsHttpServerRef.new($metricsAddress, metricsPort).valueOr: + let server = MetricsHttpServerRef.new($address, port).valueOr: fatal "Could not start metrics HTTP server", url = url, reason = error return err($error) diff --git a/beacon_chain/nimbus_validator_client.nim b/beacon_chain/nimbus_validator_client.nim index 9646ff5279..3e6fb84965 100644 --- a/beacon_chain/nimbus_validator_client.nim +++ b/beacon_chain/nimbus_validator_client.nim @@ -397,7 +397,7 @@ proc asyncInit(vc: ValidatorClientRef): Future[ValidatorClientRef] {. vc.beaconClock = await vc.initClock() - vc.metricsServer = (await vc.config.initMetricsServer()).valueOr: + vc.metricsServer = (await initMetricsServer(vc.config.metrics)).valueOr: raise newException(ValidatorClientError, "Could not initialize metrics server") @@ -419,7 +419,7 @@ proc asyncInit(vc: ValidatorClientRef): Future[ValidatorClientRef] {. "Could not initialize local validators") let - keymanagerInitResult = initKeymanagerServer(vc.config, nil) + keymanagerInitResult = initKeymanagerServer(vc.config.keymanager, vc.config.request, nil) func getCapellaForkVersion(): Opt[Version] = if vc.forkConfig.isNone(): diff --git a/vendor/nim-confutils b/vendor/nim-confutils index fd40a96a6d..3b8f3fb2b7 160000 --- a/vendor/nim-confutils +++ b/vendor/nim-confutils @@ -1 +1 @@ -Subproject commit fd40a96a6d18c48ba9145537d1a7b5ed1e4ac612 +Subproject commit 3b8f3fb2b719eab41d74a891677e540977caddf5