Double Spend Detection
Overview
UTXO Tracking and Monitoring
import { Transaction, ARC } from '@bsv/sdk'
/**
* UTXO Monitor
*
* Track UTXOs and detect double-spend attempts
*/
class UTXOMonitor {
private trackedUTXOs: Map<string, TrackedUTXO> = new Map()
private arc: ARC
constructor(arcUrl: string = 'https://arc.taal.com') {
this.arc = new ARC(arcUrl)
}
/**
* Track a UTXO for double-spend detection
*/
trackUTXO(utxo: {
txid: string
vout: number
satoshis: number
address: string
}): void {
const utxoKey = this.getUTXOKey(utxo.txid, utxo.vout)
const tracked: TrackedUTXO = {
txid: utxo.txid,
vout: utxo.vout,
satoshis: utxo.satoshis,
address: utxo.address,
status: 'unspent',
trackedAt: Date.now(),
spendingTxids: []
}
this.trackedUTXOs.set(utxoKey, tracked)
console.log('Tracking UTXO:', utxoKey)
}
/**
* Check if UTXO has been spent
*/
async checkUTXOStatus(txid: string, vout: number): Promise<UTXOStatus> {
try {
const utxoKey = this.getUTXOKey(txid, vout)
const tracked = this.trackedUTXOs.get(utxoKey)
if (!tracked) {
throw new Error('UTXO not tracked')
}
// Query transaction status
const txStatus = await this.arc.getTransactionStatus(txid)
// Check if any spending transactions exist
const spendingTxs = await this.findSpendingTransactions(txid, vout)
const status: UTXOStatus = {
utxoKey,
spent: spendingTxs.length > 0,
spendingTxids: spendingTxs.map(tx => tx.txid),
doubleSpendDetected: spendingTxs.length > 1,
confirmedSpend: spendingTxs.some(tx => tx.confirmed),
timestamp: Date.now()
}
// Update tracked UTXO
if (status.spent) {
tracked.status = status.doubleSpendDetected ? 'double-spend' : 'spent'
tracked.spendingTxids = status.spendingTxids
}
if (status.doubleSpendDetected) {
console.warn('DOUBLE-SPEND DETECTED:', utxoKey)
console.warn('Spending transactions:', status.spendingTxids)
}
return status
} catch (error) {
throw new Error(`UTXO status check failed: ${error.message}`)
}
}
/**
* Find all transactions spending a UTXO
*/
private async findSpendingTransactions(
txid: string,
vout: number
): Promise<Array<{ txid: string; confirmed: boolean }>> {
try {
// In production, query mempool and blockchain for spending txs
// This is a simplified example
const spendingTxs: Array<{ txid: string; confirmed: boolean }> = []
// Query ARC for spending transactions
// Note: Actual implementation depends on ARC API capabilities
return spendingTxs
} catch (error) {
console.error('Error finding spending transactions:', error.message)
return []
}
}
/**
* Monitor tracked UTXOs continuously
*/
async startMonitoring(
callback: (alert: DoubleSpendAlert) => void,
interval: number = 5000
): Promise<void> {
console.log('Starting UTXO monitoring...')
const monitor = async () => {
for (const [utxoKey, tracked] of this.trackedUTXOs.entries()) {
if (tracked.status === 'unspent') {
try {
const status = await this.checkUTXOStatus(tracked.txid, tracked.vout)
if (status.doubleSpendDetected) {
const alert: DoubleSpendAlert = {
utxoKey,
txid: tracked.txid,
vout: tracked.vout,
spendingTxids: status.spendingTxids,
detectedAt: Date.now(),
severity: status.confirmedSpend ? 'high' : 'medium'
}
callback(alert)
}
} catch (error) {
console.error('Monitoring error for', utxoKey, ':', error.message)
}
}
}
setTimeout(monitor, interval)
}
monitor()
}
/**
* Remove UTXO from tracking
*/
untrackUTXO(txid: string, vout: number): void {
const utxoKey = this.getUTXOKey(txid, vout)
this.trackedUTXOs.delete(utxoKey)
console.log('Stopped tracking:', utxoKey)
}
/**
* Get UTXO key
*/
private getUTXOKey(txid: string, vout: number): string {
return `${txid}:${vout}`
}
/**
* Get all tracked UTXOs
*/
getTrackedUTXOs(): TrackedUTXO[] {
return Array.from(this.trackedUTXOs.values())
}
}
interface TrackedUTXO {
txid: string
vout: number
satoshis: number
address: string
status: 'unspent' | 'spent' | 'double-spend'
trackedAt: number
spendingTxids: string[]
}
interface UTXOStatus {
utxoKey: string
spent: boolean
spendingTxids: string[]
doubleSpendDetected: boolean
confirmedSpend: boolean
timestamp: number
}
interface DoubleSpendAlert {
utxoKey: string
txid: string
vout: number
spendingTxids: string[]
detectedAt: number
severity: 'low' | 'medium' | 'high'
}
/**
* Usage Example
*/
async function utxoMonitorExample() {
const monitor = new UTXOMonitor()
// Track UTXO
monitor.trackUTXO({
txid: 'utxo-txid...',
vout: 0,
satoshis: 100000,
address: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'
})
// Check status
const status = await monitor.checkUTXOStatus('utxo-txid...', 0)
console.log('UTXO status:', status)
// Start continuous monitoring
await monitor.startMonitoring((alert) => {
console.error('DOUBLE-SPEND ALERT:', alert)
// Take action: notify admin, block payment, etc.
})
}Transaction Conflict Detection
Payment Security Manager
Related Examples
See Also
Last updated
