From 80e83c92f87b51bec2aaf2bdc4d42808a5cf8269 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Sun, 2 Jun 2024 17:00:21 +0200 Subject: [PATCH] feat: add cli flags to configure http wire format --- packages/api/src/index.ts | 3 +- packages/api/src/utils/client/httpClient.ts | 2 +- .../beacon-node/test/utils/node/validator.ts | 4 ++- packages/cli/src/cmds/validator/handler.ts | 26 ++++++++++++++-- packages/cli/src/cmds/validator/options.ts | 18 +++++++++++ packages/validator/src/validator.ts | 31 ++++++++++++------- 6 files changed, 68 insertions(+), 16 deletions(-) diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 1342c41eceed..a93fc83d4628 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -3,7 +3,8 @@ export * from "./beacon/index.js"; export {HttpStatusCode} from "./utils/httpStatusCode.js"; export {WireFormat} from "./utils/wireFormat.js"; export type {HttpErrorCodes, HttpSuccessCodes} from "./utils/httpStatusCode.js"; -export {ApiResponse, HttpClient, FetchError, isFetchError, fetch} from "./utils/client/index.js"; +export {ApiResponse, HttpClient, FetchError, isFetchError, fetch, defaultInit} from "./utils/client/index.js"; +export type {ApiRequestInit} from "./utils/client/request.js"; export type {Endpoint} from "./utils/types.js"; export type { ApiClientMethods, diff --git a/packages/api/src/utils/client/httpClient.ts b/packages/api/src/utils/client/httpClient.ts index bde9c230cebd..29221efa1c7f 100644 --- a/packages/api/src/utils/client/httpClient.ts +++ b/packages/api/src/utils/client/httpClient.ts @@ -40,7 +40,7 @@ const URL_SCORE_DELTA_ERROR = 2 * URL_SCORE_DELTA_SUCCESS; const URL_SCORE_MAX = 10 * URL_SCORE_DELTA_SUCCESS; const URL_SCORE_MIN = 0; -const defaultInit: Required = { +export const defaultInit: Required = { timeoutMs: DEFAULT_TIMEOUT_MS, retries: DEFAULT_RETRIES, retryDelay: DEFAULT_RETRY_DELAY, diff --git a/packages/beacon-node/test/utils/node/validator.ts b/packages/beacon-node/test/utils/node/validator.ts index 29175b94d59a..1c17f0c5ed46 100644 --- a/packages/beacon-node/test/utils/node/validator.ts +++ b/packages/beacon-node/test/utils/node/validator.ts @@ -68,7 +68,9 @@ export async function getAndInitDevValidators({ Validator.initializeFromBeaconNode({ db, config: node.config, - api: useRestApi ? getNodeApiUrl(node) : getApiFromServerHandlers(node.api), + api: { + clientOrUrls: useRestApi ? getNodeApiUrl(node) : getApiFromServerHandlers(node.api), + }, slashingProtection, logger, processShutdownCallback: () => {}, diff --git a/packages/cli/src/cmds/validator/handler.ts b/packages/cli/src/cmds/validator/handler.ts index 80b1040f226a..fdd93f1ebfc9 100644 --- a/packages/cli/src/cmds/validator/handler.ts +++ b/packages/cli/src/cmds/validator/handler.ts @@ -8,7 +8,7 @@ import { ValidatorProposerConfig, defaultOptions, } from "@lodestar/validator"; -import {routes} from "@lodestar/api"; +import {WireFormat, routes} from "@lodestar/api"; import {getMetrics} from "@lodestar/validator"; import { RegistryMetricCreator, @@ -152,7 +152,13 @@ export async function validatorHandler(args: IValidatorCliArgs & GlobalArgs): Pr db, config, slashingProtection, - api: args.beaconNodes, + api: { + clientOrUrls: args.beaconNodes, + globalInit: { + requestWireFormat: parseWireFormat(args, "http.requestWireFormat"), + responseWireFormat: parseWireFormat(args, "http.responseWireFormat"), + }, + }, logger, processShutdownCallback, signers, @@ -269,3 +275,19 @@ function parseBroadcastValidation(broadcastValidation?: string): routes.beacon.B return broadcastValidation as routes.beacon.BroadcastValidation; } + +function parseWireFormat(args: IValidatorCliArgs, key: keyof IValidatorCliArgs): WireFormat | undefined { + const wireFormat = args[key]; + + if (wireFormat !== undefined) { + switch (wireFormat) { + case WireFormat.json: + case WireFormat.ssz: + break; + default: + throw new YargsError(`Invalid input for ${key}, must be one of "${WireFormat.json}" or "${WireFormat.ssz}"`); + } + } + + return wireFormat; +} diff --git a/packages/cli/src/cmds/validator/options.ts b/packages/cli/src/cmds/validator/options.ts index d1603461e438..08548edb1072 100644 --- a/packages/cli/src/cmds/validator/options.ts +++ b/packages/cli/src/cmds/validator/options.ts @@ -1,3 +1,4 @@ +import {WireFormat, defaultInit} from "@lodestar/api"; import {defaultOptions} from "@lodestar/validator"; import {CliCommandOptions} from "@lodestar/utils"; import {LogArgs, logOptions} from "../../options/logOptions.js"; @@ -55,6 +56,9 @@ export type IValidatorCliArgs = AccountValidatorArgs & importKeystores?: string[]; importKeystoresPassword?: string; + "http.requestWireFormat"?: string; + "http.responseWireFormat"?: string; + "externalSigner.url"?: string; "externalSigner.pubkeys"?: string[]; "externalSigner.fetch"?: boolean; @@ -304,6 +308,20 @@ export const validatorOptions: CliCommandOptions = { type: "boolean", }, + "http.requestWireFormat": { + type: "string", + description: `Wire format to use in HTTP requests to beacon node. Can be one of \`${WireFormat.json}\` or \`${WireFormat.ssz}\``, + defaultDescription: `${defaultInit.requestWireFormat}`, + group: "http", + }, + + "http.responseWireFormat": { + type: "string", + description: `Preferred wire format for HTTP responses from beacon node. Can be one of \`${WireFormat.json}\` or \`${WireFormat.ssz}\``, + defaultDescription: `${defaultInit.responseWireFormat}`, + group: "http", + }, + // External signer "externalSigner.url": { diff --git a/packages/validator/src/validator.ts b/packages/validator/src/validator.ts index 6b18e90b0b6a..3d9c28977719 100644 --- a/packages/validator/src/validator.ts +++ b/packages/validator/src/validator.ts @@ -3,7 +3,7 @@ import {BLSPubkey, phase0, ssz} from "@lodestar/types"; import {createBeaconConfig, BeaconConfig, ChainForkConfig} from "@lodestar/config"; import {Genesis} from "@lodestar/types/phase0"; import {Logger, toSafePrintableUrl} from "@lodestar/utils"; -import {getClient, ApiClient, routes} from "@lodestar/api"; +import {getClient, ApiClient, routes, ApiRequestInit, defaultInit} from "@lodestar/api"; import {computeEpochAtSlot, getCurrentSlot} from "@lodestar/state-transition"; import {Clock, IClock} from "./util/clock.js"; import {waitForGenesis} from "./genesis.js"; @@ -45,7 +45,10 @@ export type ValidatorOptions = { slashingProtection: ISlashingProtection; db: LodestarValidatorDatabaseController; config: ChainForkConfig; - api: ApiClient | string | string[]; + api: { + clientOrUrls: ApiClient | string | string[]; + globalInit?: ApiRequestInit; + }; signers: Signer[]; logger: Logger; processShutdownCallback: ProcessShutdownCallback; @@ -158,20 +161,21 @@ export class Validator { const loggerVc = getLoggerVc(logger, clock); let api: ApiClient; - if (typeof opts.api === "string" || Array.isArray(opts.api)) { + const {clientOrUrls, globalInit} = opts.api; + if (typeof clientOrUrls === "string" || Array.isArray(clientOrUrls)) { // This new api instance can make do with default timeout as a faster timeout is // not necessary since this instance won't be used for validator duties api = getClient( { - urls: typeof opts.api === "string" ? [opts.api] : opts.api, + urls: typeof clientOrUrls === "string" ? [clientOrUrls] : clientOrUrls, // Validator would need the beacon to respond within the slot // See https://github.com/ChainSafe/lodestar/issues/5315 for rationale - globalInit: {timeoutMs: config.SECONDS_PER_SLOT * 1000, signal: controller.signal}, + globalInit: {timeoutMs: config.SECONDS_PER_SLOT * 1000, signal: controller.signal, ...globalInit}, }, {config, logger, metrics: metrics?.restApiClient} ); } else { - api = opts.api; + api = clientOrUrls; } const indicesService = new IndicesService(logger, api, metrics); @@ -270,14 +274,19 @@ export class Validator { const {logger, config} = opts; let api: ApiClient; - if (typeof opts.api === "string" || Array.isArray(opts.api)) { - const urls = typeof opts.api === "string" ? [opts.api] : opts.api; + const {clientOrUrls, globalInit} = opts.api; + if (typeof clientOrUrls === "string" || Array.isArray(clientOrUrls)) { + const urls = typeof clientOrUrls === "string" ? [clientOrUrls] : clientOrUrls; // This new api instance can make do with default timeout as a faster timeout is // not necessary since this instance won't be used for validator duties - api = getClient({urls, globalInit: {signal: opts.abortController.signal}}, {config, logger}); - logger.info("Beacon node", {urls: urls.map(toSafePrintableUrl).toString()}); + api = getClient({urls, globalInit: {signal: opts.abortController.signal, ...globalInit}}, {config, logger}); + logger.info("Beacon node", { + urls: urls.map(toSafePrintableUrl).toString(), + requestWireFormat: globalInit?.requestWireFormat ?? defaultInit.requestWireFormat, + responseWireFormat: globalInit?.responseWireFormat ?? defaultInit.responseWireFormat, + }); } else { - api = opts.api; + api = clientOrUrls; } const genesis = await waitForGenesis(api, opts.logger, opts.abortController.signal);