diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index 3b33c1ead..d2d298e09 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -42,6 +42,7 @@ class BitcoinRoutes { .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', this.getBlocks.bind(this)) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', this.getBlock) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/summary', this.getStrippedBlockTransactions) + .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/tx/:txid/summary', this.getStrippedBlockTransaction) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/audit-summary', this.getBlockAuditSummary) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/tx/:txid/audit', this.$getBlockTxAuditSummary) .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/tip/height', this.getBlockTipHeight) @@ -321,6 +322,20 @@ class BitcoinRoutes { } } + private async getStrippedBlockTransaction(req: Request, res: Response) { + try { + const transaction = await blocks.$getSingleTxFromSummary(req.params.hash, req.params.txid); + if (!transaction) { + handleError(req, res, 404, `transaction not found in summary`); + return; + } + res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString()); + res.json(transaction); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + private async getBlock(req: Request, res: Response) { try { const block = await blocks.$getBlock(req.params.hash); diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 3420d99c8..e621056ab 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -1224,6 +1224,11 @@ class Blocks { return summary.transactions; } + public async $getSingleTxFromSummary(hash: string, txid: string): Promise { + const txs = await this.$getStrippedBlockTransactions(hash); + return txs.find(tx => tx.txid === txid) || null; + } + /** * Get 15 blocks * diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index 08251ddae..3c8cf8807 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -18,6 +18,7 @@ export class ApiService { private apiBasePath: string; // network path is /testnet, etc. or '' for mainnet private requestCache = new Map, expiry: number }>; + public blockSummaryLoaded: { [hash: string]: boolean } = {}; public blockAuditLoaded: { [hash: string]: boolean } = {}; constructor( @@ -318,9 +319,14 @@ export class ApiService { } getStrippedBlockTransactions$(hash: string): Observable { + this.setBlockSummaryLoaded(hash); return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/block/' + hash + '/summary'); } + getStrippedBlockTransaction$(hash: string, txid: string): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/block/' + hash + '/tx/' + txid + '/summary'); + } + getDifficultyAdjustments$(interval: string | undefined): Observable { return this.httpClient.get( this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/difficulty-adjustments` + @@ -567,4 +573,12 @@ export class ApiService { getBlockAuditLoaded(hash) { return this.blockAuditLoaded[hash]; } + + async setBlockSummaryLoaded(hash: string) { + this.blockSummaryLoaded[hash] = true; + } + + getBlockSummaryLoaded(hash) { + return this.blockSummaryLoaded[hash]; + } }