diff --git a/lib/services/db.js b/lib/services/db.js index 9a0e117a..b3dc86c6 100644 --- a/lib/services/db.js +++ b/lib/services/db.js @@ -48,10 +48,6 @@ function DB(options) { this._setDataPath(); - this.cache = { - hashes: {}, // dictionary of hash -> prevHash - chainHashes: {} - }; this.lastSavedMetadata = null; this.lastSavedMetadataThreshold = 0; // Set this during syncing for faster performance @@ -133,7 +129,6 @@ DB.prototype.start = function(callback) { self.tip = tip; self.tip.__height = metadata.tipHeight; - self.cache = metadata.cache; self.sync(); self.emit('ready'); setImmediate(callback); @@ -311,7 +306,6 @@ DB.prototype.saveMetadata = function(callback) { var metadata = { tip: self.tip ? self.tip.hash : null, tipHeight: self.tip && self.tip.__height ? self.tip.__height : 0, - cache: self.cache }; self.lastSavedMetadata = new Date(); @@ -406,76 +400,6 @@ DB.prototype.runAllBlockHandlers = function(block, add, callback) { ); }; -/** - * Will get an array of hashes all the way to the genesis block for - * the chain based on "block hash" as the tip. - * - * @param {String} block hash - a block hash - * @param {Function} callback - A function that accepts: Error and Array of hashes - */ -DB.prototype.getHashes = function getHashes(tipHash, callback) { - var self = this; - - $.checkArgument(utils.isHash(tipHash)); - - var hashes = []; - var depth = 0; - - function getHashAndContinue(err, hash) { - /* jshint maxstatements: 20 */ - - if (err) { - return callback(err); - } - - depth++; - - hashes.unshift(hash); - - if (hash === self.genesis.hash) { - // Stop at the genesis block - self.cache.chainHashes[tipHash] = hashes; - - callback(null, hashes); - } else if(self.cache.chainHashes[hash]) { - hashes.shift(); - hashes = self.cache.chainHashes[hash].concat(hashes); - self.cache.chainHashes[tipHash] = hashes; - if(hash !== tipHash) { - delete self.cache.chainHashes[hash]; - } - callback(null, hashes); - } else { - // Continue with the previous hash - // check cache first - var prevHash = self.cache.hashes[hash]; - if(prevHash) { - // Don't let the stack get too deep. Otherwise we will crash. - if(depth >= MAX_STACK_DEPTH) { - depth = 0; - return setImmediate(function() { - getHashAndContinue(null, prevHash); - }); - } else { - return getHashAndContinue(null, prevHash); - } - } else { - // do a db call if we don't have it - self.getPrevHash(hash, function(err, prevHash) { - if(err) { - return callback(err); - } - - return getHashAndContinue(null, prevHash); - }); - } - } - } - - getHashAndContinue(null, tipHash); - -}; - /** * This function will find the common ancestor between the current chain and a forked block, * by moving backwards from the forked block until it meets the current chain. @@ -486,41 +410,60 @@ DB.prototype.findCommonAncestor = function(block, done) { var self = this; - // The current chain of hashes will likely already be available in a cache. - self.getHashes(self.tip.hash, function(err, currentHashes) { - if (err) { - done(err); + var mainPosition = self.tip.hash; + var forkPosition = block.hash; + + var mainHashesMap = {}; + var forkHashesMap = {}; + + mainHahesMap[mainPosition] = true; + forkHashesMap[forkPosition] = true; + + var commonAncestor = null; + + async.whilst( + function() { + return !commonAncestor; + }, + function(next) { + if(mainPosition) { + var mainBlockIndex = self.node.services.bitcoind.getBlockIndex(mainTip); + if(mainBlockIndex && mainBlockIndex.prevHash) { + mainHashesMap[mainBlockIndex.prevHash] = true; + mainPosition = mainBlockIndex.prevHash; + } else { + mainPosition = null; + } + } + + if(forkPosition) { + var forkBlockIndex = self.node.services.bitcoind.getBlockIndex(forkTip); + if(forkBlockIndex && forkBlockIndex.prevHash) { + forkHashesMap[forkBlockIndex.prevHash] = true; + forkPosition = forkBlockIndex.prevHash; + } else { + forkPosition = null; + } + } + + if(forkPosition && mainHashesMap[forkPosition]) { + commonAncestor = forkPosition; + } + + if(mainPosition && forkHashesMap[mainPosition]) { + commonAncestor = mainPosition; + } + + if(!mainPosition && !forkPosition) { + return next(new Error('Unknown common ancestor')); + } + + setImmediate(next); + }, + function(err) { + done(err, commonAncestor); } - - // Create a hash map for faster lookups - var currentHashesMap = {}; - var length = currentHashes.length; - for (var i = 0; i < length; i++) { - currentHashesMap[currentHashes[i]] = true; - } - - // TODO: expose prevHash as a string from bitcore - var ancestorHash = BufferUtil.reverse(block.header.prevHash).toString('hex'); - - // We only need to go back until we meet the main chain for the forked block - // and thus don't need to find the entire chain of hashes. - - while(ancestorHash && !currentHashesMap[ancestorHash]) { - var blockIndex = self.node.services.bitcoind.getBlockIndex(ancestorHash); - ancestorHash = blockIndex ? blockIndex.prevHash : null; - } - - // Hash map is no-longer needed, quickly let - // scavenging garbage collection know to cleanup - currentHashesMap = null; - - if (!ancestorHash) { - return done(new Error('Unknown common ancestor.')); - } - - done(null, ancestorHash); - - }); + ); }; /** @@ -620,28 +563,18 @@ DB.prototype.sync = function() { // Populate height block.__height = self.tip.__height + 1; - // Update cache.hashes - self.cache.hashes[block.hash] = prevHash; - - // Update cache.chainHashes - self.getHashes(block.hash, function(err, hashes) { + // Create indexes + self.connectBlock(block, function(err) { if (err) { return done(err); } - // Create indexes - self.connectBlock(block, function(err) { - if (err) { - return done(err); - } - self.tip = block; - log.debug('Saving metadata'); - self.saveMetadata(); - log.debug('Chain added block to main chain'); - self.emit('addblock', block); - setImmediate(done); - }); + self.tip = block; + log.debug('Saving metadata'); + self.saveMetadata(); + log.debug('Chain added block to main chain'); + self.emit('addblock', block); + setImmediate(done); }); - } else { // This block doesn't progress the current tip, so we'll attempt // to rewind the chain to the common ancestor of the block and