Skip to content

Commit c0c7aca

Browse files
committed
Remove migration code for old client-side wallets
1 parent 288892e commit c0c7aca

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
@@ -18,22 +18,14 @@ export class CryptoKeyRequiredError extends Error {
1818
}
1919
}
2020

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

3426
return useCallback(async () => {
35-
return await get('vault', 'key')
36-
}, [get])
27+
return await deleteDb()
28+
}, [deleteDb])
3729
}
3830

3931
export function useSetKey () {

wallets/client/hooks/query.js

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

4948
export function useWalletsQuery () {
@@ -453,127 +452,10 @@ function useEncryptConfig (defaultProtocol, options = {}) {
453452
return useMemo(() => ({ encryptConfig, ready }), [encryptConfig, ready])
454453
}
455454

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

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

0 commit comments

Comments
 (0)