From 6d558fcdc0e12d8414298bcbd61ead6a7aa07774 Mon Sep 17 00:00:00 2001 From: Rayane Charif Date: Mon, 22 Dec 2025 17:33:07 +0100 Subject: [PATCH 1/3] update insertBlockInfo to return proper status Signed-off-by: Rayane Charif --- packages/btcindexer/src/btcindexer.test.ts | 6 ++-- packages/btcindexer/src/btcindexer.ts | 7 +++-- packages/btcindexer/src/cf-storage.ts | 34 ++++++++++++++++++---- packages/btcindexer/src/models.ts | 5 ++++ packages/btcindexer/src/storage.test.ts | 6 ++-- packages/btcindexer/src/storage.ts | 3 +- 6 files changed, 45 insertions(+), 16 deletions(-) diff --git a/packages/btcindexer/src/btcindexer.test.ts b/packages/btcindexer/src/btcindexer.test.ts index 698060c..dc3ac9a 100644 --- a/packages/btcindexer/src/btcindexer.test.ts +++ b/packages/btcindexer/src/btcindexer.test.ts @@ -855,7 +855,7 @@ describe("CFStorage.insertBlockInfo (Stale Block Protection)", () => { }; const result = await indexer.storage.insertBlockInfo(record); - expect(result).toBe(true); + expect(result).toEqual({ status: "inserted", changed: true }); const db = await mf.getD1Database("DB"); const row = await db.prepare("SELECT * FROM btc_blocks WHERE height = 100").first(); expect(row).toEqual( @@ -885,7 +885,7 @@ describe("CFStorage.insertBlockInfo (Stale Block Protection)", () => { const result = await indexer.storage.insertBlockInfo(newerRecord); const db = await mf.getD1Database("DB"); - expect(result).toBe(true); + expect(result).toEqual({ status: "updated", changed: true }); const row = await db.prepare("SELECT * FROM btc_blocks WHERE height = 100").first(); expect(row).toEqual( expect.objectContaining({ @@ -913,7 +913,7 @@ describe("CFStorage.insertBlockInfo (Stale Block Protection)", () => { const result = await indexer.storage.insertBlockInfo(staleRecord); - expect(result).toBe(false); // Update rejected + expect(result).toEqual({ status: "skipped", changed: false }); // Update rejected const db = await mf.getD1Database("DB"); const row = await db.prepare("SELECT * FROM btc_blocks WHERE height = 100").first(); expect(row).toEqual( diff --git a/packages/btcindexer/src/btcindexer.ts b/packages/btcindexer/src/btcindexer.ts index 12a5a63..b54c858 100644 --- a/packages/btcindexer/src/btcindexer.ts +++ b/packages/btcindexer/src/btcindexer.ts @@ -172,13 +172,14 @@ export class Indexer { const block = Block.fromBuffer(Buffer.from(rawBlockBuffer)); const existingHash = await this.storage.getBlockHash(blockInfo.height, blockInfo.network); - const isNewOrMinted = await this.storage.insertBlockInfo(blockInfo); - if (!isNewOrMinted) { + const result = await this.storage.insertBlockInfo(blockInfo); + if (!result.changed) { logger.debug({ - msg: "Skipping processing already processed block", + msg: "Skipping block, no changes made", method: "Indexer.processBlock", height: blockInfo.height, hash: blockInfo.hash, + status: result.status, }); return; } diff --git a/packages/btcindexer/src/cf-storage.ts b/packages/btcindexer/src/cf-storage.ts index 95c1543..5fad920 100644 --- a/packages/btcindexer/src/cf-storage.ts +++ b/packages/btcindexer/src/cf-storage.ts @@ -9,6 +9,7 @@ import type { NbtcTxUpdate, NbtcBroadcastedDeposit, ConfirmingBlockInfo, + InsertBlockResult, } from "./models"; import { MintTxStatus } from "./models"; import type { Storage } from "./storage"; @@ -63,11 +64,12 @@ export class CFStorage implements Storage { * - AND The incoming `timestamp_ms` is greater than the stored `inserted_at`. * * @param b - The block record from the queue. - * @returns `true` if the block was inserted or updated, `false` otherwise. + * @returns InsertBlockResult tells us whether the block was inserted, updated, or skipped. */ - async insertBlockInfo(b: BlockQueueRecord): Promise { - // TODO: we should return here InsertResult {ignored, inserted, updated} - // we need to use batching + async insertBlockInfo(b: BlockQueueRecord): Promise { + const checkRowStmt = this.d1.prepare( + `SELECT 1 FROM btc_blocks WHERE height = ? AND network = ?`, + ); const insertStmt = this.d1.prepare( `INSERT INTO btc_blocks (hash, height, network, inserted_at) VALUES (?, ?, ?, ?) ON CONFLICT(height, network) DO UPDATE SET @@ -77,8 +79,28 @@ export class CFStorage implements Storage { WHERE btc_blocks.hash IS NOT excluded.hash AND excluded.inserted_at > btc_blocks.inserted_at`, ); try { - const result = await insertStmt.bind(b.hash, b.height, b.network, b.timestamp_ms).run(); - return result.meta.changes > 0; + const results = await this.d1.batch([ + checkRowStmt.bind(b.height, b.network), + insertStmt.bind(b.hash, b.height, b.network, b.timestamp_ms), + ]); + + const checkResult = results[0]; + const upsertResult = results[1]; + + if (!checkResult || !upsertResult) { + throw new Error("Batch operation failed"); + } + + const wasFound = checkResult.results.length > 0; + const rowsChanged = upsertResult.meta.changes > 0; + + if (!wasFound) { + return { status: "inserted", changed: true }; + } else if (rowsChanged) { + return { status: "updated", changed: true }; + } else { + return { status: "skipped", changed: false }; + } } catch (e) { logError( { diff --git a/packages/btcindexer/src/models.ts b/packages/btcindexer/src/models.ts index 9c6efe3..14b5cff 100644 --- a/packages/btcindexer/src/models.ts +++ b/packages/btcindexer/src/models.ts @@ -184,3 +184,8 @@ export interface NbtcDepositAddrVal { // Maps Bitcoin deposit address to NbtcDepositAddrMapping export type NbtcDepositAddrsMap = Map; + +export type InsertBlockResult = + | { status: "inserted"; changed: true } + | { status: "updated"; changed: true } + | { status: "skipped"; changed: false }; diff --git a/packages/btcindexer/src/storage.test.ts b/packages/btcindexer/src/storage.test.ts index 5b42401..d4b9b95 100644 --- a/packages/btcindexer/src/storage.test.ts +++ b/packages/btcindexer/src/storage.test.ts @@ -135,7 +135,7 @@ describe("CFStorage", () => { timestamp_ms: 1000, }; const result = await storage.insertBlockInfo(block); - expect(result).toBe(true); + expect(result).toEqual({ status: "inserted", changed: true }); const saved = await storage.getBlockHash(100, BtcNet.REGTEST); expect(saved).toBe("0000hash1"); @@ -155,7 +155,7 @@ describe("CFStorage", () => { network: BtcNet.REGTEST, timestamp_ms: 2000, }); - expect(result).toBe(true); + expect(result).toEqual({ status: "updated", changed: true }); const saved = await storage.getBlockHash(100, BtcNet.REGTEST); expect(saved).toBe("0000hashNew"); }); @@ -174,7 +174,7 @@ describe("CFStorage", () => { network: BtcNet.REGTEST, timestamp_ms: 1000, }); - expect(result).toBe(false); + expect(result).toEqual({ status: "skipped", changed: false }); const saved = await storage.getBlockHash(100, BtcNet.REGTEST); expect(saved).toBe("0000hashNew"); }); diff --git a/packages/btcindexer/src/storage.ts b/packages/btcindexer/src/storage.ts index cdfed72..4924771 100644 --- a/packages/btcindexer/src/storage.ts +++ b/packages/btcindexer/src/storage.ts @@ -10,6 +10,7 @@ import type { NbtcPkgCfg, NbtcDepositAddrsMap, ConfirmingBlockInfo, + InsertBlockResult, } from "./models"; import { D1Database } from "@cloudflare/workers-types"; import type { BlockQueueRecord, BtcNet } from "@gonative-cc/lib/nbtc"; @@ -18,7 +19,7 @@ import { toSuiNet } from "@gonative-cc/lib/nsui"; export interface Storage { // Block operations markBlockAsProcessed(hash: string, network: BtcNet): Promise; - insertBlockInfo(blockMessage: BlockQueueRecord): Promise; + insertBlockInfo(blockMessage: BlockQueueRecord): Promise; getLatestBlockHeight(network: BtcNet): Promise; getChainTip(network: BtcNet): Promise; setChainTip(height: number, network: BtcNet): Promise; From 831a1cf04c9ee613284a31876e2b53c590bf0d47 Mon Sep 17 00:00:00 2001 From: Rayane Charif Date: Tue, 23 Dec 2025 12:21:24 +0100 Subject: [PATCH 2/3] resolved comments Signed-off-by: Rayane Charif --- packages/btcindexer/src/btcindexer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/btcindexer/src/btcindexer.ts b/packages/btcindexer/src/btcindexer.ts index b54c858..05e8172 100644 --- a/packages/btcindexer/src/btcindexer.ts +++ b/packages/btcindexer/src/btcindexer.ts @@ -175,7 +175,7 @@ export class Indexer { const result = await this.storage.insertBlockInfo(blockInfo); if (!result.changed) { logger.debug({ - msg: "Skipping block, no changes made", + msg: "Skipping block, no already processed", method: "Indexer.processBlock", height: blockInfo.height, hash: blockInfo.hash, From 787a210133f23874c2c3c00fe7bd413e06c77e0d Mon Sep 17 00:00:00 2001 From: Rayane Charif Date: Tue, 23 Dec 2025 12:28:39 +0100 Subject: [PATCH 3/3] resolved log comment Signed-off-by: Rayane Charif --- packages/btcindexer/src/btcindexer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/btcindexer/src/btcindexer.ts b/packages/btcindexer/src/btcindexer.ts index 05e8172..769e98d 100644 --- a/packages/btcindexer/src/btcindexer.ts +++ b/packages/btcindexer/src/btcindexer.ts @@ -175,7 +175,7 @@ export class Indexer { const result = await this.storage.insertBlockInfo(blockInfo); if (!result.changed) { logger.debug({ - msg: "Skipping block, no already processed", + msg: "Skipping: block already processed", method: "Indexer.processBlock", height: blockInfo.height, hash: blockInfo.hash,