Skip to content

feat: rewrite Dash Evo Tool onto the new platform-wallet#860

Merged
lklimek merged 799 commits into
v1.0-devfrom
docs/platform-wallet-migration-design
Jul 16, 2026
Merged

feat: rewrite Dash Evo Tool onto the new platform-wallet#860
lklimek merged 799 commits into
v1.0-devfrom
docs/platform-wallet-migration-design

Conversation

@lklimek

@lklimek lklimek commented May 18, 2026

Copy link
Copy Markdown
Contributor

Goal

This PR started as a wallet-backend swap: move DET's SPV/wallet stack onto the upstream platform-wallet crate (dashpay/platform), which owns chain sync, address derivation, identity/asset-lock handling, and the shielded coordinator. That rewrite is still the foundation, and the framing below still holds: DET's bespoke src/spv/ stack and the legacy RPC wallet mode are gone, chain sync and derivation come from upstream, and the BackendTask action/channel contract is preserved.

Since it was opened, ~30 additional PRs (#861#900, excluding #862) stacked onto this branch and merged. Together they turned this from a backend swap into a full rewrite of Dash Evo Tool against the new backend — the UI layer, the identity/masternode/DPNS/DashPay workflows, the secret-storage model, and the progressive-disclosure interface were all rebuilt, re-verified, and in several cases substantially extended, not just re-plumbed onto the new engine. This PR is now the merge point for that entire rewrite into v1.0-dev.

What shipped beyond the backend swap

docs/user-stories.md is the fullest account of the resulting feature surface, persona-tagged and marked [Implemented]/[Gap].

Bugs fixed

Funds-safety

  • Funds sent to addresses beyond the SPV gap window — via Receive → New Address, the asset-lock deposit address, or platform top-up change — were never watched and never appeared in the balance. All address derivation now comes from the SPV-watched pool.
  • The asset-lock deposit QR flow could hang at "Waiting for funds…" forever (the event that advances it had no producer).
  • Incoming DashPay contact payments were never detected or credited (the detection chain had no callers).
  • Creating an identity or funding from wallet balance on a reloaded (watch-only) wallet no longer fails with a missing-private-key error.
  • Withdrawing from a Platform address to a Core address could fail with an internal "address not found in wallet" signer error even though the address's balance was visible in the withdrawal picker — a Platform address discovered by the background coordinator sync was not always registered for signing (watched_addresses could desync from platform_address_info). The wallet now reconciles this automatically via seedless reverse-derivation from the account xpub, so a visible balance can always be withdrawn. No funds were ever at risk: the withdrawal failed safely before broadcast.
  • The Withdraw screen's key pre-selection (default_withdrawal_key()) could pick a key that exists on the identity but whose private key isn't loaded locally — common on masternode/evonode identities where only the Owner key was supplied — failing signing with an unhelpful technical error. It now sources only from private-key-backed keys (Transfer-preferred, Owner-fallback).
  • send_payment now builds and signs through upstream's native TransactionBuilder (upstream removed the previous CoreWallet::send_to_addresses API) and broadcasts via broadcast_transaction_releasing_reservation, so a rejected broadcast can't leak a UTXO reservation. Error messages are now variant-specific — a "too many inputs" failure no longer tells the user to check their balance.

Balance display

  • The Wallets selector's platform balance no longer diverges from the per-address total (it previously over-counted non-owned "orphan" addresses, reading up to ~227× too high). The selector total and the per-address tab now derive from the same coordinator-pushed data and are equal by construction.
  • The wallet screen's balance breakdown could show more Dash than its Core or Platform account tabs added up to, on wallets that had handed out many addresses within a session (e.g. 75.62 vs 2.16 DASH on Platform). Root cause: DET was discarding derivation-index metadata upstream already provides, and the per-category summary only ever looked at DET's own address list, not the backend's authoritative one.
  • The header-vs-tab divergence is now closed at the source, not policed after the fact. The per-account breakdown (Core tabs, Platform tab) and the wallet header total are read from one snapshot generation — including under lock contention, where the entire prior snapshot (not just part of it) is carried forward, so the two figures are never spliced from different moments. Platform's tab reads the exact same accessor as the header (platform_balance_duffs), so it can't lag behind. A stopgap runtime health-check banner that used to detect this class of drift after the fact has been removed — it's structurally unreachable now. Default-mode users also get a reconciling tab for any balance in a normally-hidden category (e.g. CoinJoin, or a funded address the managed-wallet pools don't recognize), so visible tabs always sum to the header total, in every mode. The Platform tab is shown immediately on wallet load (matching pre-migration behavior) rather than waiting on a completed sync pass. This fix went through two independent adversarial QA passes; the first caught that the initial version's "cannot diverge" claim was true for Platform but not yet for Core (a deeper root cause — an upstream balance-update path that could silently drop updates under its own lock contention), the second independently re-verified the fix against the actual upstream source and confirmed it closed.

Transaction history

  • Transaction history rendered empty for a wallet that had received funds in an earlier session, even with a correct non-zero balance — the in-memory snapshot store was populated only by live wallet events, never hydrated from the persisted core_transactions rows on load. It is now hydrated at wallet load, insert-if-absent by txid, before the first snapshot is published.
  • The history "Fee" column always showed -: discovered TransactionRecords were always built with fee: None. Outgoing transaction fees are now derived from known inputs minus outputs, falling back to - (never a wrong number) on incomplete or overflowing data. estimate_platform_fee also now accounts for output count, so the advanced Platform→Platform preview no longer underestimates multi-output transfers.

Shielded confirmation-safety

  • Shield-from-asset-lock and the four sibling shielded spends reported success / marked notes spent even when the transaction might never have confirmed. They now surface typed *ConfirmationUnknown errors instead of a false success.

Secret hygiene

  • The passphrase was prompted at startup for wallets the user hadn't chosen to unlock; it is now requested just-in-time per operation and dropped immediately after use.
  • A single-key private key was kept in plaintext for the whole session — removed.
  • DashPay ECDH and HD encryption keys are now zeroized on drop.

Data-safety

  • Password-protected single-key wallets that silently vanished after upgrade are preserved.
  • Wallets left unloaded after the cold-start migration (which required a manual restart) are now rehydrated automatically.

Lifecycle / sync

  • User-set identity aliases are preserved during auto-discovery (previously silently overwritten).
  • Platform/identity sync no longer starts before chain quorum is ready (which previously caused a DAPI ban storm).
  • Disconnect → Connect no longer fails to reopen the wallet database (the SPV persister is now released on shutdown).
  • The wallet could wait forever with no feedback if its storage backend was ever unusually slow to finish preparing (e.g. after a network switch or cold start). The app now shows an actionable message after 30 seconds instead of leaving the wallet silently invisible. (Also confirmed, with a new regression test, that a previously-suspected "double-open blocks chain sync" failure mode is not reproducible in the current code.)
  • Expert-mode/interface-mode changes now propagate immediately across all open network contexts, instead of being stuck per-context until restart.
  • Loading a masternode/evonode by an unregistered ProTxHash now reports a specific, actionable error instead of the generic "Identity not found."

Review-remediation fixes (this session)

A dedicated audit of every stacked sub-PR's review threads (formal comments, review bodies, and bot auto-resolutions — independently re-verified against current code rather than trusted at face value) surfaced 3 HIGH + 20 MEDIUM findings still present in the merged tree. Fixed and re-merged, grouped by theme:

Also: a committed testnet-only mnemonic and WIF were found and redacted from an internal QA doc (WAL.md) — testnet material, not a live secret, removed per the project's no-committed-secrets policy.

Second-pass adversarial review + full QA sweep (this session)

A follow-up multi-agent adversarial review (3 specialist reviewers + an independent Codex reviewer) of this session's own review-remediation diff surfaced 9 additional blocking findings, all fixed and merged:

  • SPV start-latch loser race: a caller that lost a concurrent SPV-start race to another caller could observe a fabricated/generic outcome instead of the winner's real result. Concurrent callers now join one shared-result flight (a resettable Arc-backed OnceCell) so every caller sees the same real outcome.
  • Identity-deletion resurrection race: an index-derived startup snapshot could let a legacy identity row racing a deletion get resurrected by a later partial-pass migration retry. A direct storage-presence check now guards every pending insert; the shared migration guard was also found (during this fix's own re-review) to be bypassable via the public migration entry point, and is now enforced there too. The stored seed envelope's tagged-encode path is now a zeroizing buffer.
  • Network-database-clear bugs: mismatched network labels between the Network Chooser and top panel, a duplicated error banner on DPNS contested-names, and a sticky scheduled-vote-sweep banner when there's nothing to sweep for.
  • Stale-result correlation: document/claim fetch results are now matched to the specific dispatch that requested them (not just the request type), so a superseded fetch's stale error can no longer overwrite a newer success. A token-balance refresh upstream silently skipped (already in flight) no longer reports as a successful refresh.
  • Advanced-send aggregation overflow: duplicate output/input rows near the Platform credit-range boundary could panic (debug) or wrap (release) instead of being rejected; now uses checked arithmetic with a typed error.

Followed by a full desktop-GUI QA sweep — a delta re-test against a prior comprehensive 175-user-story baseline, plus one net-new story (the storage-update/migration safety path, tested live against a real legacy-schema fixture). Confirmed the large majority of previously-broken stories now pass, and found 3 more issues during the walkthrough, all fixed the same way: the wallet/identity switcher was missing from 4 screens (Contracts, Tokens, Tools, Settings); the Send screen never asked for confirmation before broadcasting and its fee-reserve hint was unreachable; and a "Creating token…" progress banner stayed on screen indefinitely after the operation actually finished. Two known issues remain open, unchanged from before this session (an in-progress-deposit list that never populates, and per-key password protection being unreachable from the identity key list) — see docs/user-stories.md and the QA report for detail. One upstream platform-wallet issue was found and triaged rather than blind-fixed in this repo: a background wallet-ID-mismatch error in identity-sync's token-balance-changeset flush, self-resolving, no observed user-visible impact.

Full evidence-backed report (plain-language): docs/ai-design/2026-07-16-pr860-qa-delta/summary-report.md.

Housekeeping

  • Retired Core-RPC backend plumbing removed. The old RPC/SPV backend-mode selector (core_backend_mode settings field + DB column, FeatureGate::RpcBackend) was already fully inert — chain sync has been SPV-only since this migration started — but the dead weight itself was left behind pending a dedicated schema migration. It's gone now: settings field removed (with a reserved wire byte to keep the existing bincode-encoded det:settings:v1 blob layout intact — no user data at risk), DB column dropped via a new backward-compatible v38 migration (existence-guarded, idempotent, no drop-and-resync needed), and the always-false feature gate removed. Verified: RPC config fields, chain-lock RPC error plumbing, and send_screen's Core/Platform source picker are genuinely still live and were left untouched.
  • debug_assert! invariant guards replaced with real runtime checks. Two sites relied on debug_assert!, which compiles out in release builds — silently disabling the check for every real user. The SPV phase-count drift guard now logs an error instead of vanishing; the tracked-lock funding empty-recipients precondition is now a typed TaskError returned in every build.
  • RUST_MIN_STACK raised to 8 MiB project-wide via .cargo/config.toml, independent of the backend-e2e suite's own dedicated 32 MiB test runtime.
  • Platform pin advanced twice: e6b508f1c2135800 (108 upstream commits; API drift notes below), then c213580044c20e3 to pick up the TransactionBuilder API after upstream removed send_to_addresses (see Bugs fixed → Funds-safety). First bump's adaptations: ManagedIdentity DashPay state sealed behind a dashpay() accessor; ContactXpubData split into { xpub, compact } with the DIP-15 account_reference now HMAC'd over the 69-byte compact xpub (an upstream fix matching the iOS/Android reference clients — DET's own pinned test vector was re-verified against upstream's own known-answer test and re-pinned to the new interop-correct value); IdentityWallet::dashpay_sync() replaced by explicit sync_contact_requests + sync_profiles calls; PlatformWalletError gained 5 variants / dropped 3; a handful of signature changes. Full gate re-verified green after each bump.
  • Simplification audit, round 1 (34 findings fixed, all independently triaged and reviewed). A dedicated audit of this PR's diff for over-engineering flagged 35 issues; 31 were fixed, 2 deferred with self-contained TODOs, 1 accepted as a documented false-positive. Highlights: the 81-method WalletBackend god-impl split into shielded.rs / identity_ops.rs / payments.rs sibling modules (mirroring the existing hydration.rs/dashpay.rs pattern, zero behavior change, funds-signing path moved verbatim); a 14-copy duplicated InMemoryKv test fake collapsed into one shared kv_test_support module; a dead one-impl PersistedWalletLoader trait seam deleted (the G2 mock-boundary swap it existed for already shipped); several near-twin TaskError variants merged; a stringly-typed warning channel in the wallet-refresh banner replaced with a typed error; stale/self-contradicting migration-module comments (some literally asserting things the code no longer does) rewritten to describe present state only; ~150 ephemeral review-round ID references (SEC-NNN/QA-NNN/etc.) stripped from committed comments per the project's own convention against citing IDs that get reassigned on every consolidation run; two unreferenced UI widgets (ContactRow/ActivityRow, ~750 lines) removed; kittest/e2e test-harness boilerplate deduplicated across 19 files.
  • Simplification audit, round 2 (fix: round-2 full-repo architecture audit (21 waves + shielded-screen consolidation) #873, 21 waves). A second full-repo pass, merged as 21 independently-reviewed waves: secret access & signing surface, wallet-backend funds paths and error integrity, backend contracts/documents/tokens, amount/fee/unified-send single-sourcing, identity_db + scheduled votes, context/contract_token_db, wallet_lifecycle orchestration, error.rs consolidation with typed messages, DashPay backend typed errors, DashPay UI, model-layer purity + MCP tools, settings/connection-status/context nits, wallet-backend KV/sidecar dedup and caches, dead UI surface deletion, tokens UI dedup, folding shielded screens into the unified Send screen, and a final comment/narration hygiene sweep.

Full audit-round details live in the audits' own report artifacts (not part of this PR's diff).

Breaking changes

  • Wallet database must be dropped and re-synced. The upstream platform-wallet pin includes a schema change that rewrites migration V001 in place — it drops the core_derived_addresses and account_address_pools tables and hardcodes core UTXO account_index = 0. Existing spv//platform-wallet.sqlite databases are not forward-compatible and must be deleted and re-synced.
  • The single Expert-mode toggle is replaced by three interface levels — Default view, Detailed view, Developer tools — set from Network Settings or first-time onboarding. Detailed view unlocks what the old Expert mode did; a few of the most advanced actions (state-transition signing overrides, proceeding without a locally held key, clearing Platform addresses) now require Developer tools. DEVELOPER_MODE in .env no longer selects the initial role.
  • Masternode/evonode identities no longer appear in the Identity Hub or Identities picker. They live exclusively on the new Masternodes tab, so a node's collateral/voting identity is never offered actions (like registering a username) that don't apply to it.

Known gaps

Updated from the original draft: the two items below marked (closed) were resolved by PRs folded into this branch since it opened.

  • Dependency pin — merge-blocker. The upstream platform-wallet / dash-sdk crates are pinned to an unreleased branch (dash-evo-tool), not a released tag. Must be re-pinned to a released version before merge-to-ship. Still open — the in-branch bumps since the original draft (most recently to pick up the dash-evo-tool PR #3968 head) pick up upstream fixes within that same unreleased branch; none resolve the need for an eventual released-tag pin.
  • Core balance under-counts after restart-in-place. The header / Balance-breakdown "Core" shows the full persisted balance while the "Dash Core" tab and per-address view under-count to the gap window (e.g. 432.41 vs 7.34 DASH). Watch-only rehydration rebuilds accounts from xpubs and derives only the default gap window (indices 0..=29) without restoring the persisted derivation depth. The fix belongs upstream in rs-platform-wallet (extend each account's AddressPool to the highest restored index on rehydration); tracked. Distinct from the header-vs-tab single-sourcing fix under Bugs fixed → Balance display, which closes a DET-side bookkeeping gap and doesn't touch rehydration's gap-window derivation.
  • (closed) Non-wallet data not migrated: app settings, top-up history, scheduled DPNS votes reset on upgrade. Closed by feat: v1.0 UI parity batch — Identity Hub contacts, shielded receive, migration fix, nav pills, disclosure closure #882's settings/vote/top-up migration on upgrade, further hardened this session (see Bugs fixed → Review-remediation fixes).
  • (closed) Token "stop tracking" undone by a refresh. Closed by feat: v1.0 UI parity batch — Identity Hub contacts, shielded receive, migration fix, nav pills, disclosure closure #882.
  • Single-key wallets partial. Balance refresh and SPV send for single-key wallets still return SingleKeyWalletsUnsupported, pending upstream watch-only registration support (register_wallet is private; public constructors require an HD seed). Import / data-loss guard / password-restore are done; confirmed still blocked during the feat: v1.0 UI parity batch — Identity Hub contacts, shielded receive, migration fix, nav pills, disclosure closure #882 parity audit.
  • One-time post-migration disclosure notice not shipped. The removed direct-QR-fund path was meant to get a one-time in-app notice on first launch after upgrade; only a generic "Storage update complete" banner shipped instead (docs/user-stories.md UX-004).
  • Bulk-identity-creation entry point not wired. The onboarding footer mentions "Create multiple test identities," but it isn't yet wired to the existing bulk-creation path (docs/user-stories.md IDH-005).
  • Unified activity timeline is a shell. The Identity Hub's Activity tab has filter chips but no aggregated feed — full aggregation across DashPay payments, funding, and platform ops depends on a backend aggregator, gated behind the identity-hub-activity-feed Cargo feature (docs/user-stories.md IDH-006).
  • Shielded note-level detail unavailable. The Shielded tab shows a total balance but no per-note table, status, or count — placeholder only (docs/user-stories.md WAL-030).
  • Contact removal unavailable. Removing a DashPay contact still returns "not yet implemented," pending Platform-level support (docs/user-stories.md DPY-010).
  • In-progress Platform deposits list never populates. A deposit created via the deposit-QR funding path confirms and works correctly under the hood, but doesn't appear in the in-progress-deposits list afterward, so there's no way to look back at its detail. Display-only; no funds are at risk.
  • Per-key identity password protection unreachable. The new key-list view correctly lists an identity's keys, but clicking into an individual key to add password protection (KeyInfoScreen) has no live UI entry point yet.
  • Minor / deferred: external user-docs update (docs.dash.org) pending; ~2,700+ lines of committed AI-review-round design/QA artifacts under docs/ai-design/ are historical record, not user-facing docs — left as-is, flagged for a possible future trim, and have grown further with this stack's own design docs. Merge-time action: confirm the dependency-pin re-check above before ship.

Testing

  • cargo build / cargo build --features headless clean; cargo clippy --all-features --all-targets -- -D warnings clean; cargo +nightly fmt --all -- --check clean.
  • Library + UI (kittest) + e2e suites green throughout the stack; workspace test count grew from ~1,327 lib tests at the original draft to 1,940+ today (kittest UI tests: 187 → 240+), reflecting the added feature surface and its regression coverage.
  • det-cli standalone smoke (network-info, tools, tool-describe, core-wallets-list) passes.
  • Funds-safety / security review on the migration, shielded retirement, address-derivation, and secret-storage changes; per-workstream design + QA reports under docs/ai-design/.
  • Simplification audit round 1: each of the 34 items built and verified independently in an isolated worktree, then merged and re-verified together on the integrated branch. Round 2 (fix: round-2 full-repo architecture audit (21 waves + shielded-screen consolidation) #873): 21 waves, each independently reviewed.
  • Balance single-sourcing fix: two independent adversarial QA passes (one that caught a real gap in the first version, one that re-verified the fix against actual upstream source after the fix). Both passes ran their own build/clippy/full test suite rather than trusting the implementer's report.
  • v1.0 UI parity batch (feat: v1.0 UI parity batch — Identity Hub contacts, shielded receive, migration fix, nav pills, disclosure closure #882): six independent adversarial agents (3 correctness, 1 security, 1 project-consistency, 1 live-desktop pass).
  • Masternodes tab (feat(masternodes): add Masternodes tab, retargeted onto platform-wallet rewrite #876): live testnet verification via both det-cli (identity load + owner-key withdrawal, confirmed via platform-withdrawals-get) and the running GUI app (real click-through withdrawal, confirmed via the app's own broadcast-completion log).
  • Secret-storage hardening (feat(wallet): unify wallet secret storage on one vault seam with per-secret at-rest encryption #865): independent security audits — SHIP verdict, 0 CRITICAL/HIGH/MEDIUM blockers, all funds-safety invariants verified.
  • Review-remediation pass (this session): a dedicated audit of every stacked sub-PR's review threads (formal comments, review bodies, bot auto-resolutions independently re-verified rather than trusted at face value) found and fixed 3 HIGH + 21 MEDIUM findings, including thepastaclaw's recurring BLOCKING finding on the SPV start-latch. Merged-tree state after that pass: 1,881 lib tests passed, 0 failed; clippy clean.
  • Second-pass adversarial review + QA sweep (this session): a follow-up multi-agent review found and fixed 9 more blocking issues (merge-gate: 559/560 targeted tests passed — the 1 failure was a confirmed pre-existing parallel-execution flake, clean in isolation — clippy and fmt clean), followed by a full desktop-GUI walkthrough (real app, real display, not code-reading) confirming the fix set and finding 3 more issues, all fixed the same session. Full plain-language report: docs/ai-design/2026-07-16-pr860-qa-delta/summary-report.md.

Attribution

🤖 Co-authored by Claudius the Magnificent AI Agent

@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 527 files, which is 427 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5094d78b-b528-4aac-be5b-4c6aefeb4cdd

📥 Commits

Reviewing files that changed from the base of the PR and between 87ba5b7 and f356e57.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • assets/Fonts/Noto_Sans_Symbols2/static/NotoSansSymbols2-Regular.ttf is excluded by !**/*.ttf
📒 Files selected for processing (579)
  • .cargo/config.toml
  • .gitignore
  • AGENTS.md
  • CHANGELOG.md
  • CLAUDE.md
  • Cargo.toml
  • README.md
  • assets/Fonts/Noto_Sans_Symbols2/OFL.txt
  • benches/wallet_hydration.rs
  • docs/CLI.md
  • docs/MCP.md
  • docs/MCP_TOOL_DEVELOPMENT.md
  • docs/ai-design/2026-04-22-identity-dashpay-redesign/README.md
  • docs/ai-design/2026-04-22-identity-dashpay-redesign/design-spec.md
  • docs/ai-design/2026-04-22-identity-dashpay-redesign/wireframe.html
  • docs/ai-design/2026-04-23-identity-hub-impl/01-requirements.md
  • docs/ai-design/2026-04-23-identity-hub-impl/02-ux-plan.md
  • docs/ai-design/2026-04-23-identity-hub-impl/03-test-case-spec.md
  • docs/ai-design/2026-04-23-identity-hub-impl/04-dev-plan.md
  • docs/ai-design/2026-05-18-platform-wallet-migration/README.md
  • docs/ai-design/2026-05-18-platform-wallet-migration/backend-architecture.md
  • docs/ai-design/2026-05-18-platform-wallet-migration/backendtask-contract.md
  • docs/ai-design/2026-05-18-platform-wallet-migration/data-model-and-migration.md
  • docs/ai-design/2026-05-18-platform-wallet-migration/dip14-migration-hardstop.md
  • docs/ai-design/2026-05-18-platform-wallet-migration/feature-coverage.md
  • docs/ai-design/2026-05-18-platform-wallet-migration/g2-mock-boundary.md
  • docs/ai-design/2026-05-18-platform-wallet-migration/open-questions.md
  • docs/ai-design/2026-05-18-platform-wallet-migration/phasing.md
  • docs/ai-design/2026-05-18-platform-wallet-migration/removal-inventory.md
  • docs/ai-design/2026-05-18-platform-wallet-migration/single-key-mock.md
  • docs/ai-design/2026-05-18-platform-wallet-migration/upstream-reality.md
  • docs/ai-design/2026-05-28-migration-tool/notes.md
  • docs/ai-design/2026-05-29-finish-unwire/notes.md
  • docs/ai-design/2026-06-01-pr860-gap-audit/gaps-report.json
  • docs/ai-design/2026-06-01-pr860-gap-audit/gaps.md
  • docs/ai-design/2026-06-02-jit-secret-access/d4-1112-identity-key-redesign.md
  • docs/ai-design/2026-06-02-jit-secret-access/d4b-authxpub-persistence.md
  • docs/ai-design/2026-06-02-jit-secret-access/design.md
  • docs/ai-design/2026-06-02-jit-secret-access/r3-completion-scope.md
  • docs/ai-design/2026-06-02-rehydration-rewire/design.md
  • docs/ai-design/2026-06-02-signtime-unlock-ux/dev-plan.md
  • docs/ai-design/2026-06-02-signtime-unlock-ux/requirements-and-ux.md
  • docs/ai-design/2026-06-02-signtime-unlock-ux/test-cases.md
  • docs/ai-design/2026-06-03-pr860-doc-followups/external-docs-draft.md
  • docs/ai-design/2026-06-08-wallet-reregistration-fix/design.md
  • docs/ai-design/2026-06-09-platform-wallet-nonce-ownership/upstream-issue.md
  • docs/ai-design/2026-06-09-pr860-full-review/findings.md
  • docs/ai-design/2026-06-16-pr860-grumpy-review/report.json
  • docs/ai-design/2026-06-16-pr860-grumpy-review/report.md
  • docs/ai-design/2026-06-17-blocking-progress-overlay/01-requirements-ux.md
  • docs/ai-design/2026-06-17-blocking-progress-overlay/02-test-spec.md
  • docs/ai-design/2026-06-17-blocking-progress-overlay/03-dev-plan.md
  • docs/ai-design/2026-06-17-blocking-progress-overlay/04-design-addendum.md
  • docs/ai-design/2026-06-17-identity-autodiscovery-gap-limit/design.md
  • docs/ai-design/2026-06-17-identity-autodiscovery-gap-limit/qa-report.md
  • docs/ai-design/2026-06-18-masternode-cli-withdraw/01-requirements-ux.md
  • docs/ai-design/2026-06-18-masternode-cli-withdraw/02-test-spec.md
  • docs/ai-design/2026-06-18-masternode-cli-withdraw/03-dev-plan.md
  • docs/ai-design/2026-06-19-secret-storage-seam/01-ux-disclosure.md
  • docs/ai-design/2026-06-19-secret-storage-seam/02-test-spec.md
  • docs/ai-design/2026-06-30-app-scoped-selection-migration/01-migration-plan.md
  • docs/ai-design/2026-07-06-platform-address-signer-reconciliation/01-design.md
  • docs/ai-design/2026-07-07-asset-lock-finality-retest/retest-findings.md
  • docs/ai-design/2026-07-08-secret-decrypt-dedup/README.md
  • docs/ai-design/2026-07-09-masternode-page-design/01-requirements.md
  • docs/ai-design/2026-07-09-masternode-page-design/02-ux-spec.md
  • docs/ai-design/2026-07-09-masternode-page-design/03-test-case-spec.md
  • docs/ai-design/2026-07-09-masternode-page-design/04-dev-plan.md
  • docs/ai-design/2026-07-10-persona-capability-gating/design.md
  • docs/ai-design/2026-07-10-qr-deposit-funding/dev-plan.md
  • docs/ai-design/2026-07-10-qr-deposit-funding/test-cases.md
  • docs/ai-design/2026-07-10-wallet-selector-linking/dev-plan.md
  • docs/ai-design/2026-07-10-wallet-selector-linking/test-cases.md
  • docs/ai-design/2026-07-13-legacy-identity-migration/design.md
  • docs/ai-design/2026-07-13-v1.0-parity-closure/not-planned-signoff.md
  • docs/ai-design/2026-07-14-migration-password-prompt/design.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/CAMPAIGN-CONTEXT.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/progress.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/ALK.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/DEV.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/DOC.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/DPN.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/DPY.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/IDH.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/IDN.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/MCP.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/MN.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/NET.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/SND.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/TOK.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/UX.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/scenarios/WAL.md
  • docs/ai-design/2026-07-14-pr892-user-story-qa/summary-report.md
  • docs/ai-design/2026-07-16-pr860-qa-delta/CAMPAIGN-CONTEXT.md
  • docs/ai-design/2026-07-16-pr860-qa-delta/progress.md
  • docs/ai-design/2026-07-16-pr860-qa-delta/summary-report.md
  • docs/gui-testing/README.md
  • docs/gui-testing/scenarios/TEMPLATE.md
  • docs/kv-keys.md
  • docs/personas/platform-developer.md
  • docs/user-roles.md
  • docs/user-stories.md
  • src/app.rs
  • src/app/reconcilers.rs
  • src/app_dir.rs
  • src/backend_task/contested_names/mod.rs
  • src/backend_task/contested_names/query_dpns_contested_resources.rs
  • src/backend_task/contested_names/query_dpns_vote_contenders.rs
  • src/backend_task/contested_names/query_ending_times.rs
  • src/backend_task/contested_names/vote_on_dpns_name.rs
  • src/backend_task/contract.rs
  • src/backend_task/core/create_asset_lock.rs
  • src/backend_task/core/mod.rs
  • src/backend_task/core/recover_asset_locks.rs
  • src/backend_task/core/refresh_single_key_wallet_info.rs
  • src/backend_task/core/refresh_wallet_info.rs
  • src/backend_task/core/send_single_key_wallet_payment.rs
  • src/backend_task/core/start_dash_qt.rs
  • src/backend_task/dapi_discovery.rs
  • src/backend_task/dashpay.rs
  • src/backend_task/dashpay/auto_accept_handler.rs
  • src/backend_task/dashpay/auto_accept_proof.rs
  • src/backend_task/dashpay/avatar_processing.rs
  • src/backend_task/dashpay/contact_info.rs
  • src/backend_task/dashpay/contact_requests.rs
  • src/backend_task/dashpay/contacts.rs
  • src/backend_task/dashpay/encryption.rs
  • src/backend_task/dashpay/encryption_tests.rs
  • src/backend_task/dashpay/errors.rs
  • src/backend_task/dashpay/incoming_payments.rs
  • src/backend_task/dashpay/payments.rs
  • src/backend_task/dashpay/profile.rs
  • src/backend_task/dashpay/validation.rs
  • src/backend_task/document.rs
  • src/backend_task/error.rs
  • src/backend_task/grovestark.rs
  • src/backend_task/identity/add_key_to_identity.rs
  • src/backend_task/identity/auth_pubkey_resolve.rs
  • src/backend_task/identity/discover_identities.rs
  • src/backend_task/identity/load_identity.rs
  • src/backend_task/identity/load_identity_by_dpns_name.rs
  • src/backend_task/identity/load_identity_from_wallet.rs
  • src/backend_task/identity/mod.rs
  • src/backend_task/identity/protect_identity_keys.rs
  • src/backend_task/identity/refresh_identity.rs
  • src/backend_task/identity/refresh_loaded_identities_dpns_names.rs
  • src/backend_task/identity/register_dpns_name.rs
  • src/backend_task/identity/register_identity.rs
  • src/backend_task/identity/top_up_identity.rs
  • src/backend_task/identity/transfer.rs
  • src/backend_task/identity/withdraw_from_identity.rs
  • src/backend_task/migration/finish_unwire.rs
  • src/backend_task/migration/legacy_settings.rs
  • src/backend_task/migration/mod.rs
  • src/backend_task/migration/single_key_restore.rs
  • src/backend_task/migration/v093_upgrade.rs
  • src/backend_task/mnlist.rs
  • src/backend_task/mod.rs
  • src/backend_task/platform_info.rs
  • src/backend_task/register_contract.rs
  • src/backend_task/shielded/bundle.rs
  • src/backend_task/shielded/mod.rs
  • src/backend_task/shielded/nullifiers.rs
  • src/backend_task/shielded/sync.rs
  • src/backend_task/system_task/mod.rs
  • src/backend_task/tokens/burn_tokens.rs
  • src/backend_task/tokens/claim_tokens.rs
  • src/backend_task/tokens/destroy_frozen_funds.rs
  • src/backend_task/tokens/freeze_tokens.rs
  • src/backend_task/tokens/mint_tokens.rs
  • src/backend_task/tokens/mod.rs
  • src/backend_task/tokens/pause_tokens.rs
  • src/backend_task/tokens/purchase_tokens.rs
  • src/backend_task/tokens/query_my_token_balances.rs
  • src/backend_task/tokens/query_token_non_claimed_perpetual_distribution_rewards.rs
  • src/backend_task/tokens/resume_tokens.rs
  • src/backend_task/tokens/set_token_price.rs
  • src/backend_task/tokens/transfer_tokens.rs
  • src/backend_task/tokens/unfreeze_tokens.rs
  • src/backend_task/tokens/update_token_config.rs
  • src/backend_task/update_data_contract.rs
  • src/backend_task/wallet/derive_identity_key_for_display.rs
  • src/backend_task/wallet/derive_key_for_display.rs
  • src/backend_task/wallet/fetch_platform_address_balances.rs
  • src/backend_task/wallet/fund_platform_address_from_asset_lock.rs
  • src/backend_task/wallet/fund_platform_address_from_wallet_utxos.rs
  • src/backend_task/wallet/generate_platform_receive_address.rs
  • src/backend_task/wallet/generate_receive_address.rs
  • src/backend_task/wallet/mod.rs
  • src/backend_task/wallet/sign_message_with_identity_key.rs
  • src/backend_task/wallet/sign_message_with_key.rs
  • src/backend_task/wallet/transfer_platform_credits.rs
  • src/backend_task/wallet/warm_identity_auth_pubkeys.rs
  • src/backend_task/wallet/withdraw_from_platform_address.rs
  • src/bin/det_cli/connect.rs
  • src/bin/det_cli/headless.rs
  • src/bin/det_cli/main.rs
  • src/boot.rs
  • src/bundled.rs
  • src/components/core_p2p_handler.rs
  • src/components/core_zmq_listener.rs
  • src/components/mod.rs
  • src/config.rs
  • src/context/connection_status.rs
  • src/context/contested_names_db.rs
  • src/context/contract_token_db.rs
  • src/context/feature_gate.rs
  • src/context/identity_db.rs
  • src/context/identity_load_registry.rs
  • src/context/migration_status.rs
  • src/context/mod.rs
  • src/context/settings_db.rs
  • src/context/shielded.rs
  • src/context/test_support.rs
  • src/context/transaction_processing.rs
  • src/context/wallet_lifecycle.rs
  • src/context/wallet_lifecycle/bootstrap.rs
  • src/context/wallet_lifecycle/mod.rs
  • src/context/wallet_lifecycle/registration.rs
  • src/context/wallet_lifecycle/removal.rs
  • src/context/wallet_lifecycle/spv.rs
  • src/context/wallet_lifecycle/tests.rs
  • src/context/wallet_lifecycle/unlock.rs
  • src/context_provider.rs
  • src/context_provider_spv.rs
  • src/database/asset_lock_transaction.rs
  • src/database/contacts.rs
  • src/database/contested_names.rs
  • src/database/contracts.rs
  • src/database/dashpay.rs
  • src/database/identities.rs
  • src/database/initialization.rs
  • src/database/legacy_import.rs
  • src/database/mod.rs
  • src/database/proof_log.rs
  • src/database/scheduled_votes.rs
  • src/database/settings.rs
  • src/database/shielded.rs
  • src/database/single_key_wallet.rs
  • src/database/test_helpers.rs
  • src/database/tokens.rs
  • src/database/top_ups.rs
  • src/database/utxo.rs
  • src/database/wallet.rs
  • src/lib.rs
  • src/logging.rs
  • src/main.rs
  • src/mcp/error.rs
  • src/mcp/mod.rs
  • src/mcp/resolve.rs
  • src/mcp/server.rs
  • src/mcp/tests.rs
  • src/mcp/tools/identity.rs
  • src/mcp/tools/masternode.rs
  • src/mcp/tools/meta.rs
  • src/mcp/tools/mod.rs
  • src/mcp/tools/network.rs
  • src/mcp/tools/platform.rs
  • src/mcp/tools/shielded.rs
  • src/mcp/tools/wallet.rs
  • src/model/address.rs
  • src/model/amount.rs
  • src/model/contested_name.rs
  • src/model/dashpay.rs
  • src/model/dashpay_derivation/dip14.rs
  • src/model/dashpay_derivation/hd.rs
  • src/model/dashpay_derivation/mod.rs
  • src/model/data_migration.rs
  • src/model/dpns.rs
  • src/model/feature_gate.rs
  • src/model/fee_estimation.rs
  • src/model/grovestark_prover.rs
  • src/model/identity_discovery.rs
  • src/model/identity_key_protection.rs
  • src/model/key_input.rs
  • src/model/masternode_input.rs
  • src/model/mod.rs
  • src/model/password_info.rs
  • src/model/proof_log_item.rs
  • src/model/qualified_contract.rs
  • src/model/qualified_identity/encrypted_key_storage.rs
  • src/model/qualified_identity/identity_meta.rs
  • src/model/qualified_identity/mod.rs
  • src/model/qualified_identity/qualified_identity_public_key.rs
  • src/model/request_type.rs
  • src/model/secret.rs
  • src/model/selected_identity.rs
  • src/model/selected_wallet.rs
  • src/model/settings.rs
  • src/model/single_key.rs
  • src/model/spv_status.rs
  • src/model/user_role.rs
  • src/model/wallet/asset_lock_transaction.rs
  • src/model/wallet/auth_pubkey_cache.rs
  • src/model/wallet/birth_height.rs
  • src/model/wallet/encryption.rs
  • src/model/wallet/meta.rs
  • src/model/wallet/mod.rs
  • src/model/wallet/passphrase.rs
  • src/model/wallet/seed_envelope.rs
  • src/model/wallet/shielded.rs
  • src/model/wallet/single_key.rs
  • src/model/wallet/utxos.rs
  • src/spv/error.rs
  • src/spv/manager.rs
  • src/spv/mod.rs
  • src/spv/tests.rs
  • src/test_support.rs
  • src/ui/components/README.md
  • src/ui/components/address_input.rs
  • src/ui/components/amount_input.rs
  • src/ui/components/avatar.rs
  • src/ui/components/breadcrumb_pill.rs
  • src/ui/components/confirmation_dialog.rs
  • src/ui/components/contract_chooser_panel.rs
  • src/ui/components/dashpay_subscreen_chooser_panel.rs
  • src/ui/components/dpns_subscreen_chooser_panel.rs
  • src/ui/components/entropy_grid.rs
  • src/ui/components/global_nav_switcher.rs
  • src/ui/components/icons.rs
  • src/ui/components/identity_selector.rs
  • src/ui/components/info_popup.rs
  • src/ui/components/left_panel.rs
  • src/ui/components/left_wallet_panel.rs
  • src/ui/components/message_banner.rs
  • src/ui/components/mod.rs
  • src/ui/components/modal_chrome.rs
  • src/ui/components/passphrase_modal.rs
  • src/ui/components/password_input.rs
  • src/ui/components/progress_overlay.rs
  • src/ui/components/secret_prompt_host.rs
  • src/ui/components/selection_dialog.rs
  • src/ui/components/styled.rs
  • src/ui/components/subscreen_chooser_panel.rs
  • src/ui/components/tokens_subscreen_chooser_panel.rs
  • src/ui/components/tools_subscreen_chooser_panel.rs
  • src/ui/components/top_panel.rs
  • src/ui/components/wallet_unlock.rs
  • src/ui/components/wallet_unlock_popup.rs
  • src/ui/contracts_documents/add_contracts_screen.rs
  • src/ui/contracts_documents/contracts_documents_screen.rs
  • src/ui/contracts_documents/document_action_screen.rs
  • src/ui/contracts_documents/group_actions_screen.rs
  • src/ui/contracts_documents/register_contract_screen.rs
  • src/ui/contracts_documents/update_contract_screen.rs
  • src/ui/dashpay/add_contact_screen.rs
  • src/ui/dashpay/contact_details.rs
  • src/ui/dashpay/contact_info_editor.rs
  • src/ui/dashpay/contact_profile_viewer.rs
  • src/ui/dashpay/contact_requests.rs
  • src/ui/dashpay/contacts_list.rs
  • src/ui/dashpay/dashpay_screen.rs
  • src/ui/dashpay/mod.rs
  • src/ui/dashpay/profile_screen.rs
  • src/ui/dashpay/profile_search.rs
  • src/ui/dashpay/qr_code_generator.rs
  • src/ui/dashpay/qr_scanner.rs
  • src/ui/dashpay/send_payment.rs
  • src/ui/dpns/dpns_contested_names_screen.rs
  • src/ui/helpers.rs
  • src/ui/identities/add_existing_identity_screen.rs
  • src/ui/identities/add_new_identity_screen/by_platform_address.rs
  • src/ui/identities/add_new_identity_screen/by_receive_deposit.rs
  • src/ui/identities/add_new_identity_screen/by_using_unused_asset_lock.rs
  • src/ui/identities/add_new_identity_screen/by_using_unused_balance.rs
  • src/ui/identities/add_new_identity_screen/by_wallet_qr_code.rs
  • src/ui/identities/add_new_identity_screen/mod.rs
  • src/ui/identities/funding_common.rs
  • src/ui/identities/identities_screen.rs
  • src/ui/identities/keys/add_key_screen.rs
  • src/ui/identities/keys/key_info_screen.rs
  • src/ui/identities/keys/keys_screen.rs
  • src/ui/identities/register_dpns_name_screen.rs
  • src/ui/identities/top_up_identity_screen/by_platform_address.rs
  • src/ui/identities/top_up_identity_screen/by_receive_deposit.rs
  • src/ui/identities/top_up_identity_screen/by_using_unused_asset_lock.rs
  • src/ui/identities/top_up_identity_screen/by_using_unused_balance.rs
  • src/ui/identities/top_up_identity_screen/by_wallet_qr_code.rs
  • src/ui/identities/top_up_identity_screen/mod.rs
  • src/ui/identities/transfer_screen.rs
  • src/ui/identities/withdraw_screen.rs
  • src/ui/identity/README.md
  • src/ui/identity/activity.rs
  • src/ui/identity/avatar.rs
  • src/ui/identity/breadcrumb_switcher.rs
  • src/ui/identity/contacts.rs
  • src/ui/identity/home.rs
  • src/ui/identity/hub_screen.rs
  • src/ui/identity/identity_hero_card.rs
  • src/ui/identity/identity_hub_tab_bar.rs
  • src/ui/identity/identity_picker_add_card.rs
  • src/ui/identity/identity_picker_card.rs
  • src/ui/identity/identity_pill.rs
  • src/ui/identity/landing.rs
  • src/ui/identity/mod.rs
  • src/ui/identity/onboarding.rs
  • src/ui/identity/onboarding_checklist.rs
  • src/ui/identity/picker.rs
  • src/ui/identity/profile_cache.rs
  • src/ui/identity/request_card.rs
  • src/ui/identity/settings.rs
  • src/ui/identity/social_profile_gate_card.rs
  • src/ui/identity/tabs.rs
  • src/ui/masternodes/card.rs
  • src/ui/masternodes/detail_screen.rs
  • src/ui/masternodes/list_screen.rs
  • src/ui/masternodes/load_form.rs
  • src/ui/masternodes/mod.rs
  • src/ui/masternodes/testnet_fixture.rs
  • src/ui/mod.rs
  • src/ui/network_chooser_screen.rs
  • src/ui/state/account_summary.rs
  • src/ui/state/avatar_cache.rs
  • src/ui/state/contacts_view.rs
  • src/ui/state/global_nav.rs
  • src/ui/state/hub_selection.rs
  • src/ui/state/masternodes_view.rs
  • src/ui/state/mod.rs
  • src/ui/state/tracked_asset_lock_cache.rs
  • src/ui/theme.rs
  • src/ui/tokens/add_token_by_id_screen.rs
  • src/ui/tokens/burn_tokens_screen.rs
  • src/ui/tokens/claim_tokens_screen.rs
  • src/ui/tokens/destroy_frozen_funds_screen.rs
  • src/ui/tokens/direct_token_purchase_screen.rs
  • src/ui/tokens/freeze_tokens_screen.rs
  • src/ui/tokens/mint_tokens_screen.rs
  • src/ui/tokens/mod.rs
  • src/ui/tokens/pause_tokens_screen.rs
  • src/ui/tokens/resume_tokens_screen.rs
  • src/ui/tokens/set_token_price_screen.rs
  • src/ui/tokens/token_action_screen.rs
  • src/ui/tokens/tokens_screen/contract_details.rs
  • src/ui/tokens/tokens_screen/data_contract_json_pop_up.rs
  • src/ui/tokens/tokens_screen/distributions.rs
  • src/ui/tokens/tokens_screen/groups.rs
  • src/ui/tokens/tokens_screen/keyword_search.rs
  • src/ui/tokens/tokens_screen/mod.rs
  • src/ui/tokens/tokens_screen/my_tokens.rs
  • src/ui/tokens/tokens_screen/structs.rs
  • src/ui/tokens/tokens_screen/token_creator.rs
  • src/ui/tokens/transfer_tokens_screen.rs
  • src/ui/tokens/unfreeze_tokens_screen.rs
  • src/ui/tokens/update_token_config.rs
  • src/ui/tokens/view_token_claims_screen.rs
  • src/ui/tools/address_balance_screen.rs
  • src/ui/tools/contract_visualizer_screen.rs
  • src/ui/tools/document_visualizer_screen.rs
  • src/ui/tools/grovestark_screen.rs
  • src/ui/tools/masternode_list_diff_screen.rs
  • src/ui/tools/mod.rs
  • src/ui/tools/platform_info_screen.rs
  • src/ui/tools/proof_log_screen.rs
  • src/ui/tools/proof_visualizer_screen.rs
  • src/ui/tools/transition_visualizer_screen.rs
  • src/ui/wallets/add_new_wallet_screen.rs
  • src/ui/wallets/asset_lock_detail_screen.rs
  • src/ui/wallets/create_asset_lock_screen.rs
  • src/ui/wallets/import_mnemonic_screen.rs
  • src/ui/wallets/import_single_key.rs
  • src/ui/wallets/mod.rs
  • src/ui/wallets/restore_single_key.rs
  • src/ui/wallets/send_screen.rs
  • src/ui/wallets/shield_screen.rs
  • src/ui/wallets/shielded_send_screen.rs
  • src/ui/wallets/shielded_tab.rs
  • src/ui/wallets/single_key_send_screen.rs
  • src/ui/wallets/unshield_credits_screen.rs
  • src/ui/wallets/wallets_screen/address_table.rs
  • src/ui/wallets/wallets_screen/asset_locks.rs
  • src/ui/wallets/wallets_screen/dialogs.rs
  • src/ui/wallets/wallets_screen/mod.rs
  • src/ui/wallets/wallets_screen/single_key_view.rs
  • src/ui/welcome_screen.rs
  • src/utils/mod.rs
  • src/utils/path.rs
  • src/wallet_backend/auth_pubkey_cache.rs
  • src/wallet_backend/avatar_cache.rs
  • src/wallet_backend/contact_profile_cache.rs
  • src/wallet_backend/coordinator_gate.rs
  • src/wallet_backend/dashpay.rs
  • src/wallet_backend/det_platform_signer.rs
  • src/wallet_backend/det_signer.rs
  • src/wallet_backend/event_bridge.rs
  • src/wallet_backend/hydration.rs
  • src/wallet_backend/identity_key_store.rs
  • src/wallet_backend/identity_meta.rs
  • src/wallet_backend/identity_ops.rs
  • src/wallet_backend/kv.rs
  • src/wallet_backend/kv_test_support.rs
  • src/wallet_backend/leak_test_support.rs
  • src/wallet_backend/loader.rs
  • src/wallet_backend/mod.rs
  • src/wallet_backend/payments.rs
  • src/wallet_backend/poison.rs
  • src/wallet_backend/secret_access.rs
  • src/wallet_backend/secret_prompt.rs
  • src/wallet_backend/secret_seam.rs
  • src/wallet_backend/shielded.rs
  • src/wallet_backend/sidecar.rs
  • src/wallet_backend/single_key.rs
  • src/wallet_backend/single_key_entry.rs
  • src/wallet_backend/snapshot.rs
  • src/wallet_backend/token_balance.rs
  • src/wallet_backend/versioned_bincode.rs
  • src/wallet_backend/wallet_meta.rs
  • src/wallet_backend/wallet_seed_store.rs
  • tests/backend-e2e/README.md
  • tests/backend-e2e/core_tasks.rs
  • tests/backend-e2e/dashpay_tasks.rs
  • tests/backend-e2e/event_bridge_live.rs
  • tests/backend-e2e/framework/cleanup.rs
  • tests/backend-e2e/framework/dashpay_helpers.rs
  • tests/backend-e2e/framework/fixtures.rs
  • tests/backend-e2e/framework/funding.rs
  • tests/backend-e2e/framework/harness.rs
  • tests/backend-e2e/framework/identity_helpers.rs
  • tests/backend-e2e/framework/mnlist_helpers.rs
  • tests/backend-e2e/framework/mod.rs
  • tests/backend-e2e/framework/shielded_helpers.rs
  • tests/backend-e2e/framework/task_runner.rs
  • tests/backend-e2e/framework/token_helpers.rs
  • tests/backend-e2e/framework/wait.rs
  • tests/backend-e2e/identity_cold_boot.rs
  • tests/backend-e2e/identity_create.rs
  • tests/backend-e2e/identity_in_vault_sign.rs
  • tests/backend-e2e/identity_masternode_withdraw.rs
  • tests/backend-e2e/identity_tasks.rs
  • tests/backend-e2e/identity_withdraw.rs
  • tests/backend-e2e/main.rs
  • tests/backend-e2e/mnlist_tasks.rs
  • tests/backend-e2e/register_dpns.rs
  • tests/backend-e2e/send_funds.rs
  • tests/backend-e2e/shielded_tasks.rs
  • tests/backend-e2e/spv_reconnect.rs
  • tests/backend-e2e/spv_wallet.rs
  • tests/backend-e2e/token_tasks.rs
  • tests/backend-e2e/tx_is_ours.rs
  • tests/backend-e2e/wallet_reregistration.rs
  • tests/backend-e2e/wallet_tasks.rs
  • tests/backend-e2e/z_broadcast_st_tasks.rs
  • tests/common/data_dir.rs
  • tests/e2e/helpers.rs
  • tests/e2e/navigation.rs
  • tests/e2e/wallet_flows.rs
  • tests/kittest/address_input.rs
  • tests/kittest/contract_screen.rs
  • tests/kittest/create_asset_lock_screen.rs
  • tests/kittest/dashpay_screen.rs
  • tests/kittest/global_nav_switcher.rs
  • tests/kittest/identities_screen.rs
  • tests/kittest/identity_hub.rs
  • tests/kittest/identity_hub_activity.rs
  • tests/kittest/identity_hub_contacts.rs
  • tests/kittest/identity_hub_onboarding.rs
  • tests/kittest/identity_hub_settings.rs
  • tests/kittest/identity_hub_switcher.rs
  • tests/kittest/identity_selector.rs
  • tests/kittest/import_single_key.rs
  • tests/kittest/info_popup.rs
  • tests/kittest/keys_screen.rs
  • tests/kittest/main.rs
  • tests/kittest/masternode_tab.rs
  • tests/kittest/migration_banner.rs
  • tests/kittest/network_chooser.rs
  • tests/kittest/progress_overlay.rs
  • tests/kittest/register_dpns_name_screen.rs
  • tests/kittest/restore_single_key.rs
  • tests/kittest/role_indicator.rs
  • tests/kittest/secret_prompt.rs
  • tests/kittest/startup.rs
  • tests/kittest/support.rs
  • tests/kittest/tokens_screen.rs
  • tests/kittest/tools_screen.rs
  • tests/kittest/wallets_screen.rs
  • tests/kittest/welcome_screen.rs
  • tests/kittest/withdraw_screen.rs
  • tests/legacy_table_surface.rs
  • tests/mcp_http_auth.rs

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/platform-wallet-migration-design

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lklimek lklimek changed the title docs: platform-wallet migration design docs: platform-wallet backend — clean-slate rewrite spec May 18, 2026
@lklimek lklimek changed the title docs: platform-wallet backend — clean-slate rewrite spec feat: platform-wallet backend rewrite (spec + implementation) May 19, 2026
lklimek added a commit that referenced this pull request May 29, 2026
refactor: complete data.db unwire — shielded + DashPay (stacked on #860)
@lklimek

lklimek commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

Doc sync — 686430a4 (docs/kv-keys.md)

Trillian audited the k/v key reference against current source and corrected three stale entries. All align the doc with code changes already described in this PR:

  • Migration sentinel — was documented as a single global det:migration:finish_unwire:v1 with constant SENTINEL_KEY. Now correctly det:migration:finish_unwire:<network>:v1 (per-network, via sentinel_key_for(network)), consts SENTINEL_KEY_PREFIX + SENTINEL_KEY_VERSION. Tracks SEC-001.
  • Single-key metadata sidecarImportedKey field list extended from 3 → 5 (has_passphrase, passphrase_hint). Tracks SEC-002 Option C.
  • HD seed envelope encoding — value is now [ version byte | bincode::serde(payload) ], not bare bincode::serde. Tracks SEC-005.

All other sections (17 platform-wallet keys, DashPay sidecar, settings, wallet selection, summary counts) verified accurate — no drift. Doc-only; no source touched.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

SPV-start fix (PROJ-001, CRITICAL) + consolidated gap report — 8d35933e

Bug: the Settings "Connect" button did nothing and SPV stayed idle. Root cause: AppContext::start_spv() was an inert Ok(()) compile-floor stub and WalletBackend::start() (the only spawner of the SPV run loop) had zero callers — so SPV / platform-address / identity sync never started in any path (Connect, auto-start, MCP, network switch).

Fix (3 commits):

  • 42388c4b — wire start_spv()WalletBackend::start(); add a per-instance StartLatch so SPV can't be double-driven.
  • 3165f98c — route all callers through one idempotent async chokepoint AppContext::ensure_wallet_backend_and_start_spv() (wire-then-start); fixes the boot auto-start race (sync constructor fired before the backend was wired), MCP ensure_spv_synced, and network-switch restart; hoist SpvStatus::Error above the DAPI gate so chain-sync failures aren't masked as "Disconnected".
  • 36f5a982 — surface start/wiring failures to the user (indicator → Error in the chokepoint; actionable banner at the Connect handler); replaces the dead AppAction::Custom no-op.

QA: two full Marvin rounds (caller-integration focus). All findings resolved; 2 LOW residuals (user-facing wiring-failure feedback + a forward-compat dead branch) closed by 36f5a982. 571 lib tests pass (+8), clippy/fmt clean. 8 new offline tests cover the start-path gating. The one kittest failure (tc_sk_004) is confirmed pre-existing.


Consolidated gap audit — 2313089a (+ status update 8d35933e)

docs/ai-design/2026-06-01-pr860-gap-audit/gaps.md — a whole-PR audit by project-reviewer Adams. 21 confirmed gaps (1 CRITICAL, 4 HIGH, 6 MEDIUM, 7 LOW, 3 INFO), each with file:line. Highlights:

  • PROJ-001 (CRITICAL) — resolved on-branch (above).
  • PROJ-005 (HIGH) — now the sole remaining open merge-blocker: Cargo.toml pins platform to the unreleased #3625 rev (release gate G1).
  • 5 net-new findings, notably PROJ-004 (HIGH) — DashPay outgoing contact-request xpub derivation uses placeholder seed material (let wallet_seed = sender_private_key;), the substrate behind the TC-037/043 association symptom.
  • 4 seeded "gaps" verified already-fixed (eager-init hard-failure → graceful warn, TC-019 precedence, core_backend_mode removal, the 5-manager readiness gate).

Remaining open gaps are catalogued in the report (not filed as separate issues by request).

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

DetKv per-object refactor + platform bump to 35e4a2f129d54d0

Reseats DET's KV layer onto upstream's per-object KvStore and retires two of three duplicate caches. 7 commits:

  • 549ddfa1 — honest error when wallet data is from a newer app version (WalletDataTooNew, replaces the misleading "check disk space" for schema-version skew).
  • dbd94356 — reseat DetKv on the 08b0ed9 per-object KvStore via a DET-side DetScope { Global, Wallet, Identity, Token } (maps to upstream ObjectId only inside kv.rs — no type leak); isolate the platform-address and token-balance caches behind PlatformAddressView/TokenBalanceView seams.
  • 845ff685 — honest error for an incompatible on-disk schema (WalletDataIncompatible, divergent-migration case); finish the token-balance seam.
  • fb50c044 — bump platform to 35e4a2f (meta_* FK relaxed to soft-cascade triggers; public per-(identity,token) balance reader).
  • f6119de7delete the det:token_balance cache; balances now read live from upstream IdentitySyncManager via a lock-free snapshot bridge (pre-sync shows "syncing", not 0).
  • d7ac9a06promote identities + DashPay overlays to Identity scope (identities/top-ups/scheduled-votes → Identity(id); dashpay private/address_indexIdentity(owner)); Global enumeration indices; explicit cleanup (the upstream soft-cascade trigger doesn't fire for DET-only KV removals); hardened private-key Debug redaction.
  • 129d54d0 — docs synced to the per-object model.

Principle applied: DET's KV stores only DET-specific metadata; canonical object state upstream owns (token balances) is read, not duplicated.

QA (Marvin, combined tree): PASS — cargo build --all-features ✓, 598 tests (597 pass, 1 ignored) ✓, clippy -D warnings ✓, fmt ✓. Zero code defects; M-DONT-LEAK-TYPES intact.

Remaining duplicate: the platform-address cache (det:platform_addr/det:platform_sync) stays, staged behind PlatformAddressView — one-line swap once upstream exposes a public per-address nonce reader (the only open platform-side dependency; the FK relaxation and token-balance reader both landed in #3625 @ 35e4a2f).

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

Mock/stub findings + SPV progress + DashPay fund-routing — 450214e5 (8 commits)

Post-35e4a2f cleanup from a mock-audit, plus a fund-routing fix the audit surfaced. All on top of the DetKv per-object refactor.

SPV / UX

  • bd0ed0e4SPV sync progress bar restored: two UI sites read an inert SpvStatusSnapshot::default(); EventBridge::on_progress already received full upstream SyncProgress (per-phase heights) but discarded it. Routed it into ConnectionStatus (single source of truth) → determinate per-phase % bar. Also fixed two dead gates (network selector now disables mid-sync; "Clear SPV Data" gated).
  • 7e2553e3 — real per-network platform activation height (was hardcoded 1).

Tokens / MCP

  • 5a047357"Stop Tracking Balance" truly un-watches the (identity, token) pair upstream (update_watched_tokens), not just a local order prune.
  • 5ba4554eplatform_withdrawals_get MCP tool: structured fields + pagination.

DashPay

  • a7327e7c — contact-request expires_at derived from created_at + 7d.
  • 3ac9b3b0update_payment_status now persists transitions (was a logging no-op). check_address_usage documented as blocked-by-design (upstream usage reader is keyed by (wallet, account-type), not arbitrary address; contact-send addresses aren't pool-managed).
  • 6c520a33PROJ-004 (fund-adjacent): contact-request xpub now derived from the real HD seed via upstream derive_contact_xpub (was a placeholder using the ECDH private key → mis-routed contact payments). Seed handling: Zeroizing, confined to the wallet_backend seam, never in the document. Smythe-audited: SAFE.
  • 450214e5SEC-001 (HIGH, fund-routing): receive-side DashPay derivation hardcoded coin-type 5' on all networks while the send side (now correct) uses 1' on testnet → testnet contact payments landed on un-scanned addresses (recoverable; mainnet unaffected). Fixed via a canonical coin_type_for_network helper threaded through all receive paths (incl. the DIP-15 root-encryption + auto-accept keys, same bug). Send==receive xpub now pinned by a cross-implementation equality test on both networks. Smythe-re-audited: SEC-001 RESOLVED.

QA/Security: Marvin (integration) + Smythe (two fund-safety passes) — cargo build --all-features ✓, 611 lib tests ✓, clippy -D warnings ✓, fmt ✓.

Standing gates / follow-ups (non-blocking):

  • Live-network test required before trusting contact payments with funds — mainnet round-trip (real funds) and testnet (now expected to pass post-SEC-001).
  • SEC-003 (G1): fund routing depends on the unreleased platform-wallet @ 35e4a2f — no mainnet release until a published rev (existing G1 gate).
  • SEC-004 (LOW): auto-accept key rooted on a 32-byte private key rather than the HD seed (self-consistent, doesn't route funds) — tracked follow-up.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Platform dep bump → ffdc28b8 + gap-audit refresh

Pushed 2 commits (450214e5..419ef952).

fd209783chore(deps): bump platform deps 35e4a2fffdc28b8 (current head of dashpay/platform#3625, a clean 15-ahead/0-behind fast-forward)

  • All four platform crates re-pinned: dash-sdk, rs-sdk-trusted-context-provider, platform-wallet, platform-wallet-storage. grovestark untouched.
  • Cargo.lock: 27 platform crates → v3.1.0-dev.8; 13 transitive grovedb crates moved (upstream-pinned, pulled in by the v3.1-dev merge 269e5783). Nothing stray.
  • Zero source fallout — no DET files needed editing. Picks up two fixes that sit under our seam:
    • 1053caa2 platform-wallet-storage single-read KvStore::get (TOCTOU, CMT-001) — public get/put/delete/list_keys signatures unchanged; our DetKv envelope intact, all 9 adapter tests pass.
    • 73eb0ae0 platform-wallet SPV client deadlock-on-stop — behavioral, no API change; WalletBackend::shutdown/stop_spv compile untouched.
  • Build + clippy (-D warnings) + nightly fmt: all green.
  • Caveat: verified API-clean, but the runtime behavior of the TOCTOU/deadlock fixes is upstream's to validate (needs the #[ignore] backend-e2e suite on a funded testnet wallet).

419ef952docs(gap-audit): refresh gaps.md to as-of-450214e5 state

  • 5 PROJ gaps flipped RESOLVED (re-verified against source, not commit messages): PROJ-001 (SPV start wired), PROJ-003 (update_payment_status persists), PROJ-004 (HD-seed contact xpub + SEC-001 per-network coin-type), PROJ-006 (per-network activation heights), PROJ-014 (offline start-path tests) — plus 4 appendix/seed items.
  • 3 new findings: PROJ-022 (LOW — reserved UpstreamPlatformAddresses stub, deferred to upstream nonce reader), PROJ-023 (LOW — pre-existing error-string parsing in add_contact_screen.rs, tracked under refactor: migrate from Result<T, String> to typed errors with TaskError #660), PROJ-024 (BLOCKED-BY-DESIGN — check_address_usage).
  • Merge-blocker verdict: no CRITICAL open. Sole remaining blocker is PROJ-005 (HIGH) — platform pin is an unreleased dev rev (this bump keeps us on ffdc28b8, so G1 release gate stands until #3625 merges and a tag cuts).

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Dead-code cleanup + gap-audit reconciliation

Pushed 2 commits (419ef952..b2febb71).

9529aedarefactor(dashpay): remove dead add_contact/remove_contact stubs + orphaned NotSupported variant (PROJ-002)

  • The two free functions in src/backend_task/dashpay/contacts.rs were orphans from PR feat: HD wallet system, DashPay integration, SPV support, and more #464 (82399a26) — zero call sites, superseded by DashPayTask::SendContactRequest. Removed both, plus the now-constructorless DashPayError::NotSupported variant (errors.rs). 35 deletions.
  • Verified the unrelated profile_search.rs::add_contact UI method is a distinct symbol (left intact). No dead imports left behind; clippy -D warnings clean.

b2febb71docs(gap-audit): resolve PROJ-002, re-file PROJ-012

  • PROJ-002 → RESOLVED (removed).
  • PROJ-012 re-filed from "deferred-by-design (LOW)" to functional gap (MEDIUM — OPEN). It was masking a real wiring bug behind #[allow(dead_code)]: the ZMQ status sender is live (app.rs:819) but rx_zmq_status is never drained and ConnectionStatus::set_zmq_status (connection_status.rs:159) has zero callers — so ZMQ connection-health events flow into a void and the status indicator never reflects ZMQ state. Binary fix: wire the receiver → set_zmq_status, or remove the whole producer→channel→setter chain (the channel pair is built as a unit at context/mod.rs:161, so no piecemeal trim).
  • Also reconciled the executive-summary tally (it didn't sum before): now an honest 23 total / 17 open / 6 resolved.

Net: open-gap count holds at 17, but the audit books are accurate and a previously-hidden wiring defect (PROJ-012) is now surfaced as a real finding.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Seedless wallet rehydration (PROJ-010) + platform → PR #3692

Pushed 5 commits (b2febb71..e6c6c017). Headline is the wallet-load re-wire.

f35ea7b9chore(deps): platform → PR #3692 head ddfa66ed

e6c6c017feat(wallet): seedless wallet load via UpstreamFromPersisted (PROJ-010)

  • Closes the reserved G2 swap-point. PersistedWalletLoader reshaped to a seedless async load() -> LoadedWallets; UpstreamFromPersisted drives upstream PlatformWalletManager::load_from_persistor() (rebuilds wallets watch-only, no seed in memory at load). SeedReregistrationLoader deleted.
  • Two deviations from the design, both forced by upstream crypto reality:
    1. Bridge matches on the BIP44 account xpub, not WalletId — upstream WalletId = SHA256(root_xpub‖chaincode) is depth-0 master, but DET persists the depth-3 account xpub, and you can't derive a parent from a child. The account xpub is the literal scanned==published routing key, so this is a stronger, migration-free guard.
    2. Identity-funding re-provision deferred to the post-unlock asset-lock chokepoint — a watch-only loaded wallet has no private key, so add_account can't run at load.
  • Fund-safety gates: (1) account-xpub-match gate rejects any loaded wallet not matching a DET sidecar (preserves fund routing); (2) seed supplied only at unlock via provide_seed (signing keeps working; load stays seedless).
  • 612 lib tests pass, clippy -D warnings clean. Fund-safety review by Smythe is in flight.
  • Deferred: skipped-wallet MessageBanner (PROJ-010-T6) — skips are logged + returned in LoadedWallets.skipped, no UI surfacing yet (load path has no egui ctx).

Planning docs (rode along): rehydration design (df38b316), and Phase 1 of the sign-time unlock prompt feature — requirements+UX (d6811732) and a 45-case test spec (bf939c69).

Note: PR #3692 is an open, unreleased dev branch — this keeps the G1 release gate (PROJ-005) open by design until it merges and a tag cuts.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Fix: no-password hydrated wallets couldn't sign (Smythe SEC-001/002)

2272bae0 — fixes a HIGH fund-signing bug Smythe found in the seedless re-wire.

Symptom: after the seedless rehydration, a no-password HD wallet hydrated as Open and the UI showed "unlocked," but inner.seeds was never populated — so signer_for returned WalletLocked for send / asset-lock / identity register+top-up. Inconsistent: DashPay/shielded (which read the seed off the Wallet struct) kept working, masking it.

Root cause: seeds reach inner.seeds only via the handle_wallet_unlockedprovide_seed chokepoint, which the no-password unlock paths bypassed; and bootstrap_loaded_wallets ran at AppContext::new (pre-wiring, empty wallet map) — a no-op.

Fix (one chokepoint, every path funnels through it):

  • Re-timed bootstrap_loaded_wallets to the tail of ensure_wallet_backend (after hydration, populated map).
  • Routed both no-password UI unlock paths (try_open_wallet_no_password, should_ask_for_password) through handle_wallet_unlocked.
  • 33 call sites threaded &self.app_context (mechanical).
  • Regression test (no_password_wallet_resignable_via_unlock_chokepoint): reproduces the seedless cold-boot state, asserts WalletLocked before, signing after.

The account-xpub fund-routing gate and seedless load path are untouched. 613 lib tests pass, clippy clean. Smythe re-verify in flight.

Separate pre-existing item (not from this change): the kittest suite has 15 Migration DivergentVersion failures from a stale on-disk migration artifact — verified identical on the clean base. Needs its own test-isolation cleanup.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

JIT secret-access refactor — HD seed no longer parked in memory (R3 complete)

Pushed 24 commits (2272bae0..43f412cf). This replaces the upfront session-long wallet-unlock model with just-in-time secret access: the HD seed is fetched, used, and zeroized within an operation, never held for the session.

Core (ed06c01906a4b92e)

  • SecretAccess chokepoint — async with_secret / with_secret_session, keyed by SecretScope{HdSeed, SingleKey}, behind a UI-agnostic SecretPrompt seam (egui host via mpsc→oneshot drained on the frame loop; NullSecretPrompt → typed error for MCP/CLI).
  • Residency: operation-scoped by default, opt-in "keep unlocked until I close the app" (RememberPolicy, boxed + Zeroizing, cleared on network switch / teardown). Timed-unlock variant modelled for the future (no GUI yet).
  • All signing routed through DetSigner (HD/single-key) and DetPlatformSigner (all 6+ platform-signer SDK sites incl. shielded), each borrowing the seed inside the secret scope — byte-parity proven against an independent reference on Testnet + Mainnet.
  • Identity-auth public keys memoized in AuthPubkeyCache (DET KV sidecar, public keys only — no DB migration / no DivergentVersion risk; the path is hardened to the leaf so xpub-only derivation is impossible).
  • Capstone: WalletSeed::open reshaped to verify-not-park; OpenWalletSeed.seed field dropped; Wallet::seed_bytes() deleted. rg "\.seed_bytes()" src/ → ZERO (compile-gate proof).

QA (three independent passes)

  • Security (Smythe): SHIP — 10/10 end-state gates; the only remaining plaintext-seed residencies are the three deliberate, sound ones (opt-in session cache, JIT single-key borrow, user-initiated DIP-15 QR). block_on on the shielded path confirmed deadlock-safe (off-UI-thread).
  • Tests (Marvin): 676 lib + 83 kittest + 3 doc — green. Added the HD signer parity test (independent reference + pinned vector), reentrancy + open_no_password guards.
  • Consistency (Adams): typed-error conventions, naming, MCP/CLI all clean.

Also (43f412cf) — QA-005: AppState kittests isolated onto a temp data dir (with_isolated_data_dir), eliminating the 14 pre-existing DivergentVersion failures and stopping tests from touching the real user data dir. kittest now 83/0.

CHANGELOG + user-story WAL-006 updated for the new unlock model.

Deferred/non-blocking: single-key send remains an upstream stub (the JIT machinery is wired and waiting). The G1 release gate stands — PR #3692 is still an unreleased dev rev.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

NEW since last push — Settings Disconnect fix + persister-gap marker + nonce-ownership upstream issue (dd5f4e7ea067a747)

9b2aacbf + a067a747 — fix: the Settings/network "Disconnect" button now actually disconnects.
AppContext::stop_spv() was an inert stub — it only nudged a throttle timer via reset_timer(), so ConnectionStatus::overall_state() (the UI's single source of truth) never moved and the button did nothing observable. The click was wired correctly; the method was the half-finished path. Rewrote stop_spv as a real async disconnect mirroring Connect: flip indicator → Stopping, backend.shutdown().await, unwire the backend via new AppContext::take_wallet_backend() (the start latch is one-shot, so the next Connect rebuilds a fresh, restartable backend), then settle Stopped/Disconnected and recompute state. The button now returns AppAction::StopSpv, dispatched off the frame thread symmetric with StartSpv. A single-winner atomic ConnectionStatus::begin_spv_stop() CAS closes a double-click window — the button disables on the click frame and a second dispatch loses the CAS, so no concurrent teardown. No String error fields, no new user-facing strings; ConnectionStatus stays the single source of truth.

QA (Marvin): both specs PASS, all hard regressions clean (no dangling backend Arc, reconnect rebuilds fresh, self-ban path stays closed, network-switch slots intact). Two LOWs he raised are closed in a067a747 (the synchronous double-click guard + a reconnect-rebuild test). 7 targeted tests pass; clippy -D warnings / +nightly fmt --check / build --all-features green.

1e19f46d — docs(wallet-backend): mark the missing core_derived_addresses seeding (utxo_address_not_derived).
Production-log triage surfaced a persister fatal-flush loop (173× across 7 wallets). The upstream rs-platform-wallet sqlite persister keeps two disjoint address tables — account_address_pools (written at registration) and core_derived_addresses (the UTXO writer's lookup, fed only by live addresses_derived events). On a genesis-rescan boot, SPV matches historical UTXOs at registered/watched addresses before their derive-event lands → UtxoAddressNotDerived → classified fatal → the whole buffered changeset is dropped → core_bridge re-emits → loop → receive/change balances under-report. Added an in-code TODO at the post-registration chokepoint; the upstream fix (seed core_derived_addresses from registration pools + narrow the buffer-wipe blast radius) is tracked separately.

dd5f4e7e710df51f — docs(platform-wallet): upstream nonce-ownership issue draft.
Drafted and filed dashpay/platform#3825 (rs-platform-wallet should own and expose the per-address platform nonce: advance-on-submit + local reader), privacy-led, with the DAPI request-count claim corrected to one avoidable request per spend.

Merge-gate update (G1 / PROJ-005): upstream #3625 is now MERGED (6fa4686), and a tagged pre-release v4.0.0-beta.4 contains the persister crate — but re-pinning to it is verified UNSAFE. beta.4 ships only the bare squash-merge whose production load() is the deferred stub (persister.rs returns wallets_rehydrated=0), while the current pin 9e1248c carries the full rehydration stack (un-gate-schema-readers fix, per-account reconstruction, secrets, the wallet_meta → wallets rename). A swap would compile but silently no-op wallet rehydration and shift the on-disk schema. The pin stays at 9e1248c; G1 remains BLOCKED until a tag is cut from a master line that includes the rehydration stack.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

NEW since last push — re-pin to dashpay/platform#3828: persister-loop fix + Orchard security patch + shielded-API port (4247c360, a0d5034a)

Re-pinned all four dashpay/platform git deps from rev 9e1248c to 4f432c9 (the head of dashpay/platform#3828). This single bump lands three things:

1. Fixes the persister fatal-flush loop. #3828 is the upstream fix for the utxo_address_not_derived loop observed in production det.log (173× across 7 wallets): apply_pools now mirrors registration pool addresses into core_derived_addresses, load() additively backfills already-persisted DBs, and the flush blast-radius is contained (a single undeclared UTXO is skipped, not the whole changeset). The DET-side TODO marker added in 1e19f46d is removed — the gap is closed upstream. Verified at the pinned source: load() is the real rehydration path (wallets_rehydrated = len), not the beta.4-tag deferred stub.

2. Patches a live shielded double-spend vulnerability. The bump carries Orchard 0.13.1 → 0.14.0 and halo2_gadgets 0.4 → 0.5.0 — the patched versions for the disclosed Orchard counterfeit/double-spend flaw (the one behind Zcash's recent emergency hard fork). Smythe confirmed the copy_advice base-tying fix is present in the new halo2_gadgets.

3. Hardens the secrets vault at rest. beta.4's persister refuses to open a vault whose parent dir is group/other-writable (InsecureParentDir). DET's open_secret_store now chmod 0700s the secrets dir right after creation — every vault open routes through that one chokepoint, so this is both real at-rest hardening and what keeps the suite green on a default-umask runner (no harness/CI hack).

Shielded/SDK API port (the cost of the bump). 4f432c9 is a newer upstream lineage, so DET's shielded code was ported to the new signatures: shielded builders now return (StateTransition, Credits) and drop the explicit fee arg; the removed sync_nullifiers API → scan-based spend detection (collect scanned-action nullifiers, flip owned notes whose nullifier matches — a line-for-line port of upstream apply_scanned_nullifier_spends); compute_minimum_shielded_fee now returns Result, propagated via a new typed TaskError::ShieldedFeeComputationFailed; shield_from_asset_lock gained surplus_output.

QA — both SHIP. Smythe (funds/privacy): nullifier rewrite correct, no lockout, no under-fee. Marvin (correctness): the 84 lib-test failures under default perms were 100% beta.4 env-hardening (closed by the chmod 0700), the one AlreadyOpen was test-only (the production Disconnect→Connect path correctly drops the backend before reconnect), no port regressions. 779 lib tests pass / 0 fail on a normal runner; clippy -D warnings / fmt clean.

Merge gate (G1 / PROJ-005) — still blocks merge. The pin advanced and is materially better, but 4f432c9 is a PR head, not a tagged release. #860 still must not merge until #3828 merges upstream and a tag is cut from a master line that carries the rehydration stack. Non-blocking follow-ups tracked: a testable seam + unit test for the nullifier spend-match, dual-cursor unification, and the remaining sync.rs chunk-size literal.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

NEW since last push — fix PROJ-028/030 nullifier-cursor regression + v0.10-dev feature-parity audit (39433dac, 96558917)

Fix: shielded nullifier-cursor reset on migration + resync (PROJ-028 HIGH, PROJ-030 MED)

A regression introduced earlier in this branch by the #3828 re-pin (4247c360): the re-pin made nullifier-spend detection scan-based, keyed off a note-tree position cursor (last_nullifier_sync_height) — but pre-#3828 that persisted value was a platform block height, and the finish-unwire migration carried it verbatim. A migrated wallet therefore started its scan past the note-tree tip → spent notes never flipped → balance silently overstated, spends fail. Caught by a v0.10-dev parity pass against migrated (not fresh) state — the isolated #3828 port QA had reviewed fresh state only.

  • PROJ-028 (finish_unwire.rs:730-738): migration now writes the cursor as (0,0) instead of carrying the legacy value, so migrated wallets rescan [0, tip] and correctly re-derive the spent set.
  • PROJ-030 (shielded_tab.rs): "Resync Notes" now also resets the cursor (via delete_shielded_wallet_meta) so a resync truly re-derives spends instead of resurrecting spent notes.
  • SEC-001 (model/wallet/shielded.rs:98): rewrote the stale "Block height" doc comment that caused the confusion — the field is now documented unambiguously as a note-tree position.
  • TC-SH-002 renamed to assert the reset (the old assertion encoded the bug) + 2 new tests (migrated-cursor→scan flips spent note; resync resets). Full suite green (781+).

QA — Smythe SHIP (funds-safe): the from-0 scan accumulates every on-chain note (can't miss a spend) and is set-only (can't re-credit); delete_shielded_wallet_meta is cursor-only (notes dropped + re-fetched separately, transient balance errs understating); the note-sync cursor is tree-derived and carries no block-height hazard.

v0.10-dev feature-parity audit → docs/ai-design/2026-06-01-pr860-gap-audit/

Audited every user-facing feature in the pre-rewrite line v0.10-dev (≈ v1.0-dev) against this PR via four parallel domain passes + consolidation. gaps.md refreshed (now 28 open / 15 resolved; 0 CRITICAL · 3 HIGH · 10 MEDIUM · 15 LOW) and a schema-validated gaps-report.json added (v3.0.0 review-report format, /triage-findings-ready).

The audit surfaced several regressions the prior audit missed. Beyond the now-fixed PROJ-028/030, the open HIGHs are:

  • PROJ-026 — Create-Asset-Lock QR funding flow soft-locks at "Waiting for funds…" forever: the only WaitingOnFunds→FundsReceived transition fires on CoreItem::ReceivedAvailableUTXOTransaction, which has zero producers. User sends DASH, it lands via SPV, the screen never completes.
  • PROJ-027 — incoming DashPay contact payments never detected/credited (all networks): process_incoming_payment/register_dashpay_contact have zero callers; the v0.10 ZMQ detection path was deleted without replacement.

Plus ~10 MEDIUM (Send-Max always errors from a Core wallet; shield-from-Core ignores the chosen source address; settings reset on upgrade; Dash-Qt launcher unreachable; DashPay history/nicknames not migrated) and doc gaps (RPC-removal CHANGELOG sweep; removed Proof-Log screen). All catalogued with file:line in gaps.md and triage-ready in gaps-report.json.

🤖 Co-authored by Claudius the Magnificent AI Agent

@thepastaclaw

Copy link
Copy Markdown
Collaborator

I checked the asset-lock recovery angle from the JS SDK discussion.

The identity paths look correct in this PR: src/backend_task/identity/register_identity.rs and src/backend_task/identity/top_up_identity.rs route wallet-funded and existing-lock flows through upstream IdentityWallet::*_with_funding with AssetLockFunding::FromWalletBalance / FromExistingAssetLock, so upstream owns tracking, resume, IS→CL fallback, submit, and consume cleanup.

But I do not think PR 860 fully handles the same issue for the non-identity asset-lock flows yet:

  • src/backend_task/wallet/fund_platform_address_from_wallet_utxos.rs creates a tracked proof via WalletBackend::create_asset_lock_proof(...), then manually calls SDK outputs.top_up(...).
  • src/backend_task/wallet/fund_platform_address_from_asset_lock.rs pulls a tracked proof from list_tracked_asset_locks(...), then manually calls SDK outputs.top_up(...).
  • src/backend_task/shielded/bundle.rs creates a tracked proof via create_asset_lock_proof(...), then manually builds/broadcasts ShieldFromAssetLock; after broadcast it treats wait_for_response failure as only a warning.

Those manual paths bypass the upstream rs-platform-wallet higher-level orchestration that exists for this exact lifecycle (PlatformAddressWallet::fund_from_asset_lock, ShieldedWallet::shielded_fund_from_asset_lock, or equivalent): submit_with_cl_height_retry, IS-proof-invalid → CL-proof upgrade, proof/status persistence before retry, post-success balance/activity bookkeeping, and consume_asset_lock cleanup.

So the fresh asset-lock transaction itself is tracked better than JS SDK, but these DET call sites still have a post-asset-lock / pre-final-accounting gap: Platform submit failures or confirmation ambiguity can leave a valid tracked lock in an intermediate/reusable state, and successful submits are not consistently marked Consumed. That is not the same “lost one-time private key forever” failure as JS SDK, but it is still an incomplete recovery/resume implementation for asset-lock-funded Platform address and shielded flows.

Suggested fix: route these non-identity flows through the upstream wallet orchestrators, or mirror the same orchestration locally: resume/upgrade tracked proof, submit_with_cl_height_retry, persist any CL upgrade before retry, and mark the asset lock Consumed only after Platform acceptance/accounting succeeds.

@lklimek

lklimek commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

Verified your analysis against current source — it holds. Here's how PR #860 now addresses it.

Shielded asset-lock flow (shield_from_asset_lock, src/backend_task/shielded/bundle.rs) — fixed. You correctly identified the real fund-safety bug: a wait_for_response failure after a successful broadcast was swallowed (warn! + .ok()) and fell through to Ok(credits) — a false success on a single-use asset lock. It now maps to a dedicated typed TaskError::ShieldedConfirmationUnknown (#[source]-chained, with an actionable, jargon-free user message) and is propagated, so an ambiguous confirmation can never report success. Two new offline tests cover the mapping and the message.

Platform-address flows (fund_platform_address_from_wallet_utxos.rs, fund_platform_address_from_asset_lock.rs) — no false-success; consume/retry orchestration deferred (upstream-gated). We traced both: the manual outputs.top_up(...) calls are ?-propagated through upstream broadcast_and_wait, so a submit/confirmation failure already surfaces as an error — no false success. What's missing is the terminal orchestration you named — submit_with_cl_height_retry, IS→CL proof upgrade, and marking the lock Consumed. Routing through PlatformAddressWallet::fund_from_asset_lock / PlatformWallet::shielded_fund_from_asset_lock is not possible from DET at the pinned rev: PlatformAddressWallet isn't re-exported, and submit_with_cl_height_retry / consume_asset_lock are pub(crate). Residual risk is a tracked asset lock left in a reusable/recoverable state on ambiguous failure — recoverable, not lost funds. Precise in-code TODOs naming the required upstream symbols sit at each choicepoint.

Also surfaced by review: the four sibling shielded spend fns (shield_credits / shielded_transfer / unshield_credits / shielded_withdrawal) swallow the same way and mark notes spent pre-confirmation — but that self-heals on the next nullifier/note resync (check_nullifiers reconciles against chain), so it's a temporary local divergence, not note loss.

All tracked in docs/ai-design/2026-06-01-pr860-gap-audit/ as PROJ-042 (this gap; shield false-success resolved in-PR, platform-address consume orchestration OPEN pending upstream API exposure) and PROJ-043 (the sibling-swallow follow-up). Verified by funds-safety (SHIP) and correctness (PASS) review passes.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

Correction to my earlier reply. I previously said the platform-address consume/retry orchestration was upstream-gated and not possible from DET. That was wrong — it is reachable through the public PlatformWallet API with no upstream change, and it's now implemented in this PR.

What landed (pushed):

  • WalletBackend::fund_platform_address routes wallet-owned platform-address asset-lock funding through the upstream orchestrator PlatformAddressWallet::fund_from_asset_lock — i.e. resolve → submit_with_cl_height_retry → IS→CL fallback → consume_asset_lock. This closes the recovery gap you flagged for the common (wallet-owned) case.
  • The orchestrator-vs-manual decision queries upstream's own pool membership (contains_platform_address) rather than any DET-side heuristic: a wallet-owned, in-pool destination gets full orchestrated recovery; a foreign/non-pool destination intentionally keeps the manual top_up path (advanced users may knowingly fund any address). Both paths propagate submit failures — no false success.
  • The shielded asset-lock false-success is fixed separately (typed ShieldedConfirmationUnknown, no longer swallowed).

Commits: 37c2889e (shield), 1010828c (orchestrated routing), c45ac35e (upstream pool-membership gate). Verified by funds-safety + correctness review. Remaining residuals are tracked in docs/ai-design/2026-06-01-pr860-gap-audit/ — PROJ-042 resolved; PROJ-043 (a self-healing sibling-swallow in the four shielded spend paths) and a minor fee-from-wallet change-recipient case are being addressed as follow-ups.

Thanks for the catch — your read on the recovery gap was correct, and so was the implication that DET should route through the upstream orchestration.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

Two reliability fixes from live testing — crash visibility + DAPI ban storm (3c61a65664632628)

Pushed 5 commits (dd3c390c..64632628). Both surfaced during fresh-boot testing on testnet.

Issue B — silent crashes now leave a trace (3c61a656, 96b61da7). The app could quit with nothing in det.log (a GUI launch loses stderr, and the Rust panic hook only catches catchable panics — native SIGSEGV/SIGABRT/abort/OOM vanish). Added: capture_stderr_to_file() (redirects fd 2 to a sidecar det-stderr.log, rotated, with a guard that no-ops when the logger already fell back to stderr) and install_fatal_signal_handler() (async-signal-safe write of a per-signal marker for SIGSEGV/SIGABRT/SIGBUS/SIGILL/SIGFPE, then re-raises so the core dump is preserved). Unix implemented + verified (abort/segv captured); Windows is a documented warn+TODO.

Issue A — Platform/identity sync no longer bans every DAPI node during initial sync (f318c06f, 9d5c865b). Root cause: sync started immediately at boot and issued proof-verifying DAPI calls before SPV had the masternode list → the context provider failed → the SDK's ban_failed_address banned all ~156 nodes (no available addresses). Fix: a CoordinatorGate (one-shot, single-winner latch) defers the platform/identity coordinator starts until the masternode list reaches Synced (which happens right after headers, well before the slow filter/block scan — so Platform still comes up early), driven event-driven from EventBridge; the SPV context provider returns a non-banning "not ready" error pre-quorum; the flag resets on disconnect/network-switch so a new session re-proves readiness. The gate captures Weak handles (a strong capture would pin the persister lock past teardown → AlreadyOpen on reconnect), with a dedicated regression test proven to catch a strong-capture revert.

QA: Smythe SHIP + Marvin PASS on both (gate proven never-strand + exactly-once; signal handler proven async-signal-safe). Consolidated tip: 987 tests pass / 0 fail, clippy + fmt clean. CHANGELOG updated.

Note: the platform pin is unchanged here (still the dev rev 4f432c9); the merge gate (re-pin to a tagged release) remains tracked separately.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

Re-pin platform deps to PR dashpay/platform#3828 branch (35f18be9)

Picks up the upstream merge that just landed on #3828 (the wallet-rehydration + core-derived-rehydration / persister fatal-flush-loop fixes). All four platform crates (dash-sdk, rs-sdk-trusted-context-provider, platform-wallet, platform-wallet-storage) re-pinned from rev = "4f432c9" to branch = "fix/wallet-core-derived-rehydration"; Cargo.lock locked to head 925dfcbf. Transitive rust-dashcore eb889af1981e97f1.

Signature-only API port (behavior-preserving): shielded builders gained sender_ovk / dummy_outputs (DET passes None / 0 — byte-equivalent to the prior single-output form; surplus_output pre-existed as None); MasternodeNetInfo.service_address became a Legacy | Extended enum (UI display helpers with a non-routable fallback); two new PlatformWalletError variants (ShieldedBroadcastUnconfirmed / ShieldedSpendUnconfirmed) bucketed Other (do-not-resubmit).

QA — funds-safety SHIP, correctness PASS. Smythe verified the shielded-builder defaults are provably equivalent to prior behavior (recipient funds/spendability unaffected; surplus folds to fee pools, nothing stranded), and the rust-dashcore bump introduced no derivation/signing/quorum drift (bip32 signing + contains_platform_address byte-identical; the breaking network-scoped wallet_id change doesn't affect DET, which routes on the BIP44 account xpub, not wallet_id). Marvin confirmed the port is signature-only with an exhaustive error match, Issue A's masternode-readiness surface unchanged, and 987 tests pass / 0 fail, clippy + fmt clean.

Note: this is a branch = pin (a moving target) to track the in-flight upstream fix; Cargo.lock keeps today's build reproducible at 925dfcbf. The release merge gate still requires an immutable tag/rev before this PR merges.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

NEW since last push — 561bce8d WalletNotLoaded root-cause + upstream BIP32/BIP44 persistor-collision fix

Wallets failed to load on cold boot (WalletNotLoaded for every wallet, even on a fresh DB). Root cause: an upstream platform-wallet-storage primary-key collision. WalletAccountCreationOptions::Default creates both a BIP32 account-0 (m/0', depth-1) and a BIP44 account-0 (m/44'/coin'/0', depth-3), but account_type_db_label collapsed both to the single label "standard", so the two rows shared the account_registrations PK (wallet_id, "standard", 0). On persist, apply_registrations (INSERT…ON CONFLICT DO UPDATE) let the BIP32 depth-1 row overwrite the BIP44 depth-3 row — silently losing the BIP44 xpub. Cold-boot seedless reload then read the wrong depth-1 key and the fund-routing gate rejected every wallet.

Fixed upstream in platform PR #3828: the BIP32/BIP44 standard accounts now use distinct labels ("standard_bip32" / "standard_bip44"), so both rows coexist and BIP32 is preserved (users with legacy m/0' funds keep that account). This branch re-pins the platform crates to #3828 HEAD 925b109d.

  • Re-pin platform crates 925dfcbf → 925b109d (Cargo.lock only; deps are branch-pinned). 561bce8d
  • Regression guard: issue7_fresh_persistor_bip44_xpub_matches_det_bridge flipped from #[ignore] to a live test — it drives the real load_from_persistor_seedless cold-boot and asserts the stored BIP44 xpub is depth-3 and matches DET's bridge, and that the BIP32 row still survives (keep-BIP32 invariant).
  • Terminal startup-failure notice (226db5fc): native startup failures now print a generic, actionable message plus log-file paths to the real terminal (stderr is redirected to a sidecar log), instead of exiting silently.
  • The earlier #251 xpub-drift self-heal (9c45d7f3, b4668129) was investigated and reverted (89378d21) — it treated a symptom; the real cause is the upstream collision above.

Known limitation: there is no heal migration for DBs already corrupted by the old collision — those need a delete + re-import (the upstream PR treats the schema as unshipped). A follow-up protected-wallet-after-unlock registration fix is in progress (locked-at-boot wallets were not re-registered on unlock).

Suite green: 887 lib tests pass; fmt + clippy clean.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up landed — 44caa892 register protected wallet on unlock

The "protected-wallet-after-unlock registration fix in progress" noted above is now in.

A password-protected wallet hydrates Closed (locked) at cold boot, so bootstrap_wallet_addresses_jit skips it on the is_open() gate (to avoid a surprise startup prompt) and it never enters the upstream fund-routing id_map. Previously the unlock gesture only promoted the verified seed into the session cache — it never re-drove registration — so a protected (or freshly migrated) wallet stayed WalletNotLoaded for the session and was skipped again on the next boot.

handle_wallet_unlocked now calls a new drive_unlock_registration, which spawns bootstrap_wallet_addresses_jit on a tracked subtask once the seed is session-cached → the wallet is upstream-registered prompt-free, no restart needed (seed promoted before the spawn ⇒ no second password prompt; the remember=false path is a no-op).

  • Touches only src/context/wallet_lifecycle.rs.
  • Test: protected_wallet_registers_upstream_on_unlock_without_restart — migration-faithful (legacy protected row → migration → hydrated Closed+unregistered → unlock → registered).
  • Suite green (888 lib tests), fmt + clippy -D warnings clean.

This closes the remaining case after the #3828 re-pin (561bce8d): the re-pin fixes the persistor BIP32/BIP44 collision for fresh/unprotected wallets; this fixes the migrated-protected wallet whose upstream registration is empty at boot and can only be registered via the unlock path.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

NEW since last push — shielded backend retired in favour of upstream platform-wallet coordinator (f637365056ac2cfe)

Pushed 16 commits (44caa892..56ac2cfe). DET's home-grown Orchard shielded subsystem is removed and every shielded operation now routes through upstream platform-wallet's shielded coordinator. The bundle.rs:634 TODO(upstream-gated) was stale — the orchestrator is pub; the only real gate was the missing platform-wallet shielded cargo feature.

What landed (net −4,509 lines):

  • Enable + wire the shielded feature; configure_shielded + ShieldedSyncManager lifecycle. (f6373650)
  • WalletBackend op + bind methods wrapping the upstream ops (shielded_fund_from_asset_lock / shielded_shield_from_account / shielded_transfer_to / shielded_unshield_to / shielded_withdraw_to) with an exhaustive map_shielded_op_errorShieldedSpendUnconfirmed can never report success. (caa443af, 18aa6ee2)
  • Push balance snapshot: AppContext::shielded_balances written by EventBridge::on_shielded_sync_completed (maps upstream WalletIdWalletSeedHash), read synchronously in the egui frame loop — no block_in_place. Lazy bind via ensure_shielded_bound on JIT unlock. (7df68d78, 47c6eeba, fa0e46de)
  • Deleted the 6 old shielded modules (context/shielded.rs, wallet_backend/shielded.rs, backend_task/shielded/{bundle,sync,nullifiers}.rs, database/shielded.rs, model/wallet/shielded.rs) + ShieldedTask reduction + a forward DROP TABLE migration + per-network legacy-file cleanup. (479c8c18)
  • 5 new det-cli/MCP self-test tools: core_wallet_import, shielded_init, shielded_sync, shielded_balance_get, shielded_address_get. (63b2e796)

Also in this push (cleanups): module-placement policy added to CLAUDE.md + TrackedAssetLockCacheui/state/; WalletUnlockPopup slimmed to a thin passphrase_modal wrapper; legacy ScreenWithWalletUnlock removed (migrated 3 screens). PROJ-032 closed as N/A (DashPay was never persisted in any released version — verified against v0.9.3); PROJ-034 confirmed real and tracked as a follow-up.

Verification: 874 lib + 88 kittest pass, clippy --all-features --all-targets clean (0 warnings), 3 build profiles green. A full grumpy-review (docs/ai-design/2026-06-16-pr860-grumpy-review/) found 0 critical / 0 high / 4 medium / 21 low; the medium + clear-low findings were fixed (56ac2cfe: mnemonic redact+zeroize in core_wallet_import, doc-accuracy, dead bind-guard, typed errors, stale-balance eviction, awaited coordinator wipe).

Open for reviewers:

  • One MEDIUM left for discussion, not auto-changed: the typed passphrase is parked in a single global egui cache slot keyed by window title (password_input.rs) — possible cross-prompt leak; needs a UX-security call.
  • Live testnet self-test of the shield→sync→transfer→unshield→withdraw loop via det-cli is recommended before merge.
  • Merge remains gated on the upstream fix/wallet-core-derived-rehydration branch being released (the platform deps are pinned to it).

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

NEW since last push — 0484bcb6 post-migration identity auto-discovery (By-Wallet) + rolling gap-limit

Why: After the platform-wallet migration, a wallet's identities were only loaded if the user manually opened Load Identity → By Wallet and searched. This push discovers them automatically once Platform connectivity is ready, with a rolling gap-limit so newly-created identities are always found, and preserves user-set aliases on re-discovery.

What it does:

  • Auto-discovery on Platform-ready. Once the SPV masternode list reaches Synced (the point at which Platform/DAPI is safe to query — querying earlier gets DAPI nodes banned), DET runs By-Wallet identity discovery across all open wallets. Two triggers: a global once-per-session sweep (AtomicBool latch, cleared on SPV stop so it re-arms on reconnect) and a per-wallet, latch-independent trigger fired when a wallet is unlocked.
  • Rolling gap-limit (IDENTITY_GAP_LIMIT = 5). Scans to max(existing identity index) + 5, extending the window on each discovery so 5 empty indices always trail the highest found index (mirrors the BIP-44 address gap-limit). Bounded by a hard cap of 100. Pure decision logic in model/identity_discovery.rs (10 unit tests); one shared scan fn used by both the UI By-Wallet path and the auto-trigger.
  • Alias preservation on re-discovery. Re-discovered identities are updated in place, but DET-only metadata (alias, wallet binding) is carried forward; top-ups are unaffected (stored under a separate key).

Safety items fixed in this push (surfaced by review):

  • (High, pre-existing bug) the single-index By-Wallet search silently wiped a user's alias on every search — fixed (both single-index and gap-limit paths now carry the alias forward).
  • (High, UX hazard) a background sweep over a locked protected wallet would have popped a passphrase modal unprompted (identity-auth keys are hardened-to-leaf, so a cold cache miss triggers a secret prompt); the background path now runs allow_prompt=false and skips locked wallets, and the eventual unlock re-triggers discovery for that wallet.

Verification: headless build clean · clippy -D warnings zero · 889 lib tests pass (4 new offline guards: latch one-shot + stop_spv re-arm, locked-wallet exclusion, alias preservation, search-index cap). Design + QA reports added under docs/ai-design/2026-06-17-identity-autodiscovery-gap-limit/.

🤖 Co-authored by Claudius the Magnificent AI Agent

@lklimek

lklimek commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Session update — 5 commits pushed (d2f4fd87..1c4f4dcd)

Three fixes landed against the platform-wallet backend, each root-caused from a live testnet symptom:

Reconnect crash (09540cf8)

  • Connect → disconnect → connect failed with WalletStorage(AlreadyOpen { … platform-wallet.sqlite }).
  • Cause: WalletBackend::shutdown() never stopped the SPV run loop, so the persister path stayed registered in the process-global open-set.
  • Fix: shutdown() now calls spv().stop().await before tearing down the wallet manager.

Watch-only asset-lock failure (98bc4913, tests b8517905 + acdd1336)

  • Both "create identity" and "send funds core→identity" failed with AssetLockTransaction("Watch-only wallet has no private key").
  • Cause: wallets reload seedless (watch-only); identity funding-account provisioning called add_account(type, None), which has no key material on a cold-booted watch-only wallet.
  • Fix: provisioning now derives the account xpub from the seed (derive_extended_public_key) inside the with_secret_session scope and calls add_account(type, Some(xpub)). Regression guard uses a two-boot cold-start scenario (write persister from seed → copy data dir → cold-load seedless → assert provisioning succeeds + is idempotent).

Legacy MN-list-diff inspector + Core P2P handler removed (1c4f4dcd, −5,559 LOC)

  • The Masternode-List-Diff inspector was the only RPC-only tool and is dead in SPV-only mode. Removed the screen (~4.5k LOC), core_p2p_handler.rs, MnListTask, TaskError::P2P, all wiring, and the backend-e2e mnlist tests.
  • Verified independently: git grep clean of all removed symbols; surviving chainlock paths use only CoreTask::GetBestChainLock (no dependency on the removed task/handler).

Verification: cargo build (default + --features headless) ✅ · clippy --all-features --all-targets -D warnings ✅ zero · test --all-features --workspace998 passed, 0 failed · +nightly fmt --check ✅ clean.

🤖 Generated with Claude Code

@lklimek

lklimek commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Pushed 1c4f4dcd..976ad0d4 (3 commits): backend-e2e coverage (B reconnect + C/D cold-boot), event-driven ensure_spv_synced (②), and the interim persister-release barrier for the reconnect AlreadyOpen regression (B-2).

The B-2 barrier is verified as an interim stopgap (closes the common coordinator-thread drain race). The durable fix — a reusable cancel + await-exit shutdown primitive (StructuredShutdown) covering both DET-owned wallet subtasks and the upstream coordinator threads, with the architecturally clean variant being keep-the-WalletBackend-alive + SPV/coordinator restart-in-place — is designed and will retire the barrier. An upstream rs-platform-wallet issue (make coordinator quiesce() join, not cancel-and-drain) will be filed. See the "Additional fixes folded in (2026-06-18)" section in the description.

🤖 Co-authored by Claudius the Magnificent AI Agent

…lures

Fixes thepastaclaw's recurring BLOCKING finding on PR #860: a failed
SPV init permanently stranded chain sync with no way to retry or
recover via Disconnect. See fix/pr860-spv-start-latch (00201d0).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017x3fA7FZHMKATyRXWD2DEZ
@lklimek lklimek changed the title feat: platform-wallet backend rewrite (spec + implementation) feat: rewrite Dash Evo Tool onto the new platform-wallet Jul 16, 2026
resp.info_tooltip() isn't #[must_use] and every other call site in the
repo calls it bare, no binding — matches lklimek's PR #860 review
comment questioning why the unused let _resp exists.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017x3fA7FZHMKATyRXWD2DEZ

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Preliminary review — Codex only

The SPV retry defect is fixed: initialization failures reset the latch, propagate to callers, and permit Disconnect recovery; the targeted regression test passes. Two new blocking lifecycle defects are confirmed in token refresh and scheduled-vote sweeping, and the three carried-forward documentation/test findings remain valid.

Validated blockers were found in the Codex precheck. Sonnet is deferred until a fresh Codex revalidation clears the blocker gate.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (failed_pre_evidence), gpt-5.6-sol — general (completed)
  • Verifier: gpt-5.6-sol — verifier
  • Sonnet: not run (deferred by blocker gate)

🔴 2 blocking

3 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/backend_task/tokens/query_my_token_balances.rs`:
- [BLOCKING] src/backend_task/tokens/query_my_token_balances.rs:197-217: Expired refresh lease permits false-success retries
  The maximum-lifetime timer clears only DET's single-flight flag; it does not stop the managed refresh or release upstream's synchronization slot. At the pinned platform revision, `IdentitySyncManager::sync_now()` immediately returns without work while `is_syncing` remains set. After this lease expires, a retry can acquire a new lease, encounter that upstream no-op, republish the unchanged snapshot, and return `FetchedTokenBalances`. In the permanent-hang case the change is intended to recover from, every later refresh can therefore report success while balances remain stale. Couple lease release to completion of the upstream pass or introduce an upstream cancellation/reset mechanism before allowing another successful refresh.

In `src/app.rs`:
- [BLOCKING] src/app.rs:2075-2080: Scheduled-vote guard survives backend task panics
  The in-progress network entry is removed only for `ScheduledVoteSweepCompleted` and `ScheduledVoteSweepFailed`. A panic in the spawned backend task is converted by the join watcher into `TaskError::BackendTaskFailed`, while `CastDueScheduledVotes` is classified as `BackendTaskContext::Other`. That result reaches the generic error arm without removing the network from `scheduled_vote_sweeps_in_progress`, so no later sweep is dispatched for that network until restart. Preserve the sweep network in its task context and clear the guard for every terminal result, including join failures.

Comment thread src/backend_task/tokens/query_my_token_balances.rs
Comment thread src/app.rs
lklimek and others added 17 commits July 16, 2026 19:22
…llers

WalletBackend::start_once() previously let a losing caller in a
concurrent-start race fall through to its own independent call into
upstream SpvRuntime rather than observing the winner's actual result —
so concurrent start() callers could see inconsistent outcomes (a
fabricated/generic error on one side, the real error or success on
the other). Reworked StartLatch to hold a resettable Arc<StartFlight>
backed by a tokio::sync::OnceCell: every concurrent caller now joins
one shared-result flight and all observe the same outcome, while a
completed flight still short-circuits repeat calls without spawning a
second SPV run loop, and a stale flight captured before a reset cannot
initialize the replacement generation.

TaskError::WalletBackend now carries Arc<PlatformWalletError> (rather
than an owned value) so a single flight's error can be cloned out to
every waiting caller — TaskError itself isn't Clone. A lifecycle mutex
now serializes start() against stop_in_place() so a stop can't race a
concurrent start attempt.

New test: concurrent_spv_start_failure_is_returned_to_every_caller.
context::wallet_lifecycle 62/62 and wallet_backend 321/321 pass;
clippy and fmt clean.

Grumpy-review PR #860 finding (start-latch loser race), blocking.

Co-Authored-By: Codex Sol <noreply@openai.com>

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>
… encoding

Two grumpy-review findings against PR #860's migration path, plus a
guard-bypass gap surfaced during the fix's own re-review:

- migrate_identities_from_conn() trusted an index-derived startup
  snapshot to decide whether a legacy identity row was already
  migrated. A deletion racing the migration between snapshot and
  insert could leave a stale legacy row that a later partial-pass
  retry then resurrected into the modern store as a "deleted"
  identity reappearing. Added a direct has_current_blob() check
  against storage before every pending insert, and record_processed()
  durably advances the processed set after either an insert or a
  direct-presence discovery so a retry can't repeat the race.

- StoredSeedEnvelope's tagged encode path (encode_with_version) built
  its `[version || bincode(body)]` wire buffer in a plain, non-zeroing
  Vec<u8> even though the envelope holds a wallet seed — the buffer
  leaked seed bytes to heap until reallocation. encode() now returns
  Zeroizing<Vec<u8>>.

- Re-review of the deletion-race fix found the shared migration guard
  was only enforced at the dispatcher, so a direct caller of the
  public migration entry point could bypass it. Moved lock ownership
  to that public boundary (run_under_guard) so every entry path is
  serialized against an in-flight identity deletion.

Red-before/green-after confirmed for the guard-bypass gap
(public_migration_run_waits_behind_idle_deletion_guard panicked before
the fix, passes after). Full migration suite: 92/92 pass; clippy
clean.

Co-Authored-By: Codex Sol <noreply@openai.com>

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>
…nner after DB clear

Three grumpy-review findings, all touching the network-database-clear
path:

- The Network Chooser and the top panel derived a network's display
  label independently and disagreed after a database clear (e.g. one
  side showing "Regtest", the other "Local"). Extracted the single
  source of truth, chooser_network_label(), and routed both call
  sites through it.
- The DPNS contested-names screen rendered its own inline error
  banner in addition to the global MessageBanner AppState already
  displays for the same backend-task error, showing the failure
  twice. Removed the inline render path — MessageBanner is now the
  sole path for this screen's backend-task errors.
- A scheduled-vote-sweep failure raised while no local voting identity
  exists is expected background noise (there's nothing to sweep for),
  but it surfaced as a persistent error banner with no way to
  dismiss it short of navigating away. AppState now recognizes
  ScheduledVoteSweepFailed wrapping a NoVotingIdentity source via
  scheduled_vote_sweep_is_quiet() and handles it without a banner.

Targeted tests pass; clippy and fmt clean.

Co-Authored-By: Codex Sol <noreply@openai.com>

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>
…uccess token refreshes

Two grumpy-review findings:

- Document/claim fetch results were matched back to the screen that
  requested them by type alone, not by which specific dispatch they
  answered. A stale error from an earlier, already-superseded fetch
  could arrive after a newer identical redispatch had already
  succeeded and suppress or overwrite that success. Introduced a
  process-monotonic dispatch id (BackendTaskContext::for_dispatch),
  threaded through AppAction/AppState, so each screen's
  correlate_fetch_action() / should_suppress_backend_task_error() can
  tell an outdated response from the one it's actually waiting on.

- sync_token_balances_now() reported success whenever it merely
  *called* the upstream sync, even when upstream's own single-flight
  guard silently skipped the pass because another refresh was already
  running (is_syncing() true) — the UI showed balances as fresh when
  no sync actually happened. Added TokenBalanceSyncOutcome
  (Performed/AlreadyInFlight) so callers can distinguish a completed
  refresh from a skipped one, and a new TaskError::
  TokenBalanceRefreshSkipped for the skipped case.

Targeted suites pass (contracts_documents_screen, view_token_claims_screen,
token-balance backend, wallet-outcome unit tests); clippy clean.

Co-Authored-By: Codex Sol <noreply@openai.com>

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>
…erflow

The advanced-send screen coalesced duplicate Platform output/input
rows by summing their amounts with plain arithmetic addition — a
crafted or pasted set of duplicate rows near the Platform credit
range's upper bound panicked the app on overflow (debug builds) or
silently wrapped (release builds) instead of being rejected as
invalid input. Switched aggregation to checked_add, returning the new
typed TaskError::AdvancedPlatformOutputsOverflow /
AdvancedPlatformInputsOverflow on overflow. Also added
AdvancedPlatformAmountInvalid and AdvancedPlatformOutputAddressInvalid
so the surrounding parse failures get dedicated typed variants instead
of falling through to a generic error.

Red-before/green-after confirmed (old code panicked on the overflow
case; new code returns the typed error). ui::wallets::send_screen
14/14 pass; clippy clean.

Co-Authored-By: Codex Sol <noreply@openai.com>

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>
…success token refreshes (grumpy-review)

# Conflicts:
#	src/wallet_backend/mod.rs
… sticky sweep banner after DB clear (grumpy-review)
Co-Authored-By: Codex GPT-5 <noreply@openai.com>
…s, Settings

Closes the UX-003 gap found by the PR892 QA sweep and re-confirmed against
current HEAD: the global nav switcher was wired into Wallets, Identity Hub,
and Masternodes but not the other four root screens. Verified via Codex Sol
(gpt-5.6-sol, high effort): clippy clean, 5 new kittest cases green.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Closes two gaps from the PR892 QA sweep, re-confirmed against current HEAD:
SND-005 (no confirmation dialog before broadcasting a send, in either the
simple or Advanced Options path) and SND-014 (fee-reserve/balance-too-low
hint only rendered inside an unreachable validation branch). Verified via
Codex Sol (gpt-5.6-sol, high effort): clippy clean, 80 wallet-screen tests +
9 amount-input tests green, including 3 new regression tests proven red
before the fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Closes a QA-found gap from the PR860 delta sweep: the Token Creator
screen's "Creating token..." banner never dismissed on successful token
creation (leaked BannerHandle), same bug class as an earlier-fixed
sticky-banner issue but not covered by that fix. Verified via Codex Sol
(gpt-5.6-sol, high effort): clippy clean, new regression test proven
red before the fix, green after.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Delta re-test against the 2026-07-14/15 175-story sweep: WAL-032 (new story,
PASS), 34 baseline-FAIL stories re-verified (large majority now fixed),
category-representative PASS spot-checks (DPN/DPY/MN/DEV — DEV-005 upgraded
FAIL->PASS, all 8/8 Platform Info tools now working), plus TOK-011 (FIXED)
and ALK-002 (still failing, unchanged from baseline). Session was resumed
mid-run after an unrelated tooling kill; resumed cleanly from checkpoints.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@lklimek
lklimek merged commit 1397b43 into v1.0-dev Jul 16, 2026
5 checks passed
@lklimek
lklimek deleted the docs/platform-wallet-migration-design branch July 16, 2026 23:29

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Preliminary review — Codex only

Carried-forward prior findings: prior-5, prior-6, and prior-7 remain valid; prior-1 is outdated, while prior-2 through prior-4 are fixed. New findings in the latest delta: the token-refresh outcome inference is racy, and the simple Core-to-Platform confirmation misstates the recipient amount. Additional cumulative current-head inspection: the proposed Core aggregate-overflow blocker was dropped because its UI arithmetic predates the PR base and the claimed reachable failure was not established.

Validated blockers were found in the Codex precheck. Sonnet is deferred until a fresh Codex revalidation clears the blocker gate.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (failed_pre_evidence), gpt-5.6-sol — general (failed_pre_evidence), gpt-5.6-sol — general (failed_pre_evidence), gpt-5.6-sol — general (failed_pre_evidence), gpt-5.6-sol — general (failed_pre_evidence), gpt-5.6-sol — general (failed_pre_evidence), gpt-5.6-sol — general (failed_pre_evidence), gpt-5.6-sol — general (completed)
  • Verifier: gpt-5.6-sol — verifier
  • Sonnet: not run (deferred by blocker gate)

🔴 2 blocking

3 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/wallet_backend/mod.rs`:
- [BLOCKING] src/wallet_backend/mod.rs:124-131: Token refresh ownership cannot be inferred from before/after status samples
  Upstream `IdentitySyncManager::sync_now()` atomically claims `is_syncing` and returns no outcome when another pass owns it. The reads before and after that call are not tied to its ownership. If this call completes and the periodic loop claims the flag before the second sample, DET returns `AlreadyInFlight`, skips `refresh_token_balances()`, and surfaces `TokenBalanceRefreshSkipped` even though the requested pass completed. The DET snapshot therefore remains stale. Upstream must return an explicit performed/skipped outcome, or expose an atomic completion generation that DET can use instead of reconstructing ownership from independent samples.

In `src/ui/wallets/send_screen.rs`:
- [BLOCKING] src/ui/wallets/send_screen.rs:2988-2995: Simple confirmation hides that the fee reduces the recipient amount
  For a Core-to-Platform send, `current_fee_preview()` returns `FeePreview::deducted_from_amount`, and the prepared backend task sets `fee_deduct_from_output: true`. The final confirmation nevertheless says the entered amount will be sent to the destination and ignores `recipient_receives_credits`. The recipient receives less than the amount the confirmation claims. Include the calculated recipient amount or explicitly state that the fee is deducted from the entered amount, matching the advanced confirmation.

Comment thread src/wallet_backend/mod.rs
Comment thread src/ui/wallets/send_screen.rs
@Claudius-Maginificent

Copy link
Copy Markdown
Collaborator

Follow-up: the review feedback below that was still unaddressed at merge time has been triaged and fixed in #902 (13 distinct findings — 2 HIGH, 4 MEDIUM, 7 LOW). Each unresolved thread was independently re-verified against current code before being marked; several bot auto-resolutions here were incorrect (commit-diff heuristic, not semantic verification) and have been corrected. Threads now resolved reference this comment for context.

🤖 Co-authored by Claudius the Magnificent AI Agent

lklimek added a commit that referenced this pull request Jul 18, 2026
…ad push branch (#898)

* ci: run tests and clippy on every non-draft pull request

The `pull_request` triggers for both gates were path-filtered, so a PR that
touched no Rust source got no test, clippy, or fmt run at all — and reported
green anyway. A skipped gate and a passing gate are indistinguishable at the
merge button, which makes "CI is green" mean "green, or it never ran".

Drop the `paths` filter from both `pull_request` triggers so every non-draft PR
runs the full gate. Pushes to master / v*-dev stay path-filtered, where the
filter is a pure cost saving and nothing gates on the result.

Add `workflow_dispatch` to both. Draft PRs are suppressed by the existing
`draft != true` guard and previously had no way to run CI at all; a manual run
now covers them (against the branch head, not the PR merge commit).

Fix the push trigger's dead `main` branch entry — this repo's release branch is
`master`, so pushes to it matched nothing and ran no tests.

Document the resulting local-vs-CI split in CLAUDE.md: CI owns the full sweep,
so local runs should stay narrowly scoped to the change rather than duplicating
what CI is about to do.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018m6b2TRP1e6S3uQHcaq3Cq

* ci: allow manual runs, cover the toolchain, drop the dead push branch

Address review feedback on the previous commit: keep the `paths` filters
(documentation changes should not spend CI minutes) and take CLAUDE.md out of
this PR entirely — it collides with PR #860, which rewrites the same section of
that file, so it ships separately against #860's branch.

Three changes remain, all narrow:

- Add `workflow_dispatch` to both workflows. Draft PRs are suppressed by the
  existing `draft != true` guard and previously had no way to run CI at all —
  the only route to a green check was marking the PR ready for review. A manual
  run now covers them; it tests the branch head rather than the PR merge commit.

- Add `rust-toolchain.toml` to the path filters. The file pins the toolchain for
  every build, but matched no filter, so a toolchain bump triggered neither
  tests nor clippy. `**/*.rs` already covers build.rs and all crate sources, so
  the filters otherwise fire on any Rust code change and on no documentation
  change, which is the intent.

- Drop the push branch list down to `v*-dev`. It previously listed `main`, which
  does not exist in this repo, and `master` is release-only — neither needs a
  push-triggered run.

The path lists are shared between the `push` and `pull_request` triggers via a
YAML anchor so the two cannot drift apart; GitHub Actions has supported anchors
since 2025-09-18, and path filters are a documented use case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018m6b2TRP1e6S3uQHcaq3Cq

* ci: run the toolchain the project actually pins

Review feedback: the previous commit added rust-toolchain.toml to the path
filters, but neither job used the file's compiler, so the trigger proved
nothing. Both installed a toolchain by name with `override: true`, and a rustup
directory override takes precedence over rust-toolchain.toml.

Worse than a hollow trigger, the test job named `stable` while the project pins
1.92 — so the suite has never run on the pinned toolchain at all. Clippy named
1.92, matching only because the number was duplicated by hand; the next bump
would have silently kept linting the old compiler.

Install via actions-rust-lang/setup-rust-toolchain with no toolchain input, so
both jobs take the channel and components straight from rust-toolchain.toml.
That file becomes the single source of truth, and — since a change to it now
triggers both workflows — a toolchain bump is tested and linted by the compiler
it introduces. Clippy's components move into the file for the same reason.

Two of the action's defaults are pinned rather than inherited: it defaults
RUSTFLAGS to `-D warnings`, which would newly fail the test job on any warning
(Clippy owns that gate, and its own step already passes the flag), and it
defaults to its own cargo cache, which the existing actions/cache step already
covers.

Also drops actions-rs/toolchain from both jobs; the action is archived, and
dtolnay/rust-toolchain would not fix this because it takes its toolchain from
its @ref rather than reading rust-toolchain.toml.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018m6b2TRP1e6S3uQHcaq3Cq

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
lklimek added a commit that referenced this pull request Jul 20, 2026
* fix(wallets): disclose deducted fees in confirmations

Co-Authored-By: Claude GPT-5 <noreply@anthropic.com>

* fix: harden identity deposit onboarding

Reconcile deposit state from wallet snapshots, prevent duplicate address requests, and preserve retry controls on derivation failures.

Co-Authored-By: Claude GPT-5 Codex <noreply@anthropic.com>

* fix: harden app task and network guards

Release scheduled-vote sweep guards after panicking task joins and require explicit network confirmation when legacy settings I/O cannot restore the saved network.

Co-Authored-By: OpenAI Codex GPT-5 <noreply@openai.com>

* docs(gui-testing): make scenario launch portable

Co-Authored-By: Codex GPT-5 <noreply@openai.com>

* docs(gui-testing): vendor GUI launch procedure

Co-Authored-By: Codex GPT-5 <noreply@openai.com>

* test(settings): pin legacy wire fixture

Co-Authored-By: Codex GPT-5 <noreply@openai.com>

* fix(migration): guard partial settings import

Co-Authored-By: Codex GPT-5 <noreply@openai.com>

* fix: harden wallet history hydration and soften token-sync banner

Skip corrupt transaction-history rows instead of aborting wallet
registration, and surface a skipped token-balance refresh as an info
banner instead of a sticky error (the balance is very likely still
fresh from the periodic sync loop that raced it).

Co-Authored-By: OpenAI Codex GPT-5 <noreply@openai.com>

* docs: log PR #860 follow-up fixes in changelog

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: address PR 902 review findings

Addresses bbca6608394e, e9bdf38afeeb, SEC-001, SEC-002,
SEC-004, SEC-005, SEC-007, PROJ-002, PROJ-003, PROJ-004,
CODE-002, CODE-003, CODE-004, CODE-005, CODE-007, CODE-008,
CODE-009, CODE-010, CODE-012, CODE-013, CODE-014, and CODE-015.

Co-Authored-By: Claude GPT-5 <noreply@anthropic.com>

* docs: correct changelog and gui-testing docs per PR 902 review (DOC-001..003)

- CHANGELOG: add the missing token-balance-refresh-status entry (DOC-002).
- docs/gui-testing/README.md + scenarios/TEMPLATE.md: resolve the test
  binary via `cargo metadata`'s target_directory instead of assuming
  `${CARGO_TARGET_DIR:-target}`, which silently breaks when the target dir
  is redirected via .cargo/config.toml rather than the env var (DOC-001).
  Also stop implying the desktop-gui automation skill is required (DOC-003).

Co-Authored-By: Codex Sol <noreply@openai.com>

---------

Co-authored-by: Lukasz Klimek <842586+lklimek@users.noreply.github.com>
Co-authored-by: Claude GPT-5 <noreply@anthropic.com>
Co-authored-by: OpenAI Codex GPT-5 <noreply@openai.com>
orchardpaytl pushed a commit to orchardpaytl/orchardpay that referenced this pull request Jul 21, 2026
)

Upstream landed a full rewrite (581 files, new platform-wallet/
wallet_backend crates, egui 0.33->0.35, dash-sdk rev bump, src/spv/
removed) since our original fork point. Rather than merge that
through OrchardPay's ~101 mostly-mechanical rebrand-diff files, this
re-forks from upstream's current v1.0-dev tip and reapplies:

- The dash-evo-tool -> OrchardPay rebrand pass (crate name/path, env
  vars, app-dir paths, log filenames, Flatpak/macOS bundle IDs, user
  agent string) across every living file. Historical docs/ai-design/
  QA-campaign records are deliberately left untouched — they document
  what upstream actually observed testing the real "Dash Evo Tool" at
  the time, and rewriting them would misrepresent history. Real
  upstream GitHub issue/repo references (e.g. dashpay/dash-evo-tool
  dashpay#660, dashpay#889) are likewise left pointing at the real upstream, not
  rewritten into a nonexistent dashpay/orchardpay repo.
- The 5 ORCHARDPAY-TODO(dashpay-legacy) markers and LEGACY module doc
  comments flagging DashPay's contact/messaging code as superseded by
  OrchardPay's ZK-based model (docs/orchardpay/PROTOCOL_DESIGN.md,
  docs/ORCHARDPAY_MIGRATION.md), adapted to their new locations (the
  RootScreenType enum moved into src/model/settings.rs; the standalone
  Dashpay nav entry is now hidden by upstream itself).
- The onboarding guided-setup stepper (src/ui/components/
  onboarding_progress.rs, wired into src/app.rs), adapted to eframe
  0.35's ui-based App::ui entry point and egui's unified Panel type
  (TopBottomPanel was replaced by Panel::top). Added a kittest
  regression test exercising the real stepper end-to-end.
- Fixed a rebrand bug the new test caught: src/logging.rs's log
  rotation used bare "det"/"det-stderr" stems (without a .log suffix)
  that the original substitution pass missed, causing rotation to
  look for the wrong filenames.

Verified: cargo build/clippy/test all green on the new base
(1944 tests, 0 failures).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.

3 participants