From 72b6e43b0bfbbf4021de56a33b2f989824817464 Mon Sep 17 00:00:00 2001 From: galargh Date: Mon, 10 Feb 2025 17:36:32 +0000 Subject: [PATCH] feat: send EDR panics to sentry --- .../network-manager/edr/edr-provider.ts | 103 +++++++++++------- .../network-manager/http-provider.ts | 12 +- .../network-manager/provider-errors.ts | 8 ++ .../internal/cli/telemetry/sentry/reporter.ts | 12 +- 4 files changed, 92 insertions(+), 43 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/network-manager/edr/edr-provider.ts b/v-next/hardhat/src/internal/builtin-plugins/network-manager/edr/edr-provider.ts index 0d3a452597..b997ccd872 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/network-manager/edr/edr-provider.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/network-manager/edr/edr-provider.ts @@ -7,6 +7,7 @@ import type { } from "../../../../types/config.js"; import type { EthSubscription, + JsonRpcRequest, JsonRpcResponse, RequestArguments, SuccessfulJsonRpcResponse, @@ -31,6 +32,7 @@ import { HardhatError, } from "@ignored/hardhat-vnext-errors"; import { toSeconds } from "@ignored/hardhat-vnext-utils/date"; +import { ensureError } from "@ignored/hardhat-vnext-utils/error"; import { numberToHexString } from "@ignored/hardhat-vnext-utils/hex"; import { deepEqual } from "@ignored/hardhat-vnext-utils/lang"; import debug from "debug"; @@ -39,7 +41,11 @@ import { EDR_NETWORK_REVERT_SNAPSHOT_EVENT } from "../../../constants.js"; import { DEFAULT_HD_ACCOUNTS_CONFIG_PARAMS } from "../accounts/constants.js"; import { BaseProvider } from "../base-provider.js"; import { getJsonRpcRequest, isFailedJsonRpcResponse } from "../json-rpc.js"; -import { InvalidArgumentsError, ProviderError } from "../provider-errors.js"; +import { + InvalidArgumentsError, + ProviderError, + UnknownError, +} from "../provider-errors.js"; import { getGlobalEdrContext } from "./edr-context.js"; import { createSolidityErrorWithStackTrace } from "./stack-traces/stack-trace-solidity-errors.js"; @@ -145,30 +151,41 @@ export class EdrProvider extends BaseProvider { const providerConfig = await getProviderConfig(networkConfig); - const context = await getGlobalEdrContext(); - const provider = await context.createProvider( - hardhatChainTypeToEdrChainType(networkConfig.chainType), - providerConfig, - { - enable: loggerConfig.enabled, - decodeConsoleLogInputsCallback: ConsoleLogger.getDecodedLogs, - printLineCallback: (message: string, replace: boolean) => { - if (replace) { - replaceLastLineFn(message); - } else { - printLineFn(message); - } + let edrProvider: EdrProvider; + + // We need to catch errors here, as the provider creation can panic unexpectedly, + // and we want to make sure such a crash is propagated as a ProviderError. + try { + const context = await getGlobalEdrContext(); + const provider = await context.createProvider( + hardhatChainTypeToEdrChainType(networkConfig.chainType), + providerConfig, + { + enable: loggerConfig.enabled, + decodeConsoleLogInputsCallback: ConsoleLogger.getDecodedLogs, + printLineCallback: (message: string, replace: boolean) => { + if (replace) { + replaceLastLineFn(message); + } else { + printLineFn(message); + } + }, }, - }, - { - subscriptionCallback: (event: SubscriptionEvent) => { - edrProvider.onSubscriptionEvent(event); + { + subscriptionCallback: (event: SubscriptionEvent) => { + edrProvider.onSubscriptionEvent(event); + }, }, - }, - tracingConfig, - ); + tracingConfig, + ); - const edrProvider = new EdrProvider(provider, jsonRpcRequestWrapper); + edrProvider = new EdrProvider(provider, jsonRpcRequestWrapper); + } catch (error) { + ensureError(error); + + // eslint-disable-next-line no-restricted-syntax -- allow throwing UnknownError + throw new UnknownError(error.message, error); + } return edrProvider; } @@ -210,24 +227,10 @@ export class EdrProvider extends BaseProvider { if (this.#jsonRpcRequestWrapper !== undefined) { jsonRpcResponse = await this.#jsonRpcRequestWrapper( jsonRpcRequest, - async (request) => { - assertHardhatInvariant( - this.#provider !== undefined, - "The provider is not defined", - ); - - const stringifiedArgs = JSON.stringify(request); - const edrResponse = - await this.#provider.handleRequest(stringifiedArgs); - - return this.#handleEdrResponse(edrResponse); - }, + this.#handleRequest.bind(this), ); } else { - const stringifiedArgs = JSON.stringify(jsonRpcRequest); - const edrResponse = await this.#provider.handleRequest(stringifiedArgs); - - jsonRpcResponse = await this.#handleEdrResponse(edrResponse); + jsonRpcResponse = await this.#handleRequest(jsonRpcRequest); } // this can only happen if a wrapper doesn't call the default @@ -352,6 +355,30 @@ export class EdrProvider extends BaseProvider { }; this.emit("message", message); } + + async #handleRequest(request: JsonRpcRequest): Promise { + assertHardhatInvariant( + this.#provider !== undefined, + "The provider is not defined", + ); + + const stringifiedArgs = JSON.stringify(request); + + let edrResponse: Response; + + // We need to catch errors here, as the provider creation can panic unexpectedly, + // and we want to make sure such a crash is propagated as a ProviderError. + try { + edrResponse = await this.#provider.handleRequest(stringifiedArgs); + } catch (error) { + ensureError(error); + + // eslint-disable-next-line no-restricted-syntax -- allow throwing UnknownError + throw new UnknownError(error.message, error); + } + + return this.#handleEdrResponse(edrResponse); + } } async function getProviderConfig( diff --git a/v-next/hardhat/src/internal/builtin-plugins/network-manager/http-provider.ts b/v-next/hardhat/src/internal/builtin-plugins/network-manager/http-provider.ts index 005ba9f3a5..870c4be749 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/network-manager/http-provider.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/network-manager/http-provider.ts @@ -11,6 +11,7 @@ import type { } from "@ignored/hardhat-vnext-utils/request"; import { HardhatError } from "@ignored/hardhat-vnext-errors"; +import { ensureError } from "@ignored/hardhat-vnext-utils/error"; import { sleep, isObject } from "@ignored/hardhat-vnext-utils/lang"; import { getDispatcher, @@ -31,7 +32,11 @@ import { isFailedJsonRpcResponse, parseJsonRpcResponse, } from "./json-rpc.js"; -import { ProviderError, LimitExceededError } from "./provider-errors.js"; +import { + ProviderError, + LimitExceededError, + UnknownError, +} from "./provider-errors.js"; const TOO_MANY_REQUEST_STATUS = 429; const MAX_RETRIES = 6; @@ -181,6 +186,8 @@ export class HttpProvider extends BaseProvider { this.#dispatcher, ); } catch (e) { + ensureError(e); + if (e instanceof ConnectionRefusedError) { throw new HardhatError( HardhatError.ERRORS.NETWORK.CONNECTION_REFUSED, @@ -218,7 +225,8 @@ export class HttpProvider extends BaseProvider { throw new LimitExceededError(undefined, e); } - throw e; + // eslint-disable-next-line no-restricted-syntax -- allow throwing ProviderError + throw new UnknownError(e.message, e); } return parseJsonRpcResponse(await response.body.text()); diff --git a/v-next/hardhat/src/internal/builtin-plugins/network-manager/provider-errors.ts b/v-next/hardhat/src/internal/builtin-plugins/network-manager/provider-errors.ts index b9885e503a..2f23cb950e 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/network-manager/provider-errors.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/network-manager/provider-errors.ts @@ -127,3 +127,11 @@ export class InvalidResponseError extends ProviderError { super(message, InvalidResponseError.CODE, parent); } } + +export class UnknownError extends ProviderError { + public static readonly CODE = -1; + + constructor(message: string = "Unknown error", parent?: Error) { + super(message, UnknownError.CODE, parent); + } +} diff --git a/v-next/hardhat/src/internal/cli/telemetry/sentry/reporter.ts b/v-next/hardhat/src/internal/cli/telemetry/sentry/reporter.ts index 8a1d599cb2..f4874ecff9 100644 --- a/v-next/hardhat/src/internal/cli/telemetry/sentry/reporter.ts +++ b/v-next/hardhat/src/internal/cli/telemetry/sentry/reporter.ts @@ -5,7 +5,10 @@ import { import { flush } from "@sentry/node"; import debug from "debug"; -import { ProviderError } from "../../../builtin-plugins/network-manager/provider-errors.js"; +import { + ProviderError, + UnknownError, +} from "../../../builtin-plugins/network-manager/provider-errors.js"; import { getHardhatVersion } from "../../../utils/package.js"; import { isTelemetryAllowed } from "../telemetry-permissions.js"; @@ -128,8 +131,11 @@ class Reporter { return false; } - if (ProviderError.isProviderError(error)) { - // We don't report network related errors + if ( + ProviderError.isProviderError(error) && + error.code !== UnknownError.CODE + ) { + // We don't report known network related errors return false; }