diff --git a/public/js/controllers/StatsController.js b/public/js/controllers/StatsController.js
index 0757b3e25..11e493f67 100755
--- a/public/js/controllers/StatsController.js
+++ b/public/js/controllers/StatsController.js
@@ -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");
@@ -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) + "
" + (d.data.count) + " ");
+ div.html((d.data._id) + "
" + (d.data.count) + "
(" + d3.format(".2%")(d.data.count / total) + ")");
});
slice
.on("mouseout", function (d) {
diff --git a/routes/stats.js b/routes/stats.js
index 0ddcc21bc..dc7fb3dfa 100644
--- a/routes/stats.js
+++ b/routes/stats.js
@@ -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();
diff --git a/tools/stats.js b/tools/stats.js
index 8b9763c22..11154cbc4 100644
--- a/tools/stats.js
+++ b/tools/stats.js
@@ -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()) {
@@ -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 {
@@ -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,
@@ -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 ){
@@ -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;
+ }
}
})
@@ -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);
+}