Attach to non-fork ganache vm (#391)

truffle-plugin
cgewecke 5 years ago
parent 07ca4d3f02
commit ce6a524ffc
  1. 5
      dist/truffle.plugin.js
  2. 69
      lib/app.js
  3. 3
      lib/ui.js
  4. 4
      test/units/app.js

@ -32,7 +32,6 @@ const path = require('path');
const dir = require('node-dir'); const dir = require('node-dir');
const Web3 = require('web3'); const Web3 = require('web3');
const util = require('util'); const util = require('util');
const ganache = require('ganache-core-sc');
const globby = require('globby'); const globby = require('globby');
async function plugin(truffleConfig){ async function plugin(truffleConfig){
@ -70,7 +69,7 @@ async function plugin(truffleConfig){
death(app.cleanUp); death(app.cleanUp);
// Launch in-process provider // Launch in-process provider
const provider = await app.provider(ganache); const provider = await app.provider(truffle.ganache);
const web3 = new Web3(provider); const web3 = new Web3(provider);
const accounts = await web3.eth.getAccounts(); const accounts = await web3.eth.getAccounts();
const nodeInfo = await web3.eth.getNodeInfo(); const nodeInfo = await web3.eth.getNodeInfo();
@ -153,6 +152,8 @@ function loadTruffleLibrary(){
try { return require("./truffle.library")} catch(err) {}; try { return require("./truffle.library")} catch(err) {};
// TO DO: throw error? This point should never be reached. // 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.
} }
/** /**

@ -3,6 +3,7 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const istanbul = require('istanbul'); const istanbul = require('istanbul');
const util = require('util'); const util = require('util');
const assert = require('assert');
const Instrumenter = require('./instrumenter'); const Instrumenter = require('./instrumenter');
const Coverage = require('./coverage'); const Coverage = require('./coverage');
@ -113,15 +114,31 @@ class App {
* *
* TODO: generalize provider options setting for non-ganache clients.. * TODO: generalize provider options setting for non-ganache clients..
*/ */
provider(client){ async provider(client){
if(!this.client) this.client = client; let retry = false;
if(!this.client) this.client = client; // Prefer client from options
this.collector = new DataCollector(this.instrumenter.instrumentationData); this.collector = new DataCollector(this.instrumenter.instrumentationData);
this.providerOptions.gasLimit = this.gasLimitString; this.providerOptions.gasLimit = this.gasLimitString;
this.providerOptions.allowUnlimitedContractSize = true; 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; return this.provider;
} }
@ -176,6 +193,50 @@ class App {
} }
// ------------------------------------------ Utils ---------------------------------------------- // ------------------------------------------ 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 // File I/O
// ======== // ========

@ -22,6 +22,9 @@ class UI {
const ds = c.bold.yellow('>'); const ds = c.bold.yellow('>');
const kinds = { 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` + 'truffle-help': `Usage: truffle run coverage [options]\n\n` +
`Options:\n` + `Options:\n` +

@ -100,7 +100,9 @@ describe('app', function() {
await plugin(truffleConfig); 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(); assertCleanInitialState();
mock.installFullProject('multiple-migrations'); mock.installFullProject('multiple-migrations');
await plugin(truffleConfig); await plugin(truffleConfig);

Loading…
Cancel
Save