diff --git a/lib/blockchain/chaindb.js b/lib/blockchain/chaindb.js index 5783792a..cd5f1615 100644 --- a/lib/blockchain/chaindb.js +++ b/lib/blockchain/chaindb.js @@ -973,14 +973,15 @@ ChainDB.prototype.getFullBlock = co(function* getFullBlock(hash) { /** * Fill a transaction with coins (only unspents). * @param {TX} tx - * @returns {Promise} - Returns {@link TX}. + * @returns {Promise} */ ChainDB.prototype.fillCoins = co(function* fillCoins(tx) { + var found = true; var i, input, prevout, coin; if (tx.isCoinbase()) - return; + return found; for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; @@ -991,27 +992,33 @@ ChainDB.prototype.fillCoins = co(function* fillCoins(tx) { coin = yield this.getCoin(prevout.hash, prevout.index); - if (!coin) + if (!coin) { + input.coin = null; + found = false; continue; + } input.coin = coin; } + + return found; }); /** * Fill a transaction with coins (all historical coins). * @param {TX} tx - * @returns {Promise} - Returns {@link TX}. + * @returns {Promise} */ ChainDB.prototype.fillHistory = co(function* fillHistory(tx) { + var found = true; var i, input, prevout, prev; if (!this.options.indexTX) - return; + return found; if (tx.isCoinbase()) - return; + return found; for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; @@ -1022,14 +1029,16 @@ ChainDB.prototype.fillHistory = co(function* fillHistory(tx) { prev = yield this.getTX(prevout.hash); - if (!prev) - continue; - - if (prevout.index >= prev.outputs.length) + if (!prev || prevout.index >= prev.outputs.length) { + input.coin = null; + found = false; continue; + } input.coin = Coin.fromTX(prev, prevout.index); } + + return found; }); /** diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index 0a29a63f..ba6c18aa 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -290,7 +290,7 @@ Mempool.prototype._reset = function reset() { * @returns {Promise} */ -Mempool.prototype.limitMempoolSize = function limitMempoolSize(entryHash) { +Mempool.prototype.limitSize = function limitSize(entryHash) { var trimmed = false; var i, hashes, hash, end, entry; @@ -717,7 +717,7 @@ Mempool.prototype._addTX = co(function* _addTX(tx) { yield this.addEntry(entry); // Trim size if we're too big. - if (this.limitMempoolSize(hash)) { + if (this.limitSize(hash)) { throw new VerifyError(tx, 'insufficientfee', 'mempool full', @@ -911,9 +911,9 @@ Mempool.prototype.verify = co(function* verify(entry) { // Script verification. try { yield this.verifyInputs(tx, flags1); - } catch (error) { + } catch (err) { if (tx.hasWitness()) - throw error; + throw err; // Try without segwit and cleanstack. result = yield this.verifyResult(tx, flags2); @@ -921,7 +921,7 @@ Mempool.prototype.verify = co(function* verify(entry) { // If it failed, the first verification // was the only result we needed. if (!result) - throw error; + throw err; // If it succeeded, segwit may be causing the // failure. Try with segwit but without cleanstack. @@ -929,11 +929,11 @@ Mempool.prototype.verify = co(function* verify(entry) { // Cleanstack was causing the failure. if (result) - throw error; + throw err; // Do not insert into reject cache. - error.malleated = true; - throw error; + err.malleated = true; + throw err; } // Paranoid checks. @@ -1313,89 +1313,6 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx, missing) { return true; }; -/** - * Spend coins for transaction. - * @param {TX} tx - * @returns {Boolean} - */ - -Mempool.prototype.injectCoins = function injectCoins(tx, view) { - var missing = []; - var i, input, prevout, coin; - - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - prevout = input.prevout; - coin = view.getCoin(prevout.hash, prevout.index); - - if (!coin) { - missing.push(prevout.hash); - continue; - } - - input.coin = coin; - } - - return missing; -}; - -/** - * Store an orphaned transaction. - * @param {TX} tx - */ - -Mempool.prototype.maybeStoreOrphan = function maybeStoreOrphan(tx) { - var missing = {}; - var i, hash, input, prev; - - if (tx.getWeight() > constants.tx.MAX_WEIGHT) { - this.logger.debug('Ignoring large orphan: %s', tx.txid()); - if (!tx.hasWitness()) - this.rejects.add(tx.hash()); - return; - } - - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - - if (input.coin) - continue; - - if (this.hasReject(input.prevout.hash)) { - this.logger.debug('Not storing orphan %s (rejected parents).', tx.txid()); - this.rejects.add(tx.hash()); - return; - } - - missing[input.prevout.hash] = true; - } - - hash = tx.hash('hex'); - missing = Object.keys(missing); - - assert(missing.length > 0); - - for (i = 0; i < missing.length; i++) { - prev = missing[i]; - - if (!this.waiting[prev]) - this.waiting[prev] = []; - - this.waiting[prev].push(hash); - } - - this.orphans[hash] = new Orphan(tx, missing.length); - this.totalOrphans++; - - this.logger.debug('Added orphan %s to mempool.', tx.txid()); - - this.emit('add orphan', tx); - - this.limitOrphans(); - - return missing; -}; - /** * Potentially resolve any transactions * that redeem the passed-in transaction. @@ -1521,165 +1438,6 @@ Mempool.prototype.isDoubleSpend = function isDoubleSpend(tx) { return false; }; -/** - * Fill a transaction with all available transaction outputs - * in the mempool. This differs from {@link Mempool#fillCoins} - * in that it will fill with all historical coins and not - * just unspent coins. - * @param {TX} tx - */ - -Mempool.prototype.fillHistory = function fillHistory(tx) { - var found = true; - var i, input, prevout, prev; - - if (tx.isCoinbase()) - return false; - - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - prevout = input.prevout; - prev = this.getTX(prevout.hash); - - if (!prev) { - input.coin = null; - found = false; - continue; - } - - if (prevout.index >= prev.outputs.length) { - input.coin = null; - found = false; - continue; - } - - input.coin = Coin.fromTX(prev, prevout.index); - } - - return found; -}; - -/** - * Fill a transaction with all available (unspent) coins - * in the mempool. - * @param {TX} tx - */ - -Mempool.prototype.fillCoins = function fillCoins(tx) { - var found = true; - var i, input, prevout, coin; - - if (tx.isCoinbase()) - return false; - - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - prevout = input.prevout; - coin = this.getCoin(prevout.hash, prevout.index); - - if (!coin) { - input.coin = null; - found = false; - continue; - } - - input.coin = coin; - } - - return found; -}; - -/** - * Fill transaction with all unspent _and spent_ - * coins. Similar to {@link Mempool#fillHistory} - * except that it will also fill with coins - * from the blockchain as well. - * @param {TX} tx - * @returns {Promise} - Returns {@link TX}. - */ - -Mempool.prototype.fillAllHistory = function fillAllHistory(tx) { - this.fillHistory(tx); - - if (tx.hasCoins()) - return Promise.resolve(tx); - - return this.chain.db.fillHistory(tx); -}; - -/** - * Fill transaction with all unspent - * coins. Similar to {@link Mempool#fillCoins} - * except that it will also fill with coins - * from the blockchain as well. - * @param {TX} tx - * @returns {Promise} - Returns {@link TX}. - */ - -Mempool.prototype.fillAllCoins = co(function* fillAllCoins(tx) { - var found = true; - var i, input, hash, index, coin; - - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - hash = input.prevout.hash; - index = input.prevout.index; - - coin = this.getCoin(hash, index); - - if (coin) { - input.coin = coin; - continue; - } - - if (this.isSpent(hash, index)) { - input.coin = null; - found = false; - continue; - } - - coin = yield this.chain.db.getCoin(hash, index); - - if (!coin) { - input.coin = null; - found = false; - continue; - } - - input.coin = coin; - } - - return found; -}); - -/** - * Get coin viewpoint. - * @param {TX} tx - * @returns {Promise} - Returns {@link CoinView}. - */ - -Mempool.prototype.getCoinViewSlow = co(function* getCoinViewSlow(tx) { - var view = yield this.chain.db.getCoinView(tx); - var entries = view.toArray(); - var i, coins, entry; - - for (i = 0; i < entries.length; i++) { - coins = entries[i]; - - if (!coins.isEmpty()) - continue; - - entry = this.getEntry(coins.hash); - - if (!entry) - continue; - - view.addTX(entry.tx, -1); - } - - return view; -}); - /** * Get coin viewpoint. * @param {TX} tx @@ -1715,6 +1473,32 @@ Mempool.prototype.getCoinView = co(function* getCoinView(tx) { return view; }); +/** + * Spend coins for transaction. + * @param {TX} tx + * @returns {Boolean} + */ + +Mempool.prototype.injectCoins = function injectCoins(tx, view) { + var missing = []; + var i, input, prevout, coin; + + for (i = 0; i < tx.inputs.length; i++) { + input = tx.inputs[i]; + prevout = input.prevout; + coin = view.getCoin(prevout.hash, prevout.index); + + if (!coin) { + missing.push(prevout.hash); + continue; + } + + input.coin = coin; + } + + return missing; +}; + /** * Get a snapshot of all transaction hashes in the mempool. Used * for generating INV packets in response to MEMPOOL packets. @@ -1960,6 +1744,106 @@ Mempool.prototype.getSize = function getSize() { return this.size; }; +/** + * Fill transaction with all unspent _and spent_ + * coins. Similar to {@link Mempool#fillHistory} + * except that it will also fill with coins + * from the blockchain as well. + * @param {TX} tx + * @returns {Promise} - Returns {@link TX}. + */ + +Mempool.prototype.fillHistory = co(function* fillHistory(tx) { + var found = true; + var i, input, prevout, prev; + + if (tx.isCoinbase()) + return found; + + for (i = 0; i < tx.inputs.length; i++) { + input = tx.inputs[i]; + prevout = input.prevout; + + if (input.coin) + continue; + + prev = this.getTX(prevout.hash); + + if (prev) { + if (prevout.index >= prev.outputs.length) { + input.coin = null; + found = false; + continue; + } + input.coin = Coin.fromTX(prev, prevout.index); + continue; + } + + prev = yield this.chain.db.getTX(prevout.hash); + + if (!prev || prevout.index >= prev.outputs.length) { + input.coin = null; + found = false; + continue; + } + + input.coin = Coin.fromTX(prev, prevout.index); + } + + return found; +}); + +/** + * Fill transaction with all unspent + * coins. Similar to {@link Mempool#fillCoins} + * except that it will also fill with coins + * from the blockchain as well. + * @param {TX} tx + * @returns {Promise} - Returns {@link TX}. + */ + +Mempool.prototype.fillCoins = co(function* fillCoins(tx) { + var found = true; + var i, input, hash, index, coin; + + if (tx.isCoinbase()) + return found; + + for (i = 0; i < tx.inputs.length; i++) { + input = tx.inputs[i]; + hash = input.prevout.hash; + index = input.prevout.index; + + if (input.coin) + continue; + + coin = this.getCoin(hash, index); + + if (coin) { + input.coin = coin; + continue; + } + + if (this.isSpent(hash, index)) { + input.coin = null; + found = false; + continue; + } + + coin = yield this.chain.db.getCoin(hash, index); + + if (!coin) { + input.coin = null; + found = false; + continue; + } + + input.coin = coin; + } + + return found; +}); + /** * Address Index */ diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index c9da091d..34a4e108 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -422,8 +422,10 @@ FullNode.prototype.getCoinsByAddress = co(function* getCoinsByAddress(addresses) coin = blockCoins[i]; spent = this.mempool.isSpent(coin.hash, coin.index); - if (!spent) - coins.push(coin); + if (spent) + continue; + + coins.push(coin); } return coins; @@ -492,7 +494,7 @@ FullNode.prototype.isSpent = function isSpent(hash, index) { */ FullNode.prototype.fillCoins = function fillCoins(tx) { - return this.mempool.fillAllCoins(tx); + return this.mempool.fillCoins(tx); }; /** @@ -503,7 +505,7 @@ FullNode.prototype.fillCoins = function fillCoins(tx) { */ FullNode.prototype.fillHistory = function fillHistory(tx) { - return this.mempool.fillAllHistory(tx); + return this.mempool.fillHistory(tx); }; /*