Skip to content
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
5 changes: 3 additions & 2 deletions dist/truffle.plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ const path = require('path');
const dir = require('node-dir');
const Web3 = require('web3');
const util = require('util');
const ganache = require('ganache-core-sc');
const globby = require('globby');

async function plugin(truffleConfig){
Expand Down Expand Up @@ -70,7 +69,7 @@ async function plugin(truffleConfig){
death(app.cleanUp);

// Launch in-process provider
const provider = await app.provider(ganache);
const provider = await app.provider(truffle.ganache);
const web3 = new Web3(provider);
const accounts = await web3.eth.getAccounts();
const nodeInfo = await web3.eth.getNodeInfo();
Expand Down Expand Up @@ -153,6 +152,8 @@ function loadTruffleLibrary(){
try { return require("./truffle.library")} catch(err) {};

// TO DO: throw error? This point should never be reached.
// Validate that truffle.ganache exists? Have checked that
// a non-existent prop defaults to the ganache-core-sc fallback FWIW.
}

/**
Expand Down
69 changes: 65 additions & 4 deletions lib/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const fs = require('fs');
const path = require('path');
const istanbul = require('istanbul');
const util = require('util');
const assert = require('assert');

const Instrumenter = require('./instrumenter');
const Coverage = require('./coverage');
Expand Down Expand Up @@ -113,15 +114,31 @@ class App {
*
* TODO: generalize provider options setting for non-ganache clients..
*/
provider(client){
if(!this.client) this.client = client;
async provider(client){
let retry = false;

if(!this.client) this.client = client; // Prefer client from options

this.collector = new DataCollector(this.instrumenter.instrumentationData);

this.providerOptions.gasLimit = this.gasLimitString;
this.providerOptions.allowUnlimitedContractSize = true;
this.providerOptions.logger = { log: this.collector.step.bind(this.collector) };

this.provider = this.client.provider(this.providerOptions);
// Try to launch provider and attach to vm step of
// either plugin's ganache or a provider passed via options
try {
this.provider = await this.attachToVM();
} catch(err){
retry = true;
this.ui.report('vm-fail', [])
}

// Fallback to ganache-core-sc (eq: ganache-core 2.7.0)
if (retry){
this.providerOptions.logger = { log: this.collector.step.bind(this.collector) };
this.client = require('ganache-core-sc');
this.provider = this.client.provider(this.providerOptions);
}

return this.provider;
}
Expand Down Expand Up @@ -176,6 +193,50 @@ class App {
}
// ------------------------------------------ Utils ----------------------------------------------

// ========
// Provider
// ========
async attachToVM(){
const self = this;
const provider = this.client.provider(this.providerOptions);

this.assertHasBlockchain(provider);

await this.vmIsResolved(provider);

const blockchain = provider.engine.manager.state.blockchain;
const createVM = blockchain.createVMFromStateTrie;

// Attach to VM which ganache has already instantiated
// and which it uses to execute eth_send
blockchain.vm.on('step', self.collector.step.bind(self.collector));

// Attach/hijack createVM method which ganache uses to run eth_calls
blockchain.createVMFromStateTrie = function(state, activatePrecompiles) {
const vm = createVM.apply(blockchain, arguments);
vm.on('step', self.collector.step.bind(self.collector));
return vm;
}

return provider;
}

assertHasBlockchain(provider){
assert(provider.engine.manager.state.blockchain !== undefined);
assert(provider.engine.manager.state.blockchain.createVMFromStateTrie !== undefined);
}

async vmIsResolved(provider){
return new Promise(resolve => {
const interval = setInterval(() => {
if (provider.engine.manager.state.blockchain.vm !== undefined){
clearInterval(interval);
resolve();
}
});
})
}

// ========
// File I/O
// ========
Expand Down
3 changes: 3 additions & 0 deletions lib/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class UI {
const ds = c.bold.yellow('>');

const kinds = {
'vm-fail': `:warning: ${c.red('There was a problem attaching to the ganache-core VM.')} `+
`${c.red('Check the provider option syntax in solidity-coverage docs.')}\n`+
`:warning: ${c.red('Using ganache-core-sc (eq. core v2.7.0) instead.')}\n`,

'truffle-help': `Usage: truffle run coverage [options]\n\n` +
`Options:\n` +
Expand Down
4 changes: 3 additions & 1 deletion test/units/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ describe('app', function() {
await plugin(truffleConfig);
});

it('project uses multiple migrations', async function() {
// This project has three contract suites and uses .deployed() instances which
// depend on truffle's migratons and the inter-test evm_revert / evm_snapshot mechanism.
it('project evm_reverts repeatedly', async function() {
assertCleanInitialState();
mock.installFullProject('multiple-migrations');
await plugin(truffleConfig);
Expand Down