commit
01eee61d3b
2
dist/index.html
vendored
2
dist/index.html
vendored
File diff suppressed because one or more lines are too long
2
dist/js/netstats.min.js
vendored
2
dist/js/netstats.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/js/netstats.min.js.map
vendored
2
dist/js/netstats.min.js.map
vendored
File diff suppressed because one or more lines are too long
@ -30,7 +30,7 @@ Collection.prototype.update = function(id, stats, callback)
|
|||||||
{
|
{
|
||||||
this._blockchain.clean(this.getBestBlockFromItems());
|
this._blockchain.clean(this.getBestBlockFromItems());
|
||||||
|
|
||||||
var block = this._blockchain.add(stats.block, id);
|
var block = this._blockchain.add(stats.block, id, node.trusted);
|
||||||
|
|
||||||
if (!block)
|
if (!block)
|
||||||
{
|
{
|
||||||
@ -61,7 +61,7 @@ Collection.prototype.addBlock = function(id, stats, callback)
|
|||||||
{
|
{
|
||||||
this._blockchain.clean(this.getBestBlockFromItems());
|
this._blockchain.clean(this.getBestBlockFromItems());
|
||||||
|
|
||||||
var block = this._blockchain.add(stats, id);
|
var block = this._blockchain.add(stats, id, node.trusted);
|
||||||
|
|
||||||
if (!block)
|
if (!block)
|
||||||
{
|
{
|
||||||
@ -121,7 +121,7 @@ Collection.prototype.addHistory = function(id, blocks, callback)
|
|||||||
|
|
||||||
for (var i = 0; i <= blocks.length - 1; i++)
|
for (var i = 0; i <= blocks.length - 1; i++)
|
||||||
{
|
{
|
||||||
this._blockchain.add(blocks[i], id);
|
this._blockchain.add(blocks[i], id, node.trusted, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getCharts();
|
this.getCharts();
|
||||||
@ -262,9 +262,9 @@ Collection.prototype.getHistory = function()
|
|||||||
|
|
||||||
Collection.prototype.getBestBlockFromItems = function()
|
Collection.prototype.getBestBlockFromItems = function()
|
||||||
{
|
{
|
||||||
return _.result(_.max(this._items, function(item) {
|
return Math.max(this._blockchain.bestBlockNumber(), _.result(_.max(this._items, function(item) {
|
||||||
return item.stats.block.number;
|
return ( !item.trusted ? 0 : item.stats.block.number );
|
||||||
}), 'stats.block.number', 0);
|
}), 'stats.block.number', 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection.prototype.canNodeUpdate = function(id)
|
Collection.prototype.canNodeUpdate = function(id)
|
||||||
|
206
lib/history.js
206
lib/history.js
@ -1,12 +1,13 @@
|
|||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var d3 = require('d3');
|
var d3 = require('d3');
|
||||||
|
|
||||||
var MAX_HISTORY = 1000;
|
var MAX_HISTORY = 2000;
|
||||||
|
|
||||||
var MAX_PEER_PROPAGATION = 40;
|
var MAX_PEER_PROPAGATION = 40;
|
||||||
var MIN_PROPAGATION_RANGE = 0;
|
var MIN_PROPAGATION_RANGE = 0;
|
||||||
var MAX_PROPAGATION_RANGE = 10000;
|
var MAX_PROPAGATION_RANGE = 10000;
|
||||||
|
|
||||||
|
var MAX_UNCLES = 1000;
|
||||||
var MAX_UNCLES_PER_BIN = 25;
|
var MAX_UNCLES_PER_BIN = 25;
|
||||||
var MAX_BINS = 40;
|
var MAX_BINS = 40;
|
||||||
|
|
||||||
@ -14,88 +15,110 @@ var History = function History(data)
|
|||||||
{
|
{
|
||||||
this._items = [];
|
this._items = [];
|
||||||
this._callback = null;
|
this._callback = null;
|
||||||
|
|
||||||
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)
|
History.prototype.add = function(block, id, trusted, addingHistory)
|
||||||
{
|
{
|
||||||
var changed = false;
|
var changed = false;
|
||||||
|
|
||||||
if( !_.isUndefined(block) && !_.isUndefined(block.number) && !_.isUndefined(block.uncles) && !_.isUndefined(block.transactions) && !_.isUndefined(block.difficulty) && block.number > 0 )
|
if( !_.isUndefined(block) && !_.isUndefined(block.number) && !_.isUndefined(block.uncles) && !_.isUndefined(block.transactions) && !_.isUndefined(block.difficulty) && block.number > 0 )
|
||||||
{
|
{
|
||||||
var historyBlock = this.search(block.number);
|
var historyBlock = this.search(block.number);
|
||||||
|
var forkIndex = -1;
|
||||||
|
|
||||||
var now = _.now();
|
var now = _.now();
|
||||||
|
|
||||||
|
block.trusted = trusted;
|
||||||
block.arrived = now;
|
block.arrived = now;
|
||||||
block.received = now;
|
block.received = now;
|
||||||
block.propagation = 0;
|
block.propagation = 0;
|
||||||
|
block.fork = 0;
|
||||||
|
|
||||||
if( historyBlock )
|
if( historyBlock )
|
||||||
{
|
{
|
||||||
|
// We already have a block with this height in collection
|
||||||
|
|
||||||
|
// Check if node already checked this block height
|
||||||
var propIndex = _.findIndex( historyBlock.propagTimes, { node: id } );
|
var propIndex = _.findIndex( historyBlock.propagTimes, { node: id } );
|
||||||
|
|
||||||
|
// Check if node already check a fork with this height
|
||||||
|
forkIndex = compareForks(historyBlock, block);
|
||||||
|
|
||||||
if( propIndex === -1 )
|
if( propIndex === -1 )
|
||||||
{
|
{
|
||||||
block.arrived = historyBlock.block.arrived;
|
// Node didn't submit this block before
|
||||||
block.received = now;
|
if( forkIndex >= 0 && !_.isUndefined(historyBlock.forks[forkIndex]) )
|
||||||
block.propagation = now - historyBlock.block.received;
|
{
|
||||||
|
// Found fork => update data
|
||||||
|
block.arrived = historyBlock.forks[forkIndex].arrived;
|
||||||
|
block.propagation = now - historyBlock.forks[forkIndex].received;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No fork found => add a new one
|
||||||
|
forkIndex = historyBlock.forks.push(block) - 1;
|
||||||
|
historyBlock.forks[forkIndex].fork = forkIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push propagation time
|
||||||
historyBlock.propagTimes.push({
|
historyBlock.propagTimes.push({
|
||||||
node: id,
|
node: id,
|
||||||
|
trusted: trusted,
|
||||||
|
fork: forkIndex,
|
||||||
received: now,
|
received: now,
|
||||||
propagation: block.propagation
|
propagation: block.propagation
|
||||||
});
|
});
|
||||||
|
|
||||||
changed = true;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
block.arrived = historyBlock.block.arrived;
|
// Node submited the block before
|
||||||
|
if( forkIndex >= 0 && !_.isUndefined(historyBlock.forks[forkIndex]) )
|
||||||
|
{
|
||||||
|
// Matching fork found => update data
|
||||||
|
block.arrived = historyBlock.forks[forkIndex].arrived;
|
||||||
|
|
||||||
|
if( forkIndex === historyBlock.propagTimes[propIndex].fork )
|
||||||
|
{
|
||||||
|
// Fork index is the same
|
||||||
|
block.received = historyBlock.propagTimes[propIndex].received;
|
||||||
|
block.propagation = historyBlock.propagTimes[propIndex].propagation;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fork index is different
|
||||||
|
historyBlock.propagTimes[propIndex].fork = forkIndex;
|
||||||
|
historyBlock.propagTimes[propIndex].propagation = block.propagation = now - historyBlock.forks[forkIndex].received;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No matching fork found => replace old one
|
||||||
block.received = historyBlock.propagTimes[propIndex].received;
|
block.received = historyBlock.propagTimes[propIndex].received;
|
||||||
block.propagation = historyBlock.propagTimes[propIndex].propagation;
|
block.propagation = historyBlock.propagTimes[propIndex].propagation;
|
||||||
|
|
||||||
if(historyBlock.hash !== block.hash || historyBlock.totalDifficulty !== block.totalDifficulty || historyBlock.transactions.length !== block.transactions.length)
|
forkIndex = historyBlock.forks.push(block) - 1;
|
||||||
{
|
historyBlock.forks[forkIndex].fork = forkIndex;
|
||||||
index = _.findIndex( this._items, { height: block.number } );
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this._items[index].hash = block.hash;
|
if( trusted && !compareBlocks(historyBlock.block, historyBlock.forks[forkIndex]) )
|
||||||
this._items[index].parentHash = block.parentHash;
|
{
|
||||||
this._items[index].nonce = block.nonce;
|
// If source is trusted update the main block
|
||||||
this._items[index].sha3Uncles = block.sha3Uncles;
|
historyBlock.forks[forkIndex].trusted = trusted;
|
||||||
this._items[index].transactionsRoot = block.transactionsRoot;
|
historyBlock.block = historyBlock.forks[forkIndex];
|
||||||
this._items[index].stateRoot = block.stateRoot;
|
}
|
||||||
this._items[index].miner = block.miner;
|
|
||||||
this._items[index].difficulty = block.difficulty;
|
block.fork = forkIndex;
|
||||||
this._items[index].totalDifficulty = block.totalDifficulty;
|
|
||||||
this._items[index].size = block.size;
|
|
||||||
this._items[index].extraData = block.extraData;
|
|
||||||
this._items[index].gasLimit = block.gasLimit;
|
|
||||||
this._items[index].gasUsed = block.gasUsed;
|
|
||||||
this._items[index].timestamp = block.timestamp;
|
|
||||||
this._items[index].transactions = block.transactions;
|
|
||||||
this._items[index].uncles = block.uncles;
|
|
||||||
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Couldn't find block with this height
|
||||||
|
|
||||||
|
// Getting previous max block
|
||||||
var prevBlock = this.prevMaxBlock(block.number);
|
var prevBlock = this.prevMaxBlock(block.number);
|
||||||
|
|
||||||
if( prevBlock )
|
if( prevBlock )
|
||||||
@ -113,13 +136,16 @@ History.prototype.add = function(block, id)
|
|||||||
var item = {
|
var item = {
|
||||||
height: block.number,
|
height: block.number,
|
||||||
block: block,
|
block: block,
|
||||||
|
forks: [block],
|
||||||
propagTimes: []
|
propagTimes: []
|
||||||
}
|
}
|
||||||
|
|
||||||
if( this._items.length === 0 || block.number >= (this.bestBlockNumber() - MAX_HISTORY + 1) )
|
if( this._items.length === 0 || (this._items.length === MAX_HISTORY && block.number > this.worstBlockNumber() && !addingHistory) || (this._items.length < MAX_HISTORY && block.number < this.bestBlockNumber()) )
|
||||||
{
|
{
|
||||||
item.propagTimes.push({
|
item.propagTimes.push({
|
||||||
node: id,
|
node: id,
|
||||||
|
trusted: trusted,
|
||||||
|
fork: 0,
|
||||||
received: now,
|
received: now,
|
||||||
propagation: block.propagation
|
propagation: block.propagation
|
||||||
});
|
});
|
||||||
@ -139,6 +165,43 @@ History.prototype.add = function(block, id)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function compareBlocks(block1, block2)
|
||||||
|
{
|
||||||
|
if( block1.hash !== block2.hash ||
|
||||||
|
block1.parentHash !== block2.parentHash ||
|
||||||
|
block1.nonce !== block2.nonce ||
|
||||||
|
block1.sha3Uncles !== block2.sha3Uncles ||
|
||||||
|
block1.transactionsRoot !== block2.transactionsRoot ||
|
||||||
|
block1.stateRoot !== block2.stateRoot ||
|
||||||
|
block1.miner !== block2.miner ||
|
||||||
|
block1.difficulty !== block2.difficulty ||
|
||||||
|
block1.totalDifficulty !== block2.totalDifficulty ||
|
||||||
|
block1.size !== block2.size ||
|
||||||
|
block1.extraData !== block2.extraData ||
|
||||||
|
block1.gasLimit !== block2.gasLimit ||
|
||||||
|
block1.gasUsed !== block2.gasUsed ||
|
||||||
|
block1.transactions.length !== block2.transactions.length ||
|
||||||
|
block1.uncles.length !== block2.uncles.length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareForks(historyBlock, block2)
|
||||||
|
{
|
||||||
|
if( _.isUndefined(historyBlock) )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if( _.isUndefined(historyBlock.forks) || historyBlock.forks.length === 0 )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for(var x = 0; x < historyBlock.forks.length; x++)
|
||||||
|
if(compareBlocks(historyBlock.forks[x], block2))
|
||||||
|
return x;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
History.prototype._save = function(block)
|
History.prototype._save = function(block)
|
||||||
{
|
{
|
||||||
this._items.unshift(block);
|
this._items.unshift(block);
|
||||||
@ -160,7 +223,7 @@ History.prototype.clean = function(max)
|
|||||||
console.log("History items before:", this._items.length);
|
console.log("History items before:", this._items.length);
|
||||||
|
|
||||||
this._items = _(this._items).filter(function(item) {
|
this._items = _(this._items).filter(function(item) {
|
||||||
return item.height <= max;
|
return (item.height <= max && item.block.trusted === false);
|
||||||
}).value();
|
}).value();
|
||||||
|
|
||||||
console.log("History items after:", this._items.length);
|
console.log("History items after:", this._items.length);
|
||||||
@ -204,6 +267,22 @@ History.prototype.bestBlockNumber = function()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
History.prototype.worstBlock = function()
|
||||||
|
{
|
||||||
|
return _.min(this._items, 'height');
|
||||||
|
}
|
||||||
|
|
||||||
|
History.prototype.worstBlockNumber = function(trusted)
|
||||||
|
{
|
||||||
|
var worst = this.worstBlock();
|
||||||
|
|
||||||
|
if( !_.isUndefined(worst.height) )
|
||||||
|
return worst.height;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
History.prototype.getNodePropagation = function(id)
|
History.prototype.getNodePropagation = function(id)
|
||||||
{
|
{
|
||||||
var propagation = new Array( MAX_PEER_PROPAGATION );
|
var propagation = new Array( MAX_PEER_PROPAGATION );
|
||||||
@ -292,6 +371,11 @@ History.prototype.getUncleCount = function()
|
|||||||
{
|
{
|
||||||
var uncles = _( this._items )
|
var uncles = _( this._items )
|
||||||
.sortByOrder( 'height', false )
|
.sortByOrder( 'height', false )
|
||||||
|
.filter(function (item)
|
||||||
|
{
|
||||||
|
return item.block.trusted;
|
||||||
|
})
|
||||||
|
.slice(0, MAX_UNCLES)
|
||||||
.map(function (item)
|
.map(function (item)
|
||||||
{
|
{
|
||||||
return item.block.uncles.length;
|
return item.block.uncles.length;
|
||||||
@ -315,6 +399,10 @@ History.prototype.getBlockTimes = function()
|
|||||||
{
|
{
|
||||||
var blockTimes = _( this._items )
|
var blockTimes = _( this._items )
|
||||||
.sortByOrder( 'height', false )
|
.sortByOrder( 'height', false )
|
||||||
|
.filter(function (item)
|
||||||
|
{
|
||||||
|
return item.block.trusted;
|
||||||
|
})
|
||||||
.slice(0, MAX_BINS)
|
.slice(0, MAX_BINS)
|
||||||
.reverse()
|
.reverse()
|
||||||
.map(function (item)
|
.map(function (item)
|
||||||
@ -330,6 +418,10 @@ History.prototype.getDifficulty = function()
|
|||||||
{
|
{
|
||||||
var difficultyHistory = _( this._items )
|
var difficultyHistory = _( this._items )
|
||||||
.sortByOrder( 'height', false )
|
.sortByOrder( 'height', false )
|
||||||
|
.filter(function (item)
|
||||||
|
{
|
||||||
|
return item.block.trusted;
|
||||||
|
})
|
||||||
.slice(0, MAX_BINS)
|
.slice(0, MAX_BINS)
|
||||||
.reverse()
|
.reverse()
|
||||||
.map(function (item)
|
.map(function (item)
|
||||||
@ -345,6 +437,10 @@ History.prototype.getTransactionsCount = function()
|
|||||||
{
|
{
|
||||||
var txCount = _( this._items )
|
var txCount = _( this._items )
|
||||||
.sortByOrder( 'height', false )
|
.sortByOrder( 'height', false )
|
||||||
|
.filter(function (item)
|
||||||
|
{
|
||||||
|
return item.block.trusted;
|
||||||
|
})
|
||||||
.slice(0, MAX_BINS)
|
.slice(0, MAX_BINS)
|
||||||
.reverse()
|
.reverse()
|
||||||
.map(function (item)
|
.map(function (item)
|
||||||
@ -360,6 +456,10 @@ History.prototype.getGasSpending = function()
|
|||||||
{
|
{
|
||||||
var gasSpending = _( this._items )
|
var gasSpending = _( this._items )
|
||||||
.sortByOrder( 'height', false )
|
.sortByOrder( 'height', false )
|
||||||
|
.filter(function (item)
|
||||||
|
{
|
||||||
|
return item.block.trusted;
|
||||||
|
})
|
||||||
.slice(0, MAX_BINS)
|
.slice(0, MAX_BINS)
|
||||||
.reverse()
|
.reverse()
|
||||||
.map(function (item)
|
.map(function (item)
|
||||||
@ -378,6 +478,10 @@ History.prototype.getAvgHashrate = function()
|
|||||||
|
|
||||||
var blocktimeHistory = _( this._items )
|
var blocktimeHistory = _( this._items )
|
||||||
.sortByOrder( 'height', false )
|
.sortByOrder( 'height', false )
|
||||||
|
.filter(function (item)
|
||||||
|
{
|
||||||
|
return item.block.trusted;
|
||||||
|
})
|
||||||
.slice(0, 64)
|
.slice(0, 64)
|
||||||
.map(function (item)
|
.map(function (item)
|
||||||
{
|
{
|
||||||
@ -394,6 +498,10 @@ History.prototype.getMinersCount = function()
|
|||||||
{
|
{
|
||||||
var miners = _( this._items )
|
var miners = _( this._items )
|
||||||
.sortByOrder( 'height', false )
|
.sortByOrder( 'height', false )
|
||||||
|
.filter(function (item)
|
||||||
|
{
|
||||||
|
return item.block.trusted;
|
||||||
|
})
|
||||||
.slice(0, MAX_BINS)
|
.slice(0, MAX_BINS)
|
||||||
.map(function (item)
|
.map(function (item)
|
||||||
{
|
{
|
||||||
@ -425,6 +533,10 @@ History.prototype.getCharts = function()
|
|||||||
{
|
{
|
||||||
var chartHistory = _( this._items )
|
var chartHistory = _( this._items )
|
||||||
.sortByOrder( 'height', false )
|
.sortByOrder( 'height', false )
|
||||||
|
.filter(function (item)
|
||||||
|
{
|
||||||
|
return item.block.trusted;
|
||||||
|
})
|
||||||
.slice(0, MAX_BINS)
|
.slice(0, MAX_BINS)
|
||||||
.reverse()
|
.reverse()
|
||||||
.map(function (item)
|
.map(function (item)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
var geoip = require('geoip-lite');
|
var geoip = require('geoip-lite');
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
var trusted = require('./utils/config');
|
||||||
|
|
||||||
var MAX_HISTORY = 40;
|
var MAX_HISTORY = 40;
|
||||||
var MAX_INACTIVE_TIME = 1000*60*60*4;
|
var MAX_INACTIVE_TIME = 1000*60*60*4;
|
||||||
@ -7,6 +8,7 @@ var MAX_INACTIVE_TIME = 1000*60*60*4;
|
|||||||
var Node = function(data)
|
var Node = function(data)
|
||||||
{
|
{
|
||||||
this.id = null;
|
this.id = null;
|
||||||
|
this.trusted = false;
|
||||||
this.info = {};
|
this.info = {};
|
||||||
this.geo = {}
|
this.geo = {}
|
||||||
this.stats = {
|
this.stats = {
|
||||||
@ -79,6 +81,11 @@ Node.prototype.setInfo = function(data, callback)
|
|||||||
|
|
||||||
if( !_.isUndefined(data.ip) )
|
if( !_.isUndefined(data.ip) )
|
||||||
{
|
{
|
||||||
|
if( trusted.indexOf(data.ip) >= 0 )
|
||||||
|
{
|
||||||
|
this.trusted = true;
|
||||||
|
}
|
||||||
|
|
||||||
this.setGeo(data.ip);
|
this.setGeo(data.ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,7 +367,7 @@ Node.prototype.getBlockNumber = function()
|
|||||||
|
|
||||||
Node.prototype.canUpdate = function()
|
Node.prototype.canUpdate = function()
|
||||||
{
|
{
|
||||||
return this.info.canUpdateHistory || false;
|
return (this.info.canUpdateHistory && this.trusted) || false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node.prototype.isInactiveAndOld = function()
|
Node.prototype.isInactiveAndOld = function()
|
||||||
|
14
lib/utils/config.js
Normal file
14
lib/utils/config.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
var trusted = [
|
||||||
|
'54.94.239.50',
|
||||||
|
'52.16.188.185',
|
||||||
|
'52.4.40.229',
|
||||||
|
'52.4.131.128',
|
||||||
|
'52.0.243.36',
|
||||||
|
'52.4.180.23',
|
||||||
|
'52.5.60.7',
|
||||||
|
'52.5.26.21',
|
||||||
|
'52.5.25.137',
|
||||||
|
'::ffff:127.0.0.1',
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = trusted;
|
@ -116,12 +116,12 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
|
|||||||
console.log('We are scheduling a reconnect operation', opts);
|
console.log('We are scheduling a reconnect operation', opts);
|
||||||
})
|
})
|
||||||
.on('data', function incoming(data) {
|
.on('data', function incoming(data) {
|
||||||
socketAction(data.action, data.data);
|
$scope.$apply(socketAction(data.action, data.data));
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('init', function(data)
|
socket.on('init', function(data)
|
||||||
{
|
{
|
||||||
socketAction("init", data.nodes);
|
$scope.$apply(socketAction("init", data.nodes));
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('client-latency', function(data)
|
socket.on('client-latency', function(data)
|
||||||
@ -142,7 +142,10 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
|
|||||||
_.forEach($scope.nodes, function (node, index) {
|
_.forEach($scope.nodes, function (node, index) {
|
||||||
// Init hashrate
|
// Init hashrate
|
||||||
if( _.isUndefined(node.stats.hashrate) )
|
if( _.isUndefined(node.stats.hashrate) )
|
||||||
$scope.nodes[index].stats.hashrate = 0;
|
node.stats.hashrate = 0;
|
||||||
|
|
||||||
|
// Init latency
|
||||||
|
latencyFilter(node);
|
||||||
|
|
||||||
// Init history
|
// Init history
|
||||||
if( _.isUndefined(data.history) )
|
if( _.isUndefined(data.history) )
|
||||||
@ -152,7 +155,7 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Init or recover pin
|
// Init or recover pin
|
||||||
$scope.nodes[index].pinned = ($scope.pinned.indexOf(node.id) >= 0 ? true : false);
|
node.pinned = ($scope.pinned.indexOf(node.id) >= 0 ? true : false);
|
||||||
});
|
});
|
||||||
|
|
||||||
if( $scope.nodes.length > 0 )
|
if( $scope.nodes.length > 0 )
|
||||||
@ -281,6 +284,9 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
|
|||||||
if( _.isUndefined($scope.nodes[index].pinned) )
|
if( _.isUndefined($scope.nodes[index].pinned) )
|
||||||
$scope.nodes[index].pinned = false;
|
$scope.nodes[index].pinned = false;
|
||||||
|
|
||||||
|
// Init latency
|
||||||
|
latencyFilter($scope.nodes[index]);
|
||||||
|
|
||||||
updateActiveNodes();
|
updateActiveNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,15 +359,19 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "latency":
|
case "latency":
|
||||||
|
if( !_.isUndefined(data.id) && !_.isUndefined(data.latency) )
|
||||||
|
{
|
||||||
var index = findIndex({id: data.id});
|
var index = findIndex({id: data.id});
|
||||||
|
|
||||||
if( !_.isUndefined(data.id) && index >= 0 )
|
if( index >= 0 )
|
||||||
{
|
{
|
||||||
var node = $scope.nodes[index];
|
var node = $scope.nodes[index];
|
||||||
|
|
||||||
if( !_.isUndefined(node) && !_.isUndefined(node.stats) && !_.isUndefined(node.stats.latency) )
|
if( !_.isUndefined(node) && !_.isUndefined(node.stats) && !_.isUndefined(node.stats.latency) && node.stats.latency !== data.latency )
|
||||||
{
|
{
|
||||||
$scope.nodes[index].stats.latency = data.latency;
|
node.stats.latency = data.latency;
|
||||||
|
latencyFilter(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,7 +386,7 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.$apply();
|
// $scope.$apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
function findIndex(search)
|
function findIndex(search)
|
||||||
@ -449,6 +459,7 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
|
|||||||
$scope.nodesTotal = $scope.nodes.length;
|
$scope.nodesTotal = $scope.nodes.length;
|
||||||
|
|
||||||
$scope.nodesActive = _.filter($scope.nodes, function (node) {
|
$scope.nodesActive = _.filter($scope.nodes, function (node) {
|
||||||
|
forkFilter(node);
|
||||||
return node.stats.active == true;
|
return node.stats.active == true;
|
||||||
}).length;
|
}).length;
|
||||||
|
|
||||||
@ -481,8 +492,49 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
|
|||||||
{
|
{
|
||||||
if( $scope.nodes.length )
|
if( $scope.nodes.length )
|
||||||
{
|
{
|
||||||
var bestBlock = _.max($scope.nodes, function (node) {
|
var chains = {};
|
||||||
|
var maxScore = 0;
|
||||||
|
|
||||||
|
_($scope.nodes)
|
||||||
|
.map(function (item)
|
||||||
|
{
|
||||||
|
maxScore += (item.trusted ? 50 : 1);
|
||||||
|
|
||||||
|
if( _.isUndefined(chains[item.stats.block.number]) )
|
||||||
|
chains[item.stats.block.number] = [];
|
||||||
|
|
||||||
|
if( _.isUndefined(chains[item.stats.block.number][item.stats.block.fork]) )
|
||||||
|
chains[item.stats.block.number][item.stats.block.fork] = {
|
||||||
|
fork: item.stats.block.fork,
|
||||||
|
count: 0,
|
||||||
|
trusted: 0,
|
||||||
|
score: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if(item.stats.block.trusted)
|
||||||
|
chains[item.stats.block.number][item.stats.block.fork].trusted++;
|
||||||
|
else
|
||||||
|
chains[item.stats.block.number][item.stats.block.fork].count++;
|
||||||
|
|
||||||
|
chains[item.stats.block.number][item.stats.block.fork].score = chains[item.stats.block.number][item.stats.block.fork].trusted * 50 + chains[item.stats.block.number][item.stats.block.fork].count;
|
||||||
|
})
|
||||||
|
.value();
|
||||||
|
|
||||||
|
$scope.maxScore = maxScore;
|
||||||
|
$scope.chains = _.reduce(chains, function (result, item, key)
|
||||||
|
{
|
||||||
|
result[key] = _.max(item, 'score');
|
||||||
|
return result;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
var bestBlock = _.max($scope.nodes, function (node)
|
||||||
|
{
|
||||||
|
if( $scope.chains[node.stats.block.number].fork === node.stats.block.fork && $scope.chains[node.stats.block.number].score / $scope.maxScore >= 0.5 )
|
||||||
|
{
|
||||||
return parseInt(node.stats.block.number);
|
return parseInt(node.stats.block.number);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}).stats.block.number;
|
}).stats.block.number;
|
||||||
|
|
||||||
if( bestBlock !== $scope.bestBlock )
|
if( bestBlock !== $scope.bestBlock )
|
||||||
@ -497,4 +549,64 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function forkFilter(node)
|
||||||
|
{
|
||||||
|
if( _.isUndefined(node.readable) )
|
||||||
|
node.readable = {};
|
||||||
|
|
||||||
|
if( $scope.chains[node.stats.block.number].fork === node.stats.block.fork && $scope.chains[node.stats.block.number].score / $scope.maxScore >= 0.5 )
|
||||||
|
{
|
||||||
|
node.readable.forkClass = 'hidden';
|
||||||
|
node.readable.forkMessage = '';
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $scope.chains[node.stats.block.number].fork !== node.stats.block.fork )
|
||||||
|
{
|
||||||
|
node.readable.forkClass = 'text-danger';
|
||||||
|
node.readable.forkMessage = 'Wrong chain.<br/>This chain is a fork.';
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $scope.chains[node.stats.block.number].score / $scope.maxScore < 0.5)
|
||||||
|
{
|
||||||
|
node.readable.forkClass = 'text-warning';
|
||||||
|
node.readable.forkMessage = 'May not be main chain.<br/>Waiting for more confirmations.';
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function latencyFilter(node)
|
||||||
|
{
|
||||||
|
if( _.isUndefined(node.readable) )
|
||||||
|
node.readable = {};
|
||||||
|
|
||||||
|
if( _.isUndefined(node.stats) ) {
|
||||||
|
node.readable.latencyClass = 'text-danger';
|
||||||
|
node.readable.latency = 'offline';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.stats.active === false)
|
||||||
|
{
|
||||||
|
node.readable.latencyClass = 'text-danger';
|
||||||
|
node.readable.latency = 'offline';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (node.stats.latency <= 100)
|
||||||
|
node.readable.latencyClass = 'text-success';
|
||||||
|
|
||||||
|
if (node.stats.latency > 100 && node.stats.latency <= 1000)
|
||||||
|
node.readable.latencyClass = 'text-warning';
|
||||||
|
|
||||||
|
if (node.stats.latency > 1000)
|
||||||
|
node.readable.latencyClass = 'text-danger';
|
||||||
|
|
||||||
|
node.readable.latency = node.stats.latency + ' ms';
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
@ -184,7 +184,7 @@ block content
|
|||||||
th
|
th
|
||||||
i.icon-bulb(data-toggle="tooltip", data-placement="top", title="Up-time", ng-click="orderTable(['-stats.uptime'], false)")
|
i.icon-bulb(data-toggle="tooltip", data-placement="top", title="Up-time", ng-click="orderTable(['-stats.uptime'], false)")
|
||||||
tbody(ng-cloak)
|
tbody(ng-cloak)
|
||||||
tr(ng-repeat='node in nodes | orderBy:predicate track by node.id', class="{{ node.stats | mainClass : bestBlock }}")
|
tr(ng-repeat='node in nodes | orderBy:predicate track by node.id', class="{{ node.stats | mainClass : bestBlock }}", id="node_{{node.id}}")
|
||||||
td.td-nodecheck
|
td.td-nodecheck
|
||||||
i(ng-click="pinNode(node.id)", class="{{ node.pinned | nodePinClass }}", data-toggle="tooltip", data-placement="right", data-original-title="Click to {{ node.pinned ? 'un' : '' }}pin")
|
i(ng-click="pinNode(node.id)", class="{{ node.pinned | nodePinClass }}", data-toggle="tooltip", data-placement="right", data-original-title="Click to {{ node.pinned ? 'un' : '' }}pin")
|
||||||
td.nodeInfo(rel="{{node.id}}")
|
td.nodeInfo(rel="{{node.id}}")
|
||||||
@ -194,12 +194,15 @@ block content
|
|||||||
i.icon-warning-o
|
i.icon-warning-o
|
||||||
td
|
td
|
||||||
div.small(ng-bind-html="node.info.node | nodeVersion")
|
div.small(ng-bind-html="node.info.node | nodeVersion")
|
||||||
td(class="{{ node.stats | latencyClass }}")
|
td(class="{{ node.readable.latencyClass }}")
|
||||||
span.small {{node.stats | latencyFilter}}
|
span.small {{ node.readable.latency }}
|
||||||
td(class="{{ node.stats.mining | hashrateClass : node.stats.active }}", ng-bind-html="node.stats.hashrate | hashrateFilter : node.stats.mining")
|
td(class="{{ node.stats.mining | hashrateClass : node.stats.active }}", ng-bind-html="node.stats.hashrate | hashrateFilter : node.stats.mining")
|
||||||
td(class="{{ node.stats.peers | peerClass : node.stats.active }}", style="padding-left: 11px;") {{node.stats.peers}}
|
td(class="{{ node.stats.peers | peerClass : node.stats.active }}", style="padding-left: 11px;") {{node.stats.peers}}
|
||||||
td(style="padding-left: 15px;") {{node.stats.pending}}
|
td(style="padding-left: 15px;") {{node.stats.pending}}
|
||||||
td(class="{{ node.stats | blockClass : bestBlock }}") {{'#'}}{{ node.stats.block.number | number }}
|
td(class="{{ node.stats | blockClass : bestBlock }}")
|
||||||
|
span(class="{{ node.readable.forkMessage ? node.readable.forkClass : '' }}") {{'#'}}{{ node.stats.block.number | number }}
|
||||||
|
a.small(data-toggle="tooltip", data-placement="top", data-html="true", data-original-title="{{ node.readable.forkMessage }}", class="{{ node.readable.forkClass }}")
|
||||||
|
i.icon-warning-o
|
||||||
td(class="{{ node.stats | blockClass : bestBlock }}")
|
td(class="{{ node.stats | blockClass : bestBlock }}")
|
||||||
span.small {{node.stats.block.hash | hashFilter}}
|
span.small {{node.stats.block.hash | hashFilter}}
|
||||||
td(class="{{ node.stats | blockClass : bestBlock }}")
|
td(class="{{ node.stats | blockClass : bestBlock }}")
|
||||||
|
Loading…
Reference in New Issue
Block a user