Skip to content

Commit

Permalink
Split hardhat & builder / support HardhatEVM (#560)
Browse files Browse the repository at this point in the history
  • Loading branch information
cgewecke authored Nov 10, 2020
1 parent 85f8384 commit 1e9ef8b
Show file tree
Hide file tree
Showing 13 changed files with 1,398 additions and 1,440 deletions.
13 changes: 13 additions & 0 deletions hardhat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const plugin = require("./plugins/hardhat.plugin");
const PluginUI = require('./plugins/resources/nomiclabs.ui');

// UI for the task flags...
const ui = new PluginUI();

task("coverage", "Generates a code coverage report for tests")
.addOptionalParam("testfiles", ui.flags.file, "", types.string)
.addOptionalParam("solcoverjs", ui.flags.solcoverjs, "", types.string)
.addOptionalParam('temp', ui.flags.temp, "", types.string)
.setAction(async function(args, env){
await plugin(args, env)
});
8 changes: 8 additions & 0 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,14 @@ class API {
})
}

hardhatTraceHandler(trace, isTraceFromCall){
if (trace.bytecode && trace.bytecode.instructions){
for (const instruction of trace.bytecode.instructions){
this.collector.trackHardhatEVMInstruction(instruction)
}
}
}

// ========
// File I/O
// ========
Expand Down
29 changes: 24 additions & 5 deletions lib/collector.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,34 @@ class DataCollector {
if (this.validOpcodes[info.opcode.name] && info.stack.length > 0){
const idx = info.stack.length - 1;
let hash = web3Utils.toHex(info.stack[idx]).toString();
hash = this._normalizeHash(hash);

if(this.instrumentationData[hash]){
this.instrumentationData[hash].hits++;
}
this._registerHash(hash)
}
} catch (err) { /*Ignore*/ };
}

/**
* Converts pushData value to string and registers in instrumentation map.
* @param {HardhatEVMTraceInstruction} instruction
*/
trackHardhatEVMInstruction(instruction){
if (instruction.pushData){
let hash = `0x` + instruction.pushData.toString('hex');
this._registerHash(hash)
}
}

/**
* Normalizes has string and marks hit.
* @param {String} hash bytes32 hash
*/
_registerHash(hash){
hash = this._normalizeHash(hash);

if(this.instrumentationData[hash]){
this.instrumentationData[hash].hits++;
}
}

/**
* Left-pads zero prefixed bytes 32 hashes to length 66. The '59' in the
* comparison below is arbitrary. It provides a margin for recurring zeros
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,18 @@
"recursive-readdir": "^2.2.2",
"sc-istanbul": "^0.4.5",
"shelljs": "^0.8.3",
"web3": "^1.3.0"
"web3": "1.2.9"
},
"devDependencies": {
"@nomiclabs/buidler": "^1.3.6",
"@nomiclabs/buidler-truffle5": "^1.3.4",
"@nomiclabs/buidler-web3": "^1.3.4",
"@nomiclabs/hardhat-truffle5": "^2.0.0",
"@nomiclabs/hardhat-web3": "^2.0.0",
"@truffle/contract": "^4.0.36",
"buidler-gas-reporter": "^0.1.3",
"decache": "^4.5.1",
"hardhat": "^2.0.2",
"mocha": "5.2.0",
"nyc": "^14.1.1",
"solc": "^0.5.10",
Expand Down
74 changes: 19 additions & 55 deletions plugins/buidler.plugin.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
const API = require('./../lib/api');
const utils = require('./resources/plugin.utils');
const buidlerUtils = require('./resources/buidler.utils');
const PluginUI = require('./resources/buidler.ui');
const buidlerUtils = require('./resources/nomiclabs.utils');
const PluginUI = require('./resources/nomiclabs.ui');

const pkg = require('./../package.json');
const death = require('death');
const path = require('path');
const Web3 = require('web3');

const { task, types } = require("@nomiclabs/buidler/config");
const { ensurePluginLoadedWithUsePlugin } = require("@nomiclabs/buidler/plugins");

const {
TASK_TEST,
TASK_COMPILE,
TASK_COMPILE_SOLIDITY_GET_COMPILER_INPUT,
TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOB_FOR_FILE,
TASK_COMPILE_GET_COMPILER_INPUT
} = require("@nomiclabs/buidler/builtin-tasks/task-names");

ensurePluginLoadedWithUsePlugin();

function plugin() {

// UI for the task flags...
const ui = new PluginUI();

let measureCoverage = false;
let instrumentedSources;
// Unset useLiteralContent due to solc metadata size restriction
task(TASK_COMPILE_GET_COMPILER_INPUT).setAction(async (_, __, runSuper) => {
const input = await runSuper();
input.settings.metadata.useLiteralContent = false;
return input;
})

task("coverage", "Generates a code coverage report for tests")

Expand All @@ -36,7 +42,6 @@ function plugin() {
let ui;
let api;
let config;
instrumentedSources = {};

try {
death(buidlerUtils.finish.bind(null, config, api)); // Catch interrupt signals
Expand All @@ -48,7 +53,7 @@ function plugin() {
// ==============
// Server launch
// ==============
const network = buidlerUtils.setupNetwork(env, api, ui);
const network = buidlerUtils.setupBuidlerNetwork(env, api, ui);

const client = api.client || require('ganache-cli');
const address = await api.ganache(client);
Expand Down Expand Up @@ -86,9 +91,6 @@ function plugin() {
} = utils.assembleFiles(config, skipFiles);

targets = api.instrument(targets);
for (const target of targets) {
instrumentedSources[target.canonicalPath] = target.source;
}
utils.reportSkipped(config, skipped);

// ==============
Expand All @@ -102,11 +104,14 @@ function plugin() {
} = utils.getTempLocations(config);

utils.setupTempFolders(config, tempContractsDir, tempArtifactsDir)
utils.save(targets, config.paths.sources, tempContractsDir);
utils.save(skipped, config.paths.sources, tempContractsDir);

config.paths.sources = tempContractsDir;
config.paths.artifacts = tempArtifactsDir;
config.paths.cache = buidlerUtils.tempCacheDir(config);
config.solc.optimizer.enabled = false;

measureCoverage = true;
await env.run(TASK_COMPILE);

await api.onCompileComplete(config);
Expand All @@ -132,55 +137,14 @@ function plugin() {
await api.onIstanbulComplete(config);

} catch(e) {
error = e;
} finally {
measureCoverage = false;
error = e;
}

await buidlerUtils.finish(config, api);

if (error !== undefined ) throw error;
if (process.exitCode > 0) throw new Error(ui.generate('tests-fail', [process.exitCode]));
});

task(TASK_COMPILE_SOLIDITY_GET_COMPILER_INPUT).setAction(async (_, { config }, runSuper) => {
const solcInput = await runSuper();
if (measureCoverage) {
// The source name here is actually the global name in the solc input,
// but buidler uses the fully qualified contract names.
for (const [sourceName, source] of Object.entries(solcInput.sources)) {
const absolutePath = path.join(config.paths.root, sourceName);
// Patch in the instrumented source code.
if (absolutePath in instrumentedSources) {
source.content = instrumentedSources[absolutePath];
}
}
}
return solcInput;
});

// Solidity settings are best set here instead of the TASK_COMPILE_SOLIDITY_GET_COMPILER_INPUT task.
task(TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOB_FOR_FILE).setAction(async (_, __, runSuper) => {
const compilationJob = await runSuper();
if (measureCoverage && typeof compilationJob === "object") {
if (compilationJob.solidityConfig.settings === undefined) {
compilationJob.solidityConfig.settings = {};
}

const { settings } = compilationJob.solidityConfig;
if (settings.metadata === undefined) {
settings.metadata = {};
}
if (settings.optimizer === undefined) {
settings.optimizer = {};
}
// Unset useLiteralContent due to solc metadata size restriction
settings.metadata.useLiteralContent = false;
// Override optimizer settings for all compilers
settings.optimizer.enabled = false;
}
return compilationJob;
});
})
}

module.exports = plugin;
Loading

0 comments on commit 1e9ef8b

Please sign in to comment.