From ca3aea79236bdc83b125bf91c6cbce422c280dbd Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Tue, 9 Oct 2018 16:43:38 -0400 Subject: [PATCH] BROKEN: solc doesnt load --- cmd/cmd_controller.js | 3 + lib/core/engine.js | 3 +- lib/modules/blockchain_connector/index.js | 61 ++++- lib/modules/solidity/index.js | 2 + lib/modules/solidity/solcP.js | 4 +- lib/modules/solidity/solcW.js | 2 + lib/modules/tests/index.js | 2 +- lib/modules/tests/test.js | 310 +++++++++++----------- 8 files changed, 222 insertions(+), 165 deletions(-) diff --git a/cmd/cmd_controller.js b/cmd/cmd_controller.js index 8331dbdd0..e9ac99472 100644 --- a/cmd/cmd_controller.js +++ b/cmd/cmd_controller.js @@ -554,6 +554,9 @@ class EmbarkController { engine.init({}, callback); }, function startServices(callback) { + engine.startService("processManager"); + engine.startService("web3", {wait: true}); // Empty web3 as Test changes it + engine.startService("deployment"); engine.startService("testRunner"); callback(); }, diff --git a/lib/core/engine.js b/lib/core/engine.js index a4720ca3c..c2af6a828 100644 --- a/lib/core/engine.js +++ b/lib/core/engine.js @@ -242,7 +242,8 @@ class Engine { this.registerModule('blockchain_connector', { isDev: this.isDev, - web3: options.web3 + web3: options.web3, + wait: options.wait }); this.registerModule('whisper'); diff --git a/lib/modules/blockchain_connector/index.js b/lib/modules/blockchain_connector/index.js index 14b1cb85b..a9e129abc 100644 --- a/lib/modules/blockchain_connector/index.js +++ b/lib/modules/blockchain_connector/index.js @@ -20,6 +20,7 @@ class BlockchainConnector { this.isDev = options.isDev; this.web3Endpoint = ''; this.isWeb3Ready = false; + this.wait = options.wait; self.events.setCommandHandler("blockchain:web3:isReady", (cb) => { cb(self.isWeb3Ready); @@ -34,14 +35,13 @@ class BlockchainConnector { } else { this.isWeb3Ready = true; } + this.registerServiceCheck(); this.registerRequests(); this.registerWeb3Object(); this.registerEvents(); - this.subscribeToPendingTransactions(); } - //initWeb3() { initWeb3(cb) { if (!cb) { cb = function(){}; @@ -54,22 +54,35 @@ class BlockchainConnector { const self = this; this.web3 = new Web3(); - if (this.contractsConfig.deployment.type !== "rpc" && this.contractsConfig.deployment.type !== "ws") { - const message = __("contracts config error: unknown deployment type %s", this.contractsConfig.deployment.type); - this.logger.error(message); + // TODO find a better way + if (self.wait) { + return cb(); } - const protocol = (this.contractsConfig.deployment.type === "rpc") ? this.contractsConfig.deployment.protocol : 'ws'; + let {type, host, port, accounts, protocol} = this.contractsConfig.deployment; - this.web3Endpoint = utils.buildUrl(protocol, this.contractsConfig.deployment.host, this.contractsConfig.deployment.port);//`${protocol}://${this.contractsConfig.deployment.host}:${this.contractsConfig.deployment.port}`; + if (!BlockchainConnector.ACCEPTED_TYPES.includes(type)) { + this.logger.error(__("contracts config error: unknown deployment type %s", type)); + this.logger.error(__("Accepted types are: %s", BlockchainConnector.ACCEPTED_TYPES.join(', '))); + } + + if (type === 'vm') { + this.provider = this._getSimulator().provider; + this.web3.setProvider(this.provider(this.contractsConfig.deployment)); + return self._emitWeb3Ready(); + } + + protocol = (type === "rpc") ? protocol : 'ws'; + + this.web3Endpoint = utils.buildUrl(protocol, host, port); const providerOptions = { web3: this.web3, - accountsConfig: this.contractsConfig.deployment.accounts, + accountsConfig: accounts, blockchainConfig: this.blockchainConfig, logger: this.logger, isDev: this.isDev, - type: this.contractsConfig.deployment.type, + type: type, web3Endpoint: self.web3Endpoint }; this.provider = new Provider(providerOptions); @@ -89,14 +102,36 @@ class BlockchainConnector { }) .catch(console.error); self.provider.fundAccounts(() => { - self.isWeb3Ready = true; - self.events.emit(WEB3_READY); - self.registerWeb3Object(); + self._emitWeb3Ready(); }); }); }); } + _emitWeb3Ready() { + this.isWeb3Ready = true; + this.events.emit(WEB3_READY); + this.registerWeb3Object(); + } + + _getSimulator() { + try { + return require('ganache-cli'); + } catch (e) { + const moreInfo = 'For more information see https://github.com/trufflesuite/ganache-cli'; + if (e.code === 'MODULE_NOT_FOUND') { + this.logger.error(__('Simulator not found; Please install it with "%s"', 'npm install ganache-cli --save')); + this.logger.error(moreInfo); + throw e; + } + this.logger.error("=============="); + this.logger.error(__("Tried to load Ganache CLI (testrpc), but an error occurred. This is a problem with Ganache CLI")); + this.logger.error(moreInfo); + this.logger.error("=============="); + throw e; + } + } + registerEvents() { const self = this; self.events.on('check:wentOffline:Ethereum', () => { @@ -284,5 +319,7 @@ class BlockchainConnector { } } +BlockchainConnector.ACCEPTED_TYPES = ['rpc', 'ws', 'vm']; + module.exports = BlockchainConnector; diff --git a/lib/modules/solidity/index.js b/lib/modules/solidity/index.js index 7459b996d..d18c1389b 100644 --- a/lib/modules/solidity/index.js +++ b/lib/modules/solidity/index.js @@ -55,6 +55,7 @@ class Solidity { self.logger.info(__("loading solc compiler") + ".."); self.solcW.load_compiler(function (err) { + console.log('LOADED'); self.solcAlreadyLoaded = true; callback(err); }); @@ -146,6 +147,7 @@ class Solidity { callback(null, compiled_object); } ], function (err, result) { + console.log('COMPLETED COMPILATION', err); cb(err, result); }); } diff --git a/lib/modules/solidity/solcP.js b/lib/modules/solidity/solcP.js index eb3711c63..caf34484a 100644 --- a/lib/modules/solidity/solcP.js +++ b/lib/modules/solidity/solcP.js @@ -10,6 +10,7 @@ const NpmTimer = require('../library_manager/npmTimer'); class SolcProcess extends ProcessWrapper { constructor(options){ + console.error('ALLO'); super({pingParent: false}); this._logger = options.logger; this._showSpinner = options.showSpinner === true; @@ -66,6 +67,7 @@ class SolcProcess extends ProcessWrapper { let solcProcess; process.on('message', (msg) => { + console.error('MSG', msg.action); if (msg.action === "init") { solcProcess = new SolcProcess(msg.options); return process.send({result: "initiated"}); @@ -76,7 +78,7 @@ process.on('message', (msg) => { return process.send({result: "loadedCompiler"}); } - else if (msg.action == 'installAndLoadCompiler') { + else if (msg.action === 'installAndLoadCompiler') { solcProcess.installAndLoadCompiler(msg.solcVersion, msg.packagePath).then(() => { return process.send({result: "loadedCompiler"}); }); diff --git a/lib/modules/solidity/solcW.js b/lib/modules/solidity/solcW.js index 355508e33..07e4daa07 100644 --- a/lib/modules/solidity/solcW.js +++ b/lib/modules/solidity/solcW.js @@ -45,6 +45,7 @@ class SolcW { events: self.events, silent: false }); + console.log(utils.joinPath(__dirname, 'solcP.js')); this.solcProcess.once("result", "initiated", () => { this.events.request("version:get:solc", function(solcVersion) { @@ -65,6 +66,7 @@ class SolcW { self.compilerLoaded = true; done(); }); + // FIXME this.solcProcess.send({action: "init", options: {logger: self.logger, showSpinner: !self.useDashboard}}); if (this.ipc.isServer()) { diff --git a/lib/modules/tests/index.js b/lib/modules/tests/index.js index d226c250d..407589c55 100644 --- a/lib/modules/tests/index.js +++ b/lib/modules/tests/index.js @@ -125,7 +125,7 @@ class TestRunner { async.waterfall([ function setupGlobalNamespace(next) { // TODO put default config - const test = new Test({loglevel, node: options.node, events: self.events}); + const test = new Test({loglevel, node: options.node, events: self.events, logger: self.logger, config: self.embark.config}); global.embark = test; global.assert = assert; global.config = test.config.bind(test); diff --git a/lib/modules/tests/test.js b/lib/modules/tests/test.js index 753738d60..6eb1cbb1a 100644 --- a/lib/modules/tests/test.js +++ b/lib/modules/tests/test.js @@ -1,7 +1,7 @@ const async = require('async'); -const Engine = require('../../core/engine.js'); -const TestLogger = require('./test_logger.js'); -const Web3 = require('web3'); +// const Engine = require('../../core/engine.js'); +// const TestLogger = require('./test_logger.js'); +// const Web3 = require('web3'); const AccountParser = require('../../utils/accountParser'); // TODO: breaks module isolation; tests need to be refactored to use the engine and avoid this const Provider = require('../blockchain_connector/provider.js'); @@ -9,157 +9,43 @@ const utils = require('../../utils/utils'); const EmbarkJS = require('embarkjs'); -function getSimulator() { - try { - return require('ganache-cli'); - } catch (e) { - const moreInfo = 'For more information see https://github.com/trufflesuite/ganache-cli'; - if (e.code === 'MODULE_NOT_FOUND') { - console.error(__('Simulator not found; Please install it with "%s"', 'npm install ganache-cli --save')); - console.error(moreInfo); - throw e; - } - console.error("=============="); - console.error(__("Tried to load Ganache CLI (testrpc), but an error occurred. This is a problem with Ganache CLI")); - console.error(moreInfo); - console.error("=============="); - throw e; - } -} - class Test { constructor(options) { this.options = options || {}; this.simOptions = {}; this.events = options.events; + this.logger = options.logger; + this.configObj = options.config; this.ready = true; this.firstRunConfig = true; this.error = false; this.contracts = {}; this.firstDeployment = true; this.needConfig = true; - this.web3 = new Web3(); - this.engine = new Engine({ + this.web3 = null; + this.blockchainConnector = null; + this.provider = null; + this.accounts = []; + + /*this.engine = new Engine({ env: this.options.env || 'test', // TODO: config will need to detect if this is a obj embarkConfig: this.options.embarkConfig || 'embark.json', interceptLogs: false - }); - } - - initWeb3Provider(callback) { - const self = this; - if (this.provider) { - this.provider.stop(); - } - - if (this.simOptions.accounts) { - this.simOptions.accounts = this.simOptions.accounts.map((account) => { - if (!account.hexBalance) { - account.hexBalance = '0x8AC7230489E80000'; // 10 ether - } - return {balance: account.hexBalance, secretKey: account.privateKey}; - }); - } - - if (this.simOptions.host || (this.options.node && this.options.node !== 'vm')) { - let options = this.simOptions; - if (this.options.node) { - options = utils.deconstructUrl(this.options.node); - } - - let {host, port, type, protocol, accounts} = options; - if (!protocol) { - protocol = (options.type === "rpc") ? 'http' : 'ws'; - } - const endpoint = `${protocol}://${host}:${port}`; - const providerOptions = { - web3: this.web3, - type, - accountsConfig: accounts, - blockchainConfig: this.engine.config.blockchainConfig, - logger: this.engine.logger, - isDev: false, - web3Endpoint: endpoint - }; - console.info(`Connecting to node at ${endpoint}`.cyan); - - return utils.pingEndpoint(host, port, type, protocol, this.engine.config.blockchainConfig.wsOrigins.split(',')[0], (err) => { - if (err) { - console.error(`Error connecting to the node, there might be an error in ${endpoint}`.red); - return callback(err); - } - - self.provider = new Provider(providerOptions); - return self.provider.startWeb3Provider((err) => { - if (err) { - return callback(err); - } - callback(); - }); - }); - } - - if (!this.sim) { - this.sim = getSimulator(); - } - - let simProvider = this.sim.provider(this.simOptions); - - if (this.options.coverage) { - // Here we patch the sendAsync method on the provider. The goal behind this is to force pure/constant/view calls to become - // transactions, so that we can pull in execution traces and account for those executions in code coverage. - // - // Instead of a simple call, here's what happens: - // - // 1) A transaction is sent with the same payload, and a pre-defined gas price; - // 2) We wait for the transaction to be mined by asking for the receipt; - // 3) Once we get the receipt back, we dispatch the real call and pass the original callback; - // - // This will still allow tests to get the return value from the call and run contracts unmodified. - simProvider.realSendAsync = simProvider.sendAsync.bind(simProvider); - simProvider.sendAsync = function(payload, cb) { - if(payload.method !== 'eth_call') { - return simProvider.realSendAsync(payload, cb); - } - self.engine.events.request('reporter:toggleGasListener'); - let newParams = Object.assign({}, payload.params[0], {gasPrice: '0x77359400'}); - let newPayload = { - id: payload.id + 1, - method: 'eth_sendTransaction', - params: [newParams], - jsonrpc: payload.jsonrpc - }; - - simProvider.realSendAsync(newPayload, (_err, response) => { - let txHash = response.result; - self.web3.eth.getTransactionReceipt(txHash, (_err, _res) => { - self.engine.events.request('reporter:toggleGasListener'); - simProvider.realSendAsync(payload, cb); - }); - }); - }; - } - - this.web3.setProvider(simProvider); - callback(); - } - - initDeployServices() { - this.engine.startService("web3", { - web3: this.web3 - }); - this.engine.startService("deployment", { - trackContracts: false, - compileOnceOnly: true, - disableOptimizations: this.options.coverage - }); - this.gasLimit = 6000000; - this.engine.events.request('deploy:setGasLimit', this.gasLimit); + });*/ } init(callback) { - let self = this; + this.showNodeHttpWarning(); + + this.events.request('blockchain:object', (connector) => { + this.blockchainConnector = connector; + // TODO use events instead of using web3 directly? + this.web3 = this.blockchainConnector.web3; + callback(); + }); + + /*let self = this; async.waterfall([ function initEngine(cb) { self.engine.init({ @@ -190,10 +76,132 @@ class Test { self.showNodeHttpWarning(); cb(); } - ], callback); + ], callback);*/ } - connectToIpcNode(cb) { + initWeb3Provider(callback) { + const self = this; + if (this.provider) { + this.provider.stop(); + } + + if (this.simOptions.accounts) { + this.simOptions.accounts = this.simOptions.accounts.map((account) => { + if (!account.hexBalance) { + account.hexBalance = '0x8AC7230489E80000'; // 10 ether + } + return {balance: account.hexBalance, secretKey: account.privateKey}; + }); + } + + // TODO use event for this + if (!this.simOptions.host || (this.options.node && this.options.node === 'vm')) { + this.simOptions.type = 'vm'; + } + this.configObj.contractsConfig.deployment = this.simOptions; + this.blockchainConnector.contractsConfig = this.configObj.contractsConfig; + this.blockchainConnector.isWeb3Ready = false; + + // TODO change this + if (this.simOptions.host || (this.options.node && this.options.node !== 'vm')) { + let options = this.simOptions; + if (this.options.node) { + options = utils.deconstructUrl(this.options.node); + } + + let {host, port, type, protocol, accounts} = options; + if (!protocol) { + protocol = (options.type === "rpc") ? 'http' : 'ws'; + } + const endpoint = `${protocol}://${host}:${port}`; + const providerOptions = { + web3: this.web3, + type, + accountsConfig: accounts, + blockchainConfig: this.configObj.blockchainConfig, + logger: this.logger, + isDev: false, + web3Endpoint: endpoint + }; + console.info(`Connecting to node at ${endpoint}`.cyan); + + return utils.pingEndpoint(host, port, type, protocol, this.configObj.blockchainConfig.wsOrigins.split(',')[0], (err) => { + if (err) { + console.error(`Error connecting to the node, there might be an error in ${endpoint}`.red); + return callback(err); + } + + self.provider = new Provider(providerOptions); + return self.provider.startWeb3Provider((err) => { + if (err) { + return callback(err); + } + callback(); + }); + }); + } + + // if (!this.sim) { + // this.sim = getSimulator(); + // } + + // let simProvider = this.sim.provider(this.simOptions); + + // TODO change this + /*if (this.options.coverage) { + // Here we patch the sendAsync method on the provider. The goal behind this is to force pure/constant/view calls to become + // transactions, so that we can pull in execution traces and account for those executions in code coverage. + // + // Instead of a simple call, here's what happens: + // + // 1) A transaction is sent with the same payload, and a pre-defined gas price; + // 2) We wait for the transaction to be mined by asking for the receipt; + // 3) Once we get the receipt back, we dispatch the real call and pass the original callback; + // + // This will still allow tests to get the return value from the call and run contracts unmodified. + simProvider.realSendAsync = simProvider.sendAsync.bind(simProvider); + simProvider.sendAsync = function(payload, cb) { + if(payload.method !== 'eth_call') { + return simProvider.realSendAsync(payload, cb); + } + self.events.request('reporter:toggleGasListener'); + let newParams = Object.assign({}, payload.params[0], {gasPrice: '0x77359400'}); + let newPayload = { + id: payload.id + 1, + method: 'eth_sendTransaction', + params: [newParams], + jsonrpc: payload.jsonrpc + }; + + simProvider.realSendAsync(newPayload, (_err, response) => { + let txHash = response.result; + self.web3.eth.getTransactionReceipt(txHash, (_err, _res) => { + self.events.request('reporter:toggleGasListener'); + simProvider.realSendAsync(payload, cb); + }); + }); + }; + }*/ + + this.blockchainConnector.initWeb3(callback); + // this.web3.setProvider(simProvider); + // callback(); + } + + /*initDeployServices() { + this.engine.startService("web3", { + web3: this.web3 + }); + this.engine.startService("deployment", { + trackContracts: false, + compileOnceOnly: true, + disableOptimizations: this.options.coverage + }); + this.gasLimit = 6000000; + this.engine.events.request('deploy:setGasLimit', this.gasLimit); + }*/ + + /*connectToIpcNode(cb) { this.engine.ipc.request('blockchain:node', {}, (err, node) => { if (err) { this.engine.logger.error(err.message || err); @@ -203,11 +211,11 @@ class Test { this.showNodeHttpWarning(); cb(); }); - } + }*/ showNodeHttpWarning() { if (this.options.node.startsWith('http')) { - this.engine.logger.warn("You are using http to connect to the node, as a result the gas details won't be correct." + + this.logger.warn("You are using http to connect to the node, as a result the gas details won't be correct." + " For correct gas details reporting, please use a websockets connection to your node."); } } @@ -252,7 +260,7 @@ class Test { if (accounts) { self.simOptions.accounts = AccountParser.parseAccountsConfig(accounts, self.web3); } else { - self.simOptions.account = null; + self.simOptions.accounts = null; } Object.assign(self.simOptions, { @@ -270,7 +278,7 @@ class Test { return callback(err); } self.firstRunConfig = false; - self.initDeployServices(); + // self.initDeployServices(); callback(); }); } @@ -299,14 +307,14 @@ class Test { if (!self.firstDeployment) { return next(); } - console.info('Compiling contracts'.cyan); - self.engine.events.request("contracts:build", false, (err) => { + self.logger.info('Compiling contracts'.cyan); + self.events.request("contracts:build", false, (err) => { self.firstDeployment = false; next(err); }); }, function resetContracts(next) { - self.engine.events.request("contracts:reset:dependencies", next); + self.events.request("contracts:reset:dependencies", next); }, function deploy(next) { self._deploy(options, (err, accounts) => { @@ -323,6 +331,7 @@ class Test { } ], (err, accounts) => { if (err) { + // TODO Do not exit in case of not a normal run (eg after a change) process.exit(1); } callback(null, accounts); @@ -333,7 +342,8 @@ class Test { const self = this; async.waterfall([ function getConfig(next) { - self.engine.config.contractsConfig = {contracts: config.contracts, versions: self.versions_default}; + // TODO use events instead of modifying directly + self.configObj.contractsConfig = {contracts: config.contracts, versions: self.versions_default}; next(); }, function getAccounts(next) { @@ -349,7 +359,7 @@ class Test { function getBalance(accounts, next) { self.web3.eth.getBalance(self.web3.eth.defaultAccount).then((balance) => { if (parseInt(balance, 10) === 0) { - console.warn("Warning: default account has no funds"); + self.logger.warn("Warning: default account has no funds"); } next(null, accounts); }).catch((err) => { @@ -357,12 +367,12 @@ class Test { }); }, function deploy(accounts, next) { - self.engine.events.request('deploy:contracts:test', () => { + self.events.request('deploy:contracts:test', () => { next(null, accounts); }); }, function createContractObject(accounts, next) { - self.engine.events.request('contracts:all', (err, contracts) => { + self.events.request('contracts:all', (err, contracts) => { async.each(contracts, (contract, eachCb) => { if (!self.contracts[contract.className]) { @@ -397,7 +407,7 @@ class Test { } ], function (err, accounts) { if (err) { - console.log(__('terminating due to error')); + self.logger.log(__('terminating due to error')); return callback(err); } callback(null, accounts);