Skip to content

Commit 005caa9

Browse files
committed
Remove migration code for old client-side wallets
1 parent 919e71d commit 005caa9

File tree

4 files changed

+24
-200
lines changed

4 files changed

+24
-200
lines changed

wallets/client/context/hooks.js

Lines changed: 15 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ import { NORMAL_POLL_INTERVAL_MS } from '@/lib/constants'
55
import useInvoice from '@/components/use-invoice'
66
import { useMe } from '@/components/me'
77
import {
8-
useWalletsQuery, useWalletPayment, useGenerateRandomKey, useSetKey, useLoadKey, useLoadOldKey,
9-
useWalletMigrationMutation, CryptoKeyRequiredError, useIsWrongKey,
10-
useWalletLogger
8+
useWalletsQuery, useWalletPayment, useGenerateRandomKey, useSetKey, useIsWrongKey, useWalletLogger, useDeleteOldDb
119
} from '@/wallets/client/hooks'
1210
import { WalletConfigurationError } from '@/wallets/client/errors'
1311
import { SET_WALLETS, WRONG_KEY, KEY_MATCH, useWalletsDispatch, WALLETS_QUERY_ERROR, KEY_STORAGE_UNAVAILABLE } from '@/wallets/client/context'
@@ -120,8 +118,7 @@ export function useKeyInit () {
120118

121119
const generateRandomKey = useGenerateRandomKey()
122120
const setKey = useSetKey()
123-
const loadKey = useLoadKey()
124-
const loadOldKey = useLoadOldKey()
121+
const deleteOldDb = useDeleteOldDb()
125122
const [db, setDb] = useState(null)
126123
const { open } = useIndexedDB()
127124

@@ -150,13 +147,12 @@ export function useKeyInit () {
150147

151148
async function keyInit () {
152149
try {
153-
// TODO(wallet-v2): remove migration code
154-
// and delete the old IndexedDB after wallet v2 has been released for some time
150+
// delete the old IndexedDB since wallet v2 has been released 2 months ago
151+
await deleteOldDb()
155152

156-
// load old key and create random key before opening transaction in case we need them
153+
// create random key before opening transaction in case we need it
157154
// because we can't run async code in a transaction because it will close the transaction
158155
// see https://javascript.info/indexeddb#transactions-autocommit
159-
const oldKeyAndHash = await loadOldKey()
160156
const { key: randomKey, hash: randomHash } = await generateRandomKey()
161157

162158
// run read and write in one transaction to avoid race conditions
@@ -176,12 +172,6 @@ export function useKeyInit () {
176172
return resolve(read.result)
177173
}
178174

179-
if (oldKeyAndHash) {
180-
// return key+hash found in old db
181-
logger.debug('key init: key found in old IndexedDB')
182-
return resolve(oldKeyAndHash)
183-
}
184-
185175
// no key found, write and return generated random key
186176
const updatedAt = Date.now()
187177
const write = tx.objectStore('vault').put({ key: randomKey, hash: randomHash, updatedAt }, 'key')
@@ -206,52 +196,19 @@ export function useKeyInit () {
206196
}
207197
}
208198
keyInit()
209-
}, [me?.id, db, generateRandomKey, loadOldKey, setKey, loadKey, logger])
199+
}, [me?.id, db, deleteOldDb, generateRandomKey, setKey, logger])
210200
}
211201

212-
// TODO(wallet-v2): remove migration code
213-
// =============================================================
214-
// ****** Below is the migration code for WALLET v1 -> v2 ******
215-
// remove when we can assume migration is complete (if ever)
216-
// =============================================================
217-
218-
export function useWalletMigration () {
202+
export function useDeleteLocalWallets () {
219203
const { me } = useMe()
220-
const { migrate: walletMigration, ready } = useWalletMigrationMutation()
221204

222205
useEffect(() => {
223-
if (!me?.id || !ready) return
224-
225-
async function migrate () {
226-
const localWallets = Object.entries(window.localStorage)
227-
.filter(([key]) => key.startsWith('wallet:'))
228-
.filter(([key]) => key.split(':').length < 3 || key.endsWith(me.id))
229-
.reduce((acc, [key, value]) => {
230-
try {
231-
const config = JSON.parse(value)
232-
acc.push({ key, ...config })
233-
} catch (err) {
234-
console.error(`useLocalWallets: ${key}: invalid JSON:`, err)
235-
}
236-
return acc
237-
}, [])
238-
239-
await Promise.allSettled(
240-
localWallets.map(async ({ key, ...localWallet }) => {
241-
const name = key.split(':')[1].toUpperCase()
242-
try {
243-
await walletMigration({ ...localWallet, name })
244-
window.localStorage.removeItem(key)
245-
} catch (err) {
246-
if (err instanceof CryptoKeyRequiredError) {
247-
// key not set yet, skip this wallet
248-
return
249-
}
250-
console.error(`${name}: wallet migration failed:`, err)
251-
}
252-
})
253-
)
254-
}
255-
migrate()
256-
}, [ready, me?.id, walletMigration])
206+
if (!me?.id) return
207+
208+
// we used to store wallets locally so this makes sure we delete them if there are any left over
209+
Object.keys(window.localStorage)
210+
.filter((key) => key.startsWith('wallet:'))
211+
.filter((key) => key.split(':').length < 3 || key.endsWith(me.id))
212+
.forEach((key) => window.localStorage.removeItem(key))
213+
}, [me?.id])
257214
}

wallets/client/context/provider.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createContext, useContext, useReducer } from 'react'
22
import walletsReducer from './reducer'
3-
import { useServerWallets, useAutomatedRetries, useKeyInit, useWalletMigration } from './hooks'
3+
import { useServerWallets, useAutomatedRetries, useKeyInit, useDeleteLocalWallets } from './hooks'
44
import { WebLnProvider } from '@/wallets/lib/protocols/webln'
55

66
// https://react.dev/learn/scaling-up-with-reducer-and-context
@@ -80,14 +80,7 @@ function WalletHooks ({ children }) {
8080
useServerWallets()
8181
useAutomatedRetries()
8282
useKeyInit()
83-
84-
// TODO(wallet-v2): remove migration code
85-
// =============================================================
86-
// ****** Below is the migration code for WALLET v1 -> v2 ******
87-
// remove when we can assume migration is complete (if ever)
88-
// =============================================================
89-
90-
useWalletMigration()
83+
useDeleteLocalWallets()
9184

9285
return children
9386
}

wallets/client/hooks/crypto.js

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,14 @@ export class CryptoKeyRequiredError extends Error {
1919
}
2020
}
2121

22-
export function useLoadKey () {
23-
const { get } = useIndexedDB()
24-
25-
return useCallback(async () => {
26-
return await get('vault', 'key')
27-
}, [get])
28-
}
29-
30-
export function useLoadOldKey () {
22+
export function useDeleteOldDb () {
3123
const { me } = useMe()
3224
const oldDbName = me?.id ? `app:storage:${me?.id}:vault` : undefined
33-
const { get } = useIndexedDB(oldDbName)
25+
const { deleteDb } = useIndexedDB(oldDbName)
3426

3527
return useCallback(async () => {
36-
return await get('vault', 'key')
37-
}, [get])
28+
return await deleteDb()
29+
}, [deleteDb])
3830
}
3931

4032
export function useSetKey () {

wallets/client/hooks/query.js

Lines changed: 3 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,16 @@ import {
3030
} from '@/wallets/client/fragments'
3131
import { gql, useApolloClient, useMutation, useQuery } from '@apollo/client'
3232
import { useDecryption, useEncryption, useSetKey, useWalletLoggerFactory, useWalletsUpdatedAt, WalletStatus } from '@/wallets/client/hooks'
33-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
33+
import { useCallback, useEffect, useMemo, useState } from 'react'
3434
import {
35-
isEncryptedField, isTemplate, isWallet, protocolAvailable, protocolClientSchema, protocolLogName, reverseProtocolRelationName,
36-
walletLud16Domain
35+
isEncryptedField, isTemplate, isWallet, protocolAvailable, protocolLogName, reverseProtocolRelationName, walletLud16Domain
3736
} from '@/wallets/lib/util'
3837
import { protocolTestSendPayment } from '@/wallets/client/protocols'
3938
import { timeoutSignal } from '@/lib/time'
4039
import { FAST_POLL_INTERVAL_MS, WALLET_SEND_PAYMENT_TIMEOUT_MS } from '@/lib/constants'
4140
import { useToast } from '@/components/toast'
4241
import { useMe } from '@/components/me'
43-
import { useTemplates, useWallets, useWalletsLoading } from '@/wallets/client/context'
42+
import { useTemplates, useWallets } from '@/wallets/client/context'
4443
import { requestPersistentStorage } from '@/components/use-indexeddb'
4544

4645
export function useWalletsQuery () {
@@ -446,127 +445,10 @@ function useEncryptConfig (defaultProtocol, options = {}) {
446445
return useMemo(() => ({ encryptConfig, ready }), [encryptConfig, ready])
447446
}
448447

449-
// TODO(wallet-v2): remove migration code
450-
// =============================================================
451-
// ****** Below is the migration code for WALLET v1 -> v2 ******
452-
// remove when we can assume migration is complete (if ever)
453-
// =============================================================
454-
455-
export function useWalletMigrationMutation () {
456-
const wallets = useWallets()
457-
const loading = useWalletsLoading()
458-
const client = useApolloClient()
459-
const { encryptConfig, ready } = useEncryptConfig()
460-
461-
// XXX We use a ref for the wallets to avoid duplicate wallets
462-
// Without a ref, the migrate callback would depend on the wallets and thus update every time the migration creates a wallet.
463-
// 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.
464-
const walletsRef = useRef(wallets)
465-
useEffect(() => {
466-
if (!loading) walletsRef.current = wallets
467-
}, [loading])
468-
469-
const migrate = useCallback(async ({ name, enabled, ...configV1 }) => {
470-
const protocol = { name, send: true }
471-
472-
const configV2 = migrateConfig(protocol, configV1)
473-
474-
const isSameProtocol = (p) => {
475-
const sameName = p.name === protocol.name
476-
const sameSend = p.send === protocol.send
477-
const sameConfig = Object.keys(p.config)
478-
.filter(k => !['__typename', 'id'].includes(k))
479-
.every(k => p.config[k] === configV2[k])
480-
return sameName && sameSend && sameConfig
481-
}
482-
483-
const exists = walletsRef.current.some(w => w.name === name && w.protocols.some(isSameProtocol))
484-
if (exists) return
485-
486-
const schema = protocolClientSchema(protocol)
487-
await schema.validate(configV2)
488-
489-
const encrypted = await encryptConfig(configV2, { protocol })
490-
491-
// decide if we create a new wallet (templateName) or use an existing one (walletId)
492-
const templateName = getWalletTemplateName(protocol)
493-
let walletId
494-
const wallet = walletsRef.current.find(w =>
495-
w.name === name && !w.protocols.some(p => p.name === protocol.name && p.send)
496-
)
497-
if (wallet) {
498-
walletId = Number(wallet.id)
499-
}
500-
501-
await client.mutate({
502-
mutation: protocolUpsertMutation(protocol),
503-
variables: {
504-
...(walletId ? { walletId } : { templateName }),
505-
enabled,
506-
...encrypted
507-
}
508-
})
509-
}, [client, encryptConfig])
510-
511-
return useMemo(() => ({ migrate, ready: ready && !loading }), [migrate, ready, loading])
512-
}
513-
514448
export function useUpdateKeyHash () {
515449
const [mutate] = useMutation(UPDATE_KEY_HASH)
516450

517451
return useCallback(async (keyHash) => {
518452
await mutate({ variables: { keyHash } })
519453
}, [mutate])
520454
}
521-
522-
function migrateConfig (protocol, config) {
523-
switch (protocol.name) {
524-
case 'LNBITS':
525-
return {
526-
url: config.url,
527-
apiKey: config.adminKey
528-
}
529-
case 'PHOENIXD':
530-
return {
531-
url: config.url,
532-
apiKey: config.primaryPassword
533-
}
534-
case 'BLINK':
535-
return {
536-
url: config.url,
537-
apiKey: config.apiKey,
538-
currency: config.currency
539-
}
540-
case 'LNC':
541-
return {
542-
pairingPhrase: config.pairingPhrase,
543-
localKey: config.localKey,
544-
remoteKey: config.remoteKey,
545-
serverHost: config.serverHost
546-
}
547-
case 'WEBLN':
548-
return {}
549-
case 'NWC':
550-
return {
551-
url: config.nwcUrl
552-
}
553-
default:
554-
return config
555-
}
556-
}
557-
558-
function getWalletTemplateName (protocol) {
559-
switch (protocol.name) {
560-
case 'LNBITS':
561-
case 'PHOENIXD':
562-
case 'BLINK':
563-
case 'NWC':
564-
return protocol.name
565-
case 'LNC':
566-
return 'LND'
567-
case 'WEBLN':
568-
return 'ALBY'
569-
default:
570-
return null
571-
}
572-
}

0 commit comments

Comments
 (0)