From 71557ba95c71b4a162e8cd1a08ff40fc003e9c15 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Fri, 19 Feb 2016 02:14:10 -0800 Subject: [PATCH] more chain improvements. --- lib/bcoin/chain.js | 203 +++++++++++++++++++++++++++++++++++++++------ lib/bcoin/pool.js | 2 +- 2 files changed, 177 insertions(+), 28 deletions(-) diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index e3fdc7db..b150018d 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -870,6 +870,19 @@ Chain.prototype.resetTime = function resetTime(ts) { return this.resetHeight(entry.height); }; +Chain.prototype.resetTimeAsync = function resetTimeAsync(ts, callback) { + var self = this; + this.byTimeAsync(ts, function(err, entry) { + if (err) + return callback(err); + + if (!entry) + return callback(); + + self.resetHeightAsync(entry.height, callback); + }); +}; + Chain.prototype.add = function add(initial, peer, callback) { var self = this; var host = peer ? peer.host : 'unknown'; @@ -1232,21 +1245,6 @@ Chain.prototype.has = function has(hash) { return false; }; -Chain.prototype.byHeight = function byHeight(height) { - if (height < 0 || height == null) - return; - return this.db.get(height); -}; - -Chain.prototype.byHash = function byHash(hash) { - if (utils.isBuffer(hash)) - hash = utils.toHex(hash); - else if (hash.hash) - hash = hash.hash('hex'); - - return this.byHeight(this.db.getHeight(hash)); -}; - Chain.prototype.byTime = function byTime(ts) { var start = 0; var end = this.height + 1; @@ -1276,7 +1274,65 @@ Chain.prototype.byTime = function byTime(ts) { return this.db.get(start); }; +Chain.prototype.byTimeAsync = function byTimeAsync(ts, callback) { + var self = this; + var start = 0; + var end = this.height + 1; + var pos, delta; + + callback = utils.asyncify(callback); + + function done(err, result) { + if (err) + return callback(err); + + if (result) + return callback(null, result); + + self.db.getAsync(start, callback); + } + + if (ts >= this.tip.ts) + return done(null, this.tip); + + // Do a binary search for a block + // mined within an hour of the + // timestamp. + (function next() { + if (start >= end) + return done(); + + pos = (start + end) >> 1; + + self.db.getAsync(pos, function(err, entry) { + if (err) + return done(err); + + delta = Math.abs(ts - entry.ts); + + if (delta <= 60 * 60) + return done(null, entry); + + if (ts < entry.ts) { + end = pos; + } else { + start = pos + 1; + } + + next(); + }); + })(); +}; + Chain.prototype.hasBlock = function hasBlock(hash) { + if (typeof hash === 'number') + return this.db.has(hash); + + if (utils.isBuffer(hash)) + hash = utils.toHex(hash); + else if (hash.hash) + hash = hash.hash('hex'); + return this.db.has(hash); }; @@ -1295,8 +1351,26 @@ Chain.prototype.hasPending = function hasPending(hash) { Chain.prototype.getBlock = function getBlock(hash) { if (typeof hash === 'number') - return this.byHeight(hash); - return this.byHash(hash); + return this.db.get(hash); + + if (utils.isBuffer(hash)) + hash = utils.toHex(hash); + else if (hash.hash) + hash = hash.hash('hex'); + + return this.db.get(hash); +}; + +Chain.prototype.getBlockAsync = function getBlockAsync(hash, callback) { + if (typeof hash === 'number') + return this.db.getAsync(hash, callback); + + if (utils.isBuffer(hash)) + hash = utils.toHex(hash); + else if (hash.hash) + hash = hash.hash('hex'); + + return this.db.getAsync(hash, callback); }; Chain.prototype.getOrphan = function getOrphan(hash) { @@ -1319,7 +1393,7 @@ Chain.prototype.isFull = function isFull() { return delta < 40 * 60; }; -Chain.prototype.fillPercent = function fillPercent() { +Chain.prototype.getProgress = function getProgress() { if (!this.tip) return 0; return Math.min(1, this.tip.ts / (utils.now() - 40 * 60)); @@ -1332,6 +1406,9 @@ Chain.prototype.hashRange = function hashRange(start, end) { start = this.byTime(start); end = this.byTime(end); + if (!end) + end = this.tip; + if (!start || !end) return hashes; @@ -1341,6 +1418,59 @@ Chain.prototype.hashRange = function hashRange(start, end) { return hashes; }; +Chain.prototype.hashRangeAsync = function hashRangeAsync(start, end, callback) { + var self = this; + var called; + + function done(err, result) { + if (called) + return; + called = true; + callback(err, result); + } + + this.byTimeAsync(start, function(err, start) { + if (err) + return done(err); + + self.byTimeAsync(end, function(err, end) { + var hashes, i, pending; + + if (err) + return done(err); + + hashes = []; + + if (!end) + end = self.tip; + + if (!start || !end) + return done(null, hashes); + + pending = (end.height + 1) - start.height; + + for (i = start.height; i < end.height + 1; i++) + getHash(i); + + function getHash(i) { + self.db.getAsync(i, function(err, entry) { + if (err) + return done(err); + + if (!entry) + return done(new Error('No entry for hash range.')); + + hashes.push(entry.hash); + + if (!--pending) + return done(null, hashes); + }); + } + }); + }); +}; + + Chain.prototype.getLocator = function getLocator(start) { var hashes = []; var top = this.height; @@ -1495,20 +1625,38 @@ Chain.prototype.getHeight = function getHeight(hash) { }; Chain.prototype.getNextBlock = function getNextBlock(hash) { - var entry = this.byHash(hash); - var next; + var height = this.db.getHeight(hash); + var entry; - if (!entry) - return null; - - next = entry.next; - - if (!next) + if (height === -1) return; - return next.hash; + entry = this.db.get(height + 1); + + if (!entry) + return; + + return entry.hash; }; +Chain.prototype.getNextBlockAsync = function getNextBlockAsync(hash, callback) { + var height = this.db.getHeight(hash); + + if (height === -1) + return callback(); + + return this.db.getAsync(height + 1, function(err, entry) { + if (err) + return callback(err); + + if (!entry) + return callback(); + + return callback(null, entry.hash); + }); +}; + + Chain.prototype.getSize = function getSize() { return this.db.count(); }; @@ -1553,6 +1701,7 @@ Chain.prototype.getTarget = function getTarget(last, block) { } // Back 2 weeks + // NOTE: This is cached. first = this.db.get(last.height - (network.powDiffInterval - 1)); assert(first); diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index 8c25c850..6220e4c7 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -674,7 +674,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) { if (added === 0) return callback(null, false); - self.emit('chain-progress', self.chain.fillPercent(), peer); + self.emit('chain-progress', self.chain.getProgress(), peer); if (self.chain.height % 20 === 0) { utils.debug(