From ae8a59cf9b72475595d2a59b9c35cb8cf84f834c Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Wed, 19 Sep 2018 19:47:38 -0500 Subject: [PATCH 01/13] timer utility --- lib/utils/utils.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/utils/utils.js b/lib/utils/utils.js index 293c06b5c..1eacdc2ac 100644 --- a/lib/utils/utils.js +++ b/lib/utils/utils.js @@ -490,6 +490,10 @@ function errorMessage(e) { return e; } +async function timer(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + module.exports = { joinPath, dirname, @@ -532,5 +536,6 @@ module.exports = { sample, last, interceptLogs, - errorMessage + errorMessage, + timer }; From 3846db79f76ac30061055ce7a813aae164f90067 Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Wed, 19 Sep 2018 19:48:00 -0500 Subject: [PATCH 02/13] make sure port is an integer, port var should hold offset port value --- lib/modules/blockchain_process/simulator.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/modules/blockchain_process/simulator.js b/lib/modules/blockchain_process/simulator.js index 6e0e848cf..ab415968f 100644 --- a/lib/modules/blockchain_process/simulator.js +++ b/lib/modules/blockchain_process/simulator.js @@ -30,8 +30,9 @@ class Simulator { let useProxy = this.blockchainConfig.proxy || false; let host = (dockerHostSwap(options.host || this.blockchainConfig.rpcHost) || defaultHost); let port = (options.port || this.blockchainConfig.rpcPort || 8545); + port = parseInt(port) + (useProxy ? constants.blockchain.servicePortOnProxy : 0); - cmds.push("-p " + (port + (useProxy ? constants.blockchain.servicePortOnProxy : 0))); + cmds.push("-p " + port); cmds.push("-h " + host); cmds.push("-a " + (options.numAccounts || 10)); cmds.push("-e " + (options.defaultBalance || 100)); From b9975668b83d3375e14f67f531695fb7b2000c8b Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Wed, 19 Sep 2018 19:59:59 -0500 Subject: [PATCH 03/13] proxy should attempt to wait on target --- lib/modules/blockchain_process/proxy.js | 27 ++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/modules/blockchain_process/proxy.js b/lib/modules/blockchain_process/proxy.js index 8779bae75..fc731b4fd 100644 --- a/lib/modules/blockchain_process/proxy.js +++ b/lib/modules/blockchain_process/proxy.js @@ -1,6 +1,7 @@ const httpProxy = require('http-proxy'); const http = require('http'); const constants = require('../../constants.json'); +const utils = require('../../utils/utils'); let commList = {}; let transactions = {}; @@ -63,7 +64,31 @@ const parseResponse = function (ipc, resBody) { } }; -exports.serve = function (ipc, host, port, ws) { +exports.serve = async function (ipc, host, port, ws, origin) { + const _origin = origin ? origin.split(',')[0] : void 0; + const start = Date.now(); + + function awaitTarget() { + return new Promise((resolve, reject) => { + utils.pingEndpoint( + canonicalHost(host), port, ws ? 'ws': false, 'http', _origin, async (err) => { + if (!err || (Date.now() - start > 10000)) { + // if (Date.now() - start > 10000) { + // console.warn('!!! TIMEOUT !!!'); + // } else { + // console.warn('!!! CONNECT !!!'); + // } + return resolve(); + } + // console.warn('!!! WAITING !!!'); + await utils.timer(250).then(awaitTarget).then(resolve); + } + ); + }); + } + + await awaitTarget(); + let proxy = httpProxy.createProxyServer({ target: { host: canonicalHost(host), From 35c772d727a8288cd688c332d74eaabd5cdba9cf Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Wed, 19 Sep 2018 20:10:46 -0500 Subject: [PATCH 04/13] async setupProxy --- lib/modules/blockchain_process/blockchain.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/modules/blockchain_process/blockchain.js b/lib/modules/blockchain_process/blockchain.js index 90e4a1619..4cb42eaad 100644 --- a/lib/modules/blockchain_process/blockchain.js +++ b/lib/modules/blockchain_process/blockchain.js @@ -93,13 +93,17 @@ Blockchain.prototype.initProxy = function() { this.config.wsPort += constants.blockchain.servicePortOnProxy; }; -Blockchain.prototype.setupProxy = function() { +Blockchain.prototype.setupProxy = async function() { const proxy = require('./proxy'); const Ipc = require('../../core/ipc'); let ipcObject = new Ipc({ipcRole: 'client'}); - this.rpcProxy = proxy.serve(ipcObject, this.config.rpcHost, this.config.rpcPort, false); - this.wsProxy = proxy.serve(ipcObject, this.config.wsHost, this.config.wsPort, true); + const [rpcProxy, wsProxy] = await Promise.all([ + proxy.serve(ipcObject, this.config.rpcHost, this.config.rpcPort, false), + proxy.serve(ipcObject, this.config.wsHost, this.config.wsPort, true, this.config.wsOrigins) + ]); + this.rpcProxy = rpcProxy; + this.wsProxy = wsProxy; }; Blockchain.prototype.shutdownProxy = function() { From aae795402049fbe49511a17cd98e7abae0455fbb Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Wed, 19 Sep 2018 20:11:36 -0500 Subject: [PATCH 05/13] await proxy just before createFundAndUnlockAccounts --- lib/modules/blockchain_process/blockchain.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/modules/blockchain_process/blockchain.js b/lib/modules/blockchain_process/blockchain.js index 4cb42eaad..50fe067f7 100644 --- a/lib/modules/blockchain_process/blockchain.js +++ b/lib/modules/blockchain_process/blockchain.js @@ -153,13 +153,10 @@ Blockchain.prototype.run = function() { }, function getMainCommand(next) { self.client.mainCommand(address, function(cmd, args) { - if (self.config.proxy) { - self.setupProxy(); - } next(null, cmd, args); }, true); } - ], function (err, cmd, args) { + ], async function (err, cmd, args) { if (err) { console.error(err.message); return; @@ -182,10 +179,13 @@ Blockchain.prototype.run = function() { console.error(`Geth error: ${data}`); }); // Geth logs appear in stderr somehow - self.child.stderr.on('data', (data) => { + self.child.stderr.on('data', async (data) => { data = data.toString(); if (!self.readyCalled && data.indexOf('WebSocket endpoint opened') > -1) { if (self.isDev) { + if (self.config.proxy) { + await self.setupProxy(); + } self.createFundAndUnlockAccounts((err) => { // TODO: this is never called! if(err) console.error('Error creating, unlocking, and funding accounts', err); From fa274675a546212c5668b48985e635f358d624be Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Wed, 19 Sep 2018 21:11:55 -0500 Subject: [PATCH 06/13] timer function doesn't need to be async, since it returns a promise --- lib/utils/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils/utils.js b/lib/utils/utils.js index 1eacdc2ac..436d48310 100644 --- a/lib/utils/utils.js +++ b/lib/utils/utils.js @@ -490,8 +490,8 @@ function errorMessage(e) { return e; } -async function timer(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); +function timer(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); } module.exports = { From 1408abf8d97cc9781d0cd37095b30ff1b5440ef5 Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Wed, 19 Sep 2018 21:12:20 -0500 Subject: [PATCH 07/13] supply radix --- lib/modules/blockchain_process/simulator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/blockchain_process/simulator.js b/lib/modules/blockchain_process/simulator.js index ab415968f..4bb0ade6e 100644 --- a/lib/modules/blockchain_process/simulator.js +++ b/lib/modules/blockchain_process/simulator.js @@ -30,7 +30,7 @@ class Simulator { let useProxy = this.blockchainConfig.proxy || false; let host = (dockerHostSwap(options.host || this.blockchainConfig.rpcHost) || defaultHost); let port = (options.port || this.blockchainConfig.rpcPort || 8545); - port = parseInt(port) + (useProxy ? constants.blockchain.servicePortOnProxy : 0); + port = parseInt(port, 10) + (useProxy ? constants.blockchain.servicePortOnProxy : 0); cmds.push("-p " + port); cmds.push("-h " + host); From 2b816af9a8769c4c8043acbcde0c66572272f063 Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Wed, 19 Sep 2018 21:12:37 -0500 Subject: [PATCH 08/13] eslint doesn't like `void 0` here --- lib/modules/blockchain_process/proxy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/blockchain_process/proxy.js b/lib/modules/blockchain_process/proxy.js index fc731b4fd..f310990cd 100644 --- a/lib/modules/blockchain_process/proxy.js +++ b/lib/modules/blockchain_process/proxy.js @@ -65,7 +65,7 @@ const parseResponse = function (ipc, resBody) { }; exports.serve = async function (ipc, host, port, ws, origin) { - const _origin = origin ? origin.split(',')[0] : void 0; + const _origin = origin ? origin.split(',')[0] : undefined; const start = Date.now(); function awaitTarget() { From e19b3b2f514f45bff6611c7b050847703293b190 Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Wed, 19 Sep 2018 21:13:06 -0500 Subject: [PATCH 09/13] simplify syntax --- lib/modules/blockchain_process/proxy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/blockchain_process/proxy.js b/lib/modules/blockchain_process/proxy.js index f310990cd..ea81f1436 100644 --- a/lib/modules/blockchain_process/proxy.js +++ b/lib/modules/blockchain_process/proxy.js @@ -69,7 +69,7 @@ exports.serve = async function (ipc, host, port, ws, origin) { const start = Date.now(); function awaitTarget() { - return new Promise((resolve, reject) => { + return new Promise(resolve => { utils.pingEndpoint( canonicalHost(host), port, ws ? 'ws': false, 'http', _origin, async (err) => { if (!err || (Date.now() - start > 10000)) { From 917757b04c9a7dfaf23a7316d7f3ea958680e172 Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Wed, 19 Sep 2018 21:13:19 -0500 Subject: [PATCH 10/13] outer function doesn't need to be async --- lib/modules/blockchain_process/blockchain.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/blockchain_process/blockchain.js b/lib/modules/blockchain_process/blockchain.js index 50fe067f7..144d0516b 100644 --- a/lib/modules/blockchain_process/blockchain.js +++ b/lib/modules/blockchain_process/blockchain.js @@ -156,7 +156,7 @@ Blockchain.prototype.run = function() { next(null, cmd, args); }, true); } - ], async function (err, cmd, args) { + ], function (err, cmd, args) { if (err) { console.error(err.message); return; From eba9ce361e4ebccf860304a7013dc50cf1d682e6 Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Wed, 19 Sep 2018 22:31:14 -0500 Subject: [PATCH 11/13] setupProxy should not be conditional on isDev --- lib/modules/blockchain_process/blockchain.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/modules/blockchain_process/blockchain.js b/lib/modules/blockchain_process/blockchain.js index 144d0516b..6bca2825b 100644 --- a/lib/modules/blockchain_process/blockchain.js +++ b/lib/modules/blockchain_process/blockchain.js @@ -182,10 +182,10 @@ Blockchain.prototype.run = function() { self.child.stderr.on('data', async (data) => { data = data.toString(); if (!self.readyCalled && data.indexOf('WebSocket endpoint opened') > -1) { + if (self.config.proxy) { + await self.setupProxy(); + } if (self.isDev) { - if (self.config.proxy) { - await self.setupProxy(); - } self.createFundAndUnlockAccounts((err) => { // TODO: this is never called! if(err) console.error('Error creating, unlocking, and funding accounts', err); From c477445896084fce5405315fe8a9609ce63899a3 Mon Sep 17 00:00:00 2001 From: emizzle Date: Thu, 20 Sep 2018 15:08:02 +1000 Subject: [PATCH 12/13] Start HTTP and WS proxies individually As geth is starting up, the output is monitored for endpoint info. Once the HTTP or WS endpoints are opened, the corresponding proxies are started. This is more maintainable in the long run in case the geth process being started does not allow for rpc or websockets, or geth modifies the order in which endpoint are opened. --- lib/modules/blockchain_process/blockchain.js | 27 ++++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/lib/modules/blockchain_process/blockchain.js b/lib/modules/blockchain_process/blockchain.js index 6bca2825b..fa013e21a 100644 --- a/lib/modules/blockchain_process/blockchain.js +++ b/lib/modules/blockchain_process/blockchain.js @@ -18,6 +18,7 @@ var Blockchain = function(options) { this.isDev = options.isDev; this.onReadyCallback = options.onReadyCallback || (() => {}); this.onExitCallback = options.onExitCallback; + this.proxyIpc = null; if ((this.blockchainConfig === {} || JSON.stringify(this.blockchainConfig) === '{"enabled":true}') && this.env !== 'development') { console.log("===> " + __("warning: running default config on a non-development environment")); @@ -93,17 +94,18 @@ Blockchain.prototype.initProxy = function() { this.config.wsPort += constants.blockchain.servicePortOnProxy; }; -Blockchain.prototype.setupProxy = async function() { +Blockchain.prototype.setupProxy = async function(type) { const proxy = require('./proxy'); const Ipc = require('../../core/ipc'); - let ipcObject = new Ipc({ipcRole: 'client'}); - const [rpcProxy, wsProxy] = await Promise.all([ - proxy.serve(ipcObject, this.config.rpcHost, this.config.rpcPort, false), - proxy.serve(ipcObject, this.config.wsHost, this.config.wsPort, true, this.config.wsOrigins) - ]); - this.rpcProxy = rpcProxy; - this.wsProxy = wsProxy; + if(!this.proxyIpc) this.proxyIpc = new Ipc({ipcRole: 'client'}); + + if (type === 'rpc') { + this.rpcProxy = await proxy.serve(this.proxyIpc, this.config.rpcHost, this.config.rpcPort, false); + } + else if (type === 'ws'){ + this.wsProxy = await proxy.serve(this.proxyIpc, this.config.wsHost, this.config.wsPort, true, this.config.wsOrigins); + } }; Blockchain.prototype.shutdownProxy = function() { @@ -111,8 +113,8 @@ Blockchain.prototype.shutdownProxy = function() { return; } - this.rpcProxy.close(); - this.wsProxy.close(); + if(this.rpcProxy) this.rpcProxy.close(); + if(this.wsProxy) this.wsProxy.close(); }; Blockchain.prototype.runCommand = function(cmd, options, callback) { @@ -181,9 +183,12 @@ Blockchain.prototype.run = function() { // Geth logs appear in stderr somehow self.child.stderr.on('data', async (data) => { data = data.toString(); + if (!self.readyCalled && data.indexOf('HTTP endpoint opened') > -1 && self.config.proxy) { + await self.setupProxy('rpc'); + } if (!self.readyCalled && data.indexOf('WebSocket endpoint opened') > -1) { if (self.config.proxy) { - await self.setupProxy(); + await self.setupProxy('ws'); } if (self.isDev) { self.createFundAndUnlockAccounts((err) => { From 907b486531f0a4397bcc06b5ab1706f9bf677363 Mon Sep 17 00:00:00 2001 From: Anthony Laibe Date: Thu, 20 Sep 2018 10:16:48 +0100 Subject: [PATCH 13/13] Send ready only when the proxy is started --- lib/modules/blockchain_process/blockchain.js | 37 +++++++++++++------- lib/modules/blockchain_process/proxy.js | 13 +++---- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/lib/modules/blockchain_process/blockchain.js b/lib/modules/blockchain_process/blockchain.js index fa013e21a..26e03bf60 100644 --- a/lib/modules/blockchain_process/blockchain.js +++ b/lib/modules/blockchain_process/blockchain.js @@ -94,18 +94,21 @@ Blockchain.prototype.initProxy = function() { this.config.wsPort += constants.blockchain.servicePortOnProxy; }; -Blockchain.prototype.setupProxy = async function(type) { +Blockchain.prototype.setupProxy = async function() { const proxy = require('./proxy'); const Ipc = require('../../core/ipc'); if(!this.proxyIpc) this.proxyIpc = new Ipc({ipcRole: 'client'}); - if (type === 'rpc') { - this.rpcProxy = await proxy.serve(this.proxyIpc, this.config.rpcHost, this.config.rpcPort, false); - } - else if (type === 'ws'){ - this.wsProxy = await proxy.serve(this.proxyIpc, this.config.wsHost, this.config.wsPort, true, this.config.wsOrigins); + let wsProxy; + if(this.config.wsRPC) { + wsProxy = proxy.serve(this.proxyIpc, this.config.wsHost, this.config.wsPort, true, this.config.wsOrigins); } + + [this.rpcProxy, this.wsProxy] = await Promise.all([ + proxy.serve(this.proxyIpc, this.config.rpcHost, this.config.rpcPort, false), + wsProxy + ]); }; Blockchain.prototype.shutdownProxy = function() { @@ -180,25 +183,33 @@ Blockchain.prototype.run = function() { self.child.stdout.on('data', (data) => { console.error(`Geth error: ${data}`); }); + let httpReady = false; + let wsReady = !self.config.wsRPC; // Geth logs appear in stderr somehow self.child.stderr.on('data', async (data) => { data = data.toString(); - if (!self.readyCalled && data.indexOf('HTTP endpoint opened') > -1 && self.config.proxy) { - await self.setupProxy('rpc'); + if (data.indexOf('HTTP endpoint opened') > -1) { + httpReady = true; } - if (!self.readyCalled && data.indexOf('WebSocket endpoint opened') > -1) { - if (self.config.proxy) { - await self.setupProxy('ws'); - } + + if (data.indexOf('WebSocket endpoint opened') > -1) { + wsReady = true; + } + + if (!self.readyCalled && wsReady && httpReady) { + self.readyCalled = true; if (self.isDev) { self.createFundAndUnlockAccounts((err) => { // TODO: this is never called! if(err) console.error('Error creating, unlocking, and funding accounts', err); }); } - self.readyCalled = true; + if (self.config.proxy) { + await self.setupProxy(); + } self.readyCallback(); } + console.log('Geth: ' + data); }); self.child.on('exit', (code) => { diff --git a/lib/modules/blockchain_process/proxy.js b/lib/modules/blockchain_process/proxy.js index ea81f1436..fa9dff2fb 100644 --- a/lib/modules/blockchain_process/proxy.js +++ b/lib/modules/blockchain_process/proxy.js @@ -73,14 +73,8 @@ exports.serve = async function (ipc, host, port, ws, origin) { utils.pingEndpoint( canonicalHost(host), port, ws ? 'ws': false, 'http', _origin, async (err) => { if (!err || (Date.now() - start > 10000)) { - // if (Date.now() - start > 10000) { - // console.warn('!!! TIMEOUT !!!'); - // } else { - // console.warn('!!! CONNECT !!!'); - // } return resolve(); } - // console.warn('!!! WAITING !!!'); await utils.timer(250).then(awaitTarget).then(resolve); } ); @@ -152,6 +146,9 @@ exports.serve = async function (ipc, host, port, ws, origin) { }); } const listenPort = port - constants.blockchain.servicePortOnProxy; - server.listen(listenPort, defaultHost); - return server; + return new Promise(resolve => { + server.listen(listenPort, defaultHost, () => { + resolve(server); + }); + }); };