bigblocks
Version:
Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React
209 lines (208 loc) • 8.26 kB
JavaScript
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,
};
}