ethstats-server/lib/node.js

398 lines
7.5 KiB
JavaScript
Raw Normal View History

2014-12-04 22:35:09 +01:00
var geoip = require('geoip-lite');
2015-04-17 11:10:20 +02:00
var _ = require('lodash');
2015-07-30 23:13:11 +02:00
var trusted = require('./utils/config').trusted;
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;
2015-06-09 18:28:29 +02:00
this.trusted = false;
2015-02-17 03:04:15 +01:00
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
},
2016-02-01 22:15:51 +01:00
syncing: false,
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-09 01:40:59 +02:00
this.setInfo(data, null);
2015-01-20 19:29:31 +01:00
}
2015-06-09 01:40:59 +02:00
Node.prototype.setInfo = function(data, callback)
2015-02-17 10:30:41 +01:00
{
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-07-29 19:15:45 +02:00
if( trusted.indexOf(data.ip) >= 0 || process.env.LITE === 'true')
2015-06-09 18:28:29 +02:00
{
this.trusted = true;
}
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-06-09 01:40:59 +02:00
if(callback !== null)
{
callback(null, this.getInfo());
}
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
{
if (ip.substr(0, 7) == "::ffff:") {
ip = ip.substr(7)
}
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-06-09 01:40:59 +02:00
Node.prototype.getInfo = function(callback)
2015-02-17 06:12:44 +01:00
{
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,
2016-02-01 22:15:51 +01:00
syncing: this.stats.syncing,
2015-06-03 02:51:19 +02:00
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-06-09 01:40:59 +02:00
Node.prototype.setStats = function(stats, history, callback)
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-09 01:40:59 +02:00
this.setBlock( _.result(stats, 'block', this.stats.block), history, function (err, block) {} );
2015-04-28 17:22:11 +02:00
2015-06-09 01:40:59 +02:00
this.setBasicStats(stats, function (err, stats) {});
2015-04-28 17:22:11 +02:00
2015-06-09 01:40:59 +02:00
this.setPending( _.result(stats, 'pending', this.stats.pending), function (err, stats) {} );
2015-03-27 10:44:07 +01:00
2015-06-09 01:40:59 +02:00
callback(null, this.getStats());
2015-03-27 10:44:07 +01:00
}
2015-06-09 01:40:59 +02:00
callback('Stats undefined', null);
2015-03-27 10:44:07 +01:00
}
2015-06-09 01:40:59 +02:00
Node.prototype.setBlock = function(block, history, callback)
2015-06-01 21:51:31 +02:00
{
2015-06-09 01:40:59 +02:00
if( !_.isUndefined(block) && !_.isUndefined(block.number) )
2015-06-01 21:51:31 +02:00
{
2015-06-09 01:40:59 +02:00
if ( !_.isEqual(history, this.history) || !_.isEqual(block, this.stats.block) )
2015-06-03 02:51:19 +02:00
{
2015-06-09 01:40:59 +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-09 01:40:59 +02:00
this.setHistory(history);
2015-06-01 21:51:31 +02:00
2015-06-09 01:40:59 +02:00
callback(null, this.getBlockStats());
}
else
{
callback(null, null);
}
}
else
{
callback('Block undefined', null);
2015-06-01 21:51:31 +02:00
}
}
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-09 01:40:59 +02:00
Node.prototype.setPending = function(stats, callback)
2015-06-01 20:42:50 +02:00
{
2015-06-09 01:40:59 +02:00
if( !_.isUndefined(stats) && !_.isUndefined(stats.pending))
2015-06-01 20:42:50 +02:00
{
2015-06-09 01:40:59 +02:00
if(!_.isEqual(stats.pending, this.stats.pending))
{
this.stats.pending = stats.pending;
2015-06-01 20:42:50 +02:00
2015-06-09 01:40:59 +02:00
callback(null, {
id: this.id,
pending: this.stats.pending
});
}
else
{
callback(null, null);
}
}
else
{
callback('Stats undefined', null);
2015-06-01 20:42:50 +02:00
}
}
2015-06-09 01:40:59 +02:00
Node.prototype.setBasicStats = function(stats, callback)
2015-06-01 23:04:34 +02:00
{
2015-06-09 01:40:59 +02:00
if( !_.isUndefined(stats) )
{
if( !_.isEqual(stats, {
2015-06-03 02:51:19 +02:00
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-09 01:40:59 +02:00
{
this.stats.active = stats.active;
this.stats.mining = stats.mining;
2016-02-01 22:15:51 +01:00
this.stats.syncing = (!_.isUndefined(stats.syncing) ? stats.syncing : false);
2015-06-09 01:40:59 +02:00
this.stats.hashrate = stats.hashrate;
this.stats.peers = stats.peers;
this.stats.gasPrice = stats.gasPrice;
this.stats.uptime = stats.uptime;
callback(null, this.getBasicStats());
}
else
{
callback(null, null);
}
}
else
2015-06-01 23:04:34 +02:00
{
2015-06-09 01:40:59 +02:00
callback('Stats undefined', null);
2015-06-01 23:04:34 +02:00
}
}
2015-06-09 01:40:59 +02:00
Node.prototype.setLatency = function(latency, callback)
2015-04-03 06:00:06 +02:00
{
2015-06-09 01:40:59 +02:00
if( !_.isUndefined(latency) )
2015-04-03 06:00:06 +02:00
{
2015-06-09 01:40:59 +02:00
if( !_.isEqual(latency, this.stats.latency) )
{
this.stats.latency = latency;
2015-04-03 06:00:06 +02:00
2015-06-09 01:40:59 +02:00
callback(null, {
id: this.id,
latency: latency
});
}
else
{
callback(null, null);
}
}
else
{
callback('Latency undefined', null);
2015-04-03 06:00:06 +02:00
}
}
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,
2016-02-01 22:15:51 +01:00
syncing: this.stats.syncing,
2015-06-03 02:51:19 +02:00
hashrate: this.stats.hashrate,
peers: this.stats.peers,
gasPrice: this.stats.gasPrice,
block: this.stats.block,
propagationAvg: this.stats.propagationAvg,
uptime: this.stats.uptime,
2015-06-13 11:54:34 +02:00
pending: this.stats.pending,
latency: this.stats.latency
2015-06-03 02:51:19 +02:00
},
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,
2016-02-01 22:15:51 +01:00
syncing: this.stats.syncing,
2015-06-01 23:04:34 +02:00
hashrate: this.stats.hashrate,
peers: this.stats.peers,
gasPrice: this.stats.gasPrice,
2015-06-13 11:54:34 +02:00
uptime: this.stats.uptime,
latency: this.stats.latency
2015-06-01 23:04:34 +02:00
}
};
}
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
}
Node.prototype.getBlockNumber = function()
{
return this.stats.block.number;
}
2015-04-28 09:43:56 +02:00
Node.prototype.canUpdate = function()
{
2016-12-11 17:32:33 +01:00
if (this.trusted) {
return true;
}
2015-10-19 11:48:02 +02:00
// return (this.info.canUpdateHistory && this.trusted) || false;
2016-02-01 22:15:51 +01:00
return (this.info.canUpdateHistory || (this.stats.syncing === false && this.stats.peers > 0)) || false;
2015-04-28 09:43:56 +02:00
}
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;
}
module.exports = Node;