added fork check

This commit is contained in:
cubedro
2015-06-09 19:28:29 +03:00
parent 4b5bdf3e3e
commit f67529a75e
9 changed files with 270 additions and 60 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+6 -6
View File
@@ -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)
+161 -47
View File
@@ -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)
+8 -1
View File
@@ -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()
+14
View 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;
+74 -2
View File
@@ -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) )
+4 -1
View File
@@ -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 }}")