diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index 5d0a89787..55500d0c9 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -201,6 +201,8 @@ class BitcoinRoutes { res.json({ ancestors: tx.ancestors, bestDescendant: tx.bestDescendant || null, + descendants: tx.descendants || null, + effectiveFeePerVsize: tx.effectiveFeePerVsize || null, }); return; } diff --git a/backend/src/api/mempool-blocks.ts b/backend/src/api/mempool-blocks.ts index 334362458..e8ab48230 100644 --- a/backend/src/api/mempool-blocks.ts +++ b/backend/src/api/mempool-blocks.ts @@ -155,6 +155,7 @@ class MempoolBlocks { if (newMempool[txid] && mempool[txid]) { newMempool[txid].effectiveFeePerVsize = mempool[txid].effectiveFeePerVsize; newMempool[txid].ancestors = mempool[txid].ancestors; + newMempool[txid].descendants = mempool[txid].descendants; newMempool[txid].bestDescendant = mempool[txid].bestDescendant; newMempool[txid].cpfpChecked = mempool[txid].cpfpChecked; } diff --git a/backend/src/api/tx-selection-worker.ts b/backend/src/api/tx-selection-worker.ts index 10f65000b..206d26fe3 100644 --- a/backend/src/api/tx-selection-worker.ts +++ b/backend/src/api/tx-selection-worker.ts @@ -108,36 +108,38 @@ function makeBlockTemplates({ mempool, blockLimit, weightLimit, condenseRest }: if (blockWeight + nextTx.ancestorWeight < config.MEMPOOL.BLOCK_WEIGHT_UNITS) { blockWeight += nextTx.ancestorWeight; const ancestors: AuditTransaction[] = Array.from(nextTx.ancestorMap.values()); + const descendants: AuditTransaction[] = []; // sort ancestors by dependency graph (equivalent to sorting by ascending ancestor count) const sortedTxSet = [...ancestors.sort((a, b) => { return (a.ancestorMap.size || 0) - (b.ancestorMap.size || 0); }), nextTx]; const effectiveFeeRate = nextTx.ancestorFee / (nextTx.ancestorWeight / 4); - sortedTxSet.forEach((ancestor, i, arr) => { + + while (sortedTxSet.length) { + const ancestor = sortedTxSet.pop(); const mempoolTx = mempool[ancestor.txid]; if (ancestor && !ancestor?.used) { ancestor.used = true; // update original copy of this tx with effective fee rate & relatives data mempoolTx.effectiveFeePerVsize = effectiveFeeRate; - mempoolTx.ancestors = (Array.from(ancestor.ancestorMap?.values()) as AuditTransaction[]).map((a) => { + mempoolTx.ancestors = sortedTxSet.map((a) => { + return { + txid: a.txid, + fee: a.fee, + weight: a.weight, + }; + }).reverse(); + mempoolTx.descendants = descendants.map((a) => { return { txid: a.txid, fee: a.fee, weight: a.weight, }; }); + descendants.push(ancestor); mempoolTx.cpfpChecked = true; - if (i < arr.length - 1) { - mempoolTx.bestDescendant = { - txid: arr[arr.length - 1].txid, - fee: arr[arr.length - 1].fee, - weight: arr[arr.length - 1].weight, - }; - } else { - mempoolTx.bestDescendant = null; - } transactions.push(ancestor); blockSize += ancestor.size; } - }); + } // remove these as valid package ancestors for any descendants remaining in the mempool if (sortedTxSet.length) { diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index 01bc45742..11de304b8 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -72,6 +72,7 @@ export interface TransactionExtended extends IEsploraApi.Transaction { firstSeen?: number; effectiveFeePerVsize: number; ancestors?: Ancestor[]; + descendants?: Ancestor[]; bestDescendant?: BestDescendant | null; cpfpChecked?: boolean; deleteAfter?: number;