diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index 17c4b4af..b365c23c 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -502,6 +502,28 @@ class Mempool extends EventEmitter { return Coin.fromTX(entry.tx, index, -1); } + /** + * Check whether coin is still unspent. + * @param {Hash} hash + * @param {Number} index + * @returns {boolean} + */ + + hasCoin(hash, index) { + const entry = this.map.get(hash); + + if (!entry) + return false; + + if (this.isSpent(hash, index)) + return false; + + if (index >= entry.tx.outputs.length) + return false; + + return true; + } + /** * Check to see if a coin has been spent. This differs from * {@link ChainDB#isSpent} in that it actually maintains a @@ -1689,6 +1711,8 @@ class Mempool extends EventEmitter { /** * Get coin viewpoint (lock). + * Note: this does not return + * historical view of coins from the indexers. * @method * @param {TX} tx * @returns {Promise} - Returns {@link CoinView}. @@ -1697,12 +1721,40 @@ class Mempool extends EventEmitter { async getSpentView(tx) { const unlock = await this.locker.lock(); try { - return await this.getCoinView(tx); + return await this._getSpentView(tx); } finally { unlock(); } } + /** + * Get coin viewpoint + * @param {TX} tx + * @returns {Promise} - Returns {@link CoinView} + */ + + async _getSpentView(tx) { + const view = new CoinView(); + + for (const {prevout} of tx.inputs) { + const {hash, index} = prevout; + const tx = this.getTX(hash); + + if (tx) { + if (index < tx.outputs.length) + view.addIndex(tx, index, -1); + continue; + } + + const coin = await this.chain.readCoin(prevout); + + if (coin) + view.addEntry(prevout, coin); + } + + return view; + } + /** * Get coin viewpoint (no lock). * @method @@ -1718,7 +1770,7 @@ class Mempool extends EventEmitter { const tx = this.getTX(hash); if (tx) { - if (index < tx.outputs.length) + if (this.hasCoin(hash, index)) view.addIndex(tx, index, -1); continue; } diff --git a/test/mempool-test.js b/test/mempool-test.js index 992e82e4..1828c215 100644 --- a/test/mempool-test.js +++ b/test/mempool-test.js @@ -177,6 +177,56 @@ describe('Mempool', function() { })); }); + it('should get spend coins and reflect in coinview', async () => { + const wallet = new MemWallet(); + const script = Script.fromAddress(wallet.getAddress()); + const dummyCoin = dummyInput(script, random.randomBytes(32)); + + // spend first output + const mtx1 = new MTX(); + mtx1.addOutput(wallet.getAddress(), 50000); + mtx1.addCoin(dummyCoin); + wallet.sign(mtx1); + + // spend second tx + const tx1 = mtx1.toTX(); + const coin1 = Coin.fromTX(tx1, 0, -1); + const mtx2 = new MTX(); + + mtx2.addOutput(wallet.getAddress(), 10000); + mtx2.addOutput(wallet.getAddress(), 30000); // 10k fee.. + mtx2.addCoin(coin1); + + wallet.sign(mtx2); + + const tx2 = mtx2.toTX(); + + await mempool.addTX(tx1); + + { + const view = await mempool.getCoinView(tx2); + assert(view.hasEntry(coin1)); + } + + await mempool.addTX(tx2); + + // we should not have coins available in the mempool for these txs. + { + const view = await mempool.getCoinView(tx1); + const sview = await mempool.getSpentView(tx1); + + assert(!view.hasEntry(dummyCoin)); + assert(sview.hasEntry(dummyCoin)); + } + + { + const view = await mempool.getCoinView(tx2); + const sview = await mempool.getSpentView(tx2); + assert(!view.hasEntry(coin1)); + assert(sview.hasEntry(coin1)); + } + }); + it('should handle locktime', async () => { const key = KeyRing.generate();