diff --git a/lib/cmds/blockchain/miner.js b/lib/cmds/blockchain/miner.js index 2dedf4e3..16590a10 100644 --- a/lib/cmds/blockchain/miner.js +++ b/lib/cmds/blockchain/miner.js @@ -20,6 +20,7 @@ class GethMiner { this.config = {}; self.interval = null; self.callback = null; + self.started = null; self.commandQueue = async.queue((task, callback) => { self.callback = callback; @@ -67,10 +68,11 @@ class GethMiner { }); if (this.config.mine_normally) { - return this.sendCommand(minerStart); + this.startMiner(); + return; } - self.sendCommand(minerStop, () => { + self.stopMiner(() => { self.fundAccount(function (err) { if (err) { console.error(err); @@ -89,11 +91,34 @@ class GethMiner { params = []; } if (!callback) { - callback = function() {}; + callback = function () { + }; } this.commandQueue.push({method, params: params || []}, callback); } + startMiner(callback) { + if (this.started) { + if (callback) { + callback(); + } + return; + } + this.started = true; + this.sendCommand(minerStart, callback); + } + + stopMiner(callback) { + if (!this.started) { + if (callback) { + callback(); + } + return; + } + this.started = false; + this.sendCommand(minerStop, callback); + } + getCoinbase(callback) { if (this.coinbase) { return callback(null, this.coinbase); @@ -120,17 +145,16 @@ class GethMiner { if (err) { return callback(err); } - callback(null, result >= self.config.initial_ether); + callback(null, parseInt(result, 16) >= self.config.initial_ether); }); }); } - watchBlocks(filterCommand, callback) { + watchBlocks(filterCommand, callback, delay) { const self = this; self.sendCommand(filterCommand, (err, filterId) => { if (err) { - callback(err); - return; + return callback(err); } self.interval = setInterval(() => { self.sendCommand(getChanges, [filterId], (err, changes) => { @@ -143,13 +167,13 @@ class GethMiner { } callback(null, changes); }); - }, 1000); + }, delay || 1000); }); } mineUntilFunded(callback) { const self = this; - this.sendCommand(minerStart); + this.startMiner(); self.watchBlocks(newBlockFilter, (err) => { if (err) { console.error(err); @@ -158,7 +182,7 @@ class GethMiner { self.accountFunded((err, funded) => { if (funded) { clearTimeout(self.interval); - self.sendCommand(minerStop); + self.stopMiner(); callback(); } }); @@ -198,7 +222,7 @@ class GethMiner { let timeout_set = false; let next_block_in_ms; - self.sendCommand(minerStart); + self.startMiner(); self.watchBlocks(newBlockFilter, (err) => { if (err) { console.error(err); @@ -234,7 +258,7 @@ class GethMiner { } else { next_block_in_ms = (self.config.interval_ms - ms_since_block); } - self.sendCommand(minerStop); + self.stopMiner(); console.log("== Looking for next block in " + next_block_in_ms + "ms"); next(); }, @@ -242,8 +266,7 @@ class GethMiner { setTimeout(function () { console.log("== Looking for next block"); timeout_set = false; - //miner_obj.start(config.threads); - self.sendCommand(minerStart); + self.startMiner(); next(); }, next_block_in_ms); } @@ -260,6 +283,7 @@ class GethMiner { start_transaction_mining() { const self = this; + const pendingTrasactionsMessage = "== Pending transactions! Looking for next block..."; self.watchBlocks(pendingBlockFilter, (err) => { if (err) { console.error(err); @@ -268,11 +292,10 @@ class GethMiner { self.sendCommand(getHashRate, (err, result) => { if (result > 0) return; - console.log("== Pending transactions! Looking for next block..."); - self.sendCommand(minerStart); + console.log(pendingTrasactionsMessage); + self.startMiner(); }); - }); - + }, 2000); if (self.config.mine_periodically) return; @@ -288,10 +311,13 @@ class GethMiner { } if (!count) { console.log("== No transactions left. Stopping miner..."); - self.sendCommand(minerStop); + self.stopMiner(); + } else { + console.log(pendingTrasactionsMessage); + self.startMiner(); } }); - }); + }, 2000); } } diff --git a/lib/core/proxy.js b/lib/core/proxy.js new file mode 100644 index 00000000..888790c2 --- /dev/null +++ b/lib/core/proxy.js @@ -0,0 +1,124 @@ +const httpProxy = require('http-proxy'); +const http = require('http'); +const constants = require('../constants.json'); + +let commList = {}; +let transactions = {}; +let receipts = {}; + +const parseRequest = function(reqBody){ + let jsonO; + try { + jsonO = JSON.parse(reqBody); + } catch(e){ + return; // Request is not a json. Do nothing + } + if(jsonO.method === "eth_sendTransaction"){ + commList[jsonO.id] = { + type: 'contract-log', + address: jsonO.params[0].to, + data: jsonO.params[0].data + }; + } else if(jsonO.method === "eth_getTransactionReceipt"){ + if(transactions[jsonO.params[0]]){ + transactions[jsonO.params[0]].receiptId = jsonO.id; + receipts[jsonO.id] = transactions[jsonO.params[0]].commListId; + } + } +}; + +const parseResponse = function(ipc, resBody){ + let jsonO; + try { + jsonO = JSON.parse(resBody); + } catch(e) { + return; // Response is not a json. Do nothing + } + + if(commList[jsonO.id]){ + commList[jsonO.id].transactionHash = jsonO.result; + transactions[jsonO.result] = {commListId: jsonO.id}; + } else if(receipts[jsonO.id] && jsonO.result && jsonO.result.blockNumber){ + commList[receipts[jsonO.id]].blockNumber = jsonO.result.blockNumber; + commList[receipts[jsonO.id]].gasUsed = jsonO.result.gasUsed; + commList[receipts[jsonO.id]].status = jsonO.result.status; + + if(ipc.connected && !ipc.connecting){ + ipc.request('log', commList[receipts[jsonO.id]]); + } else { + ipc.connecting = true; + ipc.connect(() => { + ipc.connecting = false; + }); + } + + delete transactions[commList[receipts[jsonO.id]].transactionHash]; + delete receipts[jsonO.id]; + delete commList[jsonO.id]; + } +}; + +exports.serve = function(ipc, host, port, ws){ + let proxy = httpProxy.createProxyServer({ + target: { + host, + port: port + constants.blockchain.servicePortOnProxy + }, + ws: ws + }); + + proxy.on('error', function () { + console.error(__("Error forwarding requests to blockchain/simulator")); + }); + + proxy.on('proxyRes', (proxyRes) => { + let resBody = []; + proxyRes.on('data', (b) => resBody.push(b)); + proxyRes.on('end', function () { + resBody = Buffer.concat(resBody).toString(); + if(resBody){ + parseResponse(ipc, resBody); + } + }); + }); + + let server = http.createServer((req, res) => { + let reqBody = []; + req.on('data', (b) => { reqBody.push(b); }) + .on('end', () => { + reqBody = Buffer.concat(reqBody).toString(); + if(reqBody){ + parseRequest(reqBody); + } + }); + + if(!ws){ + proxy.web(req, res); + } + }); + + if(ws){ + const WsParser = require('simples/lib/parsers/ws'); // npm install simples + + server.on('upgrade', function (req, socket, head) { + proxy.ws(req, socket, head); + }); + + proxy.on('open', (proxySocket) => { + proxySocket.on('data', (data) => { + parseResponse(ipc, data.toString().substr(data.indexOf("{"))); + }); + }); + + proxy.on('proxyReqWs', (proxyReq, req, socket) => { + var parser = new WsParser(0, false); + socket.pipe(parser); + parser.on('frame', function (frame) { + parseRequest(frame.data); + }); + + }); + } + + server.listen(port); +};