2015-02-12 13:26:47 +01:00
|
|
|
var web3 = require('ethereum.js');
|
2015-02-17 02:07:40 +01:00
|
|
|
var _ = require('lodash');
|
2015-02-12 13:26:47 +01:00
|
|
|
var os = require('os');
|
2015-02-17 23:15:34 +01:00
|
|
|
var shelljs = require('shelljs');
|
2015-02-12 13:26:47 +01:00
|
|
|
|
2015-02-16 22:30:21 +01:00
|
|
|
var Primus = require('primus'),
|
|
|
|
Emitter = require('primus-emit'),
|
2015-02-23 14:57:59 +01:00
|
|
|
Latency = require('primus-spark-latency'),
|
2015-02-16 22:30:21 +01:00
|
|
|
Socket;
|
|
|
|
|
2015-03-22 22:09:34 +01:00
|
|
|
var ETH_VERSION,
|
|
|
|
NET_VERSION,
|
|
|
|
API_VERSION;
|
|
|
|
var INSTANCE_NAME = process.env.INSTANCE_NAME;
|
|
|
|
|
2015-03-27 12:38:41 +01:00
|
|
|
web3.setProvider(new web3.providers.HttpProvider('http://' + (process.env.RPC_HOST || 'localhost') + ':' + (process.env.RPC_PORT || '8545')));
|
2015-03-22 22:09:34 +01:00
|
|
|
|
2015-02-16 22:30:21 +01:00
|
|
|
Socket = Primus.createSocket({
|
|
|
|
transformer: 'websockets',
|
|
|
|
pathname: '/api',
|
2015-03-15 22:52:15 +01:00
|
|
|
timeout: 10000,
|
2015-02-23 14:57:59 +01:00
|
|
|
plugin: {emitter: Emitter, sparkLatency: Latency}
|
2015-02-16 22:30:21 +01:00
|
|
|
});
|
|
|
|
|
2015-03-22 22:09:34 +01:00
|
|
|
if(process.env.NODE_ENV === 'production' && INSTANCE_NAME === "")
|
2015-02-17 23:15:34 +01:00
|
|
|
{
|
2015-03-22 22:09:34 +01:00
|
|
|
INSTANCE_NAME = shelljs.exec('ec2metadata --instance-id', {silent: true}).output;
|
2015-02-17 23:15:34 +01:00
|
|
|
}
|
|
|
|
|
2015-02-16 22:30:21 +01:00
|
|
|
var socket = new Socket(process.env.WS_SERVER || 'ws://localhost:3000');
|
2015-02-26 22:58:46 +01:00
|
|
|
var WS_SECRET = process.env.WS_SECRET || "eth-net-stats-has-a-secret";
|
2015-02-16 17:44:26 +01:00
|
|
|
|
2015-02-18 09:55:50 +01:00
|
|
|
var MAX_BLOCKS_HISTORY = 12;
|
2015-02-12 13:26:47 +01:00
|
|
|
|
2015-02-12 22:59:01 +01:00
|
|
|
function Node()
|
2015-02-11 23:54:33 +01:00
|
|
|
{
|
2015-02-16 17:44:26 +01:00
|
|
|
var self = this;
|
|
|
|
|
2015-03-22 21:26:55 +01:00
|
|
|
try {
|
2015-03-22 21:55:15 +01:00
|
|
|
ETH_VERSION = web3.version.client;
|
|
|
|
NET_VERSION = web3.version.network;
|
|
|
|
API_VERSION = web3.version.api;
|
2015-03-22 21:26:55 +01:00
|
|
|
}
|
|
|
|
catch (err) {
|
2015-03-27 12:38:41 +01:00
|
|
|
console.error("couldn't get Version");
|
2015-03-22 21:26:55 +01:00
|
|
|
}
|
2015-03-20 07:36:52 +01:00
|
|
|
|
2015-02-11 23:54:33 +01:00
|
|
|
this.info = {
|
2015-02-17 23:15:34 +01:00
|
|
|
name: INSTANCE_NAME || (process.env.EC2_INSTANCE_ID || os.hostname()),
|
2015-03-22 21:55:15 +01:00
|
|
|
node: ETH_VERSION,
|
|
|
|
net: NET_VERSION,
|
|
|
|
api: API_VERSION,
|
2015-02-12 13:26:47 +01:00
|
|
|
os: os.platform(),
|
|
|
|
os_v: os.release()
|
2015-02-11 23:54:33 +01:00
|
|
|
};
|
|
|
|
|
2015-02-17 05:08:25 +01:00
|
|
|
this.id = _.camelCase(this.info.name);
|
2015-02-17 02:07:40 +01:00
|
|
|
|
2015-02-12 14:38:09 +01:00
|
|
|
console.log(this.info);
|
2015-02-17 02:07:40 +01:00
|
|
|
|
2015-02-12 13:26:47 +01:00
|
|
|
this.stats = {
|
2015-02-11 23:54:33 +01:00
|
|
|
active: false,
|
2015-02-12 13:26:47 +01:00
|
|
|
listening: false,
|
2015-02-11 23:54:33 +01:00
|
|
|
mining: false,
|
2015-02-12 13:26:47 +01:00
|
|
|
peers: 0,
|
|
|
|
pending: 0,
|
|
|
|
gasPrice: 0,
|
|
|
|
block: {},
|
2015-02-17 02:07:40 +01:00
|
|
|
blocktimeAvg: 0,
|
2015-02-12 13:26:47 +01:00
|
|
|
difficulty: [],
|
2015-02-17 22:42:52 +01:00
|
|
|
txDensity: [],
|
2015-02-23 15:47:18 +01:00
|
|
|
blockTimes: [],
|
2015-02-17 05:08:25 +01:00
|
|
|
uptime: 0,
|
2015-02-12 13:26:47 +01:00
|
|
|
errors: []
|
2015-02-17 05:08:25 +01:00
|
|
|
};
|
|
|
|
this._lastStats = JSON.stringify(this.stats);
|
|
|
|
|
|
|
|
this._tries = 0;
|
|
|
|
this._down = 0;
|
2015-02-18 03:59:03 +01:00
|
|
|
this._lastSent = 0;
|
2015-02-11 23:54:33 +01:00
|
|
|
|
2015-02-17 02:07:40 +01:00
|
|
|
this.blocks = [];
|
|
|
|
|
2015-02-16 22:53:32 +01:00
|
|
|
this._socket = null;
|
2015-02-12 13:26:47 +01:00
|
|
|
this.pendingWatch = false;
|
|
|
|
this.chainWatch = false;
|
|
|
|
this.updateInterval = false;
|
|
|
|
|
2015-02-16 22:30:21 +01:00
|
|
|
socket.on('open', function open() {
|
2015-02-26 22:58:46 +01:00
|
|
|
socket.emit('hello', { id: self.id, info: self.info, secret: WS_SECRET });
|
2015-03-27 12:38:41 +01:00
|
|
|
console.info('The connection has been opened.');
|
|
|
|
console.info('Trying to login');
|
|
|
|
})
|
|
|
|
.on('end', function end() {
|
2015-02-16 22:53:32 +01:00
|
|
|
self._socket = false;
|
2015-03-27 12:38:41 +01:00
|
|
|
console.error('Socket connection closed');
|
|
|
|
})
|
|
|
|
.on('error', function error(err) {
|
|
|
|
console.error(err);
|
|
|
|
})
|
|
|
|
.on('reconnecting', function reconnecting(opts) {
|
|
|
|
console.warn('We are scheduling a reconnect operation', opts);
|
|
|
|
})
|
|
|
|
.on('data', function incoming(data) {
|
|
|
|
console.info('Received some data', data);
|
2015-02-16 17:44:26 +01:00
|
|
|
});
|
2015-02-11 23:54:33 +01:00
|
|
|
|
2015-02-17 05:08:25 +01:00
|
|
|
socket.on('ready', function()
|
|
|
|
{
|
2015-02-18 03:32:03 +01:00
|
|
|
self._socket = true;
|
|
|
|
self.sendUpdate(true);
|
2015-02-17 05:08:25 +01:00
|
|
|
|
2015-03-27 12:38:41 +01:00
|
|
|
console.info('The connection has been established.');
|
2015-02-26 22:45:24 +01:00
|
|
|
});
|
2015-02-17 05:08:25 +01:00
|
|
|
|
2015-02-17 02:07:40 +01:00
|
|
|
this.init();
|
2015-02-11 23:54:33 +01:00
|
|
|
|
2015-02-17 02:07:40 +01:00
|
|
|
return this;
|
2015-02-12 14:38:09 +01:00
|
|
|
}
|
|
|
|
|
2015-02-12 13:26:47 +01:00
|
|
|
Node.prototype.isActive = function()
|
2015-02-11 23:54:33 +01:00
|
|
|
{
|
2015-02-17 05:08:25 +01:00
|
|
|
this._tries++;
|
2015-02-12 13:26:47 +01:00
|
|
|
this.stats.errors = [];
|
2015-02-11 23:54:33 +01:00
|
|
|
|
|
|
|
try {
|
2015-03-22 22:16:30 +01:00
|
|
|
var peers = web3.toDecimal(web3.net.peerCount);
|
2015-02-12 13:26:47 +01:00
|
|
|
|
2015-02-18 03:32:03 +01:00
|
|
|
if(peers !== null)
|
|
|
|
{
|
|
|
|
this.stats.peers = peers;
|
|
|
|
this.stats.active = true;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2015-02-11 23:54:33 +01:00
|
|
|
}
|
|
|
|
catch (err) {
|
2015-02-12 13:26:47 +01:00
|
|
|
this.stats.errors.push({
|
|
|
|
code: '1',
|
|
|
|
msg: err
|
|
|
|
});
|
2015-03-27 12:38:41 +01:00
|
|
|
console.error(err);
|
2015-02-11 23:54:33 +01:00
|
|
|
}
|
2015-02-18 03:32:03 +01:00
|
|
|
|
|
|
|
this.stats.active = false;
|
|
|
|
this.stats.listening = false;
|
|
|
|
this.stats.mining = false;
|
|
|
|
this.stats.peers = 0;
|
|
|
|
this._down++;
|
|
|
|
|
|
|
|
return false;
|
2015-02-12 13:26:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Node.prototype.getBlock = function(number)
|
|
|
|
{
|
|
|
|
var block = {
|
|
|
|
number: 0,
|
|
|
|
hash: '?',
|
|
|
|
difficulty: 0,
|
2015-02-23 17:48:53 +01:00
|
|
|
timestamp: 0,
|
|
|
|
arrival: 0
|
2015-02-12 13:26:47 +01:00
|
|
|
};
|
2015-02-11 23:54:33 +01:00
|
|
|
|
2015-02-12 13:26:47 +01:00
|
|
|
if(typeof number === 'undefined'){
|
|
|
|
try {
|
2015-03-27 12:38:41 +01:00
|
|
|
number = web3.eth.blockNumber;
|
2015-02-12 13:26:47 +01:00
|
|
|
|
|
|
|
if(number === this.stats.block.number + 1)
|
|
|
|
return this.stats.block;
|
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
this.stats.errors.push({
|
|
|
|
code: '3',
|
|
|
|
msg: err
|
|
|
|
});
|
2015-03-27 12:38:41 +01:00
|
|
|
console.error(err);
|
2015-02-11 23:54:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-12 13:26:47 +01:00
|
|
|
try {
|
2015-03-22 22:09:34 +01:00
|
|
|
block = web3.eth.getBlock(number, true);
|
2015-02-23 17:51:48 +01:00
|
|
|
block.arrival = Date.now();
|
|
|
|
block.propagation = block.arrival - (block.timestamp * 1000);
|
2015-02-12 13:26:47 +01:00
|
|
|
|
|
|
|
if(block.hash != '?' && typeof block.difficulty !== 'undefined')
|
|
|
|
{
|
|
|
|
block.difficulty = web3.toDecimal(block.difficulty);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
this.stats.errors.push({
|
|
|
|
code: '2',
|
|
|
|
msg: err
|
|
|
|
});
|
2015-03-27 12:38:41 +01:00
|
|
|
console.error(err);
|
2015-02-12 13:26:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node.prototype.getLatestBlocks = function()
|
|
|
|
{
|
|
|
|
var bestBlock = this.stats.block.number;
|
|
|
|
var maxIterations = MAX_BLOCKS_HISTORY;
|
|
|
|
var minBlock = 0;
|
|
|
|
|
2015-02-17 02:07:40 +01:00
|
|
|
if(this.blocks.length > 0)
|
2015-02-12 13:26:47 +01:00
|
|
|
{
|
2015-02-17 02:07:40 +01:00
|
|
|
maxIterations = Math.min(bestBlock - this.blocks[0].number, MAX_BLOCKS_HISTORY);
|
2015-02-12 13:26:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
minBlock = Math.max(0, parseInt(bestBlock) - maxIterations);
|
|
|
|
|
|
|
|
for (var i = minBlock; i < bestBlock; i++)
|
|
|
|
{
|
|
|
|
this.addBlockHistory(this.getBlock(i));
|
|
|
|
};
|
|
|
|
|
|
|
|
this.addBlockHistory(this.stats.block);
|
|
|
|
|
2015-02-23 15:47:18 +01:00
|
|
|
this.stats.blockTimes = this.calculateBlockTimes();
|
2015-02-12 13:26:47 +01:00
|
|
|
this.stats.blocktimeAvg = this.blockTimesAvg();
|
|
|
|
this.stats.difficulty = this.difficultyChart();
|
2015-02-17 22:42:52 +01:00
|
|
|
this.stats.txDensity = this.txDensityChart();
|
2015-02-12 13:26:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Node.prototype.addBlockHistory = function(block)
|
|
|
|
{
|
2015-02-18 09:55:50 +01:00
|
|
|
if(this.blocks.length === 0 || block.number !== this.blocks[0].number)
|
2015-02-12 13:26:47 +01:00
|
|
|
{
|
2015-02-17 02:07:40 +01:00
|
|
|
if(this.blocks.length === MAX_BLOCKS_HISTORY)
|
|
|
|
{
|
|
|
|
this.blocks.pop();
|
|
|
|
}
|
2015-02-12 13:26:47 +01:00
|
|
|
|
2015-02-17 02:07:40 +01:00
|
|
|
this.blocks.unshift(block);
|
|
|
|
}
|
2015-02-12 13:26:47 +01:00
|
|
|
}
|
2015-02-11 23:54:33 +01:00
|
|
|
|
2015-02-12 13:26:47 +01:00
|
|
|
Node.prototype.calculateBlockTimes = function()
|
|
|
|
{
|
|
|
|
var self = this;
|
|
|
|
|
2015-02-17 02:07:40 +01:00
|
|
|
var blockTimes = _.map(this.blocks, function(block, key, list)
|
2015-02-12 13:26:47 +01:00
|
|
|
{
|
2015-02-18 09:55:50 +01:00
|
|
|
var diff = (key > 0 ? list[key - 1].timestamp : Math.floor(Date.now()/1000)) - block.timestamp;
|
2015-02-12 13:26:47 +01:00
|
|
|
|
|
|
|
return diff;
|
|
|
|
});
|
|
|
|
|
2015-02-23 15:18:43 +01:00
|
|
|
blockTimes.shift();
|
|
|
|
|
2015-02-12 13:26:47 +01:00
|
|
|
return blockTimes;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node.prototype.blockTimesAvg = function()
|
|
|
|
{
|
2015-02-23 15:47:18 +01:00
|
|
|
var sum = _.reduce(this.stats.blockTimes, function(memo, time) { return memo + time;}, 0);
|
2015-02-12 13:26:47 +01:00
|
|
|
|
2015-02-23 15:47:18 +01:00
|
|
|
return sum/this.stats.blockTimes.length;
|
2015-02-12 13:26:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Node.prototype.difficultyChart = function()
|
|
|
|
{
|
2015-02-17 02:07:40 +01:00
|
|
|
return difficulty = _.map(this.blocks, function(block)
|
2015-02-12 13:26:47 +01:00
|
|
|
{
|
|
|
|
return block.difficulty;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-02-17 22:42:52 +01:00
|
|
|
Node.prototype.txDensityChart = function()
|
|
|
|
{
|
|
|
|
return txDensity = _.map(this.blocks, function(block)
|
|
|
|
{
|
2015-03-22 22:09:34 +01:00
|
|
|
return block.transactions.length;
|
2015-02-17 22:42:52 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-02-12 13:26:47 +01:00
|
|
|
Node.prototype.uptime = function()
|
|
|
|
{
|
2015-02-17 05:08:25 +01:00
|
|
|
this.stats.uptime = ((this._tries - this._down) / this._tries) * 100;
|
2015-02-12 13:26:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Node.prototype.getStats = function()
|
|
|
|
{
|
2015-02-17 05:08:25 +01:00
|
|
|
if(this._socket)
|
|
|
|
this._lastStats = JSON.stringify(this.stats);
|
|
|
|
|
2015-02-12 13:26:47 +01:00
|
|
|
if(this.isActive())
|
|
|
|
{
|
|
|
|
this.stats.block = this.getBlock();
|
|
|
|
|
|
|
|
// Get last MAX_BLOCKS_HISTORY blocks for calculations
|
|
|
|
if(this.stats.block.number > 0)
|
|
|
|
this.getLatestBlocks();
|
|
|
|
|
|
|
|
this.stats.mining = web3.eth.mining;
|
2015-03-22 21:26:55 +01:00
|
|
|
this.stats.gasPrice = web3.eth.gasPrice.toString(10);
|
2015-03-20 07:17:13 +01:00
|
|
|
this.stats.listening = web3.net.listening;
|
2015-02-12 13:26:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
this.uptime();
|
|
|
|
}
|
|
|
|
|
2015-02-17 05:08:25 +01:00
|
|
|
Node.prototype.changed = function()
|
|
|
|
{
|
|
|
|
var changed = ! _.isEqual(this._lastStats, JSON.stringify(this.stats));
|
2015-02-18 03:59:03 +01:00
|
|
|
|
|
|
|
if(this._tries - this._lastSent > 5)
|
|
|
|
{
|
|
|
|
this._lastSent = this._tries;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-02-17 05:08:25 +01:00
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2015-02-12 15:02:44 +01:00
|
|
|
Node.prototype.prepareStats = function()
|
|
|
|
{
|
|
|
|
return {
|
2015-02-17 05:08:25 +01:00
|
|
|
id: this.id,
|
2015-02-12 15:02:44 +01:00
|
|
|
stats: this.stats
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2015-02-17 19:22:06 +01:00
|
|
|
Node.prototype.sendUpdate = function(force)
|
2015-02-17 05:08:25 +01:00
|
|
|
{
|
2015-02-17 19:22:06 +01:00
|
|
|
if(this.changed() || force)
|
2015-02-17 05:08:25 +01:00
|
|
|
this.emit('update', this.prepareStats());
|
|
|
|
}
|
|
|
|
|
2015-02-12 13:26:47 +01:00
|
|
|
Node.prototype.update = function()
|
|
|
|
{
|
|
|
|
this.getStats();
|
|
|
|
|
2015-02-17 05:08:25 +01:00
|
|
|
this.sendUpdate();
|
2015-02-12 15:02:44 +01:00
|
|
|
|
2015-02-12 13:26:47 +01:00
|
|
|
return this.stats;
|
2015-02-11 23:54:33 +01:00
|
|
|
};
|
|
|
|
|
2015-02-12 13:26:47 +01:00
|
|
|
Node.prototype.setWatches = function()
|
|
|
|
{
|
|
|
|
var self = this;
|
2015-02-17 02:07:40 +01:00
|
|
|
|
2015-03-22 22:16:30 +01:00
|
|
|
// this.pendingWatch = web3.eth.filter('pending');
|
|
|
|
// this.pendingWatch.watch( function(log) {
|
2015-03-27 10:21:17 +01:00
|
|
|
// console.log(log);
|
2015-03-22 22:16:30 +01:00
|
|
|
// // console.log(self.pendingWatch.get());
|
|
|
|
// // console.log('pending changed');
|
|
|
|
// // self.stats.pending = parseInt(log.number);
|
|
|
|
// });
|
2015-02-12 13:26:47 +01:00
|
|
|
|
2015-03-20 07:17:13 +01:00
|
|
|
// this.chainWatch = web3.eth.watch('chain');
|
|
|
|
// this.chainWatch.messages(function(log) {
|
|
|
|
// // console.log('block changed');
|
|
|
|
// self.update();
|
|
|
|
// });
|
2015-02-12 13:26:47 +01:00
|
|
|
|
|
|
|
this.updateInterval = setInterval(function(){
|
|
|
|
self.update();
|
2015-03-27 12:38:41 +01:00
|
|
|
}, 5000);
|
2015-02-12 13:26:47 +01:00
|
|
|
}
|
|
|
|
|
2015-02-16 17:44:26 +01:00
|
|
|
Node.prototype.emit = function(message, payload)
|
2015-02-12 13:26:47 +01:00
|
|
|
{
|
2015-02-16 22:53:32 +01:00
|
|
|
if(this._socket){
|
2015-02-16 17:44:26 +01:00
|
|
|
try {
|
|
|
|
socket.emit(message, payload);
|
|
|
|
}
|
|
|
|
catch (err) {
|
2015-03-27 12:38:41 +01:00
|
|
|
console.error(err);
|
2015-02-16 17:44:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-02-12 15:02:44 +01:00
|
|
|
|
2015-02-16 17:44:26 +01:00
|
|
|
Node.prototype.init = function()
|
|
|
|
{
|
2015-02-12 13:26:47 +01:00
|
|
|
this.update();
|
|
|
|
this.setWatches();
|
|
|
|
}
|
|
|
|
|
|
|
|
Node.prototype.stop = function()
|
|
|
|
{
|
2015-02-16 22:53:32 +01:00
|
|
|
if(this._socket)
|
2015-02-18 07:05:58 +01:00
|
|
|
socket.end();
|
2015-02-12 15:02:44 +01:00
|
|
|
|
2015-03-22 21:26:55 +01:00
|
|
|
|
2015-02-12 13:26:47 +01:00
|
|
|
if(this.updateInterval)
|
|
|
|
clearInterval(this.updateInterval);
|
|
|
|
|
2015-03-22 22:22:08 +01:00
|
|
|
web3.reset();
|
|
|
|
|
|
|
|
// if(this.pendingWatch)
|
|
|
|
// this.pendingWatch.stopWatching();
|
2015-02-12 13:26:47 +01:00
|
|
|
|
2015-03-20 07:17:13 +01:00
|
|
|
// if(this.chainWatch)
|
|
|
|
// this.chainWatch.uninstall();
|
2015-02-12 13:26:47 +01:00
|
|
|
}
|
|
|
|
|
2015-02-11 23:54:33 +01:00
|
|
|
module.exports = Node;
|