Skip to content

Commit

Permalink
Merge pull request #6250 from NomicFoundation/sentry-edr-errors
Browse files Browse the repository at this point in the history
feat: send EDR panics to sentry
  • Loading branch information
galargh authored Feb 10, 2025
2 parents 8a9ff54 + 72b6e43 commit 71d666f
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
} from "../../../../types/config.js";
import type {
EthSubscription,
JsonRpcRequest,
JsonRpcResponse,
RequestArguments,
SuccessfulJsonRpcResponse,
Expand All @@ -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";
Expand All @@ -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";
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -352,6 +355,30 @@ export class EdrProvider extends BaseProvider {
};
this.emit("message", message);
}

async #handleRequest(request: JsonRpcRequest): Promise<JsonRpcResponse> {
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
12 changes: 9 additions & 3 deletions v-next/hardhat/src/internal/cli/telemetry/sentry/reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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;
}

Expand Down

0 comments on commit 71d666f

Please sign in to comment.