This commit is contained in:
Chris Kleeschulte 2017-06-28 18:54:15 -04:00
parent 1cf3d6ccd2
commit 90b0e0e94b
2 changed files with 50 additions and 60 deletions

View File

@ -25,7 +25,7 @@ var BlockService = function(options) {
return n.length * (1 * 1024 * 1024); // 50 MB of blocks return n.length * (1 * 1024 * 1024); // 50 MB of blocks
} }
}); // header -> block }); // header -> block
this._chainTips = LRU(50); // chain tip -> [ tip-1 hash, tip-2 hash, tip-N hash] this._chainTips = LRU(50); // chain tip hash -> [ tip-prev hash, tip-prevprev hash, tip-prevprevprev hash, ... ]
}; };
inherits(BlockService, BaseService); inherits(BlockService, BaseService);
@ -47,22 +47,23 @@ BlockService.prototype.start = function(callback) {
self._setListeners(); self._setListeners();
callback(); callback();
}); });
}; };
BlockService.prototype.stop = function(callback) { BlockService.prototype.stop = function(callback) {
if (callback) { if (callback) {
setImmediate(callback); setImmediate(callback);
} }
}; };
BlockService.prototype.getAPIMethods = function() { BlockService.prototype.getAPIMethods = function() {
var methods = [ var methods = [];
['processBlockOperations', this, this.processBlockOperations, 1]
];
return methods; return methods;
}; };
BlockService.prototype.getPublishEvents = function() { BlockService.prototype.getPublishEvents = function() {
return [ return [
{ {
name: 'block/block', name: 'block/block',
@ -77,61 +78,35 @@ BlockService.prototype.getPublishEvents = function() {
unsubscribe: this.unsubscribe.bind(this, 'reorg') unsubscribe: this.unsubscribe.bind(this, 'reorg')
} }
]; ];
}; };
BlockService.prototype.subscribe = function(name, emitter) { BlockService.prototype.subscribe = function(name, emitter) {
this._subscriptions[name].push(emitter); this._subscriptions[name].push(emitter);
log.info(emitter.remoteAddress, 'subscribe:', 'block/' + name, 'total:', this._subscriptions[name].length); log.info(emitter.remoteAddress, 'subscribe:', 'block/' + name, 'total:', this._subscriptions[name].length);
}; };
BlockService.prototype.unsubscribe = function(name, emitter) { BlockService.prototype.unsubscribe = function(name, emitter) {
var index = this._subscriptions[name].indexOf(emitter); var index = this._subscriptions[name].indexOf(emitter);
if (index > -1) { if (index > -1) {
this._subscriptions[name].splice(index, 1); this._subscriptions[name].splice(index, 1);
} }
log.info(emitter.remoteAddress, 'unsubscribe:', 'block/' + name, 'total:', this._subscriptions[name].length); log.info(emitter.remoteAddress, 'unsubscribe:', 'block/' + name, 'total:', this._subscriptions[name].length);
};
BlockService.prototype._printTipInfo = function(prependedMessage) {
log.info(
prependedMessage + ' Serial Tip: ' + this.tip.hash +
' Concurrent tip: ' + this.concurrentTip.hash
);
}; };
BlockService.prototype._reportBootStatus = function() { BlockService.prototype._reportBootStatus = function() {
var blockInfoString = utils.getBlockInfoString(this.tip.height, this.bestHeight); var blockInfoString = utils.getBlockInfoString(this.tip.height, this.bestHeight);
log.info('Block Service tip is currently height: ' + this.tip.height + ' hash: ' + log.info('Block Service tip is currently height: ' + this.tip.height + ' hash: ' +
this.tip.hash + ' P2P network best height: ' + this.bestHeight + '. Block Service is: ' + this.tip.hash + ' P2P network best height: ' + this._bestHeight + '. Block Service is: ' +
blockInfoString); blockInfoString);
};
BlockService.prototype.processBlockOperations = function(opts, callback) {
if (!_.isArray(opts.operations)) {
return;
}
var self = this;
self._db.batch(opts.operations, function(err) {
if(err) {
return callback(err);
}
if (!opts.serviceName) {
opts.serviceName = 'unknown';
}
self.setTip(opts);
self._reportStatus(opts.serviceName);
callback();
});
}; };
@ -155,18 +130,21 @@ BlockService.prototype._startSubscriptions = function() {
}; };
BlockService.prototype._onHeaders = function(headers) { BlockService.prototype._onHeaders = function(headers) {
log.info('New headers received.'); log.info('New headers received.');
this._cacheHeaders(headers); this._cacheHeaders(headers);
}; };
BlockService.prototype._blockAlreadyProcessed = function(block) { BlockService.prototype._blockAlreadyProcessed = function(block) {
return this._blockHeaderQueue.get(block.hash); return this._blockHeaderQueue.get(block.hash);
}; };
BlockService.prototype._mergeBlockIntoChainTips = function(block) { BlockService.prototype._mergeBlockIntoChainTips = function(block) {
var prevHash = utils.reverseBufferToString(block.header.prevHash); var prevHash = utils.reverseBufferToString(block.header.prevHash);
var hasChildren = false;
var chain = this._chainTips.get(prevHash); var chain = this._chainTips.get(prevHash);
@ -177,8 +155,7 @@ BlockService.prototype._mergeBlockIntoChainTips = function(block) {
} }
var longestChain = this._findLongestChainForHash(prevHash); var longestChain = this._findLongestChainForHash(prevHash);
hasChildren = this._setChainOnTip(block.hash, chain, longestChain); var hasChildren = this._setChainOnTip(block.hash, chain, longestChain);
if (!hasChildren) { if (!hasChildren) {
this._chainTips.set(block.hash, chain || longestChain); this._chainTips.set(block.hash, chain || longestChain);
@ -209,20 +186,28 @@ BlockService.prototype._setChainOnTip = function(hash, chain, longestPrevChain)
}; };
BlockService.prototype._findLongestChainForHash = function(hash) { BlockService.prototype._findLongestChainForHash = function(hash) {
var longestChain = []; var longestChain = [];
var keys = this._chainTips.keys(); var keys = this._chainTips.keys();
for(var j = 0; j < keys.length; j++) {
var key = keys[j]; for(var i = 0; i < keys.length; i++) {
var key = keys[i];
var searchChain = this._chainTips.get(key); var searchChain = this._chainTips.get(key);
assert(searchChain.length > 0, 'chain tips collection appears to be invalid'); assert(searchChain.length > 0, 'chain tips collection appears to be invalid');
var chainIndex = searchChain.indexOf(hash); var chainIndex = searchChain.indexOf(hash);
if (chainIndex > -1) { if (chainIndex > -1) {
var chain = searchChain.slice(chainIndex); var chain = searchChain.slice(chainIndex);
if (chain.length > longestChain.length) { if (chain.length > longestChain.length) {
longestChain = chain; longestChain = chain;
} }
} }
} }
longestChain = longestChain.length <= 1 ? [hash] : longestChain; longestChain = longestChain.length <= 1 ? [hash] : longestChain;
return longestChain; return longestChain;
}; };
@ -261,16 +246,24 @@ BlockService.prototype._onBlock = function(block) {
}; };
BlockService.prototype._selectActiveChain = function(block) { BlockService.prototype._selectActiveChain = function(hash) {
// the active chain is the one that the passed in block hash has as its tip.
// there can only be one without there being a ambiguous situation.
// If more than one chain does have the latest incoming block, then we have a reorg
// situation on our hands and the active chain will be decided elsewhere
}; };
BlockService.prototype._getAllUnsentBlocksFromActiveChain = function(tip) { BlockService.prototype._getAllUnsentBlocksFromActiveChain = function(block) {
var blocksToSend = [block];
if (!this._chainTips.get(block.hash)) { if (!this._chainTips.get(block.hash)) {
var keys = this._chainTips.keys(); var keys = this._chainTips.keys();
for(var i = 0; i < keys.length; i++) { for(var i = 0; i < keys.length; i++) {
var key = keys[i]; var key = keys[i];
var searchChain = this._chainTips.get(key); var searchChain = this._chainTips.get(key);
var index = searchChain.indexOf(block.hash); var index = searchChain.indexOf(block.hash);
@ -281,7 +274,6 @@ BlockService.prototype._getAllUnsentBlocksFromActiveChain = function(tip) {
blocksToSend.concat(additionalBlocks); blocksToSend.concat(additionalBlocks);
blocksToSend.reverse(); blocksToSend.reverse();
break; break;
} }
} }
} }
@ -358,8 +350,6 @@ BlockService.prototype._findCommonAncestor = function(block) {
var oldChain = this._chainTips.get(this.tip.hash); var oldChain = this._chainTips.get(this.tip.hash);
var newChain = this._chainTips.get(block.hash); var newChain = this._chainTips.get(block.hash);
// if there is more than one chain tip collection that contains this blocks, how do we know which one if the real chain?
// the blocks to come will decide for sure.
if (!newChain) { if (!newChain) {
newChain = this._findLongestChainForHash(block.hash); newChain = this._findLongestChainForHash(block.hash);
} }
@ -404,9 +394,9 @@ BlockService.prototype._determineBlockState = function(block) {
In point 1, waiting longer for blocks to arrive is the best action. In point 1, waiting longer for blocks to arrive is the best action.
In point 2, waiting won't help. The block's parent block may be in an orphaned chain and this chain may In point 2, waiting won't help. The block's parent block may be in an orphaned chain and this chain may
never become the main chain. Also, your peers may nor may not give you all the parent blocks for this orphan chain. never become the main chain. Also, your peers may nor may not give you all the parent blocks for this orphan
It is best to not assign this block a height until all of its parents are linked. We should, however, call getBlocks with chain. It is best to not assign this block a height until all of its parents are linked. We should, however,
startHash of our tip and end hash of the list of orphaned blocks periodically. call getBlocks with startHash of our tip and end hash of the list of orphaned blocks periodically.
*/ */

View File

@ -59,7 +59,6 @@ describe('Block Service', function() {
blockService._mergeBlockIntoChainTips(block); blockService._mergeBlockIntoChainTips(block);
}); });
expect(blockService._chainTips.length).to.equal(2); expect(blockService._chainTips.length).to.equal(2);
expect(blockService._chainTips.get('ee')).to.deep.equal(['dd', 'cc', 'aa', '00']); expect(blockService._chainTips.get('ee')).to.deep.equal(['dd', 'cc', 'aa', '00']);
expect(blockService._chainTips.get('bb')).to.deep.equal(['aa', '00']); expect(blockService._chainTips.get('bb')).to.deep.equal(['aa', '00']);
@ -156,16 +155,17 @@ describe('Block Service', function() {
}); });
describe('Send all unsent blocks from main chain', function() { describe('Send all unsent blocks from main chain', function() {
var blocks = ['ee','aa','bb','dd','cc'];
var prevBlocks = ['dd','00','aa','cc','bb'];
blocks.forEach(function(n, index) { it('should send all unsent blocks from the active/main chain', function() {
var block = { header: { prevHash: new Buffer(prevBlocks[index], 'hex') }, hash: n }; var blocks = ['ee','aa','bb','dd','cc'];
blockService._mergeBlockIntoChainTips(block); var prevBlocks = ['dd','00','aa','cc','bb'];
blocks.forEach(function(n, index) {
var block = { header: { prevHash: new Buffer(prevBlocks[index], 'hex') }, hash: n };
blockService._mergeBlockIntoChainTips(block);
});
}); });
}); });
}); });