diff --git a/node/mempool.js b/node/mempool.js index 84122b45..b5c34df9 100644 --- a/node/mempool.js +++ b/node/mempool.js @@ -2,19 +2,22 @@ const typeforce = require('typeforce'); const debugLib = require('debug'); -const { sleep } = require('../utils'); +const {sleep} = require('../utils'); const types = require('../types'); const Tick = require('tick-tock'); +const {Dag} = require('dagjs'); const debug = debugLib('mempool:'); // TODO: add tx expiration (14 days?) -module.exports = ({ Constants, Transaction }) => +module.exports = ({Constants, Transaction}) => class Mempool { constructor(options) { this._mapTxns = new Map(); this._tock = new Tick(this); + this._dag = new Dag(); + this._dag.testForCyclic = false; this._tock.setInterval('outdatedTimer', this.purgeOutdated.bind(this), Constants.MEMPOOL_OUTDATED_INTERVAL); } @@ -35,6 +38,7 @@ module.exports = ({ Constants, Transaction }) => // TODO: think about: is it problem that TX isn't present in mempool, but present in block if (this._mapTxns.has(txHash)) { this._mapTxns.delete(txHash); + this._dag.removeVertex(txHash); } else { debug(`removeTxns: no TX ${txHash} in mempool`); } @@ -45,6 +49,7 @@ module.exports = ({ Constants, Transaction }) => this._mapTxns.forEach((tx, hash) => { if (tx.arrived < Date.now() - Constants.MEMPOOL_TX_LIFETIME) { this._mapTxns.delete(hash); + this._dag.removeVertex(hash); } }) } @@ -54,6 +59,7 @@ module.exports = ({ Constants, Transaction }) => let i = Math.floor(this._mapTxns.size / 3); for (let [hash, tx] of this._mapTxns) { this._mapTxns.delete(hash); + this._dag.removeVertex(hash); if (--i == 0) break; } } @@ -77,7 +83,10 @@ module.exports = ({ Constants, Transaction }) => const strHash = tx.hash(); if (this._mapTxns.has(strHash)) throw new Error(`tx ${strHash} already in mempool`); - this._mapTxns.set(strHash, { tx, arrived: Date.now() }); + this._mapTxns.set(strHash, {tx, arrived: Date.now()}); + for (const input of tx.inputs) { + this._dag.add(input.txHash.toString('hex'), strHash); + } debug(`TX ${strHash} added`); } @@ -107,9 +116,40 @@ module.exports = ({ Constants, Transaction }) => // TODO: implement lock_time // TODO: implement cache if mempool becomes huge const arrResult = []; - for (let r of this._mapTxns.values()) { - if (r.tx.witnessGroupId === groupId) arrResult.push(r.tx); + for (let hash of this._sortTxns()){ + const r = this._mapTxns.get(hash); + if (r.tx.witnessGroupId == groupId) arrResult.push(r.tx); } return arrResult; } + + /** + * + * @returns sorted tx hashes array + */ + _sortTxns() { + let level = []; + let used = [...this._dag.tips]; + let notused = [...this._mapTxns.keys()]; + while (notused.length > 0) { + level[level.length] = new Array(); + for (let i = 0; i < notused.length; i++) { + const v = notused[i] + let k = 0; + const edgesTo = this._dag._edges.get(v); + if (edgesTo) { + k = edgesTo.length; + const fromPrevLevel = edgesTo.filter(e => ~used.indexOf(e.from)); + if(fromPrevLevel) k -= fromPrevLevel.length + } + if (k == 0) { + level[level.length - 1].push(v) + notused.splice(i, 1); + i--; + } + } + used.push(...level[level.length - 1]) + } + return [].concat(...level); + } }; diff --git a/tests/mempool.spec.js b/tests/mempool.spec.js index ef5226ac..ebf223cd 100644 --- a/tests/mempool.spec.js +++ b/tests/mempool.spec.js @@ -31,6 +31,7 @@ describe('Mempool tests', () => { mempool.addTx(tx); assert.isOk(mempool.hasTx(tx.hash())); + assert.isOk(mempool._dag.hasVertex(tx.hash())) }); it('should FAIL add tx to mempool (already exists)', async () => { @@ -82,6 +83,9 @@ describe('Mempool tests', () => { assert.isNotOk(mempool.hasTx(tx1.hash())); assert.isNotOk(mempool.hasTx(tx2.hash())); + assert.isNotOk(mempool._dag.hasVertex(tx1.hash())) + assert.isNotOk(mempool._dag.hasVertex(tx2.hash())) + }); it('should get tx by hash', async () => { @@ -168,4 +172,19 @@ describe('Mempool tests', () => { }); + it('should sort txns', () => { + const mempool = new factory.Mempool(); + const tx1 = new factory.Transaction(createDummyTx()); + const tx2 = new factory.Transaction(createDummyTx()); + const tx3 = new factory.Transaction(); + tx3.addInput(tx1.hash(), 1); + tx3.addInput(tx2.hash(), 2); + mempool.addTx(tx2); + mempool.addTx(tx3); + mempool.addTx(tx1); + + const arrTxns = mempool.getFinalTxns(0); + assert.deepEqual(arrTxns,[tx2, tx1, tx3]); + }) + });