Skip to content

fix(wallet): close last QA-20 hole — strict marker read in exists()#103

Merged
epicexcelsior merged 1 commit into
anonmesh:stagingfrom
epicexcelsior:night3/wallet-guards
Jun 13, 2026
Merged

fix(wallet): close last QA-20 hole — strict marker read in exists()#103
epicexcelsior merged 1 commit into
anonmesh:stagingfrom
epicexcelsior:night3/wallet-guards

Conversation

@epicexcelsior

Copy link
Copy Markdown
Collaborator

What

Closes the residual QA-20 funds-safety hole. A transient Keystore read failure could make LocalWallet.exists() return false, letting the create path overwrite a funded wallet's secret.

  • exists() now reads the wallet marker via secureGetStrict (throws on read failure) instead of the swallow-to-null secureGet, so a transient outage surfaces as an error rather than a false "absent".
  • Both callers handle it: WalletFactory.hasLocalWallet treats unknown-presence as present and routes to connect (never create); createLocal re-throws without writing. The sole create() path now requires a verified-absent wallet.

Scope

2 files: LocalWallet.ts, WalletFactory.ts (+44/-5). secureGetStrict already on staging.

Test

tsc clean · tier0 config/services clean · fake-money + lint clean. Code-path fix; invisible until the day it stops an overwrite of a funded wallet.

QA-20's fix (88bfd5e) stopped hasLocalWallet()/createLocal() deleting on a
keychain read error, but both gate on LocalWallet.exists(), whose marker
read still used the error-swallowing secureGet. A transient keystore outage
(device just unlocked, cross-process lock) read as 'no wallet': cold start
routed to onboarding, and tapping create would overwrite the stored secret
of a real, funded wallet.

- exists() reads the marker with secureGetStrict and throws on read failure
- hasLocalWallet() treats a failed marker read as 'wallet present' so the
  unlock path surfaces a retryable error instead of routing to create
- createLocal() refuses to create when existence can't be verified —
  nothing is written over unknown state
- connect() distinguishes a failed pubkey read (retryable, keys intact)
  from verified absence — 'recreate your wallet' was exactly the wrong
  advice during a transient outage
@epicexcelsior epicexcelsior merged commit 8ca7757 into anonmesh:staging Jun 13, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant