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

Split node task in subtasks #874

Merged
merged 27 commits into from
Oct 15, 2020
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
457de15
Add subtasks for creating the provider and the server
fvictorio Oct 13, 2020
6056633
Avoij creating a new provider
fvictorio Oct 13, 2020
5deaccd
Use network.provider when it corresponds to hardhat network
fvictorio Oct 13, 2020
a7372eb
Add RPC for enabling logging
fvictorio Oct 13, 2020
e46b8a3
Add subtask hooks in the node task
fvictorio Oct 13, 2020
2c72f91
Merge branch '2.0' into split-node-task
fvictorio Oct 13, 2020
c4f6d59
Add fork params to node task
fvictorio Oct 13, 2020
525f67f
Create provider when network is not hardhat
fvictorio Oct 14, 2020
a93fcf8
Add params to TASK_NODE_GET_PROVIDER
fvictorio Oct 14, 2020
2410d2e
Enable logging after reset
fvictorio Oct 14, 2020
e614962
Simplify condition for throwing JSONRPC_UNSUPPORTED_NETWORK
fvictorio Oct 14, 2020
375a31a
Add interface for JsonRpcServer
fvictorio Oct 14, 2020
9b20f94
Simplify logic when creating the provider
fvictorio Oct 14, 2020
e747103
Make fork params optional in subtask
fvictorio Oct 14, 2020
3a9ece5
Fix wrong network name
fvictorio Oct 14, 2020
dc2d57c
Use 0.0.0.0 as default hostname when inside docker
fvictorio Oct 14, 2020
d6417d1
Merge branch '2.0' into split-node-task
fvictorio Oct 14, 2020
cdb92ea
Fix linter and error-list test
fvictorio Oct 14, 2020
d38b9bb
Add hardhat_setLoggingEnabled to private rpc methods
fvictorio Oct 15, 2020
69b541e
Update packages/hardhat-core/src/builtin-tasks/node.ts
fvictorio Oct 15, 2020
5669d83
Move logs to the TASK_NODE_SERVER_READY subtask
fvictorio Oct 15, 2020
2306026
Remove getProvider from JsonRpcServer interface
fvictorio Oct 15, 2020
18026e3
Add hostname and port params to node subtask
fvictorio Oct 15, 2020
10381fa
Remove unnecessary import
fvictorio Oct 15, 2020
05e32f4
Fix typecheck problem
fvictorio Oct 15, 2020
ab99200
Merge branch '2.0' into split-node-task
fvictorio Oct 15, 2020
ff66873
Fix error code numbers
fvictorio Oct 15, 2020
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
219 changes: 180 additions & 39 deletions packages/hardhat-core/src/builtin-tasks/node.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,36 @@
import chalk from "chalk";
import fsExtra from "fs-extra";
import debug from "debug";
import { BN, bufferToHex, privateToAddress, toBuffer } from "ethereumjs-util";

import { HARDHAT_NETWORK_NAME } from "../internal/constants";
import { task, types } from "../internal/core/config/config-env";
import { subtask, task, types } from "../internal/core/config/config-env";
import { HardhatError } from "../internal/core/errors";
import { ERRORS } from "../internal/core/errors-list";
import { createProvider } from "../internal/core/providers/construction";
import {
JsonRpcServer,
JsonRpcServer as JsonRpcServerImpl,
JsonRpcServerConfig,
} from "../internal/hardhat-network/jsonrpc/server";
import { Reporter } from "../internal/sentry/reporter";
import { lazyObject } from "../internal/util/lazy";
import {
Artifacts,
EthereumProvider,
HardhatConfig,
HardhatNetworkConfig,
JsonRpcServer,
} from "../types";

import { TASK_NODE } from "./task-names";
import {
TASK_NODE,
TASK_NODE_CREATE_SERVER,
TASK_NODE_GET_PROVIDER,
TASK_NODE_SERVER_CREATED,
TASK_NODE_SERVER_READY,
} from "./task-names";
import { watchCompilerOutput } from "./utils/watch";

const log = debug("hardhat:core:tasks:node");

function _createHardhatNetworkProvider(
config: HardhatConfig,
artifacts: Artifacts
): EthereumProvider {
log("Creating HardhatNetworkProvider");

const networkName = HARDHAT_NETWORK_NAME;
const networkConfig = config.networks[HARDHAT_NETWORK_NAME];

return lazyObject(() => {
log(`Creating hardhat provider for JSON-RPC sever`);
return createProvider(
networkName,
{ loggingEnabled: true, ...networkConfig },
config.paths,
artifacts
);
});
}

function logHardhatNetworkAccounts(networkConfig: HardhatNetworkConfig) {
if (networkConfig.accounts === undefined) {
return;
Expand All @@ -66,11 +52,127 @@ Private Key: ${privateKey}
}
}

subtask(TASK_NODE_GET_PROVIDER)
.addOptionalParam("forkUrl", undefined, undefined, types.string)
.addOptionalParam("forkBlockNumber", undefined, undefined, types.int)
.setAction(
async (
{
forkBlockNumber: forkBlockNumberParam,
forkUrl: forkUrlParam,
}: {
forkBlockNumber?: number;
forkUrl?: string;
},
{ artifacts, config, network }
): Promise<EthereumProvider> => {
let provider = network.provider;

if (network.name !== HARDHAT_NETWORK_NAME) {
const networkConfig = config.networks[HARDHAT_NETWORK_NAME];

log(`Creating hardhat provider for JSON-RPC server`);
provider = createProvider(
HARDHAT_NETWORK_NAME,
networkConfig,
config.paths,
artifacts
);
}

const hardhatNetworkConfig = config.networks[HARDHAT_NETWORK_NAME];

const forkUrl = forkUrlParam ?? hardhatNetworkConfig.forking?.url;
const forkBlockNumber =
forkBlockNumberParam ?? hardhatNetworkConfig.forking?.blockNumber;

// we use the hardhat_reset RPC method to enable the fork
if (forkUrl !== undefined) {
await provider.request({
method: "hardhat_reset",
params: [
{
forking: {
jsonRpcUrl: forkUrl,
blockNumber: forkBlockNumber,
},
},
],
});
} else if (forkBlockNumber !== undefined) {
// we throw an error if the user specified a forkBlockNumber but not a
// forkUrl
throw new HardhatError(
ERRORS.BUILTIN_TASKS.NODE_FORK_BLOCK_NUMBER_WITHOUT_URL
);
}

// enable logging
await provider.request({
method: "hardhat_setLoggingEnabled",
params: [true],
});

return provider;
}
);

subtask(TASK_NODE_CREATE_SERVER)
.addParam("hostname", undefined, undefined, types.string)
.addParam("port", undefined, undefined, types.int)
.addParam("provider", undefined, undefined, types.any)
.setAction(
async ({
hostname,
port,
provider,
}: {
hostname: string;
port: number;
provider: EthereumProvider;
fvictorio marked this conversation as resolved.
Show resolved Hide resolved
}): Promise<JsonRpcServer> => {
alcuadrado marked this conversation as resolved.
Show resolved Hide resolved
const serverConfig: JsonRpcServerConfig = {
hostname,
port,
provider,
};

const server = new JsonRpcServerImpl(serverConfig);

return server;
}
);

/**
* This task will be called when the server was successfully created, but it's
* not ready for receiving requests yet.
*/
subtask(TASK_NODE_SERVER_CREATED)
alcuadrado marked this conversation as resolved.
Show resolved Hide resolved
.addParam("provider", undefined, undefined, types.any)
.addParam("server", undefined, undefined, types.any)
.setAction(
async ({}: { provider: EthereumProvider; server: JsonRpcServer }) => {
// this task is meant to be overriden by plugin writers
}
);

/**
* This subtask will be run when the server is ready to accept requests
*/
subtask(TASK_NODE_SERVER_READY)
.addParam("provider", undefined, undefined, types.any)
.addParam("server", undefined, undefined, types.any)
.setAction(
async ({}: { provider: EthereumProvider; server: JsonRpcServer }) => {
// this task is meant to be overriden by plugin writers
}
);

task(TASK_NODE, "Starts a JSON-RPC server on top of Hardhat Network")
.addOptionalParam(
"hostname",
"The host to which to bind to for new connections",
fvictorio marked this conversation as resolved.
Show resolved Hide resolved
"localhost",
undefined,
types.string
)
.addOptionalParam(
Expand All @@ -79,33 +181,70 @@ task(TASK_NODE, "Starts a JSON-RPC server on top of Hardhat Network")
8545,
types.int
)
.addOptionalParam(
"forkUrl",
"The URL of the JSON-RPC server to fork from",
undefined,
types.string
)
.addOptionalParam(
"forkBlockNumber",
"The block number to fork from",
undefined,
types.int
)
.setAction(
async (
{ hostname, port },
{ artifacts, network, hardhatArguments, config }
{
forkBlockNumber,
forkUrl,
hostname: hostnameParam,
port,
}: {
forkBlockNumber?: number;
forkUrl?: string;
hostname?: string;
port: number;
},
{ config, hardhatArguments, network, run }
) => {
// we throw if the user specified a network argument and it's not hardhat
if (
network.name !== HARDHAT_NETWORK_NAME &&
alcuadrado marked this conversation as resolved.
Show resolved Hide resolved
// We normally set the default network as hardhatArguments.network,
// so this check isn't enough, and we add the next one. This has the
// effect of `--network <defaultNetwork>` being a false negative, but
// not a big deal.
hardhatArguments.network !== undefined &&
hardhatArguments.network !== config.defaultNetwork
hardhatArguments.network !== undefined
) {
throw new HardhatError(
ERRORS.BUILTIN_TASKS.JSONRPC_UNSUPPORTED_NETWORK
);
}

try {
const serverConfig: JsonRpcServerConfig = {
const provider: EthereumProvider = await run(TASK_NODE_GET_PROVIDER, {
forkBlockNumber,
forkUrl,
});

// the default hostname is "localhost" unless we are inside a docker
// container, in that case we use "0.0.0.0"
let hostname: string;
if (hostnameParam !== undefined) {
hostname = hostnameParam;
} else {
const insideDocker = fsExtra.existsSync("/.dockerenv");
if (insideDocker) {
hostname = "0.0.0.0";
} else {
hostname = "localhost";
}
}

const server: JsonRpcServer = await run(TASK_NODE_CREATE_SERVER, {
hostname,
port,
provider: _createHardhatNetworkProvider(config, artifacts),
};
provider,
});

const server = new JsonRpcServer(serverConfig);
await run(TASK_NODE_SERVER_CREATED, { provider, server });
Copy link
Member

Choose a reason for hiding this comment

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

This plugin should receive the hostname and port


const { port: actualPort, address } = await server.listen();

Expand All @@ -118,7 +257,7 @@ task(TASK_NODE, "Starts a JSON-RPC server on top of Hardhat Network")
console.log();
Copy link
Member

Choose a reason for hiding this comment

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

I think these console.log calls should be placed in TASK_NODE_SERVER_READY


try {
await watchCompilerOutput(server.getProvider(), config.paths);
await watchCompilerOutput(provider, config.paths);
} catch (error) {
console.warn(
chalk.yellow(
Expand All @@ -137,6 +276,8 @@ task(TASK_NODE, "Starts a JSON-RPC server on top of Hardhat Network")
const networkConfig = config.networks[HARDHAT_NETWORK_NAME];
logHardhatNetworkAccounts(networkConfig);
Copy link
Member

Choose a reason for hiding this comment

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

Also, this should be in TASK_NODE_SERVER_READY


await run(TASK_NODE_SERVER_READY, { provider, server });
Copy link
Member

Choose a reason for hiding this comment

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

This task should receive the hostname and actual port.


await server.waitUntilClosed();
} catch (error) {
if (HardhatError.isHardhatError(error)) {
Expand Down
4 changes: 4 additions & 0 deletions packages/hardhat-core/src/builtin-tasks/task-names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ export const TASK_HELP = "help";
export const TASK_RUN = "run";

export const TASK_NODE = "node";
export const TASK_NODE_GET_PROVIDER = "node:get-provider";
export const TASK_NODE_CREATE_SERVER = "node:create-server";
export const TASK_NODE_SERVER_CREATED = "node:server-created";
export const TASK_NODE_SERVER_READY = "node:server-ready";

export const TASK_TEST = "test";

Expand Down
8 changes: 8 additions & 0 deletions packages/hardhat-core/src/internal/core/errors-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,14 @@ To start the JSON-RPC server, retry the command without the --network parameter.
description: `The project cannot be compiled with the current settings.`,
shouldBeReported: false,
},
NODE_FORK_BLOCK_NUMBER_WITHOUT_URL: {
number: 608,
message: `You specified a fork block number but not an URL.`,
title: "Missing fork URL",
description: `You passed a block number to fork from, but not URL. Hardhat cannot fork
if the URL of the JSON-RPC weren't set.`,
shouldBeReported: false,
},
},
ARTIFACTS: {
NOT_FOUND: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import http, { Server } from "http";
import { AddressInfo } from "net";
import { Server as WSServer } from "ws";

import { EIP1193Provider } from "../../../types";
import {
EIP1193Provider,
JsonRpcServer as IJsonRpcServer,
} from "../../../types";
import { HttpProvider } from "../../core/providers/http";

import JsonRpcHandler from "./handler";
Expand All @@ -17,7 +20,7 @@ export interface JsonRpcServerConfig {
provider: EIP1193Provider;
}

export class JsonRpcServer {
export class JsonRpcServer implements IJsonRpcServer {
private _config: JsonRpcServerConfig;
private _httpServer: Server;
private _wsServer: WSServer;
Expand Down Expand Up @@ -62,11 +65,11 @@ export class JsonRpcServer {
this._wsServer.once("close", resolve);
});

return Promise.all([httpServerClosed, wsServerClosed]);
await Promise.all([httpServerClosed, wsServerClosed]);
};

public close = async () => {
return Promise.all([
await Promise.all([
new Promise((resolve, reject) => {
log("Closing JSON-RPC server");
this._httpServer.close((err) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,11 @@ export function validateParams(
forkConfig: typeof optionalRpcHardhatNetworkConfig
): [RpcHardhatNetworkConfig | undefined];

export function validateParams(
params: any[],
loggingEnabled: typeof t.boolean
): [boolean];

// tslint:disable only-hardhat-error

export function validateParams(params: any[], ...types: Array<t.Type<any>>) {
Expand Down
Loading