fix(btcindexer): isolate minted and reorged txs by network#355
fix(btcindexer): isolate minted and reorged txs by network#355
Conversation
Signed-off-by: sczembor <stanislaw.czembor@gmail.com>
Reviewer's GuideImplements network isolation for minted transaction queries and reorg detection by threading explicit btc/sui network parameters through the storage and indexer layers, and adds tests to verify correct behavior across multiple networks. Sequence diagram for network-isolated minted tx reorg detectionsequenceDiagram
participant Indexer
participant Storage
participant D1Database
Indexer->>Storage: getMintedTxs(blockHeight, btcNet, suiNet)
Storage->>D1Database: SELECT minted txs WHERE block_height >= blockHeight AND btc_network = btcNet AND sui_network = suiNet
D1Database-->>Storage: FinalizedTxRow[]
Storage-->>Indexer: FinalizedTxRow[]
loop each finalized tx in FinalizedTxRow[]
Indexer->>Indexer: locate tx in block.transactions by tx_id
alt tx not found in block
Indexer->>Indexer: handleMissingFinalizedMintingTx(txId, btcNet)
Indexer->>Storage: getTxStatus(txId, btcNet)
Storage->>D1Database: SELECT status FROM nbtc_minting JOIN setups WHERE tx_id = txId AND btc_network = btcNet
D1Database-->>Storage: MintTxStatus | null
Storage-->>Indexer: MintTxStatus | null
Indexer->>Storage: updateNbtcTxsStatus([txId], MintTxStatus.MintedReorg)
else tx found
Indexer->>Indexer: include tx in mint batch for its setup_id
end
end
Indexer->>Storage: detectMintedReorgs(blockHeight, btcNet)
Indexer->>Storage: getReorgedMintedTxs(blockHeight, btcNet)
Storage->>D1Database: SELECT reorged minted txs WHERE block_height >= blockHeight AND btc_blocks.network = btcNet
D1Database-->>Storage: ReorgedMintedTx[]
Storage-->>Indexer: ReorgedMintedTx[]
loop each reorged in ReorgedMintedTx[]
Indexer->>Storage: updateNbtcTxsStatus([reorged.tx_id], MintTxStatus.MintedReorg)
end
Class diagram for updated storage and indexer network isolationclassDiagram
class Storage {
<<interface>>
+insertBlockInfo(blockInfo: BlockQueueRecord) Promise~InsertBlockStatus~
+insertOrUpdateNbtcTxs(txs: NbtcTxInsertion[]) Promise~void~
+getNbtcMintCandidates(maxRetries: number) Promise~FinalizedTxRow[]~
+getMintedTxs(blockHeight: number, btcNet: BtcNet, suiNet: SuiNet) Promise~FinalizedTxRow[]~
+getTxStatus(txId: string, btcNet: BtcNet) Promise~MintTxStatus | null~
+getReorgedMintedTxs(blockHeight: number, btcNet: BtcNet) Promise~ReorgedMintedTx[]~
+updateNbtcTxsStatus(txIds: string[], status: MintTxStatus) Promise~void~
+batchUpdateNbtcMintTxs(updates: NbtcTxUpdate[]) Promise~void~
+updateConfirmingTxsToReorg(blockHashes: string[]) Promise~void~
}
class CFStorage {
-d1: D1Database
+insertBlockInfo(blockInfo: BlockQueueRecord) Promise~InsertBlockStatus~
+insertOrUpdateNbtcTxs(txs: NbtcTxInsertion[]) Promise~void~
+getNbtcMintCandidates(maxRetries: number) Promise~FinalizedTxRow[]~
+getMintedTxs(blockHeight: number, btcNet: BtcNet, suiNet: SuiNet) Promise~FinalizedTxRow[]~
+getTxStatus(txId: string, btcNet: BtcNet) Promise~MintTxStatus | null~
+getReorgedMintedTxs(blockHeight: number, btcNet: BtcNet) Promise~ReorgedMintedTx[]~
+updateNbtcTxsStatus(txIds: string[], status: MintTxStatus) Promise~void~
+batchUpdateNbtcMintTxs(updates: NbtcTxUpdate[]) Promise~void~
+updateConfirmingTxsToReorg(blockHashes: string[]) Promise~void~
}
class Indexer {
-storage: Storage
+processBlock(blockInfo: BlockQueueRecord) Promise~boolean~
+detectMintedReorgs(blockHeight: number, btcNet: BtcNet) Promise~void~
-handleMissingFinalizedMintingTx(txId: string, btcNet: BtcNet) Promise~void~
-getPackageConfig(setupId: number) PackageConfig
-processMintedTxBatches(block: BlockQueueRecord, txs: FinalizedTxRow[]) Promise~void~
}
class FinalizedTxRow {
+tx_id: string
+vout: number
+block_hash: string
+block_height: number
+nbtc_pkg: string
+sui_network: SuiNet
+btc_network: BtcNet
+setup_id: number
}
class ReorgedMintedTx {
+tx_id: string
+old_block_hash: string
+new_block_hash: string
+block_height: number
}
class PackageConfig {
+btc_network: BtcNet
+sui_network: SuiNet
+nbtc_pkg: string
+nbtc_contract: string
+lc_pkg: string
+lc_contract: string
+nbtc_fallback_addr: string
}
Storage <|.. CFStorage
Indexer --> Storage
CFStorage --> D1Database
Indexer --> PackageConfig
CFStorage --> FinalizedTxRow
CFStorage --> ReorgedMintedTx
File-Level Changes
Assessment against linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
@sourcery-ai title |
Signed-off-by: sczembor <stanislaw.czembor@gmail.com>
Signed-off-by: sczembor <43810037+sczembor@users.noreply.github.com>
Signed-off-by: sczembor <stanislaw.czembor@gmail.com>
Signed-off-by: sczembor <stanislaw.czembor@gmail.com>
There was a problem hiding this comment.
Pull request overview
This PR implements network isolation for the Bitcoin indexer by introducing setupId as a parameter to isolate nBTC minting transactions, reorged transactions, and transaction status lookups across different Bitcoin/Sui network pairs. The changes address critical cross-network data leakage issues where transactions from one network could interfere with another.
Changes:
- Extended storage and indexer interfaces to accept
setupIdparameter for all transaction queries and updates, replacing rawBtcNetparameters in several methods - Modified database schema to use composite primary key
(tx_id, address_id)innbtc_mintingtable, allowing the same Bitcoin transaction ID across different setups - Added comprehensive network isolation tests covering minted transactions, reorged transactions, and transaction status queries across multiple networks
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/btcindexer/src/storage.ts | Updated Storage interface to add setupId parameter to transaction query methods |
| packages/btcindexer/src/cf-storage.ts | Implemented network isolation in SQL queries using setupId filtering and JOIN conditions |
| packages/btcindexer/src/btcindexer.ts | Updated business logic to propagate setupId throughout the codebase and handle reorgs per setup |
| packages/btcindexer/src/router.ts | Added setupId query parameter validation for HTTP endpoints |
| packages/btcindexer/src/rpc.ts | Updated RPC methods to accept setupId parameter |
| packages/btcindexer/src/rpc-mock.ts | Updated mock RPC implementation signatures |
| packages/btcindexer/src/rpc-interface.ts | Updated RPC interface method signatures |
| packages/btcindexer/src/models.ts | Added setupId field to transaction models |
| packages/btcindexer/src/storage.test.ts | Added comprehensive network isolation tests and updated existing tests |
| packages/btcindexer/src/btcindexer.test.ts | Updated tests to pass setupId parameter |
| packages/btcindexer/src/btcindexer.helpers.test.ts | Updated test helper to support optional setupId filtering |
| packages/btcindexer/db/migrations/0001_initial_schema.sql | Changed PRIMARY KEY from tx_id to composite (tx_id, address_id) |
| packages/block-ingestor/README.md | Minor markdown formatting improvements |
| AGENTS.md | Minor markdown formatting and table alignment improvements |
Signed-off-by: sczembor <stanislaw.czembor@gmail.com>
Signed-off-by: sczembor <stanislaw.czembor@gmail.com>
Signed-off-by: sczembor <stanislaw.czembor@gmail.com>
Signed-off-by: sczembor <stanislaw.czembor@gmail.com>
robert-zaremba
left a comment
There was a problem hiding this comment.
approving, but I would prefer to not add setup_id to the objects when it's not necessary.
Signed-off-by: sczembor <stanislaw.czembor@gmail.com>
Description
Closes: #342 #343 #344
Author Checklist
All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.
I have...
!to the type prefix if API or client breaking changeCHANGELOG.mdSummary by Sourcery
Isolate nBTC minting and reorg handling by Bitcoin and Sui networks and extend tests to cover multi-network behaviour.
Bug Fixes:
Enhancements:
Tests: