Avoid fetching full audit on tx page

This commit is contained in:
natsoni
2024-07-10 13:13:53 +09:00
parent 2d03ab6346
commit 99ea1ad0a0
9 changed files with 128 additions and 30 deletions

View File

@@ -295,6 +295,7 @@ export class BlockComponent implements OnInit, OnDestroy {
),
!this.isAuditAvailableFromBlockHeight(block.height) ? of(null) : this.apiService.getBlockAudit$(block.id)
.pipe(
tap(() => this.cacheService.setBlockAuditLoaded(block.id)),
catchError((err) => {
this.overviewError = err;
return of(null);

View File

@@ -322,6 +322,7 @@ export class TrackerComponent implements OnInit, OnDestroy {
})
),
fetchAudit ? this.apiService.getBlockAudit$(hash).pipe(
tap((blockAudit) => this.cacheService.setBlockAuditLoaded(hash)),
map(audit => {
const isAdded = audit.addedTxs.includes(txid);
const isPrioritized = audit.prioritizedTxs.includes(txid);

View File

@@ -42,7 +42,7 @@ interface Pool {
slug: string;
}
interface AuditStatus {
export interface TxAuditStatus {
seen?: boolean;
expected?: boolean;
added?: boolean;
@@ -100,7 +100,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
sigops: number | null;
adjustedVsize: number | null;
pool: Pool | null;
auditStatus: AuditStatus | null;
auditStatus: TxAuditStatus | null;
isAcceleration: boolean = false;
filters: Filter[] = [];
showCpfpDetails = false;
@@ -364,33 +364,41 @@ 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;
return fetchAudit ? this.apiService.getBlockAudit$(hash).pipe(
map(audit => {
const isAdded = audit.addedTxs.includes(txid);
const isPrioritized = audit.prioritizedTxs.includes(txid);
const isAccelerated = audit.acceleratedTxs.includes(txid);
const isConflict = audit.fullrbfTxs.includes(txid);
const isExpected = audit.template.some(tx => tx.txid === txid);
const firstSeen = audit.template.find(tx => tx.txid === txid)?.time;
return {
seen: isExpected || isPrioritized || isAccelerated,
expected: isExpected,
added: isAdded,
prioritized: isPrioritized,
conflict: isConflict,
accelerated: isAccelerated,
firstSeen,
};
}),
retry({ count: 3, delay: 2000 }),
catchError(() => {
return of(null);
})
) : of(isCoinbase ? { coinbase: true } : null);
if (fetchAudit) {
// If block audit is already cached, use it to get transaction audit
const blockAuditLoaded = this.cacheService.getBlockAuditLoaded(hash);
if (blockAuditLoaded) {
return this.apiService.getBlockAudit$(hash).pipe(
map(audit => {
const isAdded = audit.addedTxs.includes(txid);
const isPrioritized = audit.prioritizedTxs.includes(txid);
const isAccelerated = audit.acceleratedTxs.includes(txid);
const isConflict = audit.fullrbfTxs.includes(txid);
const isExpected = audit.template.some(tx => tx.txid === txid);
const firstSeen = audit.template.find(tx => tx.txid === txid)?.time;
return {
seen: isExpected || isPrioritized || isAccelerated,
expected: isExpected,
added: isAdded,
prioritized: isPrioritized,
conflict: isConflict,
accelerated: isAccelerated,
firstSeen,
};
})
)
} else {
return this.apiService.getBlockTxAudit$(hash, txid).pipe(
retry({ count: 3, delay: 2000 }),
catchError(() => {
return of(null);
})
)
}
} else {
return of(isCoinbase ? { coinbase: true } : null);
}
}),
catchError((e) => {
return of(null);
})
).subscribe(auditStatus => {
this.auditStatus = auditStatus;
if (this.auditStatus?.firstSeen) {

View File

@@ -8,6 +8,7 @@ import { Transaction } from '../interfaces/electrs.interface';
import { Conversion } from './price.service';
import { StorageService } from './storage.service';
import { WebsocketResponse } from '../interfaces/websocket.interface';
import { TxAuditStatus } from '../components/transaction/transaction.component';
@Injectable({
providedIn: 'root'
@@ -374,6 +375,12 @@ export class ApiService {
);
}
getBlockTxAudit$(hash: string, txid: string) : Observable<TxAuditStatus> {
return this.httpClient.get<TxAuditStatus>(
this.apiBaseUrl + this.apiBasePath + `/api/v1/block/${hash}/tx/${txid}/audit`
);
}
getBlockAuditScores$(from: number): Observable<AuditScore[]> {
return this.httpClient.get<AuditScore[]>(
this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/blocks/audit/scores` +

View File

@@ -20,6 +20,7 @@ export class CacheService {
network: string;
blockHashCache: { [hash: string]: BlockExtended } = {};
blockCache: { [height: number]: BlockExtended } = {};
blockAuditLoaded: { [hash: string]: boolean } = {};
blockLoading: { [height: number]: boolean } = {};
copiesInBlockQueue: { [height: number]: number } = {};
blockPriorities: number[] = [];
@@ -97,6 +98,10 @@ export class CacheService {
}
}
async setBlockAuditLoaded(hash: string) {
this.blockAuditLoaded[hash] = true;
}
// increase the priority of a block, to delay removal
bumpBlockPriority(height) {
this.blockPriorities.push(height);
@@ -124,6 +129,7 @@ export class CacheService {
resetBlockCache() {
this.blockHashCache = {};
this.blockCache = {};
this.blockAuditLoaded = {};
this.blockLoading = {};
this.copiesInBlockQueue = {};
this.blockPriorities = [];
@@ -132,4 +138,8 @@ export class CacheService {
getCachedBlock(height) {
return this.blockCache[height];
}
getBlockAuditLoaded(hash) {
return this.blockAuditLoaded[hash];
}
}