Merge pull request #4840 from mempool/mononaut/multi-tx-tracking

Add track-txs websocket subscription
This commit is contained in:
softsimon 2024-03-31 18:51:00 +09:00 committed by GitHub
commit 1630d71e7b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 135 additions and 2 deletions

View File

@ -2,7 +2,7 @@ import logger from '../logger';
import * as WebSocket from 'ws'; import * as WebSocket from 'ws';
import { import {
BlockExtended, TransactionExtended, MempoolTransactionExtended, WebsocketResponse, BlockExtended, TransactionExtended, MempoolTransactionExtended, WebsocketResponse,
OptimizedStatistic, ILoadingIndicators, GbtCandidates, OptimizedStatistic, ILoadingIndicators, GbtCandidates, TxTrackingInfo,
} from '../mempool.interfaces'; } from '../mempool.interfaces';
import blocks from './blocks'; import blocks from './blocks';
import memPool from './mempool'; import memPool from './mempool';
@ -209,6 +209,52 @@ class WebsocketHandler {
} }
} }
if (parsedMessage && parsedMessage['track-txs']) {
const txids: string[] = [];
if (Array.isArray(parsedMessage['track-txs'])) {
for (const txid of parsedMessage['track-txs']) {
if (/^[a-fA-F0-9]{64}$/.test(txid)) {
txids.push(txid);
}
}
}
const txs: { [txid: string]: TxTrackingInfo } = {};
for (const txid of txids) {
const txInfo: TxTrackingInfo = {
confirmed: true,
};
const rbfCacheTxid = rbfCache.getReplacedBy(txid);
if (rbfCacheTxid) {
txInfo.replacedBy = rbfCacheTxid;
txInfo.confirmed = false;
}
const tx = memPool.getMempool()[txid];
if (tx && tx.position) {
txInfo.position = {
...tx.position
};
if (tx.acceleration) {
txInfo.accelerated = tx.acceleration;
}
}
if (tx) {
txInfo.confirmed = false;
}
txs[txid] = txInfo;
}
if (txids.length) {
client['track-txs'] = txids;
} else {
client['track-txs'] = null;
}
if (Object.keys(txs).length) {
response['tracked-txs'] = JSON.stringify(txs);
}
}
if (parsedMessage && parsedMessage['track-address']) { if (parsedMessage && parsedMessage['track-address']) {
const validAddress = this.testAddress(parsedMessage['track-address']); const validAddress = this.testAddress(parsedMessage['track-address']);
if (validAddress) { if (validAddress) {
@ -517,6 +563,11 @@ class WebsocketHandler {
if (client['track-tx']) { if (client['track-tx']) {
trackedTxs.add(client['track-tx']); trackedTxs.add(client['track-tx']);
} }
if (client['track-txs']) {
for (const txid of client['track-txs']) {
trackedTxs.add(txid);
}
}
}); });
if (trackedTxs.size > 0) { if (trackedTxs.size > 0) {
for (const tx of newTransactions) { for (const tx of newTransactions) {
@ -713,6 +764,46 @@ class WebsocketHandler {
} }
} }
if (client['track-txs']) {
const txids = client['track-txs'];
const txs: { [txid: string]: TxTrackingInfo } = {};
for (const txid of txids) {
const txInfo: TxTrackingInfo = {};
const outspends = outspendCache[txid];
if (outspends && Object.keys(outspends).length) {
txInfo.utxoSpent = outspends;
}
const replacedBy = rbfChanges.map[txid] ? rbfCache.getReplacedBy(txid) : false;
if (replacedBy) {
txInfo.replacedBy = replacedBy;
}
const mempoolTx = newMempool[txid];
if (mempoolTx && mempoolTx.position) {
txInfo.position = {
...mempoolTx.position,
accelerated: mempoolTx.acceleration || undefined,
};
if (!mempoolTx.cpfpChecked) {
calculateCpfp(mempoolTx, newMempool);
}
if (mempoolTx.cpfpDirty) {
txInfo.cpfp = {
ancestors: mempoolTx.ancestors,
bestDescendant: mempoolTx.bestDescendant || null,
descendants: mempoolTx.descendants || null,
effectiveFeePerVsize: mempoolTx.effectiveFeePerVsize || null,
sigops: mempoolTx.sigops,
adjustedVsize: mempoolTx.adjustedVsize,
};
}
}
txs[txid] = txInfo;
}
if (Object.keys(txs).length) {
response['tracked-txs'] = JSON.stringify(txs);
}
}
if (client['track-mempool-block'] >= 0 && memPool.isInSync()) { if (client['track-mempool-block'] >= 0 && memPool.isInSync()) {
const index = client['track-mempool-block']; const index = client['track-mempool-block'];
if (mBlockDeltas[index]) { if (mBlockDeltas[index]) {
@ -931,6 +1022,28 @@ class WebsocketHandler {
} }
} }
if (client['track-txs']) {
const txs: { [txid: string]: TxTrackingInfo } = {};
for (const txid of client['track-txs']) {
if (confirmedTxids[txid]) {
txs[txid] = { confirmed: true };
} else {
const mempoolTx = _memPool[txid];
if (mempoolTx && mempoolTx.position) {
txs[txid] = {
position: {
...mempoolTx.position,
},
accelerated: mempoolTx.acceleration || undefined,
};
}
}
}
if (Object.keys(txs).length) {
response['tracked-txs'] = JSON.stringify(txs);
}
}
if (client['track-address']) { if (client['track-address']) {
const foundTransactions: TransactionExtended[] = Array.from(addressCache[client['track-address']]?.values() || []); const foundTransactions: TransactionExtended[] = Array.from(addressCache[client['track-address']]?.values() || []);
@ -1126,6 +1239,7 @@ class WebsocketHandler {
private printLogs(): void { private printLogs(): void {
if (this.wss) { if (this.wss) {
let numTxSubs = 0; let numTxSubs = 0;
let numTxsSubs = 0;
let numProjectedSubs = 0; let numProjectedSubs = 0;
let numRbfSubs = 0; let numRbfSubs = 0;
@ -1133,6 +1247,9 @@ class WebsocketHandler {
if (client['track-tx']) { if (client['track-tx']) {
numTxSubs++; numTxSubs++;
} }
if (client['track-txs']) {
numTxsSubs++;
}
if (client['track-mempool-block'] != null && client['track-mempool-block'] >= 0) { if (client['track-mempool-block'] != null && client['track-mempool-block'] >= 0) {
numProjectedSubs++; numProjectedSubs++;
} }
@ -1145,7 +1262,7 @@ class WebsocketHandler {
const diff = count - this.numClients; const diff = count - this.numClients;
this.numClients = count; this.numClients = count;
logger.debug(`${count} websocket clients | ${this.numConnected} connected | ${this.numDisconnected} disconnected | (${diff >= 0 ? '+' : ''}${diff})`); logger.debug(`${count} websocket clients | ${this.numConnected} connected | ${this.numDisconnected} disconnected | (${diff >= 0 ? '+' : ''}${diff})`);
logger.debug(`websocket subscriptions: track-tx: ${numTxSubs}, track-mempool-block: ${numProjectedSubs} track-rbf: ${numRbfSubs}`); logger.debug(`websocket subscriptions: track-tx: ${numTxSubs}, track-txs: ${numTxsSubs}, track-mempool-block: ${numProjectedSubs} track-rbf: ${numRbfSubs}`);
this.numConnected = 0; this.numConnected = 0;
this.numDisconnected = 0; this.numDisconnected = 0;
} }

View File

@ -411,6 +411,22 @@ export interface OptimizedStatistic {
vsizes: number[]; vsizes: number[];
} }
export interface TxTrackingInfo {
replacedBy?: string,
position?: { block: number, vsize: number, accelerated?: boolean },
cpfp?: {
ancestors?: Ancestor[],
bestDescendant?: Ancestor | null,
descendants?: Ancestor[] | null,
effectiveFeePerVsize?: number | null,
sigops: number,
adjustedVsize: number,
},
utxoSpent?: { [vout: number]: { vin: number, txid: string } },
accelerated?: boolean,
confirmed?: boolean
}
export interface WebsocketResponse { export interface WebsocketResponse {
action: string; action: string;
data: string[]; data: string[];