diff --git a/backend/src/api/bitcoin/electrs-api.ts b/backend/src/api/bitcoin/electrs-api.ts index 565cf319e..87ae5cc7c 100644 --- a/backend/src/api/bitcoin/electrs-api.ts +++ b/backend/src/api/bitcoin/electrs-api.ts @@ -15,7 +15,7 @@ class ElectrsApi { } else if (res.statusCode !== 200) { reject(response); } else { - if (!response.count) { + if (typeof response.count !== 'number') { reject('Empty data'); return; } diff --git a/backend/src/api/mempool-blocks.ts b/backend/src/api/mempool-blocks.ts index ad32bfc57..3381bf237 100644 --- a/backend/src/api/mempool-blocks.ts +++ b/backend/src/api/mempool-blocks.ts @@ -1,13 +1,26 @@ const config = require('../../mempool-config.json'); -import { MempoolBlock, TransactionExtended } from '../interfaces'; +import { MempoolBlock, TransactionExtended, MempoolBlockWithTransactions } from '../interfaces'; import { Common } from './common'; class MempoolBlocks { - private mempoolBlocks: MempoolBlock[] = []; + private mempoolBlocks: MempoolBlockWithTransactions[] = []; constructor() {} public getMempoolBlocks(): MempoolBlock[] { + 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[] { return this.mempoolBlocks; } @@ -24,8 +37,8 @@ class MempoolBlocks { this.mempoolBlocks = this.calculateMempoolBlocks(transactionsSorted); } - private calculateMempoolBlocks(transactionsSorted: TransactionExtended[]): MempoolBlock[] { - const mempoolBlocks: MempoolBlock[] = []; + private calculateMempoolBlocks(transactionsSorted: TransactionExtended[]): MempoolBlockWithTransactions[] { + const mempoolBlocks: MempoolBlockWithTransactions[] = []; let blockVSize = 0; let blockSize = 0; let transactions: TransactionExtended[] = []; @@ -48,7 +61,7 @@ class MempoolBlocks { } private dataToMempoolBlocks(transactions: TransactionExtended[], - blockSize: number, blockVSize: number, blocksIndex: number): MempoolBlock { + blockSize: number, blockVSize: number, blocksIndex: number): MempoolBlockWithTransactions { let rangeLength = 4; if (blocksIndex === 0) { rangeLength = 8; @@ -65,6 +78,7 @@ class MempoolBlocks { totalFees: transactions.reduce((acc, cur) => acc + cur.fee, 0), medianFee: Common.median(transactions.map((tx) => tx.feePerVsize)), feeRange: Common.getFeesInRange(transactions, rangeLength), + transactionIds: transactions.map((tx) => tx.txid), }; } } diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index 1f58b7d20..f29d7a8bb 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -1,7 +1,7 @@ const config = require('../../mempool-config.json'); import * as WebSocket from 'ws'; -import { Block, TransactionExtended, Statistic, WebsocketResponse } from '../interfaces'; +import { Block, TransactionExtended, Statistic, WebsocketResponse, MempoolBlock } from '../interfaces'; import blocks from './blocks'; import memPool from './mempool'; import backendInfo from './backend-info'; @@ -215,6 +215,32 @@ class WebsocketHandler { throw new Error('WebSocket.Server is not set'); } + // Check how many transactions in the new block matches the latest projected mempool block + // If it's more than 0, recalculate the mempool blocks and send to client in the same update + let mBlocks: undefined | MempoolBlock[]; + let matchRate = 0; + const _mempoolBlocks = mempoolBlocks.getMempoolBlocksWithTransactions(); + if (_mempoolBlocks[0]) { + const matches: string[] = []; + for (const txId of txIds) { + if (_mempoolBlocks[0].transactionIds.indexOf(txId) > -1) { + matches.push(txId); + } + } + + matchRate = Math.ceil((matches.length / txIds.length) * 100); + if (matchRate > 0) { + const currentMemPool = memPool.getMempool(); + for (const txId of matches) { + delete currentMemPool[txId]; + } + mempoolBlocks.updateMempoolBlocks(currentMemPool); + mBlocks = mempoolBlocks.getMempoolBlocks(); + } + } + + block.matchRate = matchRate; + this.wss.clients.forEach((client) => { if (client.readyState !== WebSocket.OPEN) { return; @@ -228,6 +254,10 @@ class WebsocketHandler { 'block': block }; + if (mBlocks && client['want-mempool-blocks']) { + response['mempool-blocks'] = mBlocks; + } + if (client['track-tx'] && txIds.indexOf(client['track-tx']) > -1) { client['track-tx'] = null; response['txConfirmed'] = true; diff --git a/backend/src/interfaces.ts b/backend/src/interfaces.ts index 62de6eac8..341cee0de 100644 --- a/backend/src/interfaces.ts +++ b/backend/src/interfaces.ts @@ -16,6 +16,12 @@ export interface MempoolBlock { feeRange: number[]; } + +export interface MempoolBlockWithTransactions extends MempoolBlock { + transactionIds: string[]; +} + + export interface Transaction { txid: string; version: number; @@ -107,6 +113,7 @@ export interface Block { feeRange?: number[]; reward?: number; coinbaseTx?: Transaction; + matchRate?: number; } export interface Address {