--- name: walrus-debugger description: Walrus 错误诊断专家 - 智能分析和解决 Walrus 应用中的问题 parameters: - name: error-type type: string description: 错误类型 (upload/download/network/configuration/timeout/authentication) required: true - name: error-context type: string description: 错误信息、代码片段或问题描述 required: true - name: environment type: string description: 运行环境 (browser/nodejs/nextjs/production) default: nodejs --- # Walrus 错误诊断技能 ## 技能概述 `walrus-debugger` 是专业的错误诊断助手,专门分析 Walrus 应用中的各种问题,提供智能的解决方案和预防措施。 ## 常见错误类型及解决方案 ### 1. 上传失败错误 #### 错误类型: Upload Failed ```typescript // 错误示例 Error: Upload failed: insufficient storage balance Error: Upload failed: blob size exceeds maximum limit Error: Upload failed: transaction rejected // 诊断和解决方案 class UploadErrorDiagnostics { async diagnoseUploadError(error: Error, context: UploadContext): Promise { const analysis = this.analyzeErrorMessage(error.message); switch (analysis.type) { case 'INSUFFICIENT_BALANCE': return this.diagnoseBalanceError(context); case 'SIZE_LIMIT': return this.diagnoseSizeError(context); case 'TRANSACTION_REJECTED': return this.diagnoseTransactionError(error, context); case 'TIMEOUT': return this.diagnoseTimeoutError(context); default: return this.diagnoseGenericUploadError(error, context); } } private async diagnoseBalanceError(context: UploadContext): Promise { const walletBalance = await this.checkWalletBalance(context.signerAddress); return { errorType: 'INSUFFICIENT_BALANCE', description: '钱包余额不足,无法支付存储费用', causes: [ 'WAL 代币余额不足', 'SUI 代币余额不足以支付 gas 费', '存储成本计算错误' ], solutions: [ { title: '检查并充值钱包', code: ` // 检查钱包余额 async function checkBalances(address: string) { const suiClient = new SuiClient({ url: getFullnodeUrl('testnet') }); const balance = await suiClient.getBalance({ owner: address }); console.log('SUI Balance:', balance.totalBalance); // 检查 WAL 余额(如果支持) const walBalance = await suiClient.getBalance({ owner: address, coinType: '0x2::sui::SUI' // 替换为 WAL coin type }); return { sui: balance.totalBalance, wal: walBalance.totalBalance }; }` }, { title: '优化存储策略', code: ` // 使用更短的存储周期 const result = await client.walrus.writeFiles({ files: [file], epochs: 5, // 减少存储周期 deletable: true, signer: keypair }); // 或者使用更小的文件 function compressFile(file: File): Promise { return new Promise((resolve) => { if (file.size > 10 * 1024 * 1024) { // 10MB // 压缩或分割文件 compressImage(file).then(resolve); } else { resolve(file); } }); }` } ], prevention: [ '定期检查钱包余额', '设置余额预警', '实施费用估算功能' ] }; } private async diagnoseSizeError(context: UploadContext): Promise { const fileStats = await this.analyzeFileSize(context.file); return { errorType: 'SIZE_LIMIT', description: '文件大小超出限制', causes: [ `文件大小 ${fileStats.size}MB 超出限制`, '网络环境不支持大文件传输', '客户端内存不足' ], solutions: [ { title: '文件压缩', code: ` // 图片压缩 async function compressImage(file: File, quality = 0.8): Promise { return new Promise((resolve) => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d')!; const img = new Image(); img.onload = () => { // 计算压缩后的尺寸 const scale = Math.min(1, 1920 / Math.max(img.width, img.height)); canvas.width = img.width * scale; canvas.height = img.height * scale; ctx.drawImage(img, 0, 0, canvas.width, canvas.height); canvas.toBlob((blob) => { resolve(new File([blob!], file.name, { type: file.type })); }, file.type, quality); }; img.src = URL.createObjectURL(file); }); } // 文本文件压缩 async function compressTextFile(file: File): Promise { const text = await file.text(); const compressed = await this.compressString(text); return new File([compressed], file.name + '.gz', { type: 'application/gzip' }); }` }, { title: '文件分割上传', code: ` // 分割大文件 async function splitAndUpload(file: File, chunkSize = 5 * 1024 * 1024): Promise { const chunks: string[] = []; const totalChunks = Math.ceil(file.size / chunkSize); for (let i = 0; i < totalChunks; i++) { const start = i * chunkSize; const end = Math.min(start + chunkSize, file.size); const chunk = file.slice(start, end); const chunkFile = new File([chunk], \`\${file.name}.part\${i}\`); const result = await uploadSingleFile(chunkFile); chunks.push(result.blobId); } return chunks; } // 创建清单文件记录分割信息 async function createManifest(chunks: string[], originalFile: File): Promise { const manifest = { originalName: originalFile.name, originalSize: originalFile.size, chunks: chunks.map((blobId, index) => ({ blobId, index, size: originalFile.size / chunks.length })) }; const manifestBlob = new Blob([JSON.stringify(manifest)], { type: 'application/json' }); const manifestFile = new File([manifestBlob], \`\${originalFile.name}.manifest\`); const result = await uploadSingleFile(manifestFile); return result.blobId; }` } ], prevention: [ '预先检查文件大小', '实施文件大小限制', '提供文件压缩工具' ] }; } } ``` ### 2. 网络连接错误 #### 错误类型: Network Error ```typescript // 错误示例 Error: Network timeout Error: Connection refused Error: Failed to fetch class NetworkErrorDiagnostics { async diagnoseNetworkError(error: Error, context: NetworkContext): Promise { const networkStatus = await this.checkNetworkStatus(); return { errorType: 'NETWORK_ERROR', description: '网络连接问题', causes: [ '网络连接不稳定', 'RPC 节点不可用', '防火墙或代理阻止连接', 'DNS 解析失败' ], solutions: [ { title: '实施自动重试机制', code: ` class RobustWalrusClient { private readonly MAX_RETRIES = 3; private readonly RETRY_DELAY_BASE = 1000; async withRetry( operation: () => Promise, operationName: string ): Promise { for (let attempt = 1; attempt <= this.MAX_RETRIES; attempt++) { try { return await operation(); } catch (error) { if (!this.isRetryableError(error) || attempt === this.MAX_RETRIES) { throw new Error(\`\${operationName} failed after \${attempt} attempts: \${error.message}\`); } const delay = this.RETRY_DELAY_BASE * Math.pow(2, attempt - 1); console.warn(\`\${operationName} failed (attempt \${attempt}/\${this.MAX_RETRIES}), retrying in \${delay}ms\`); await this.sleep(delay); } } throw new Error('Should not reach here'); } private isRetryableError(error: any): boolean { return error instanceof RetryableWalrusClientError || error.message.includes('timeout') || error.message.includes('network') || error.message.includes('connection') || error.status >= 500; // Server errors } private sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } async uploadFiles(files: WalrusFile[]): Promise { return this.withRetry( () => this.client.walrus.writeFiles({ files, epochs: 10, signer: this.signer }), 'File upload' ); } }` }, { title: '实施节点健康检查', code: ` // 节点健康检查 class NodeHealthChecker { private nodes: string[] = [ 'https://fullnode.testnet.sui.io', 'https://fullnode.testnet.sui.io:443', // 添加备用节点 ]; async getHealthyNode(): Promise { for (const node of this.nodes) { try { const response = await fetch(\`\${node}/\`, { method: 'HEAD', timeout: 5000 }); if (response.ok) { console.log(\`Node \${node} is healthy\`); return node; } } catch (error) { console.warn(\`Node \${node} is unhealthy: \${error.message}\`); } } throw new Error('No healthy nodes available'); } async testAllNodes(): Promise { const results = await Promise.allSettled( this.nodes.map(async (node) => { const startTime = Date.now(); try { const response = await fetch(\`\${node}/\`, { method: 'HEAD', timeout: 5000 }); return { node, healthy: response.ok, responseTime: Date.now() - startTime, error: null }; } catch (error) { return { node, healthy: false, responseTime: Date.now() - startTime, error: error.message }; } }) ); return results.map(result => result.status === 'fulfilled' ? result.value : null ).filter(Boolean) as NodeHealth[]; } }` }, { title: '配置超时和重试', code: ` // 配置自定义超时和重试 const client = new SuiJsonRpcClient({ url: getFullnodeUrl('testnet'), network: 'testnet', }).$extend(walrus({ storageNodeClientOptions: { timeout: 60000, // 60秒超时 retryAttempts: 5, retryDelay: 2000, onError: (error) => { console.error('Storage node error:', error); // 发送错误到监控系统 this.trackError('storage_node_error', error); } } }));` } ], prevention: [ '实施网络监控', '使用多个备用节点', '设置合理的超时时间', '实施断路器模式' ] }; } private async checkNetworkStatus(): Promise { try { const response = await fetch('https://httpbin.org/ip'); const ipInfo = await response.json(); return { online: true, ip: ipInfo.ip, latency: Date.now() - this.startTime }; } catch (error) { return { online: false, error: error.message }; } } } ``` ### 3. 配置错误 #### 错误类型: Configuration Error ```typescript // 错误示例 Error: Invalid network configuration Error: WASM module failed to load Error: Invalid signer configuration class ConfigurationErrorDiagnostics { async diagnoseConfigError(error: Error, context: ConfigContext): Promise { return { errorType: 'CONFIGURATION_ERROR', description: '配置问题', causes: [ '网络配置错误', 'WASM 模块路径错误', '签名者配置无效', '环境变量缺失' ], solutions: [ { title: '验证配置完整性', code: ` // 配置验证器 class ConfigValidator { static validateWalrusConfig(config: any): ValidationResult { const errors: string[] = []; // 验证必需字段 if (!config.network) { errors.push('network is required (testnet/mainnet)'); } if (!config.rpcUrl) { errors.push('rpcUrl is required'); } // 验证网络配置 if (config.network && !['testnet', 'mainnet'].includes(config.network)) { errors.push('network must be either "testnet" or "mainnet"'); } // 验证 URL 格式 if (config.rpcUrl && !this.isValidUrl(config.rpcUrl)) { errors.push('rpcUrl must be a valid URL'); } // 验证超时配置 if (config.timeout && (config.timeout < 1000 || config.timeout > 300000)) { errors.push('timeout must be between 1000ms and 300000ms'); } return { valid: errors.length === 0, errors }; } static async validateEnvironment(): Promise { const errors: string[] = []; // 检查环境变量 const requiredEnvVars = ['WALRUS_NETWORK', 'SUI_RPC_URL']; for (const envVar of requiredEnvVars) { if (!process.env[envVar]) { errors.push(\`Environment variable \${envVar} is required\`); } } // 检查 WASM 支持 if (typeof window !== 'undefined' && !window.WebAssembly) { errors.push('WebAssembly is not supported in this browser'); } // 验证网络连接 try { const response = await fetch(process.env.SUI_RPC_URL!, { method: 'HEAD', timeout: 5000 }); if (!response.ok) { errors.push('Cannot connect to SUI RPC endpoint'); } } catch (error) { errors.push(\`Network connection failed: \${error.message}\`); } return { valid: errors.length === 0, errors }; } private static isValidUrl(string: string): boolean { try { new URL(string); return true; } catch { return false; } } } // 使用配置验证 const config = { network: 'testnet', rpcUrl: getFullnodeUrl('testnet'), timeout: 30000 }; const validation = ConfigValidator.validateWalrusConfig(config); if (!validation.valid) { console.error('Configuration validation failed:', validation.errors); throw new Error('Invalid configuration'); }` }, { title: 'WASM 模块配置', code: ` // WASM 配置验证和加载 class WasmLoader { static async loadWasmWithFallback(): Promise { const strategies = [ () => this.loadFromBundle(), () => this.loadFromCDN(), () => this.loadFromLocal() ]; for (const strategy of strategies) { try { const wasm = await strategy(); console.log('WASM loaded successfully'); return wasm; } catch (error) { console.warn('WASM loading strategy failed:', error.message); } } throw new Error('All WASM loading strategies failed'); } private static async loadFromBundle(): Promise { // Vite 示例 if (typeof import.meta !== 'undefined' && import.meta.url) { const wasmUrl = new URL('@mysten/walrus-wasm/web/walrus_wasm_bg.wasm', import.meta.url); const wasmModule = await import(wasmUrl); return wasmModule.default; } throw new Error('Bundle loading not supported'); } private static async loadFromCDN(): Promise { const cdnUrls = [ 'https://unpkg.com/@mysten/walrus-wasm@latest/web/walrus_wasm_bg.wasm', 'https://cdn.jsdelivr.net/npm/@mysten/walrus-wasm@latest/web/walrus_wasm_bg.wasm' ]; for (const url of cdnUrls) { try { const response = await fetch(url); if (response.ok) { const wasmBuffer = await response.arrayBuffer(); return WebAssembly.compile(wasmBuffer); } } catch (error) { console.warn(\`CDN loading failed for \${url}:\`, error.message); } } throw new Error('All CDN URLs failed'); } private static async loadFromLocal(): Promise { // 尝试从本地路径加载 const localPaths = [ '/walrus/walrus_wasm_bg.wasm', './node_modules/@mysten/walrus-wasm/web/walrus_wasm_bg.wasm' ]; for (const path of localPaths) { try { const response = await fetch(path); if (response.ok) { const wasmBuffer = await response.arrayBuffer(); return WebAssembly.compile(wasmBuffer); } } catch (error) { console.warn(\`Local loading failed for \${path}:\`, error.message); } } throw new Error('Local loading failed'); } } // 在客户端配置中使用 WASM const client = new SuiJsonRpcClient({ url: getFullnodeUrl('testnet'), network: 'testnet', }).$extend( walrus({ wasmUrl: await WasmLoader.loadWasmWithFallback() }) );` } ], prevention: [ '实施配置验证', '提供配置模板', '设置默认值', '实施配置监控' ] }; } } ``` ### 4. 浏览器环境特定错误 #### 错误类型: Browser Environment Error ```typescript class BrowserErrorDiagnostics { async diagnoseBrowserError(error: Error, context: BrowserContext): Promise { return { errorType: 'BROWSER_ERROR', description: '浏览器环境特定问题', causes: [ 'WASM 不支持', 'CORS 策略阻止', '内存不足', '钱包连接问题' ], solutions: [ { title: '浏览器兼容性检查', code: ` // 浏览器兼容性检查 class BrowserCompatibility { static checkCompatibility(): BrowserCompatibilityResult { const checks = { webAssembly: this.checkWebAssembly(), fetch: this.checkFetch(), blob: this.checkBlob(), fileAPI: this.checkFileAPI(), webWorkers: this.checkWebWorkers(), localStorage: this.checkLocalStorage() }; const supported = Object.values(checks).every(check => check.supported); return { supported, checks, recommendations: this.getRecommendations(checks) }; } private static checkWebAssembly(): CompatibilityCheck { const supported = typeof WebAssembly === 'object' && WebAssembly !== null; return { supported, message: supported ? 'WebAssembly is supported' : 'WebAssembly is not supported', fix: !supported ? 'Please use a modern browser that supports WebAssembly' : null }; } private static getRecommendations(checks: Record): string[] { const recommendations: string[] = []; if (!checks.webAssembly.supported) { recommendations.push('Upgrade to a modern browser (Chrome 57+, Firefox 52+, Safari 11+)'); } if (!checks.webWorkers.supported) { recommendations.push('Consider enabling Web Workers for better performance'); } return recommendations; } } // 使用兼容性检查 const compatibility = BrowserCompatibility.checkCompatibility(); if (!compatibility.supported) { console.error('Browser compatibility issues:', compatibility.recommendations); // 显示用户友好的错误信息 this.showCompatibilityError(compatibility); }` }, { title: '内存管理', code: ` // 浏览器内存管理 class BrowserMemoryManager { private memoryThreshold = 100 * 1024 * 1024; // 100MB async checkMemoryUsage(): Promise { if ('memory' in performance) { const memory = (performance as any).memory; return { used: memory.usedJSHeapSize, total: memory.totalJSHeapSize, limit: memory.jsHeapSizeLimit, usageRatio: memory.usedJSHeapSize / memory.jsHeapSizeLimit }; } return { used: 0, total: 0, limit: 0, usageRatio: 0 }; } async manageMemoryForLargeFile(file: File): Promise { const memoryUsage = await this.checkMemoryUsage(); // 检查是否有足够内存处理文件 const estimatedUsage = file.size * 3; // 估算处理过程中需要的内存 const availableMemory = memoryUsage.limit - memoryUsage.used; if (estimatedUsage > availableMemory) { console.warn('Insufficient memory for file processing'); // 触发垃圾回收(如果可能) if (window.gc) { window.gc(); } // 重新检查内存 const newUsage = await this.checkMemoryUsage(); return (file.size * 3) < (newUsage.limit - newUsage.used); } return true; } // 流式处理大文件 async processLargeFileInChunks( file: File, processor: (chunk: Uint8Array) => Promise, chunkSize = 1024 * 1024 // 1MB ): Promise { const reader = new FileReader(); let offset = 0; const processChunk = () => { if (offset >= file.size) { return Promise.resolve(); } return new Promise((resolve, reject) => { const chunk = file.slice(offset, offset + chunkSize); reader.onload = async (event) => { try { if (event.target?.result) { const arrayBuffer = event.target.result as ArrayBuffer; const uint8Array = new Uint8Array(arrayBuffer); await processor(uint8Array); offset += chunkSize; // 检查内存使用 const memoryUsage = await this.checkMemoryUsage(); if (memoryUsage.usageRatio > 0.8) { console.warn('High memory usage, forcing garbage collection'); if (window.gc) window.gc(); } await processChunk(); } resolve(); } catch (error) { reject(error); } }; reader.onerror = () => reject(new Error('File reading failed')); reader.readAsArrayBuffer(chunk); }); }; return processChunk(); } }` } ], prevention: [ '实施浏览器兼容性检查', '监控内存使用', '提供降级方案', '实施优雅错误处理' ] }; } } ``` ## 使用方法 ### 上传错误诊断 ```typescript skill: "walrus-debugger" // error-type: upload // error-context: Upload failed: insufficient storage balance, trying to upload 10MB file to testnet // environment: browser ``` ### 网络连接问题 ```typescript skill: "walrus-debugger" // error-type: network // error-context: Network timeout when connecting to storage nodes, intermittent connection issues // environment: production ``` ### 配置验证 ```typescript skill: "walrus-debugger" // error-type: configuration // error-context: WASM module failed to load in Vite production build, error: "Failed to resolve module" // environment: nodejs ``` ### 性能问题 ```typescript skill: "walrus-debugger" // error-type: performance // error-context: Memory usage spikes when uploading multiple large files, browser crashes // environment: browser ``` --- *更新时间:2025-11-11*