Skip to content

Commit df2e585

Browse files
committed
Remove migration code for old client-side wallets
1 parent b4298ca commit df2e585

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
@@ -32,17 +32,16 @@ import {
3232
} from '@/wallets/client/fragments'
3333
import { gql, useApolloClient, useMutation, useQuery } from '@apollo/client'
3434
import { useDecryption, useEncryption, useSetKey, useWalletLoggerFactory, useWalletsUpdatedAt, WalletStatus } from '@/wallets/client/hooks'
35-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
35+
import { useCallback, useEffect, useMemo, useState } from 'react'
3636
import {
37-
isEncryptedField, isTemplate, isWallet, protocolAvailable, protocolClientSchema, protocolLogName, reverseProtocolRelationName,
38-
walletLud16Domain
37+
isEncryptedField, isTemplate, isWallet, protocolAvailable, protocolLogName, reverseProtocolRelationName, walletLud16Domain
3938
} from '@/wallets/lib/util'
4039
import { protocolTestSendPayment } from '@/wallets/client/protocols'
4140
import { timeoutSignal } from '@/lib/time'
4241
import { FAST_POLL_INTERVAL_MS, WALLET_SEND_PAYMENT_TIMEOUT_MS } from '@/lib/constants'
4342
import { useToast } from '@/components/toast'
4443
import { useMe } from '@/components/me'
45-
import { useTemplates, useWallets, useWalletsLoading } from '@/wallets/client/context'
44+
import { useTemplates, useWallets } from '@/wallets/client/context'
4645
import { requestPersistentStorage } from '@/components/use-indexeddb'
4746

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

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

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

0 commit comments

Comments
 (0)