fix(@embark/console): Fix console not working with VM2/monorepo

The console was not working correctly with the latest VM2/monorepo updates. This PR addresses namely fixes this problem, but also adds a few more notable changes:

* SIGNIFICANT improvement in loading time for `embark console` with an already running `embark run`. This is due to removing unneeded services starting, and instead forwarding user input to the main `embark run` process.
* All user input commands are now forwarded to the `embark run` process via IPC insteaad of evaluating the command in the `embark console` process.
* Removed IPC console history as it's no longer needed due to the above. Side effects:
  ** The signature of the `runcode:eval` and `runcode:register` events was changed to remove the `toRecord` parameter.
  ** Old `runcode:eval` signature: `events.request("runcode:eval", "code to be evaluated", (err, result) => {}, isNotUserInput, tolerateError)`
  ** New `runcode:eval` signature: `events.request("runcode:eval", "code to be evaluated", (err, result) => {}, tolerateError)`
  ** Old `runcode:register` signature: `events.request("runcode:register", "varName", variableValue, toRecord, (err, result) => {})`
  ** New `runcode:register` signature: `events.request("runcode:register", "varName", variableValue, (err, result) => {})`

* Removed unneeded `forceRegister` flag.
* Removed the `VM.getWeb3Config` method as it's no longer being used (EmbarkJS contracts are pulled out from the VM instead).
* Updated `web3Connector` `blockchain:connector:ready` to allow for event requests or event emissions.
* In the tests, removed the initial `initWeb3Provider` in the `init` as it was being called twice.
* In the tests, removed the `web3Connector` check message as the tests are now using the Console, and the console does this check. This was causing duplicate messages to be displayed in the output.
* Fix `web3 is not defined` browser error
This commit is contained in:
emizzle 2019-02-11 15:29:37 +11:00 committed by Pascal Precht
parent 84ca98f962
commit fc823bb7eb
No known key found for this signature in database
GPG Key ID: 0EE28D8D6FD85D7D
10 changed files with 79 additions and 153 deletions

View File

@ -278,6 +278,8 @@ class EmbarkController {
webpackConfigName: options.webpackConfigName
});
const isSecondaryProcess = (engine) => { return engine.ipc.connected && engine.ipc.isClient(); };
async.waterfall([
function initEngine(callback) {
engine.init({}, callback);
@ -288,63 +290,36 @@ class EmbarkController {
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
if (engine.ipc.connected) {
engine.startService("codeRunner");
engine.startService("console");
engine.startService("web3");
engine.startService("deployment");
engine.startService("codeGenerator");
engine.startService("codeRunner");
engine.startService("console");
if (isSecondaryProcess(engine)) {
return callback();
}
engine.startService("processManager");
engine.startService("serviceMonitor");
engine.startService("libraryManager");
engine.startService("codeRunner");
engine.startService("web3");
engine.startService("pipeline");
engine.startService("deployment");
engine.startService("storage");
engine.startService("codeGenerator");
engine.startService("console");
engine.startService("cockpit");
engine.startService("pluginCommand");
engine.events.once('check:backOnline:Ethereum', () => callback());
engine.events.request('blockchain:ready', callback);
},
function ipcConnect(callback) {
// Do specific work in case we are connected to a socket:
// - Setup Web3
// - Apply history
if(!engine.ipc.connected || engine.ipc.isServer()) {
if(isSecondaryProcess(engine)) {
return callback();
}
const Provider = require('../lib/modules/blockchain_connector/provider');
const Web3 = require('web3');
let web3 = new Web3();
engine.ipc.request("runcode:getCommands", null, (_, {web3Config, commands}) => {
const providerOptions = {
web3: web3,
accountsConfig: engine.config.contractsConfig.deployment.accounts,
blockchainConfig: engine.config.blockchainConfig,
logger: engine.logger,
isDev: engine.isDev,
type: engine.config.contractsConfig.deployment.type,
web3Endpoint: web3Config.providerUrl
};
const provider = new Provider(providerOptions);
web3.eth.defaultAccount = web3Config.defaultAccount;
provider.startWeb3Provider(() => {
engine.events.emit("runcode:register", "web3", web3);
async.each(commands, ({varName, code}, next) => {
if (varName) {
engine.events.emit("runcode:register", varName, code);
} else {
engine.events.request("runcode:eval", code);
}
next();
}, callback);
});
});
engine.events.request("console:provider:ready", callback);
},
function deploy(callback) {
// Skip if we are connected to a websocket, the server will do it
if(engine.ipc.connected && engine.ipc.isClient()) {
if(isSecondaryProcess(engine)) {
return callback();
}
engine.config.reloadConfig();
@ -354,7 +329,7 @@ class EmbarkController {
},
function waitForWriteFinish(callback) {
// Skip if we are connected to a websocket, the server will do it
if(engine.ipc.connected && engine.ipc.isClient()) {
if(isSecondaryProcess(engine)) {
return callback();
}
engine.logger.info("Finished deploying".underline);
@ -657,7 +632,7 @@ class EmbarkController {
});
engine.startService("storage");
engine.startService("codeGenerator");
engine.startService("console", {forceRegister: true});
engine.startService("console");
engine.startService("pluginCommand");
if (options.coverage) {
engine.startService("codeCoverage");

View File

@ -169,15 +169,14 @@ class Engine {
this.registerModule('plugin_cmd', {embarkConfigFile: this.embarkConfig, embarkConfig: this.config.embarkConfig, packageFile: 'package.json'});
}
console(options) {
console(_options) {
this.registerModule('console', {
events: this.events,
plugins: this.plugins,
version: this.version,
ipc: this.ipc,
logger: this.logger,
config: this.config,
forceRegister: options.forceRegister
config: this.config
});
}

View File

@ -192,8 +192,8 @@ class BlockchainConnector {
}
_emitWeb3Ready() {
this.isWeb3Ready = true;
this.registerWeb3Object(() => {
this.isWeb3Ready = true;
this.events.emit(WEB3_READY);
});
this.subscribeToPendingTransactions();
@ -695,7 +695,7 @@ class BlockchainConnector {
registerWeb3Object(cb = () => {}) {
// doesn't feel quite right, should be a cmd or plugin method
// can just be a command without a callback
this.events.emit("runcode:register", "web3", this.web3, false, cb);
this.events.emit("runcode:register", "web3", this.web3, cb);
}
subscribeToPendingTransactions() {

View File

@ -1,6 +1,5 @@
const VM = require('./vm');
const fs = require('../../core/fs');
const deepEqual = require('deep-equal');
const EmbarkJS = require('embarkjs');
const IpfsApi = require("ipfs-api");
const Web3 = require('web3');
@ -45,39 +44,12 @@ class CodeRunner {
this.embark = embark;
this.commands = [];
this.registerIpcEvents();
this.IpcClientListen();
this.registerEvents();
this.registerCommands();
this.events.emit('runcode:ready');
this.ready = true;
}
registerIpcEvents() {
if (!this.ipc.isServer()) {
return;
}
this.ipc.on('runcode:getCommands', (_err, callback) => {
let result = {web3Config: this.vm.getWeb3Config(), commands: this.commands};
callback(null, result);
});
}
IpcClientListen() {
if (!this.ipc.isClient() || !this.ipc.connected) {
return;
}
this.ipc.listenTo('runcode:newCommand', (command) => {
if (command.varName) {
this.events.emit("runcode:register", command.varName, command.code);
} else {
this.events.request("runcode:eval", command.code);
}
});
}
registerEvents() {
this.events.on("runcode:register", this.registerVar.bind(this));
@ -133,37 +105,25 @@ class CodeRunner {
this.events.request("code-generator:embarkjs:init-provider-code", (providerCode) => {
this.evalCode(providerCode, (err, _result) => {
cb(err);
}, false, true);
}, true);
});
}, true);
});
}
registerVar(varName, code, toRecord = true, cb = () => {}) {
const command = {varName, code};
if (toRecord && !this.commands.some(cmd => deepEqual(cmd, command))) {
if (this.ipc.isServer()) {
this.commands.push(command);
this.ipc.broadcast("runcode:newCommand", command);
}
}
registerVar(varName, code, cb = () => {}) {
this.vm.registerVar(varName, code, cb);
}
evalCode(code, cb, isNotUserInput = false, tolerateError = false) {
evalCode(code, cb, tolerateError = false) {
cb = cb || function () {};
if (!code) return cb(null, '');
this.vm.doEval(code, tolerateError, (err, result) => {
if(err) {
if (err) {
return cb(err);
}
const command = {code};
if (isNotUserInput && this.ipc.isServer() && !this.commands.some(cmd => cmd.code === command.code)) {
this.commands.push(command);
this.ipc.broadcast("runcode:newCommand", command);
}
cb(null, result);
});

View File

@ -40,6 +40,8 @@ class VM {
"@babel/runtime-corejs2/core-js/object/assign",
"eth-ens-namehash",
"swarm-api",
"rxjs",
"rxjs/operators",
],
},
sandbox: { __dirname: fs.dappPath() },
@ -102,7 +104,9 @@ class VM {
if (error.message && error.message.indexOf(WEB3_INVALID_RESPONSE_ERROR) !== -1) {
error.message += ". Are you connected to an Ethereum node?";
}
if (typeof error === "string") {
error = new Error(error);
}
return cb(error);
}
return cb(null, result);
@ -151,27 +155,6 @@ class VM {
this.vm = new NodeVM(this.options);
cb();
}
/**
* Gets the registered @type {Web3} object, and returns an @type {object} with it's
* defaultAccount and provider URL.
* @typedef {getWeb3Config}
* @property {string} defaultAccount
* @property {string} providerUrl
* @returns {getWeb3Config} The configured values of the web3 object registered to this
* VM instance.
*/
public getWeb3Config() {
const Web3 = require("web3");
const provider = this.options.sandbox.web3.currentProvider;
let providerUrl;
if (provider instanceof Web3.providers.HttpProvider) {
providerUrl = provider.host;
} else if (provider instanceof Web3.providers.WebsocketProvider) {
providerUrl = provider.connection._url;
}
return { defaultAccount: this.options.sandbox.web3.eth.defaultAccount, providerUrl };
}
}
module.exports = VM;

View File

@ -26,9 +26,8 @@ class Console {
private config: any;
private history: string[];
private cmdHistoryFile: string;
private suggestions: Suggestions;
private suggestions?: Suggestions;
private providerReady: boolean;
private forceRegister: boolean;
constructor(embark: Embark, options: any) {
this.embark = embark;
@ -39,14 +38,22 @@ class Console {
this.fs = embark.fs;
this.ipc = options.ipc;
this.config = options.config;
this.forceRegister = options.forceRegister;
this.history = [];
this.cmdHistoryFile = options.cmdHistoryFile || this.fs.dappPath(".embark", "cmd_history");
this.providerReady = false;
this.loadHistory();
if (this.ipc.isServer()) {
this.ipc.on("console:executeCmd", this.executeCmd.bind(this));
this.ipc.on("console:executeCmd", (cmd: string, cb: any) => {
this.executeCmd(cmd, (err: string, result: any) => {
let error = null;
if (err) {
// reformat for IPC reply
error = { name: "Console error", message: err, stack: err };
}
cb(error, result);
});
});
this.ipc.on("console:history:save", true, (cmd: string) => {
this.saveHistory(cmd, true);
});
@ -59,6 +66,11 @@ class Console {
}
this.events.once("console:provider:done", cb);
});
this.registerConsoleCommands();
if (this.isEmbarkConsole) {
return;
}
this.registerEmbarkJs((err?: Error | null) => {
if (err) {
return this.logger.error(err);
@ -66,12 +78,15 @@ class Console {
this.providerReady = true;
this.events.emit("console:provider:done");
});
this.registerConsoleCommands();
this.registerApi();
this.suggestions = new Suggestions(embark, options);
}
private get isEmbarkConsole() {
return this.ipc.connected && this.ipc.isClient();
}
private cmdHistorySize() {
return env.anchoredValue(env.CMD_HISTORY_SIZE);
}
@ -165,14 +180,18 @@ class Console {
return callback(null, output);
}
try {
this.events.request("runcode:eval", cmd, callback);
} catch (e) {
if (this.ipc.connected && this.ipc.isClient()) {
return this.ipc.request("console:executeCmd", cmd, callback);
}
callback(e);
// if this is the embark console process, send the command to the process
// running all the needed services (ie the process running `embark run`)
if (this.isEmbarkConsole) {
return this.ipc.request("console:executeCmd", cmd, callback);
}
this.events.request("runcode:eval", cmd, (err: Error, result: any) => {
if (err) {
return callback(err.message);
}
callback(null, result);
}, true);
}
private registerEmbarkJs(cb: Callback<null>) {
@ -187,19 +206,19 @@ class Console {
// TODO add docs link to how to install one
this.logger.warn(__("If you did not install a blockchain connector, stop this process and install one"));
}, 5000);
this.events.once("blockchain:connector:ready", () => {
this.events.request("blockchain:connector:ready", () => {
clearTimeout(waitingForReady);
next();
});
},
(next: any) => {
if (this.isEmbarkConsole) {
return next();
}
this.events.request("runcode:blockchain:connected", next);
},
// for every other case (including when asked for force), get the embarkjs
// provider code and eval it in the VM (either main running VM or console VM
// in the secondary process)
(next: any) => {
if (this.ipc.connected && !this.forceRegister) {
if (this.isEmbarkConsole) {
return next();
}
const connectCode = `EmbarkJS.Blockchain.connectConsole((err) => {

View File

@ -92,7 +92,7 @@ export default class Suggestions {
}
return cb(this.searchSuggestions(cmd, suggestions));
}, false, true);
}, true);
} catch (e) {
}

View File

@ -211,8 +211,7 @@ class ContractDeployer {
self.events.emit("deploy:contract:deployed", contract);
self.events.request('code-generator:contract:custom', contract, (contractCode) => {
self.events.request('runcode:eval', contractCode, () => {}, true);
return callback();
self.events.request('runcode:eval', contractCode, callback);
});
}
@ -344,7 +343,7 @@ class ContractDeployer {
self.plugins.runActionsForEvent('deploy:contract:deployed', {contract: contract}, () => {
return next(null, receipt);
});
}, true);
});
});
}, hash => {
self.logFunction(contract)(__("deploying") + " " + contract.className.bold.cyan + " " + __("with").green + " " + contract.gas + " " + __("gas at the price of").green + " " + contract.gasPrice + " " + __("Wei, estimated cost:").green + " " + estimatedCost + " Wei".green + " (txHash: " + hash.bold.cyan + ")");

View File

@ -33,20 +33,6 @@ class Test {
(next) => {
this.events.request('runcode:ready', next);
},
(next) => {
this.initWeb3Provider(next);
},
(next) => {
const waitingForReady = setTimeout(() => {
this.logger.warn('Waiting for the blockchain connector to be ready...');
// TODO add docs link to how to install one
this.logger.warn('If you did not install a blockchain connector, stop this process and install one');
}, 5000);
this.events.request('blockchain:connector:ready', () => {
clearTimeout(waitingForReady);
next();
});
},
(next) => {
this.gasLimit = constants.tests.gasLimit;
this.events.request('deploy:setGasLimit', this.gasLimit);
@ -185,7 +171,9 @@ class Test {
return callback(err);
}
self.firstRunConfig = false;
self.events.request("runcode:embarkjs:reset", callback);
self.events.request("blockchain:ready", () => {
self.events.request("runcode:embarkjs:reset", callback);
});
});
});
}
@ -346,7 +334,7 @@ class Test {
Object.setPrototypeOf(self.embarkjs, embarkjs);
}
next(err, accounts);
}, true);
});
}
], function (err, accounts) {
if (err) {

View File

@ -38,19 +38,22 @@ module.exports = async (embark) => {
if (blockchainConnectorReady) {
return cb();
}
web3LocationPromise.then((_web3Location) => {
blockchainConnectorReady = true;
embark.events.emit('blockchain:connector:ready');
embark.events.once("blockchain:connector:ready", () => {
cb();
});
});
web3LocationPromise.then((_web3Location) => {
blockchainConnectorReady = true;
embark.events.emit('blockchain:connector:ready');
});
let web3Location = await web3LocationPromise;
web3Location = web3Location.replace(/\\/g, '/');
embark.events.emit('runcode:register', '__Web3', require(web3Location), false);
embark.events.emit('runcode:register', '__Web3', require(web3Location));
let code = `\nconst Web3 = global.__Web3 || require('${web3Location}');`;
code += `\nglobal.Web3 = Web3;`;
@ -60,7 +63,7 @@ module.exports = async (embark) => {
code += "\nEmbarkJS.Blockchain.registerProvider('web3', web3Connector);";
code += "\nEmbarkJS.Blockchain.setProvider('web3', {web3});";
code += "\nEmbarkJS.Blockchain.setProvider('web3', {});";
embark.addCodeToEmbarkJS(code);