From ee9a3682b4339f8ee5699e586f93a72af7322d6e Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Thu, 6 Jul 2017 14:54:53 -0400 Subject: [PATCH] wip --- lib/services/block/index.js | 55 +++++++++++++++++++------------ test/services/block/index.unit.js | 18 ++++++++++ 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/lib/services/block/index.js b/lib/services/block/index.js index cc722771..7159b758 100644 --- a/lib/services/block/index.js +++ b/lib/services/block/index.js @@ -12,18 +12,17 @@ var assert = require('assert'); var BN = require('bn.js'); var consensus = require('bcoin').consensus; var constants = require('../../constants'); -var async = require('async'); var BlockService = function(options) { BaseService.call(this, options); this._tip = null; - this._headerTip = null; this._p2p = this.node.services.p2p; this._db = this.node.services.db; this._subscriptions = {}; this._subscriptions.block = []; this._subscriptions.reorg = []; - this._heightIndex = []; // tracks block height to block hash + this._latestHeaderHashReceived = null; + this._heightIndex = null; // tracks block height to block hash this._blockHeaderQueue = LRU({ max: 50, length: function(n) { @@ -113,6 +112,21 @@ BlockService.prototype.unsubscribe = function(name, emitter) { }; // --- start private prototype functions + +BlockService.prototype._applyHeaderHeights = function() { + + var genesis = constants.BITCOIN_GENESIS_HASH[this.node.getNetworkName()]; + this._heightIndex = new Array(this._blockHeaderQueue.length + 1); + this._heightIndex.push(genesis); + + var _tip = this._latestHeaderHashReceived; + while (_tip !== genesis) { + this._heightIndex.push(_tip); + _tip = this._blockHeaderQueue.get(_tip).prevHash; + } + +}; + BlockService.prototype._blockAlreadyProcessed = function(block) { return this._blockHeaderQueue.get(block.hash) ? true : false; @@ -150,13 +164,16 @@ BlockService.prototype._cacheBlock = function(block) { BlockService.prototype._cacheHeader = function(header) { - // 1. save to in-memory cache first + // 1. put a height on the haeder + header.height = ++this._latestBlockHeight; + + // 2. save to in-memory cache first this._blockHeaderQueue.set(header.hash, header.toObject()); - // 2. save to in-memory map block index + // 3. save to in-memory map block index this._heightIndex.push(header); - // 3. get operations + // 4. get operations return this._getHeaderOperations(header); }; @@ -470,6 +487,11 @@ BlockService.prototype._isOrphanBlock = function(block) { BlockService.prototype._loadHeaders = function(cb) { + // on initial sync of headers...we don't have block heights with the headers + // we just have a list of headers that have a prevhash pointer and that's about it + // we can't be sure that headers have arrived in order, so we can pick the last header + // that came in and work backwards to the genesis block. If we don't process exactly the + // number of total blocks, then we know we did not really have the last header and we should try again var self = this; var start = self._encoding.encodeHashKey(new Array(65).join('0')); var end = Buffer.concat([ utils.getTerminalKey(start.slice(0, 3)), start.slice(3) ]); @@ -513,18 +535,6 @@ BlockService.prototype._loadHeights = function(cb) { BlockService.prototype._loadTip = function() { var self = this; self._db.getServiceTip('block'); - async.parallel([ - self._loadHeaders.bind(self), - self._loadHeights.bind(self) - ], function(err) { - if (err) { - self._onDbError(err); - return; - } - self._setHeaderTip(); - self._startSubscriptions(); - self._startSync('header'); - }); }; BlockService.prototype._onBestHeight = function(height) { @@ -606,6 +616,8 @@ BlockService.prototype._onTipBlock = function(tip) { } + self._startSync('header'); + }; BlockService.prototype._reportBootStatus = function() { @@ -692,7 +704,7 @@ BlockService.prototype._setTip = function(block) { BlockService.prototype._startSync = function(type) { - var currentHeight = type === 'block' ? this._tip.height : this._headerTip.height; + var currentHeight = type === 'block' ? this._tip.height : 0; //we gather all headers on each boot this._numNeeded = this._bestHeight - currentHeight; this._numCompleted = currentHeight; if (this._numNeeded <= 0) { @@ -750,7 +762,7 @@ BlockService.prototype._sync = function(type) { if (counter > 0) { - log.info(name + ' download progress: ' + this._numCompleted + '/' + + log.debug(name + ' download progress: ' + this._numCompleted + '/' + this._numNeeded + ' (' + (this._numCompleted/this._numNeeded*100).toFixed(2) + '%)'); func.call(this._p2p, obj); return; @@ -758,7 +770,8 @@ BlockService.prototype._sync = function(type) { } if ('header') { - this._startSync('block'); + // we are done loading headers, proceed to number them + this._applyHeaderHeights(this._startSync.bind(this, 'block')); } }; diff --git a/test/services/block/index.unit.js b/test/services/block/index.unit.js index c593cfbe..b4d2cf86 100644 --- a/test/services/block/index.unit.js +++ b/test/services/block/index.unit.js @@ -22,6 +22,24 @@ describe('Block Service', function() { blockService._encoding = new Encoding(new Buffer('0000', 'hex')); }); + describe.only('#_applyHeaderHeights', function() { + it('should apply heights to the list of headers', function() { + var genesis = '0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206'; + blockService._blockHeaderQueue = LRU(100); + blockService._blockHeaderQueue.set(genesis, { prevHash: '00' }); + blockService._latestHeaderHashReceived = '100'; + blockService.node = { getNetworkName: function() { return 'regtest'; } }; + for(var i = 0; i < 99; i++) { + var prevHash = i.toString(); + if (i === 0) { + prevHash = genesis; + } + blockService._blockHeaderQueue.set((i+1).toString(), { prevHash: prevHash }); + } + blockService._applyHeaderHeights(); + }); + }); + describe('#_blockAlreadyProcessed', function() { it('should detect that a block has already been delivered to us', function() {