2023-07-18 15:05:44 +09:00
|
|
|
import { GbtGenerator, GbtResult, ThreadTransaction as RustThreadTransaction, ThreadAcceleration as RustThreadAcceleration } from '../../rust-gbt';
|
2021-03-18 23:47:40 +07:00
|
|
|
import logger from '../logger';
|
2023-06-13 17:03:36 -04:00
|
|
|
import { MempoolBlock, MempoolTransactionExtended, TransactionStripped, MempoolBlockWithTransactions, MempoolBlockDelta, Ancestor, CompactThreadTransaction, EffectiveFeeStats, PoolTag } from '../mempool.interfaces';
|
2023-05-10 12:59:05 -06:00
|
|
|
import { Common, OnlineFeeStatsCalculator } from './common';
|
2021-03-18 05:52:46 +00:00
|
|
|
import config from '../config';
|
2022-12-06 05:51:44 +09:00
|
|
|
import { Worker } from 'worker_threads';
|
2022-11-16 18:17:07 -06:00
|
|
|
import path from 'path';
|
2023-05-30 19:35:39 -04:00
|
|
|
import mempool from './mempool';
|
2020-02-16 22:15:07 +07:00
|
|
|
|
2023-06-25 23:42:33 -04:00
|
|
|
const MAX_UINT32 = Math.pow(2, 32) - 1;
|
|
|
|
|
2020-02-16 22:15:07 +07:00
|
|
|
class MempoolBlocks {
|
2020-06-08 02:08:51 +07:00
|
|
|
private mempoolBlocks: MempoolBlockWithTransactions[] = [];
|
2022-05-31 21:36:42 +00:00
|
|
|
private mempoolBlockDeltas: MempoolBlockDelta[] = [];
|
2022-12-06 05:51:44 +09:00
|
|
|
private txSelectionWorker: Worker | null = null;
|
2023-06-23 18:37:03 -04:00
|
|
|
private rustInitialized: boolean = false;
|
2023-06-24 23:05:43 -07:00
|
|
|
private rustGbtGenerator: GbtGenerator = new GbtGenerator();
|
2020-02-16 22:15:07 +07:00
|
|
|
|
2023-05-02 17:46:48 -06:00
|
|
|
private nextUid: number = 1;
|
|
|
|
private uidMap: Map<number, string> = new Map(); // map short numerical uids to full txids
|
|
|
|
|
2020-02-16 22:15:07 +07:00
|
|
|
public getMempoolBlocks(): MempoolBlock[] {
|
2020-06-08 02:08:51 +07:00
|
|
|
return this.mempoolBlocks.map((block) => {
|
|
|
|
return {
|
|
|
|
blockSize: block.blockSize,
|
|
|
|
blockVSize: block.blockVSize,
|
|
|
|
nTx: block.nTx,
|
|
|
|
totalFees: block.totalFees,
|
|
|
|
medianFee: block.medianFee,
|
|
|
|
feeRange: block.feeRange,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public getMempoolBlocksWithTransactions(): MempoolBlockWithTransactions[] {
|
2020-02-16 22:15:07 +07:00
|
|
|
return this.mempoolBlocks;
|
|
|
|
}
|
|
|
|
|
2022-05-31 21:36:42 +00:00
|
|
|
public getMempoolBlockDeltas(): MempoolBlockDelta[] {
|
2022-06-02 01:29:03 +04:00
|
|
|
return this.mempoolBlockDeltas;
|
2022-05-31 21:36:42 +00:00
|
|
|
}
|
|
|
|
|
2023-05-29 15:56:29 -04:00
|
|
|
public updateMempoolBlocks(memPool: { [txid: string]: MempoolTransactionExtended }, saveResults: boolean = false): MempoolBlockWithTransactions[] {
|
2020-02-16 22:15:07 +07:00
|
|
|
const latestMempool = memPool;
|
2023-05-29 15:56:29 -04:00
|
|
|
const memPoolArray: MempoolTransactionExtended[] = [];
|
2020-02-16 22:15:07 +07:00
|
|
|
for (const i in latestMempool) {
|
2023-06-26 11:15:52 -04:00
|
|
|
memPoolArray.push(latestMempool[i]);
|
2020-02-16 22:15:07 +07:00
|
|
|
}
|
2021-03-18 23:47:40 +07:00
|
|
|
const start = new Date().getTime();
|
|
|
|
|
|
|
|
// Clear bestDescendants & ancestors
|
|
|
|
memPoolArray.forEach((tx) => {
|
|
|
|
tx.bestDescendant = null;
|
|
|
|
tx.ancestors = [];
|
|
|
|
tx.cpfpChecked = false;
|
|
|
|
if (!tx.effectiveFeePerVsize) {
|
2023-05-29 15:56:29 -04:00
|
|
|
tx.effectiveFeePerVsize = tx.adjustedFeePerVsize;
|
2021-03-18 23:47:40 +07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// First sort
|
2023-04-21 09:20:56 +09:00
|
|
|
memPoolArray.sort((a, b) => {
|
2023-05-29 15:56:29 -04:00
|
|
|
if (a.adjustedFeePerVsize === b.adjustedFeePerVsize) {
|
2023-04-21 09:20:56 +09:00
|
|
|
// tie-break by lexicographic txid order for stability
|
|
|
|
return a.txid < b.txid ? -1 : 1;
|
|
|
|
} else {
|
2023-05-29 15:56:29 -04:00
|
|
|
return b.adjustedFeePerVsize - a.adjustedFeePerVsize;
|
2023-04-21 09:20:56 +09:00
|
|
|
}
|
|
|
|
});
|
2021-03-18 23:47:40 +07:00
|
|
|
|
|
|
|
// Loop through and traverse all ancestors and sum up all the sizes + fees
|
|
|
|
// Pass down size + fee to all unconfirmed children
|
|
|
|
let sizes = 0;
|
2023-03-12 14:51:29 +09:00
|
|
|
memPoolArray.forEach((tx) => {
|
2021-04-10 21:26:05 +04:00
|
|
|
sizes += tx.weight;
|
2021-03-18 23:47:40 +07:00
|
|
|
if (sizes > 4000000 * 8) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Common.setRelativesAndGetCpfpInfo(tx, memPool);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Final sort, by effective fee
|
2023-04-21 09:20:56 +09:00
|
|
|
memPoolArray.sort((a, b) => {
|
|
|
|
if (a.effectiveFeePerVsize === b.effectiveFeePerVsize) {
|
|
|
|
// tie-break by lexicographic txid order for stability
|
|
|
|
return a.txid < b.txid ? -1 : 1;
|
|
|
|
} else {
|
|
|
|
return b.effectiveFeePerVsize - a.effectiveFeePerVsize;
|
|
|
|
}
|
|
|
|
});
|
2021-03-18 23:47:40 +07:00
|
|
|
|
|
|
|
const end = new Date().getTime();
|
|
|
|
const time = end - start;
|
|
|
|
logger.debug('Mempool blocks calculated in ' + time / 1000 + ' seconds');
|
|
|
|
|
2023-03-12 14:51:29 +09:00
|
|
|
const blocks = this.calculateMempoolBlocks(memPoolArray);
|
2022-10-27 10:21:39 -06:00
|
|
|
|
2023-01-30 16:26:37 -06:00
|
|
|
if (saveResults) {
|
|
|
|
const deltas = this.calculateMempoolDeltas(this.mempoolBlocks, blocks);
|
|
|
|
this.mempoolBlocks = blocks;
|
|
|
|
this.mempoolBlockDeltas = deltas;
|
|
|
|
}
|
|
|
|
|
|
|
|
return blocks;
|
2020-02-16 22:15:07 +07:00
|
|
|
}
|
|
|
|
|
2023-05-29 15:56:29 -04:00
|
|
|
private calculateMempoolBlocks(transactionsSorted: MempoolTransactionExtended[]): MempoolBlockWithTransactions[] {
|
2020-06-08 02:08:51 +07:00
|
|
|
const mempoolBlocks: MempoolBlockWithTransactions[] = [];
|
2023-05-10 12:59:05 -06:00
|
|
|
let feeStatsCalculator: OnlineFeeStatsCalculator = new OnlineFeeStatsCalculator(config.MEMPOOL.BLOCK_WEIGHT_UNITS);
|
|
|
|
let onlineStats = false;
|
2023-05-08 19:03:39 -06:00
|
|
|
let blockSize = 0;
|
2021-08-14 03:24:31 +03:00
|
|
|
let blockWeight = 0;
|
2023-04-21 09:20:56 +09:00
|
|
|
let blockVsize = 0;
|
2023-05-08 19:03:39 -06:00
|
|
|
let blockFees = 0;
|
|
|
|
const sizeLimit = (config.MEMPOOL.BLOCK_WEIGHT_UNITS / 4) * 1.2;
|
|
|
|
let transactionIds: string[] = [];
|
2023-05-29 15:56:29 -04:00
|
|
|
let transactions: MempoolTransactionExtended[] = [];
|
2023-05-10 12:59:05 -06:00
|
|
|
transactionsSorted.forEach((tx, index) => {
|
2021-10-05 04:11:13 +04:00
|
|
|
if (blockWeight + tx.weight <= config.MEMPOOL.BLOCK_WEIGHT_UNITS
|
|
|
|
|| mempoolBlocks.length === config.MEMPOOL.MEMPOOL_BLOCKS_AMOUNT - 1) {
|
2023-04-21 09:20:56 +09:00
|
|
|
tx.position = {
|
|
|
|
block: mempoolBlocks.length,
|
|
|
|
vsize: blockVsize + (tx.vsize / 2),
|
|
|
|
};
|
2021-08-14 03:24:31 +03:00
|
|
|
blockWeight += tx.weight;
|
2023-04-21 09:20:56 +09:00
|
|
|
blockVsize += tx.vsize;
|
2023-05-08 19:03:39 -06:00
|
|
|
blockSize += tx.size;
|
|
|
|
blockFees += tx.fee;
|
|
|
|
if (blockVsize <= sizeLimit) {
|
|
|
|
transactions.push(tx);
|
|
|
|
}
|
|
|
|
transactionIds.push(tx.txid);
|
2023-05-10 12:59:05 -06:00
|
|
|
if (onlineStats) {
|
|
|
|
feeStatsCalculator.processNext(tx);
|
|
|
|
}
|
2020-02-16 22:15:07 +07:00
|
|
|
} else {
|
2023-05-08 19:03:39 -06:00
|
|
|
mempoolBlocks.push(this.dataToMempoolBlocks(transactionIds, transactions, blockSize, blockWeight, blockFees));
|
2023-04-21 09:20:56 +09:00
|
|
|
blockVsize = 0;
|
|
|
|
tx.position = {
|
|
|
|
block: mempoolBlocks.length,
|
|
|
|
vsize: blockVsize + (tx.vsize / 2),
|
|
|
|
};
|
2023-05-10 12:59:05 -06:00
|
|
|
|
|
|
|
if (mempoolBlocks.length === config.MEMPOOL.MEMPOOL_BLOCKS_AMOUNT - 1) {
|
|
|
|
const stackWeight = transactionsSorted.slice(index).reduce((total, tx) => total + (tx.weight || 0), 0);
|
|
|
|
if (stackWeight > config.MEMPOOL.BLOCK_WEIGHT_UNITS) {
|
|
|
|
onlineStats = true;
|
2023-06-07 13:22:27 -04:00
|
|
|
feeStatsCalculator = new OnlineFeeStatsCalculator(stackWeight, 0.5, [10, 20, 30, 40, 50, 60, 70, 80, 90]);
|
2023-05-10 12:59:05 -06:00
|
|
|
feeStatsCalculator.processNext(tx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-21 09:20:56 +09:00
|
|
|
blockVsize += tx.vsize;
|
2021-08-14 03:24:31 +03:00
|
|
|
blockWeight = tx.weight;
|
2023-05-08 19:03:39 -06:00
|
|
|
blockSize = tx.size;
|
|
|
|
blockFees = tx.fee;
|
|
|
|
transactionIds = [tx.txid];
|
2020-06-08 18:55:53 +07:00
|
|
|
transactions = [tx];
|
2020-02-16 22:15:07 +07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
if (transactions.length) {
|
2023-05-10 12:59:05 -06:00
|
|
|
const feeStats = onlineStats ? feeStatsCalculator.getRawFeeStats() : undefined;
|
|
|
|
mempoolBlocks.push(this.dataToMempoolBlocks(transactionIds, transactions, blockSize, blockWeight, blockFees, feeStats));
|
2020-02-16 22:15:07 +07:00
|
|
|
}
|
2022-10-10 22:13:04 +00:00
|
|
|
|
2022-11-16 18:17:07 -06:00
|
|
|
return mempoolBlocks;
|
|
|
|
}
|
|
|
|
|
|
|
|
private calculateMempoolDeltas(prevBlocks: MempoolBlockWithTransactions[], mempoolBlocks: MempoolBlockWithTransactions[]): MempoolBlockDelta[] {
|
|
|
|
const mempoolBlockDeltas: MempoolBlockDelta[] = [];
|
2022-05-31 21:36:42 +00:00
|
|
|
for (let i = 0; i < Math.max(mempoolBlocks.length, prevBlocks.length); i++) {
|
2022-06-02 01:29:03 +04:00
|
|
|
let added: TransactionStripped[] = [];
|
|
|
|
let removed: string[] = [];
|
2023-07-18 15:05:44 +09:00
|
|
|
const changed: { txid: string, rate: number | undefined, acc: boolean | undefined }[] = [];
|
2022-05-31 21:36:42 +00:00
|
|
|
if (mempoolBlocks[i] && !prevBlocks[i]) {
|
2022-06-02 01:29:03 +04:00
|
|
|
added = mempoolBlocks[i].transactions;
|
2022-05-31 21:36:42 +00:00
|
|
|
} else if (!mempoolBlocks[i] && prevBlocks[i]) {
|
2022-06-02 01:29:03 +04:00
|
|
|
removed = prevBlocks[i].transactions.map(tx => tx.txid);
|
2022-05-31 21:36:42 +00:00
|
|
|
} else if (mempoolBlocks[i] && prevBlocks[i]) {
|
2022-06-02 01:29:03 +04:00
|
|
|
const prevIds = {};
|
|
|
|
const newIds = {};
|
2022-05-31 21:36:42 +00:00
|
|
|
prevBlocks[i].transactions.forEach(tx => {
|
2023-04-05 08:40:43 +09:00
|
|
|
prevIds[tx.txid] = tx;
|
2022-06-02 01:29:03 +04:00
|
|
|
});
|
2022-05-31 21:36:42 +00:00
|
|
|
mempoolBlocks[i].transactions.forEach(tx => {
|
2022-06-02 01:29:03 +04:00
|
|
|
newIds[tx.txid] = true;
|
|
|
|
});
|
2022-05-31 21:36:42 +00:00
|
|
|
prevBlocks[i].transactions.forEach(tx => {
|
2022-06-02 01:29:03 +04:00
|
|
|
if (!newIds[tx.txid]) {
|
|
|
|
removed.push(tx.txid);
|
|
|
|
}
|
|
|
|
});
|
2022-05-31 21:36:42 +00:00
|
|
|
mempoolBlocks[i].transactions.forEach(tx => {
|
2022-06-02 01:29:03 +04:00
|
|
|
if (!prevIds[tx.txid]) {
|
|
|
|
added.push(tx);
|
2023-05-26 21:10:32 -04:00
|
|
|
} else if (tx.rate !== prevIds[tx.txid].rate || tx.acc !== prevIds[tx.txid].acc) {
|
|
|
|
changed.push({ txid: tx.txid, rate: tx.rate, acc: tx.acc });
|
2022-06-02 01:29:03 +04:00
|
|
|
}
|
|
|
|
});
|
2022-05-31 21:36:42 +00:00
|
|
|
}
|
|
|
|
mempoolBlockDeltas.push({
|
|
|
|
added,
|
2023-04-05 08:40:43 +09:00
|
|
|
removed,
|
|
|
|
changed,
|
2022-06-02 01:29:03 +04:00
|
|
|
});
|
2022-05-31 21:36:42 +00:00
|
|
|
}
|
2022-11-16 18:17:07 -06:00
|
|
|
return mempoolBlockDeltas;
|
2020-02-16 22:15:07 +07:00
|
|
|
}
|
|
|
|
|
2023-06-13 17:03:36 -04:00
|
|
|
public async $makeBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, saveResults: boolean = false, useAccelerations: boolean = false, accelerationPool?: number): Promise<MempoolBlockWithTransactions[]> {
|
2023-05-08 19:03:39 -06:00
|
|
|
const start = Date.now();
|
|
|
|
|
2023-05-02 17:46:48 -06:00
|
|
|
// reset mempool short ids
|
|
|
|
this.resetUids();
|
|
|
|
for (const tx of Object.values(newMempool)) {
|
2023-05-30 19:35:39 -04:00
|
|
|
this.setUid(tx, true);
|
2023-05-02 17:46:48 -06:00
|
|
|
}
|
|
|
|
|
2023-06-03 16:54:12 -04:00
|
|
|
const accelerations = useAccelerations ? mempool.getAccelerations() : {};
|
2023-05-30 19:35:39 -04:00
|
|
|
|
2022-12-06 05:51:44 +09:00
|
|
|
// prepare a stripped down version of the mempool with only the minimum necessary data
|
|
|
|
// to reduce the overhead of passing this data to the worker thread
|
2023-05-02 17:46:48 -06:00
|
|
|
const strippedMempool: Map<number, CompactThreadTransaction> = new Map();
|
2023-04-07 09:41:25 +09:00
|
|
|
Object.values(newMempool).forEach(entry => {
|
2023-06-26 11:15:52 -04:00
|
|
|
if (entry.uid !== null && entry.uid !== undefined) {
|
2023-06-23 16:42:58 -04:00
|
|
|
const stripped = {
|
2023-05-02 17:46:48 -06:00
|
|
|
uid: entry.uid,
|
2023-06-13 17:03:36 -04:00
|
|
|
fee: entry.fee + (useAccelerations && (!accelerationPool || accelerations[entry.txid]?.pools?.includes(accelerationPool)) ? (accelerations[entry.txid]?.feeDelta || 0) : 0),
|
2023-05-29 15:56:29 -04:00
|
|
|
weight: (entry.adjustedVsize * 4),
|
2023-05-29 17:21:02 -04:00
|
|
|
sigops: entry.sigops,
|
2023-05-29 15:56:29 -04:00
|
|
|
feePerVsize: entry.adjustedFeePerVsize || entry.feePerVsize,
|
|
|
|
effectiveFeePerVsize: entry.effectiveFeePerVsize || entry.adjustedFeePerVsize || entry.feePerVsize,
|
2023-06-26 11:15:52 -04:00
|
|
|
inputs: entry.vin.map(v => this.getUid(newMempool[v.txid])).filter(uid => (uid !== null && uid !== undefined)) as number[],
|
2023-06-23 16:42:58 -04:00
|
|
|
};
|
|
|
|
strippedMempool.set(entry.uid, stripped);
|
2023-05-02 17:46:48 -06:00
|
|
|
}
|
2022-12-06 05:51:44 +09:00
|
|
|
});
|
2022-10-10 22:13:04 +00:00
|
|
|
|
2022-12-07 14:51:26 -06:00
|
|
|
// (re)initialize tx selection worker thread
|
2022-12-06 05:51:44 +09:00
|
|
|
if (!this.txSelectionWorker) {
|
|
|
|
this.txSelectionWorker = new Worker(path.resolve(__dirname, './tx-selection-worker.js'));
|
2022-12-07 14:51:26 -06:00
|
|
|
// if the thread throws an unexpected error, or exits for any other reason,
|
|
|
|
// reset worker state so that it will be re-initialized on the next run
|
2022-12-06 05:51:44 +09:00
|
|
|
this.txSelectionWorker.once('error', () => {
|
|
|
|
this.txSelectionWorker = null;
|
|
|
|
});
|
|
|
|
this.txSelectionWorker.once('exit', () => {
|
|
|
|
this.txSelectionWorker = null;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// run the block construction algorithm in a separate thread, and wait for a result
|
2022-12-07 14:51:26 -06:00
|
|
|
let threadErrorListener;
|
|
|
|
try {
|
2023-05-08 19:03:39 -06:00
|
|
|
const workerResultPromise = new Promise<{ blocks: number[][], rates: Map<number, number>, clusters: Map<number, number[]> }>((resolve, reject) => {
|
2022-12-07 14:51:26 -06:00
|
|
|
threadErrorListener = reject;
|
|
|
|
this.txSelectionWorker?.once('message', (result): void => {
|
|
|
|
resolve(result);
|
|
|
|
});
|
|
|
|
this.txSelectionWorker?.once('error', reject);
|
2022-12-06 05:51:44 +09:00
|
|
|
});
|
2022-12-07 14:51:26 -06:00
|
|
|
this.txSelectionWorker.postMessage({ type: 'set', mempool: strippedMempool });
|
2023-05-08 19:03:39 -06:00
|
|
|
const { blocks, rates, clusters } = this.convertResultTxids(await workerResultPromise);
|
2022-10-27 10:21:39 -06:00
|
|
|
|
2022-12-07 14:51:26 -06:00
|
|
|
// clean up thread error listener
|
|
|
|
this.txSelectionWorker?.removeListener('error', threadErrorListener);
|
2023-01-30 16:26:37 -06:00
|
|
|
|
2023-07-18 15:05:44 +09:00
|
|
|
const processed = this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), accelerations, accelerationPool, saveResults);
|
2023-06-23 16:42:58 -04:00
|
|
|
|
2023-05-08 19:03:39 -06:00
|
|
|
logger.debug(`makeBlockTemplates completed in ${(Date.now() - start)/1000} seconds`);
|
2023-06-23 16:42:58 -04:00
|
|
|
|
2023-05-08 19:03:39 -06:00
|
|
|
return processed;
|
2022-12-07 14:51:26 -06:00
|
|
|
} catch (e) {
|
|
|
|
logger.err('makeBlockTemplates failed. ' + (e instanceof Error ? e.message : e));
|
|
|
|
}
|
2023-01-30 16:26:37 -06:00
|
|
|
return this.mempoolBlocks;
|
2022-12-06 05:51:44 +09:00
|
|
|
}
|
|
|
|
|
2023-06-03 16:54:12 -04:00
|
|
|
public async $updateBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], accelerationDelta: string[] = [], saveResults: boolean = false, useAccelerations: boolean = false): Promise<void> {
|
2022-12-06 05:51:44 +09:00
|
|
|
if (!this.txSelectionWorker) {
|
|
|
|
// need to reset the worker
|
2023-06-03 16:54:12 -04:00
|
|
|
await this.$makeBlockTemplates(newMempool, saveResults, useAccelerations);
|
2023-01-30 16:26:37 -06:00
|
|
|
return;
|
2022-12-06 05:51:44 +09:00
|
|
|
}
|
2023-05-02 17:46:48 -06:00
|
|
|
|
2023-05-08 19:03:39 -06:00
|
|
|
const start = Date.now();
|
|
|
|
|
2023-06-03 16:54:12 -04:00
|
|
|
const accelerations = useAccelerations ? mempool.getAccelerations() : {};
|
|
|
|
const addedAndChanged: MempoolTransactionExtended[] = useAccelerations ? accelerationDelta.map(txid => newMempool[txid]).filter(tx => tx != null).concat(added) : added;
|
2023-05-30 19:35:39 -04:00
|
|
|
|
|
|
|
for (const tx of addedAndChanged) {
|
2023-06-23 18:37:03 -04:00
|
|
|
this.setUid(tx, true);
|
2023-05-02 17:46:48 -06:00
|
|
|
}
|
2023-05-26 21:10:32 -04:00
|
|
|
const removedUids = removed.map(tx => this.getUid(tx)).filter(uid => uid != null) as number[];
|
2023-05-30 19:35:39 -04:00
|
|
|
|
2022-12-06 05:51:44 +09:00
|
|
|
// prepare a stripped down version of the mempool with only the minimum necessary data
|
|
|
|
// to reduce the overhead of passing this data to the worker thread
|
2023-05-30 19:35:39 -04:00
|
|
|
const addedStripped: CompactThreadTransaction[] = addedAndChanged.filter(entry => entry.uid != null).map(entry => {
|
2022-12-06 05:51:44 +09:00
|
|
|
return {
|
2023-05-02 17:46:48 -06:00
|
|
|
uid: entry.uid || 0,
|
2023-06-13 17:03:36 -04:00
|
|
|
fee: entry.fee + (useAccelerations ? (accelerations[entry.txid]?.feeDelta || 0) : 0),
|
2023-05-29 15:56:29 -04:00
|
|
|
weight: (entry.adjustedVsize * 4),
|
2023-05-29 17:21:02 -04:00
|
|
|
sigops: entry.sigops,
|
2023-05-29 15:56:29 -04:00
|
|
|
feePerVsize: entry.adjustedFeePerVsize || entry.feePerVsize,
|
|
|
|
effectiveFeePerVsize: entry.effectiveFeePerVsize || entry.adjustedFeePerVsize || entry.feePerVsize,
|
2023-06-26 11:15:52 -04:00
|
|
|
inputs: entry.vin.map(v => this.getUid(newMempool[v.txid])).filter(uid => (uid !== null && uid !== undefined)) as number[],
|
2022-12-06 05:51:44 +09:00
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
// run the block construction algorithm in a separate thread, and wait for a result
|
2022-12-07 14:51:26 -06:00
|
|
|
let threadErrorListener;
|
|
|
|
try {
|
2023-05-08 19:03:39 -06:00
|
|
|
const workerResultPromise = new Promise<{ blocks: number[][], rates: Map<number, number>, clusters: Map<number, number[]> }>((resolve, reject) => {
|
2022-12-07 14:51:26 -06:00
|
|
|
threadErrorListener = reject;
|
|
|
|
this.txSelectionWorker?.once('message', (result): void => {
|
|
|
|
resolve(result);
|
|
|
|
});
|
|
|
|
this.txSelectionWorker?.once('error', reject);
|
2022-12-06 05:51:44 +09:00
|
|
|
});
|
2023-05-02 17:46:48 -06:00
|
|
|
this.txSelectionWorker.postMessage({ type: 'update', added: addedStripped, removed: removedUids });
|
2023-05-08 19:03:39 -06:00
|
|
|
const { blocks, rates, clusters } = this.convertResultTxids(await workerResultPromise);
|
2022-12-06 05:51:44 +09:00
|
|
|
|
2023-05-02 17:46:48 -06:00
|
|
|
this.removeUids(removedUids);
|
|
|
|
|
2022-12-07 14:51:26 -06:00
|
|
|
// clean up thread error listener
|
|
|
|
this.txSelectionWorker?.removeListener('error', threadErrorListener);
|
2023-01-30 16:26:37 -06:00
|
|
|
|
2023-07-18 15:05:44 +09:00
|
|
|
this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), accelerations, null, saveResults);
|
2023-05-08 19:03:39 -06:00
|
|
|
logger.debug(`updateBlockTemplates completed in ${(Date.now() - start) / 1000} seconds`);
|
2022-12-07 14:51:26 -06:00
|
|
|
} catch (e) {
|
|
|
|
logger.err('updateBlockTemplates failed. ' + (e instanceof Error ? e.message : e));
|
|
|
|
}
|
2022-12-06 05:51:44 +09:00
|
|
|
}
|
|
|
|
|
2023-06-25 20:37:42 -04:00
|
|
|
private resetRustGbt(): void {
|
|
|
|
this.rustInitialized = false;
|
|
|
|
this.rustGbtGenerator = new GbtGenerator();
|
|
|
|
}
|
|
|
|
|
2023-07-18 16:08:25 +09:00
|
|
|
public async $rustMakeBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, saveResults: boolean = false, useAccelerations: boolean = false, accelerationPool?: number): Promise<MempoolBlockWithTransactions[]> {
|
|
|
|
console.log('$rustMakeBlockTemplates');
|
2023-06-23 16:42:58 -04:00
|
|
|
const start = Date.now();
|
|
|
|
|
|
|
|
// reset mempool short ids
|
2023-06-25 20:37:42 -04:00
|
|
|
if (saveResults) {
|
|
|
|
this.resetUids();
|
|
|
|
}
|
|
|
|
// set missing short ids
|
2023-06-23 16:42:58 -04:00
|
|
|
for (const tx of Object.values(newMempool)) {
|
2023-06-25 20:37:42 -04:00
|
|
|
this.setUid(tx, !saveResults);
|
2023-06-23 16:42:58 -04:00
|
|
|
}
|
2023-06-28 16:31:59 -04:00
|
|
|
// set short ids for transaction inputs
|
|
|
|
for (const tx of Object.values(newMempool)) {
|
|
|
|
tx.inputs = tx.vin.map(v => this.getUid(newMempool[v.txid])).filter(uid => (uid !== null && uid !== undefined)) as number[];
|
|
|
|
}
|
2023-06-23 16:42:58 -04:00
|
|
|
|
2023-07-18 15:05:44 +09:00
|
|
|
const accelerations = useAccelerations ? mempool.getAccelerations() : {};
|
|
|
|
const acceleratedList = accelerationPool ? Object.values(accelerations).filter(acc => newMempool[acc.txid] && acc.pools.includes(accelerationPool)) : Object.values(accelerations).filter(acc => newMempool[acc.txid]);
|
|
|
|
const convertedAccelerations = acceleratedList.map(acc => {
|
|
|
|
return {
|
|
|
|
uid: this.getUid(newMempool[acc.txid]),
|
|
|
|
delta: acc.feeDelta,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2023-06-23 16:42:58 -04:00
|
|
|
// run the block construction algorithm in a separate thread, and wait for a result
|
2023-06-25 20:37:42 -04:00
|
|
|
const rustGbt = saveResults ? this.rustGbtGenerator : new GbtGenerator();
|
2023-06-23 16:42:58 -04:00
|
|
|
try {
|
2023-06-26 14:22:10 -04:00
|
|
|
const { blocks, blockWeights, rates, clusters } = this.convertNapiResultTxids(
|
2023-07-18 15:05:44 +09:00
|
|
|
await rustGbt.make(Object.values(newMempool) as RustThreadTransaction[], convertedAccelerations as RustThreadAcceleration[], this.nextUid),
|
2023-06-24 11:40:25 -07:00
|
|
|
);
|
2023-06-25 20:37:42 -04:00
|
|
|
if (saveResults) {
|
|
|
|
this.rustInitialized = true;
|
|
|
|
}
|
2023-07-18 15:05:44 +09:00
|
|
|
const processed = this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, accelerations, accelerationPool, saveResults);
|
2023-06-23 16:42:58 -04:00
|
|
|
logger.debug(`RUST makeBlockTemplates completed in ${(Date.now() - start)/1000} seconds`);
|
|
|
|
return processed;
|
|
|
|
} catch (e) {
|
|
|
|
logger.err('RUST makeBlockTemplates failed. ' + (e instanceof Error ? e.message : e));
|
2023-06-25 20:37:42 -04:00
|
|
|
if (saveResults) {
|
|
|
|
this.resetRustGbt();
|
|
|
|
}
|
2023-06-23 16:42:58 -04:00
|
|
|
}
|
|
|
|
return this.mempoolBlocks;
|
|
|
|
}
|
|
|
|
|
2023-07-18 15:05:44 +09:00
|
|
|
public async $oneOffRustBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, useAccelerations: boolean, accelerationPool?: number): Promise<MempoolBlockWithTransactions[]> {
|
|
|
|
return this.$rustMakeBlockTemplates(newMempool, false, useAccelerations, accelerationPool);
|
2023-06-25 20:37:42 -04:00
|
|
|
}
|
|
|
|
|
2023-07-18 15:05:44 +09:00
|
|
|
public async $rustUpdateBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, mempoolSize: number, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], useAccelerations: boolean, accelerationPool?: number): Promise<void> {
|
2023-07-03 22:01:54 -04:00
|
|
|
// GBT optimization requires that uids never get too sparse
|
|
|
|
// as a sanity check, we should also explicitly prevent uint32 uid overflow
|
|
|
|
if (this.nextUid + added.length >= Math.min(Math.max(262144, 2 * mempoolSize), MAX_UINT32)) {
|
2023-06-25 20:38:18 -04:00
|
|
|
this.resetRustGbt();
|
|
|
|
}
|
2023-07-18 15:05:44 +09:00
|
|
|
|
2023-06-23 18:37:03 -04:00
|
|
|
if (!this.rustInitialized) {
|
|
|
|
// need to reset the worker
|
2023-07-18 15:05:44 +09:00
|
|
|
await this.$rustMakeBlockTemplates(newMempool, true, useAccelerations, accelerationPool);
|
2023-06-23 18:37:03 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const start = Date.now();
|
2023-06-28 16:31:59 -04:00
|
|
|
// set missing short ids
|
|
|
|
for (const tx of added) {
|
2023-06-23 18:37:03 -04:00
|
|
|
this.setUid(tx, true);
|
|
|
|
}
|
2023-06-28 16:31:59 -04:00
|
|
|
// set short ids for transaction inputs
|
|
|
|
for (const tx of added) {
|
|
|
|
tx.inputs = tx.vin.map(v => this.getUid(newMempool[v.txid])).filter(uid => (uid !== null && uid !== undefined)) as number[];
|
|
|
|
}
|
2023-06-26 11:15:52 -04:00
|
|
|
const removedUids = removed.map(tx => this.getUid(tx)).filter(uid => (uid !== null && uid !== undefined)) as number[];
|
2023-06-23 18:37:03 -04:00
|
|
|
|
2023-07-18 15:05:44 +09:00
|
|
|
const accelerations = useAccelerations ? mempool.getAccelerations() : {};
|
|
|
|
const acceleratedList = accelerationPool ? Object.values(accelerations).filter(acc => newMempool[acc.txid] && acc.pools.includes(accelerationPool)) : Object.values(accelerations).filter(acc => newMempool[acc.txid]);
|
|
|
|
const convertedAccelerations = acceleratedList.map(acc => {
|
|
|
|
return {
|
|
|
|
uid: this.getUid(newMempool[acc.txid]),
|
|
|
|
delta: acc.feeDelta,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2023-06-23 18:37:03 -04:00
|
|
|
// run the block construction algorithm in a separate thread, and wait for a result
|
|
|
|
try {
|
2023-06-26 14:22:10 -04:00
|
|
|
const { blocks, blockWeights, rates, clusters } = this.convertNapiResultTxids(
|
2023-06-24 23:05:43 -07:00
|
|
|
await this.rustGbtGenerator.update(
|
2023-06-28 16:31:59 -04:00
|
|
|
added as RustThreadTransaction[],
|
|
|
|
removedUids,
|
2023-07-18 15:05:44 +09:00
|
|
|
convertedAccelerations as RustThreadAcceleration[],
|
2023-07-03 22:01:54 -04:00
|
|
|
this.nextUid,
|
2023-06-24 19:28:19 -07:00
|
|
|
),
|
2023-06-24 11:40:25 -07:00
|
|
|
);
|
2023-06-28 19:51:52 -04:00
|
|
|
const resultMempoolSize = blocks.reduce((total, block) => total + block.length, 0);
|
|
|
|
if (mempoolSize !== resultMempoolSize) {
|
2023-06-25 21:23:30 -04:00
|
|
|
throw new Error('GBT returned wrong number of transactions, cache is probably out of sync');
|
|
|
|
} else {
|
2023-07-18 15:05:44 +09:00
|
|
|
this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, accelerations, accelerationPool, true);
|
2023-06-25 21:23:30 -04:00
|
|
|
}
|
2023-06-28 19:51:52 -04:00
|
|
|
this.removeUids(removedUids);
|
2023-06-23 18:37:03 -04:00
|
|
|
logger.debug(`RUST updateBlockTemplates completed in ${(Date.now() - start)/1000} seconds`);
|
|
|
|
} catch (e) {
|
|
|
|
logger.err('RUST updateBlockTemplates failed. ' + (e instanceof Error ? e.message : e));
|
2023-06-25 20:37:42 -04:00
|
|
|
this.resetRustGbt();
|
2023-06-23 18:37:03 -04:00
|
|
|
}
|
2023-06-23 16:42:58 -04:00
|
|
|
}
|
|
|
|
|
2023-07-18 15:05:44 +09:00
|
|
|
private processBlockTemplates(mempool: { [txid: string]: MempoolTransactionExtended }, blocks: string[][], blockWeights: number[] | null, rates: [string, number][], clusters: string[][], accelerations, accelerationPool, saveResults): MempoolBlockWithTransactions[] {
|
2023-06-30 14:46:44 -04:00
|
|
|
for (const [txid, rate] of rates) {
|
2023-05-08 19:03:39 -06:00
|
|
|
if (txid in mempool) {
|
2023-06-30 14:46:44 -04:00
|
|
|
mempool[txid].effectiveFeePerVsize = rate;
|
2023-06-26 14:22:10 -04:00
|
|
|
mempool[txid].cpfpChecked = false;
|
2023-05-08 19:03:39 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-28 19:51:52 -04:00
|
|
|
const lastBlockIndex = blocks.length - 1;
|
2023-05-10 12:59:05 -06:00
|
|
|
let hasBlockStack = blocks.length >= 8;
|
|
|
|
let stackWeight;
|
|
|
|
let feeStatsCalculator: OnlineFeeStatsCalculator | void;
|
|
|
|
if (hasBlockStack) {
|
2023-06-26 14:22:10 -04:00
|
|
|
if (blockWeights && blockWeights[7] !== null) {
|
|
|
|
stackWeight = blockWeights[7];
|
|
|
|
} else {
|
2023-06-28 19:51:52 -04:00
|
|
|
stackWeight = blocks[lastBlockIndex].reduce((total, tx) => total + (mempool[tx]?.weight || 0), 0);
|
2023-06-26 14:22:10 -04:00
|
|
|
}
|
2023-05-10 12:59:05 -06:00
|
|
|
hasBlockStack = stackWeight > config.MEMPOOL.BLOCK_WEIGHT_UNITS;
|
2023-06-07 13:22:27 -04:00
|
|
|
feeStatsCalculator = new OnlineFeeStatsCalculator(stackWeight, 0.5, [10, 20, 30, 40, 50, 60, 70, 80, 90]);
|
2023-05-10 12:59:05 -06:00
|
|
|
}
|
|
|
|
|
2023-06-30 14:46:44 -04:00
|
|
|
for (const cluster of clusters) {
|
2023-06-26 14:22:10 -04:00
|
|
|
for (const memberTxid of cluster) {
|
2023-06-28 19:51:52 -04:00
|
|
|
const mempoolTx = mempool[memberTxid];
|
|
|
|
if (mempoolTx) {
|
2023-06-26 14:22:10 -04:00
|
|
|
const ancestors: Ancestor[] = [];
|
|
|
|
const descendants: Ancestor[] = [];
|
|
|
|
let matched = false;
|
|
|
|
cluster.forEach(txid => {
|
|
|
|
if (txid === memberTxid) {
|
|
|
|
matched = true;
|
|
|
|
} else {
|
|
|
|
const relative = {
|
|
|
|
txid: txid,
|
|
|
|
fee: mempool[txid].fee,
|
|
|
|
weight: (mempool[txid].adjustedVsize * 4),
|
|
|
|
};
|
|
|
|
if (matched) {
|
|
|
|
descendants.push(relative);
|
2023-07-16 13:49:33 +09:00
|
|
|
mempoolTx.lastBoosted = Math.max(mempoolTx.lastBoosted || 0, mempool[txid].firstSeen || 0);
|
2023-06-26 14:22:10 -04:00
|
|
|
} else {
|
|
|
|
ancestors.push(relative);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2023-06-28 19:51:52 -04:00
|
|
|
Object.assign(mempoolTx, {ancestors, descendants, bestDescendant: null, cpfpChecked: true});
|
2023-06-26 14:22:10 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-08 19:03:39 -06:00
|
|
|
const sizeLimit = (config.MEMPOOL.BLOCK_WEIGHT_UNITS / 4) * 1.2;
|
2022-12-06 05:51:44 +09:00
|
|
|
// update this thread's mempool with the results
|
2023-06-30 14:46:44 -04:00
|
|
|
let mempoolTx: MempoolTransactionExtended;
|
|
|
|
const mempoolBlocks: MempoolBlockWithTransactions[] = blocks.map((block, blockIndex) => {
|
2023-05-08 19:03:39 -06:00
|
|
|
let totalSize = 0;
|
|
|
|
let totalVsize = 0;
|
|
|
|
let totalWeight = 0;
|
|
|
|
let totalFees = 0;
|
2023-05-29 15:56:29 -04:00
|
|
|
const transactions: MempoolTransactionExtended[] = [];
|
2023-06-26 14:22:10 -04:00
|
|
|
for (const txid of block) {
|
2023-05-08 19:03:39 -06:00
|
|
|
if (txid) {
|
|
|
|
mempoolTx = mempool[txid];
|
2023-04-21 08:40:21 +09:00
|
|
|
// save position in projected blocks
|
2023-05-08 19:03:39 -06:00
|
|
|
mempoolTx.position = {
|
2023-04-21 08:40:21 +09:00
|
|
|
block: blockIndex,
|
2023-05-08 19:03:39 -06:00
|
|
|
vsize: totalVsize + (mempoolTx.vsize / 2),
|
2023-04-21 08:40:21 +09:00
|
|
|
};
|
2023-06-26 14:22:10 -04:00
|
|
|
if (!mempoolTx.cpfpChecked) {
|
|
|
|
if (mempoolTx.ancestors?.length) {
|
|
|
|
mempoolTx.ancestors = [];
|
|
|
|
}
|
|
|
|
if (mempoolTx.descendants?.length) {
|
|
|
|
mempoolTx.descendants = [];
|
|
|
|
}
|
|
|
|
mempoolTx.bestDescendant = null;
|
|
|
|
mempoolTx.cpfpChecked = true;
|
|
|
|
}
|
2023-05-08 19:03:39 -06:00
|
|
|
|
2023-07-18 15:05:44 +09:00
|
|
|
const acceleration = accelerations[txid];
|
|
|
|
if (acceleration && (!accelerationPool || acceleration.pools.includes(accelerationPool))) {
|
|
|
|
mempoolTx.acceleration = true;
|
|
|
|
for (const ancestor of mempoolTx.ancestors || []) {
|
|
|
|
mempool[ancestor.txid].acceleration = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
delete mempoolTx.acceleration;
|
|
|
|
}
|
2023-06-03 16:54:12 -04:00
|
|
|
|
2023-05-10 12:59:05 -06:00
|
|
|
// online calculation of stack-of-blocks fee stats
|
2023-06-28 19:51:52 -04:00
|
|
|
if (hasBlockStack && blockIndex === lastBlockIndex && feeStatsCalculator) {
|
2023-05-10 12:59:05 -06:00
|
|
|
feeStatsCalculator.processNext(mempoolTx);
|
|
|
|
}
|
|
|
|
|
2023-05-08 19:03:39 -06:00
|
|
|
totalSize += mempoolTx.size;
|
|
|
|
totalVsize += mempoolTx.vsize;
|
|
|
|
totalWeight += mempoolTx.weight;
|
|
|
|
totalFees += mempoolTx.fee;
|
2023-04-21 08:40:21 +09:00
|
|
|
|
2023-05-08 19:03:39 -06:00
|
|
|
if (totalVsize <= sizeLimit) {
|
|
|
|
transactions.push(mempoolTx);
|
2022-12-06 05:51:44 +09:00
|
|
|
}
|
2023-05-08 19:03:39 -06:00
|
|
|
}
|
|
|
|
}
|
2023-06-30 14:46:44 -04:00
|
|
|
return this.dataToMempoolBlocks(
|
|
|
|
block,
|
2023-05-08 19:03:39 -06:00
|
|
|
transactions,
|
|
|
|
totalSize,
|
|
|
|
totalWeight,
|
2023-05-10 12:59:05 -06:00
|
|
|
totalFees,
|
2023-06-30 14:46:44 -04:00
|
|
|
(hasBlockStack && blockIndex === lastBlockIndex && feeStatsCalculator) ? feeStatsCalculator.getRawFeeStats() : undefined,
|
|
|
|
);
|
2023-05-10 12:59:05 -06:00
|
|
|
});
|
2022-12-06 05:51:44 +09:00
|
|
|
|
2023-01-30 16:26:37 -06:00
|
|
|
if (saveResults) {
|
|
|
|
const deltas = this.calculateMempoolDeltas(this.mempoolBlocks, mempoolBlocks);
|
|
|
|
this.mempoolBlocks = mempoolBlocks;
|
|
|
|
this.mempoolBlockDeltas = deltas;
|
|
|
|
}
|
2022-12-06 05:51:44 +09:00
|
|
|
|
2023-01-30 16:26:37 -06:00
|
|
|
return mempoolBlocks;
|
2022-10-10 22:13:04 +00:00
|
|
|
}
|
|
|
|
|
2023-05-29 15:56:29 -04:00
|
|
|
private dataToMempoolBlocks(transactionIds: string[], transactions: MempoolTransactionExtended[], totalSize: number, totalWeight: number, totalFees: number, feeStats?: EffectiveFeeStats ): MempoolBlockWithTransactions {
|
2023-05-10 12:59:05 -06:00
|
|
|
if (!feeStats) {
|
2023-07-18 16:08:25 +09:00
|
|
|
feeStats = Common.calcEffectiveFeeStatistics(transactions.filter(tx => !tx.acceleration));
|
2023-05-10 12:59:05 -06:00
|
|
|
}
|
2020-02-16 22:15:07 +07:00
|
|
|
return {
|
2022-12-06 05:51:44 +09:00
|
|
|
blockSize: totalSize,
|
2023-05-08 19:03:39 -06:00
|
|
|
blockVSize: (totalWeight / 4), // fractional vsize to avoid rounding errors
|
|
|
|
nTx: transactionIds.length,
|
|
|
|
totalFees: totalFees,
|
2023-03-12 14:51:06 +09:00
|
|
|
medianFee: feeStats.medianFee, // Common.percentile(transactions.map((tx) => tx.effectiveFeePerVsize), config.MEMPOOL.RECOMMENDED_FEE_PERCENTILE),
|
|
|
|
feeRange: feeStats.feeRange, //Common.getFeesInRange(transactions, rangeLength),
|
2023-05-08 19:03:39 -06:00
|
|
|
transactionIds: transactionIds,
|
|
|
|
transactions: transactions.map((tx) => Common.stripTransaction(tx)),
|
2020-02-16 22:15:07 +07:00
|
|
|
};
|
|
|
|
}
|
2023-05-02 17:46:48 -06:00
|
|
|
|
|
|
|
private resetUids(): void {
|
|
|
|
this.uidMap.clear();
|
|
|
|
this.nextUid = 1;
|
|
|
|
}
|
|
|
|
|
2023-06-23 18:37:03 -04:00
|
|
|
private setUid(tx: MempoolTransactionExtended, skipSet = false): number {
|
2023-06-26 11:15:52 -04:00
|
|
|
if (tx.uid === null || tx.uid === undefined || !skipSet) {
|
2023-06-23 18:37:03 -04:00
|
|
|
const uid = this.nextUid;
|
|
|
|
this.nextUid++;
|
|
|
|
this.uidMap.set(uid, tx.txid);
|
|
|
|
tx.uid = uid;
|
|
|
|
return uid;
|
|
|
|
} else {
|
|
|
|
return tx.uid;
|
|
|
|
}
|
2023-05-02 17:46:48 -06:00
|
|
|
}
|
|
|
|
|
2023-05-29 15:56:29 -04:00
|
|
|
private getUid(tx: MempoolTransactionExtended): number | void {
|
2023-06-26 11:15:52 -04:00
|
|
|
if (tx?.uid !== null && tx?.uid !== undefined && this.uidMap.has(tx.uid)) {
|
2023-05-02 17:46:48 -06:00
|
|
|
return tx.uid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private removeUids(uids: number[]): void {
|
|
|
|
for (const uid of uids) {
|
|
|
|
this.uidMap.delete(uid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-08 19:03:39 -06:00
|
|
|
private convertResultTxids({ blocks, rates, clusters }: { blocks: number[][], rates: Map<number, number>, clusters: Map<number, number[]>})
|
|
|
|
: { blocks: string[][], rates: { [root: string]: number }, clusters: { [root: string]: string[] }} {
|
|
|
|
const convertedBlocks: string[][] = blocks.map(block => block.map(uid => {
|
|
|
|
return this.uidMap.get(uid) || '';
|
|
|
|
}));
|
|
|
|
const convertedRates = {};
|
|
|
|
for (const rateUid of rates.keys()) {
|
|
|
|
const rateTxid = this.uidMap.get(rateUid);
|
|
|
|
if (rateTxid) {
|
|
|
|
convertedRates[rateTxid] = rates.get(rateUid);
|
2023-05-02 17:46:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
const convertedClusters = {};
|
|
|
|
for (const rootUid of clusters.keys()) {
|
|
|
|
const rootTxid = this.uidMap.get(rootUid);
|
|
|
|
if (rootTxid) {
|
|
|
|
const members = clusters.get(rootUid)?.map(uid => {
|
|
|
|
return this.uidMap.get(uid);
|
|
|
|
});
|
|
|
|
convertedClusters[rootTxid] = members;
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 19:03:39 -06:00
|
|
|
return { blocks: convertedBlocks, rates: convertedRates, clusters: convertedClusters } as { blocks: string[][], rates: { [root: string]: number }, clusters: { [root: string]: string[] }};
|
2023-05-02 17:46:48 -06:00
|
|
|
}
|
2023-06-23 16:42:58 -04:00
|
|
|
|
2023-06-30 14:46:44 -04:00
|
|
|
private convertNapiResultTxids({ blocks, blockWeights, rates, clusters }: GbtResult)
|
|
|
|
: { blocks: string[][], blockWeights: number[], rates: [string, number][], clusters: string[][] } {
|
2023-06-23 16:42:58 -04:00
|
|
|
const convertedBlocks: string[][] = blocks.map(block => block.map(uid => {
|
2023-06-25 21:23:30 -04:00
|
|
|
const txid = this.uidMap.get(uid);
|
|
|
|
if (txid !== undefined) {
|
|
|
|
return txid;
|
|
|
|
} else {
|
|
|
|
throw new Error('GBT returned a block containing a transaction with unknown uid');
|
|
|
|
}
|
2023-06-23 16:42:58 -04:00
|
|
|
}));
|
2023-06-30 14:46:44 -04:00
|
|
|
const convertedRates: [string, number][] = [];
|
|
|
|
for (const [rateUid, rate] of rates) {
|
|
|
|
const rateTxid = this.uidMap.get(rateUid) as string;
|
|
|
|
convertedRates.push([rateTxid, rate]);
|
2023-06-23 16:42:58 -04:00
|
|
|
}
|
2023-06-30 14:46:44 -04:00
|
|
|
const convertedClusters: string[][] = [];
|
|
|
|
for (const cluster of clusters) {
|
|
|
|
convertedClusters.push(cluster.map(uid => this.uidMap.get(uid)) as string[]);
|
2023-06-23 16:42:58 -04:00
|
|
|
}
|
2023-06-30 14:46:44 -04:00
|
|
|
return { blocks: convertedBlocks, blockWeights, rates: convertedRates, clusters: convertedClusters };
|
2023-06-23 16:42:58 -04:00
|
|
|
}
|
2020-02-16 22:15:07 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
export default new MempoolBlocks();
|