diff --git a/lib/node.js b/lib/node.js index bff0496..08f68dd 100644 --- a/lib/node.js +++ b/lib/node.js @@ -1,6 +1,7 @@ 'use strict'; var web3 = require('web3'); +var async = require('async'); var _ = require('lodash'); var os = require('os'); var shelljs = require('shelljs'); @@ -78,6 +79,7 @@ function Node() }; this._lastStats = JSON.stringify(this.stats); + this._lastFetch = 0; this._tries = 0; this._down = 0; @@ -97,7 +99,7 @@ function Node() this.pingInterval = false; this.connectionInterval = false; - this._lastLatestLog = 0; + this._lastChainLog = 0; this._lastPendingLog = 0; this._called = 0 @@ -175,9 +177,10 @@ Node.prototype.setupSockets = function() .on('ready', function() { self._socket = true; - self.sendUpdate(true); - console.info('==> The connection has been established.'); + + self.updateBlock(); + self.update(true); }) .on('data', function incoming(data) { @@ -214,7 +217,7 @@ Node.prototype.setupSockets = function() }) .on('reconnecting', function reconnecting(opts) { - console.warn('We are scheduling a reconnect operation', opts); + console.warn('!!!', 'We are scheduling a reconnect operation', opts); }); } @@ -231,36 +234,26 @@ Node.prototype.emit = function(message, payload) } } -Node.prototype.isActive = function() +Node.prototype.setInactive = function() { - this._tries++; - - try { - var peers = web3.toDecimal(web3.net.peerCount); - - if(peers !== null) - { - this.stats.peers = peers; - this.stats.active = true; - - return true; - } - } - catch (err) { - console.error("peerCount:", err); - } - this.stats.active = false; - this.stats.listening = false; - this.stats.mining = false; this.stats.peers = 0; + this.stats.pending = 0; + this.stats.mining = false; + this.stats.hashrate = 0; + this.stats.gasPrice = 0; + this.stats.miner = false; this._down++; - return false; + return this; } Node.prototype.getInfo = function() { + var start = _.now(); + + console.info('==>', 'Getting info'); + try { this.info.coinbase = web3.eth.coinbase; this.info.node = web3.version.client; @@ -268,7 +261,8 @@ Node.prototype.getInfo = function() this.info.protocol = web3.toDecimal(web3.version.ethereum); this.info.api = web3.version.api; - console.info(this.info); + console.info('==>', 'Got info in', _.now() - start, 'ms'); + console.info(' i ', this.info); return true; } @@ -285,7 +279,8 @@ Node.prototype.getBlock = function(number) number: 0, hash: '?', difficulty: 0, - timestamp: 0 + timestamp: 0, + miner: '' }; if( _.isUndefined(number) ) @@ -294,30 +289,16 @@ Node.prototype.getBlock = function(number) try { block = web3.eth.getBlock(number, true); - if( block.hash != '?' && !_.isUndefined(block.difficulty) ) + if( block.hash != '?' && !_.isUndefined(block.difficulty) && !_.isUndefined(block.totalDifficulty) ) { block.difficulty = web3.toDecimal( block.difficulty ); + block.totalDifficulty = web3.toDecimal( block.totalDifficulty ); } } catch (err) { console.error("getBlock(" + number + "):", err); - if(number > 0) - { - try { - number--; - - block = web3.eth.getBlock(number, true); - - if(block.hash !== '?' && !_.isUndefined(block.difficulty) ) - { - block.difficulty = web3.toDecimal( block.difficulty ); - } - } - catch (err) { - console.error("getBlock(" + number + "):", err); - } - } + return false; } return block; @@ -325,26 +306,26 @@ Node.prototype.getBlock = function(number) Node.prototype.getMinerName = function(miner) { - var result = _.find(this._knownMiners, {miner: miner}); + var result = _.find(this._knownMiners, { miner: miner }); - if(result !== undefined) + if (result !== undefined) { return result.name; } else { - if(this._Registrar !== null) + if (this._Registrar !== null) { var name = this._Registrar.name(miner); if(name.length > 0) { - this._knownMiners.push({miner: miner, name: name}); + this._knownMiners.push({ miner: miner, name: name }); return name; } } - this._knownMiners.push({miner: miner, name: false}); + this._knownMiners.push({ miner: miner, name: false }); } return false; @@ -355,74 +336,182 @@ Node.prototype.uptime = function() this.stats.uptime = ((this._tries - this._down) / this._tries) * 100; } -Node.prototype.getStats = function() +Node.prototype.getStats = function(forced) +{ + var self = this; + var now = _.now(); + var lastFetchAgo = now - this._lastFetch; + this._lastFetch = now; + + if (this._socket) + this._lastStats = JSON.stringify(this.stats); + + if (this._web3 && (lastFetchAgo >= UPDATE_INTERVAL || forced === true)) + { + console.log('==> Getting stats; last update:', lastFetchAgo, '- forced:', (forced === true)); + + async.parallel({ + start: function (callback) + { + callback(null, _.now()); + }, + peers: function (callback) + { + async.nextTick(function () { + var peers; + + try { + peers = web3.toDecimal(web3.net.peerCount); + } + catch (err) { + console.error('xx> PeerCount failed: ', err); + callback(err, null); + } + + callback(null, peers); + }); + }, + pending: function (callback) + { + async.nextTick(function () { + try { + web3.eth.getBlockTransactionCount('pending', callback); + } + catch (err) { + console.error('xx> Pending failed: ', err); + callback(err, null); + } + }); + }, + mining: function (callback) + { + async.nextTick(function () { + var mining; + + try { + mining = web3.eth.mining; + } + catch (err) { + console.error('xx> Mining failed: ', err); + callback(err, null); + } + + callback(null, mining); + }); + }, + hashrate: function (callback) + { + async.nextTick(function () { + var hashrate; + + try { + hashrate = web3.eth.hashrate; + } + catch (err) { + console.error('xx> Hashrate failed: ', err); + callback(err, null); + } + + callback(null, hashrate); + }); + }, + gasPrice: function (callback) + { + async.nextTick(function () { + var gasPrice; + + try { + gasPrice = web3.toBigNumber(web3.eth.gasPrice).toString(10); + } + catch (err) { + console.error('xx> gasPrice failed: ', err); + callback(err, null); + } + + callback(null, gasPrice); + }); + }, + minerName: function (callback) + { + async.nextTick(function () { + var minerName; + + try { + minerName = self.getMinerName(self.stats.block.miner); + } + catch (err) { + console.error('xx> minerName failed: ', err); + callback(err, null); + } + + callback(null, minerName); + }); + } + }, + function (err, results) + { + self._tries++; + + if (err) { + console.error('xx> getStats error: ', err); + + self.setInactive(); + + return false; + } + + results.end = _.now(); + results.diff = results.end - results.start; + + // console.log('==> Got getStats results: ', results); + console.log('==> Got getStats results in', results.diff, 'ms'); + + if(results.peers !== null) + { + self.stats.active = true; + self.stats.peers = results.peers; + self.stats.pending = results.pending; + self.stats.mining = results.mining; + self.stats.hashrate = results.hashrate; + self.stats.gasPrice = results.gasPrice; + } + else { + self.setInactive(); + } + + self.uptime(); + + self.sendUpdate(); + }); + } +} + +Node.prototype.getStatsBlock = function () { - var start = _.now(); - console.log('==> statsstart: ', start); if(this._socket) this._lastStats = JSON.stringify(this.stats); if(this._web3) { - var blockStart = _.now(); - console.log('==> blockstart: ', blockStart); - console.log('==> blockstart diff: ', (blockStart - start)); + var start = _.now(); var block = this.getBlock(); - var blockEnd = _.now(); - console.log('==> blockend: ', blockEnd); - console.log('==> block diff: ', (blockEnd - blockStart)); + var end = _.now(); if( !_.isUndefined(block) && !_.isUndefined(block.number) && !_.isUndefined(block.hash) && block.hash !== '?' ) { + console.log("==> Got block:", block.number, 'in', end - start, 'ms'); this.stats.block = block; - this.sendUpdate(); - - this.isActive(); - - if(PENDING_WORKS) { - try - { - this.stats.pending = web3.eth.getBlockTransactionCount('pending'); - } - catch (err) - { - PENDING_WORKS = false; - console.error("getBlockTransactionCount('pending'):", err); - } - } - - this.stats.mining = web3.eth.mining; - - if(this.stats.mining) - { - try { - this.stats.hashrate = web3.eth.hashrate; - } - catch (err) - { - console.error('hashrate: ', err); - this.stats.hashrate = 0; - } - } - else - this.stats.hashrate = 0; - - this.stats.gasPrice = web3.toBigNumber(web3.eth.gasPrice).toString(10); } else { - console.error("getStats: couldn't fetch block..."); + console.error("xx> getStatsBlock: couldn't fetch block..."); } } - this.uptime(); - - var end = _.now(); - console.log('==> end: ', end); - console.log('==> stats diff: ', (end - start)); + this.sendUpdate(); } -Node.prototype.getHistory = function(range) +Node.prototype.getHistory = function (range) { var history = []; var interv = {}; @@ -443,7 +532,7 @@ Node.prototype.getHistory = function(range) }; } - for(var i = interv.min; i <= interv.max; i++) + for (var i = interv.min; i <= interv.max; i++) { var block = this.getBlock(( !_.isUndefined(range.list) ? range.list[i] : i)); @@ -456,21 +545,28 @@ Node.prototype.getHistory = function(range) return history.reverse(); } -Node.prototype.changed = function() +Node.prototype.updateBlock = function() +{ + this.getStatsBlock(); + + return this; +}; + +Node.prototype.update = function(forced) +{ + this.getStats(forced); + + return this; +}; + +Node.prototype.changed = function () { var changed = ! _.isEqual( this._lastStats, JSON.stringify(this.stats) ); - if(this._tries - this._lastSent > 5) - { - this._lastSent = this._tries; - - return true; - } - return changed; } -Node.prototype.prepareStats = function() +Node.prototype.prepareStats = function () { return { id: this.id, @@ -478,36 +574,12 @@ Node.prototype.prepareStats = function() }; } -Node.prototype.sendUpdate = function(force) +Node.prototype.sendUpdate = function (force) { if( this.changed() || force ) this.emit('update', this.prepareStats()); } -Node.prototype.update = function() -{ - this.getStats(); - - this.sendUpdate(); - - return this.stats; -}; - -Node.prototype.updatePending = function() -{ - if(PENDING_WORKS) - { - try { - this.stats.pending = web3.eth.getBlockTransactionCount('pending'); - this.sendUpdate(); - } - catch (err) { - PENDING_WORKS = false; - console.error("getBlockTransactionCount('pending'):", err); - } - } -} - Node.prototype.ping = function() { this._latency = _.now(); @@ -519,25 +591,52 @@ Node.prototype.setWatches = function() var self = this; try { - this.pendingFilter = web3.eth.filter('chain'); + this.chainFilter = web3.eth.filter('chain'); + this.chainFilter.watch( function (log) + { + var now = _.now(); + var time = now - self._lastChainLog; + self._lastChainLog = now; + + console.log('>>> Chain Filter triggered: ', now, '- last trigger:', time); + + if(time > 50) + { + self.updateBlock(); + } + else + { + debounce(function() { + self.updateBlock(); + }, 50); + } + }); + } + catch (err) + { + console.error("Couldn't set up chain filter"); + console.error(err); + } + + try { + this.pendingFilter = web3.eth.filter('pending'); this.pendingFilter.watch( function (log) { - if(PENDING_WORKS) { - var now = _.now(); - var time = now - self._lastPendingLog; + var now = _.now(); + var time = now - self._lastPendingLog; + self._lastPendingLog = now; - if(time > 50) - { - self.update(); - } - else - { - debounce(function() { - self.updatePending(); - }, 50); - } + console.log('>>> Pending Filter triggered: ', now, '- last trigger:', time); - self._lastPendingLog = now; + if(time > 50) + { + self.update(true); + } + else + { + debounce(function() { + self.update(true); + }, 50); } }); } @@ -558,24 +657,34 @@ Node.prototype.setWatches = function() Node.prototype.installContract = function() { + var start = _.now(); + try { Contract = web3.eth.contract( registrar.desc ); this._Registrar = new Contract( registrar.address ); + + console.log('==>', 'Installed Registrar contract in', _.now() - start, 'ms'); } catch (err) { - console.error("Couldn't set up registrar contract"); + console.error("!!!", "Couldn't set up registrar contract"); console.error(err); } } Node.prototype.init = function() { + // Fetch node info this.getInfo(); - this.startSocketConnection(); + + // Install Registrar contract this.installContract(); + + // Start socket connection + this.startSocketConnection(); + + // Set filters this.setWatches(); - this.update(); } Node.prototype.stop = function() @@ -583,7 +692,6 @@ Node.prototype.stop = function() if(this._socket) socket.end(); - if(this.updateInterval) clearInterval(this.updateInterval); diff --git a/package.json b/package.json index 9478efa..9d630b2 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "lib": "./lib" }, "dependencies": { + "async": "^0.9.0", "debounce": "1.0.0", "debug": "2.1.3", "lodash": "3.7.0",