Merge pull request #4840 from mempool/mononaut/multi-tx-tracking
Add track-txs websocket subscription
This commit is contained in:
commit
1630d71e7b
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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[];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user