From 379fa3fe49cc042e05d2d2e62b37b1ccae0f2de7 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 30 May 2023 19:35:39 -0400 Subject: [PATCH] Refactor acceleration tracking --- backend/src/api/mempool-blocks.ts | 38 ++++++++----- backend/src/api/mempool.ts | 71 +++++++++++++++--------- backend/src/api/services/acceleration.ts | 20 +++++++ backend/src/api/websocket-handler.ts | 6 +- 4 files changed, 91 insertions(+), 44 deletions(-) create mode 100644 backend/src/api/services/acceleration.ts diff --git a/backend/src/api/mempool-blocks.ts b/backend/src/api/mempool-blocks.ts index c1f2646ab..e3895b215 100644 --- a/backend/src/api/mempool-blocks.ts +++ b/backend/src/api/mempool-blocks.ts @@ -4,6 +4,7 @@ import { Common, OnlineFeeStatsCalculator } from './common'; import config from '../config'; import { Worker } from 'worker_threads'; import path from 'path'; +import mempool from './mempool'; class MempoolBlocks { private mempoolBlocks: MempoolBlockWithTransactions[] = []; @@ -211,9 +212,11 @@ class MempoolBlocks { // reset mempool short ids this.resetUids(); for (const tx of Object.values(newMempool)) { - this.setUid(tx); + this.setUid(tx, true); } + const accelerations = mempool.getAccelerations(); + // 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 const strippedMempool: Map = new Map(); @@ -221,7 +224,7 @@ class MempoolBlocks { if (entry.uid != null) { strippedMempool.set(entry.uid, { uid: entry.uid, - fee: entry.fee + (entry.acceleration || 0), + fee: entry.fee + (accelerations[entry.txid] || 0), weight: (entry.adjustedVsize * 4), sigops: entry.sigops, feePerVsize: entry.adjustedFeePerVsize || entry.feePerVsize, @@ -269,7 +272,7 @@ class MempoolBlocks { return this.mempoolBlocks; } - public async $updateBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], saveResults: boolean = false): Promise { + public async $updateBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], accelerationDelta: string[] = [], saveResults: boolean = false): Promise { if (!this.txSelectionWorker) { // need to reset the worker await this.$makeBlockTemplates(newMempool, saveResults); @@ -278,17 +281,20 @@ class MempoolBlocks { const start = Date.now(); - for (const tx of Object.values(added)) { + const accelerations = mempool.getAccelerations(); + const addedAndChanged: MempoolTransactionExtended[] = accelerationDelta.map(txid => newMempool[txid]).filter(tx => tx != null).concat(added); + + for (const tx of addedAndChanged) { this.setUid(tx); } - const removedUids = removed.map(tx => this.getUid(tx)).filter(uid => uid != null) as number[]; + // 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 - const addedStripped: CompactThreadTransaction[] = added.filter(entry => entry.uid != null).map(entry => { + const addedStripped: CompactThreadTransaction[] = addedAndChanged.filter(entry => entry.uid != null).map(entry => { return { uid: entry.uid || 0, - fee: entry.fee + (entry.acceleration || 0), + fee: entry.fee + (accelerations[entry.txid] || 0), weight: (entry.adjustedVsize * 4), sigops: entry.sigops, feePerVsize: entry.adjustedFeePerVsize || entry.feePerVsize, @@ -450,12 +456,18 @@ class MempoolBlocks { this.nextUid = 1; } - private setUid(tx: MempoolTransactionExtended): number { - const uid = this.nextUid; - this.nextUid++; - this.uidMap.set(uid, tx.txid); - tx.uid = uid; - return uid; + // use reset=true to overwrite existing uids held by tx objects (required after resetUids) + private setUid(tx: MempoolTransactionExtended, reset = false): number { + let uid = reset ? null : this.getUid(tx); + if (uid == null) { + uid = this.nextUid; + this.nextUid++; + this.uidMap.set(uid, tx.txid); + tx.uid = uid; + return uid; + } else { + return uid; + } } private getUid(tx: MempoolTransactionExtended): number | void { diff --git a/backend/src/api/mempool.ts b/backend/src/api/mempool.ts index f87924ba2..f9b8ed323 100644 --- a/backend/src/api/mempool.ts +++ b/backend/src/api/mempool.ts @@ -9,6 +9,7 @@ import loadingIndicators from './loading-indicators'; import bitcoinClient from './bitcoin/bitcoin-client'; import bitcoinSecondClient from './bitcoin/bitcoin-second-client'; import rbfCache from './rbf-cache'; +import accelerationApi, { Acceleration } from './services/acceleration'; class Mempool { private inSync: boolean = false; @@ -18,9 +19,9 @@ class Mempool { private mempoolInfo: IBitcoinApi.MempoolInfo = { loaded: false, size: 0, bytes: 0, usage: 0, total_fee: 0, maxmempool: 300000000, mempoolminfee: 0.00001000, minrelaytxfee: 0.00001000 }; private mempoolChangedCallback: ((newMempool: {[txId: string]: MempoolTransactionExtended; }, newTransactions: MempoolTransactionExtended[], - deletedTransactions: MempoolTransactionExtended[]) => void) | undefined; + deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]) => void) | undefined; private $asyncMempoolChangedCallback: ((newMempool: {[txId: string]: MempoolTransactionExtended; }, newTransactions: MempoolTransactionExtended[], - deletedTransactions: MempoolTransactionExtended[]) => Promise) | undefined; + deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]) => Promise) | undefined; private accelerations: { [txId: string]: number } = {}; @@ -67,12 +68,12 @@ class Mempool { } public setMempoolChangedCallback(fn: (newMempool: { [txId: string]: MempoolTransactionExtended; }, - newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[]) => void): void { + newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]) => void): void { this.mempoolChangedCallback = fn; } public setAsyncMempoolChangedCallback(fn: (newMempool: { [txId: string]: MempoolTransactionExtended; }, - newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[]) => Promise): void { + newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]) => Promise): void { this.$asyncMempoolChangedCallback = fn; } @@ -92,10 +93,10 @@ class Mempool { } } if (this.mempoolChangedCallback) { - this.mempoolChangedCallback(this.mempoolCache, [], []); + this.mempoolChangedCallback(this.mempoolCache, [], [], []); } if (this.$asyncMempoolChangedCallback) { - await this.$asyncMempoolChangedCallback(this.mempoolCache, [], []); + await this.$asyncMempoolChangedCallback(this.mempoolCache, [], [], []); } this.addToSpendMap(Object.values(this.mempoolCache)); } @@ -231,20 +232,14 @@ class Mempool { } } + const accelerationDelta = await this.$updateAccelerations(); + if (accelerationDelta.length) { + hasChange = true; + } + const newTransactionsStripped = newTransactions.map((tx) => Common.stripTransaction(tx)); this.latestTransactions = newTransactionsStripped.concat(this.latestTransactions).slice(0, 6); - const newAccelerations: { txid: string, delta: number }[] = []; - newTransactions.forEach(tx => { - if (tx.txid.startsWith('00')) { - const delta = Math.floor(Math.random() * 100000) + 100000; - newAccelerations.push({ txid: tx.txid, delta }); - tx.acceleration = delta; - } - }); - this.addAccelerations(newAccelerations); - this.removeAccelerations(deletedTransactions.map(tx => tx.txid)); - if (!this.inSync && transactions.length === Object.keys(this.mempoolCache).length) { this.inSync = true; logger.notice('The mempool is now in sync!'); @@ -254,11 +249,11 @@ class Mempool { this.mempoolCacheDelta = Math.abs(transactions.length - Object.keys(this.mempoolCache).length); if (this.mempoolChangedCallback && (hasChange || deletedTransactions.length)) { - this.mempoolChangedCallback(this.mempoolCache, newTransactions, deletedTransactions); + this.mempoolChangedCallback(this.mempoolCache, newTransactions, deletedTransactions, accelerationDelta); } if (this.$asyncMempoolChangedCallback && (hasChange || deletedTransactions.length)) { this.updateTimerProgress(timer, 'running async mempool callback'); - await this.$asyncMempoolChangedCallback(this.mempoolCache, newTransactions, deletedTransactions); + await this.$asyncMempoolChangedCallback(this.mempoolCache, newTransactions, deletedTransactions, accelerationDelta); this.updateTimerProgress(timer, 'completed async mempool callback'); } @@ -273,15 +268,37 @@ class Mempool { return this.accelerations; } - public addAccelerations(newAccelerations: { txid: string, delta: number }[]): void { - for (const acceleration of newAccelerations) { - this.accelerations[acceleration.txid] = acceleration.delta; - } - } + public async $updateAccelerations(): Promise { + try { + const newAccelerations = await accelerationApi.fetchAccelerations$(); - public removeAccelerations(txids: string[]): void { - for (const txid of txids) { - delete this.accelerations[txid]; + const changed: string[] = []; + + const newAccelerationMap: { [txid: string]: number } = {}; + for (const acceleration of newAccelerations) { + newAccelerationMap[acceleration.txid] = acceleration.feeDelta; + if (this.accelerations[acceleration.txid] == null) { + // new acceleration + changed.push(acceleration.txid); + } else if (this.accelerations[acceleration.txid] !== acceleration.feeDelta) { + // feeDelta changed + changed.push(acceleration.txid); + } + } + + for (const oldTxid of Object.keys(this.accelerations)) { + if (!newAccelerationMap[oldTxid]) { + // removed + changed.push(oldTxid); + } + } + + this.accelerations = newAccelerationMap; + + return changed; + } catch (e: any) { + logger.debug(`Failed to update accelerations: ` + (e instanceof Error ? e.message : e)); + return []; } } diff --git a/backend/src/api/services/acceleration.ts b/backend/src/api/services/acceleration.ts new file mode 100644 index 000000000..efbf32f5d --- /dev/null +++ b/backend/src/api/services/acceleration.ts @@ -0,0 +1,20 @@ +import { query } from '../../utils/axios-query'; +import config from '../../config'; + +export interface Acceleration { + txid: string, + feeDelta: number, +} + +class AccelerationApi { + public async fetchAccelerations$(): Promise { + if (config.MEMPOOL_SERVICES.ACCELERATIONS) { + const response = await query(`${config.MEMPOOL_SERVICES.API}/accelerations`); + return (response as Acceleration[]) || []; + } else { + return []; + } + } +} + +export default new AccelerationApi(); \ No newline at end of file diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index 27f141d21..fceeb902a 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -303,7 +303,7 @@ class WebsocketHandler { } async $handleMempoolChange(newMempool: { [txid: string]: MempoolTransactionExtended }, - newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[]): Promise { + newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]): Promise { if (!this.wss) { throw new Error('WebSocket.Server is not set'); } @@ -311,7 +311,7 @@ class WebsocketHandler { this.printLogs(); if (config.MEMPOOL.ADVANCED_GBT_MEMPOOL) { - await mempoolBlocks.$updateBlockTemplates(newMempool, newTransactions, deletedTransactions, true); + await mempoolBlocks.$updateBlockTemplates(newMempool, newTransactions, deletedTransactions, accelerationDelta, true); } else { mempoolBlocks.updateMempoolBlocks(newMempool, true); } @@ -625,8 +625,6 @@ class WebsocketHandler { const da = difficultyAdjustment.getDifficultyAdjustment(); const fees = feeApi.getRecommendedFee(); - memPool.removeAccelerations(txIds); - // update init data this.updateInitData();