mempool: reflect spent coins in mempool coinview.
Currently coinview does not account for spent coins in the mempool, This does not create problems because we have additional checks in right places which detect double spends, but technically coinview should help you detect double spent in the mempool as well. This way it will be compatible with chain.getCoinView. getSpentView will still return all outputs that are available in the mempool. (This could also return spentView from indexers if available, this method is used by `signrawtransaction`.)
This commit is contained in:
parent
e305d900d6
commit
32cba1bf4a
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user