Skip to content

fix: wipe during VSS setup re-caches the old wallet's store id #1331

Description

@jvsena42

Found during review of #1246 (the 2.5.0 back-merge). The code arrived via #1266 and is on master; nothing here blocks that PR.

What happens

WipeWalletUseCase.stopNode() calls backupRepo.reset() as its first action, which resets both VSS clients and clears the store-id cache. If a VSS setup() is still in flight at that moment, the wipe leaves the old wallet's store id cached again, and it survives until the process dies.

Sequence:

  1. BackupRepo.startObservingBackups() launches vssBackupClient.setupWithRetry { … } in an untracked scope.launch. stopObservingBackups() cancels backupJobs, statusObserverJobs, dataListenerJobs and periodicCheckJob, but not this one.
  2. setup() captures val gate = isSetup, reads the store id, and enters withTimeout(30.seconds) around the network call.
  3. The user starts a wipe. reset() runs isSetup.cancel(), installs a fresh deferred under synchronized(this) (while setup() holds setupMutex, a different lock), and calls vssStoreIdProvider.clearCache().
  4. The network call returns. gate.complete(Unit) is a no-op on the cancelled deferred, so runCatching still succeeds, onFailure never runs, and the isSetup === gate swap is skipped. setup() returns Result.success with the live deferred never completed.
  5. That success runs .onSuccess { scope.launch { vssBackupClientLdk.setup() } }, which calls vssStoreIdProvider.getVssStoreId(walletIndex) again.
  6. keychain.wipe() has not run yet — it comes after lightningRepo.stop() (up to 30 s) and cleanupRemote() — so the old mnemonic is still readable and the old store id is derived and cached.
  7. clearCache() is only ever called from VssBackupClient.reset(), so nothing clears it afterwards.

Why it matters

If the user creates or restores a wallet in the same process, LightningService calls vssStoreIdProvider.getVssStoreId(walletIndex) and gets the old wallet's store id, then builder.buildWithVssStore(vssUrl, vssStoreId, lnurlAuthServerUrl, …). The new node is built against the old wallet's VSS store, and VssBackupClientLdk.setup() early-returns on the already-completed gate, so the LDK deletes in LightningRepo point there too.

Unverified: whether the VSS server binds a store id to the LNURL-auth identity. If it does, the new node fails to start until the app is restarted. If it does not, the new wallet's LDK state is written under the old wallet's store id.

The orphaned deferred on its own is harmless: every real caller runs setup() again before awaiting, so nothing hangs. The store-id re-caching is the part worth fixing.

Reachability

Low but real: the user has to reach Settings → Back up or reset → Reset and Restore → Reset while the first setup() after launch is still running. That window is a few seconds on a healthy network and up to 30 s on a slow one, which is exactly when the retry loop exists.

Suggested fix

One line in each client, on the success path — VssBackupClient.kt and VssBackupClientLdk.kt:

check(gate.complete(Unit)) { "VSS client reset during setup" }

A reset that lands mid-setup then turns the call into a failure: onFailure no-ops on the cancelled gate, the isSetup === gate swap is correctly skipped, setupWithRetry reports the failure, and the LDK setup launch never fires — so the old store id is never re-derived.

Worth pairing with either of:

  • clearing the store-id cache on the wallet create/restore path, so a stale entry can never outlive a wipe;
  • assigning that scope.launch to a job and cancelling it in stopObservingBackups().

Files

  • app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt — setup(), reset()
  • app/src/main/java/to/bitkit/data/backup/VssBackupClientLdk.kt — same shape
  • app/src/main/java/to/bitkit/data/backup/VssStoreIdProvider.kt — getVssStoreId(), clearCache()
  • app/src/main/java/to/bitkit/repositories/BackupRepo.kt — startObservingBackups(), stopObservingBackups()
  • app/src/main/java/to/bitkit/usecases/WipeWalletUseCase.kt — stopNode(), wipeLocal()
  • app/src/main/java/to/bitkit/services/LightningService.kt — buildWithVssStore call site

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions