added fork check
This commit is contained in:
parent
4b5bdf3e3e
commit
f67529a75e
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
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());
|
||||
|
||||
var block = this._blockchain.add(stats.block, id);
|
||||
var block = this._blockchain.add(stats.block, id, node.trusted);
|
||||
|
||||
if (!block)
|
||||
{
|
||||
|
@ -61,7 +61,7 @@ Collection.prototype.addBlock = function(id, stats, callback)
|
|||
{
|
||||
this._blockchain.clean(this.getBestBlockFromItems());
|
||||
|
||||
var block = this._blockchain.add(stats, id);
|
||||
var block = this._blockchain.add(stats, id, node.trusted);
|
||||
|
||||
if (!block)
|
||||
{
|
||||
|
@ -121,7 +121,7 @@ Collection.prototype.addHistory = function(id, blocks, callback)
|
|||
|
||||
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();
|
||||
|
@ -262,9 +262,9 @@ Collection.prototype.getHistory = function()
|
|||
|
||||
Collection.prototype.getBestBlockFromItems = function()
|
||||
{
|
||||
return _.result(_.max(this._items, function(item) {
|
||||
return item.stats.block.number;
|
||||
}), 'stats.block.number', 0);
|
||||
return Math.max(this._blockchain.bestBlockNumber(), _.result(_.max(this._items, function(item) {
|
||||
return ( !item.trusted ? 0 : item.stats.block.number );
|
||||
}), 'stats.block.number', 0));
|
||||
}
|
||||
|
||||
Collection.prototype.canNodeUpdate = function(id)
|
||||
|
|
208
lib/history.js
208
lib/history.js
|
@ -1,12 +1,13 @@
|
|||
var _ = require('lodash');
|
||||
var d3 = require('d3');
|
||||
|
||||
var MAX_HISTORY = 1000;
|
||||
var MAX_HISTORY = 2000;
|
||||
|
||||
var MAX_PEER_PROPAGATION = 40;
|
||||
var MIN_PROPAGATION_RANGE = 0;
|
||||
var MAX_PROPAGATION_RANGE = 10000;
|
||||
|
||||
var MAX_UNCLES = 1000;
|
||||
var MAX_UNCLES_PER_BIN = 25;
|
||||
var MAX_BINS = 40;
|
||||
|
||||
|
@ -14,86 +15,110 @@ var History = function History(data)
|
|||
{
|
||||
this._items = [];
|
||||
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;
|
||||
|
||||
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 forkIndex = -1;
|
||||
|
||||
var now = _.now();
|
||||
|
||||
block.trusted = trusted;
|
||||
block.arrived = now;
|
||||
block.received = now;
|
||||
block.propagation = 0;
|
||||
block.fork = 0;
|
||||
|
||||
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 } );
|
||||
|
||||
// Check if node already check a fork with this height
|
||||
forkIndex = compareForks(historyBlock, block);
|
||||
|
||||
if( propIndex === -1 )
|
||||
{
|
||||
block.arrived = historyBlock.block.arrived;
|
||||
block.received = now;
|
||||
block.propagation = now - historyBlock.block.received;
|
||||
// Node didn't submit this block before
|
||||
if( forkIndex >= 0 && !_.isUndefined(historyBlock.forks[forkIndex]) )
|
||||
{
|
||||
// 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({
|
||||
node: id,
|
||||
trusted: trusted,
|
||||
fork: forkIndex,
|
||||
received: now,
|
||||
propagation: block.propagation
|
||||
});
|
||||
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
block.arrived = historyBlock.block.arrived;
|
||||
block.received = historyBlock.propagTimes[propIndex].received;
|
||||
block.propagation = historyBlock.propagTimes[propIndex].propagation;
|
||||
|
||||
if(historyBlock.hash !== block.hash || historyBlock.totalDifficulty !== block.totalDifficulty || historyBlock.transactions.length !== block.transactions.length)
|
||||
// Node submited the block before
|
||||
if( forkIndex >= 0 && !_.isUndefined(historyBlock.forks[forkIndex]) )
|
||||
{
|
||||
historyBlock.hash = block.hash;
|
||||
historyBlock.parentHash = block.parentHash;
|
||||
historyBlock.nonce = block.nonce;
|
||||
historyBlock.sha3Uncles = block.sha3Uncles;
|
||||
historyBlock.transactionsRoot = block.transactionsRoot;
|
||||
historyBlock.stateRoot = block.stateRoot;
|
||||
historyBlock.miner = block.miner;
|
||||
historyBlock.difficulty = block.difficulty;
|
||||
historyBlock.totalDifficulty = block.totalDifficulty;
|
||||
historyBlock.size = block.size;
|
||||
historyBlock.extraData = block.extraData;
|
||||
historyBlock.gasLimit = block.gasLimit;
|
||||
historyBlock.gasUsed = block.gasUsed;
|
||||
historyBlock.timestamp = block.timestamp;
|
||||
historyBlock.transactions = block.transactions;
|
||||
historyBlock.uncles = block.uncles;
|
||||
// Matching fork found => update data
|
||||
block.arrived = historyBlock.forks[forkIndex].arrived;
|
||||
|
||||
changed = true;
|
||||
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.propagation = historyBlock.propagTimes[propIndex].propagation;
|
||||
|
||||
forkIndex = historyBlock.forks.push(block) - 1;
|
||||
historyBlock.forks[forkIndex].fork = forkIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if( trusted && !compareBlocks(historyBlock.block, historyBlock.forks[forkIndex]) )
|
||||
{
|
||||
// If source is trusted update the main block
|
||||
historyBlock.forks[forkIndex].trusted = trusted;
|
||||
historyBlock.block = historyBlock.forks[forkIndex];
|
||||
}
|
||||
|
||||
block.fork = forkIndex;
|
||||
|
||||
changed = true;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Couldn't find block with this height
|
||||
|
||||
// Getting previous max block
|
||||
var prevBlock = this.prevMaxBlock(block.number);
|
||||
|
||||
if( prevBlock )
|
||||
|
@ -111,13 +136,16 @@ History.prototype.add = function(block, id)
|
|||
var item = {
|
||||
height: block.number,
|
||||
block: block,
|
||||
forks: [block],
|
||||
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({
|
||||
node: id,
|
||||
trusted: trusted,
|
||||
fork: 0,
|
||||
received: now,
|
||||
propagation: block.propagation
|
||||
});
|
||||
|
@ -137,6 +165,43 @@ History.prototype.add = function(block, id)
|
|||
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)
|
||||
{
|
||||
this._items.unshift(block);
|
||||
|
@ -158,7 +223,7 @@ History.prototype.clean = function(max)
|
|||
console.log("History items before:", this._items.length);
|
||||
|
||||
this._items = _(this._items).filter(function(item) {
|
||||
return item.height <= max;
|
||||
return (item.height <= max && item.block.trusted === false);
|
||||
}).value();
|
||||
|
||||
console.log("History items after:", this._items.length);
|
||||
|
@ -202,6 +267,22 @@ History.prototype.bestBlockNumber = function()
|
|||
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)
|
||||
{
|
||||
var propagation = new Array( MAX_PEER_PROPAGATION );
|
||||
|
@ -290,6 +371,11 @@ History.prototype.getUncleCount = function()
|
|||
{
|
||||
var uncles = _( this._items )
|
||||
.sortByOrder( 'height', false )
|
||||
.filter(function (item)
|
||||
{
|
||||
return item.block.trusted;
|
||||
})
|
||||
.slice(0, MAX_UNCLES)
|
||||
.map(function (item)
|
||||
{
|
||||
return item.block.uncles.length;
|
||||
|
@ -313,6 +399,10 @@ History.prototype.getBlockTimes = function()
|
|||
{
|
||||
var blockTimes = _( this._items )
|
||||
.sortByOrder( 'height', false )
|
||||
.filter(function (item)
|
||||
{
|
||||
return item.block.trusted;
|
||||
})
|
||||
.slice(0, MAX_BINS)
|
||||
.reverse()
|
||||
.map(function (item)
|
||||
|
@ -328,6 +418,10 @@ History.prototype.getDifficulty = function()
|
|||
{
|
||||
var difficultyHistory = _( this._items )
|
||||
.sortByOrder( 'height', false )
|
||||
.filter(function (item)
|
||||
{
|
||||
return item.block.trusted;
|
||||
})
|
||||
.slice(0, MAX_BINS)
|
||||
.reverse()
|
||||
.map(function (item)
|
||||
|
@ -343,6 +437,10 @@ History.prototype.getTransactionsCount = function()
|
|||
{
|
||||
var txCount = _( this._items )
|
||||
.sortByOrder( 'height', false )
|
||||
.filter(function (item)
|
||||
{
|
||||
return item.block.trusted;
|
||||
})
|
||||
.slice(0, MAX_BINS)
|
||||
.reverse()
|
||||
.map(function (item)
|
||||
|
@ -358,6 +456,10 @@ History.prototype.getGasSpending = function()
|
|||
{
|
||||
var gasSpending = _( this._items )
|
||||
.sortByOrder( 'height', false )
|
||||
.filter(function (item)
|
||||
{
|
||||
return item.block.trusted;
|
||||
})
|
||||
.slice(0, MAX_BINS)
|
||||
.reverse()
|
||||
.map(function (item)
|
||||
|
@ -376,6 +478,10 @@ History.prototype.getAvgHashrate = function()
|
|||
|
||||
var blocktimeHistory = _( this._items )
|
||||
.sortByOrder( 'height', false )
|
||||
.filter(function (item)
|
||||
{
|
||||
return item.block.trusted;
|
||||
})
|
||||
.slice(0, 64)
|
||||
.map(function (item)
|
||||
{
|
||||
|
@ -392,6 +498,10 @@ History.prototype.getMinersCount = function()
|
|||
{
|
||||
var miners = _( this._items )
|
||||
.sortByOrder( 'height', false )
|
||||
.filter(function (item)
|
||||
{
|
||||
return item.block.trusted;
|
||||
})
|
||||
.slice(0, MAX_BINS)
|
||||
.map(function (item)
|
||||
{
|
||||
|
@ -423,6 +533,10 @@ History.prototype.getCharts = function()
|
|||
{
|
||||
var chartHistory = _( this._items )
|
||||
.sortByOrder( 'height', false )
|
||||
.filter(function (item)
|
||||
{
|
||||
return item.block.trusted;
|
||||
})
|
||||
.slice(0, MAX_BINS)
|
||||
.reverse()
|
||||
.map(function (item)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var geoip = require('geoip-lite');
|
||||
var _ = require('lodash');
|
||||
var trusted = require('./utils/config');
|
||||
|
||||
var MAX_HISTORY = 40;
|
||||
var MAX_INACTIVE_TIME = 1000*60*60*4;
|
||||
|
@ -7,6 +8,7 @@ var MAX_INACTIVE_TIME = 1000*60*60*4;
|
|||
var Node = function(data)
|
||||
{
|
||||
this.id = null;
|
||||
this.trusted = false;
|
||||
this.info = {};
|
||||
this.geo = {}
|
||||
this.stats = {
|
||||
|
@ -79,6 +81,11 @@ Node.prototype.setInfo = function(data, callback)
|
|||
|
||||
if( !_.isUndefined(data.ip) )
|
||||
{
|
||||
if( trusted.indexOf(data.ip) >= 0 )
|
||||
{
|
||||
this.trusted = true;
|
||||
}
|
||||
|
||||
this.setGeo(data.ip);
|
||||
}
|
||||
|
||||
|
@ -360,7 +367,7 @@ Node.prototype.getBlockNumber = function()
|
|||
|
||||
Node.prototype.canUpdate = function()
|
||||
{
|
||||
return this.info.canUpdateHistory || false;
|
||||
return (this.info.canUpdateHistory && this.trusted) || false;
|
||||
}
|
||||
|
||||
Node.prototype.isInactiveAndOld = function()
|
||||
|
|
|
@ -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;
|
|
@ -459,6 +459,7 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
|
|||
$scope.nodesTotal = $scope.nodes.length;
|
||||
|
||||
$scope.nodesActive = _.filter($scope.nodes, function (node) {
|
||||
forkFilter(node);
|
||||
return node.stats.active == true;
|
||||
}).length;
|
||||
|
||||
|
@ -491,8 +492,49 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
|
|||
{
|
||||
if( $scope.nodes.length )
|
||||
{
|
||||
var bestBlock = _.max($scope.nodes, function (node) {
|
||||
return parseInt(node.stats.block.number);
|
||||
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 0;
|
||||
}).stats.block.number;
|
||||
|
||||
if( bestBlock !== $scope.bestBlock )
|
||||
|
@ -508,6 +550,36 @@ 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) )
|
||||
|
|
|
@ -199,7 +199,10 @@ block content
|
|||
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(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 }}")
|
||||
span.small {{node.stats.block.hash | hashFilter}}
|
||||
td(class="{{ node.stats | blockClass : bestBlock }}")
|
||||
|
|
Loading…
Reference in New Issue