diff --git a/wallets/client/context/hooks.js b/wallets/client/context/hooks.js index 5d1001eff8..8b05ab02c7 100644 --- a/wallets/client/context/hooks.js +++ b/wallets/client/context/hooks.js @@ -1,9 +1,7 @@ import { useEffect, useState } from 'react' import { useMe } from '@/components/me' import { - useWalletsQuery, useGenerateRandomKey, useSetKey, useLoadKey, useLoadOldKey, - useWalletMigrationMutation, CryptoKeyRequiredError, useIsWrongKey, - useWalletLogger + useWalletsQuery, useGenerateRandomKey, useSetKey, useIsWrongKey, useWalletLogger, useDeleteOldDb } from '@/wallets/client/hooks' import { SET_WALLETS, WRONG_KEY, KEY_MATCH, useWalletsDispatch, WALLETS_QUERY_ERROR, KEY_STORAGE_UNAVAILABLE } from '@/wallets/client/context' import { useIndexedDB } from '@/components/use-indexeddb' @@ -43,8 +41,7 @@ export function useKeyInit () { const generateRandomKey = useGenerateRandomKey() const setKey = useSetKey() - const loadKey = useLoadKey() - const loadOldKey = useLoadOldKey() + const deleteOldDb = useDeleteOldDb() const [db, setDb] = useState(null) const { open } = useIndexedDB() @@ -73,13 +70,12 @@ export function useKeyInit () { async function keyInit () { try { - // TODO(wallet-v2): remove migration code - // and delete the old IndexedDB after wallet v2 has been released for some time + // delete the old IndexedDB since wallet v2 has been released 2 months ago + await deleteOldDb() - // load old key and create random key before opening transaction in case we need them + // create random key before opening transaction in case we need it // because we can't run async code in a transaction because it will close the transaction // see https://javascript.info/indexeddb#transactions-autocommit - const oldKeyAndHash = await loadOldKey() const { key: randomKey, hash: randomHash } = await generateRandomKey() // run read and write in one transaction to avoid race conditions @@ -99,12 +95,6 @@ export function useKeyInit () { return resolve(read.result) } - if (oldKeyAndHash) { - // return key+hash found in old db - logger.debug('key init: key found in old IndexedDB') - return resolve(oldKeyAndHash) - } - // no key found, write and return generated random key const updatedAt = Date.now() const write = tx.objectStore('vault').put({ key: randomKey, hash: randomHash, updatedAt }, 'key') @@ -129,52 +119,19 @@ export function useKeyInit () { } } keyInit() - }, [me?.id, db, generateRandomKey, loadOldKey, setKey, loadKey, logger]) + }, [me?.id, db, deleteOldDb, generateRandomKey, setKey, logger]) } -// TODO(wallet-v2): remove migration code -// ============================================================= -// ****** Below is the migration code for WALLET v1 -> v2 ****** -// remove when we can assume migration is complete (if ever) -// ============================================================= - -export function useWalletMigration () { +export function useDeleteLocalWallets () { const { me } = useMe() - const { migrate: walletMigration, ready } = useWalletMigrationMutation() useEffect(() => { - if (!me?.id || !ready) return - - async function migrate () { - const localWallets = Object.entries(window.localStorage) - .filter(([key]) => key.startsWith('wallet:')) - .filter(([key]) => key.split(':').length < 3 || key.endsWith(me.id)) - .reduce((acc, [key, value]) => { - try { - const config = JSON.parse(value) - acc.push({ key, ...config }) - } catch (err) { - console.error(`useLocalWallets: ${key}: invalid JSON:`, err) - } - return acc - }, []) - - await Promise.allSettled( - localWallets.map(async ({ key, ...localWallet }) => { - const name = key.split(':')[1].toUpperCase() - try { - await walletMigration({ ...localWallet, name }) - window.localStorage.removeItem(key) - } catch (err) { - if (err instanceof CryptoKeyRequiredError) { - // key not set yet, skip this wallet - return - } - console.error(`${name}: wallet migration failed:`, err) - } - }) - ) - } - migrate() - }, [ready, me?.id, walletMigration]) + if (!me?.id) return + + // we used to store wallets locally so this makes sure we delete them if there are any left over + Object.keys(window.localStorage) + .filter((key) => key.startsWith('wallet:')) + .filter((key) => key.split(':').length < 3 || key.endsWith(me.id)) + .forEach((key) => window.localStorage.removeItem(key)) + }, [me?.id]) } diff --git a/wallets/client/context/provider.js b/wallets/client/context/provider.js index d7bf1a66f6..b3cdc30a35 100644 --- a/wallets/client/context/provider.js +++ b/wallets/client/context/provider.js @@ -1,6 +1,6 @@ import { createContext, useContext, useReducer } from 'react' import walletsReducer from './reducer' -import { useServerWallets, useKeyInit, useWalletMigration } from './hooks' +import { useServerWallets, useKeyInit, useDeleteLocalWallets } from './hooks' import { useAutoRetryPayIns } from '@/components/payIn/hooks/use-auto-retry-pay-ins' import { WebLnProvider } from '@/wallets/lib/protocols/webln' @@ -81,14 +81,7 @@ function WalletHooks ({ children }) { useServerWallets() useAutoRetryPayIns() useKeyInit() - - // TODO(wallet-v2): remove migration code - // ============================================================= - // ****** Below is the migration code for WALLET v1 -> v2 ****** - // remove when we can assume migration is complete (if ever) - // ============================================================= - - useWalletMigration() + useDeleteLocalWallets() return children } diff --git a/wallets/client/hooks/crypto.js b/wallets/client/hooks/crypto.js index b4b5d16437..ff190bde67 100644 --- a/wallets/client/hooks/crypto.js +++ b/wallets/client/hooks/crypto.js @@ -19,22 +19,14 @@ export class CryptoKeyRequiredError extends Error { } } -export function useLoadKey () { - const { get } = useIndexedDB() - - return useCallback(async () => { - return await get('vault', 'key') - }, [get]) -} - -export function useLoadOldKey () { +export function useDeleteOldDb () { const { me } = useMe() const oldDbName = me?.id ? `app:storage:${me?.id}:vault` : undefined - const { get } = useIndexedDB(oldDbName) + const { deleteDb } = useIndexedDB(oldDbName) return useCallback(async () => { - return await get('vault', 'key') - }, [get]) + return await deleteDb() + }, [deleteDb]) } export function useSetKey () { diff --git a/wallets/client/hooks/query.js b/wallets/client/hooks/query.js index acb06678cc..82e9dddb08 100644 --- a/wallets/client/hooks/query.js +++ b/wallets/client/hooks/query.js @@ -32,17 +32,16 @@ import { } from '@/wallets/client/fragments' import { gql, useApolloClient, useMutation, useQuery } from '@apollo/client' import { useDecryption, useEncryption, useSetKey, useWalletLoggerFactory, useWalletsUpdatedAt, WalletStatus } from '@/wallets/client/hooks' -import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { - isEncryptedField, isTemplate, isWallet, protocolAvailable, protocolClientSchema, protocolLogName, reverseProtocolRelationName, - walletLud16Domain + isEncryptedField, isTemplate, isWallet, protocolAvailable, protocolLogName, reverseProtocolRelationName, walletLud16Domain } from '@/wallets/lib/util' import { protocolTestSendPayment } from '@/wallets/client/protocols' import { timeoutSignal } from '@/lib/time' import { FAST_POLL_INTERVAL_MS, WALLET_SEND_PAYMENT_TIMEOUT_MS } from '@/lib/constants' import { useToast } from '@/components/toast' import { useMe } from '@/components/me' -import { useTemplates, useWallets, useWalletsLoading } from '@/wallets/client/context' +import { useTemplates, useWallets } from '@/wallets/client/context' import { requestPersistentStorage } from '@/components/use-indexeddb' export function useWalletsQuery () { @@ -452,71 +451,6 @@ function useEncryptConfig (defaultProtocol, options = {}) { return useMemo(() => ({ encryptConfig, ready }), [encryptConfig, ready]) } -// TODO(wallet-v2): remove migration code -// ============================================================= -// ****** Below is the migration code for WALLET v1 -> v2 ****** -// remove when we can assume migration is complete (if ever) -// ============================================================= - -export function useWalletMigrationMutation () { - const wallets = useWallets() - const loading = useWalletsLoading() - const client = useApolloClient() - const { encryptConfig, ready } = useEncryptConfig() - - // XXX We use a ref for the wallets to avoid duplicate wallets - // Without a ref, the migrate callback would depend on the wallets and thus update every time the migration creates a wallet. - // This update would then cause the useEffect in wallets/client/context/hooks that triggers the migration to run again before the first migration is complete. - const walletsRef = useRef(wallets) - useEffect(() => { - if (!loading) walletsRef.current = wallets - }, [loading]) - - const migrate = useCallback(async ({ name, enabled, ...configV1 }) => { - const protocol = { name, send: true } - - const configV2 = migrateConfig(protocol, configV1) - - const isSameProtocol = (p) => { - const sameName = p.name === protocol.name - const sameSend = p.send === protocol.send - const sameConfig = Object.keys(p.config) - .filter(k => !['__typename', 'id'].includes(k)) - .every(k => p.config[k] === configV2[k]) - return sameName && sameSend && sameConfig - } - - const exists = walletsRef.current.some(w => w.name === name && w.protocols.some(isSameProtocol)) - if (exists) return - - const schema = protocolClientSchema(protocol) - await schema.validate(configV2) - - const encrypted = await encryptConfig(configV2, { protocol }) - - // decide if we create a new wallet (templateName) or use an existing one (walletId) - const templateName = getWalletTemplateName(protocol) - let walletId - const wallet = walletsRef.current.find(w => - w.name === name && !w.protocols.some(p => p.name === protocol.name && p.send) - ) - if (wallet) { - walletId = Number(wallet.id) - } - - await client.mutate({ - mutation: protocolUpsertMutation(protocol), - variables: { - ...(walletId ? { walletId } : { templateName }), - enabled, - ...encrypted - } - }) - }, [client, encryptConfig]) - - return useMemo(() => ({ migrate, ready: ready && !loading }), [migrate, ready, loading]) -} - export function useUpdateKeyHash () { const [mutate] = useMutation(UPDATE_KEY_HASH) @@ -524,55 +458,3 @@ export function useUpdateKeyHash () { await mutate({ variables: { keyHash } }) }, [mutate]) } - -function migrateConfig (protocol, config) { - switch (protocol.name) { - case 'LNBITS': - return { - url: config.url, - apiKey: config.adminKey - } - case 'PHOENIXD': - return { - url: config.url, - apiKey: config.primaryPassword - } - case 'BLINK': - return { - url: config.url, - apiKey: config.apiKey, - currency: config.currency - } - case 'LNC': - return { - pairingPhrase: config.pairingPhrase, - localKey: config.localKey, - remoteKey: config.remoteKey, - serverHost: config.serverHost - } - case 'WEBLN': - return {} - case 'NWC': - return { - url: config.nwcUrl - } - default: - return config - } -} - -function getWalletTemplateName (protocol) { - switch (protocol.name) { - case 'LNBITS': - case 'PHOENIXD': - case 'BLINK': - case 'NWC': - return protocol.name - case 'LNC': - return 'LND' - case 'WEBLN': - return 'ALBY' - default: - return null - } -}