Skip to content

Commit 3e9cb62

Browse files
refactor(btcindexer): rename types and API to more logical one
types: - BtcTxStatus -> MintTxStatus - TxStatusResp -> NbtcTxResp RPC (and similar in the Indexer): - statusByTxid -> nbtcMintTx - statusBySuiAddress -> nbtcMintTxsBySuiAddr Signed-off-by: Robert Zaremba <robert@zaremba.ch>
1 parent e788d76 commit 3e9cb62

File tree

8 files changed

+90
-96
lines changed

8 files changed

+90
-96
lines changed

packages/btcindexer/src/btcindexer.ts

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import type {
1010
MintBatchArg,
1111
GroupedFinalizedTx,
1212
NbtcAddress,
13+
NbtcTxRow,
1314
} from "./models";
14-
import { BlockStatus, BtcTxStatus } from "./models";
15+
import { BlockStatus, MintTxStatus } from "./models";
1516
import { toSerializableError } from "./errutils";
1617
import type { Electrs } from "./electrs";
1718
import { ElectrsService } from "./electrs";
@@ -213,7 +214,7 @@ export class Indexer {
213214
await this.storage.insertOrUpdateNbtcTxs(nbtcTxs);
214215
}
215216
if (senders.length > 0) {
216-
await this.storage.insertSenderDeposits(senders);
217+
await this.storage.insertBtcDeposit(senders);
217218
}
218219

219220
if (nbtcTxs.length === 0) {
@@ -281,7 +282,7 @@ export class Indexer {
281282
}
282283

283284
async processFinalizedTransactions(): Promise<void> {
284-
const finalizedTxs = await this.storage.getFinalizedTxs(this.maxNbtcMintTxRetries);
285+
const finalizedTxs = await this.storage.getNbtcFinalizedTxs(this.maxNbtcMintTxRetries);
285286

286287
if (!finalizedTxs || finalizedTxs.length === 0) {
287288
return;
@@ -351,7 +352,7 @@ export class Indexer {
351352
});
352353
try {
353354
// TODO: need to distniguish FINALIZED_REORG and MINTED_REORG
354-
await this.storage.updateTxsStatus([txId], BtcTxStatus.FinalizedReorg);
355+
await this.storage.updateNbtcTxsStatus([txId], MintTxStatus.FinalizedReorg);
355356
} catch (e) {
356357
console.error({
357358
msg: "Minting: Failed to update status to 'finalized-reorg'",
@@ -467,7 +468,7 @@ export class Indexer {
467468
processedPrimaryKeys.map((p) => ({
468469
tx_id: p.tx_id,
469470
vout: p.vout,
470-
status: BtcTxStatus.Minted,
471+
status: MintTxStatus.Minted,
471472
suiTxDigest,
472473
})),
473474
);
@@ -477,7 +478,7 @@ export class Indexer {
477478
processedPrimaryKeys.map((p) => ({
478479
tx_id: p.tx_id,
479480
vout: p.vout,
480-
status: BtcTxStatus.MintFailed,
481+
status: MintTxStatus.MintFailed,
481482
})),
482483
);
483484
}
@@ -575,7 +576,7 @@ export class Indexer {
575576
});
576577
// This requires a new method in the Storage interface like:
577578
// updateTxsStatus(txIds: string[], status: TxStatus): Promise<void>
578-
await this.storage.updateTxsStatus(reorgedTxIds, BtcTxStatus.Reorg);
579+
await this.storage.updateNbtcTxsStatus(reorgedTxIds, MintTxStatus.Reorg);
579580
}
580581

581582
// TODO: add a unit test for it so we make sure we do not finalize reorrged tx.
@@ -587,7 +588,7 @@ export class Indexer {
587588
msg: "Finalization: Applying status updates to D1",
588589
finalizedCount: finalizationTxIds.length,
589590
});
590-
await this.storage.finalizeTxs(finalizationTxIds);
591+
await this.storage.finalizeNbtcTxs(finalizationTxIds);
591592
}
592593
}
593594

@@ -631,37 +632,27 @@ export class Indexer {
631632
return txIds;
632633
}
633634

634-
async getStatusByTxid(txid: string): Promise<NbtcTxResp | null> {
635-
const latestHeight = await this.storage.getChainTip();
636-
const tx = await this.storage.getStatusByTxid(txid);
637-
638-
if (!tx) {
639-
return null;
640-
}
635+
// queries NbtcTxResp by BTC Tx ID
636+
async getNbtcMintTx(txid: string): Promise<NbtcTxResp | null> {
637+
const nbtMintRow = await this.storage.getNbtcMintTx(txid);
638+
if (!nbtMintRow) return null;
641639

642-
const blockHeight = tx.block_height as number;
643-
const confirmations = blockHeight && latestHeight ? latestHeight - blockHeight + 1 : 0;
640+
const latestHeight = await this.storage.getChainTip();
644641

645-
return {
646-
...tx,
647-
btc_tx_id: tx.tx_id,
648-
status: tx.status as BtcTxStatus,
649-
block_height: blockHeight,
650-
confirmations: confirmations > 0 ? confirmations : 0,
651-
};
642+
return nbtcRowToResp(nbtMintRow, latestHeight);
652643
}
653644

654-
async getStatusBySuiAddress(suiAddress: string): Promise<NbtcTxResp[]> {
645+
async getNbtcMintTxsBySuiAddr(suiAddress: string): Promise<NbtcTxResp[]> {
655646
const latestHeight = await this.storage.getChainTip();
656-
const dbResult = await this.storage.getStatusBySuiAddress(suiAddress);
647+
const dbResult = await this.storage.getNbtcMintTxsBySuiAddr(suiAddress);
657648

658649
return dbResult.map((tx): NbtcTxResp => {
659650
const blockHeight = tx.block_height as number;
660651
const confirmations = blockHeight && latestHeight ? latestHeight - blockHeight + 1 : 0;
661652
return {
662653
...tx,
663654
btc_tx_id: tx.tx_id,
664-
status: tx.status as BtcTxStatus,
655+
status: tx.status as MintTxStatus,
665656
block_height: blockHeight,
666657
confirmations: confirmations > 0 ? confirmations : 0,
667658
};
@@ -696,22 +687,10 @@ export class Indexer {
696687
}
697688

698689
async getDepositsBySender(btcAddress: string): Promise<NbtcTxResp[]> {
699-
const dbResult = await this.storage.getDepositsBySender(btcAddress);
690+
const nbtcMintRows = await this.storage.getNbtcMintTxsByBtcSender(btcAddress);
700691
const latestHeight = await this.storage.getChainTip();
701692

702-
return dbResult.map((tx): NbtcTxResp => {
703-
const bh = tx.block_height;
704-
const confirmations = bh && latestHeight ? latestHeight - bh + 1 : 0;
705-
const btc_tx_id = tx.tx_id;
706-
// @ts-expect-error The operand of a 'delete' operator must be optional
707-
delete tx.tx_id;
708-
709-
return {
710-
btc_tx_id,
711-
confirmations: confirmations > 0 ? confirmations : 0,
712-
...tx,
713-
};
714-
});
693+
return nbtcMintRows.map((r) => nbtcRowToResp(r, latestHeight));
715694
}
716695

717696
private async getSenderAddresses(tx: Transaction): Promise<string[]> {
@@ -764,3 +743,17 @@ function parseSuiRecipientFromOpReturn(script: Buffer): string | null {
764743
// for now we cannot determine the recipient
765744
return null;
766745
}
746+
747+
function nbtcRowToResp(r: NbtcTxRow, latestHeight: number | null): NbtcTxResp {
748+
const bh = r.block_height;
749+
const confirmations = bh && latestHeight ? latestHeight - bh + 1 : 0;
750+
const btc_tx_id = r.tx_id;
751+
// @ts-expect-error The operand of a 'delete' operator must be optional
752+
delete r.tx_id;
753+
754+
return {
755+
btc_tx_id,
756+
confirmations: confirmations > 0 ? confirmations : 0,
757+
...r,
758+
};
759+
}

packages/btcindexer/src/cf-storage.ts

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Block } from "bitcoinjs-lib";
22
import { toSerializableError } from "./errutils";
33
import type { BlockInfo, FinalizedTxRow, NbtcTxRow, PendingTx } from "./models";
4-
import { BlockStatus, BtcTxStatus } from "./models";
4+
import { BlockStatus, MintTxStatus } from "./models";
55
import type { Storage } from "./storage";
66

77
export class CFStorage implements Storage {
@@ -146,11 +146,11 @@ export class CFStorage implements Storage {
146146
const now = Date.now();
147147
const insertOrUpdateNbtcTxStmt = this.d1.prepare(
148148
`INSERT INTO nbtc_minting (tx_id, vout, block_hash, block_height, sui_recipient, amount_sats, status, created_at, updated_at)
149-
VALUES (?, ?, ?, ?, ?, ?, '${BtcTxStatus.Confirming}', ?, ?)
149+
VALUES (?, ?, ?, ?, ?, ?, '${MintTxStatus.Confirming}', ?, ?)
150150
ON CONFLICT(tx_id, vout) DO UPDATE SET
151151
block_hash = excluded.block_hash,
152152
block_height = excluded.block_height,
153-
status = '${BtcTxStatus.Confirming}',
153+
status = '${MintTxStatus.Confirming}',
154154
updated_at = excluded.updated_at`,
155155
);
156156
const statements = txs.map((tx) =>
@@ -176,17 +176,17 @@ export class CFStorage implements Storage {
176176
}
177177
}
178178

179-
async getFinalizedTxs(maxRetries: number): Promise<FinalizedTxRow[]> {
179+
async getNbtcFinalizedTxs(maxRetries: number): Promise<FinalizedTxRow[]> {
180180
const finalizedTxs = await this.d1
181181
.prepare(
182-
`SELECT tx_id, vout, block_hash, block_height, retry_count, nbtc_pkg, sui_network FROM nbtc_minting WHERE (status = '${BtcTxStatus.Finalized}' OR (status = '${BtcTxStatus.MintFailed}' AND retry_count <= ?)) AND status != '${BtcTxStatus.FinalizedReorg}'`,
182+
`SELECT tx_id, vout, block_hash, block_height, retry_count, nbtc_pkg, sui_network FROM nbtc_minting WHERE (status = '${MintTxStatus.Finalized}' OR (status = '${MintTxStatus.MintFailed}' AND retry_count <= ?)) AND status != '${MintTxStatus.FinalizedReorg}'`,
183183
)
184184
.bind(maxRetries)
185185
.all<FinalizedTxRow>();
186186
return finalizedTxs.results ?? [];
187187
}
188188

189-
async updateTxsStatus(txIds: string[], status: BtcTxStatus): Promise<void> {
189+
async updateNbtcTxsStatus(txIds: string[], status: MintTxStatus): Promise<void> {
190190
if (txIds.length === 0) {
191191
return;
192192
}
@@ -201,7 +201,7 @@ export class CFStorage implements Storage {
201201
}
202202

203203
async batchUpdateNbtcTxs(
204-
updates: { tx_id: string; vout: number; status: BtcTxStatus; suiTxDigest?: string }[],
204+
updates: { tx_id: string; vout: number; status: MintTxStatus; suiTxDigest?: string }[],
205205
): Promise<void> {
206206
const now = Date.now();
207207
const setMintedStmt = this.d1.prepare(
@@ -212,10 +212,10 @@ export class CFStorage implements Storage {
212212
);
213213

214214
const statements = updates.map((p) => {
215-
if (p.status === BtcTxStatus.Minted) {
216-
return setMintedStmt.bind(BtcTxStatus.Minted, p.suiTxDigest, now, p.tx_id, p.vout);
215+
if (p.status === MintTxStatus.Minted) {
216+
return setMintedStmt.bind(MintTxStatus.Minted, p.suiTxDigest, now, p.tx_id, p.vout);
217217
} else {
218-
return setFailedStmt.bind(BtcTxStatus.MintFailed, now, p.tx_id, p.vout);
218+
return setFailedStmt.bind(MintTxStatus.MintFailed, now, p.tx_id, p.vout);
219219
}
220220
});
221221
try {
@@ -236,7 +236,7 @@ export class CFStorage implements Storage {
236236
// This ensures we only try to verify blocks we know about.
237237
const blocksToVerify = await this.d1
238238
.prepare(
239-
`SELECT DISTINCT block_hash FROM nbtc_minting WHERE status = '${BtcTxStatus.Confirming}' AND block_hash IS NOT NULL`,
239+
`SELECT DISTINCT block_hash FROM nbtc_minting WHERE status = '${MintTxStatus.Confirming}' AND block_hash IS NOT NULL`,
240240
)
241241
.all<{ block_hash: string }>();
242242
return blocksToVerify.results ?? [];
@@ -247,7 +247,7 @@ export class CFStorage implements Storage {
247247
const placeholders = blockHashes.map(() => "?").join(",");
248248
const updateStmt = this.d1
249249
.prepare(
250-
`UPDATE nbtc_minting SET status = '${BtcTxStatus.Reorg}', updated_at = ? WHERE block_hash IN (${placeholders})`,
250+
`UPDATE nbtc_minting SET status = '${MintTxStatus.Reorg}', updated_at = ? WHERE block_hash IN (${placeholders})`,
251251
)
252252
.bind(now, ...blockHashes);
253253
await updateStmt.run();
@@ -256,33 +256,32 @@ export class CFStorage implements Storage {
256256
async getConfirmingTxs(): Promise<PendingTx[]> {
257257
const pendingTxs = await this.d1
258258
.prepare(
259-
`SELECT tx_id, block_hash, block_height FROM nbtc_minting WHERE status = '${BtcTxStatus.Confirming}'`,
259+
`SELECT tx_id, block_hash, block_height FROM nbtc_minting WHERE status = '${MintTxStatus.Confirming}'`,
260260
)
261261
.all<PendingTx>();
262262
return pendingTxs.results ?? [];
263263
}
264264

265-
async finalizeTxs(txIds: string[]): Promise<void> {
265+
async finalizeNbtcTxs(txIds: string[]): Promise<void> {
266266
if (txIds.length === 0) {
267267
return;
268268
}
269269
const now = Date.now();
270270
const finalizeStmt = this.d1.prepare(
271-
`UPDATE nbtc_minting SET status = '${BtcTxStatus.Finalized}', updated_at = ${now} WHERE tx_id = ?`,
271+
`UPDATE nbtc_minting SET status = '${MintTxStatus.Finalized}', updated_at = ${now} WHERE tx_id = ?`,
272272
);
273273
const statements = txIds.map((txId) => finalizeStmt.bind(txId));
274274
await this.d1.batch(statements);
275275
}
276276

277-
// TODO: rename to getNbtcMineTx
278-
async getStatusByTxid(txid: string): Promise<NbtcTxRow | null> {
277+
async getNbtcMintTx(txId: string): Promise<NbtcTxRow | null> {
279278
return this.d1
280279
.prepare("SELECT * FROM nbtc_minting WHERE tx_id = ?")
281-
.bind(txid)
280+
.bind(txId)
282281
.first<NbtcTxRow>();
283282
}
284283

285-
async getStatusBySuiAddress(suiAddress: string): Promise<NbtcTxRow[]> {
284+
async getNbtcMintTxsBySuiAddr(suiAddress: string): Promise<NbtcTxRow[]> {
286285
const dbResult = await this.d1
287286
.prepare("SELECT * FROM nbtc_minting WHERE sui_recipient = ? ORDER BY created_at DESC")
288287
.bind(suiAddress)
@@ -296,7 +295,7 @@ export class CFStorage implements Storage {
296295
const now = Date.now();
297296
const insertStmt = this.d1.prepare(
298297
`INSERT OR IGNORE INTO nbtc_minting (tx_id, vout, sui_recipient, amount_sats, status, created_at, updated_at)
299-
VALUES (?, ?, ?, ?, '${BtcTxStatus.Broadcasting}', ?, ?)`,
298+
VALUES (?, ?, ?, ?, '${MintTxStatus.Broadcasting}', ?, ?)`,
300299
);
301300

302301
const statements = deposits.map((deposit) =>
@@ -312,7 +311,7 @@ export class CFStorage implements Storage {
312311
await this.d1.batch(statements);
313312
}
314313

315-
async getDepositsBySender(btcAddress: string): Promise<NbtcTxRow[]> {
314+
async getNbtcMintTxsByBtcSender(btcAddress: string): Promise<NbtcTxRow[]> {
316315
const query = this.d1.prepare(`
317316
SELECT m.* FROM nbtc_minting m
318317
JOIN nbtc_sender_deposits s ON m.tx_id = s.tx_id
@@ -323,7 +322,7 @@ export class CFStorage implements Storage {
323322
return dbResult.results ?? [];
324323
}
325324

326-
async insertSenderDeposits(senders: { txId: string; sender: string }[]): Promise<void> {
325+
async insertBtcDeposit(senders: { txId: string; sender: string }[]): Promise<void> {
327326
if (senders.length === 0) {
328327
return;
329328
}

packages/btcindexer/src/models.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export interface GroupedFinalizedTx {
5858
* - **reorg**: A blockchain reorg detected while the tx was in the 'confirming' state. The tx block is no longer part of the canonical chain.
5959
* - **finalized-reorg**: An edge-case status indicating that a tx was marked 'finalized', but was later discovered to be on an orphaned (re-org deeper than the confirmation depth).
6060
*/
61-
export const enum BtcTxStatus {
61+
export const enum MintTxStatus {
6262
Broadcasting = "broadcasting",
6363
Confirming = "confirming",
6464
Finalized = "finalized",
@@ -75,7 +75,7 @@ export const enum BlockStatus {
7575

7676
export interface NbtcTxResp extends Omit<NbtcTxRow, "tx_id"> {
7777
btc_tx_id: string;
78-
status: BtcTxStatus;
78+
status: MintTxStatus;
7979
confirmations: number;
8080
}
8181

@@ -88,7 +88,7 @@ export interface NbtcTxRow {
8888
block_height: number | null;
8989
sui_recipient: string;
9090
amount_sats: number;
91-
status: BtcTxStatus;
91+
status: MintTxStatus;
9292
// epoch time in ms
9393
created_at: number;
9494
// epoch time in ms

packages/btcindexer/src/router.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ export default class HttpRouter {
3232

3333
r.post(RestPath.nbtcTx, this.postNbtcTx);
3434
// ?sui_recipient="0x..." - query by sui address
35-
r.get(RestPath.nbtcTx, this.getStatusBySuiAddress);
36-
r.get(RestPath.nbtcTx + "/:txid", this.getStatusByTxid); // query by bitcoin_tx_id
35+
r.get(RestPath.nbtcTx, this.getNbtcMintTxsBySuiAddr);
36+
r.get(RestPath.nbtcTx + "/:txid", this.getNbtcMintTx); // query by bitcoin_tx_id
3737
r.get(RestPath.depositsBySender, this.getDepositsBySender);
3838

3939
//
@@ -133,7 +133,7 @@ export default class HttpRouter {
133133
return kv.get(key);
134134
};
135135

136-
getStatusByTxid = async (req: IRequest) => {
136+
getNbtcMintTx = async (req: IRequest) => {
137137
const params = req.params;
138138
if (!params) {
139139
return error(400, "Missing parameters");
@@ -142,23 +142,23 @@ export default class HttpRouter {
142142
if (!txid) {
143143
return error(400, "Missing txid parameter");
144144
}
145-
const result = await this.indexer().getStatusByTxid(txid);
145+
const result = await this.indexer().getNbtcMintTx(txid);
146146

147147
if (result === null) {
148148
return error(404, "Transaction not found.");
149149
}
150150
return result;
151151
};
152152

153-
getStatusBySuiAddress = async (req: IRequest) => {
153+
getNbtcMintTxsBySuiAddr = async (req: IRequest) => {
154154
const suiRecipient = req.query.sui_recipient;
155155
if (!suiRecipient || typeof suiRecipient !== "string") {
156156
return error(400, "Missing or invalid sui_recipient query parameter.");
157157
}
158158
if (!isValidSuiAddress(suiRecipient)) {
159159
return error(400, "Invalid SUI address format.");
160160
}
161-
return this.indexer().getStatusBySuiAddress(suiRecipient);
161+
return this.indexer().getNbtcMintTxsBySuiAddr(suiRecipient);
162162
};
163163

164164
getLatestHeight = () => {

packages/btcindexer/src/rpc-interface.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export interface BtcIndexerRpcI {
88
putBlocks(blocks: PutBlocks[]): Promise<number>;
99
latestHeight(): Promise<{ height: number | null }>;
1010
putNbtcTx(txHex: string): Promise<{ tx_id: string; registered_deposits: number }>;
11-
statusByTxid(txid: string): Promise<NbtcTxResp | null>;
12-
statusBySuiAddress(suiAddress: string): Promise<NbtcTxResp[]>;
11+
nbtcMintTx(txId: string): Promise<NbtcTxResp | null>;
12+
nbtcMintTxsBySuiAddr(suiAddress: string): Promise<NbtcTxResp[]>;
1313
depositsBySender(address: string): Promise<NbtcTxResp[]>;
1414
}

0 commit comments

Comments
 (0)