2014-12-04 22:35:09 +01:00
|
|
|
var geoip = require('geoip-lite');
|
2015-04-17 11:10:20 +02:00
|
|
|
var _ = require('lodash');
|
2014-12-04 22:35:09 +01:00
|
|
|
|
2015-04-28 09:43:56 +02:00
|
|
|
var MAX_HISTORY = 40;
|
2015-05-11 21:10:19 +02:00
|
|
|
var MAX_INACTIVE_TIME = 1000*60*60*4;
|
2015-04-06 16:08:36 +02:00
|
|
|
|
2015-06-03 02:51:19 +02:00
|
|
|
var Node = function(data)
|
2015-01-20 19:29:31 +01:00
|
|
|
{
|
2015-02-17 03:04:15 +01:00
|
|
|
this.id = null;
|
|
|
|
this.info = {};
|
|
|
|
this.geo = {}
|
|
|
|
this.stats = {
|
2015-01-20 19:29:31 +01:00
|
|
|
active: false,
|
|
|
|
mining: false,
|
2015-06-01 23:04:34 +02:00
|
|
|
hashrate: 0,
|
2015-02-17 03:04:15 +01:00
|
|
|
peers: 0,
|
|
|
|
pending: 0,
|
|
|
|
gasPrice: 0,
|
2015-02-17 10:30:41 +01:00
|
|
|
block: {
|
|
|
|
number: 0,
|
2015-06-03 02:51:19 +02:00
|
|
|
hash: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
|
|
difficulty: 0,
|
|
|
|
totalDifficulty: 0,
|
2015-02-17 10:30:41 +01:00
|
|
|
gasLimit: 0,
|
2015-02-23 18:10:17 +01:00
|
|
|
timestamp: 0,
|
2015-04-29 07:49:43 +02:00
|
|
|
time: 0,
|
2015-02-23 18:10:17 +01:00
|
|
|
arrival: 0,
|
2015-03-27 20:05:43 +01:00
|
|
|
received: 0,
|
2015-06-03 02:51:19 +02:00
|
|
|
propagation: 0,
|
2015-04-24 07:37:03 +02:00
|
|
|
transactions: [],
|
|
|
|
uncles: []
|
2015-02-17 10:30:41 +01:00
|
|
|
},
|
2015-04-28 17:22:11 +02:00
|
|
|
propagationAvg: 0,
|
2015-02-23 14:57:41 +01:00
|
|
|
latency: 0,
|
2015-06-03 02:51:19 +02:00
|
|
|
uptime: 100
|
2015-02-17 03:04:15 +01:00
|
|
|
};
|
2015-04-29 07:49:43 +02:00
|
|
|
|
2015-04-17 11:10:20 +02:00
|
|
|
this.history = new Array(MAX_HISTORY);
|
2015-04-29 07:49:43 +02:00
|
|
|
|
2015-03-27 11:28:55 +01:00
|
|
|
this.uptime = {
|
|
|
|
started: null,
|
2015-06-03 02:51:19 +02:00
|
|
|
up: 0,
|
|
|
|
down: 0,
|
|
|
|
lastStatus: null,
|
|
|
|
lastUpdate: null
|
2015-03-27 11:28:55 +01:00
|
|
|
};
|
|
|
|
|
2015-04-29 07:49:43 +02:00
|
|
|
this.init(data);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node.prototype.init = function(data)
|
|
|
|
{
|
2015-04-17 11:10:20 +02:00
|
|
|
_.fill(this.history, -1);
|
2015-04-07 19:02:17 +02:00
|
|
|
|
2015-06-03 02:51:19 +02:00
|
|
|
if( this.id === null && this.uptime.started === null )
|
2015-03-27 11:28:55 +01:00
|
|
|
this.setState(true);
|
2015-01-20 19:29:31 +01:00
|
|
|
|
2015-06-03 02:51:19 +02:00
|
|
|
this.id = _.result(data, 'id', this.id);
|
2015-02-18 07:06:41 +01:00
|
|
|
|
2015-04-29 07:49:43 +02:00
|
|
|
if( !_.isUndefined(data.latency) )
|
2015-02-23 15:20:05 +01:00
|
|
|
this.stats.latency = data.latency;
|
|
|
|
|
2015-06-03 02:51:19 +02:00
|
|
|
this.setInfo(data);
|
2015-01-20 19:29:31 +01:00
|
|
|
}
|
|
|
|
|
2015-02-17 10:30:41 +01:00
|
|
|
Node.prototype.setInfo = function(data)
|
|
|
|
{
|
2015-04-29 07:49:43 +02:00
|
|
|
if( !_.isUndefined(data.info) )
|
|
|
|
{
|
2015-02-17 10:30:41 +01:00
|
|
|
this.info = data.info;
|
|
|
|
|
2015-06-03 02:51:19 +02:00
|
|
|
if( !_.isUndefined(data.info.canUpdateHistory) )
|
|
|
|
{
|
|
|
|
this.info.canUpdateHistory = _.result(data, 'info.canUpdateHistory', false);
|
|
|
|
}
|
2015-04-28 09:43:56 +02:00
|
|
|
}
|
|
|
|
|
2015-04-29 07:49:43 +02:00
|
|
|
if( !_.isUndefined(data.ip) )
|
|
|
|
{
|
2015-06-03 02:51:19 +02:00
|
|
|
this.setGeo(data.ip);
|
2015-02-17 10:30:41 +01:00
|
|
|
}
|
2015-02-18 07:06:41 +01:00
|
|
|
|
2015-06-03 02:51:19 +02:00
|
|
|
this.spark = _.result(data, 'spark', null);
|
2015-04-29 07:49:43 +02:00
|
|
|
|
2015-06-03 02:51:19 +02:00
|
|
|
this.setState(true);
|
2015-04-29 07:49:43 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 02:51:19 +02:00
|
|
|
Node.prototype.setGeo = function(ip)
|
2015-04-29 07:49:43 +02:00
|
|
|
{
|
2015-06-03 02:51:19 +02:00
|
|
|
this.info.ip = ip;
|
|
|
|
this.geo = geoip.lookup(ip);
|
2015-02-17 10:30:41 +01:00
|
|
|
}
|
|
|
|
|
2015-02-17 06:12:44 +01:00
|
|
|
Node.prototype.getInfo = function()
|
|
|
|
{
|
2015-04-29 07:49:43 +02:00
|
|
|
return {
|
|
|
|
id: this.id,
|
|
|
|
info: this.info,
|
2015-06-03 02:51:19 +02:00
|
|
|
stats: {
|
|
|
|
active: this.stats.active,
|
|
|
|
mining: this.stats.mining,
|
|
|
|
hashrate: this.stats.hashrate,
|
|
|
|
peers: this.stats.peers,
|
|
|
|
gasPrice: this.stats.gasPrice,
|
|
|
|
block: this.stats.block,
|
|
|
|
propagationAvg: this.stats.propagationAvg,
|
|
|
|
uptime: this.stats.uptime,
|
|
|
|
latency: this.stats.latency,
|
|
|
|
pending: this.stats.pending,
|
|
|
|
},
|
|
|
|
history: this.history,
|
|
|
|
geo: this.geo
|
2015-04-29 07:49:43 +02:00
|
|
|
};
|
2015-02-17 06:12:44 +01:00
|
|
|
}
|
|
|
|
|
2015-04-17 11:10:20 +02:00
|
|
|
Node.prototype.setStats = function(stats, history)
|
2015-03-27 10:44:07 +01:00
|
|
|
{
|
2015-06-03 02:51:19 +02:00
|
|
|
if( !_.isUndefined(stats) )
|
2015-03-27 10:44:07 +01:00
|
|
|
{
|
2015-06-03 02:51:19 +02:00
|
|
|
this.setBlock( _.result(stats, 'block', this.stats.block), history );
|
2015-04-28 17:22:11 +02:00
|
|
|
|
2015-06-03 02:51:19 +02:00
|
|
|
this.setBasicStats(stats);
|
2015-04-28 17:22:11 +02:00
|
|
|
|
2015-06-03 02:51:19 +02:00
|
|
|
this.setPending( _.result(stats, 'pending', this.stats.pending) );
|
2015-03-27 10:44:07 +01:00
|
|
|
|
|
|
|
return this.getStats();
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-06-01 21:51:31 +02:00
|
|
|
Node.prototype.setBlock = function(block, history)
|
|
|
|
{
|
2015-06-03 02:51:19 +02:00
|
|
|
if( !_.isUndefined(block) && !_.isUndefined(block.number) && ( !_.isEqual(history, this.history) || !_.isEqual(block, this.stats.block) ))
|
2015-06-01 21:51:31 +02:00
|
|
|
{
|
2015-06-03 02:51:19 +02:00
|
|
|
if(block.number !== this.stats.block.number && block.hash !== this.stats.block.hash)
|
|
|
|
{
|
|
|
|
this.stats.block = block;
|
|
|
|
}
|
2015-06-01 21:51:31 +02:00
|
|
|
|
2015-06-03 02:51:19 +02:00
|
|
|
this.setHistory(history);
|
2015-06-01 21:51:31 +02:00
|
|
|
|
|
|
|
return this.getBlockStats();
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-06-03 02:51:19 +02:00
|
|
|
Node.prototype.setHistory = function(history)
|
|
|
|
{
|
|
|
|
if( _.isEqual(history, this.history) )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !_.isArray(history) )
|
|
|
|
{
|
|
|
|
this.history = _.fill( new Array(MAX_HISTORY), -1 );
|
|
|
|
this.stats.propagationAvg = 0;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.history = history;
|
|
|
|
|
|
|
|
var positives = _.filter(history, function(p) {
|
|
|
|
return p >= 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
this.stats.propagationAvg = ( positives.length > 0 ? Math.round( _.sum(positives) / positives.length ) : 0 );
|
|
|
|
positives = null;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-01 20:42:50 +02:00
|
|
|
Node.prototype.setPending = function(stats)
|
|
|
|
{
|
2015-06-03 02:51:19 +02:00
|
|
|
if( !_.isUndefined(stats) && !_.isUndefined(stats.pending) && !_.isEqual(stats.pending, this.stats.pending))
|
2015-06-01 20:42:50 +02:00
|
|
|
{
|
|
|
|
this.stats.pending = stats.pending;
|
|
|
|
|
|
|
|
return {
|
|
|
|
id: this.id,
|
|
|
|
pending: this.stats.pending
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-06-01 23:04:34 +02:00
|
|
|
Node.prototype.setBasicStats = function(stats)
|
|
|
|
{
|
2015-06-03 02:51:19 +02:00
|
|
|
if( !_.isUndefined(stats) && !_.isEqual(stats, {
|
|
|
|
active: this.stats.active,
|
|
|
|
mining: this.stats.mining,
|
|
|
|
hashrate: this.stats.hashrate,
|
|
|
|
peers: this.stats.peers,
|
|
|
|
gasPrice: this.stats.gasPrice,
|
|
|
|
uptime: this.stats.uptime
|
|
|
|
}) )
|
2015-06-01 23:04:34 +02:00
|
|
|
{
|
|
|
|
this.stats.active = stats.active;
|
|
|
|
this.stats.mining = stats.mining;
|
|
|
|
this.stats.hashrate = stats.hashrate;
|
|
|
|
this.stats.peers = stats.peers;
|
|
|
|
this.stats.gasPrice = stats.gasPrice;
|
|
|
|
this.stats.uptime = stats.uptime;
|
|
|
|
|
|
|
|
return this.getBasicStats();
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-04-03 06:00:06 +02:00
|
|
|
Node.prototype.setLatency = function(latency)
|
|
|
|
{
|
2015-06-03 02:51:19 +02:00
|
|
|
if( !_.isUndefined(latency) && !_.isEqual(latency, this.stats.latency) )
|
2015-04-03 06:00:06 +02:00
|
|
|
{
|
|
|
|
this.stats.latency = latency;
|
|
|
|
|
2015-04-29 07:49:43 +02:00
|
|
|
return {
|
|
|
|
id: this.id,
|
|
|
|
latency: latency
|
|
|
|
};
|
2015-04-03 06:00:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-02-17 06:12:44 +01:00
|
|
|
Node.prototype.getStats = function()
|
|
|
|
{
|
2015-04-29 07:49:43 +02:00
|
|
|
return {
|
|
|
|
id: this.id,
|
2015-06-03 02:51:19 +02:00
|
|
|
stats: {
|
|
|
|
active: this.stats.active,
|
|
|
|
mining: this.stats.mining,
|
|
|
|
hashrate: this.stats.hashrate,
|
|
|
|
peers: this.stats.peers,
|
|
|
|
gasPrice: this.stats.gasPrice,
|
|
|
|
block: this.stats.block,
|
|
|
|
propagationAvg: this.stats.propagationAvg,
|
|
|
|
uptime: this.stats.uptime,
|
|
|
|
pending: this.stats.pending
|
|
|
|
},
|
2015-04-29 07:49:43 +02:00
|
|
|
history: this.history
|
|
|
|
};
|
2015-02-17 06:12:44 +01:00
|
|
|
}
|
|
|
|
|
2015-06-01 21:51:31 +02:00
|
|
|
Node.prototype.getBlockStats = function()
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
id: this.id,
|
|
|
|
block: this.stats.block,
|
|
|
|
propagationAvg: this.stats.propagationAvg,
|
|
|
|
history: this.history
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2015-06-01 23:04:34 +02:00
|
|
|
Node.prototype.getBasicStats = function()
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
id: this.id,
|
|
|
|
stats: {
|
|
|
|
active: this.stats.active,
|
|
|
|
mining: this.stats.mining,
|
|
|
|
hashrate: this.stats.hashrate,
|
|
|
|
peers: this.stats.peers,
|
|
|
|
gasPrice: this.stats.gasPrice,
|
|
|
|
uptime: this.stats.uptime
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2015-03-27 11:28:55 +01:00
|
|
|
Node.prototype.setState = function(active)
|
|
|
|
{
|
2015-06-03 02:51:19 +02:00
|
|
|
var now = _.now();
|
|
|
|
|
|
|
|
if(this.uptime.started !== null)
|
|
|
|
{
|
|
|
|
if(this.uptime.lastStatus === active)
|
|
|
|
{
|
|
|
|
this.uptime[(active ? 'up' : 'down')] += now - this.uptime.lastUpdate;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.uptime[(active ? 'down' : 'up')] += now - this.uptime.lastUpdate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.uptime.started = now;
|
|
|
|
}
|
|
|
|
|
2015-03-27 11:28:55 +01:00
|
|
|
this.stats.active = active;
|
2015-06-03 02:51:19 +02:00
|
|
|
this.uptime.lastStatus = active;
|
|
|
|
this.uptime.lastUpdate = now;
|
|
|
|
|
|
|
|
this.stats.uptime = this.calculateUptime();
|
|
|
|
|
|
|
|
now = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node.prototype.calculateUptime = function()
|
|
|
|
{
|
|
|
|
if(this.uptime.lastUpdate === this.uptime.started)
|
|
|
|
{
|
|
|
|
return 100;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Math.round( this.uptime.up / (this.uptime.lastUpdate - this.uptime.started) * 100);
|
2015-03-27 11:28:55 +01:00
|
|
|
}
|
|
|
|
|
2015-04-29 01:33:49 +02:00
|
|
|
Node.prototype.getBlockNumber = function()
|
|
|
|
{
|
|
|
|
return this.stats.block.number;
|
|
|
|
}
|
|
|
|
|
2015-04-28 09:43:56 +02:00
|
|
|
Node.prototype.canUpdate = function()
|
|
|
|
{
|
|
|
|
return this.info.canUpdateHistory || false;
|
|
|
|
}
|
|
|
|
|
2015-05-11 20:41:37 +02:00
|
|
|
Node.prototype.isInactiveAndOld = function()
|
|
|
|
{
|
2015-06-03 02:51:19 +02:00
|
|
|
if( this.uptime.lastStatus === false && this.uptime.lastUpdate !== null && (_.now() - this.uptime.lastUpdate) > MAX_INACTIVE_TIME )
|
2015-05-11 20:41:37 +02:00
|
|
|
{
|
2015-06-03 02:51:19 +02:00
|
|
|
return true;
|
2015-05-11 20:41:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-02-11 11:56:33 +01:00
|
|
|
module.exports = Node;
|