From db321c3fa5ca257c31e83ac416e841b921eacc3d Mon Sep 17 00:00:00 2001 From: natsoni Date: Wed, 30 Oct 2024 15:40:05 +0100 Subject: [PATCH] Get tx first seen from block summary if not available in audit --- .../transaction/transaction.component.ts | 69 +++++++++++++++++-- frontend/src/app/services/state.service.ts | 8 +++ .../mempool-frontend-config.mainnet.json | 1 + 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index b8621ba0c..f19a5bcbd 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -406,6 +406,30 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { const auditAvailable = this.isAuditAvailable(height); const isCoinbase = this.tx.vin.some(v => v.is_coinbase); const fetchAudit = auditAvailable && !isCoinbase; + + const addFirstSeen = (audit: TxAuditStatus | null, hash: string, height: number, txid: string, useFullSummary: boolean) => { + if ( + this.isFirstSeenAvailable(height) + && !audit?.firstSeen // firstSeen is not already in audit + && (!audit || audit?.seen) // audit is disabled or tx is already seen (meaning 'firstSeen' is in block summary) + ) { + return useFullSummary ? + this.apiService.getStrippedBlockTransactions$(hash).pipe( + map(strippedTxs => { + return { audit, firstSeen: strippedTxs.find(tx => tx.txid === txid)?.time }; + }), + catchError(() => of({ audit })) + ) : + this.apiService.getStrippedBlockTransaction$(hash, txid).pipe( + map(strippedTx => { + return { audit, firstSeen: strippedTx?.time }; + }), + catchError(() => of({ audit })) + ); + } + return of({ audit }); + }; + if (fetchAudit) { // If block audit is already cached, use it to get transaction audit const blockAuditLoaded = this.apiService.getBlockAuditLoaded(hash); @@ -428,24 +452,31 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { accelerated: isAccelerated, firstSeen, }; + }), + switchMap(audit => addFirstSeen(audit, hash, height, txid, true)), + catchError(() => { + return of({ audit: null }); }) ) } else { return this.apiService.getBlockTxAudit$(hash, txid).pipe( retry({ count: 3, delay: 2000 }), + switchMap(audit => addFirstSeen(audit, hash, height, txid, false)), catchError(() => { - return of(null); + return of({ audit: null }); }) ) } } else { - return of(isCoinbase ? { coinbase: true } : null); + const audit = isCoinbase ? { coinbase: true } : null; + return addFirstSeen(audit, hash, height, txid, this.apiService.getBlockSummaryLoaded(hash)); } }), ).subscribe(auditStatus => { - this.auditStatus = auditStatus; - if (this.auditStatus?.firstSeen) { - this.transactionTime = this.auditStatus.firstSeen; + this.auditStatus = auditStatus?.audit; + const firstSeen = this.auditStatus?.firstSeen || auditStatus['firstSeen']; + if (firstSeen) { + this.transactionTime = firstSeen; } this.setIsAccelerated(); }); @@ -940,6 +971,34 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { return true; } + isFirstSeenAvailable(blockHeight: number): boolean { + if (this.stateService.env.BASE_MODULE !== 'mempool') { + return false; + } + switch (this.stateService.network) { + case 'testnet': + if (this.stateService.env.TESTNET_TX_FIRST_SEEN_START_HEIGHT && blockHeight >= this.stateService.env.TESTNET_TX_FIRST_SEEN_START_HEIGHT) { + return true; + } + break; + case 'testnet4': + if (this.stateService.env.TESTNET4_TX_FIRST_SEEN_START_HEIGHT && blockHeight >= this.stateService.env.TESTNET4_TX_FIRST_SEEN_START_HEIGHT) { + return true; + } + break; + case 'signet': + if (this.stateService.env.SIGNET_TX_FIRST_SEEN_START_HEIGHT && blockHeight >= this.stateService.env.SIGNET_TX_FIRST_SEEN_START_HEIGHT) { + return true; + } + break; + default: + if (this.stateService.env.MAINNET_TX_FIRST_SEEN_START_HEIGHT && blockHeight >= this.stateService.env.MAINNET_TX_FIRST_SEEN_START_HEIGHT) { + return true; + } + } + return false; + } + resetTransaction() { this.firstLoad = false; this.gotInitialPosition = false; diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index e6fed4cd2..2feb266d1 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -70,6 +70,10 @@ export interface Env { TESTNET_BLOCK_AUDIT_START_HEIGHT: number; TESTNET4_BLOCK_AUDIT_START_HEIGHT: number; SIGNET_BLOCK_AUDIT_START_HEIGHT: number; + MAINNET_TX_FIRST_SEEN_START_HEIGHT: number; + TESTNET_TX_FIRST_SEEN_START_HEIGHT: number; + TESTNET4_TX_FIRST_SEEN_START_HEIGHT: number; + SIGNET_TX_FIRST_SEEN_START_HEIGHT: number; HISTORICAL_PRICE: boolean; ACCELERATOR: boolean; ACCELERATOR_BUTTON: boolean; @@ -110,6 +114,10 @@ const defaultEnv: Env = { 'TESTNET_BLOCK_AUDIT_START_HEIGHT': 0, 'TESTNET4_BLOCK_AUDIT_START_HEIGHT': 0, 'SIGNET_BLOCK_AUDIT_START_HEIGHT': 0, + 'MAINNET_TX_FIRST_SEEN_START_HEIGHT': 0, + 'TESTNET_TX_FIRST_SEEN_START_HEIGHT': 0, + 'TESTNET4_TX_FIRST_SEEN_START_HEIGHT': 0, + 'SIGNET_TX_FIRST_SEEN_START_HEIGHT': 0, 'HISTORICAL_PRICE': true, 'ACCELERATOR': false, 'ACCELERATOR_BUTTON': true, diff --git a/production/mempool-frontend-config.mainnet.json b/production/mempool-frontend-config.mainnet.json index 61a8c2c2a..79acaecc5 100644 --- a/production/mempool-frontend-config.mainnet.json +++ b/production/mempool-frontend-config.mainnet.json @@ -13,6 +13,7 @@ "MAINNET_BLOCK_AUDIT_START_HEIGHT": 773911, "TESTNET_BLOCK_AUDIT_START_HEIGHT": 2417829, "SIGNET_BLOCK_AUDIT_START_HEIGHT": 127609, + "MAINNET_TX_FIRST_SEEN_START_HEIGHT": 838316, "ITEMS_PER_PAGE": 25, "LIGHTNING": true, "ACCELERATOR": true,