From 23bf4d00fdf1753cb0b9d02853222e6f27d8a83f Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Thu, 9 Mar 2023 15:02:27 +0100 Subject: [PATCH 1/2] Add logging support to prover --- packages/prover/package.json | 4 +- packages/prover/src/cli/cli.ts | 2 + packages/prover/src/cli/cmds/index.ts | 4 +- packages/prover/src/cli/cmds/start/handler.ts | 10 +- packages/prover/src/cli/cmds/start/index.ts | 3 +- packages/prover/src/cli/cmds/start/options.ts | 11 -- packages/prover/src/cli/options.ts | 36 ++++++ packages/prover/src/interfaces.ts | 15 ++- .../src/proof_provider/payload_store.ts | 21 +++- .../src/proof_provider/proof_provider.ts | 41 +++++-- packages/prover/src/utils/consensus.ts | 19 ++- packages/prover/src/utils/execution.ts | 10 +- packages/prover/src/utils/logger.ts | 112 ++++++++++++------ .../src/verified_requests/eth_getBalance.ts | 6 +- packages/prover/src/web3_provider.ts | 65 ++++++---- packages/prover/src/web3_proxy.ts | 39 +++--- yarn.lock | 26 ++++ 17 files changed, 306 insertions(+), 118 deletions(-) create mode 100644 packages/prover/src/cli/options.ts diff --git a/packages/prover/package.json b/packages/prover/package.json index 8894844ba2e1..42dd39daa779 100644 --- a/packages/prover/package.json +++ b/packages/prover/package.json @@ -68,7 +68,9 @@ "http-proxy": "^1.18.1", "yargs": "^16.1.0", "source-map-support": "^0.5.19", - "find-up": "^5.0.0" + "find-up": "^5.0.0", + "winston": "^3.3.3", + "winston-transport": "^4.5.0" }, "devDependencies": { "@types/http-proxy": "^1.17.10", diff --git a/packages/prover/src/cli/cli.ts b/packages/prover/src/cli/cli.ts index cfd6e8213fb9..31f11e3a300d 100644 --- a/packages/prover/src/cli/cli.ts +++ b/packages/prover/src/cli/cli.ts @@ -5,6 +5,7 @@ import {hideBin} from "yargs/helpers"; import {registerCommandToYargs} from "../utils/command.js"; import {getVersionData} from "../utils/version.js"; import {cmds} from "./cmds/index.js"; +import {globalOptions} from "./options.js"; const {version} = getVersionData(); const topBanner = `🌟 Lodestar Prover Proxy: Ethereum RPC proxy for RPC responses, verified against the trusted block hashes. @@ -30,6 +31,7 @@ export function getLodestarProverCli(): yargs.Argv { // Manually processing options is typesafe tho more verbose "dot-notation": false, }) + .options(globalOptions) // blank scriptName so that help text doesn't display the cli name before each command .scriptName("") .demandCommand(1) diff --git a/packages/prover/src/cli/cmds/index.ts b/packages/prover/src/cli/cmds/index.ts index c96cf7d067bd..3b888ab41d83 100644 --- a/packages/prover/src/cli/cmds/index.ts +++ b/packages/prover/src/cli/cmds/index.ts @@ -1,3 +1,5 @@ +import {CliCommand} from "../../utils/command.js"; +import {GlobalArgs} from "../options.js"; import {proverProxyStartCommand} from "./start/index.js"; -export const cmds = [proverProxyStartCommand]; +export const cmds: Required>>["subcommands"] = [proverProxyStartCommand]; diff --git a/packages/prover/src/cli/cmds/start/handler.ts b/packages/prover/src/cli/cmds/start/handler.ts index 29ed0f71046e..37e1b91acf43 100644 --- a/packages/prover/src/cli/cmds/start/handler.ts +++ b/packages/prover/src/cli/cmds/start/handler.ts @@ -1,16 +1,18 @@ import {LCTransport} from "../../../interfaces.js"; import {createVerifiedExecutionProxy, VerifiedProxyOptions} from "../../../web3_proxy.js"; -import {stdLogger} from "../../../utils/logger.js"; +import {GlobalArgs, parseGlobalArgs} from "../../options.js"; import {parseStartArgs, StartArgs} from "./options.js"; /** * Runs a beacon node. */ -export async function proverProxyStartHandler(args: StartArgs): Promise { +export async function proverProxyStartHandler(args: StartArgs & GlobalArgs): Promise { + const {network, logLevel} = parseGlobalArgs(args); const opts = parseStartArgs(args); - const {network, executionRpcUrl, port, wsCheckpoint} = opts; + const {executionRpcUrl, port, wsCheckpoint} = opts; + const options: VerifiedProxyOptions = { - logger: stdLogger, + logLevel, network, executionRpcUrl, wsCheckpoint, diff --git a/packages/prover/src/cli/cmds/start/index.ts b/packages/prover/src/cli/cmds/start/index.ts index b28f182e5dfa..307fcdb7916f 100644 --- a/packages/prover/src/cli/cmds/start/index.ts +++ b/packages/prover/src/cli/cmds/start/index.ts @@ -1,8 +1,9 @@ import {CliCommand, CliCommandOptions} from "../../../utils/command.js"; +import {GlobalArgs} from "../../options.js"; import {proverProxyStartHandler} from "./handler.js"; import {StartArgs, startOptions} from "./options.js"; -export const proverProxyStartCommand: CliCommand = { +export const proverProxyStartCommand: CliCommand = { command: "start", describe: "Start proxy server", examples: [ diff --git a/packages/prover/src/cli/cmds/start/options.ts b/packages/prover/src/cli/cmds/start/options.ts index 647317aae498..30904b885f03 100644 --- a/packages/prover/src/cli/cmds/start/options.ts +++ b/packages/prover/src/cli/cmds/start/options.ts @@ -1,10 +1,8 @@ -import {NetworkName, networksChainConfig} from "@lodestar/config/networks"; import {LCTransport} from "../../../interfaces.js"; import {CliCommandOptions} from "../../../utils/command.js"; export type StartArgs = { port: number; - network: string; "execution-rpc-url": string; transport: "rest" | "p2p"; "beacon-urls"?: string[]; @@ -13,7 +11,6 @@ export type StartArgs = { }; export type StartOptions = { - network: NetworkName; executionRpcUrl: string; port: number; wsCheckpoint?: string; @@ -25,13 +22,6 @@ export const startOptions: CliCommandOptions = { type: "number", default: 8080, }, - - network: { - description: "Specify the network to connect.", - type: "string", - choices: Object.keys(networksChainConfig), - }, - "execution-rpc-url": { description: "RPC url for the execution node.", type: "string", @@ -68,7 +58,6 @@ export function parseStartArgs(args: StartArgs): StartOptions { // Remove undefined values to allow deepmerge to inject default values downstream return { port: args["port"], - network: args["network"] as NetworkName, executionRpcUrl: args["execution-rpc-url"], transport: args["transport"] === "p2p" ? LCTransport.P2P : LCTransport.Rest, urls: args["transport"] === "rest" ? args["beacon-urls"] ?? [] : [], diff --git a/packages/prover/src/cli/options.ts b/packages/prover/src/cli/options.ts new file mode 100644 index 000000000000..a8dc4a4b5a1c --- /dev/null +++ b/packages/prover/src/cli/options.ts @@ -0,0 +1,36 @@ +import {NetworkName, networksChainConfig} from "@lodestar/config/networks"; +import {LogLevel, LogLevels} from "@lodestar/utils"; +import {CliCommandOptions} from "../utils/command.js"; + +export type GlobalArgs = { + network: string; + "log-level": string; +}; + +export type GlobalOptions = { + logLevel: LogLevel; + network: NetworkName; +}; + +export const globalOptions: CliCommandOptions = { + network: { + description: "Specify the network to connect.", + type: "string", + choices: Object.keys(networksChainConfig), + }, + + "log-level": { + description: "Set the log level.", + type: "string", + choices: LogLevels, + default: "info", + }, +}; + +export function parseGlobalArgs(args: GlobalArgs): GlobalOptions { + // Remove undefined values to allow deepmerge to inject default values downstream + return { + network: args["network"] as NetworkName, + logLevel: args["log-level"] as LogLevel, + }; +} diff --git a/packages/prover/src/interfaces.ts b/packages/prover/src/interfaces.ts index a3c4d5bb484c..9d2c513fc742 100644 --- a/packages/prover/src/interfaces.ts +++ b/packages/prover/src/interfaces.ts @@ -1,5 +1,6 @@ import {ChainForkConfig} from "@lodestar/config"; import {NetworkName} from "@lodestar/config/networks"; +import {Logger, LogLevel} from "@lodestar/utils"; import {ProofProvider} from "./proof_provider/proof_provider.js"; import {ELRequestPayload, ELResponse} from "./types.js"; @@ -11,9 +12,10 @@ export enum LCTransport { export type RootProviderInitOptions = { network: NetworkName; signal: AbortSignal; + logger: Logger; config?: ChainForkConfig; wsCheckpoint?: string; -} & ({transport: LCTransport.Rest; urls: string[]} | {transport: LCTransport.P2P; bootnodes: string[]}); +} & ConsensusNodeOptions; export type ELRequestMethod = (payload: ELRequestPayload) => Promise; @@ -47,5 +49,14 @@ export type Web3Provider = SendProvider | EthersProvider | SendAsyncProvider | R export type ELVerifiedRequestHandler = (opts: { payload: ELRequestPayload; handler: ELRequestMethod; - rootProvider: ProofProvider; + proofProvider: ProofProvider; + logger: Logger; }) => Promise>; + +// Either a logger is provided by user or user specify a log level +// If both are skipped then we don't log anything (useful for browser plugins) +export type LogOptions = {logger?: Logger; logLevel?: never} | {logLevel?: LogLevel; logger?: never}; + +export type ConsensusNodeOptions = + | {transport: LCTransport.Rest; urls: string[]} + | {transport: LCTransport.P2P; bootnodes: string[]}; diff --git a/packages/prover/src/proof_provider/payload_store.ts b/packages/prover/src/proof_provider/payload_store.ts index d5dc1e74a593..685b82454985 100644 --- a/packages/prover/src/proof_provider/payload_store.ts +++ b/packages/prover/src/proof_provider/payload_store.ts @@ -1,5 +1,6 @@ import {Api} from "@lodestar/api"; import {allForks, capella} from "@lodestar/types"; +import {Logger} from "@lodestar/utils"; import {MAX_PAYLOAD_HISTORY} from "../constants.js"; import {getExecutionPayloadForBlockNumber, getExecutionPayloads} from "../utils/consensus.js"; import {bufferToHex, hexToNumber} from "../utils/conversion.js"; @@ -23,7 +24,7 @@ export class PayloadStore { private latestBlockRoot: BlockELRoot | null = null; - constructor(private opts: {api: Api}) {} + constructor(private opts: {api: Api; logger: Logger}) {} get finalized(): allForks.ExecutionPayload | undefined { const finalizedMaxRoot = this.finalizedRoots.get(this.finalizedRoots.max); @@ -129,7 +130,14 @@ export class PayloadStore { else if (finalized && !existingELRoot) { this.payloads.set( bufferToHex(header.execution.blockHash), - (await getExecutionPayloads(this.opts.api, blockSlot, blockSlot))[blockSlot] + ( + await getExecutionPayloads({ + api: this.opts.api, + startSlot: blockSlot, + endSlot: blockSlot, + logger: this.opts.logger, + }) + )[blockSlot] ); } @@ -149,7 +157,14 @@ export class PayloadStore { } // We do not have the payload for this block, we need to fetch it - const payload = (await getExecutionPayloads(this.opts.api, blockSlot, blockSlot))[blockSlot]; + const payload = ( + await getExecutionPayloads({ + api: this.opts.api, + startSlot: blockSlot, + endSlot: blockSlot, + logger: this.opts.logger, + }) + )[blockSlot]; this.set(payload, false); this.prune(); } diff --git a/packages/prover/src/proof_provider/proof_provider.ts b/packages/prover/src/proof_provider/proof_provider.ts index 221460f27191..0e6930f6f54b 100644 --- a/packages/prover/src/proof_provider/proof_provider.ts +++ b/packages/prover/src/proof_provider/proof_provider.ts @@ -5,6 +5,7 @@ import {Lightclient, LightclientEvent, RunStatusCode} from "@lodestar/light-clie import {LightClientRestTransport} from "@lodestar/light-client/transport"; import {isForkWithdrawals} from "@lodestar/params"; import {allForks, capella} from "@lodestar/types"; +import {Logger} from "@lodestar/utils"; import {LCTransport, RootProviderInitOptions} from "../interfaces.js"; import {assertLightClient} from "../utils/assertion.js"; import { @@ -13,6 +14,7 @@ import { getSyncCheckpoint, getUnFinalizedRangeForPayloads, } from "../utils/consensus.js"; +import {bufferToHex} from "../utils/conversion.js"; import {PayloadStore} from "./payload_store.js"; type RootProviderOptions = Omit & { @@ -23,13 +25,15 @@ type RootProviderOptions = Omit & { export class ProofProvider { private store: PayloadStore; + private logger: Logger; // Make sure readyPromise doesn't throw unhandled exceptions private readyPromise?: Promise; lightClient?: Lightclient; constructor(private opts: RootProviderOptions) { - this.store = new PayloadStore({api: opts.api}); + this.store = new PayloadStore({api: opts.api, logger: opts.logger}); + this.logger = opts.logger; } async waitToBeReady(): Promise { @@ -40,7 +44,10 @@ export class ProofProvider { if (opts.transport === LCTransport.P2P) { throw new Error("P2P mode not supported yet"); } - + opts.logger.info("Creating ProofProvider instance with REST APIs", { + network: opts.network, + urls: opts.urls.join(","), + }); const config = createChainForkConfig(networksChainConfig[opts.network]); const api = getClient({urls: opts.urls}, {config}); const transport = new LightClientRestTransport(api); @@ -66,11 +73,12 @@ export class ProofProvider { if (this.lightClient !== undefined) { throw Error("Light client already initialized and syncing."); } - + this.logger.info("Starting sync for proof provider"); const {api, config, transport} = this.opts; const checkpointRoot = await getSyncCheckpoint(api, wsCheckpoint); const genesisData = await getGenesisData(api); + this.logger.info("Initializing lightclient", {checkpointRoot: bufferToHex(checkpointRoot)}); this.lightClient = await Lightclient.initializeFromCheckpointRoot({ checkpointRoot, config, @@ -88,21 +96,36 @@ export class ProofProvider { } }; this.lightClient?.emitter.on(LightclientEvent.statusChange, lightClientStarted); + this.logger.info("Initiating lightclient"); this.lightClient?.start(); }); + this.logger.info("Lightclient synced", this.getStatus()); this.registerEvents(); // Load the payloads from the CL + this.logger.info("Building EL payload history"); const {start, end} = await getUnFinalizedRangeForPayloads(this.lightClient); - const payloads = await getExecutionPayloads(this.opts.api, start, end); + const payloads = await getExecutionPayloads({ + api: this.opts.api, + startSlot: start, + endSlot: end, + logger: this.logger, + }); for (const payload of Object.values(payloads)) { this.store.set(payload, false); } // Load the finalized payload from the CL const finalizedSlot = this.lightClient.getFinalized().beacon.slot; - const finalizedPayload = await getExecutionPayloads(this.opts.api, finalizedSlot, finalizedSlot); + this.logger.debug("Getting finalized slot from lightclient", {finalizedSlot}); + const finalizedPayload = await getExecutionPayloads({ + api: this.opts.api, + startSlot: finalizedSlot, + endSlot: finalizedSlot, + logger: this.logger, + }); this.store.set(finalizedPayload[finalizedSlot], true); + this.logger.info("Proof provider ready"); } getStatus(): {latest: number; finalized: number; status: RunStatusCode} { @@ -172,17 +195,13 @@ export class ProofProvider { this.lightClient.emitter.on(LightclientEvent.lightClientFinalityHeader, async (data) => { await this.processLCHeader(data, true).catch((e) => { - // Will be replaced with logger in next PR. - // eslint-disable-next-line no-console - console.error(e); + this.logger.error("Error processing finality update", null, e); }); }); this.lightClient.emitter.on(LightclientEvent.lightClientOptimisticHeader, async (data) => { await this.processLCHeader(data).catch((e) => { - // Will be replaced with logger in next PR. - // eslint-disable-next-line no-console - console.error(e); + this.logger.error("Error processing optimistic update", null, e); }); }); } diff --git a/packages/prover/src/utils/consensus.ts b/packages/prover/src/utils/consensus.ts index 38c1b163f484..0845db9cd78e 100644 --- a/packages/prover/src/utils/consensus.ts +++ b/packages/prover/src/utils/consensus.ts @@ -2,6 +2,7 @@ import {Api} from "@lodestar/api/beacon"; import {allForks, Bytes32, capella} from "@lodestar/types"; import {GenesisData, Lightclient} from "@lodestar/light-client"; import {ApiError} from "@lodestar/api"; +import {Logger} from "@lodestar/utils"; import {MAX_PAYLOAD_HISTORY} from "../constants.js"; import {hexToBuffer} from "./conversion.js"; @@ -32,13 +33,19 @@ export async function getUnFinalizedRangeForPayloads(lightClient: Lightclient): }; } -export async function getExecutionPayloads( - api: Api, - startSlot: number, - endSlot: number -): Promise> { +export async function getExecutionPayloads({ + api, + startSlot, + endSlot, + logger, +}: { + api: Api; + startSlot: number; + endSlot: number; + logger: Logger; +}): Promise> { [startSlot, endSlot] = [Math.min(startSlot, endSlot), Math.max(startSlot, endSlot)]; - + logger.debug("Fetching EL payloads", {startSlot, endSlot}); const payloads: Record = {}; let slot = endSlot; diff --git a/packages/prover/src/utils/execution.ts b/packages/prover/src/utils/execution.ts index b42ff8b19f0a..f3271bb70db6 100644 --- a/packages/prover/src/utils/execution.ts +++ b/packages/prover/src/utils/execution.ts @@ -3,6 +3,7 @@ import {Trie} from "@ethereumjs/trie"; import {Account} from "@ethereumjs/util"; import {keccak256} from "ethereum-cryptography/keccak.js"; import {Bytes32} from "@lodestar/types"; +import {Logger} from "@lodestar/utils"; import {ethGetBalance} from "../verified_requests/eth_getBalance.js"; import {ELRequestPayload, ELResponse, ELProof, ELStorageProof, HexString} from "../types.js"; import {ProofProvider} from "../proof_provider/proof_provider.js"; @@ -19,20 +20,23 @@ export async function processAndVerifyRequest({ payload, handler, proofProvider, + logger, }: { payload: ELRequestPayload; handler: ELRequestMethod; proofProvider: ProofProvider; + logger: Logger; }): Promise { await proofProvider.waitToBeReady(); + logger.debug("Processing request", {method: payload.method, params: JSON.stringify(payload.params)}); const verifiedHandler = supportedELRequests[payload.method]; if (verifiedHandler !== undefined) { - return verifiedHandler({payload, handler, rootProvider: proofProvider}); + logger.verbose("Verified request handler found", {method: payload.method}); + return verifiedHandler({payload, handler, proofProvider, logger}); } - // eslint-disable-next-line no-console - console.warn(`Request handler for ${payload.method} is not implemented.`); + logger.warn("Verified request handler not found. Falling back to proxy.", {method: payload.method}); return handler(payload); } diff --git a/packages/prover/src/utils/logger.ts b/packages/prover/src/utils/logger.ts index db2b6d6920f9..ba4bfc17c005 100644 --- a/packages/prover/src/utils/logger.ts +++ b/packages/prover/src/utils/logger.ts @@ -1,46 +1,86 @@ -import {LogData, Logger, LoggerChildOpts} from "@lodestar/utils"; -import {ELRequestPayload} from "../types.js"; - -const printLogData = (data: LogData): string => { - if (!Array.isArray(data) && data !== null && typeof data === "object") { - return Object.entries(data) - .map(([key, value]) => `${key}=${value}`) - .join(" "); +import winston from "winston"; +import Transport from "winston-transport"; +import {LogData, Logger, LoggerChildOpts, createWinstonLogger} from "@lodestar/utils"; +import {LogOptions} from "../interfaces.js"; + +type BrowserLogLevels = "error" | "warn" | "info" | "debug"; + +class BrowserConsole extends Transport { + name = "BrowserConsole"; + private levels: Record = { + error: 0, + warn: 1, + info: 2, + debug: 4, + }; + + private methods: Record = { + error: "error", + warn: "warn", + info: "info", + debug: "log", + }; + + constructor(opts: winston.transport.TransportStreamOptions | undefined) { + super(opts); + this.level = opts?.level && this.levels.hasOwnProperty(opts.level) ? opts.level : "info"; } - return JSON.stringify(data); -}; -const stdLogHandler = (level: string): ((message: string, context?: LogData, error?: Error | undefined) => void) => { - if (process === undefined) { - return (message: string, context?: LogData, error?: Error | undefined): void => { - // eslint-disable-next-line no-console - console.log( - `${level}: ${message} ${context === undefined ? "" : printLogData(context)} ${error ? error.stack : ""}` - ); - }; + log(method: string | number, message: unknown): void { + setImmediate(() => { + this.emit("logged", method); + }); + + const val = this.levels[method as BrowserLogLevels]; + const mappedMethod = this.methods[method as BrowserLogLevels]; + + if (val <= this.levels[this.level as BrowserLogLevels]) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, no-console, @typescript-eslint/ban-ts-comment + // @ts-expect-error + console[mappedMethod](message); + } } - return (message: string, context?: LogData, error?: Error | undefined): void => { - const stream = level === "error" ? process.stderr : process.stdout; - stream.write( - `${level}: ${message} ${context === undefined ? "" : printLogData(context)} ${error ? error.stack : ""}\n` - ); - }; -}; +} -export const stdLogger: Logger = { - error: stdLogHandler("error"), - warn: stdLogHandler("warn"), - info: stdLogHandler("info"), - debug: stdLogHandler("debug"), - verbose: stdLogHandler("verb"), +const emptyLogger: Logger = { + // eslint-disable-next-line func-names + error: function (_message: string, _context?: LogData, _error?: Error | undefined): void { + // Do nothing + }, + // eslint-disable-next-line func-names + warn: function (_message: string, _context?: LogData, _error?: Error | undefined): void { + // Do nothing + }, + // eslint-disable-next-line func-names + info: function (_message: string, _context?: LogData, _error?: Error | undefined): void { + // Do nothing + }, + // eslint-disable-next-line func-names + verbose: function (_message: string, _context?: LogData, _error?: Error | undefined): void { + // Do nothing + }, + // eslint-disable-next-line func-names + debug: function (_message: string, _context?: LogData, _error?: Error | undefined): void { + // Do nothing + }, // eslint-disable-next-line func-names child: function (_options: LoggerChildOpts): Logger { - throw new Error("Not supported."); + return emptyLogger; }, }; -export function logRequest({logger, payload}: {logger: Logger; payload: ELRequestPayload}): void { - logger.debug( - `Req method=${payload.method} params=${payload.params === undefined ? "" : JSON.stringify(payload.params)}` - ); +export function getLogger(opts: LogOptions): Logger { + if (opts.logger) return opts.logger; + + // Code is running in the node environment + if (opts.logLevel && process !== undefined) { + return createWinstonLogger({level: opts.logLevel, module: "prover"}, [new winston.transports.Console()]); + } + + if (opts.logLevel && process === undefined) { + return createWinstonLogger({level: opts.logLevel, module: "prover"}, [new BrowserConsole({level: opts.logLevel})]); + } + + // For the case when user don't want to fill in the logs of consumer browser + return emptyLogger; } diff --git a/packages/prover/src/verified_requests/eth_getBalance.ts b/packages/prover/src/verified_requests/eth_getBalance.ts index 2ec1fe5fb296..db0f5f42715c 100644 --- a/packages/prover/src/verified_requests/eth_getBalance.ts +++ b/packages/prover/src/verified_requests/eth_getBalance.ts @@ -6,12 +6,13 @@ import {generateRPCResponseForPayload, generateUnverifiedResponseForPayload} fro export const ethGetBalance: ELVerifiedRequestHandler<[address: string, block?: number | string], string> = async ({ handler, payload, - rootProvider, + logger, + proofProvider, }) => { const { params: [address, block], } = payload; - const executionPayload = await rootProvider.getExecutionPayload(block ?? "latest"); + const executionPayload = await proofProvider.getExecutionPayload(block ?? "latest"); const proof = await getELProof(handler, [address, [], bufferToHex(executionPayload.blockHash)]); if ( @@ -25,5 +26,6 @@ export const ethGetBalance: ELVerifiedRequestHandler<[address: string, block?: n return generateRPCResponseForPayload(payload, proof.balance); } + logger.error("Request could not be verified."); return generateUnverifiedResponseForPayload(payload, "eth_getBalance request can not be verified."); }; diff --git a/packages/prover/src/web3_provider.ts b/packages/prover/src/web3_provider.ts index b1e0ad7123a4..daaf03bc4642 100644 --- a/packages/prover/src/web3_provider.ts +++ b/packages/prover/src/web3_provider.ts @@ -1,8 +1,10 @@ import {NetworkName} from "@lodestar/config/networks"; +import {Logger} from "@lodestar/utils"; import { + ConsensusNodeOptions, EIP1193Provider, EthersProvider, - LCTransport, + LogOptions, RequestProvider, SendAsyncProvider, SendProvider, @@ -18,11 +20,10 @@ import { isSendProvider, } from "./utils/assertion.js"; import {processAndVerifyRequest} from "./utils/execution.js"; +import {getLogger} from "./utils/logger.js"; -type ProvableProviderInitOpts = {network?: NetworkName; wsCheckpoint?: string} & ( - | {transport: LCTransport.Rest; urls: string[]} - | {transport: LCTransport.P2P; bootnodes: string[]} -); +type ProvableProviderInitOpts = {network?: NetworkName; wsCheckpoint?: string; signal?: AbortSignal} & LogOptions & + ConsensusNodeOptions; const defaultNetwork = "mainnet"; @@ -30,37 +31,44 @@ export function createVerifiedExecutionProvider( provider: T, opts: ProvableProviderInitOpts ): {provider: T; proofProvider: ProofProvider} { - const controller = new AbortController(); + const signal = opts.signal ?? new AbortController().signal; + const logger = getLogger(opts); const proofProvider = ProofProvider.init({ ...opts, network: opts.network ?? defaultNetwork, - signal: controller.signal, + signal, + logger, }); if (isSendProvider(provider)) { - return {provider: handleSendProvider(provider, proofProvider) as T, proofProvider: proofProvider}; + logger.debug("Creating a provider which is recognized as legacy provider with 'send' method."); + return {provider: handleSendProvider(provider, proofProvider, logger) as T, proofProvider}; } if (isEthersProvider(provider)) { - return {provider: handleEthersProvider(provider, proofProvider) as T, proofProvider: proofProvider}; + logger.debug("Creating a provider which is recognized as 'ethers' provider."); + return {provider: handleEthersProvider(provider, proofProvider, logger) as T, proofProvider}; } if (isRequestProvider(provider)) { - return {provider: handleRequestProvider(provider, proofProvider) as T, proofProvider: proofProvider}; + logger.debug("Creating a provider which is recognized as legacy provider with 'request' method."); + return {provider: handleRequestProvider(provider, proofProvider, logger) as T, proofProvider}; } if (isSendAsyncProvider(provider)) { - return {provider: handleSendAsyncProvider(provider, proofProvider) as T, proofProvider: proofProvider}; + logger.debug("Creating a provider which is recognized as legacy provider with 'sendAsync' method."); + return {provider: handleSendAsyncProvider(provider, proofProvider, logger) as T, proofProvider}; } if (isEIP1193Provider(provider)) { - return {provider: handleEIP1193Provider(provider, proofProvider) as T, proofProvider: proofProvider}; + logger.debug("Creating a provider which is recognized as 'EIP1193' provider."); + return {provider: handleEIP1193Provider(provider, proofProvider, logger) as T, proofProvider}; } return {provider, proofProvider: proofProvider}; } -function handleSendProvider(provider: SendProvider, rootProvider: ProofProvider): SendProvider { +function handleSendProvider(provider: SendProvider, proofProvider: ProofProvider, logger: Logger): SendProvider { const send = provider.send.bind(provider); const handler = (payload: ELRequestPayload): Promise => new Promise((resolve, reject) => { @@ -74,7 +82,7 @@ function handleSendProvider(provider: SendProvider, rootProvider: ProofProvider) }); function newSend(payload: ELRequestPayload, callback: (err?: Error | null, response?: ELResponse) => void): void { - processAndVerifyRequest({payload, handler, proofProvider: rootProvider}) + processAndVerifyRequest({payload, handler, proofProvider, logger}) .then((response) => callback(undefined, response)) .catch((err) => callback(err, undefined)); } @@ -82,7 +90,11 @@ function handleSendProvider(provider: SendProvider, rootProvider: ProofProvider) return Object.assign(provider, {send: newSend}); } -function handleRequestProvider(provider: RequestProvider, rootProvider: ProofProvider): RequestProvider { +function handleRequestProvider( + provider: RequestProvider, + proofProvider: ProofProvider, + logger: Logger +): RequestProvider { const request = provider.request.bind(provider); const handler = (payload: ELRequestPayload): Promise => new Promise((resolve, reject) => { @@ -96,7 +108,7 @@ function handleRequestProvider(provider: RequestProvider, rootProvider: ProofPro }); function newRequest(payload: ELRequestPayload, callback: (err?: Error | null, response?: ELResponse) => void): void { - processAndVerifyRequest({payload, handler, proofProvider: rootProvider}) + processAndVerifyRequest({payload, handler, proofProvider, logger}) .then((response) => callback(undefined, response)) .catch((err) => callback(err, undefined)); } @@ -104,29 +116,37 @@ function handleRequestProvider(provider: RequestProvider, rootProvider: ProofPro return Object.assign(provider, {request: newRequest}); } -function handleSendAsyncProvider(provider: SendAsyncProvider, rootProvider: ProofProvider): SendAsyncProvider { +function handleSendAsyncProvider( + provider: SendAsyncProvider, + proofProvider: ProofProvider, + logger: Logger +): SendAsyncProvider { const sendAsync = provider.sendAsync.bind(provider); const handler = (payload: ELRequestPayload): Promise => sendAsync(payload); async function newSendAsync(payload: ELRequestPayload): Promise { - return processAndVerifyRequest({payload, handler, proofProvider: rootProvider}); + return processAndVerifyRequest({payload, handler, proofProvider, logger}); } return Object.assign(provider, {sendAsync: newSendAsync}); } -function handleEIP1193Provider(provider: EIP1193Provider, rootProvider: ProofProvider): EIP1193Provider { +function handleEIP1193Provider( + provider: EIP1193Provider, + proofProvider: ProofProvider, + logger: Logger +): EIP1193Provider { const request = provider.request.bind(provider); const handler = (payload: ELRequestPayload): Promise => request(payload); async function newRequest(payload: ELRequestPayload): Promise { - return processAndVerifyRequest({payload, handler, proofProvider: rootProvider}); + return processAndVerifyRequest({payload, handler, proofProvider, logger}); } return Object.assign(provider, {request: newRequest}); } -function handleEthersProvider(provider: EthersProvider, rootProvider: ProofProvider): EthersProvider { +function handleEthersProvider(provider: EthersProvider, proofProvider: ProofProvider, logger: Logger): EthersProvider { const send = provider.send.bind(provider); const handler = (payload: ELRequestPayload): Promise => send(payload.method, payload.params); @@ -134,7 +154,8 @@ function handleEthersProvider(provider: EthersProvider, rootProvider: ProofProvi return processAndVerifyRequest({ payload: {jsonrpc: "2.0", id: 0, method, params}, handler, - proofProvider: rootProvider, + proofProvider, + logger, }); } diff --git a/packages/prover/src/web3_proxy.ts b/packages/prover/src/web3_proxy.ts index 0cdd85ea23cc..8e1a8434f886 100644 --- a/packages/prover/src/web3_proxy.ts +++ b/packages/prover/src/web3_proxy.ts @@ -3,37 +3,40 @@ import https from "node:https"; import url from "node:url"; import httpProxy from "http-proxy"; import {NetworkName} from "@lodestar/config/networks"; -import {Logger} from "@lodestar/utils"; -import {LCTransport} from "./interfaces.js"; +import {ConsensusNodeOptions, LogOptions} from "./interfaces.js"; import {ProofProvider} from "./proof_provider/proof_provider.js"; import {ELRequestPayload, ELResponse} from "./types.js"; import {processAndVerifyRequest} from "./utils/execution.js"; -import {logRequest} from "./utils/logger.js"; import {generateRPCResponseForPayload} from "./utils/json_rpc.js"; +import {getLogger} from "./utils/logger.js"; import {fetchRequestPayload, fetchResponseBody} from "./utils/req_resp.js"; export type VerifiedProxyOptions = { network: NetworkName; executionRpcUrl: string; - logger: Logger; wsCheckpoint?: string; -} & ({transport: LCTransport.Rest; urls: string[]} | {transport: LCTransport.P2P; bootnodes: string[]}); + signal?: AbortSignal; +} & LogOptions & + ConsensusNodeOptions; export function createVerifiedExecutionProxy( opts: VerifiedProxyOptions ): {server: http.Server; proofProvider: ProofProvider} { - const {executionRpcUrl: executionUrl, logger, network} = opts; - const controller = new AbortController(); + const {executionRpcUrl, network} = opts; + const signal = opts.signal ?? new AbortController().signal; + const logger = getLogger(opts); const proofProvider = ProofProvider.init({ ...opts, - network: network, - signal: controller.signal, + network, + signal, + logger, }); + logger.info("Creating http proxy", {url: executionRpcUrl}); const proxy = httpProxy.createProxy({ - target: executionUrl, - ws: executionUrl.startsWith("ws"), + target: executionRpcUrl, + ws: executionRpcUrl.startsWith("ws"), agent: https.globalAgent, xfwd: true, ignorePath: true, @@ -51,7 +54,7 @@ export function createVerifiedExecutionProxy( path: "/proxy", port: proxyServerListeningAddress.port, host: proxyServerListeningAddress.host, - signal: controller.signal, + signal, headers: { "Content-Type": "application/json", }, @@ -60,13 +63,16 @@ export function createVerifiedExecutionProxy( fetchResponseBody(res).then(resolve).catch(reject); } ); + logger.debug("Sending request to proxy endpoint", {method: payload.method}); req.write(JSON.stringify(payload)); req.end(); }); } + logger.info("Creating http server"); const proxyServer = http.createServer(function proxyRequestHandler(req, res) { if (req.url === "/proxy") { + logger.verbose("Forwarding request to execution layer"); proxy.web(req, res); return; } @@ -75,14 +81,16 @@ export function createVerifiedExecutionProxy( fetchRequestPayload(req) .then((data) => { payload = data; - logRequest({payload, logger}); - return processAndVerifyRequest({payload, proofProvider, handler}); + logger.debug("Received request", {method: payload.method}); + return processAndVerifyRequest({payload, proofProvider, handler, logger}); }) .then((response) => { + logger.debug("Sending response", {method: payload.method}); res.write(JSON.stringify(response)); res.end(); }) .catch((err) => { + logger.error("Error processing request", {method: payload.method}, err); res.write(JSON.stringify(generateRPCResponseForPayload(payload, undefined, {message: (err as Error).message}))); res.end(); }); @@ -111,10 +119,11 @@ export function createVerifiedExecutionProxy( }); proxyServer.on("upgrade", function proxyRequestUpgrade(req, socket, head) { + logger.debug("Upgrading the ws connection"); proxy.ws(req, socket, head); }); - controller.signal.addEventListener("abort", () => { + signal.addEventListener("abort", () => { proxyServer.close(); }); diff --git a/yarn.lock b/yarn.lock index 508f4194a109..063261b063a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9878,6 +9878,18 @@ logform@^2.2.0: ms "^2.1.1" triple-beam "^1.3.0" +logform@^2.3.2: + version "2.5.1" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.5.1.tgz#44c77c34becd71b3a42a3970c77929e52c6ed48b" + integrity sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg== + dependencies: + "@colors/colors" "1.5.0" + "@types/triple-beam" "^1.3.2" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + long@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" @@ -12550,6 +12562,11 @@ safe-regex2@^2.0.0: dependencies: ret "~0.2.0" +safe-stable-stringify@^2.3.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz#ec7b037768098bf65310d1d64370de0dc02353aa" + integrity sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA== + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -14571,6 +14588,15 @@ winston-transport@^4.3.0, winston-transport@^4.4.0: readable-stream "^2.3.7" triple-beam "^1.2.0" +winston-transport@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" + integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== + dependencies: + logform "^2.3.2" + readable-stream "^3.6.0" + triple-beam "^1.3.0" + winston@^3.3.3: version "3.3.3" resolved "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz" From 74e1b1ce1f3be75cfb1d15eba8085096c191c393 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Mon, 13 Mar 2023 13:25:22 +0100 Subject: [PATCH 2/2] Fix lint errors --- packages/prover/src/utils/logger.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/prover/src/utils/logger.ts b/packages/prover/src/utils/logger.ts index ba4bfc17c005..9035993833b2 100644 --- a/packages/prover/src/utils/logger.ts +++ b/packages/prover/src/utils/logger.ts @@ -35,8 +35,9 @@ class BrowserConsole extends Transport { const mappedMethod = this.methods[method as BrowserLogLevels]; if (val <= this.levels[this.level as BrowserLogLevels]) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call, no-console, @typescript-eslint/ban-ts-comment + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, no-console console[mappedMethod](message); } }