added node model functionality
This commit is contained in:
parent
7e6f81cd10
commit
82e659834f
47
app.js
47
app.js
@ -1,10 +1,11 @@
|
|||||||
var express = require('express.io');
|
var express = require('express.io');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
var nodeModel = require('./lib/node');
|
||||||
var config;
|
var config;
|
||||||
|
|
||||||
var app = express();
|
var app = express();
|
||||||
app.http().io();
|
app.io();
|
||||||
|
|
||||||
if(fs.existsSync('./config.js')){
|
if(fs.existsSync('./config.js')){
|
||||||
config = require('./config');
|
config = require('./config');
|
||||||
@ -12,37 +13,27 @@ if(fs.existsSync('./config.js')){
|
|||||||
config = require('./config.default');
|
config = require('./config.default');
|
||||||
}
|
}
|
||||||
|
|
||||||
var node = new require('./lib/node')(config);
|
var node = new nodeModel(config);
|
||||||
|
|
||||||
// catch 404 and forward to error handler
|
console.log(node.stats);
|
||||||
app.use(function(req, res, next) {
|
|
||||||
var err = new Error('Not Found');
|
|
||||||
err.status = 404;
|
|
||||||
next(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
// error handlers
|
var gracefulShutdown = function() {
|
||||||
// development error handler
|
console.log("Received kill signal, shutting down gracefully.");
|
||||||
// will print stacktrace
|
|
||||||
if (app.get('env') === 'development') {
|
node.stop();
|
||||||
app.use(function(err, req, res, next) {
|
console.log("Closed node watcher");
|
||||||
res.status(err.status || 500);
|
|
||||||
res.render('error', {
|
setTimeout(function(){
|
||||||
message: err.message,
|
console.log("Closed out remaining connections.");
|
||||||
error: err
|
process.exit()
|
||||||
});
|
}, 2*1000);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// production error handler
|
// listen for TERM signal .e.g. kill
|
||||||
// no stacktraces leaked to user
|
process.on('SIGTERM', gracefulShutdown);
|
||||||
app.use(function(err, req, res, next) {
|
|
||||||
res.status(err.status || 500);
|
// listen for INT signal e.g. Ctrl-C
|
||||||
res.render('error', {
|
process.on('SIGINT', gracefulShutdown);
|
||||||
message: err.message,
|
|
||||||
error: {}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = app;
|
module.exports = app;
|
||||||
|
8
bin/www
8
bin/www
@ -1,9 +1,3 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
var debug = require('debug')('eth-netstats');
|
var debug = require('debug')('eth-netstatsservice');
|
||||||
var app = require('../app');
|
var app = require('../app');
|
||||||
|
|
||||||
app.set('port', process.env.PORT || 3000);
|
|
||||||
|
|
||||||
var server = app.listen(app.get('port'), function() {
|
|
||||||
debug('Express server listening on port ' + server.address().port);
|
|
||||||
});
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
var config = {
|
var config = {
|
||||||
name: 'Node',
|
name: 'Node',
|
||||||
type: 'C++',
|
type: 'C++',
|
||||||
os: 'linux',
|
|
||||||
rpcHost: 'localhost',
|
rpcHost: 'localhost',
|
||||||
rpcPort: '8080',
|
rpcPort: '8080',
|
||||||
serverHost: 'localhost',
|
serverHost: 'localhost',
|
||||||
|
247
lib/node.js
247
lib/node.js
@ -1,65 +1,254 @@
|
|||||||
var Node = function Node(options)
|
var web3 = require('ethereum.js');
|
||||||
|
var _ = require('underscore');
|
||||||
|
var os = require('os');
|
||||||
|
|
||||||
|
var MAX_BLOCKS_HISTORY = 12,
|
||||||
|
LOWEST_TIMESTAMP = 0;
|
||||||
|
|
||||||
|
function Node(options)
|
||||||
{
|
{
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.info = {
|
this.info = {
|
||||||
name: options.name,
|
name: options.name,
|
||||||
ip: options.rpcHost,
|
ip: options.rpcHost,
|
||||||
type: options.type,
|
type: options.type,
|
||||||
os: options.os
|
os: os.platform(),
|
||||||
|
os_v: os.release()
|
||||||
};
|
};
|
||||||
|
|
||||||
this.info.id = this.info.ip;
|
this.info.id = this.info.ip;
|
||||||
this.info.stats = {
|
this.stats = {
|
||||||
active: false,
|
active: false,
|
||||||
peers: 0,
|
listening: false,
|
||||||
mining: false,
|
mining: false,
|
||||||
block: {
|
peers: 0,
|
||||||
number: 0,
|
pending: 0,
|
||||||
hash: '?',
|
gasPrice: 0,
|
||||||
timestamp: 0
|
block: {},
|
||||||
},
|
blocks: [],
|
||||||
|
difficulty: [],
|
||||||
uptime: {
|
uptime: {
|
||||||
down: 0,
|
down: 0,
|
||||||
inc: 0,
|
inc: 0,
|
||||||
total: 0
|
total: 0
|
||||||
}
|
},
|
||||||
|
errors: []
|
||||||
}
|
}
|
||||||
|
|
||||||
this.web3 = require('ethereum.js');
|
this.pendingWatch = false;
|
||||||
|
this.chainWatch = false;
|
||||||
|
this.updateInterval = false;
|
||||||
|
|
||||||
var sock = new this.web3.providers.HttpSyncProvider('http://' + this.options.rpcHost + ':' + this.options.rpcPort);
|
var sock = new web3.providers.HttpSyncProvider('http://' + this.options.rpcHost + ':' + this.options.rpcPort);
|
||||||
this.web3.setProvider(sock);
|
web3.setProvider(sock);
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node.prototype.update = function()
|
Node.prototype.isActive = function()
|
||||||
{
|
{
|
||||||
var eth = this.web3.eth;
|
this.stats.uptime.inc++;
|
||||||
|
this.stats.errors = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.info.stats.peers = eth.peerCount;
|
this.stats.peers = web3.eth.peerCount;
|
||||||
|
this.stats.active = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.info.stats.peers = null;
|
this.stats.active = false;
|
||||||
|
this.stats.listening = false;
|
||||||
|
this.stats.mining = false;
|
||||||
|
this.stats.peers = 0;
|
||||||
|
this.stats.uptime.down++;
|
||||||
|
|
||||||
|
this.stats.errors.push({
|
||||||
|
code: '1',
|
||||||
|
msg: err
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Node.prototype.getBlock = function(number)
|
||||||
|
{
|
||||||
|
var block = {
|
||||||
|
number: 0,
|
||||||
|
hash: '?',
|
||||||
|
difficulty: 0,
|
||||||
|
timestamp: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if(typeof number === 'undefined'){
|
||||||
|
try {
|
||||||
|
number = parseInt(web3.eth.number);
|
||||||
|
|
||||||
|
if(number === this.stats.block.number + 1)
|
||||||
|
return this.stats.block;
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.stats.errors.push({
|
||||||
|
code: '3',
|
||||||
|
msg: err
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.info.stats.peers != null) {
|
try {
|
||||||
this.info.stats.block = eth.block(parseInt(eth.number));
|
block = web3.eth.block(number);
|
||||||
if(this.info.stats.block.hash != '?'){
|
|
||||||
this.info.stats.block.difficulty = this.web3.toDecimal(this.info.stats.block.difficulty);
|
if(block.hash != '?' && typeof block.difficulty !== 'undefined')
|
||||||
|
{
|
||||||
|
block.difficulty = web3.toDecimal(block.difficulty);
|
||||||
}
|
}
|
||||||
this.info.stats.mining = eth.mining;
|
}
|
||||||
this.info.stats.active = true;
|
catch (err) {
|
||||||
} else {
|
this.stats.errors.push({
|
||||||
this.info.stats.active = false;
|
code: '2',
|
||||||
this.info.stats.uptime.down++;
|
msg: err
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.info.stats.uptime.inc++;
|
return block;
|
||||||
this.info.stats.uptime.total = ((this.info.stats.uptime.inc - this.info.stats.uptime.down) / this.info.stats.uptime.inc) * 100;
|
}
|
||||||
|
|
||||||
return this.info;
|
Node.prototype.getLatestBlocks = function()
|
||||||
|
{
|
||||||
|
var bestBlock = this.stats.block.number;
|
||||||
|
var maxIterations = MAX_BLOCKS_HISTORY;
|
||||||
|
var minBlock = 0;
|
||||||
|
|
||||||
|
if(this.stats.blocks.length > 0)
|
||||||
|
{
|
||||||
|
maxIterations = Math.min(bestBlock - this.stats.blocks[0].number, MAX_BLOCKS_HISTORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
minBlock = Math.max(0, parseInt(bestBlock) - maxIterations);
|
||||||
|
|
||||||
|
for (var i = minBlock; i < bestBlock; i++)
|
||||||
|
{
|
||||||
|
this.addBlockHistory(this.getBlock(i));
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addBlockHistory(this.stats.block);
|
||||||
|
|
||||||
|
this.calculateBlockTimes();
|
||||||
|
this.stats.blocktimeAvg = this.blockTimesAvg();
|
||||||
|
this.stats.difficulty = this.difficultyChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
Node.prototype.addBlockHistory = function(block)
|
||||||
|
{
|
||||||
|
if(this.stats.blocks.length === MAX_BLOCKS_HISTORY)
|
||||||
|
{
|
||||||
|
LOWEST_TIMESTAMP = this.stats.blocks[MAX_BLOCKS_HISTORY - 1].timestamp;
|
||||||
|
this.stats.blocks.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stats.blocks.unshift(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node.prototype.calculateBlockTimes = function()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var blockTimes = _.map(this.stats.blocks, function(block, key, list)
|
||||||
|
{
|
||||||
|
var diff = block.timestamp - (key < list.length - 1 ? list[key + 1].timestamp : LOWEST_TIMESTAMP);
|
||||||
|
|
||||||
|
self.stats.blocks[key].blocktime = diff;
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
});
|
||||||
|
|
||||||
|
return blockTimes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node.prototype.blockTimesAvg = function()
|
||||||
|
{
|
||||||
|
var sum = _.reduce(this.stats.blocks, function(memo, block) { return memo + block.blocktime;}, 0);
|
||||||
|
|
||||||
|
return sum/this.stats.blocks.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node.prototype.difficultyChart = function()
|
||||||
|
{
|
||||||
|
return difficulty = _.map(this.stats.blocks, function(block)
|
||||||
|
{
|
||||||
|
return block.difficulty;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Node.prototype.uptime = function()
|
||||||
|
{
|
||||||
|
this.stats.uptime.total = ((this.stats.uptime.inc - this.stats.uptime.down) / this.stats.uptime.inc) * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node.prototype.getStats = function()
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
this.stats.gasPrice = web3.toDecimal(web3.eth.gasPrice);
|
||||||
|
this.stats.listening = web3.eth.listening;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.uptime();
|
||||||
|
}
|
||||||
|
|
||||||
|
Node.prototype.update = function()
|
||||||
|
{
|
||||||
|
this.getStats();
|
||||||
|
|
||||||
|
return this.stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Node.prototype.setWatches = function()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
this.pendingWatch = web3.eth.watch('pending');
|
||||||
|
this.pendingWatch.changed(function(log) {
|
||||||
|
console.log('pending changed');
|
||||||
|
self.stats.pending = parseInt(log.number);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.chainWatch = web3.eth.watch('chain');
|
||||||
|
this.chainWatch.messages(function(log) {
|
||||||
|
console.log('block changed');
|
||||||
|
self.update();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.updateInterval = setInterval(function(){
|
||||||
|
self.update();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node.prototype.init = function()
|
||||||
|
{
|
||||||
|
this.update();
|
||||||
|
this.setWatches();
|
||||||
|
}
|
||||||
|
|
||||||
|
Node.prototype.stop = function()
|
||||||
|
{
|
||||||
|
if(this.updateInterval)
|
||||||
|
clearInterval(this.updateInterval);
|
||||||
|
|
||||||
|
if(this.pendingWatch)
|
||||||
|
this.pendingWatch.uninstall();
|
||||||
|
|
||||||
|
if(this.chainWatch)
|
||||||
|
this.chainWatch.uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = Node;
|
module.exports = Node;
|
@ -8,6 +8,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "~2.0.0",
|
"debug": "~2.0.0",
|
||||||
"ethereum.js": "*",
|
"ethereum.js": "*",
|
||||||
"express.io": "^1.1.13"
|
"express.io": "^1.1.13",
|
||||||
|
"underscore": "^1.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user