Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: send EDR panics to sentry #6250

Merged
merged 1 commit into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may report every HTTP-related error. We can start with reporting them, but we may need to filter most out later. LGTM though

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking was to err on the side of reporting too much rather than too little.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed

}

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
Loading