Add track-txs websocket subscription
This commit is contained in:
		
							parent
							
								
									8f19a376fa
								
							
						
					
					
						commit
						c5300c950b
					
				@ -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(client['track-tx']);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    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