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