From 900e66aef7aee9329c7bccc60a5d7eb1907d4176 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 2 Feb 2023 17:37:32 -0600 Subject: [PATCH] More robust error checking & handling in CPFP repositories --- backend/src/repositories/CpfpRepository.ts | 46 +++++++++++-------- .../src/repositories/TransactionRepository.ts | 34 +++++++------- 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/backend/src/repositories/CpfpRepository.ts b/backend/src/repositories/CpfpRepository.ts index ce7432d5b..9d8b2fe75 100644 --- a/backend/src/repositories/CpfpRepository.ts +++ b/backend/src/repositories/CpfpRepository.ts @@ -111,7 +111,7 @@ class CpfpRepository { } } - public async $getCluster(clusterRoot: string): Promise { + public async $getCluster(clusterRoot: string): Promise { const [clusterRows]: any = await DB.query( ` SELECT * @@ -121,8 +121,11 @@ class CpfpRepository { [clusterRoot] ); const cluster = clusterRows[0]; - cluster.txs = this.unpack(cluster.txs); - return cluster; + if (cluster?.txs) { + cluster.txs = this.unpack(cluster.txs); + return cluster; + } + return; } public async $deleteClustersFrom(height: number): Promise { @@ -136,9 +139,9 @@ class CpfpRepository { [height] ) as RowDataPacket[][]; if (rows?.length) { - for (let clusterToDelete of rows) { - const txs = this.unpack(clusterToDelete.txs); - for (let tx of txs) { + for (const clusterToDelete of rows) { + const txs = this.unpack(clusterToDelete?.txs); + for (const tx of txs) { await transactionRepository.$removeTransaction(tx.txid); } } @@ -204,20 +207,25 @@ class CpfpRepository { return []; } - const arrayBuffer = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); - const txs: Ancestor[] = []; - const view = new DataView(arrayBuffer); - for (let offset = 0; offset < arrayBuffer.byteLength; offset += 44) { - const txid = Array.from(new Uint8Array(arrayBuffer, offset, 32)).reverse().map(b => b.toString(16).padStart(2, '0')).join(''); - const weight = view.getUint32(offset + 32); - const fee = Number(view.getBigUint64(offset + 36)); - txs.push({ - txid, - weight, - fee - }); + try { + const arrayBuffer = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); + const txs: Ancestor[] = []; + const view = new DataView(arrayBuffer); + for (let offset = 0; offset < arrayBuffer.byteLength; offset += 44) { + const txid = Array.from(new Uint8Array(arrayBuffer, offset, 32)).reverse().map(b => b.toString(16).padStart(2, '0')).join(''); + const weight = view.getUint32(offset + 32); + const fee = Number(view.getBigUint64(offset + 36)); + txs.push({ + txid, + weight, + fee + }); + } + return txs; + } catch (e) { + logger.warn(`Failed to unpack CPFP cluster. Reason: ` + (e instanceof Error ? e.message : e)); + return []; } - return txs; } } diff --git a/backend/src/repositories/TransactionRepository.ts b/backend/src/repositories/TransactionRepository.ts index 061617451..279a2bdad 100644 --- a/backend/src/repositories/TransactionRepository.ts +++ b/backend/src/repositories/TransactionRepository.ts @@ -3,15 +3,6 @@ import logger from '../logger'; import { Ancestor, CpfpInfo } from '../mempool.interfaces'; import cpfpRepository from './CpfpRepository'; -interface CpfpSummary { - txid: string; - cluster: string; - root: string; - txs: Ancestor[]; - height: number; - fee_rate: number; -} - class TransactionRepository { public async $setCluster(txid: string, clusterRoot: string): Promise { try { @@ -72,7 +63,9 @@ class TransactionRepository { const txid = txRows[0].id.toLowerCase(); const clusterId = txRows[0].root.toLowerCase(); const cluster = await cpfpRepository.$getCluster(clusterId); - return this.convertCpfp(txid, cluster); + if (cluster) { + return this.convertCpfp(txid, cluster); + } } } catch (e) { logger.err('Cannot get transaction cpfp info from db. Reason: ' + (e instanceof Error ? e.message : e)); @@ -81,13 +74,18 @@ class TransactionRepository { } public async $removeTransaction(txid: string): Promise { - await DB.query( - ` - DELETE FROM compact_transactions - WHERE txid = UNHEX(?) - `, - [txid] - ); + try { + await DB.query( + ` + DELETE FROM compact_transactions + WHERE txid = UNHEX(?) + `, + [txid] + ); + } catch (e) { + logger.warn('Cannot delete transaction cpfp info from db. Reason: ' + (e instanceof Error ? e.message : e)); + throw e; + } } private convertCpfp(txid, cluster): CpfpInfo { @@ -95,7 +93,7 @@ class TransactionRepository { const ancestors: Ancestor[] = []; let matched = false; - for (const tx of cluster.txs) { + for (const tx of (cluster?.txs || [])) { if (tx.txid === txid) { matched = true; } else if (!matched) {