UNPKG

bigblocks

Version:

Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React

209 lines (208 loc) 8.26 kB
import { useMutation } from "@tanstack/react-query"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useBitcoinAuth } from "../../../hooks/useBitcoinAuth.js"; export function useMasterKeyMigration() { const { user, actions } = useBitcoinAuth(); const [progress, setProgress] = useState(0); // Detect backup format from structure const detectBackupFormat = useCallback((backup) => { if (!backup || typeof backup !== "object") { return { format: "legacy", isValid: false, hasEncryptedData: false, keyInfo: { hasXprv: false, hasMnemonic: false, hasRootPk: false, hasIds: false, }, }; } const b = backup; const keyInfo = { hasXprv: typeof b.xprv === "string" && b.xprv.length > 0, hasMnemonic: typeof b.mnemonic === "string" && b.mnemonic.length > 0, hasRootPk: typeof b.rootPk === "string" && b.rootPk.length > 0, hasIds: typeof b.ids === "string" && b.ids.length > 0, }; // Type 42 detection: has rootPk and ids, no xprv if (keyInfo.hasRootPk && keyInfo.hasIds && !keyInfo.hasXprv) { return { format: "type42", isValid: true, hasEncryptedData: keyInfo.hasIds, keyInfo, estimatedCreationDate: typeof b.createdAt === "string" ? b.createdAt : undefined, }; } // Legacy detection: has xprv and mnemonic if (keyInfo.hasXprv && keyInfo.hasMnemonic) { return { format: "legacy", isValid: true, hasEncryptedData: keyInfo.hasIds, keyInfo, estimatedCreationDate: typeof b.createdAt === "string" ? b.createdAt : undefined, }; } // Invalid or incomplete backup return { format: "legacy", isValid: false, hasEncryptedData: keyInfo.hasIds, keyInfo, }; }, []); // Validate backup format with detailed errors const _validateBackupFormat = useCallback((backup) => { const baseResult = detectBackupFormat(backup); const errors = []; const warnings = []; if (!baseResult.isValid) { if (!baseResult.keyInfo.hasIds) { errors.push({ field: "ids", message: "Missing encrypted identity data", severity: "error", }); } if (baseResult.format === "legacy") { if (!baseResult.keyInfo.hasXprv) { errors.push({ field: "xprv", message: "Missing extended private key (xprv)", severity: "error", }); } if (!baseResult.keyInfo.hasMnemonic) { errors.push({ field: "mnemonic", message: "Missing BIP39 mnemonic phrase", severity: "error", }); } } else { if (!baseResult.keyInfo.hasRootPk) { errors.push({ field: "rootPk", message: "Missing Type 42 root private key (WIF)", severity: "error", }); } } } // Warnings for legacy format if (baseResult.format === "legacy" && baseResult.isValid) { warnings.push({ field: "format", message: "Legacy BIP32 format detected - consider migrating to Type 42", severity: "warning", }); } return { ...baseResult, errors, warnings, canProceed: errors.length === 0, }; }, [detectBackupFormat]); // Get current user's backup info const [currentBackupInfo, setCurrentBackupInfo] = useState(null); useEffect(() => { if (!user) { setCurrentBackupInfo(null); return; } // Safely get backup from session storage const loadBackupInfo = async () => { try { const backup = await actions.getCurrentBackup(); if (backup) { setCurrentBackupInfo(detectBackupFormat(backup)); } else { setCurrentBackupInfo(null); } } catch { setCurrentBackupInfo(null); } }; loadBackupInfo(); }, [user, actions, detectBackupFormat]); // Check if user can migrate (has legacy master backup) const canMigrate = useMemo(() => { const info = currentBackupInfo; return (info?.format === "legacy" && info.isValid && info.keyInfo.hasXprv && info.keyInfo.hasMnemonic); }, [currentBackupInfo]); // Check if user already has Type 42 backup const hasType42Backup = useMemo(() => { const info = currentBackupInfo; return info?.format === "type42" && info.isValid; }, [currentBackupInfo]); // Migration mutation const migrationMutation = useMutation({ mutationFn: async (options = {}) => { if (!user || !canMigrate) { throw new Error("No legacy master backup available for migration"); } // Safely get current backup from session storage const backup = await actions.getCurrentBackup(); if (!backup || !("xprv" in backup) || !("mnemonic" in backup)) { throw new Error("Invalid legacy backup: missing xprv or mnemonic"); } const currentBackup = backup; setProgress(10); try { // Step 1: Import current legacy backup and extract private key setProgress(25); // NOTE: This is where we'd use the updated bsv-bap library // In practice, we'd need to: // 1. Extract the root private key from the legacy xprv // 2. Create a new Type 42 BAP instance with the derived WIF // 3. Export the new backup in Type 42 format // For now, we'll simulate the conversion process // In reality, this would use the actual bsv-bap and bitcoin-backup libraries setProgress(50); // Step 2: Create Type 42 backup (simulated) // const privateKey = PrivateKey.fromWif(derivedWif); // const newBapInstance = new BAP({ rootPk: privateKey.toWif() }); setProgress(75); // Step 3: Export new Type 42 backup const newBackup = { ids: currentBackup.ids, // Re-use encrypted identity data for now rootPk: "L1d5g2CJoWuwjoWVyG2FS7KvmLkmoi9VX8qDqpuijiWcHxVTHo2F", // Simulated Type 42 WIF label: options.newLabel || `${currentBackup.label || "Migrated"} (Type 42)`, createdAt: new Date().toISOString(), }; setProgress(100); return { success: true, newBackup, warning: "Migration creates new identity keys - existing attestations will not transfer automatically.", }; } catch (error) { console.error("Migration failed:", error); throw error; } }, }); const migrate = useCallback((options) => migrationMutation.mutateAsync(options || {}), [migrationMutation]); return { detectBackupFormat, canMigrate, hasType42Backup, migrate, currentBackupInfo, isLoading: migrationMutation.isPending, error: migrationMutation.error, progress, }; }