From 8ee0cf86040a169bacb5397e2d5e77275ad631c2 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 17 Aug 2016 19:58:25 -0700 Subject: [PATCH] chaindb: chain state buffer hash. --- lib/bcoin/chaindb.js | 78 +++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/lib/bcoin/chaindb.js b/lib/bcoin/chaindb.js index 17c189fc..41835c13 100644 --- a/lib/bcoin/chaindb.js +++ b/lib/bcoin/chaindb.js @@ -290,7 +290,7 @@ ChainDB.prototype._open = function open(callback) { self.logger.info('Chain successfully loaded.'); self.logger.info('Chain State: hash=%s tx=%d coin=%d value=%s.', - utils.revHex(self.state.hash), + self.state.rhash, self.state.tx, self.state.coin, utils.btc(self.state.value) @@ -303,7 +303,7 @@ ChainDB.prototype._open = function open(callback) { if (err) return done(err); - self.db.has(layout.h(self.network.genesis.hash), function(err, exists) { + self.db.has(layout.e(self.network.genesis.hash), function(err, exists) { if (err) return done(err); @@ -394,17 +394,29 @@ ChainDB.prototype.drop = function drop() { ChainDB.prototype.commit = function commit(callback) { var self = this; + assert(this.current); assert(this.pending); + this.current.write(function(err) { if (err) { self.current = null; self.pending = null; return callback(err); } + self.current = null; - self.state = self.pending; + + // Overwrite the entire state + // with our new best state + // only if it is committed. + // Note that alternate chain + // tips do not commit anything. + if (self.pending.committed) + self.state = self.pending; + self.pending = null; + callback(); }); }; @@ -689,7 +701,7 @@ ChainDB.prototype.save = function save(entry, block, view, connect, callback) { self.drop(); return callback(err); } - self.put(layout.R, self.pending.mark(hash)); + self.put(layout.R, self.pending.commit(hash)); self.commit(callback); }); }; @@ -742,22 +754,13 @@ ChainDB.prototype.reconnect = function reconnect(entry, block, view, callback) { this.cacheHash.set(entry.hash, entry); this.cacheHeight.set(entry.height, entry); - if (this.options.spv) { - this.put(layout.R, this.pending.mark(hash)); - return this.commit(function(err) { - if (err) - return callback(err); - return callback(null, entry, block); - }); - } - this.connectBlock(block, view, function(err) { if (err) { self.drop(); return callback(err); } - self.put(layout.R, self.pending.mark(hash)); + self.put(layout.R, self.pending.commit(hash)); self.commit(function(err) { if (err) return callback(err); @@ -783,7 +786,7 @@ ChainDB.prototype.disconnect = function disconnect(entry, callback) { this.cacheHeight.remove(entry.height); if (this.options.spv) { - this.put(layout.R, this.pending.mark(entry.prevBlock)); + this.put(layout.R, this.pending.commit(entry.prevBlock)); return this.commit(function(err) { if (err) return callback(err); @@ -808,7 +811,7 @@ ChainDB.prototype.disconnect = function disconnect(entry, callback) { return callback(err); } - self.put(layout.R, self.pending.mark(entry.prevBlock)); + self.put(layout.R, self.pending.commit(entry.prevBlock)); self.commit(function(err) { if (err) return callback(err); @@ -848,8 +851,10 @@ ChainDB.prototype.isMainChain = function isMainChain(hash, callback) { query = hash; } - if (hash === this.chain.tip.hash || hash === this.network.genesis.hash) + if (hash === this.chain.tip.hash + || hash === this.network.genesis.hash) { return utils.asyncify(callback)(null, true); + } this.getHeight(query, function(err, height) { if (err) @@ -901,7 +906,7 @@ ChainDB.prototype.reset = function reset(block, callback) { self.start(); if (tip.hash === entry.hash) { - self.put(layout.R, self.pending.mark(tip.hash)); + self.put(layout.R, self.pending.commit(tip.hash)); return self.commit(callback); } @@ -985,9 +990,6 @@ ChainDB.prototype.removeBlock = function removeBlock(hash, callback) { self.del(layout.b(block.hash())); - if (self.options.spv) - return callback(null, block); - self.disconnectBlock(block, callback); }); }; @@ -1082,7 +1084,7 @@ ChainDB.prototype.connectBlock = function connectBlock(block, view, callback) { if (undo.written > 0) this.put(layout.u(block.hash()), undo.render()); - this._pruneBlock(block, function(err) { + this.pruneBlock(block, function(err) { if (err) return callback(err); callback(null, block); @@ -1727,7 +1729,7 @@ ChainDB.prototype.hasCoins = function hasCoins(hash, callback) { * @param {Function} callback */ -ChainDB.prototype._pruneBlock = function _pruneBlock(block, callback) { +ChainDB.prototype.pruneBlock = function pruneBlock(block, callback) { var futureHeight, key; if (this.options.spv) @@ -1764,15 +1766,24 @@ ChainDB.prototype._pruneBlock = function _pruneBlock(block, callback) { }; function ChainState() { - this.hash = null; + this.tip = constants.ZERO_HASH; this.tx = 0; this.coin = 0; this.value = 0; + this.committed = false; } +ChainState.prototype.__defineGetter__('hash', function() { + return this.tip.toString('hex'); +}); + +ChainState.prototype.__defineGetter__('rhash', function() { + return utils.revHex(this.hash); +}); + ChainState.prototype.clone = function clone() { var state = new ChainState(); - state.hash = this.hash; + state.tip = this.tip; state.tx = this.tx; state.coin = this.coin; state.value = this.value; @@ -1797,16 +1808,17 @@ ChainState.prototype.spend = function spend(coin) { this.value -= coin.value; }; -ChainState.prototype.mark = function mark(hash) { - this.hash = hash; - if (typeof this.hash !== 'string') - this.hash = this.hash.toString('hex'); - return this.toRaw(hash); +ChainState.prototype.commit = function commit(hash) { + if (typeof hash === 'string') + hash = new Buffer(hash, 'hex'); + this.tip = hash; + this.committed = true; + return this.toRaw(); }; -ChainState.prototype.toRaw = function toRaw(hash) { +ChainState.prototype.toRaw = function toRaw() { var p = new BufferWriter(); - p.writeHash(hash || this.hash); + p.writeHash(this.tip); p.writeU64(this.tx); p.writeU64(this.coin); p.writeU64(this.value); @@ -1816,7 +1828,7 @@ ChainState.prototype.toRaw = function toRaw(hash) { ChainState.fromRaw = function fromRaw(data) { var state = new ChainState(); var p = new BufferReader(data); - state.hash = p.readHash('hex'); + state.tip = p.readHash(); // XXX Allow just a hash until I write a migration. if (p.left() > 0) { state.tx = p.readU53();