Skip to content

Commit

Permalink
Merge pull request #3382 from NomicFoundation/hardhat-metadata
Browse files Browse the repository at this point in the history
Add hardhat_metadata RPC method
  • Loading branch information
fvictorio committed Nov 29, 2022
2 parents e683625 + 34f6c34 commit 3adb6f6
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/real-donuts-hope.md
@@ -0,0 +1,5 @@
---
"hardhat": patch
---

Added a new `hardhat_metadata` RPC method
@@ -0,0 +1,32 @@
export interface HardhatMetadata {
// A string identifying the version of Hardhat, for debugging purposes,
// not meant to be displayed to users.
clientVersion: string;

// The chain's id. Used to sign transactions.
chainId: number;

// A 0x-prefixed hex-encoded 32 bytes id which uniquiely identifies an instance/run
// of Hardhat Network. Running Hardhat Network more than once (even with the same version
// and parameters) would always result in different `instanceId`s.
// Running `hardhat_reset` would change the `instanceId` of an existing Hardhat Network.
instanceId: string;

// The latest block's number in Hardhat Network
latestBlockNumber: number;

// The latest block's hash in Hardhat Network
latestBlockHash: string;

// This field is only present when Hardhat Network is forking another chain.
forkedNetwork?: {
// The chainId of the network that is being forked
chainId: number;

// The number of the block that the network forked from.
forkBlockNumber: number;

// The hash of the block that the network forked from.
forkBlockHash: string;
};
}
Expand Up @@ -22,6 +22,7 @@ import {
rpcCompilerOutput,
} from "../../../core/jsonrpc/types/input/solc";
import { validateParams } from "../../../core/jsonrpc/types/input/validation";
import { HardhatMetadata } from "../../../core/jsonrpc/types/output/metadata";
import {
InvalidInputError,
MethodNotFoundError,
Expand Down Expand Up @@ -93,6 +94,9 @@ export class HardhatModule {
...this._dropTransactionParams(params)
);

case "hardhat_metadata":
return this._metadataAction(...this._metadataParams(params));

case "hardhat_setBalance":
return this._setBalanceAction(...this._setBalanceParams(params));

Expand Down Expand Up @@ -272,6 +276,16 @@ export class HardhatModule {
return this._node.dropTransaction(hash);
}

// hardhat_metadata

private _metadataParams(params: any[]): [] {
return validateParams(params);
}

private async _metadataAction(): Promise<HardhatMetadata> {
return this._node.getMetadata();
}

// hardhat_setBalance

private _setBalanceParams(params: any[]): [Buffer, bigint] {
Expand Down
Expand Up @@ -5,11 +5,13 @@ import {
import { validateParams } from "../../../core/jsonrpc/types/input/validation";
import { MethodNotFoundError } from "../../../core/providers/errors";
import { keccak256 } from "../../../util/keccak";
import { getPackageJson } from "../../../util/packageInfo";
import { HardhatNode } from "../node";

/* eslint-disable @nomiclabs/hardhat-internal-rules/only-hardhat-error */

export class Web3Module {
constructor(private readonly _node: HardhatNode) {}

public async processRequest(
method: string,
params: any[] = []
Expand All @@ -32,9 +34,7 @@ export class Web3Module {
}

private async _clientVersionAction(): Promise<string> {
const hardhatPackage = await getPackageJson();
const ethereumjsVMPackage = require("@nomicfoundation/ethereumjs-vm/package.json");
return `HardhatNetwork/${hardhatPackage.version}/@nomicfoundation/ethereumjs-vm/${ethereumjsVMPackage.version}`;
return this._node.getClientVersion();
}

// web3_sha3
Expand Down
Expand Up @@ -16,6 +16,7 @@ import {
privateToAddress,
setLengthLeft,
toBuffer,
bufferToBigInt,
} from "@nomicfoundation/ethereumjs-util";
import {
Bloom,
Expand All @@ -32,6 +33,7 @@ import {
} from "@nomicfoundation/ethereumjs-statemanager";
import { SignTypedDataVersion, signTypedData } from "@metamask/eth-sig-util";
import chalk from "chalk";
import { randomBytes } from "crypto";
import debug from "debug";
import EventEmitter from "events";

Expand All @@ -51,13 +53,15 @@ import {
InvalidInputError,
TransactionExecutionError,
} from "../../core/providers/errors";
import { HardhatMetadata } from "../../core/jsonrpc/types/output/metadata";
import { Reporter } from "../../sentry/reporter";
import { getDifferenceInSeconds } from "../../util/date";
import {
getHardforkName,
hardforkGte,
HardforkName,
} from "../../util/hardforks";
import { getPackageJson } from "../../util/packageInfo";
import { createModelsAndDecodeBytecodes } from "../stack-traces/compiler-to-model";
import { ConsoleLogger } from "../stack-traces/consoleLogger";
import { ContractsIdentifier } from "../stack-traces/contracts-identifier";
Expand Down Expand Up @@ -150,6 +154,7 @@ export class HardhatNode extends EventEmitter {
let nextBlockBaseFeePerGas: bigint | undefined;
let forkNetworkId: number | undefined;
let forkBlockNum: bigint | undefined;
let forkBlockHash: string | undefined;
let hardforkActivations: HardforkHistoryConfig = new Map();

const initialBaseFeePerGasConfig =
Expand All @@ -168,11 +173,13 @@ export class HardhatNode extends EventEmitter {
forkClient: _forkClient,
forkBlockNumber,
forkBlockTimestamp,
forkBlockHash: _forkBlockHash,
} = await makeForkClient(config.forkConfig, config.forkCachePath);
forkClient = _forkClient;

forkNetworkId = forkClient.getNetworkId();
forkBlockNum = forkBlockNumber;
forkBlockHash = _forkBlockHash;

this._validateHardforks(
config.forkConfig.blockNumber,
Expand Down Expand Up @@ -267,8 +274,11 @@ export class HardhatNode extends EventEmitter {
blockchain,
});

const instanceId = bufferToBigInt(randomBytes(32));

const node = new HardhatNode(
vm,
instanceId,
stateManager,
blockchain,
txPool,
Expand All @@ -286,6 +296,7 @@ export class HardhatNode extends EventEmitter {
tracingConfig,
forkNetworkId,
forkBlockNum,
forkBlockHash,
nextBlockBaseFeePerGas,
forkClient
);
Expand Down Expand Up @@ -352,6 +363,7 @@ Hardhat Network's forking functionality only works with blocks from at least spu

private constructor(
private readonly _vm: VM,
private readonly _instanceId: bigint,
private readonly _stateManager: StateManager,
private readonly _blockchain: HardhatBlockchainInterface,
private readonly _txPool: TxPool,
Expand All @@ -369,6 +381,7 @@ Hardhat Network's forking functionality only works with blocks from at least spu
tracingConfig?: TracingConfig,
private _forkNetworkId?: number,
private _forkBlockNumber?: bigint,
private _forkBlockHash?: string,
nextBlockBaseFee?: bigint,
private _forkClient?: JsonRpcClient
) {
Expand Down Expand Up @@ -2519,6 +2532,51 @@ Hardhat Network's forking functionality only works with blocks from at least spu
this._mixHashGenerator.setNext(prevRandao);
}

public async getClientVersion(): Promise<string> {
const hardhatPackage = await getPackageJson();
const ethereumjsVMPackage = require("@nomicfoundation/ethereumjs-vm/package.json");
return `HardhatNetwork/${hardhatPackage.version}/@nomicfoundation/ethereumjs-vm/${ethereumjsVMPackage.version}`;
}

public async getMetadata(): Promise<HardhatMetadata> {
const clientVersion = await this.getClientVersion();

const instanceIdHex = BigIntUtils.toEvmWord(this._instanceId);
const instanceId = `0x${instanceIdHex}`;

const latestBlock = await this.getLatestBlock();

const latestBlockHashHex = latestBlock.header.hash().toString("hex");
const latestBlockHash = `0x${latestBlockHashHex}`;

const metadata: HardhatMetadata = {
clientVersion,
chainId: this._configChainId,
instanceId,
latestBlockNumber: Number(latestBlock.header.number),
latestBlockHash,
};

if (this._forkBlockNumber !== undefined) {
assertHardhatInvariant(
this._forkNetworkId !== undefined,
"this._forkNetworkId should be defined if this._forkBlockNumber is defined"
);
assertHardhatInvariant(
this._forkBlockHash !== undefined,
"this._forkBlockhash should be defined if this._forkBlockNumber is defined"
);

metadata.forkedNetwork = {
chainId: this._forkNetworkId,
forkBlockNumber: Number(this._forkBlockNumber),
forkBlockHash: this._forkBlockHash,
};
}

return metadata;
}

private _getNextMixHash(): Buffer {
return this._mixHashGenerator.next();
}
Expand Down
Expand Up @@ -265,7 +265,7 @@ export class HardhatNetworkProvider
const miningTimer = this._makeMiningTimer();

this._netModule = new NetModule(common);
this._web3Module = new Web3Module();
this._web3Module = new Web3Module(node);
this._evmModule = new EvmModule(
node,
miningTimer,
Expand Down
@@ -1,6 +1,7 @@
import chalk from "chalk";

import { HARDHAT_NETWORK_NAME } from "../../../constants";
import { assertHardhatInvariant } from "../../../core/errors";
import {
numberToRpcQuantity,
rpcQuantityToNumber,
Expand Down Expand Up @@ -29,6 +30,7 @@ export async function makeForkClient(
forkClient: JsonRpcClient;
forkBlockNumber: bigint;
forkBlockTimestamp: number;
forkBlockHash: string;
}> {
const provider = new HttpProvider(
forkConfig.jsonRpcUrl,
Expand Down Expand Up @@ -88,7 +90,14 @@ Please use block number ${lastSafeBlock} or wait for the block to get ${
cacheToDiskEnabled ? forkCachePath : undefined
);

return { forkClient, forkBlockNumber, forkBlockTimestamp };
const forkBlockHash = block.hash;

assertHardhatInvariant(
forkBlockHash !== null,
"Forked block should have a hash"
);

return { forkClient, forkBlockNumber, forkBlockTimestamp, forkBlockHash };
}

async function getBlockByNumber(
Expand Down
@@ -0,0 +1,8 @@
module.exports = {
solidity: "0.5.15",
networks: {
hardhat: {
chainId: 1000,
},
},
};

0 comments on commit 3adb6f6

Please sign in to comment.