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

Network manager cleanup Pt1 #6003

Merged
merged 8 commits into from
Dec 6, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type {
CompilerInput,
CompilerOutput,
} from "../../../../types/solidity/compiler-io.js";
import type { JsonRpcRequestWrapperFunction } from "../network-manager.js";
import type {
RawTrace,
SubscriptionEvent,
Expand Down Expand Up @@ -92,10 +93,12 @@ export async function getGlobalEdrContext(): Promise<EdrContext> {
return _globalEdrContext;
}

export type JsonRpcRequestWrapperFunction = (
request: JsonRpcRequest,
defaultBehavior: (r: JsonRpcRequest) => Promise<JsonRpcResponse>,
) => Promise<JsonRpcResponse>;
interface EdrProviderConfig {
networkConfig: EdrNetworkConfig;
loggerConfig?: LoggerConfig;
tracingConfig?: TracingConfig;
jsonRpcRequestWrapper?: JsonRpcRequestWrapperFunction;
}

export class EdrProvider extends EventEmitter implements EthereumProvider {
readonly #provider: Provider;
Expand All @@ -107,23 +110,25 @@ export class EdrProvider extends EventEmitter implements EthereumProvider {
#vmTracer?: VMTracerT;
#nextRequestId = 1;

// TODO: should take an object with all the config like the HTTP provider
public static async create(
config: EdrNetworkConfig,
loggerConfig: LoggerConfig,
tracingConfig?: TracingConfig,
jsonRpcRequestWrapper?: JsonRpcRequestWrapperFunction,
): Promise<EdrProvider> {
const coinbase = config.coinbase ?? DEFAULT_COINBASE;
/**
* Creates a new instance of `EdrProvider`.
*/
public static async create({
networkConfig,
loggerConfig = { enabled: false },
tracingConfig = {},
jsonRpcRequestWrapper,
}: EdrProviderConfig): Promise<EdrProvider> {
const coinbase = networkConfig.coinbase ?? DEFAULT_COINBASE;

let fork;
if (config.forkConfig !== undefined) {
if (networkConfig.forkConfig !== undefined) {
let httpHeaders: HttpHeader[] | undefined;
if (config.forkConfig.httpHeaders !== undefined) {
if (networkConfig.forkConfig.httpHeaders !== undefined) {
httpHeaders = [];

for (const [name, value] of Object.entries(
config.forkConfig.httpHeaders,
networkConfig.forkConfig.httpHeaders,
)) {
httpHeaders.push({
name,
Expand All @@ -133,66 +138,70 @@ export class EdrProvider extends EventEmitter implements EthereumProvider {
}

fork = {
jsonRpcUrl: config.forkConfig.jsonRpcUrl,
jsonRpcUrl: networkConfig.forkConfig.jsonRpcUrl,
blockNumber:
config.forkConfig.blockNumber !== undefined
? BigInt(config.forkConfig.blockNumber)
networkConfig.forkConfig.blockNumber !== undefined
? BigInt(networkConfig.forkConfig.blockNumber)
: undefined,
httpHeaders,
};
}

const initialDate =
config.initialDate !== undefined
? BigInt(Math.floor(config.initialDate.getTime() / 1000))
networkConfig.initialDate !== undefined
? BigInt(Math.floor(networkConfig.initialDate.getTime() / 1000))
: undefined;

const printLineFn = loggerConfig.printLineFn ?? printLine;
const replaceLastLineFn = loggerConfig.replaceLastLineFn ?? replaceLastLine;

const vmTraceDecoder = await createVmTraceDecoder();

const hardforkName = getHardforkName(config.hardfork);
const hardforkName = getHardforkName(networkConfig.hardfork);

const context = await getGlobalEdrContext();
const provider = await context.createProvider(
config.chainType === "optimism"
networkConfig.chainType === "optimism"
? OPTIMISM_CHAIN_TYPE
: GENERIC_CHAIN_TYPE, // TODO: l1 is missing here
{
allowBlocksWithSameTimestamp:
config.allowBlocksWithSameTimestamp ?? false,
allowUnlimitedContractSize: config.allowUnlimitedContractSize,
bailOnCallFailure: config.throwOnCallFailures,
bailOnTransactionFailure: config.throwOnTransactionFailures,
blockGasLimit: BigInt(config.blockGasLimit),
chainId: BigInt(config.chainId),
chains: this.#convertToEdrChains(config.chains),
cacheDir: config.forkCachePath,
networkConfig.allowBlocksWithSameTimestamp ?? false,
allowUnlimitedContractSize: networkConfig.allowUnlimitedContractSize,
bailOnCallFailure: networkConfig.throwOnCallFailures,
bailOnTransactionFailure: networkConfig.throwOnTransactionFailures,
blockGasLimit: BigInt(networkConfig.blockGasLimit),
chainId: BigInt(networkConfig.chainId),
chains: this.#convertToEdrChains(networkConfig.chains),
cacheDir: networkConfig.forkCachePath,
coinbase: Buffer.from(coinbase.slice(2), "hex"),
enableRip7212: config.enableRip7212,
enableRip7212: networkConfig.enableRip7212,
fork,
hardfork: ethereumsjsHardforkToEdrSpecId(hardforkName),
genesisAccounts: config.genesisAccounts.map((account) => {
genesisAccounts: networkConfig.genesisAccounts.map((account) => {
return {
secretKey: account.privateKey,
balance: BigInt(account.balance),
};
}),
initialDate,
initialBaseFeePerGas:
config.initialBaseFeePerGas !== undefined
? BigInt(config.initialBaseFeePerGas)
networkConfig.initialBaseFeePerGas !== undefined
? BigInt(networkConfig.initialBaseFeePerGas)
: undefined,
minGasPrice: config.minGasPrice,
minGasPrice: networkConfig.minGasPrice,
mining: {
autoMine: config.automine,
interval: ethereumjsIntervalMiningConfigToEdr(config.intervalMining),
autoMine: networkConfig.automine,
interval: ethereumjsIntervalMiningConfigToEdr(
networkConfig.intervalMining,
),
memPool: {
order: ethereumjsMempoolOrderToEdrMineOrdering(config.mempoolOrder),
order: ethereumjsMempoolOrderToEdrMineOrdering(
networkConfig.mempoolOrder,
),
},
},
networkId: BigInt(config.networkId),
networkId: BigInt(networkConfig.networkId),
},
{
enable: loggerConfig.enabled,
Expand Down Expand Up @@ -231,6 +240,13 @@ export class EdrProvider extends EventEmitter implements EthereumProvider {
return edrProvider;
}

/**
* @private
*
* This constructor is intended for internal use only.
* Use the static method {@link EdrProvider.create} to create an instance of
* `EdrProvider`.
*/
private constructor(
provider: Provider,
vmTraceDecoder: VmTraceDecoder,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { JsonRpcRequestWrapperFunction } from "./network-manager.js";
import type {
EthereumProvider,
JsonRpcRequest,
Expand Down Expand Up @@ -39,10 +40,14 @@ const TOO_MANY_REQUEST_STATUS = 429;
const MAX_RETRIES = 6;
const MAX_RETRY_WAIT_TIME_SECONDS = 5;

export type JsonRpcRequestWrapperFunction = (
request: JsonRpcRequest,
defaultBehavior: (r: JsonRpcRequest) => Promise<JsonRpcResponse>,
) => Promise<JsonRpcResponse>;
interface HttpProviderConfig {
url: string;
networkName: string;
extraHeaders?: Record<string, string>;
timeout: number;
jsonRpcRequestWrapper?: JsonRpcRequestWrapperFunction;
testDispatcher?: Dispatcher;
}

export class HttpProvider extends EventEmitter implements EthereumProvider {
readonly #url: string;
Expand All @@ -62,20 +67,16 @@ export class HttpProvider extends EventEmitter implements EthereumProvider {
extraHeaders = {},
timeout,
jsonRpcRequestWrapper,
}: {
url: string;
networkName: string;
extraHeaders?: Record<string, string>;
timeout: number;
jsonRpcRequestWrapper?: JsonRpcRequestWrapperFunction;
}): Promise<HttpProvider> {
testDispatcher,
}: HttpProviderConfig): Promise<HttpProvider> {
if (!isValidUrl(url)) {
throw new HardhatError(HardhatError.ERRORS.NETWORK.INVALID_URL, {
value: url,
});
}

const dispatcher = await getHttpDispatcher(url, timeout);
const dispatcher =
testDispatcher ?? (await getHttpDispatcher(url, timeout));

const httpProvider = new HttpProvider(
url,
Expand All @@ -95,8 +96,7 @@ export class HttpProvider extends EventEmitter implements EthereumProvider {
* Use the static method {@link HttpProvider.create} to create an instance of
* `HttpProvider`.
*/
// TODO: make the constructor private, but we need to fix the tests first
constructor(
private constructor(
url: string,
networkName: string,
extraHeaders: Record<string, string>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,24 @@ import type {
DefaultChainType,
NetworkConnection,
} from "../../../types/network.js";
import type { EthereumProvider } from "../../../types/providers.js";
import type {
EthereumProvider,
JsonRpcRequest,
JsonRpcResponse,
} from "../../../types/providers.js";

import {
assertHardhatInvariant,
HardhatError,
} from "@ignored/hardhat-vnext-errors";
import { HardhatError } from "@ignored/hardhat-vnext-errors";

import { EdrProvider } from "./edr/edr-provider.js";
import { HttpProvider } from "./http-provider.js";
import { NetworkConnectionImplementation } from "./network-connection.js";
import { isNetworkConfig, validateNetworkConfig } from "./type-validation.js";

export type JsonRpcRequestWrapperFunction = (
request: JsonRpcRequest,
defaultBehavior: (r: JsonRpcRequest) => Promise<JsonRpcResponse>,
) => Promise<JsonRpcResponse>;

export class NetworkManagerImplementation {
readonly #defaultNetwork: string;
readonly #defaultChainType: DefaultChainType;
Expand Down Expand Up @@ -136,11 +142,16 @@ export class NetworkManagerImplementation {
const createProvider = async (
networkConnection: NetworkConnectionImplementation<ChainTypeT>,
): Promise<EthereumProvider> => {
assertHardhatInvariant(
resolvedNetworkConfig.type === "edr" ||
resolvedNetworkConfig.type === "http",
`Invalid network type ${resolvedNetworkConfig.type}`,
);
const jsonRpcRequestWrapper: JsonRpcRequestWrapperFunction = (
request,
defaultBehavior,
) =>
hookManager.runHandlerChain(
"network",
"onRequest",
[networkConnection, request],
async (_context, _connection, req) => defaultBehavior(req),
);

if (resolvedNetworkConfig.type === "edr") {
if (
Expand All @@ -154,40 +165,25 @@ export class NetworkManagerImplementation {
);
}

return EdrProvider.create(
return EdrProvider.create({
// The resolvedNetworkConfig can have its chainType set to `undefined`
// so we default to the default chain type here.
{
networkConfig: {
...resolvedNetworkConfig,
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions --
This case is safe because we have a check above */
chainType: resolvedChainType as ChainType,
},
{ enabled: false },
{},
(request, defaultBehavior) => {
return hookManager.runHandlerChain(
"network",
"onRequest",
[networkConnection, request],
async (_context, _connection, req) => defaultBehavior(req),
);
},
);
jsonRpcRequestWrapper,
});
}

return HttpProvider.create({
url: resolvedNetworkConfig.url,
networkName: resolvedNetworkName,
extraHeaders: resolvedNetworkConfig.httpHeaders,
timeout: resolvedNetworkConfig.timeout,
jsonRpcRequestWrapper: (request, defaultBehavior) =>
hookManager.runHandlerChain(
"network",
"onRequest",
[networkConnection, request],
async (_context, _connection, req) => defaultBehavior(req),
),
jsonRpcRequestWrapper,
});
};

Expand Down
Loading