From 69e44665276af1daef03272f8493988811274df5 Mon Sep 17 00:00:00 2001 From: AricRedemption Date: Tue, 3 Sep 2024 17:53:00 +0800 Subject: [PATCH] feat: encrypet mnemonic --- src/components/ResetModal.vue | 31 ++----- src/lib/account.ts | 14 ++- src/lib/crypto.ts | 4 + src/lib/lock.ts | 14 +-- src/lib/migrate.ts | 89 +++++++++++++----- src/lib/storage/key.ts | 1 + src/lib/wallet.ts | 91 ++++++++++--------- src/pages/manage/Edit.vue | 43 +++------ src/pages/manage/Index.vue | 7 +- src/pages/migrateV2/Index.vue | 11 ++- src/pages/wallet/AccountHeader.vue | 48 ++++------ src/pages/wallet/Backup.vue | 47 +++++----- src/pages/wallet/SetPassword.vue | 30 +++--- src/pages/welcome/components/Activate.vue | 69 ++++++-------- src/pages/welcome/components/ImportPhrase.vue | 4 +- src/router.ts | 13 +-- src/stores/WalletStore.ts | 5 +- 17 files changed, 268 insertions(+), 253 deletions(-) diff --git a/src/components/ResetModal.vue b/src/components/ResetModal.vue index f7795cd7..0b390947 100644 --- a/src/components/ResetModal.vue +++ b/src/components/ResetModal.vue @@ -4,7 +4,7 @@ import useStorage from '@/lib/storage' import { PASSWORD_KEY } from '@/lib/password' import { Checkbox } from '@/components/ui/checkbox' import { XMarkIcon } from '@heroicons/vue/20/solid' -import { V3_WALLETS_STORAGE_KEY } from '@/lib/storage/key' +import { V3_ENCRYPTED_WALLETS_STORAGE_KEY } from '@/lib/storage/key' defineProps<{ show: boolean @@ -24,7 +24,7 @@ const checkedAll = computed(() => checked1.value && checked2.value && checked3.v const disconnect = async () => { emit('update:show', false) await storage.delete(PASSWORD_KEY) - await storage.delete(V3_WALLETS_STORAGE_KEY) + await storage.delete(V3_ENCRYPTED_WALLETS_STORAGE_KEY) window.location.reload() } @@ -44,10 +44,8 @@ const close = () => {
-
+
Reset Wallet @@ -83,27 +81,18 @@ const close = () => { To confirm wallet reset, please enter: RESET

- +
- -
diff --git a/src/lib/account.ts b/src/lib/account.ts index 205b3ab4..64f26368 100644 --- a/src/lib/account.ts +++ b/src/lib/account.ts @@ -5,7 +5,7 @@ import { fetchUtxos } from '@/queries/utxos' import { notifyContent } from '@/lib/notify-content' import { generateRandomString } from '@/lib/helpers' import { getBtcNetwork, getNetwork } from '@/lib/network' -import { getActiveWalletOnlyAccount, getCurrentWallet } from './wallet' +import { getActiveWalletOnlyAccount, getCurrentWallet, getV3EncryptedWallets } from './wallet' import type { V1Account, V2Account, Chain, ChainDetail } from './types' import { ScriptType, Chain as UtxoChain } from '@metalet/utxo-wallet-service' import { fetchSpaceBalance, fetchBtcBalance, doNothing } from '@/queries/balance' @@ -128,7 +128,17 @@ export async function getAccount(accountId: string): Promise { if (IS_DEV) { - return password.value + return (await getEncryptedPassword()) || '' } else { - return await notifyBg('getPassword')() + return (await notifyBg('getPassword')()) || '' } } diff --git a/src/lib/migrate.ts b/src/lib/migrate.ts index a3345945..a326b72e 100644 --- a/src/lib/migrate.ts +++ b/src/lib/migrate.ts @@ -38,6 +38,7 @@ import { getLegacyAccounts, getCurrentAccountId, } from './account' +import { getPassword } from './lock' const storage = useStorage() @@ -125,7 +126,11 @@ async function needEncryptV3(): Promise { } const v3Wallets = await getV3Wallets() const v3EncryptedWallets = await getV3EncryptedWallets() - const needed = v3Wallets.some((mne) => !v3EncryptedWallets.includes(mne)) + const v3EncryptedMnemonics = v3EncryptedWallets.map((encryptedWallet) => encryptedWallet.mnemonic) + const password = await getPassword() + const needed = v3Wallets.some((wallet) => !v3EncryptedMnemonics.includes(encrypt(wallet.mnemonic, password))) + + console.log('needEncryptV3', needed) if (!needed) { await storage.set(ACCOUNT_V3_Encrypted_KEY, true) @@ -339,7 +344,9 @@ export async function migrateToV2() { } export async function needMigrate() { - return (await needMigrateV1ToV2()) || (await needMigrateV0ToV2()) || (await needMigrateV2ToV3()) + return ( + (await needMigrateV1ToV2()) || (await needMigrateV0ToV2()) || (await needMigrateV2ToV3()) || (await needEncryptV3()) + ) } async function migrateV2ToV3(): Promise { @@ -467,44 +474,82 @@ async function migrateV2ToV3(): Promise { } export async function migrateToV3() { - const title = 'Migrate V2 account' - const { code, message: description } = await migrateV2ToV3() - if (code === MigrateResultCode.FAILED) { - toast({ title, toastType: 'fail', description }) - } else if (code === MigrateResultCode.SUCCESS) { - toast({ title, toastType: 'success', description }) - } else if (code === MigrateResultCode.UNDO) { - toast({ title, toastType: 'info', description }) + if (await needMigrateV2ToV3()) { + const title = 'Migrate V2 account' + const { code, message: description } = await migrateV2ToV3() + if (code === MigrateResultCode.FAILED) { + toast({ title, toastType: 'fail', description }) + } else if (code === MigrateResultCode.SUCCESS) { + toast({ title, toastType: 'success', description }) + } else if (code === MigrateResultCode.UNDO) { + toast({ title, toastType: 'info', description }) + } } } -async function encryptV3Accounts(password: string) { +async function encryptV3Accounts(): Promise { const v3Wallets = await getV3Wallets() const v3EncryptedWallets = await getV3EncryptedWallets() const encryptedMnemonics = new Set(v3EncryptedWallets.map((wallet) => wallet.mnemonic)) + let totalEncryptions = v3Wallets.length + let successfulEncryptions = 0 + let alreadyEncryptedCount = 0 + let failedEncryptions = 0 + + const password = await getPassword() + v3Wallets.forEach((wallet) => { - if (!encryptedMnemonics.has(wallet.mnemonic)) { - v3EncryptedWallets.push({ ...wallet, mnemonic: encrypt(wallet.mnemonic, password) }) + try { + if (!encryptedMnemonics.has(encrypt(wallet.mnemonic, password))) { + v3EncryptedWallets.push({ ...wallet, mnemonic: encrypt(wallet.mnemonic, password) }) + successfulEncryptions++ + } else { + alreadyEncryptedCount++ + } + } catch (e: any) { + addMigrateErrorAccount(wallet.mnemonic, JSON.stringify(wallet), 'V3', e) + failedEncryptions++ } }) + setV3EncryptedWalletsStorage( v3EncryptedWallets.reduce>((acc, wallet) => { acc[wallet.id] = wallet return acc }, {}) ) -} -export async function encryptV2Accounts(password: string): Promise { - const v2Accounts = await getV2AccountsObj() - console.log('v2Accounts', v2Accounts) + let code = MigrateResultCode.UNDO + if (failedEncryptions === totalEncryptions) { + code = MigrateResultCode.FAILED + } else if (successfulEncryptions > 0 && failedEncryptions === 0) { + code = MigrateResultCode.SUCCESS + } - const encryptedText = encrypt(JSON.stringify(v2Accounts), password) - console.log('encryptedText', encryptedText) + return { + code, + message: ` + Encryption Summary:\n + - Total Wallets Processed: ${totalEncryptions}\n + - Successful Encryptions: ${successfulEncryptions}\n + - Already Encrypted Wallets: ${alreadyEncryptedCount}\n + - Failed Encryptions: ${failedEncryptions} + `, + } +} - const decryptedText = decrypt(encryptedText, password) - console.log('decryptedText', decryptedText) - console.log('JSON decryptedText', JSON.parse(decryptedText)) +export async function migrateToV3Encrypted() { + if (await needEncryptV3()) { + const title = 'Encrypt V3 wallet' + const { code, message: description } = await encryptV3Accounts() + if (code === MigrateResultCode.FAILED) { + toast({ title, toastType: 'fail', description }) + } else if (code === MigrateResultCode.SUCCESS) { + toast({ title, toastType: 'success', description }) + } else if (code === MigrateResultCode.UNDO) { + toast({ title, toastType: 'info', description }) + } + } } diff --git a/src/lib/storage/key.ts b/src/lib/storage/key.ts index 2f422d64..038e1e03 100644 --- a/src/lib/storage/key.ts +++ b/src/lib/storage/key.ts @@ -22,3 +22,4 @@ export const WALLET_NUM = 'wallet_num' export const CURRENT_WALLET_ID = 'currentWalletId' export const V3_WALLETS_STORAGE_KEY = 'wallets_v3' export const V3_ENCRYPTED_WALLETS_STORAGE_KEY = 'encrypted_wallets_v3' +export const V3_ENCRYPTED_WALLETS_STORAGE_BACKUP_KEY = 'encrypted_wallets_v3_backup' diff --git a/src/lib/wallet.ts b/src/lib/wallet.ts index 704405ae..bafdcdbe 100644 --- a/src/lib/wallet.ts +++ b/src/lib/wallet.ts @@ -4,42 +4,55 @@ import { type V3Wallet } from './types' import { getCurrentAccountId } from './account' import { getV3AddressTypeStorage } from './addressType' import { Chain, MvcWallet, BtcWallet, AddressType, CoinType } from '@metalet/utxo-wallet-service' - import { WALLET_NUM, CURRENT_WALLET_ID, V3_WALLETS_STORAGE_KEY, V3_ENCRYPTED_WALLETS_STORAGE_KEY, + V3_ENCRYPTED_WALLETS_STORAGE_BACKUP_KEY, } from '@/lib/storage/key' const storage = useStorage() -export async function hasV3Wallets(): Promise { - return !!(await storage.get>(V3_WALLETS_STORAGE_KEY)) -} - export async function getV3WalletsStorage() { return await storage.get>(V3_WALLETS_STORAGE_KEY, { defaultValue: {}, }) } +export async function getV3Wallets() { + return Object.values(await getV3WalletsStorage()) +} + +export async function setV3WalletsStorage(wallets: Record) { + await storage.set(V3_WALLETS_STORAGE_KEY, JSON.stringify(wallets)) +} + +export async function hasV3EncryptedWallets(): Promise { + return !!(await storage.get>(V3_ENCRYPTED_WALLETS_STORAGE_KEY)) +} + export async function getV3EncryptedWalletsStorage() { - return await storage.get>(V3_WALLETS_STORAGE_KEY, { + return await storage.get>(V3_ENCRYPTED_WALLETS_STORAGE_KEY, { defaultValue: {}, }) } -export async function getV3EncryptedWallets() { - return Object.values(await getV3EncryptedWalletsStorage()) +export async function setV3EncryptedWalletsStorage(wallets: Record) { + await storage.set(V3_ENCRYPTED_WALLETS_STORAGE_KEY, JSON.stringify(wallets)) } -export async function getV3Wallets() { - return Object.values(await getV3WalletsStorage()) +export async function backupV3EncryptedWalletsStorage() { + const wallets = await getV3EncryptedWalletsStorage() + await storage.set(V3_ENCRYPTED_WALLETS_STORAGE_BACKUP_KEY, JSON.stringify(wallets)) +} + +export async function getV3EncryptedWallets() { + return Object.values(await getV3EncryptedWalletsStorage()) } export async function getV3WalletsNum() { - const wallets = await getV3Wallets() + const wallets = await getV3EncryptedWallets() return await storage.get(WALLET_NUM, { defaultValue: wallets.length, }) @@ -50,25 +63,27 @@ export async function setV3WalletsNum(num: number) { } export async function addV3Wallet(wallet: V3Wallet) { - const v3Wallet = await getV3Wallets() + const v3Wallet = await getV3EncryptedWallets() if (v3Wallet.find((w) => w.mnemonic === wallet.mnemonic)) { throw new Error('Wallet already exists') } - const wallets = await getV3WalletsStorage() + const wallets = await getV3EncryptedWalletsStorage() wallets[wallet.id] = wallet - await setV3WalletsStorage(wallets) -} - -export async function setV3WalletsStorage(wallets: Record) { - await storage.set(V3_WALLETS_STORAGE_KEY, JSON.stringify(wallets)) -} - -export async function setV3EncryptedWalletsStorage(wallets: Record) { - await storage.set(V3_ENCRYPTED_WALLETS_STORAGE_KEY, JSON.stringify(wallets)) + await setV3EncryptedWalletsStorage(wallets) } export async function getCurrentWalletId() { - return await storage.get(CURRENT_WALLET_ID) + const currentWalletId = await storage.get(CURRENT_WALLET_ID) + if (!currentWalletId) { + const wallets = await getV3EncryptedWallets() + if (wallets.length) { + await setCurrentWalletId(wallets[0].id) + return wallets[0].id + } else { + throw new Error('current wallet id not found') + } + } + return currentWalletId } export async function setCurrentWalletId(walletId: string) { @@ -80,7 +95,7 @@ export async function getV3CurrentWallet() { if (!walletId) { throw new Error('current wallet id not found') } - const wallets = await getV3Wallets() + const wallets = await getV3EncryptedWallets() if (!wallets.length) { throw new Error('wallets not found') } @@ -95,7 +110,7 @@ export async function getWalletOnlyAccount(walletId: string, accountId: string) if (!walletId) { throw new Error('wallet id not found') } - const wallets = await getV3Wallets() + const wallets = await getV3EncryptedWallets() if (!wallets.length) { throw new Error('wallets not found') } @@ -122,13 +137,7 @@ export async function getWalletOnlyAccount(walletId: string, accountId: string) export async function getActiveWalletOnlyAccount() { const currentWalletId = await getCurrentWalletId() - if (!currentWalletId) { - throw new Error('current wallet id not found') - } const currentAccountId = await getCurrentAccountId() - if (!currentAccountId) { - throw new Error('current account id not found') - } return getWalletOnlyAccount(currentWalletId, currentAccountId) } @@ -137,7 +146,7 @@ export async function getInactiveWallets() { if (!currentWalletId) { throw new Error('Current wallet id not found.') } - const wallets = await getV3Wallets() + const wallets = await getV3EncryptedWallets() if (!wallets.length) { throw new Error('No wallets found. Please create a wallet first.') } @@ -148,7 +157,7 @@ export async function getWalletOtherAccounts(walletId: string, accountId: string if (!walletId) { throw new Error('wallet id not found') } - const wallets = await getV3Wallets() + const wallets = await getV3EncryptedWallets() if (!wallets.length) { throw new Error('wallets not found') } @@ -183,7 +192,7 @@ export async function getActiveWalletOtherAccounts() { export async function getV3CurrentAccount() { const walletId = await getCurrentWalletId() - const wallets = await getV3Wallets() + const wallets = await getV3EncryptedWallets() if (!walletId || !wallets.length) { throw new Error('current wallet id not found') } @@ -208,7 +217,7 @@ export async function getV3CurrentAccount() { } export async function updateWalletName(walletId: string, name: string) { - const walletsMap = await getV3WalletsStorage() + const walletsMap = await getV3EncryptedWalletsStorage() if (!walletsMap) { throw new Error('V3 wallets storage not found.') } @@ -217,11 +226,11 @@ export async function updateWalletName(walletId: string, name: string) { throw new Error(`Wallet not found with id ${walletId}.`) } wallet.name = name - await setV3WalletsStorage(walletsMap) + await setV3EncryptedWalletsStorage(walletsMap) } export async function updateAccountName(walletId: string, accountId: string, name: string) { - const walletsMap = await getV3WalletsStorage() + const walletsMap = await getV3EncryptedWalletsStorage() if (!walletsMap) { throw new Error('V3 wallets storage not found.') } @@ -238,11 +247,11 @@ export async function updateAccountName(walletId: string, accountId: string, nam throw new Error(`Account not found with id ${accountId}.`) } account.name = name - await setV3WalletsStorage(walletsMap) + await setV3EncryptedWalletsStorage(walletsMap) } export async function hasWallets() { - const wallets = await getV3Wallets() + const wallets = await getV3EncryptedWallets() return !!wallets.length } @@ -269,10 +278,10 @@ export async function getCurrentWallet(chain: T, _addressIndex? } export async function deleteV3Wallet(walletId: string) { - const walletsMap = await getV3WalletsStorage() + const walletsMap = await getV3EncryptedWalletsStorage() if (!walletsMap) { throw new Error('V3 wallets storage not found.') } delete walletsMap[walletId] - await setV3WalletsStorage(walletsMap) + await setV3EncryptedWalletsStorage(walletsMap) } diff --git a/src/pages/manage/Edit.vue b/src/pages/manage/Edit.vue index 3dfc8dfb..a8d2a3ad 100644 --- a/src/pages/manage/Edit.vue +++ b/src/pages/manage/Edit.vue @@ -12,7 +12,7 @@ import PencilIcon from '@/assets/icons-v3/pencil.svg' import { EllipsisHorizontalIcon } from '@heroicons/vue/24/solid' import { useChainWalletsStore } from '@/stores/ChainWalletsStore' import { getCurrentAccountId, setCurrentAccountId } from '@/lib/account' -import { deleteV3Wallet, getCurrentWalletId, setCurrentWalletId, getV3WalletsStorage, getV3Wallets } from '@/lib/wallet' +import { deleteV3Wallet, getCurrentWalletId, setCurrentWalletId, getV3WalletsStorage, getV3EncryptedWallets } from '@/lib/wallet' const { updateAllWallets } = useChainWalletsStore() @@ -72,7 +72,7 @@ const updataAccountName = (walletId: string, accountId: string, accountName: str const deleteWallet = async (walletId: string) => { await deleteV3Wallet(walletId) - const wallets = await getV3Wallets() + const wallets = await getV3EncryptedWallets() if (wallets.length) { await setCurrentWalletId(wallets[0].id) await setCurrentAccountId(wallets[0].accounts[0].id) @@ -88,13 +88,8 @@ const deleteWallet = async (walletId: string) => {