diff --git a/lib/blockchain.js b/lib/blockchain.js index 6498491b..7cadd778 100644 --- a/lib/blockchain.js +++ b/lib/blockchain.js @@ -5,12 +5,16 @@ var $ = bitcore.util.preconditions; var _ = bitcore.deps._; function BlockChain() { - this.tip = null; + this.tip = '0000000000000000000000000000000000000000000000000000000000000000'; this.work = { '0000000000000000000000000000000000000000000000000000000000000000': 0 }; - this.height = {}; - this.hashByHeight = {}; + this.height = { + '0000000000000000000000000000000000000000000000000000000000000000': -1 + }; + this.hashByHeight = { + '-1': '0000000000000000000000000000000000000000000000000000000000000000' + }; this.next = {}; this.prev = {}; } @@ -44,7 +48,7 @@ BlockChain.prototype.proposeNewBlock = function(block) { $.checkArgument(block instanceof bitcore.Block, 'Argument is not a Block instance'); var prevHash = bitcore.util.buffer.reverse(block.header.prevHash).toString('hex'); - if (!this.work[prevHash]) { + if (_.isUndefined(this.work[prevHash])) { throw new Error('No previous data to estimate work'); } this.addData(block); @@ -118,7 +122,9 @@ BlockChain.prototype.getBlockLocator = function() { var currentHeight = this.height[this.tip]; var exponentialBackOff = 1; for (var i = 0; i < 10; i++) { - result.push(this.hashByHeight[currentHeight--]); + if (currentHeight >= 0) { + result.push(this.hashByHeight[currentHeight--]); + } } while (currentHeight > 0) { result.push(this.hashByHeight[currentHeight]); diff --git a/lib/networkmonitor.js b/lib/networkmonitor.js index fb3435c3..56acfb0a 100644 --- a/lib/networkmonitor.js +++ b/lib/networkmonitor.js @@ -65,8 +65,9 @@ NetworkMonitor.prototype.setupPeer = function(peer) { }; NetworkMonitor.prototype.requestBlocks = function(start) { - $.checkArgument(_.isString(start), 'start must be a block hash string'); - this.peer.sendMessage(messages.GetBlocks([start])); + $.checkArgument(_.isArray(start) || + _.isString(start), 'start must be a block hash string or array'); + this.peer.sendMessage(messages.GetBlocks(_.isArray(start) ? start : [start])); }; NetworkMonitor.prototype.start = function() { diff --git a/lib/node.js b/lib/node.js index 3fc762eb..be57b068 100644 --- a/lib/node.js +++ b/lib/node.js @@ -35,7 +35,29 @@ var BitcoreNode = function(bus, networkMonitor, blockService, transactionService this.transactionService = transactionService; this.blockService = blockService; - this.bus.register(bitcore.Block, this.blockService.onBlock.bind(this.blockService)); + this.blockCache = {}; + + this.bus.register(bitcore.Block, function(block) { + + var prevHash = bitcore.util.buffer.reverse(block.header.prevHash).toString('hex'); + self.blockCache[block.hash] = block; + + if (!self.blockchain.hasData(prevHash)) { + self.networkMonitor.requestBlocks(self.blockchain.getBlockLocator()); + return; + } + + var blockchainChanges = self.blockchain.proposeNewBlock(block); + Promise.each(blockchainChanges.unconfirmed, function(hash) { + return self.blockService.unconfirm(self.blockCache[hash]); + }) + .then(Promise.each(blockchainChanges.confirmed, function(hash) { + return self.blockService.confirm(self.blockCache[hash]); + })) + .then(function() { + self.networkMonitor.requestBlocks(self.blockchain.getBlockLocator()); + }); + }); this.bus.onAny(function(value) { self.emit(this.event, value); @@ -82,8 +104,19 @@ BitcoreNode.create = function(opts) { }; BitcoreNode.prototype.start = function() { - this.sync(); - this.networkMonitor.start(); + var self = this; + var genesis = bitcore.Block.fromBuffer(genesisBlocks[bitcore.Networks.defaultNetwork.name]); + this.blockService.getBlockchain().then(function(blockchain) { + if (!blockchain) { + self.blockchain = new BlockChain(); + self.blockchain.proposeNewBlock(genesis); + } + self.sync(); + self.networkMonitor.start(); + }); + this.networkMonitor.on('stop', function() { + self.blockService.saveBlockchain(self.blockchain); + }); }; BitcoreNode.prototype.stop = function() { @@ -92,24 +125,15 @@ BitcoreNode.prototype.stop = function() { BitcoreNode.prototype.sync = function() { - var genesis = bitcore.Block.fromBuffer(genesisBlocks[bitcore.Networks.defaultNetwork.name]); var self = this; this.networkMonitor.on('ready', function() { self.blockService.getBlockchain().then(function(blockchain) { - if (blockchain) { - self.blockchain = blockchain; - } else { - self.blockchain = new BlockChain(); - self.blockchain.setTip(genesis); - } - self.networkMonitor.requestBlocks(self.blockchain.tip); - }) - .catch(function(err) { - self.networkMonitor.stop(); - throw err; - }); + self.networkMonitor.requestBlocks(self.blockchain.getBlockLocator()); + }).catch(function(err) { + self.networkMonitor.stop(); + throw err; + }); }); }; - module.exports = BitcoreNode; diff --git a/lib/services/block.js b/lib/services/block.js index 3c614035..9a6a0680 100644 --- a/lib/services/block.js +++ b/lib/services/block.js @@ -226,9 +226,8 @@ BlockService.prototype.onBlock = function(block) { * @param {bitcore.Block} block * @return {Promise} a promise of the same block, for chaining */ -BlockService.prototype.save = function(block) { +BlockService.prototype.unconfirm = function(block) { // TODO: unconfirm previous tip, confirm new tip. - return this._confirmBlock(block); }; @@ -238,7 +237,7 @@ BlockService.prototype.save = function(block) { * @param {bitcore.Block} block * @return {Promise} a promise of the same block, for chaining */ -BlockService.prototype._confirmBlock = function(block) { +BlockService.prototype.confirm = function(block) { $.checkArgument(block instanceof bitcore.Block); var self = this;