SPV
Complete examples for implementing Simplified Payment Verification (SPV) clients and systems for BSV.
Overview
Simplified Payment Verification (SPV) allows lightweight clients to verify transactions without downloading the entire blockchain. This guide demonstrates building SPV clients, managing block headers, creating merkle proofs, and implementing efficient payment verification systems.
Related SDK Components:
Basic SPV Client
import { Transaction, BlockHeader, MerkleProof, Hash } from '@bsv/sdk'
/**
* Basic SPV Client
*
* Lightweight client for verifying transactions using SPV
*/
class BasicSPVClient {
private headers: Map<string, StoredHeader> = new Map()
private verifiedTxs: Set<string> = new Set()
private chainTip: string | null = null
/**
* Add block header to the client
*/
addHeader(header: BlockHeader): void {
try {
const blockHash = header.hash()
console.log('Adding block header:', blockHash)
// Store header
this.headers.set(blockHash, {
header,
height: this.calculateHeight(header),
addedAt: Date.now()
})
// Update chain tip
if (!this.chainTip || this.isNewTip(header)) {
this.chainTip = blockHash
console.log('New chain tip:', blockHash)
}
console.log('Header added successfully')
} catch (error) {
throw new Error(`Failed to add header: ${error.message}`)
}
}
/**
* Get block header by hash
*/
getHeader(blockHash: string): BlockHeader | null {
const stored = this.headers.get(blockHash)
return stored ? stored.header : null
}
/**
* Verify transaction with merkle proof
*/
async verifyTransaction(
tx: Transaction,
blockHash: string,
merkleProof: MerkleProofData
): Promise<VerificationResult> {
try {
const txid = tx.id('hex')
console.log('Verifying transaction:', txid)
console.log('Block:', blockHash)
// Get header
const stored = this.headers.get(blockHash)
if (!stored) {
return {
verified: false,
error: 'Block header not found'
}
}
// Verify merkle proof
const proofValid = this.verifyMerkleProof(
txid,
merkleProof,
stored.header.merkleRoot
)
if (!proofValid) {
return {
verified: false,
error: 'Invalid merkle proof'
}
}
// Mark as verified
this.verifiedTxs.add(txid)
console.log('Transaction verified successfully')
return {
verified: true,
blockHeight: stored.height,
confirmations: this.calculateConfirmations(blockHash)
}
} catch (error) {
return {
verified: false,
error: error.message
}
}
}
/**
* Verify merkle proof
*/
private verifyMerkleProof(
txid: string,
proof: MerkleProofData,
merkleRoot: string
): boolean {
try {
let hash = Hash.sha256(Hash.sha256(Buffer.from(txid, 'hex')))
let index = proof.index
// Climb the merkle tree
for (const sibling of proof.nodes) {
const siblingBuffer = Buffer.from(sibling, 'hex')
const isRightNode = index % 2 === 1
const combined = isRightNode
? Buffer.concat([siblingBuffer, hash])
: Buffer.concat([hash, siblingBuffer])
hash = Hash.sha256(Hash.sha256(combined))
index = Math.floor(index / 2)
}
return hash.toString('hex') === merkleRoot
} catch (error) {
console.error('Merkle proof verification failed:', error.message)
return false
}
}
/**
* Calculate block height
*/
private calculateHeight(header: BlockHeader): number {
// Find previous header and increment height
const prevHeader = this.headers.get(header.prevBlockHash)
return prevHeader ? prevHeader.height + 1 : 0
}
/**
* Check if header is new chain tip
*/
private isNewTip(header: BlockHeader): boolean {
if (!this.chainTip) return true
const currentTip = this.headers.get(this.chainTip)
if (!currentTip) return true
const newHeight = this.calculateHeight(header)
return newHeight > currentTip.height
}
/**
* Calculate confirmations for a block
*/
private calculateConfirmations(blockHash: string): number {
if (!this.chainTip) return 0
const block = this.headers.get(blockHash)
const tip = this.headers.get(this.chainTip)
if (!block || !tip) return 0
return Math.max(0, tip.height - block.height + 1)
}
/**
* Check if transaction has been verified
*/
isVerified(txid: string): boolean {
return this.verifiedTxs.has(txid)
}
/**
* Get client statistics
*/
getStats(): SPVStats {
return {
headersCount: this.headers.size,
verifiedTxsCount: this.verifiedTxs.size,
chainTip: this.chainTip,
tipHeight: this.chainTip
? this.headers.get(this.chainTip)?.height || 0
: 0
}
}
/**
* Sync headers from a source
*/
async syncHeaders(
startHeight: number,
count: number,
headerSource: (height: number) => Promise<BlockHeader>
): Promise<number> {
console.log(`Syncing ${count} headers from height ${startHeight}`)
let synced = 0
for (let i = 0; i < count; i++) {
const height = startHeight + i
try {
const header = await headerSource(height)
this.addHeader(header)
synced++
if ((i + 1) % 100 === 0) {
console.log(`Synced ${i + 1}/${count} headers`)
}
} catch (error) {
console.error(`Failed to sync header at height ${height}:`, error.message)
break
}
}
console.log(`Sync complete: ${synced} headers synced`)
return synced
}
}
interface StoredHeader {
header: BlockHeader
height: number
addedAt: number
}
interface MerkleProofData {
index: number
nodes: string[]
}
interface VerificationResult {
verified: boolean
blockHeight?: number
confirmations?: number
error?: string
}
interface SPVStats {
headersCount: number
verifiedTxsCount: number
chainTip: string | null
tipHeight: number
}
/**
* Usage Example
*/
async function basicSPVExample() {
const client = new BasicSPVClient()
console.log('=== Basic SPV Client ===')
// Add block headers
const header = new BlockHeader({
version: 1,
prevBlockHash: '0000000000000000000000000000000000000000000000000000000000000000',
merkleRoot: 'merkle-root-hash...',
time: Date.now(),
bits: 0x1d00ffff,
nonce: 12345
})
client.addHeader(header)
// Verify transaction
const tx = Transaction.fromHex('tx-hex...')
const blockHash = header.hash()
const merkleProof: MerkleProofData = {
index: 0,
nodes: ['node1...', 'node2...']
}
const result = await client.verifyTransaction(tx, blockHash, merkleProof)
console.log('Verification result:', result)
// Get statistics
const stats = client.getStats()
console.log('SPV Client stats:', stats)
}Advanced SPV Client with Header Chain Management
SPV Payment Verification System
Related Examples
See Also
SDK Components:
Merkle Proof - Merkle proof operations
Transaction - Transaction handling
Learning Paths:
Last updated
