diff --git a/gulpfile.js b/gulpfile.js index 1ec73adea..59006cf5a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -7,7 +7,7 @@ var packager = require('electron-packager'); var spawn = require('child_process').spawn; var merge = require('merge-stream'); var rename = require("gulp-rename"); -var download = require('gulp-download'); +var download = require('gulp-download-stream'); var decompress = require('gulp-decompress'); var tap = require("gulp-tap"); // const zip = require('gulp-zip'); diff --git a/interface/client/templates/popupWindows/splashScreen.html b/interface/client/templates/popupWindows/splashScreen.html index 1cf165530..008408034 100644 --- a/interface/client/templates/popupWindows/splashScreen.html +++ b/interface/client/templates/popupWindows/splashScreen.html @@ -21,7 +21,9 @@

{{#if TemplateVar.get "showProgressBar"}} - + {{#if TemplateVar.get "showStateProgressBar"}} + + {{/if}} {{/if}} \ No newline at end of file diff --git a/interface/client/templates/popupWindows/splashScreen.js b/interface/client/templates/popupWindows/splashScreen.js index b588ce249..8d47c9e0e 100644 --- a/interface/client/templates/popupWindows/splashScreen.js +++ b/interface/client/templates/popupWindows/splashScreen.js @@ -100,8 +100,10 @@ Template['popupWindows_splashScreen'].onCreated(function(){ if(web3.net.peerCount > 0) { // Check which state we are - if ( lastSyncData.pulledStates != Math.round(lastSyncData._displayState) - || lastSyncData.knownStates != Math.round(lastSyncData._displayKnownStates)) { + if ( 0 < lastSyncData._displayKnownStates && ( + lastSyncData.pulledStates != Math.round(lastSyncData._displayState) + || lastSyncData.knownStates != Math.round(lastSyncData._displayKnownStates)) + ) { // Mostly downloading new states translationString = 'mist.startScreen.nodeSyncInfoStates'; @@ -181,16 +183,13 @@ Template['popupWindows_splashScreen'].helpers({ if (!(syncData._displayBlock > -1)) { // initialize the display numbers syncData._displayBlock = Number(syncData.currentBlock); - syncData._displayState = Number(syncData.pulledStates); - syncData._displayKnownStates = Number(syncData.knownStates); - + syncData._displayState = Number(syncData.pulledStates || 0); + syncData._displayKnownStates = Number(syncData.knownStates || 0); } else { // Increment each them slowly to match target number - syncData._displayBlock = syncData._displayBlock + (Number(syncData.currentBlock) - syncData._displayBlock) / 10; - - syncData._displayState = syncData._displayState + (Number(syncData.pulledStates) - syncData._displayState) / 10; - - syncData._displayKnownStates = syncData._displayKnownStates + (Number(syncData.knownStates) - syncData._displayKnownStates) / 10; + syncData._displayBlock += (Number(syncData.currentBlock) - syncData._displayBlock) / 10; + syncData._displayState += (Number(syncData.pulledStates || 0) - syncData._displayState) / 10; + syncData._displayKnownStates += (Number(syncData.knownStates || 0) - syncData._displayKnownStates) / 10; }; // Create the fancy strings @@ -202,7 +201,10 @@ Template['popupWindows_splashScreen'].helpers({ var translatedMessage = TAPi18n.__(translationString, syncData); // Calculates both progress bars - var stateProgress = (lastSyncData._displayState / lastSyncData._displayKnownStates) * 100; + var stateProgress = null; + if (0 < lastSyncData._displayKnownStates) { + stateProgress = (lastSyncData._displayState / lastSyncData._displayKnownStates) * 100; + } var progress = ((lastSyncData._displayBlock - Number(lastSyncData.startingBlock)) / (Number(lastSyncData._highestBlock) - Number(lastSyncData.startingBlock))) * 100 ; @@ -214,7 +216,10 @@ Template['popupWindows_splashScreen'].helpers({ if(_.isFinite(progress)) { TemplateVar.set(template, 'showProgressBar', true); TemplateVar.set(template, 'progress', progress); - TemplateVar.set(template, 'stateProgress', stateProgress); + if (null !== stateProgress) { + TemplateVar.set(template, 'showStateProgressBar', true); + TemplateVar.set(template, 'stateProgress', stateProgress); + } } }, 100); diff --git a/main.js b/main.js index 239f57446..c6d844573 100644 --- a/main.js +++ b/main.js @@ -144,20 +144,14 @@ app.on('before-quit', function(event){ // delay quit, so the sockets can close setTimeout(function(){ - killedSockets = true; - ethereumNode.stop().then(function() { + killedSockets = true; app.quit(); }); }, 500); }); - - -const NODE_TYPE = 'geth'; - - var mainWindow; var splashWindow; diff --git a/modules/ethereumNode.js b/modules/ethereumNode.js index cc4b31696..8cf2a3f8f 100644 --- a/modules/ethereumNode.js +++ b/modules/ethereumNode.js @@ -24,7 +24,7 @@ const DEFAULT_NETWORK = 'main'; const UNABLE_TO_BIND_PORT_ERROR = 'unableToBindPort'; const UNABLE_TO_SPAWN_ERROR = 'unableToSpan'; const PASSWORD_WRONG_ERROR = 'badPassword'; - +const NODE_START_WAIT_MS = 3000; /** @@ -143,7 +143,7 @@ class EthereumNode extends EventEmitter { return this._start(this.defaultNodeType, this.defaultNetwork) .catch((err) => { log.error('Failed to start node', err); - + throw err; }); }); @@ -289,8 +289,8 @@ class EthereumNode extends EventEmitter { this._saveUserData('network', this._network); return this._socket.connect({ path: ipcPath }, { - timeout: 30000 /* 30s */ - }) + timeout: 30000 /* 30s */ + }) .then(() => { this.state = STATES.CONNECTED; }) @@ -336,60 +336,8 @@ class EthereumNode extends EventEmitter { log.debug(`Start node using ${binPath}`); return new Q((resolve, reject) => { - if ('eth' === nodeType) { - let modalWindow = Windows.createPopup('unlockMasterPassword', { - electronOptions: { - width: 400, - height: 220, - }, - useWeb3: false, - }); - - let called = false; - - modalWindow.on('closed', () => { - if (!called) { - app.quit(); - } - }); - - let popupCallback = function(err) { - if (err && _.get(modalWindow,'webContents')) { - log.error('unlockMasterPassword error', err); - - if(UNABLE_TO_SPAWN_ERROR === err) { - modalWindow.close(); - modalWindow = null; - } else { - modalWindow.webContents.send('data', { - masterPasswordWrong: true - }); - } - } else { - called = true; - modalWindow.close(); - modalWindow = null; - ipc.removeAllListeners('backendAction_unlockedMasterPassword'); - } - }; - - ipc.on('backendAction_unlockedMasterPassword', (ev, err, pw) => { - if (_.get(modalWindow, 'webContents') && ev.sender.getId() === modalWindow.webContents.getId()) { - if (!err) { - this.__startProcess(nodeType, network, binPath, pw, popupCallback) - .then(resolve, reject); - } else { - app.quit(); - } - - return; - } - }); - } else { - this.__startProcess(nodeType, network, binPath) - .then(resolve, reject); - } - + this.__startProcess(nodeType, network, binPath) + .then(resolve, reject); }); } @@ -397,7 +345,7 @@ class EthereumNode extends EventEmitter { /** * @return {Promise} */ - __startProcess (nodeType, network, binPath, pw, popupCallback) { + __startProcess (nodeType, network, binPath) { return new Q((resolve, reject) => { log.trace('Rotate log file'); @@ -421,8 +369,7 @@ class EthereumNode extends EventEmitter { else { args = (nodeType === 'geth') ? ['--fast', '--cache', '512'] - : ['--unsafe-transactions', '--master', pw]; - pw = null; + : ['--unsafe-transactions']; } let nodeOptions = Settings.nodeOptions; @@ -442,10 +389,6 @@ class EthereumNode extends EventEmitter { if (STATES.STARTING === this.state) { this.state = STATES.ERROR; - if (popupCallback) { - popupCallback(UNABLE_TO_SPAWN_ERROR); - } - log.info('Node startup error'); // TODO: detect this properly @@ -455,17 +398,6 @@ class EthereumNode extends EventEmitter { } }); - // node quit, e.g. master pw wrong - proc.once('exit', () => { - if ('eth' === nodeType) { - log.warn('Password wrong!'); - - if (popupCallback) { - popupCallback(PASSWORD_WRONG_ERROR); - } - } - }); - // we need to read the buff to prevent node from not working proc.stderr.pipe( fs.createWriteStream(this._buildFilePath('node.log'), { flags: 'a' }) @@ -510,20 +442,17 @@ class EthereumNode extends EventEmitter { // when data is first received this.once('data', () => { /* - Assume startup succeeded after 5 seconds. At this point - IPC connections are usually possible. + We wait a short while before marking startup as successful + because we may want to parse the initial node output for + errors, etc (see geth port-binding error above) */ setTimeout(() => { if (STATES.STARTING === this.state) { - log.info('4s elapsed, assuming node started up successfully'); - - if (popupCallback) { - popupCallback(); - } + log.info(`${NODE_START_WAIT_MS}ms elapsed, assuming node started up successfully`); resolve(proc); } - }, 4000); + }, NODE_START_WAIT_MS); }) }); }); @@ -571,8 +500,8 @@ class EthereumNode extends EventEmitter { _loadDefaults () { log.trace('Load defaults'); - this.defaultNodeType = this._loadUserData('node') || DEFAULT_NODE_TYPE; - this.defaultNetwork = this._loadUserData('network') || DEFAULT_NETWORK; + this.defaultNodeType = Settings.nodeType || this._loadUserData('node') || DEFAULT_NODE_TYPE; + this.defaultNetwork = Settings.network || this._loadUserData('network') || DEFAULT_NETWORK; } diff --git a/modules/settings.js b/modules/settings.js index 4b0072115..ee166be02 100644 --- a/modules/settings.js +++ b/modules/settings.js @@ -27,6 +27,24 @@ const argv = require('yargs') type: 'string', group: 'Mist options:', }, + node: { + demand: false, + default: null, + describe: 'Node to use: geth, eth', + requiresArg: true, + nargs: 1, + type: 'string', + group: 'Mist options:', + }, + network: { + demand: false, + default: null, + describe: 'Network to connect to: main, test', + requiresArg: true, + nargs: 1, + type: 'string', + group: 'Mist options:', + }, ipcpath: { demand: false, describe: 'Path to node IPC socket file (this will automatically get passed as an option to Geth).', @@ -162,6 +180,14 @@ class Settings { return argv.ipcpath; } + get nodeType () { + return argv.node; + } + + get network () { + return argv.network; + } + get nodeOptions () { return argv.nodeOptions; } diff --git a/modules/sockets.js b/modules/sockets.js index 4d14010bd..895948adf 100644 --- a/modules/sockets.js +++ b/modules/sockets.js @@ -10,6 +10,9 @@ const dechunker = require('./ipc/dechunker.js'); +const CONNECT_INTERVAL_MS = 1000; +const CONNECT_TIMEOUT_MS = 3000; + /** * Socket connecting to Ethereum Node. @@ -41,51 +44,66 @@ class Socket extends EventEmitter { * Connect to host. * @param {Object} connectConfig * @param {Object} [options] - * @param {Number} [options.timeout] Milliseconds to wait before timeout. + * @param {Number} [options.timeout] Milliseconds to wait before timeout (default is 5000). * @return {Promise} */ connect (connectConfig, options) { this._log.info(`Connect to ${JSON.stringify(connectConfig)}`); - options = options || {}; + options = _.extend({ + timeout: CONNECT_TIMEOUT_MS, + }, options); return this._resetSocket() .then(() => { + let connectTimerId = null; + let timeoutTimerId = null; + this._log.debug('Connecting...'); + this._log.debug(`Will wait ${options.timeout}ms for connection to happen.`); + this._state = STATE.CONNECTING; return new Q((resolve, reject) => { this._socket.once('connect', () => { - this._log.info('Connected!'); + if (STATE.CONNECTING === this._state) { + this._log.info('Connected!'); - this._state = STATE.CONNECTED; + this._state = STATE.CONNECTED; - this.emit('connect'); + clearTimeout(connectTimerId); + clearTimeout(timeoutTimerId); - resolve(); + this.emit('connect'); + + resolve(); + } }); - this._socket.once('error', (err) => { + this._socket.on('error', (err) => { if (STATE.CONNECTING === this._state) { - this._log.error('Connection error', err); - - this._state = STATE.ERROR; + this._log.warn(`Connection failed, retrying after ${CONNECT_INTERVAL_MS}ms...`); - return reject(new Error(`Unable to connect to socket: ${err.message}`)); + connectTimerId = setTimeout(() => { + this._socket.connect(connectConfig); + }, CONNECT_INTERVAL_MS); } }); - if (options.timeout) { - this._log.debug(`Will wait ${options.timeout}ms for connection to happen.`); + timeoutTimerId = setTimeout(() => { + if (STATE.CONNECTING === this._state) { + this._log.error(`Connection failed (${options.timeout}ms elapsed)`); - setTimeout(() => { - if (STATE.CONNECTING === this._state) { - this._socket.emit('error', `Connection timeout (took longer than ${options.timeout} ms)`); - } - }, options.timeout); - } + this._state = STATE.CONNECTION_TIMEOUT; + + clearTimeout(connectTimerId); + + return reject(new Error(`Unable to connect to socket: timeout`)); + } + }, options.timeout); + // initial kick-off this._socket.connect(connectConfig); }); }); @@ -388,6 +406,7 @@ const STATE = Socket.STATE = { DISCONNECTED: 4, ERROR: -1, DISCONNECTION_TIMEOUT: -2, + CONNECTION_TIMEOUT: -3, }; diff --git a/modules/windows.js b/modules/windows.js index 3adf9fdb8..7caa82297 100644 --- a/modules/windows.js +++ b/modules/windows.js @@ -177,11 +177,12 @@ class Windows { alwaysOnTop: true, resizable: false, width: 100, - height: 50, + height: 80, center: true, frame: false, useContentSize: true, titleBarStyle: 'hidden', //hidden-inset: more space + skipTaskbar: true }, }); diff --git a/package.json b/package.json index 915a67fac..0c04fce9b 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "electron-packager": "^7.0.4", "gulp": "^3.9.0", "gulp-decompress": "^2.0.0", - "gulp-download": "0.0.1", + "gulp-download-stream": "0.0.13", "gulp-rename": "^1.2.2", "gulp-replace": "^0.5.4", "gulp-tap": "^0.1.3",