From de6dbbf946026a1fd7b63d261d1994f5ad857c57 Mon Sep 17 00:00:00 2001 From: Yuexun Date: Mon, 5 Aug 2024 11:15:21 +1000 Subject: [PATCH] Fix bitcoin/ckb api service cacheable shouldCache and add more cacheable decorators (#63) --- backend/src/constants.ts | 2 ++ .../core/bitcoin-api/bitcoin-api.service.ts | 2 +- .../core/ckb-explorer/ckb-explorer.service.ts | 23 +++++++++++++---- .../core/ckb-rpc/ckb-rpc-websocket.service.ts | 25 ++++++++++++++++--- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/backend/src/constants.ts b/backend/src/constants.ts index ac9a3b92..e56563d5 100644 --- a/backend/src/constants.ts +++ b/backend/src/constants.ts @@ -17,3 +17,5 @@ export const BtcTestnetTypeMap: Record tx.status.confirmed && !!tx.status.block_time && - tx.status.block_time - Date.now() < ONE_HOUR_MS, + Date.now() - tx.status.block_time > ONE_HOUR_MS, }) public async getTx({ txid }: { txid: string }) { return this.call('getTx', { txid }); diff --git a/backend/src/core/ckb-explorer/ckb-explorer.service.ts b/backend/src/core/ckb-explorer/ckb-explorer.service.ts index b1c22e56..cf127d47 100644 --- a/backend/src/core/ckb-explorer/ckb-explorer.service.ts +++ b/backend/src/core/ckb-explorer/ckb-explorer.service.ts @@ -26,6 +26,9 @@ import { ONE_HOUR_MS, ONE_MONTH_MS } from 'src/common/date'; import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager'; import { toNumber } from 'lodash'; import { Cacheable } from 'src/decorators/cacheable.decorator'; +import { CkbRpcWebsocketService } from '../ckb-rpc/ckb-rpc-websocket.service'; +import { BI } from '@ckb-lumos/bi'; +import { CKB_MIN_SAFE_CONFIRMATIONS } from 'src/constants'; type BasePaginationParams = { page?: number; @@ -68,6 +71,7 @@ export class CkbExplorerService { constructor( private configService: ConfigService, + private ckbRpcService: CkbRpcWebsocketService, @Inject(CACHE_MANAGER) protected cacheManager: Cache, ) { this.request = axios.create({ @@ -83,6 +87,11 @@ export class CkbExplorerService { }); } + private async isSafeConfirmations(blockNumber: string): Promise { + const tipBlockNumber = await this.ckbRpcService.getTipBlockNumber(); + return BI.from(blockNumber).gt(BI.from(tipBlockNumber).add(CKB_MIN_SAFE_CONFIRMATIONS)); + } + // https://github.com/nervosnetwork/ckb-explorer-frontend/blob/b9dd537f836e8c827f1d4741e07c84484170d671/src/pages/Address/AddressPage.tsx#L50-L54 public async getAddress({ address, @@ -129,6 +138,10 @@ export class CkbExplorerService { namespace: 'CkbExplorerService', key: (heightOrHash: string) => `getBlock:${heightOrHash}`, ttl: ONE_MONTH_MS, + shouldCache: async (block: NonPaginatedResponse, that: CkbExplorerService) => { + const { number } = block.data.attributes; + return that.isSafeConfirmations(number); + }, }) public async getBlock(heightOrHash: string): Promise> { const response = await this.request.get(`/v1/blocks/${heightOrHash}`); @@ -194,11 +207,11 @@ export class CkbExplorerService { @Cacheable({ namespace: 'CkbExplorerService', key: (txHash: string) => `getTransaction:${txHash}`, - ttl: ONE_HOUR_MS, - shouldCache: async (tx: NonPaginatedResponse) => { - // cache tx for 1 month if it's committed and older than 1 hour - const { tx_status, block_timestamp } = tx.data.attributes; - return tx_status === 'committed' && Date.now() - toNumber(block_timestamp) > ONE_HOUR_MS; + ttl: ONE_MONTH_MS, + shouldCache: async (tx: NonPaginatedResponse, that: CkbExplorerService) => { + const { tx_status, block_number } = tx.data.attributes; + const isSafeConfirmations = await that.isSafeConfirmations(block_number); + return tx_status === 'committed' && isSafeConfirmations; }, }) public async getTransaction(txHash: string): Promise> { diff --git a/backend/src/core/ckb-rpc/ckb-rpc-websocket.service.ts b/backend/src/core/ckb-rpc/ckb-rpc-websocket.service.ts index c0bf95db..adfdc556 100644 --- a/backend/src/core/ckb-rpc/ckb-rpc-websocket.service.ts +++ b/backend/src/core/ckb-rpc/ckb-rpc-websocket.service.ts @@ -13,6 +13,7 @@ import { } from './ckb-rpc.interface'; import { Cacheable } from 'src/decorators/cacheable.decorator'; import { ONE_MONTH_MS } from 'src/common/date'; +import { CKB_MIN_SAFE_CONFIRMATIONS } from 'src/constants'; @Injectable() export class CkbRpcWebsocketService { @@ -29,16 +30,20 @@ export class CkbRpcWebsocketService { }); } + private async isSafeConfirmations(blockNumber: string): Promise { + const tipBlockNumber = await this.getTipBlockNumber(); + return BI.from(blockNumber).gt(BI.from(tipBlockNumber).add(CKB_MIN_SAFE_CONFIRMATIONS)); + } + @Cacheable({ namespace: 'CkbRpcWebsocketService', key: (txHash: string) => `getTransaction:${txHash}`, ttl: ONE_MONTH_MS, shouldCache: async (tx: TransactionWithStatusResponse, that: CkbRpcWebsocketService) => { - if (tx.tx_status.status !== 'committed') { + if (tx.tx_status.status !== 'committed' || !tx.tx_status.block_number) { return false; } - const block = await that.getTipBlockNumber(); - return BI.from(tx.tx_status.block_number).lt(BI.from(block)); + return that.isSafeConfirmations(tx.tx_status.block_number); }, }) public async getTransaction(txHash: string): Promise { @@ -47,6 +52,15 @@ export class CkbRpcWebsocketService { return tx as TransactionWithStatusResponse; } + @Cacheable({ + namespace: 'CkbRpcWebsocketService', + key: (blockHash: string) => `getBlock:${blockHash}`, + ttl: ONE_MONTH_MS, + shouldCache: async (block: Block, that: CkbRpcWebsocketService) => { + const { number } = block.header; + return that.isSafeConfirmations(number); + }, + }) public async getBlock(blockHash: string): Promise { this.logger.debug(`get_block - blockHash: ${blockHash}`); const block = await this.websocket.call('get_block', [blockHash]); @@ -61,6 +75,11 @@ export class CkbRpcWebsocketService { return block as Block; } + @Cacheable({ + namespace: 'CkbRpcWebsocketService', + key: (blockHash: string) => `getBlockEconomicState:${blockHash}`, + ttl: ONE_MONTH_MS, + }) public async getBlockEconomicState(blockHash: string): Promise { this.logger.debug(`get_block_economic_state - blockHash: ${blockHash}`); const blockEconomicState = await this.websocket.call('get_block_economic_state', [blockHash]);