Skip to content

Release retained wallet material on reset - #953

Merged
HDauven merged 4 commits into
mainfrom
fix/wallet-material-reset
Sep 15, 2026
Merged

HDauven merged 4 commits into
mainfrom
fix/wallet-material-reset

Conversation

@HDauven

@HDauven HDauven commented Aug 18, 2026 •

Copy link
Copy Markdown
Member

Summary

  • clear retained profiles and derived account state when the wallet resets
  • invalidate initialization and treasury synchronization that belong to an earlier wallet session/profile generation
  • prevent delayed balance/stake refreshes from republishing state after logout or a profile switch
  • check cancellation before sync preflight clears the cache and before the remaining treasury cache writes
  • invalidate welcome/unlock tabs on cross-tab reset or wallet replacement, not just authenticated tabs
  • cancel abandoned unlocks and prevent delayed legacy-password migration from restoring cleared login data

Why

walletStore.reset() cleared the public Svelte store, but the long-lived treasury retained its profile array and derived account state. Asynchronous initialization or synchronization could also settle after reset and mutate the cleared store.

The shared reset path releases those references and advances the existing wallet-session identifier. Clear-and-init reserves its session before clearing the cache so logout or a newer initialization invalidates the pending continuation. Shared balance/stake refreshes check session/profile ownership before caching and publishing results; treasury sync reuses its generation check around the final asynchronous cache-update steps.

Cross-tab invalidation now lives in the root layout. It uses the existing logout path, then reloads to discard cached login information and preferences. Unlock uses an AbortController to stop late initialization/navigation/migration after leaving the page, and rejects duplicate submissions while an attempt is pending.

This releases JavaScript references to wallet material; it does not claim guaranteed memory zeroization by the browser runtime or cancellation of already-issued browser/network operations. No new dependencies or separate lifecycle framework.

Validation

  • Rebased onto current main, preserving its removal of Lamb.
  • npm run checks — passed: formatting, lint, typecheck, 126 test files / 650 tests.
  • npm run build — passed.
  • Focused lifecycle regressions cover reset during initialization, competing clear-and-init requests, delayed balance/stake reads and cache writes, profile switching, aborted preflight, spent-note lookups, and final sync metadata.
  • Temporarily removing the earlier store/treasury fixes makes 12 test cases fail; removing the route/unlock follow-up makes 7 tests fail. Both groups pass with their fixes restored.
  • Production-browser tests against live Testnet pass for reset with an already-open Unlock tab, reset during an in-flight unlock, and Back during unlock. The last case holds real WebCrypto completion to make the async ordering deterministic; it fails without the cancellation fix and passes 3/3 with it.
  • Browser tests use a disposable wallet without funds; no transactions were broadcast. Password whitespace handling is a separate PR, not bundled here.

One full-suite run hit the untouched receive-page QR snapshot before its image rendered. The isolated test and a complete checks rerun passed; no snapshots or unrelated components were changed.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens wallet reset/logout semantics by ensuring retained treasury state is released and by preventing late-arriving async initialization/synchronization work from republishing state after a reset.

Changes:

  • Add WalletTreasury.reset() and a generation counter to invalidate in-flight updates and clear retained profiles/derived account state.
  • Introduce a walletSession identifier in walletStore to ignore async init/sync work that completes after a reset.
  • Add/extend lifecycle tests covering treasury reset behavior, init reset races, and aborted sync settling after reset.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/lib/wallet-treasury/index.js Add reset/generation invalidation and guard treasury sync work against stale sessions.
src/lib/wallet-treasury/tests/index.spec.js Add coverage asserting treasury reset discards profiles and derived state.
src/lib/stores/walletStore.js Add wallet-session gating and improved sync/reset coordination to avoid post-reset republishing.
src/lib/stores/tests/walletStore.spec.js Add tests for init-reset race and aborted-sync settling after reset; adjust timer expectations.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/lib/stores/walletStore.js Outdated
Comment thread src/lib/wallet-treasury/index.js
@HDauven
HDauven force-pushed the fix/wallet-material-reset branch 2 times, most recently from 25221fe to eec901b Compare August 19, 2026 09:19
@HDauven
HDauven marked this pull request as ready for review August 19, 2026 09:36
@HDauven
HDauven requested a review from Neotamandua August 19, 2026 09:36
@HDauven
HDauven force-pushed the fix/wallet-material-reset branch from eec901b to b76ee98 Compare September 6, 2026 16:02
@HDauven
HDauven requested a lite review from Copilot September 6, 2026 22:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

WalletTreasury.update() currently reads unspent nullifiers where spent nullifiers are required, which can break the spent/unspend reconciliation logic during sync.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 12/12 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment on lines +267 to +270
// gather all spent nullifiers in the cache
const currentSpentNullifiers =
await walletCache.getUnspentNotesNullifiers();
ensureCurrent();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed with a cache-state regression: after the network reports a previously spent note as unspent, it remains incorrectly cached as spent. The same wrong lookup predates this PR. Opened the narrowly scoped fix separately as #963, using getSpentNotesNullifiers() for the second check and verifying both cache sets after reconciliation. The regression fails before the fix and passes afterward.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Cross-tab invalidation reload currently won’t happen if logout(true) rejects, leaving the tab potentially in a stale state after a detected reset.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 12/12 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread src/routes/+layout.svelte
Comment on lines +31 to +34
// Reload after cancellation to discard cached login info and preferences
// on welcome routes as well as authenticated routes.
logout(true).then(() => location.reload());
}

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It changes core wallet lifecycle and synchronization invalidation logic across multiple layers (routing, unlock, store, treasury), so it needs final human review despite strong test additions.

Review details
  • Files reviewed: 12/12 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread src/routes/+layout.svelte
Propagate sync failures and cancellation to transaction callers while
handling background failures explicitly through syncStatus.

Coordinate login removal, replacement and conditional migration writes
with native Web Locks. Keep password encryption inside its write lock so
reset cannot be undone by that pending producer. Invalidate each root
layout only once for repeated cross-tab notifications.

Add regressions for preflight failures, stale transfers, delayed reset
notifications, queued credential writes and failed password encryption.
All 677 tests and 13 local browser fault-injection scenarios pass; the
old-head controls reproduce the reported failures. No live transactions
were broadcast.

Refs #953.
@HDauven
HDauven merged commit f8f6749 into main Sep 15, 2026
1 check passed
@HDauven
HDauven deleted the fix/wallet-material-reset branch September 15, 2026 17:30
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.

2 participants