ethstats-server/models/history.js

365 lines
7.5 KiB
JavaScript
Raw Normal View History

2015-04-17 11:10:20 +02:00
var _ = require('lodash');
2015-04-24 02:08:12 +02:00
var d3 = require('d3');
2015-04-17 11:10:20 +02:00
2015-04-24 05:23:26 +02:00
var MAX_HISTORY = 1000;
2015-04-24 05:37:52 +02:00
2015-04-23 15:17:31 +02:00
var MAX_PEER_PROPAGATION = 36;
2015-04-24 05:23:26 +02:00
var MIN_PROPAGATION_RANGE = 0;
2015-04-23 15:17:31 +02:00
var MAX_PROPAGATION_RANGE = 20000;
2015-04-24 05:37:52 +02:00
var MAX_UNCLES_PER_BIN = 25;
2015-04-23 15:17:31 +02:00
var MAX_BINS = 40;
2015-04-17 11:10:20 +02:00
var History = function History(data)
{
this._items = [];
var item = {
height: 0,
block: {
number: 0,
hash: '0x?',
arrived: 0,
received: 0,
propagation: 0,
difficulty: 0,
gasUsed: 0,
transactions: [],
uncles: []
},
propagTimes: []
};
}
History.prototype.add = function(block, id)
{
2015-04-28 09:43:56 +02:00
if(typeof block !== 'undefined' && typeof block.number !== 'undefined' && typeof block.uncles !== 'undefined' && typeof block.transactions !== 'undefined' && typeof block.difficulty !== 'undefined' && (this._items.length === 0 || block.number >= (this.bestBlock().height - MAX_HISTORY + 1)))
2015-04-17 11:10:20 +02:00
{
2015-04-28 01:50:28 +02:00
var historyBlock = this.search(block.number);
2015-04-17 11:10:20 +02:00
2015-04-28 01:50:28 +02:00
var now = (new Date()).getTime();
block.arrived = now;
block.received = now;
block.propagation = 0;
if(historyBlock)
2015-04-17 11:10:20 +02:00
{
2015-04-28 01:50:28 +02:00
var propIndex = _.findIndex(historyBlock.propagTimes, {node: id});
if(propIndex === -1)
{
block.arrived = historyBlock.block.arrived;
block.received = now;
block.propagation = now - historyBlock.block.received;
2015-04-17 11:10:20 +02:00
2015-04-28 01:50:28 +02:00
historyBlock.propagTimes.push({node: id, received: now, propagation: block.propagation});
}
else
{
block.arrived = historyBlock.block.arrived;
block.received = historyBlock.propagTimes[propIndex].received;
block.propagation = historyBlock.propagTimes[propIndex].propagation;
}
2015-04-17 11:10:20 +02:00
}
else
{
2015-04-28 05:51:04 +02:00
var prevBlock = this.prevMaxBlock(block.number);
if(prevBlock)
{
block.time = block.arrived - prevBlock.block.arrived;
2015-04-28 09:43:56 +02:00
if(block.number < this.bestBlock().height)
block.time = (block.timestamp - prevBlock.block.timestamp)*1000;
2015-04-28 05:51:04 +02:00
}
else
{
block.time = 0;
}
2015-04-28 01:50:28 +02:00
var item = {
height: block.number,
block: block,
propagTimes: []
}
item.propagTimes.push({node: id, received: now, propagation: block.propagation});
this._save(item);
2015-04-17 11:10:20 +02:00
}
2015-04-28 01:50:28 +02:00
return block;
2015-04-17 11:10:20 +02:00
}
2015-04-28 01:50:28 +02:00
return false;
2015-04-17 11:10:20 +02:00
}
History.prototype._save = function(block)
{
2015-04-28 09:43:56 +02:00
this._items.unshift(block);
2015-04-17 11:10:20 +02:00
if(this._items.length > MAX_HISTORY){
2015-04-28 09:43:56 +02:00
this._items.pop();
2015-04-17 11:10:20 +02:00
}
2015-04-28 05:51:04 +02:00
this._items = _.sortByOrder(this._items, 'height', false);
2015-04-17 11:10:20 +02:00
}
History.prototype.search = function(number)
{
var index = _.findIndex(this._items, {height: number});
if(index < 0)
return false;
return this._items[index];
}
2015-04-28 05:51:04 +02:00
History.prototype.prevMaxBlock = function(number)
{
var index = _.findIndex(this._items, function(item) {
return item.height < number;
});
if(index < 0)
return false;
return this._items[index];
}
2015-04-17 11:10:20 +02:00
History.prototype.bestBlock = function(obj)
{
return _.max(this._items, 'height');
}
History.prototype.getNodePropagation = function(id)
{
2015-04-23 15:17:31 +02:00
var propagation = new Array(MAX_PEER_PROPAGATION);
2015-04-17 11:10:20 +02:00
var bestBlock = this.bestBlock().height;
2015-04-23 15:17:31 +02:00
2015-04-17 11:10:20 +02:00
_.fill(propagation, -1);
var sorted = _(this._items)
.sortByOrder('height', false)
2015-04-23 15:17:31 +02:00
.slice(0, MAX_PEER_PROPAGATION)
2015-04-17 11:10:20 +02:00
.reverse()
.forEach(function(n, key)
{
2015-04-23 15:17:31 +02:00
var index = MAX_PEER_PROPAGATION - 1 - bestBlock + n.height;
2015-04-17 11:10:20 +02:00
if(index > 0)
{
propagation[index] = _.result(_.find(n.propagTimes, 'node', id), 'propagation', -1);
}
})
.value();
return propagation;
}
History.prototype.getBlockPropagation = function()
{
2015-04-23 15:17:31 +02:00
var propagation = [];
2015-04-28 17:44:41 +02:00
var avgPropagation = 0;
2015-04-17 11:10:20 +02:00
2015-04-24 02:08:12 +02:00
_.forEach(this._items, function(n, key)
{
_.forEach(n.propagTimes, function(p, i)
2015-04-17 11:10:20 +02:00
{
2015-04-24 02:08:12 +02:00
var prop = _.result(p, 'propagation', -1);
if(prop >= 0)
propagation.push(prop);
});
});
2015-04-28 17:44:41 +02:00
if(propagation.length > 0)
{
var avgPropagation = Math.round(_.sum(propagation) / propagation.length);
}
2015-04-24 02:08:12 +02:00
var x = d3.scale.linear()
2015-04-24 05:23:26 +02:00
.domain([MIN_PROPAGATION_RANGE, MAX_PROPAGATION_RANGE])
2015-04-24 02:08:12 +02:00
.interpolate(d3.interpolateRound);
var data = d3.layout.histogram()
.frequency(false)
.bins(x.ticks(MAX_BINS))
(propagation);
var freqCum = 0;
2015-04-24 05:23:26 +02:00
var histogram = data.map(function(val) {
2015-04-24 02:08:12 +02:00
freqCum += val.length;
var cumPercent = (freqCum / Math.max(1, propagation.length));
return {x: val.x, dx: val.dx, y: val.y, frequency: val.length, cumulative: freqCum, cumpercent: cumPercent};
});
2015-04-28 17:44:41 +02:00
return {histogram: histogram, avg: avgPropagation};
2015-04-24 05:23:26 +02:00
}
2015-04-28 03:58:19 +02:00
History.prototype.getUncleCount = function()
2015-04-24 05:23:26 +02:00
{
var uncles = _(this._items)
.sortByOrder('height', false)
.map(function(item)
{
return item.block.uncles.length;
})
.value();
2015-04-24 05:37:52 +02:00
var uncleBins = _.fill(Array(MAX_BINS), 0);
2015-04-24 05:23:26 +02:00
var sumMapper = function(array, key) {
uncleBins[key] = _.sum(array);
return _.sum(array);
};
2015-04-24 05:37:52 +02:00
_.map(_.chunk(uncles, MAX_UNCLES_PER_BIN), sumMapper);
2015-04-24 05:23:26 +02:00
return uncleBins;
2015-04-17 11:10:20 +02:00
}
2015-04-28 05:51:04 +02:00
History.prototype.getBlockTimes = function()
{
var blockTimes = _(this._items)
.sortByOrder('height', false)
.slice(0, MAX_BINS)
.reverse()
.map(function(item)
{
return item.block.time;
})
.value();
return blockTimes;
}
2015-04-28 06:25:15 +02:00
History.prototype.getDifficulty = function()
{
var difficultyHistory = _(this._items)
.sortByOrder('height', false)
.slice(0, MAX_BINS)
.reverse()
.map(function(item)
{
return item.block.difficulty;
})
.value();
return difficultyHistory;
}
2015-04-28 03:58:19 +02:00
History.prototype.getTransactionsCount = function()
2015-04-28 02:52:41 +02:00
{
2015-04-28 03:02:12 +02:00
var txCount = _(this._items)
2015-04-28 02:52:41 +02:00
.sortByOrder('height', false)
2015-04-28 03:50:21 +02:00
.slice(0, MAX_BINS)
2015-04-28 02:52:41 +02:00
.reverse()
.map(function(item)
{
return item.block.transactions.length;
})
.value();
2015-04-28 03:02:12 +02:00
return txCount;
2015-04-28 02:52:41 +02:00
}
2015-04-28 03:58:19 +02:00
History.prototype.getGasSpending = function()
2015-04-28 03:49:43 +02:00
{
var gasSpending = _(this._items)
.sortByOrder('height', false)
2015-04-28 03:50:21 +02:00
.slice(0, MAX_BINS)
2015-04-28 03:49:43 +02:00
.reverse()
.map(function(item)
{
return item.block.gasUsed;
})
.value();
return gasSpending;
}
2015-04-28 13:04:39 +02:00
History.prototype.getAvgHashrate = function()
{
if(this._items.length === 0)
return 0;
var difficultyHistory = _(this._items)
.map(function(item)
{
return item.block.difficulty;
})
.value();
var avgDifficulty = _.sum(difficultyHistory)/difficultyHistory.length;
var blocktimeHistory = _(this._items)
.map(function(item)
{
return item.block.time;
})
.value();
var avgBlocktime = _.sum(blocktimeHistory)/blocktimeHistory.length;
return avgDifficulty/1000 * 12 * (12/avgBlocktime);
}
2015-04-28 06:25:15 +02:00
History.prototype.getCharts = function()
2015-04-28 03:45:23 +02:00
{
2015-04-28 06:25:15 +02:00
var chartHistory = _(this._items)
2015-04-28 03:45:23 +02:00
.sortByOrder('height', false)
2015-04-28 03:50:21 +02:00
.slice(0, MAX_BINS)
2015-04-28 03:45:23 +02:00
.reverse()
.map(function(item)
{
2015-04-28 06:25:15 +02:00
var chart = {
2015-04-28 09:43:56 +02:00
height: item.height,
blocktime: item.block.time/1000,
2015-04-28 06:25:15 +02:00
difficulty: item.block.difficulty,
uncles: item.block.uncles.length,
transactions: item.block.transactions.length,
gasSpending: item.block.gasUsed
}
return chart;
2015-04-28 03:45:23 +02:00
})
.value();
2015-04-28 09:43:56 +02:00
var chart = {
height: _.pluck(chartHistory, 'height'),
blocktime: _.pluck(chartHistory, 'blocktime'),
2015-04-28 11:31:49 +02:00
avgBlocktime: _.sum(_.pluck(chartHistory, 'blocktime')) / (chartHistory.length === 0 ? 1 : chartHistory.length),
2015-04-28 09:43:56 +02:00
difficulty: _.pluck(chartHistory, 'difficulty'),
uncles: _.pluck(chartHistory, 'uncles'),
transactions: _.pluck(chartHistory, 'transactions'),
gasSpending: _.pluck(chartHistory, 'gasSpending'),
propagation: this.getBlockPropagation(),
2015-04-28 13:04:39 +02:00
uncleCount: this.getUncleCount(),
avgHashrate: this.getAvgHashrate()
2015-04-28 09:43:56 +02:00
}
return chart;
2015-04-28 03:45:23 +02:00
}
2015-04-17 11:10:20 +02:00
History.prototype.history = function()
{
2015-04-28 09:43:56 +02:00
return this._items;
}
History.prototype.requiresUpdate = function()
{
return ! (this._items.length === MAX_HISTORY);
}
History.prototype.getHistoryRequestInterval = function()
{
if(this._items.length === 0)
return null;
var max = _.min(this._items, 'height').height - 1;
2015-04-28 15:06:53 +02:00
var min = max - Math.min(50, (MAX_HISTORY - this._items.length + 1)) + 1;
2015-04-28 09:43:56 +02:00
return {max: max, min: min};
2015-04-17 11:10:20 +02:00
}
module.exports = History;