Skip to content

Commit

Permalink
fixed getMinerStats()/getHashrates() etc.
Browse files Browse the repository at this point in the history
 * aggregate() with proper match options
 * verify range/days req params
 * fixed tools/stats.js to support rescan with a custom interval.
 * show percent at a tooltip
  • Loading branch information
hackmod committed May 5, 2018
1 parent bcfbc9b commit 2603e78
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 43 deletions.
7 changes: 6 additions & 1 deletion public/js/controllers/StatsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ angular.module('BlocksApp').controller('StatsController', function($stateParams,
* Created by chenxiangyu on 2016/8/5.
*/
scope.init = function(dataset, chartid) {
var total = 0;
dataset.forEach(function(d) {
total += d.count;
});

var svg = d3.select(chartid)
.append("g");

Expand Down Expand Up @@ -139,7 +144,7 @@ angular.module('BlocksApp').controller('StatsController', function($stateParams,
div.style("left", d3.event.pageX + 10 + "px");
div.style("top", d3.event.pageY - 25 + "px");
div.style("display", "inline-block");
div.html((d.data._id) + "<br>" + (d.data.count) + " ");
div.html((d.data._id) + "<br>" + (d.data.count) + "<br>(" + d3.format(".2%")(d.data.count / total) + ")");
});
slice
.on("mouseout", function (d) {
Expand Down
118 changes: 94 additions & 24 deletions routes/stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,55 +14,125 @@ module.exports = function(req, res) {
res.status(400).send();

else if (req.body.action=="miners")
getMinerStats(res)
getMinerStats(req, res)

else if (req.body.action=="hashrate")
getHashrate(res);

else if (req.body.action=="hashrates")
getHashrates(res);
getHashrates(req, res);

}
/**
Aggregate miner stats
**/
var getMinerStats = function(res) {
BlockStat.aggregate([
{ $group: {
_id: '$miner',
count: {$sum: 1} }
}
], function (err, result) {
if (err) {
console.error(err);
res.status(500).send();
} else {
res.write(JSON.stringify(result));
res.end();
}
var getMinerStats = function(req, res) {
var range = 6*60*60; // 6 hours
// check validity of range
if (req.body.range && req.body.range < 60 * 60 * 24 * 7) {
range = parseInt(req.body.range);
if (range < 1800) { // minimal 30 minutes
range = 1800;
}
}

var timebefore = parseInt((new Date())/1000) - range;
Block.find({ timestamp: { $lte: timebefore } }, "timestamp number")
.lean(true).sort('-number').limit(1).exec(function (err, docs) {
if (err || !docs) {
console.error(err);
res.status(500).send();
res.end();
return;
}
var blockNumber = docs[0].number;
console.log('getMinerStats(): blockNumber = ' + blockNumber);
Block.aggregate([
{ $match: { number: { $gte: blockNumber } } },
{ $group: {
_id: '$miner',
timestamp: {$min: '$timestamp' },
count: {$sum: 1} }
}
], function (err, result) {
if (err) {
console.error(err);
res.status(500).send();
} else {
res.write(JSON.stringify(result));
res.end();
}
});
});
}

/**
Aggregate network hashrates
**/
var getHashrates = function(res) {
BlockStat.aggregate([{
$group: {
var getHashrates = function(req, res) {
// setup default range
//var range = 7 * 24 * 60 * 60; /* 7 days */
//var range = 14 * 24 * 60 * 60; /* 14 days */
//var range = 30 * 24 * 60 * 60; /* 1 months */
//var range = 2 * 30 * 24 * 60 * 60; /* 2 months */
var range = 6 * 30 * 24 * 60 * 60; /* 6 months */
if (req.body.days && req.body.days <= 365) {
var days = parseInt(req.body.days);
if (days <= 1) {
days = 1;
}
range = days * 60 * 60 * 24;
} else if (req.body.range && req.body.range < 31536000 /* 60 * 60 * 24 * 365 */) {
range = parseInt(req.body.range);
if (range < 30 * 60) {
range = 30 * 60; /* minimal range */
}
}

// select mod
var rngs = [ 30*60, 60*60, 2*60*60, 4*60*60, 6*60*60,
12*60*60, 24*60*60, 7*24*60*60, 14*24*60*60, 30*24*60*60
];
var mods = [ 1, 1, 2, 10, 10,
15, 30, 15*60, 30*60, 30*60,
60*60
];
var i = 0;
rngs.forEach(function(r) {
if (range > r) {
i++;
}
return;
});
var mod = mods[i];

var timestamp = parseInt((new Date())/1000) - range;

BlockStat.aggregate([
{ $match: { timestamp: { $gte: timestamp } } },
{ $group: {
_id: {
timestamp: {
$subtract: [ '$timestamp', { $mod: [ '$timestamp', 3600 ] } ]
$subtract: [ '$timestamp', { $mod: [ '$timestamp', mod ] } ]
}
},
blockTime: { $avg: '$blockTime' },
difficulty: { $max: '$difficulty' }
difficulty: { $max: '$difficulty' },
count: { $sum: 1 }
}},
{ $project: {
"_id": 0,
"timestamp": '$_id.timestamp',
"blockTime": 1,
"difficulty": 1,
"count": 1,
}
}]).sort('_id.timestamp').exec(function(err, docs) {
}]).sort('timestamp').exec(function(err, docs) {
var hashrates = [];
docs.forEach(function(doc) {
doc.instantHashrate = doc.difficulty / doc.blockTime;
doc.unixtime = doc._id.timestamp;
doc.timestamp = doc._id.timestamp;
doc.unixtime = doc.timestamp; /* FIXME */
doc.timestamp = doc.timestamp;
});
res.write(JSON.stringify({"hashrates": docs}));
res.end();
Expand Down
92 changes: 74 additions & 18 deletions tools/stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,35 @@ var Web3 = require('web3');
var mongoose = require( 'mongoose' );
var BlockStat = require( '../db.js' ).BlockStat;

var updateStats = function() {
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
var updateStats = function(range, interval, rescan) {
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));

mongoose.connect(process.env.MONGO_URI || 'mongodb://localhost/blockDB');
mongoose.set('debug', true);

var latestBlock = web3.eth.blockNumber;
getStats(web3, latestBlock, null, latestBlock - 1000);

interval = Math.abs(parseInt(interval));
if (!range) {
range = 1000;
}
range *= interval;
if (interval >= 10) {
latestBlock -= latestBlock % interval;
}
getStats(web3, latestBlock, null, latestBlock - range, interval, rescan);
}


var getStats = function(web3, blockNumber, nextBlock, endBlock) {
if (blockNumber <= endBlock)
process.exit(9);
var getStats = function(web3, blockNumber, nextBlock, endNumber, interval, rescan) {
if (endNumber < 0)
endNumber = 0;
if (blockNumber <= endNumber) {
if (rescan) {
process.exit(9);
}
return;
}

if(web3.isConnected()) {

Expand All @@ -35,9 +50,9 @@ var getStats = function(web3, blockNumber, nextBlock, endBlock) {
}
else {
if (nextBlock)
checkBlockDBExistsThenWrite(web3, blockData, nextBlock.timestamp);
checkBlockDBExistsThenWrite(web3, blockData, nextBlock, endNumber, interval, rescan);
else
checkBlockDBExistsThenWrite(web3, blockData, parseInt(Date.now()/1000));
checkBlockDBExistsThenWrite(web3, blockData, null, endNumber, interval, rescan);
}
});
} else {
Expand All @@ -52,9 +67,9 @@ var getStats = function(web3, blockNumber, nextBlock, endBlock) {
* if record exists: abort
* if record DNE: write a file for the block
*/
var checkBlockDBExistsThenWrite = function(web3, blockData, nextTime) {
var checkBlockDBExistsThenWrite = function(web3, blockData, nextBlock, endNumber, interval, rescan) {
BlockStat.find({number: blockData.number}, function (err, b) {
if (!b.length) {
if (!b.length && nextBlock) {
// calc hashrate, txCount, blocktime, uncleCount
var stat = {
"number": blockData.number,
Expand All @@ -64,7 +79,7 @@ var checkBlockDBExistsThenWrite = function(web3, blockData, nextTime) {
"gasUsed": blockData.gasUsed,
"gasLimit": blockData.gasLimit,
"miner": blockData.miner,
"blockTime": nextTime - blockData.timestamp,
"blockTime": (nextBlock.timestamp - blockData.timestamp) / (nextBlock.number - blockData.number),
"uncleCount": blockData.uncles.length
}
new BlockStat(stat).save( function( err, s, count ){
Expand All @@ -77,13 +92,20 @@ var checkBlockDBExistsThenWrite = function(web3, blockData, nextTime) {
} else {
console.log('DB successfully written for block number ' +
blockData.number.toString() );
getStats(web3, blockData.number - 1, blockData);
getStats(web3, blockData.number - interval, blockData, endNumber, interval, rescan);
}
});
} else {
console.log('Aborting because block number: ' + blockData.number.toString() +
' already exists in DB.');
return;
if (rescan || !nextBlock) {
getStats(web3, blockData.number - interval, blockData, endNumber, interval, rescan);
if (nextBlock) {
console.log('WARN: block number: ' + blockData.number.toString() + ' already exists in DB.');
}
} else {
console.error('Aborting because block number: ' + blockData.number.toString() +
' already exists in DB.');
return;

This comment has been minimized.

Copy link
@pyskell

pyskell Jun 5, 2018

Member

@hackmod The return here entirely halts getting the stats for older blocks if we encounter one that exists. Is this desired behavior or should we just continue?

This comment has been minimized.

Copy link
@hackmod

hackmod Jun 5, 2018

Author Contributor

It does not halt(process.exit(0)) and updateStats() continuously called at Line R156.

This comment has been minimized.

Copy link
@pyskell

pyskell Jun 5, 2018

Member

Ohh I understand now. Thanks!

}
}

})
Expand All @@ -95,6 +117,40 @@ var checkBlockDBExistsThenWrite = function(web3, blockData, nextTime) {
var minutes = 1;
statInterval = minutes * 60 * 1000;

setInterval(function() {
updateStats();
}, statInterval);
var rescan = false; /* rescan: true - rescan range */
var range = 1000;
var interval = 100;

/**
* RESCAN=1000:100000 means interval;range
*
* Usage:
* RESCAN=1000:100000 node tools/stats.js
*/
if (process.env.RESCAN) {
var tmp = process.env.RESCAN.split(/:/);
if (tmp.length > 1) {
interval = Math.abs(parseInt(tmp[0]));
if (tmp[1]) {
range = Math.abs(parseInt(tmp[1]));
}
}
var i = interval;
var j = 0;
for (var j = 0; i >= 10; j++) {
i = parseInt(i / 10);
}
interval = Math.pow(10, j);
console.log('Selected interval = ' + interval);

rescan = true;
}

// run
updateStats(range, interval, rescan);

if (!rescan) {
setInterval(function() {
updateStats(range, interval);
}, statInterval);
}

0 comments on commit 2603e78

Please sign in to comment.