@fiber-pay/sdk 0.1.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/funds/liquidity-analyzer.ts","../src/proxy/cors-proxy.ts","../src/security/key-manager.ts","../src/security/policy-engine.ts","../src/verification/invoice-verifier.ts","../src/verification/payment-proof.ts"],"sourcesContent":["/**\n * Fund Management & Liquidity Analyzer\n * Analyzes channel health, identifies liquidity gaps, and provides funding recommendations\n */\n\nimport type { FiberRpcClient } from '../rpc/client.js';\nimport type { Channel } from '../types/index.js';\nimport { ChannelState } from '../types/index.js';\nimport { shannonsToCkb } from '../utils.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Channel health score breakdown\n */\nexport interface ChannelHealthMetrics {\n channelId: string;\n peerId: string;\n localBalanceCkb: number;\n remoteBalanceCkb: number;\n totalCapacityCkb: number;\n utilizationPercent: number; // How much of capacity is used\n balanceRatioPercent: number; // % of capacity on local side (50% = perfectly balanced)\n isBalanced: boolean; // Within 40-60% = balanced\n pendingLocalCkb: number; // Pending offered payments\n pendingRemoteCkb: number; // Pending received payments\n availableToSendCkb: number; // Can actually send right now\n availableToReceiveCkb: number; // Can actually receive right now\n healthScore: number; // 0-100\n state: ChannelState;\n}\n\nexport interface LiquidityGap {\n amount: number; // How much liquidity is missing\n reason: string; // Why it's needed\n severity: 'low' | 'medium' | 'high'; // Impact on operations\n affectedChannels: string[];\n}\n\nexport interface RebalanceRecommendation {\n from: string; // Source channel ID\n to: string; // Destination channel ID\n amountCkb: number;\n reason: string;\n benefit: string; // What this achieves\n estimatedRoutingFeeCkb: number;\n priority: number; // 1 (low) to 10 (critical)\n}\n\nexport interface FundingNeed {\n amount: number; // How much funding needed\n reason: string;\n optimalChannelPeerId?: string; // Recommended peer ID to fund\n urgency: 'low' | 'normal' | 'high';\n estimatedTimeToDepletion?: number; // Days until liquidity runs out\n}\n\nexport interface LiquidityReport {\n timestamp: number;\n balance: {\n totalCkb: number;\n availableToSendCkb: number;\n availableToReceiveCkb: number;\n lockedInChannelsCkb: number;\n };\n channels: {\n count: number;\n health: ChannelHealthMetrics[];\n averageHealthScore: number;\n balancedCount: number;\n imbalancedCount: number;\n };\n liquidity: {\n gaps: LiquidityGap[];\n hasCriticalGaps: boolean;\n runway: {\n daysAtCurrentRate?: number;\n estimatedDailySpendCkb?: number;\n };\n };\n recommendations: {\n rebalances: RebalanceRecommendation[];\n funding: FundingNeed[];\n };\n summary: string; // Human-readable summary\n}\n\n// =============================================================================\n// Liquidity Analyzer\n// =============================================================================\n\nexport class LiquidityAnalyzer {\n constructor(private rpc: FiberRpcClient) {}\n\n /**\n * Comprehensive liquidity analysis\n */\n async analyzeLiquidity(): Promise<LiquidityReport> {\n const timestamp = Date.now();\n\n // Fetch data\n const channels = await this.rpc.listChannels({});\n\n // Analyze each channel\n const channelMetrics: ChannelHealthMetrics[] = channels.channels.map((ch) =>\n this.analyzeChannelHealth(ch),\n );\n\n // Calculate totals\n const totalCkb = channelMetrics.reduce(\n (sum, ch) => sum + ch.localBalanceCkb + ch.remoteBalanceCkb,\n 0,\n );\n const availableToSendCkb = channelMetrics.reduce((sum, ch) => sum + ch.availableToSendCkb, 0);\n const availableToReceiveCkb = channelMetrics.reduce(\n (sum, ch) => sum + ch.availableToReceiveCkb,\n 0,\n );\n\n // Identify gaps and issues\n const gaps = this.identifyLiquidityGaps(channelMetrics, availableToSendCkb);\n\n // Generate rebalance recommendations\n const rebalances = this.generateRebalanceRecommendations(channelMetrics);\n\n // Estimate funding needs\n const fundingNeeds = this.estimateFundingNeeds(channelMetrics, gaps);\n\n // Calculate runway\n const runway = this.estimateRunway(availableToSendCkb, channelMetrics);\n\n // Summary\n const summary = this.generateSummary(channelMetrics, gaps, fundingNeeds, runway);\n\n return {\n timestamp,\n balance: {\n totalCkb,\n availableToSendCkb,\n availableToReceiveCkb,\n lockedInChannelsCkb: totalCkb - availableToSendCkb - availableToReceiveCkb,\n },\n channels: {\n count: channels.channels.length,\n health: channelMetrics,\n averageHealthScore:\n channelMetrics.length > 0\n ? channelMetrics.reduce((sum, ch) => sum + ch.healthScore, 0) / channelMetrics.length\n : 0,\n balancedCount: channelMetrics.filter((ch) => ch.isBalanced).length,\n imbalancedCount: channelMetrics.filter((ch) => !ch.isBalanced).length,\n },\n liquidity: {\n gaps,\n hasCriticalGaps: gaps.some((g) => g.severity === 'high'),\n runway,\n },\n recommendations: {\n rebalances,\n funding: fundingNeeds,\n },\n summary,\n };\n }\n\n /**\n * Analyze individual channel health\n */\n private analyzeChannelHealth(channel: Channel): ChannelHealthMetrics {\n const localBalance = shannonsToCkb(channel.local_balance);\n const remoteBalance = shannonsToCkb(channel.remote_balance);\n const totalCapacity = localBalance + remoteBalance;\n\n const pendingLocal = shannonsToCkb(channel.offered_tlc_balance);\n const pendingRemote = shannonsToCkb(channel.received_tlc_balance);\n\n const availableToSend = Math.max(0, localBalance - pendingLocal);\n const availableToReceive = Math.max(0, remoteBalance - pendingRemote);\n\n const utilizationPercent =\n totalCapacity > 0 ? ((pendingLocal + pendingRemote) / totalCapacity) * 100 : 0;\n const balanceRatioPercent = totalCapacity > 0 ? (localBalance / totalCapacity) * 100 : 50;\n\n // Channel is balanced if local balance is 40-60% of total\n const isBalanced = balanceRatioPercent >= 40 && balanceRatioPercent <= 60;\n\n // Health score (0-100)\n let healthScore = 100;\n\n // Deduct for high utilization (pending payments)\n if (utilizationPercent > 80) healthScore -= 20;\n else if (utilizationPercent > 50) healthScore -= 10;\n\n // Deduct for imbalance\n const imbalance = Math.abs(50 - balanceRatioPercent);\n healthScore -= (imbalance / 50) * 20; // Up to 20 points for extreme imbalance\n\n // Bonus for healthy balance\n if (isBalanced && utilizationPercent < 50) healthScore += 10;\n\n healthScore = Math.max(0, Math.min(100, healthScore));\n\n return {\n channelId: channel.channel_id,\n peerId: channel.peer_id,\n localBalanceCkb: localBalance,\n remoteBalanceCkb: remoteBalance,\n totalCapacityCkb: totalCapacity,\n utilizationPercent,\n balanceRatioPercent,\n isBalanced,\n pendingLocalCkb: pendingLocal,\n pendingRemoteCkb: pendingRemote,\n availableToSendCkb: availableToSend,\n availableToReceiveCkb: availableToReceive,\n healthScore,\n state: channel.state.state_name,\n };\n }\n\n /**\n * Identify liquidity shortfalls and gaps\n */\n private identifyLiquidityGaps(\n metrics: ChannelHealthMetrics[],\n _totalSendable: number,\n ): LiquidityGap[] {\n const gaps: LiquidityGap[] = [];\n\n // Gap 1: No send-capable channels\n const sendCapableCount = metrics.filter((ch) => ch.availableToSendCkb > 0).length;\n if (sendCapableCount === 0 && metrics.length > 0) {\n gaps.push({\n amount: 100, // Arbitrary amount - need to rebalance\n reason: 'No channels currently capable of sending. All liquidity on remote side.',\n severity: 'high',\n affectedChannels: metrics.map((ch) => ch.channelId),\n });\n }\n\n // Gap 2: All channels imbalanced (all local-heavy or all remote-heavy)\n const allLocalHeavy = metrics.every((ch) => ch.balanceRatioPercent > 70);\n const allRemoteHeavy = metrics.every((ch) => ch.balanceRatioPercent < 30);\n\n if (allLocalHeavy) {\n gaps.push({\n amount: 0, // Rebalance doesn't require funding\n reason: 'All channels are local-heavy. Cannot receive payments. Need inbound liquidity.',\n severity: 'high',\n affectedChannels: metrics.map((ch) => ch.channelId),\n });\n }\n\n if (allRemoteHeavy) {\n gaps.push({\n amount: 0, // Need to open new channels or rebalance\n reason: 'All channels are remote-heavy. Cannot send without rebalancing or new channels.',\n severity: 'high',\n affectedChannels: metrics.map((ch) => ch.channelId),\n });\n }\n\n // Gap 3: High utilization in all channels\n const allHighUtilization = metrics.every((ch) => ch.utilizationPercent > 70);\n if (allHighUtilization) {\n gaps.push({\n amount: 0,\n reason:\n 'High utilization in all channels. Many pending payments. Risk of payment failures.',\n severity: 'medium',\n affectedChannels: metrics.map((ch) => ch.channelId),\n });\n }\n\n return gaps;\n }\n\n /**\n * Generate rebalance recommendations between channels\n */\n private generateRebalanceRecommendations(\n metrics: ChannelHealthMetrics[],\n ): RebalanceRecommendation[] {\n const recommendations: RebalanceRecommendation[] = [];\n\n // Look for pairs: one local-heavy, one remote-heavy\n const localHeavy = metrics.filter((ch) => ch.balanceRatioPercent > 65);\n const remoteHeavy = metrics.filter((ch) => ch.balanceRatioPercent < 35);\n\n // Sort by how imbalanced they are\n localHeavy.sort((a, b) => b.balanceRatioPercent - a.balanceRatioPercent);\n remoteHeavy.sort((a, b) => a.balanceRatioPercent - b.balanceRatioPercent);\n\n // Pair them up\n for (let i = 0; i < Math.min(localHeavy.length, remoteHeavy.length); i++) {\n const source = localHeavy[i];\n const dest = remoteHeavy[i];\n\n // Calculate how much to move to balance both closer to 50%\n const sourceExcess = source.localBalanceCkb - source.totalCapacityCkb * 0.5;\n const destDeficit = dest.totalCapacityCkb * 0.5 - dest.localBalanceCkb;\n\n const amountToMove = Math.min(sourceExcess, destDeficit) * 0.8; // Move 80% to be conservative\n\n if (amountToMove > 0.1) {\n // Only if meaningful amount\n recommendations.push({\n from: source.channelId,\n to: dest.channelId,\n amountCkb: amountToMove,\n reason: `Rebalance liquidity: source is ${source.balanceRatioPercent.toFixed(0)}% local, destination is ${dest.balanceRatioPercent.toFixed(0)}% local`,\n benefit: `Improves payment success rate by balancing channel liquidity`,\n estimatedRoutingFeeCkb: amountToMove * 0.001, // 0.1% estimated fee\n priority: Math.round(\n (Math.abs(50 - source.balanceRatioPercent) + Math.abs(50 - dest.balanceRatioPercent)) /\n 20,\n ), // 1-10\n });\n }\n }\n\n return recommendations.sort((a, b) => b.priority - a.priority);\n }\n\n /**\n * Estimate funding needs for future operations\n */\n private estimateFundingNeeds(\n metrics: ChannelHealthMetrics[],\n gaps: LiquidityGap[],\n ): FundingNeed[] {\n const needs: FundingNeed[] = [];\n\n // If there are critical gaps, funding is needed\n const criticalGaps = gaps.filter((g) => g.severity === 'high');\n if (criticalGaps.length > 0) {\n // Find the best channel to fund (most balanced, highest capacity)\n const bestChannel = [...metrics]\n .filter((ch) => ch.state === ChannelState.ChannelReady)\n .sort((a, b) => {\n const scoreA = a.healthScore + a.totalCapacityCkb / 1000; // Higher score + capacity = better\n const scoreB = b.healthScore + b.totalCapacityCkb / 1000;\n return scoreB - scoreA;\n })[0];\n\n const fundingAmount = Math.ceil(bestChannel?.totalCapacityCkb || 100);\n\n needs.push({\n amount: fundingAmount,\n reason: 'Critical liquidity gaps detected in current channels',\n optimalChannelPeerId: bestChannel?.peerId,\n urgency: 'high',\n });\n }\n\n // If all channels are small, recommend growth\n const avgCapacity =\n metrics.length > 0\n ? metrics.reduce((sum, ch) => sum + ch.totalCapacityCkb, 0) / metrics.length\n : 0;\n\n if (avgCapacity < 100) {\n needs.push({\n amount: 500, // Reasonable growth target\n reason: 'Average channel capacity is small. Consider larger channels for reliability.',\n urgency: 'low',\n });\n }\n\n return needs;\n }\n\n /**\n * Estimate runway (days until liquidity depleted at current spending rate)\n */\n private estimateRunway(\n availableToSend: number,\n _metrics: ChannelHealthMetrics[],\n ): {\n daysAtCurrentRate?: number;\n estimatedDailySpendCkb?: number;\n } {\n // This is a simplified estimate\n // In real implementation, would track historical spending\n const estimatedDailySpend = 0.1; // Default: assume 0.1 CKB per day\n\n if (availableToSend > 0 && estimatedDailySpend > 0) {\n const days = availableToSend / estimatedDailySpend;\n return {\n daysAtCurrentRate: Math.floor(days),\n estimatedDailySpendCkb: estimatedDailySpend,\n };\n }\n\n return {};\n }\n\n /**\n * Generate human-readable summary\n */\n private generateSummary(\n metrics: ChannelHealthMetrics[],\n gaps: LiquidityGap[],\n fundingNeeds: FundingNeed[],\n runway: {\n daysAtCurrentRate?: number;\n estimatedDailySpendCkb?: number;\n },\n ): string {\n const parts: string[] = [];\n\n if (metrics.length === 0) {\n return 'No channels available. Open a channel to start making payments.';\n }\n\n parts.push(\n `Channel Health: ${metrics.filter((ch) => ch.healthScore >= 70).length}/${metrics.length} channels healthy`,\n );\n\n const avgScore = metrics.reduce((sum, ch) => sum + ch.healthScore, 0) / metrics.length;\n if (avgScore >= 80) {\n parts.push('Overall liquidity is good ✓');\n } else if (avgScore >= 60) {\n parts.push('Overall liquidity is fair. Some rebalancing recommended.');\n } else {\n parts.push('Overall liquidity is poor. Rebalancing needed urgently.');\n }\n\n if (gaps.length > 0) {\n parts.push(`${gaps.length} liquidity gap(s) detected.`);\n }\n\n if (fundingNeeds.length > 0) {\n parts.push(\n `Need to fund ${fundingNeeds.map((n) => n.amount).join(', ')} CKB to resolve issues.`,\n );\n }\n\n if (runway.daysAtCurrentRate) {\n parts.push(`Estimated runway: ${runway.daysAtCurrentRate} days at current spending rate.`);\n }\n\n return parts.join(' ');\n }\n\n /**\n * Get missing liquidity for a specific amount\n */\n async getMissingLiquidityForAmount(targetCkb: number): Promise<{\n canSend: boolean;\n shortfallCkb: number;\n recommendation: string;\n }> {\n const report = await this.analyzeLiquidity();\n\n const shortfallCkb = Math.max(0, targetCkb - report.balance.availableToSendCkb);\n const canSend = shortfallCkb === 0;\n\n let recommendation = '';\n if (canSend) {\n recommendation = `You have enough liquidity to send ${targetCkb} CKB.`;\n } else {\n recommendation = `You need ${shortfallCkb.toFixed(4)} more CKB in send capacity. Consider rebalancing or opening larger channels.`;\n }\n\n return { canSend, shortfallCkb, recommendation };\n }\n}\n","/**\n * CORS Proxy Server\n * Simple HTTP proxy that adds CORS headers to Fiber RPC requests\n */\n\nimport http from 'node:http';\n\nexport interface CorsProxyConfig {\n /** Port to listen on */\n port: number;\n /** Target RPC URL to proxy to */\n targetUrl: string;\n /** Allowed origins (default: '*') */\n allowedOrigins?: string | string[];\n}\n\nexport class CorsProxy {\n private server: http.Server | null = null;\n private config: CorsProxyConfig;\n\n constructor(config: CorsProxyConfig) {\n this.config = config;\n }\n\n /**\n * Start the CORS proxy server\n */\n start(): Promise<void> {\n return new Promise((resolve, reject) => {\n const targetUrl = new URL(this.config.targetUrl);\n\n this.server = http.createServer(async (req, res) => {\n const origin = req.headers.origin || '*';\n const allowedOrigin = this.getAllowedOrigin(origin);\n\n // Set CORS headers\n res.setHeader('Access-Control-Allow-Origin', allowedOrigin);\n res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');\n res.setHeader('Access-Control-Max-Age', '86400');\n\n // Handle preflight requests\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n // Only allow POST requests\n if (req.method !== 'POST') {\n res.writeHead(405, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Method not allowed' }));\n return;\n }\n\n // Collect request body\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => {\n const body = Buffer.concat(chunks);\n\n // Forward request to Fiber RPC\n const proxyReq = http.request(\n {\n hostname: targetUrl.hostname,\n port: targetUrl.port || 80,\n path: targetUrl.pathname,\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': body.length,\n ...(req.headers.authorization && { Authorization: req.headers.authorization }),\n },\n },\n (proxyRes) => {\n // Copy status and headers from proxy response\n const headers: Record<string, string | string[] | undefined> = {\n ...proxyRes.headers,\n 'Access-Control-Allow-Origin': allowedOrigin,\n };\n res.writeHead(proxyRes.statusCode || 500, headers);\n proxyRes.pipe(res);\n },\n );\n\n proxyReq.on('error', (err) => {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: `Proxy error: ${err.message}` }));\n });\n\n proxyReq.write(body);\n proxyReq.end();\n });\n\n req.on('error', (err) => {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: `Request error: ${err.message}` }));\n });\n });\n\n this.server.on('error', (err) => {\n reject(err);\n });\n\n this.server.listen(this.config.port, () => {\n resolve();\n });\n });\n }\n\n /**\n * Stop the CORS proxy server\n */\n stop(): Promise<void> {\n return new Promise((resolve) => {\n if (this.server) {\n this.server.close(() => {\n this.server = null;\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Get the allowed origin based on config\n */\n private getAllowedOrigin(requestOrigin: string): string {\n const allowed = this.config.allowedOrigins;\n\n if (!allowed || allowed === '*') {\n return '*';\n }\n\n if (Array.isArray(allowed)) {\n return allowed.includes(requestOrigin) ? requestOrigin : allowed[0];\n }\n\n return allowed;\n }\n\n /**\n * Get the proxy URL\n */\n getUrl(): string {\n return `http://127.0.0.1:${this.config.port}`;\n }\n}\n","/**\n * Key Manager\n * Handles generation, storage, and encryption of Fiber node keys\n * Keys are isolated from LLM context for security\n */\n\nimport { createDecipheriv, createHash, randomBytes, scryptSync } from 'node:crypto';\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { HexString, KeyConfig, KeyInfo } from '../types/index.js';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst SCRYPT_N = 2 ** 14;\nconst SCRYPT_R = 8;\nconst SCRYPT_P = 1;\nconst KEY_LENGTH = 32;\nconst SALT_LENGTH = 32;\nconst IV_LENGTH = 16;\nconst AUTH_TAG_LENGTH = 16;\nconst ENCRYPTED_MAGIC = Buffer.from('FIBERENC');\n\n// =============================================================================\n// Key Manager\n// =============================================================================\n\nexport class KeyManager {\n private config: KeyConfig;\n private fiberKeyPath: string;\n private ckbKeyPath: string;\n\n constructor(config: KeyConfig) {\n this.config = config;\n this.fiberKeyPath = join(config.baseDir, 'fiber', 'sk');\n this.ckbKeyPath = join(config.baseDir, 'ckb', 'key');\n }\n\n /**\n * Initialize keys - generate if they don't exist and autoGenerate is true\n */\n async initialize(): Promise<{ fiber: KeyInfo; ckb: KeyInfo }> {\n const fiberExists = existsSync(this.fiberKeyPath);\n const ckbExists = existsSync(this.ckbKeyPath);\n\n if (!fiberExists || !ckbExists) {\n if (!this.config.autoGenerate) {\n throw new Error(\n `Keys not found and autoGenerate is disabled. ` +\n `Missing: ${[!fiberExists && 'fiber', !ckbExists && 'ckb'].filter(Boolean).join(', ')}`,\n );\n }\n }\n\n // Generate missing keys\n if (!fiberExists) {\n await this.generateKey('fiber');\n }\n if (!ckbExists) {\n await this.generateKey('ckb');\n }\n\n return {\n fiber: await this.getKeyInfo('fiber'),\n ckb: await this.getKeyInfo('ckb'),\n };\n }\n\n /**\n * Generate a new key\n */\n async generateKey(type: 'fiber' | 'ckb'): Promise<KeyInfo> {\n const keyPath = type === 'fiber' ? this.fiberKeyPath : this.ckbKeyPath;\n const keyDir = dirname(keyPath);\n\n // Create directory if it doesn't exist\n if (!existsSync(keyDir)) {\n mkdirSync(keyDir, { recursive: true });\n }\n\n // Generate 32 random bytes\n const privateKey = randomBytes(32);\n\n // The fiber node expects different formats:\n // - fiber/sk: raw 32 bytes\n // - ckb/key: hex string (64 characters)\n let keyData: string | Buffer;\n if (type === 'fiber') {\n keyData = privateKey;\n } else {\n keyData = privateKey.toString('hex');\n }\n\n // Write key file with restricted permissions\n writeFileSync(keyPath, keyData);\n chmodSync(keyPath, 0o600);\n\n return this.getKeyInfo(type);\n }\n\n /**\n * Get information about a key (without exposing the private key)\n */\n async getKeyInfo(type: 'fiber' | 'ckb'): Promise<KeyInfo> {\n const keyPath = type === 'fiber' ? this.fiberKeyPath : this.ckbKeyPath;\n\n if (!existsSync(keyPath)) {\n throw new Error(`Key not found: ${keyPath}`);\n }\n\n const keyData = readFileSync(keyPath);\n const encrypted = this.isEncrypted(keyData);\n\n // Get private key to derive public key\n const privateKey = await this.loadPrivateKey(type);\n const publicKey = this.derivePublicKey(privateKey);\n\n return {\n publicKey,\n encrypted,\n path: keyPath,\n createdAt: Date.now(), // TODO: get actual file creation time\n };\n }\n\n /**\n * Load and decrypt a private key (for internal use only)\n * This should NEVER be exposed to the LLM context\n */\n private async loadPrivateKey(type: 'fiber' | 'ckb'): Promise<Buffer> {\n const keyPath = type === 'fiber' ? this.fiberKeyPath : this.ckbKeyPath;\n const keyData = readFileSync(keyPath);\n\n if (this.isEncrypted(keyData)) {\n if (!this.config.encryptionPassword) {\n throw new Error('Key is encrypted but no password provided');\n }\n return this.decryptKey(keyData, this.config.encryptionPassword);\n }\n\n // The fiber node stores keys in different formats:\n // - fiber/sk: raw 32 bytes\n // - ckb/key: hex string (64 characters)\n if (type === 'fiber') {\n return keyData;\n } else {\n const hexString = keyData.toString('utf-8').trim();\n return Buffer.from(hexString, 'hex');\n }\n }\n\n /**\n * Export keys for use with the Fiber node process\n * Returns the password to use for FIBER_SECRET_KEY_PASSWORD env var\n */\n getNodeKeyConfig(): { password?: string } {\n return {\n password: this.config.encryptionPassword,\n };\n }\n\n /**\n * Check if key data is encrypted\n */\n private isEncrypted(data: Buffer): boolean {\n return (\n data.length >= ENCRYPTED_MAGIC.length &&\n data.subarray(0, ENCRYPTED_MAGIC.length).equals(ENCRYPTED_MAGIC)\n );\n }\n\n /**\n * Decrypt a key\n */\n private decryptKey(data: Buffer, password: string): Buffer {\n // Parse encrypted format\n let offset = ENCRYPTED_MAGIC.length;\n const salt = data.subarray(offset, offset + SALT_LENGTH);\n offset += SALT_LENGTH;\n const iv = data.subarray(offset, offset + IV_LENGTH);\n offset += IV_LENGTH;\n const authTag = data.subarray(offset, offset + AUTH_TAG_LENGTH);\n offset += AUTH_TAG_LENGTH;\n const encrypted = data.subarray(offset);\n\n // Derive key using scrypt\n const derivedKey = scryptSync(password, salt, KEY_LENGTH, {\n N: SCRYPT_N,\n r: SCRYPT_R,\n p: SCRYPT_P,\n });\n\n // Decrypt using AES-256-GCM\n const decipher = createDecipheriv('aes-256-gcm', derivedKey, iv);\n decipher.setAuthTag(authTag);\n\n return Buffer.concat([decipher.update(encrypted), decipher.final()]);\n }\n\n /**\n * Derive public key from private key (secp256k1)\n * This is a simplified version - in production, use a proper secp256k1 library\n */\n private derivePublicKey(privateKey: Buffer): HexString {\n // For now, return a placeholder - actual implementation would use\n // @noble/secp256k1 or similar library\n // The actual public key derivation requires elliptic curve math\n const hash = createHash('sha256').update(privateKey).digest();\n return `0x${hash.toString('hex')}` as HexString;\n }\n}\n\n/**\n * Create a key manager with environment-based configuration\n */\nexport function createKeyManager(baseDir: string, options?: Partial<KeyConfig>): KeyManager {\n const password = process.env.FIBER_KEY_PASSWORD || options?.encryptionPassword;\n\n return new KeyManager({\n baseDir,\n encryptionPassword: password,\n autoGenerate: options?.autoGenerate ?? true,\n });\n}\n","/**\n * Policy Engine\n * Enforces spending limits, rate limits, and other security policies\n * This operates at the SDK level and cannot be bypassed via prompts\n */\n\nimport type {\n AuditAction,\n AuditLogEntry,\n PolicyCheckResult,\n PolicyViolation,\n RateLimit,\n SecurityPolicy,\n SpendingLimit,\n} from '../types/index.js';\nimport { fromHex, toHex } from '../utils.js';\n\n// =============================================================================\n// Policy Engine\n// =============================================================================\n\nexport class PolicyEngine {\n private policy: SecurityPolicy;\n private auditLog: AuditLogEntry[] = [];\n private spendingState: SpendingLimit;\n private rateLimitState: RateLimit;\n\n constructor(policy: SecurityPolicy) {\n this.policy = policy;\n\n // Initialize spending state\n this.spendingState = {\n ...(policy.spending ?? {\n maxPerTransaction: '0x0',\n maxPerWindow: '0x0',\n windowSeconds: 3600,\n }),\n currentSpent: '0x0',\n windowStart: Date.now(),\n };\n\n // Initialize rate limit state\n this.rateLimitState = {\n ...(policy.rateLimit ?? {\n maxTransactions: 0,\n windowSeconds: 3600,\n cooldownSeconds: 0,\n }),\n currentCount: 0,\n windowStart: Date.now(),\n lastTransaction: 0,\n };\n }\n\n /**\n * Check if a payment is allowed by the policy\n */\n checkPayment(params: {\n amount: string; // hex\n recipient?: string;\n }): PolicyCheckResult {\n const violations: PolicyViolation[] = [];\n let requiresConfirmation = false;\n\n if (!this.policy.enabled) {\n return { allowed: true, violations: [], requiresConfirmation: false };\n }\n\n const amount = fromHex(params.amount as `0x${string}`);\n\n // Check spending limit per transaction\n if (this.policy.spending) {\n const maxPerTx = fromHex(this.policy.spending.maxPerTransaction as `0x${string}`);\n if (amount > maxPerTx) {\n violations.push({\n type: 'SPENDING_LIMIT_PER_TX',\n message: `Amount ${amount} exceeds per-transaction limit of ${maxPerTx}`,\n details: {\n requested: params.amount,\n limit: this.policy.spending.maxPerTransaction,\n },\n });\n }\n\n // Check spending limit per window\n this.refreshSpendingWindow();\n const currentSpent = fromHex(this.spendingState.currentSpent as `0x${string}`);\n const maxPerWindow = fromHex(this.policy.spending.maxPerWindow as `0x${string}`);\n\n if (currentSpent + amount > maxPerWindow) {\n const remaining = maxPerWindow - currentSpent;\n violations.push({\n type: 'SPENDING_LIMIT_PER_WINDOW',\n message: `Amount ${amount} would exceed window limit. Remaining: ${remaining}`,\n details: {\n requested: params.amount,\n limit: this.policy.spending.maxPerWindow,\n remaining: toHex(remaining > 0n ? remaining : 0n),\n },\n });\n }\n }\n\n // Check rate limit\n if (this.policy.rateLimit) {\n this.refreshRateLimitWindow();\n\n if ((this.rateLimitState.currentCount ?? 0) >= this.policy.rateLimit.maxTransactions) {\n violations.push({\n type: 'RATE_LIMIT_EXCEEDED',\n message: `Rate limit of ${this.policy.rateLimit.maxTransactions} transactions per ${this.policy.rateLimit.windowSeconds}s exceeded`,\n details: {},\n });\n }\n\n // Check cooldown\n const now = Date.now();\n const cooldownMs = this.policy.rateLimit.cooldownSeconds * 1000;\n const timeSinceLast = now - (this.rateLimitState.lastTransaction || 0);\n\n if (timeSinceLast < cooldownMs) {\n violations.push({\n type: 'RATE_LIMIT_COOLDOWN',\n message: `Cooldown period not elapsed. Wait ${Math.ceil((cooldownMs - timeSinceLast) / 1000)}s`,\n details: {\n cooldownRemaining: Math.ceil((cooldownMs - timeSinceLast) / 1000),\n },\n });\n }\n }\n\n // Check recipient policy\n if (this.policy.recipients && params.recipient) {\n if (this.policy.recipients.blocklist?.includes(params.recipient)) {\n violations.push({\n type: 'RECIPIENT_BLOCKED',\n message: `Recipient ${params.recipient} is blocklisted`,\n details: { recipient: params.recipient },\n });\n }\n\n if (\n this.policy.recipients.allowlist &&\n this.policy.recipients.allowlist.length > 0 &&\n !this.policy.recipients.allowlist.includes(params.recipient) &&\n !this.policy.recipients.allowUnknown\n ) {\n violations.push({\n type: 'RECIPIENT_NOT_ALLOWED',\n message: `Recipient ${params.recipient} is not in allowlist`,\n details: { recipient: params.recipient },\n });\n }\n }\n\n // Check confirmation threshold\n if (this.policy.confirmationThreshold) {\n const threshold = fromHex(this.policy.confirmationThreshold as `0x${string}`);\n if (amount > threshold) {\n requiresConfirmation = true;\n violations.push({\n type: 'REQUIRES_CONFIRMATION',\n message: `Amount ${amount} exceeds confirmation threshold of ${threshold}`,\n details: {\n requested: params.amount,\n limit: this.policy.confirmationThreshold,\n },\n });\n }\n }\n\n return {\n allowed: violations.filter((v) => v.type !== 'REQUIRES_CONFIRMATION').length === 0,\n violations,\n requiresConfirmation,\n };\n }\n\n /**\n * Check if a channel operation is allowed\n */\n checkChannelOperation(params: {\n operation: 'open' | 'close' | 'force_close';\n fundingAmount?: string; // hex\n currentChannelCount?: number;\n }): PolicyCheckResult {\n const violations: PolicyViolation[] = [];\n\n if (!this.policy.enabled || !this.policy.channels) {\n return { allowed: true, violations: [], requiresConfirmation: false };\n }\n\n const { channels } = this.policy;\n\n if (params.operation === 'open') {\n if (!channels.allowOpen) {\n violations.push({\n type: 'CHANNEL_OPEN_NOT_ALLOWED',\n message: 'Channel opening is not allowed by policy',\n details: {},\n });\n }\n\n if (params.fundingAmount && channels.maxFundingAmount) {\n const funding = fromHex(params.fundingAmount as `0x${string}`);\n const max = fromHex(channels.maxFundingAmount as `0x${string}`);\n if (funding > max) {\n violations.push({\n type: 'CHANNEL_FUNDING_EXCEEDS_MAX',\n message: `Funding amount ${funding} exceeds maximum ${max}`,\n details: {\n requested: params.fundingAmount,\n limit: channels.maxFundingAmount,\n },\n });\n }\n }\n\n if (params.fundingAmount && channels.minFundingAmount) {\n const funding = fromHex(params.fundingAmount as `0x${string}`);\n const min = fromHex(channels.minFundingAmount as `0x${string}`);\n if (funding < min) {\n violations.push({\n type: 'CHANNEL_FUNDING_BELOW_MIN',\n message: `Funding amount ${funding} below minimum ${min}`,\n details: {\n requested: params.fundingAmount,\n limit: channels.minFundingAmount,\n },\n });\n }\n }\n\n if (\n channels.maxChannels &&\n params.currentChannelCount !== undefined &&\n params.currentChannelCount >= channels.maxChannels\n ) {\n violations.push({\n type: 'MAX_CHANNELS_REACHED',\n message: `Maximum channel count of ${channels.maxChannels} reached`,\n details: {},\n });\n }\n }\n\n if (params.operation === 'close' && !channels.allowClose) {\n violations.push({\n type: 'CHANNEL_CLOSE_NOT_ALLOWED',\n message: 'Channel closing is not allowed by policy',\n details: {},\n });\n }\n\n if (params.operation === 'force_close' && !channels.allowForceClose) {\n violations.push({\n type: 'CHANNEL_FORCE_CLOSE_NOT_ALLOWED',\n message: 'Force channel closing is not allowed by policy',\n details: {},\n });\n }\n\n return {\n allowed: violations.length === 0,\n violations,\n requiresConfirmation: false,\n };\n }\n\n /**\n * Record a successful payment (updates spending and rate limit state)\n */\n recordPayment(amount: string): void {\n this.refreshSpendingWindow();\n this.refreshRateLimitWindow();\n\n // Update spending\n const currentSpent = fromHex(this.spendingState.currentSpent as `0x${string}`);\n const paymentAmount = fromHex(amount as `0x${string}`);\n this.spendingState.currentSpent = toHex(currentSpent + paymentAmount);\n\n // Update rate limit\n this.rateLimitState.currentCount = (this.rateLimitState.currentCount ?? 0) + 1;\n this.rateLimitState.lastTransaction = Date.now();\n }\n\n /**\n * Add an entry to the audit log\n */\n addAuditEntry(\n action: AuditAction,\n success: boolean,\n details: Record<string, unknown>,\n violations?: PolicyViolation[],\n ): void {\n if (!this.policy.auditLogging) return;\n\n this.auditLog.push({\n timestamp: Date.now(),\n action,\n success,\n details,\n policyViolations: violations,\n });\n\n // Keep audit log bounded (last 1000 entries)\n if (this.auditLog.length > 1000) {\n this.auditLog = this.auditLog.slice(-1000);\n }\n }\n\n /**\n * Get the audit log\n */\n getAuditLog(options?: { limit?: number; since?: number }): AuditLogEntry[] {\n let log = this.auditLog;\n\n const since = options?.since;\n if (since !== undefined) {\n log = log.filter((entry) => entry.timestamp >= since);\n }\n\n if (options?.limit) {\n log = log.slice(-options.limit);\n }\n\n return log;\n }\n\n /**\n * Get remaining spending allowance\n */\n getRemainingAllowance(): { perTransaction: bigint; perWindow: bigint } {\n this.refreshSpendingWindow();\n\n const maxPerTx = this.policy.spending\n ? fromHex(this.policy.spending.maxPerTransaction as `0x${string}`)\n : BigInt(Number.MAX_SAFE_INTEGER);\n\n const maxPerWindow = this.policy.spending\n ? fromHex(this.policy.spending.maxPerWindow as `0x${string}`)\n : BigInt(Number.MAX_SAFE_INTEGER);\n\n const currentSpent = fromHex(this.spendingState.currentSpent as `0x${string}`);\n const remainingWindow = maxPerWindow - currentSpent;\n\n return {\n perTransaction: maxPerTx,\n perWindow: remainingWindow > 0n ? remainingWindow : 0n,\n };\n }\n\n /**\n * Update the policy\n */\n updatePolicy(newPolicy: Partial<SecurityPolicy>): void {\n this.policy = { ...this.policy, ...newPolicy };\n this.addAuditEntry('POLICY_UPDATED', true, { newPolicy });\n }\n\n /**\n * Get the current policy\n */\n getPolicy(): SecurityPolicy {\n return { ...this.policy };\n }\n\n // ===========================================================================\n // Private Methods\n // ===========================================================================\n\n private refreshSpendingWindow(): void {\n if (!this.policy.spending) return;\n\n const now = Date.now();\n const windowMs = this.policy.spending.windowSeconds * 1000;\n const windowStart = this.spendingState.windowStart || now;\n\n if (now - windowStart >= windowMs) {\n // Reset window\n this.spendingState.currentSpent = '0x0';\n this.spendingState.windowStart = now;\n }\n }\n\n private refreshRateLimitWindow(): void {\n if (!this.policy.rateLimit) return;\n\n const now = Date.now();\n const windowMs = this.policy.rateLimit.windowSeconds * 1000;\n const windowStart = this.rateLimitState.windowStart || now;\n\n if (now - windowStart >= windowMs) {\n // Reset window\n this.rateLimitState.currentCount = 0;\n this.rateLimitState.windowStart = now;\n }\n }\n}\n","/**\n * Invoice Verification Engine\n * Validates invoice legitimacy, format, cryptographic correctness, and peer connectivity\n * This ensures the agent only pays valid invoices\n */\n\nimport type { FiberRpcClient } from '../rpc/client.js';\nimport type { Attribute, CkbInvoice, HexString } from '../types/index.js';\nimport { fromHex, shannonsToCkb } from '../utils.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Invoice verification result\n */\nexport interface InvoiceVerificationResult {\n /** Overall validity (true = safe to pay) */\n valid: boolean;\n /** Invoice parsed details */\n details: {\n paymentHash: string;\n amountCkb: number;\n expiresAt: number; // Unix timestamp\n description?: string;\n isExpired: boolean;\n };\n /** Peer information */\n peer: {\n nodeId?: string;\n isConnected: boolean;\n trustScore: number; // 0-100\n };\n /** Validation checks performed */\n checks: {\n validFormat: boolean;\n notExpired: boolean;\n validAmount: boolean;\n peerConnected: boolean;\n };\n /** Issues found */\n issues: VerificationIssue[];\n /** Recommendation for agent */\n recommendation: 'proceed' | 'warn' | 'reject';\n /** Human-readable reason for recommendation */\n reason: string;\n}\n\nexport interface VerificationIssue {\n type: 'warning' | 'critical';\n code: string;\n message: string;\n}\n\n// =============================================================================\n// Invoice Verifier\n// =============================================================================\n\nexport class InvoiceVerifier {\n constructor(private rpc: FiberRpcClient) {}\n\n /**\n * Fully validate an invoice before payment\n */\n async verifyInvoice(invoiceString: string): Promise<InvoiceVerificationResult> {\n const issues: VerificationIssue[] = [];\n const checks = {\n validFormat: false,\n notExpired: false,\n validAmount: false,\n peerConnected: false,\n };\n\n // 1. Validate format\n const formatCheck = this.validateInvoiceFormat(invoiceString);\n checks.validFormat = formatCheck.valid;\n if (!formatCheck.valid) {\n issues.push({\n type: 'critical',\n code: 'INVALID_INVOICE_FORMAT',\n message: formatCheck.error || 'Invoice format is invalid',\n });\n }\n\n let invoice: CkbInvoice | null = null;\n\n // 2. Parse invoice\n if (checks.validFormat) {\n try {\n const result = await this.rpc.parseInvoice({ invoice: invoiceString });\n invoice = result.invoice;\n } catch (error) {\n issues.push({\n type: 'critical',\n code: 'PARSE_INVOICE_FAILED',\n message: `Failed to parse invoice: ${error instanceof Error ? error.message : 'Unknown error'}`,\n });\n }\n }\n\n // Default details if parsing failed\n const details = {\n paymentHash: invoice?.data.payment_hash || 'unknown',\n amountCkb: invoice?.amount ? shannonsToCkb(invoice.amount) : 0,\n expiresAt: this.getExpiryTimestamp(invoice),\n description: this.getDescription(invoice),\n isExpired: invoice ? this.isInvoiceExpired(invoice) : true,\n };\n\n // 3. Validate not expired\n if (invoice) {\n if (!details.isExpired) {\n checks.notExpired = true;\n } else {\n issues.push({\n type: 'critical',\n code: 'INVOICE_EXPIRED',\n message: `Invoice expired at ${new Date(details.expiresAt).toISOString()}`,\n });\n }\n }\n\n // 4. Validate amount\n if (invoice?.amount) {\n const validAmount = this.validateAmount(invoice.amount);\n checks.validAmount = validAmount.valid;\n if (!validAmount.valid) {\n issues.push({\n type: 'critical',\n code: validAmount.code || 'INVALID_AMOUNT',\n message: validAmount.message || 'Amount validation failed',\n });\n }\n }\n\n // 5. Check peer connectivity - verify payee is reachable\n const payeePublicKey = this.extractNodeIdFromInvoice(invoice);\n try {\n const peers = await this.rpc.listPeers();\n if (payeePublicKey) {\n // We have the payee's public key - check if they're connected or reachable\n // Note: peer_id in Fiber is derived from public key, but format may differ\n // For now, we check if we have any path to peers (routing will find the payee)\n if (peers.peers && peers.peers.length > 0) {\n checks.peerConnected = true;\n } else {\n issues.push({\n type: 'warning',\n code: 'NO_PEERS_CONNECTED',\n message: `No peers connected. Cannot route payment to payee ${payeePublicKey.slice(0, 16)}...`,\n });\n }\n } else {\n // No payee public key in invoice - just check basic connectivity\n if (peers.peers && peers.peers.length > 0) {\n checks.peerConnected = true;\n } else {\n issues.push({\n type: 'warning',\n code: 'NO_PEERS_CONNECTED',\n message: 'No peers currently connected. Payment may fail.',\n });\n }\n }\n } catch {\n issues.push({\n type: 'warning',\n code: 'PEER_CHECK_FAILED',\n message: 'Could not verify peer connectivity',\n });\n }\n\n // Determine overall validity and recommendation\n const criticalIssues = issues.filter((i) => i.type === 'critical');\n const valid = criticalIssues.length === 0;\n\n let recommendation: 'proceed' | 'warn' | 'reject';\n let reason: string;\n\n if (!valid) {\n recommendation = 'reject';\n reason = `Invoice has ${criticalIssues.length} critical issue(s): ${criticalIssues.map((i) => i.code).join(', ')}`;\n } else if (issues.length > 0) {\n recommendation = 'warn';\n reason = `Invoice is valid but has warnings: ${issues.map((i) => i.code).join(', ')}`;\n } else {\n recommendation = 'proceed';\n reason = 'Invoice is valid and safe to pay';\n }\n\n return {\n valid,\n details,\n peer: {\n nodeId: payeePublicKey, // Use the already-extracted payee public key\n isConnected:\n checks.peerConnected ||\n issues.filter((i) => i.code === 'NO_PEERS_CONNECTED').length === 0,\n trustScore: this.calculateTrustScore(checks, issues),\n },\n checks: {\n ...checks,\n },\n issues,\n recommendation,\n reason,\n };\n }\n\n /**\n * Quick format validation (regex-based, before RPC call)\n */\n private validateInvoiceFormat(invoice: string): { valid: boolean; error?: string } {\n // Invoice should be bech32-encoded and start with fibt (testnet) or fibb (mainnet)\n const invoiceRegex = /^fib[tb]{1}[a-z0-9]{50,}$/i;\n\n if (!invoiceRegex.test(invoice)) {\n return {\n valid: false,\n error: 'Invoice must be bech32-encoded (fibt... for testnet or fibb... for mainnet)',\n };\n }\n\n return { valid: true };\n }\n\n /**\n * Validate amount is positive and reasonable\n */\n private validateAmount(amountHex: HexString): {\n valid: boolean;\n code?: string;\n message?: string;\n } {\n try {\n const amount = fromHex(amountHex);\n\n if (amount <= 0n) {\n return {\n valid: false,\n code: 'ZERO_AMOUNT',\n message: 'Invoice amount must be greater than zero',\n };\n }\n\n // Sanity check: prevent obviously spoofed invoices\n // Max 1 million CKB (would be ~$XXX at current rates)\n const maxShannons = BigInt(1000000) * BigInt(100000000); // 1M CKB in shannons\n if (amount > maxShannons) {\n const amountCkb = Number(amount) / 1e8;\n return {\n valid: false,\n code: 'AMOUNT_TOO_LARGE',\n message: `Invoice amount (${amountCkb.toFixed(2)} CKB) exceeds reasonable maximum`,\n };\n }\n\n return { valid: true };\n } catch {\n return {\n valid: false,\n code: 'INVALID_AMOUNT_FORMAT',\n message: 'Could not parse invoice amount',\n };\n }\n }\n\n /**\n * Check if invoice has expired\n */\n private isInvoiceExpired(invoice: CkbInvoice): boolean {\n const expiryTimestamp = this.getExpiryTimestamp(invoice);\n return Date.now() > expiryTimestamp;\n }\n\n /**\n * Get expiry timestamp in milliseconds\n */\n private getExpiryTimestamp(invoice: CkbInvoice | null): number {\n if (!invoice) return 0;\n\n // Expiry is a duration in seconds, stored as an attribute (ExpiryTime).\n // Invoice timestamp is in seconds since UNIX epoch.\n try {\n const createdSeconds = fromHex(invoice.data.timestamp as HexString);\n const expiryDeltaSeconds =\n this.getAttributeU64(invoice.data.attrs, 'ExpiryTime') ?? BigInt(60 * 60);\n return Number(createdSeconds + expiryDeltaSeconds) * 1000;\n } catch {\n // Fall through to default\n }\n\n // Default: assume 1 hour from now\n return Date.now() + 60 * 60 * 1000;\n }\n\n private getAttributeU64(\n attrs: Attribute[],\n key: 'ExpiryTime' | 'FinalHtlcTimeout' | 'FinalHtlcMinimumExpiryDelta',\n ): bigint | undefined {\n for (const attr of attrs) {\n if (key in attr) {\n return fromHex((attr as Record<string, HexString>)[key] as HexString);\n }\n }\n return undefined;\n }\n\n private getDescription(invoice: CkbInvoice | null): string | undefined {\n if (!invoice) return undefined;\n for (const attr of invoice.data.attrs) {\n if ('Description' in attr) {\n return attr.Description;\n }\n }\n return undefined;\n }\n\n /**\n * Try to extract payee node public key from invoice attributes\n * The payee public key is embedded in the invoice as a PayeePublicKey attribute\n */\n private extractNodeIdFromInvoice(invoice: CkbInvoice | null): string | undefined {\n if (!invoice) {\n return undefined;\n }\n\n // Search for PayeePublicKey attribute in the invoice data\n for (const attr of invoice.data.attrs) {\n if ('PayeePublicKey' in attr) {\n return attr.PayeePublicKey;\n }\n }\n\n return undefined;\n }\n\n /**\n * Calculate trust score (0-100) based on various factors\n */\n private calculateTrustScore(\n checks: {\n validFormat: boolean;\n notExpired: boolean;\n validAmount: boolean;\n peerConnected: boolean;\n },\n issues: VerificationIssue[],\n ): number {\n let score = 100;\n\n // Deduct for failed checks\n if (!checks.validFormat) score -= 25;\n if (!checks.notExpired) score -= 20;\n if (!checks.validAmount) score -= 15;\n if (!checks.peerConnected) score -= 10;\n\n // Deduct for warnings\n const warnings = issues.filter((i) => i.type === 'warning').length;\n score -= warnings * 5;\n\n return Math.max(0, score);\n }\n}\n","/**\n * Payment Proof & Tracking System\n * Records, stores, and verifies payment evidence for audit trail and reconciliation\n *\n * IMPORTANT LIMITATION:\n * Fiber Network RPC currently does NOT return the payment preimage to the sender\n * after a successful payment. In standard Lightning protocol, the preimage serves\n * as cryptographic proof of payment (SHA256(preimage) === payment_hash).\n *\n * Until Fiber exposes the preimage in send_payment/get_payment responses:\n * - Payment proofs are based on RPC status (Success/Failed) rather than preimage\n * - For invoices YOU create (as receiver), preimage IS available\n * - This limitation affects sender-side proof verification only\n *\n * Tracking issue: Preimage not exposed in Fiber RPC send_payment result\n */\n\nimport { createHash } from 'node:crypto';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { HexString, PaymentStatus } from '../types/index.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Complete payment proof record\n */\nexport interface PaymentProof {\n /** Unique identifier (payment hash) */\n id: string;\n /** Status at time of recording */\n status: PaymentStatus;\n /** Original invoice string */\n invoice: string;\n /** Invoice details extracted */\n invoiceDetails: {\n paymentHash: string;\n amountCkb: number;\n description?: string;\n };\n /** Payment execution details */\n execution: {\n amountCkb: number;\n feeCkb: number;\n actualTimestamp: number; // When payment settled (if succeeded)\n requestTimestamp: number; // When payment was initiated\n };\n /** Proof of payment */\n proof: {\n preimage?: HexString; // Secret revealed on successful payment\n peerAddress?: string; // Peer that received payment\n channelUsed?: string; // Channel ID used\n routePath?: string[]; // Peers involved in routing\n };\n /** Verification status */\n verified: boolean;\n verifiedAt?: number;\n verificationMethod?: 'preimage_hash' | 'rpc_status' | 'manual';\n /** Metadata for audit trail */\n metadata: {\n createdAt: number;\n updatedAt: number;\n notes?: string;\n };\n}\n\nexport interface PaymentProofSummary {\n totalProofs: number;\n verifiedCount: number;\n pendingCount: number;\n failedCount: number;\n totalAmountCkb: number;\n totalFeesCkb: number;\n timeRange: {\n earliest?: number;\n latest?: number;\n };\n}\n\n// =============================================================================\n// Payment Proof Manager\n// =============================================================================\n\nexport class PaymentProofManager {\n private proofs: Map<string, PaymentProof> = new Map();\n private proofFilePath: string;\n private maxStoredProofs = 10000; // Prevent unbounded growth\n\n constructor(dataDir: string) {\n this.proofFilePath = `${dataDir}/payment-proofs.json`;\n }\n\n /**\n * Load proofs from disk\n */\n async load(): Promise<void> {\n try {\n const data = await readFile(this.proofFilePath, 'utf-8');\n const proofs = JSON.parse(data) as PaymentProof[];\n this.proofs.clear();\n proofs.forEach((proof) => {\n this.proofs.set(proof.id, proof);\n });\n } catch {\n // File doesn't exist yet or is empty\n this.proofs.clear();\n }\n }\n\n /**\n * Save proofs to disk\n */\n async save(): Promise<void> {\n const proofs = Array.from(this.proofs.values());\n const data = JSON.stringify(proofs, null, 2);\n\n try {\n await writeFile(this.proofFilePath, data, 'utf-8');\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n // Directory doesn't exist, create it first\n await this.ensureDirectory();\n await writeFile(this.proofFilePath, data, 'utf-8');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Record a payment proof after successful execution\n */\n recordPaymentProof(\n paymentHash: string,\n invoice: string,\n invoiceDetails: {\n paymentHash: string;\n amountCkb: number;\n description?: string;\n },\n execution: {\n amountCkb: number;\n feeCkb: number;\n actualTimestamp: number;\n requestTimestamp: number;\n },\n status: PaymentStatus,\n proof?: {\n preimage?: HexString;\n peerAddress?: string;\n channelUsed?: string;\n routePath?: string[];\n },\n ): PaymentProof {\n const now = Date.now();\n const fullProof: PaymentProof = {\n id: paymentHash,\n status,\n invoice,\n invoiceDetails,\n execution,\n proof: proof || {},\n verified: false,\n metadata: {\n createdAt: now,\n updatedAt: now,\n },\n };\n\n this.proofs.set(paymentHash, fullProof);\n\n // Verify if we have preimage\n if (proof?.preimage) {\n const isValid = this.verifyPreimageHash(proof.preimage, invoiceDetails.paymentHash);\n if (isValid) {\n fullProof.verified = true;\n fullProof.verifiedAt = now;\n fullProof.verificationMethod = 'preimage_hash';\n }\n }\n\n // Cleanup if we exceed max proofs (keep newest)\n if (this.proofs.size > this.maxStoredProofs) {\n this.pruneOldestProofs(this.maxStoredProofs * 0.8); // Keep 80% after pruning\n }\n\n return fullProof;\n }\n\n /**\n * Get proof by payment hash\n */\n getProof(paymentHash: string): PaymentProof | undefined {\n return this.proofs.get(paymentHash);\n }\n\n /**\n * Verify a proof's authenticity\n */\n verifyProof(proof: PaymentProof): {\n valid: boolean;\n reason: string;\n } {\n // If already verified with preimage, trust it\n if (proof.verified && proof.verificationMethod === 'preimage_hash') {\n return {\n valid: true,\n reason: 'Verified via preimage hash match',\n };\n }\n\n // Try to verify preimage now\n if (proof.proof?.preimage) {\n const hashValid = this.verifyPreimageHash(\n proof.proof.preimage,\n proof.invoiceDetails.paymentHash,\n );\n if (!hashValid) {\n return {\n valid: false,\n reason: 'Preimage does not hash to payment hash',\n };\n }\n proof.verified = true;\n proof.verifiedAt = Date.now();\n proof.verificationMethod = 'preimage_hash';\n return {\n valid: true,\n reason: 'Preimage verified via hash',\n };\n }\n\n // If status is Success without preimage, it was verified by RPC\n if (proof.status === 'Success') {\n return {\n valid: true,\n reason: 'Payment succeeded according to RPC (preimage not available)',\n };\n }\n\n return {\n valid: false,\n reason: 'No verification method available',\n };\n }\n\n /**\n * Get payment timeline between two timestamps\n */\n getPaymentChain(startTime: number, endTime: number): PaymentProof[] {\n return Array.from(this.proofs.values())\n .filter((p) => p.metadata.createdAt >= startTime && p.metadata.createdAt <= endTime)\n .sort((a, b) => a.metadata.createdAt - b.metadata.createdAt);\n }\n\n /**\n * Get summary statistics\n */\n getSummary(): PaymentProofSummary {\n const proofs = Array.from(this.proofs.values());\n\n let verifiedCount = 0;\n let pendingCount = 0;\n let failedCount = 0;\n let totalAmountCkb = 0;\n let totalFeesCkb = 0;\n let earliest: number | undefined;\n let latest: number | undefined;\n\n proofs.forEach((proof) => {\n if (proof.verified) verifiedCount++;\n else if (proof.status === 'Inflight' || proof.status === 'Created') pendingCount++;\n else if (proof.status === 'Failed') failedCount++;\n\n totalAmountCkb += proof.execution.amountCkb;\n totalFeesCkb += proof.execution.feeCkb;\n\n const createdAt = proof.metadata.createdAt;\n if (!earliest || createdAt < earliest) earliest = createdAt;\n if (!latest || createdAt > latest) latest = createdAt;\n });\n\n return {\n totalProofs: proofs.length,\n verifiedCount,\n pendingCount,\n failedCount,\n totalAmountCkb,\n totalFeesCkb,\n timeRange: {\n earliest,\n latest,\n },\n };\n }\n\n /**\n * Export proofs as audit report\n */\n exportAuditReport(startTime?: number, endTime?: number): string {\n const proofs = Array.from(this.proofs.values())\n .filter((p) => {\n if (!startTime && !endTime) return true;\n const created = p.metadata.createdAt;\n if (startTime && created < startTime) return false;\n if (endTime && created > endTime) return false;\n return true;\n })\n .sort((a, b) => a.metadata.createdAt - b.metadata.createdAt);\n\n const lines: string[] = [\n 'Payment Audit Report',\n `Generated: ${new Date().toISOString()}`,\n `Time Range: ${startTime ? new Date(startTime).toISOString() : 'All'} - ${endTime ? new Date(endTime).toISOString() : 'All'}`,\n `Total Payments: ${proofs.length}`,\n '',\n 'Payment ID | Status | Amount CKB | Fee CKB | Verified | Created At',\n '-'.repeat(80),\n ];\n\n proofs.forEach((p) => {\n lines.push(\n `${p.id.slice(0, 16)}... | ${p.status} | ${p.execution.amountCkb.toFixed(4)} | ${p.execution.feeCkb.toFixed(8)} | ${p.verified ? 'Yes' : 'No'} | ${new Date(p.metadata.createdAt).toISOString()}`,\n );\n });\n\n return lines.join('\\n');\n }\n\n /**\n * Verify preimage hash (SHA256 of preimage = payment_hash)\n */\n private verifyPreimageHash(preimage: HexString, paymentHash: string): boolean {\n try {\n // Remove 0x prefix if present\n const preimageHex = preimage.startsWith('0x') ? preimage.slice(2) : preimage;\n const paymentHashHex = paymentHash.startsWith('0x') ? paymentHash.slice(2) : paymentHash;\n\n // Convert hex to buffer\n const preimageBuffer = Buffer.from(preimageHex, 'hex');\n const paymentHashBuffer = Buffer.from(paymentHashHex, 'hex');\n\n // SHA256 hash the preimage\n const hash = createHash('sha256').update(preimageBuffer).digest();\n\n // Compare\n return hash.equals(paymentHashBuffer);\n } catch {\n return false;\n }\n }\n\n /**\n * Remove oldest proofs to keep storage bounded\n */\n private pruneOldestProofs(count: number): void {\n const sorted = Array.from(this.proofs.values()).sort(\n (a, b) => a.metadata.createdAt - b.metadata.createdAt,\n );\n\n const toRemove = sorted.slice(0, Math.floor(sorted.length - count));\n toRemove.forEach((p) => {\n this.proofs.delete(p.id);\n });\n }\n\n /**\n * Ensure directory exists\n */\n private async ensureDirectory(): Promise<void> {\n const dir = dirname(this.proofFilePath);\n try {\n // Use Node.js 20+ mkdir with recursive option\n const fs = await import('node:fs');\n fs.mkdirSync(dir, { recursive: true });\n } catch {\n // Ignore if fails\n }\n }\n}\n"],"mappings":"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","names":["createHash","dirname"]}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@fiber-pay/sdk",
3
+ "version": "0.1.0-rc.1",
4
+ "description": "Core SDK for building Fiber Network applications on CKB Lightning",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./browser": {
14
+ "import": "./dist/browser.js",
15
+ "types": "./dist/browser.d.ts"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "keywords": [
23
+ "ckb",
24
+ "lightning",
25
+ "fiber",
26
+ "payment",
27
+ "nervos",
28
+ "sdk"
29
+ ],
30
+ "license": "MIT",
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "engines": {
35
+ "node": ">=20"
36
+ },
37
+ "dependencies": {
38
+ "zod": "^4.3.6"
39
+ },
40
+ "scripts": {
41
+ "build": "tsup",
42
+ "dev": "tsup --watch",
43
+ "test": "vitest run",
44
+ "typecheck": "tsc --noEmit"
45
+ }
46
+ }