diff --git a/lib/services/block/index.js b/lib/services/block/index.js index 858c71f0..aba6c6b3 100644 --- a/lib/services/block/index.js +++ b/lib/services/block/index.js @@ -482,6 +482,7 @@ BlockService.prototype._onBlock = function(block) { BlockService.prototype._setListeners = function() { this._header.once('headers', this._onAllHeaders.bind(this)); + this._header.on('reorg', this._handleReorg.bind(this)); }; diff --git a/lib/services/header/index.js b/lib/services/header/index.js index dc4bdd74..18369321 100644 --- a/lib/services/header/index.js +++ b/lib/services/header/index.js @@ -24,6 +24,7 @@ var HeaderService = function(options) { this.subscriptions = {}; this.subscriptions.block = []; + this._checkpoint = options.checkpoint || 2000; this.GENESIS_HASH = constants.BITCOIN_GENESIS_HASH[this.node.network]; }; @@ -93,9 +94,10 @@ HeaderService.prototype.start = function(callback) { function(tip, next) { self._tip = tip; self._originalTip = { - height: self._tip.height, - hash: self._tip.hash + hash: self._tip.hash, + height: self._tip.height }; + if (self._tip.height === 0) { var genesisHeader = { @@ -128,9 +130,6 @@ HeaderService.prototype.start = function(callback) { return callback(err); } - if (self._headers.size > 0) { - self._tip.hash = self._headers.getLastIndex().hash; - } self._setListeners(); self._startSubscriptions(); callback(); @@ -224,7 +223,7 @@ HeaderService.prototype._onHeaders = function(headers) { value: self._encoding.encodeHeaderValue(header) }); prevHeader = header; - self._headers.set(header.hash, header); + self._headers.set(header.hash, header, header.height); } var tipOps = utils.encodeTip(self._tip, self.name); @@ -243,24 +242,33 @@ HeaderService.prototype._onHeaders = function(headers) { log.debug('Header Service: download complete.'); - // have we reorg'ed since we've been shutdown? - if (self._tip.height > 0) { - - var headerHash = self._headers.getIndex(self._tip.height).hash; - if (self._tip.hash !== headerHash) { - - self.emit('reorg', headerHash, self._headers); - return; - - } + // at this point, we can check our header list to see if our starting tip diverged from the tip + // that we have now + if (self._detectReorg()) { + self._handleReorg(); + return; } log.debug('Header Service: emitting headers to block service.'); self.emit('headers', self._headers); + }); }; +HeaderService.prototype._detectReorg = function() { + // is our original tip's height and hash the same after we rewound by the checkpoint amount of blocks + // and re-imported? If so, then we've reorg'ed since we've been shut down. + if (this._originalTip.hash !== this._headers.getIndex(this._originalTip.height).hash) { + return true; + } + return false; +}; + +HeaderService.prototype._handleReorg = function() { + this.emit('reorg', this._headers.getIndex(this._originalTip.height).hash, this._headers); +}; + HeaderService.prototype._setListeners = function() { this._p2p.once('bestHeight', this._onBestHeight.bind(this)); @@ -287,10 +295,10 @@ HeaderService.prototype._startSync = function() { HeaderService.prototype._sync = function() { - log.debug('Header Service: download progress: ' + this._tip.height + '/' + - this._bestHeight + ' (' + (this._tip.height / this._bestHeight*100).toFixed(2) + '%)'); + log.debug('Header Service: download progress: ' + this._tip.height + '/' + + this._bestHeight + ' (' + (this._tip.height / this._bestHeight*100).toFixed(2) + '%)'); - this._p2p.getHeaders({ startHash: this._tip.hash }); + this._p2p.getHeaders({ startHash: this._tip.hash }); }; @@ -302,8 +310,14 @@ HeaderService.prototype._getPersistedHeaders = function(callback) { var self = this; + var startingHeight = self._tip.height; + if (self._tip.height > self._checkpoint) { + self._tip.height -= self._checkpoint; + } + var removalOps = []; + var start = self._encoding.encodeHeaderKey(0); - var end = self._encoding.encodeHeaderKey(self._tip.height + 1); + var end = self._encoding.encodeHeaderKey(startingHeight + 1); log.debug('Getting persisted headers from genesis block to block ' + (self._tip.height + 1)); @@ -320,14 +334,24 @@ HeaderService.prototype._getPersistedHeaders = function(callback) { }); stream.on('data', function(data) { - self._headers.set(self._encoding.decodeHeaderKey(data.key).hash, self._encoding.decodeHeaderValue(data.value)); + var header = self._encoding.decodeHeaderValue(data.value); + // any records with a height greater than our current tip height can be scheduled for removal + if (header.height > self._tip.height) { + removalOps.push({ + type: 'del', + key: data.key + }); + return; + } + self._headers.set(self._encoding.decodeHeaderKey(data.key).hash, header, header.height); }); stream.on('end', function() { if (streamErr) { return streamErr; } - callback(); + self._tip.hash = self._headers.getIndex(self._tip.height).hash; + self._db.batch(removalOps, callback); }); }; diff --git a/lib/utils.js b/lib/utils.js index 4aaa1683..c2fbe274 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -76,9 +76,14 @@ utils.SimpleMap = function SimpleMap() { return array[object[key]]; }; - this.set = function (key, value) { + this.set = function (key, value, pos) { object[key] = array.length; - array.push(value); + + if (pos >= 0) { + array[pos] = value; + } else { + array.push(value); + } this.size = array.length; this.length = array.length;