diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 5189339e..b99cb8d9 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -826,7 +826,7 @@ Chain.prototype.reorganizeSPV = co(function* reorganizeSPV(competitor, block) { var fork = yield this.findFork(tip, competitor); var disconnect = []; var entry = tip; - var i; + var i, block, view; assert(fork, 'No free space or data corruption.'); @@ -847,7 +847,9 @@ Chain.prototype.reorganizeSPV = co(function* reorganizeSPV(competitor, block) { // the chain has successfully reset. for (i = 0; i < disconnect.length; i++) { entry = disconnect[i]; - this.emit('disconnect', entry, entry.toHeaders()); + block = entry.toHeaders(); + view = new CoinView(); + this.emit('disconnect', entry, block, view); } this.emit('reorganize', block, tip.height, tip.hash); @@ -860,8 +862,17 @@ Chain.prototype.reorganizeSPV = co(function* reorganizeSPV(competitor, block) { */ Chain.prototype.disconnect = co(function* disconnect(entry) { - var block = yield this.db.disconnect(entry); - var prev = yield entry.getPrevious(); + var block = yield this.db.getBlock(entry.hash); + var prev, view; + + if (!block) { + if (!this.options.spv) + throw new Error('Block not found.'); + block = entry.toHeader(); + } + + prev = yield entry.getPrevious(); + view = yield this.db.disconnect(entry, block); assert(prev); @@ -870,7 +881,7 @@ Chain.prototype.disconnect = co(function* disconnect(entry) { this.bestHeight = prev.height; this.emit('tip', prev); - this.emit('disconnect', entry, block); + this.emit('disconnect', entry, block, view); }); /** @@ -886,11 +897,10 @@ Chain.prototype.reconnect = co(function* reconnect(entry) { var block = yield this.db.getBlock(entry.hash); var prev, result; - if (this.options.spv) { - assert(!block); - block = entry.toHeaders(); - } else { - assert(block); + if (!block) { + if (!this.options.spv) + throw new Error('Block not found.'); + block = entry.toHeader(); } prev = yield entry.getPrevious(); @@ -983,8 +993,8 @@ Chain.prototype.setBestChain = co(function* setBestChain(entry, block, prev) { this.setDeploymentState(result.state); this.emit('tip', entry); - - return result.view; + this.emit('block', block, entry); + this.emit('connect', entry, block, result.view); }); /** @@ -1023,6 +1033,9 @@ Chain.prototype.saveAlternate = co(function* saveAlternate(entry, block, prev) { } yield this.db.save(entry, block); + + // Emit as a "competitor" block. + this.emit('competitor', block, entry); }); /** @@ -1212,7 +1225,7 @@ Chain.prototype.add = co(function* add(block) { Chain.prototype._add = co(function* add(block) { var ret = new VerifyResult(); var initial = true; - var hash, height, entry, prev, view; + var hash, height, entry, prev; while (block) { hash = block.hash('hex'); @@ -1319,19 +1332,11 @@ Chain.prototype._add = co(function* add(block) { // Save block to an alternate chain. yield this.saveAlternate(entry, block, prev); - // Emit as a "competitor" block. - this.emit('competitor', block, entry); - if (!initial) this.emit('competitor resolved', block, entry); } else { // Attempt to add block to the chain index. - view = yield this.setBestChain(entry, block, prev); - - // Emit our block (and potentially resolved - // orphan) only if it is on the main chain. - this.emit('block', block, entry); - this.emit('connect', entry, block, view); + yield this.setBestChain(entry, block, prev); if (!initial) this.emit('resolved', block, entry); diff --git a/lib/blockchain/chaindb.js b/lib/blockchain/chaindb.js index d2d7470d..ad2cdc32 100644 --- a/lib/blockchain/chaindb.js +++ b/lib/blockchain/chaindb.js @@ -1347,16 +1347,17 @@ ChainDB.prototype._reconnect = co(function* reconnect(entry, block, view) { /** * Disconnect block from the chain. * @param {ChainEntry} entry + * @param {Block} block * @returns {Promise} */ -ChainDB.prototype.disconnect = co(function* disconnect(entry) { +ChainDB.prototype.disconnect = co(function* disconnect(entry, block) { var block; this.start(); try { - block = yield this._disconnect(entry); + block = yield this._disconnect(entry, block); } catch (e) { this.drop(); throw e; @@ -1371,11 +1372,12 @@ ChainDB.prototype.disconnect = co(function* disconnect(entry) { * Disconnect block without a batch. * @private * @param {ChainEntry} entry - * @returns {Promise} + * @param {Block} block + * @returns {Promise} - Returns {@link CoinView}. */ -ChainDB.prototype._disconnect = co(function* disconnect(entry) { - var block; +ChainDB.prototype._disconnect = co(function* disconnect(entry, block) { + var view; // Remove hash->next-block index. this.del(layout.n(entry.prevBlock)); @@ -1387,18 +1389,13 @@ ChainDB.prototype._disconnect = co(function* disconnect(entry) { // Update state caches. this.saveUpdates(); - block = yield this.getBlock(entry.hash); - - if (!block) - throw new Error('Block not found.'); - // Disconnect inputs. - yield this.disconnectBlock(entry, block); + view = yield this.disconnectBlock(entry, block); // Revert chain state to previous tip. this.put(layout.R, this.pending.commit(entry.prevBlock)); - return block; + return view; }); /** @@ -1562,18 +1559,21 @@ ChainDB.prototype._removeChain = co(function* removeChain(hash) { /** * Save a block (not an entry) to the * database and potentially connect the inputs. + * @param {ChainEntry} entry * @param {Block} block - * @param {Boolean} connect - Whether to connect the inputs. + * @param {CoinView?} view * @returns {Promise} - Returns {@link Block}. */ ChainDB.prototype.saveBlock = co(function* saveBlock(entry, block, view) { + var hash = block.hash(); + if (this.options.spv) return; // Write actual block data (this may be // better suited to flat files in the future). - this.put(layout.b(block.hash()), block.toRaw()); + this.put(layout.b(hash), block.toRaw()); if (!view) return; @@ -1630,11 +1630,14 @@ ChainDB.prototype.saveView = function saveView(view) { /** * Connect block inputs. + * @param {ChainEntry} entry * @param {Block} block + * @param {CoinView} view * @returns {Promise} - Returns {@link Block}. */ ChainDB.prototype.connectBlock = co(function* connectBlock(entry, block, view) { + var hash = block.hash(); var i, j, tx, input, output; if (this.options.spv) @@ -1675,7 +1678,7 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(entry, block, view) { // Write undo coins (if there are any). if (!view.undo.isEmpty()) - this.put(layout.u(block.hash()), view.undo.commit()); + this.put(layout.u(hash), view.undo.commit()); // Prune height-288 if pruning is enabled. yield this.pruneBlock(entry); @@ -1683,18 +1686,20 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(entry, block, view) { /** * Disconnect block inputs. - * @param {Block|Hash} block - {@link Block} or hash. - * @returns {Promise} - Returns {@link Block}. + * @param {ChainEntry} entry + * @param {Block} block + * @returns {Promise} - Returns {@link CoinView}. */ ChainDB.prototype.disconnectBlock = co(function* disconnectBlock(entry, block) { - var i, j, view, undo, tx, input, output; + var view = new CoinView(); + var hash = block.hash(); + var i, j, undo, tx, input, output; if (this.options.spv) - return; + return view; - view = new CoinView(); - undo = yield this.getUndoCoins(block.hash()); + undo = yield this.getUndoCoins(hash); this.pending.disconnect(block); @@ -1735,14 +1740,16 @@ ChainDB.prototype.disconnectBlock = co(function* disconnectBlock(entry, block) { this.saveView(view); // Remove undo coins. - this.del(layout.u(block.hash())); + this.del(layout.u(hash)); + + return view; }); /** * Prune a block from the chain and * add current block to the prune queue. * @private - * @param {Block} + * @param {ChainEntry} entry * @returns {Promise} */