diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 78c3f1ec..f6627610 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -619,12 +619,12 @@ Chain.prototype.verifyDuplicates = co(function* verifyDuplicates(block, prev, st */ Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) { + var ret = new VerifyResult(); + var view = new CoinView(); var height = prev.height + 1; var historical = prev.isHistorical(); var sigops = 0; var jobs = []; - var ret = new VerifyResult(); - var view = new CoinView(); var i, tx, valid; if (this.options.spv) @@ -639,15 +639,13 @@ Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) { // Ensure tx is not double spending an output. if (i > 0) { - if (!(yield view.hasInputs(this.db, tx))) { + if (!(yield view.spendInputs(this.db, tx))) { assert(!historical, 'BUG: Spent inputs in historical data!'); throw new VerifyError(block, 'invalid', 'bad-txns-inputs-missingorspent', 100); } - - view.spendCoins(tx); } // Skip everything if we're diff --git a/lib/blockchain/chaindb.js b/lib/blockchain/chaindb.js index f5cafc8e..30d076fe 100644 --- a/lib/blockchain/chaindb.js +++ b/lib/blockchain/chaindb.js @@ -1708,7 +1708,7 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(block, view) { // Write undo coins (if there are any). if (!view.undo.isEmpty()) - this.put(layout.u(block.hash()), view.undo.toRaw()); + this.put(layout.u(block.hash()), view.undo.commit()); // Prune height-288 if pruning is enabled. yield this.pruneBlock(block); diff --git a/lib/blockchain/coinview.js b/lib/blockchain/coinview.js index 3fe2b0ec..766d2519 100644 --- a/lib/blockchain/coinview.js +++ b/lib/blockchain/coinview.js @@ -105,12 +105,23 @@ CoinView.prototype.has = function has(hash, index) { CoinView.prototype.spend = function spend(hash, index) { var coins = this.unspent[hash]; - var entry, undo; if (!coins) return null; - entry = coins.spend(index); + return this.spendFrom(coins, index); +}; + +/** + * Remove a coin and return it. + * @param {Coins} coins + * @param {Number} index + * @returns {Coin} + */ + +CoinView.prototype.spendFrom = function spendFrom(coins, index) { + var entry = coins.spend(index); + var undo; if (!entry) return null; @@ -191,7 +202,7 @@ CoinView.prototype.ensureInputs = co(function* ensureInputs(db, tx) { /** * Spend coins for transaction. * @param {TX} tx - * @throws on missing coin + * @returns {Boolean} */ CoinView.prototype.spendCoins = function spendCoins(tx) { @@ -200,11 +211,42 @@ CoinView.prototype.spendCoins = function spendCoins(tx) { for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; prevout = input.prevout; + input.coin = this.spend(prevout.hash, prevout.index); - assert(input.coin, 'Not all coins available.'); + + if (!input.coin) + return false; } + + return true; }; +/** + * Spend coins for transaction. + * @param {TX} tx + * @returns {Boolean} + */ + +CoinView.prototype.spendInputs = co(function* spendInputs(db, tx) { + var i, input, prevout, coins; + + for (i = 0; i < tx.inputs.length; i++) { + input = tx.inputs[i]; + prevout = input.prevout; + coins = yield this.getCoins(db, prevout.hash); + + if (!coins) + return false; + + input.coin = this.spendFrom(coins, prevout.index); + + if (!input.coin) + return false; + } + + return true; +}); + /** * Convert collection to an array. * @returns {Coins[]} diff --git a/lib/blockchain/undocoins.js b/lib/blockchain/undocoins.js index a3e6f3d2..98d607ea 100644 --- a/lib/blockchain/undocoins.js +++ b/lib/blockchain/undocoins.js @@ -99,6 +99,17 @@ UndoCoins.prototype.isEmpty = function isEmpty() { return this.items.length === 0; }; +/** + * Render the undo coins. + * @returns {Buffer} + */ + +UndoCoins.prototype.commit = function commit() { + var raw = this.toRaw(); + this.items.length = 0; + return raw; +}; + /** * Retrieve the last undo coin. * @returns {UndoCoin}