chore: merge v1.0-dev into zk and consolidate migrations#789
Conversation
* fix(ci): statically link MinGW runtime for Windows builds Adds -static-libgcc, -static-libstdc++, and static winpthread rustflags for the x86_64-pc-windows-gnu target so the Windows binary no longer depends on MinGW runtime DLLs absent on user machines. Also sets OPENSSL_STATIC=1 on the build step to statically link OpenSSL. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): bundle SQLite statically and add Windows binary sanity check - Add `bundled` feature to rusqlite so SQLite is compiled from source and statically linked, eliminating the sqlite3.dll dependency - Remove the Windows libsql CI step that downloaded sqlite3.dll and created an import library with a broken (null) DLL name entry - Add post-build verification step that inspects the PE import table and fails if null DLL imports or MinGW runtime DLLs are found Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: fix wallet UTXO tests for bundled SQLite FK enforcement Insert the wallet record before the address row in `register_test_address` so the `addresses.seed_hash` FK (enforced when SQLite is compiled with `SQLITE_DEFAULT_FOREIGN_KEYS=1` via the `bundled` feature) is satisfied. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: add unified message display design documents Add UX specification, technical architecture, and HTML mockup for the MessageBanner component that will replace the ~50 ad-hoc error/message display implementations across screens with a single reusable component. Key design decisions: - Per-screen MessageBanner with show()/set_message() API - All colors via DashColors (zero hardcoded Color32 values) - 4 severity levels: Error, Warning, Success, Info - Auto-dismiss for Success/Info (5s), persistent for Error/Warning - Follows Component Design Pattern conventions (private fields, builder, show) - No changes to BackendTask/TaskResult/AppState architecture Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add MessageType::Warning * chore: initial implementation * chore: docs * chore: self review * refactor(context): replace RwLock<Sdk> with ArcSwap<Sdk> (#600) * refactor(context): replace RwLock<Sdk> with ArcSwap<Sdk> Sdk is internally thread-safe (Arc, ArcSwapOption, atomics) and all methods take &self. The RwLock was adding unnecessary contention across backend tasks. Using ArcSwap instead of plain Sdk because reinit_core_client_and_sdk() needs to atomically swap the entire Sdk instance when config changes. ArcSwap provides lock-free reads with atomic swap for the rare write. Suggested-by: lklimek * fix: address CodeRabbit review findings for ArcSwap migration - Fix import ordering: move arc_swap::ArcSwap before crossbeam_channel - Remove redundant SDK loads in load_identity_from_wallet, register_dpns_name, and load_identity — use the sdk parameter already passed to these functions - Fix stale TODO referencing removed sdk.read().unwrap() API - Rename sdk_guard → sdk in transfer, withdraw_from_identity, and refresh_loaded_identities_dpns_names (no longer lock guards) - Pass &sdk to run_platform_info_task from dispatch site instead of reloading internally - Fix leftover sdk.write() call in context_provider.rs (RwLock remnant) - Add missing Color32 import in wallets dialogs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: address remaining CodeRabbit review feedback on ArcSwap migration - Move SDK load outside for loop in refresh_loaded_identities_dpns_names.rs so it's loaded once for the batch instead of on each iteration - Update stale TODO comment in default_platform_version() to reflect that this is a free function with no sdk access Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: consolidate double read-lock on spv_context_provider Clone the SPV provider in a single lock acquisition, then bind app context on the clone instead of locking twice. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * refactor: remove unused Insight API and show_in_ui config fields (#597) * refactor: remove unused Insight API references The `insight_api_url` field in `NetworkConfig` and its associated `insight_api_uri()` method were never used in production code (both marked `#[allow(dead_code)]`). Remove the field, method, config entries, env example lines, and related tests. https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn * refactor: remove unused `show_in_ui` field from NetworkConfig The `show_in_ui` field was defined on `NetworkConfig` and serialized in `save()`, but never read by any production code to control network visibility. Remove the field, its serialization, env example lines, and test references. https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn * fix: add missing `Color32` import in wallet dialogs https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn --------- Co-authored-by: Claude <noreply@anthropic.com> * build: add Flatpak packaging and CI workflow (#589) * build: remove snap version * build: add Flatpak packaging and CI workflow Add Flatpak build manifest, desktop entry, AppStream metadata, and GitHub Actions workflow for building and distributing Flatpak bundles. Uses freedesktop 25.08 runtime with rust-stable and llvm21 extensions. No application source code changes required - works in SPV mode by default. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * build: address review findings for Flatpak packaging - Pin GitHub Actions to commit SHAs for supply chain security - Upgrade softprops/action-gh-release from v1 to v2.2.2 - Remove redundant --socket=x11 (fallback-x11 suffices) - Remove duplicate tag trigger preventing double builds on release - Remove duplicate env vars inherited from top-level build-options - Add Flatpak build artifacts to .gitignore - Add bugtracker URL to AppStream metainfo - Remove deprecated <categories> from metainfo (use .desktop instead) - Add Terminal=false and Keywords to desktop entry - Add disk space check after SDK install in CI - Rename artifact to include architecture suffix Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * build: simplify CI workflows for Linux-only releases - Remove "Free disk space" step from flatpak and release workflows - Remove Windows and macOS builds from release workflow - Use "ubuntu" runner image instead of pinned versions - Clean up unused matrix.ext references Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * build: attach to existing releases instead of creating new ones - Replace release-creating job with attach-to-release (only on release event) - Add 14-day retention for build artifacts - On tag push or workflow_dispatch, only upload artifacts (no release) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * revert: restore release.yml to original v1.0-dev version The release workflow changes were out of scope for the Flatpak PR. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address CodeRabbit review comments - Fix CRLF line endings in Flatpak manifest (convert to LF) - Set app_id on ViewportBuilder to match desktop StartupWMClass - Use --locked flag for reproducible cargo builds in Flatpak - Rename --repo=repo to --repo=flatpak-repo to match .gitignore - Add architecture note for protoc x86_64 binary Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add Flatpak install instructions to README Add a dedicated section for installing via Flatpak on Linux, clarify that prerequisites are only needed for building from source, and rename "Installation" to "Build from Source" for clarity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: match StartupWMClass to Flatpak app_id Use reverse-DNS format org.dash.DashEvoTool to match the Wayland app_id set via ViewportBuilder::with_app_id(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use ** glob for branch trigger to match feat/flatpak Single * doesn't match path separators in GitHub Actions branch filters. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add aarch64 Flatpak build and caching to CI - Add matrix strategy for parallel x86_64 and aarch64 builds - Patch protoc URL/sha256 per architecture at build time - Cache .flatpak-builder directory keyed on arch + manifest + lockfile - Pin actions/cache to SHA Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: convert desktop and metainfo files to LF line endings Flatpak builder validates desktop files and rejects CRLF line endings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * build: cancel in-progress Flatpak builds on new push Add concurrency group keyed on git ref so a new push cancels any running build for the same branch or release. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review findings for Flatpak packaging - Remove unnecessary --filesystem=xdg-config/dash-evo-tool:create (Flatpak already redirects XDG_CONFIG_HOME to sandbox) - Add categories and keywords to AppStream metainfo for discoverability - Update README with both x86_64/aarch64 install commands, uninstall instructions, and Flatpak data path note - Clarify aarch64 comment in manifest to reference CI sed patching Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: workflow timeout and perms * fix: move permissions to job level in Flatpak workflow Step-level permissions are not valid in GitHub Actions. Move contents: write to the job level where it is needed for release attachment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * build: cache Cargo registry and target in Flatpak CI Bind-mount host-side cargo-cache and cargo-target directories into the Flatpak build sandbox so CARGO_HOME and target/ persist across builds. Uses split restore/save with cleanup of incremental and registry/src (similar to Swatinem/rust-cache). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: scope cargo cache bind-mount sed to build-args only The previous sed matched --share=network in both finish-args and build-args, corrupting finish-args. Use a sed range to only target the build-args section. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Apply suggestions from code review --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * chore: fix build * chore: use new error handling everywhere - not self reviewed * chore: use message banner to show progress * fix: start elapsed counter at 1s instead of 0s Aligns elapsed display with the countdown timer which already adds 1 to avoid showing "0s" immediately. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: rabbit review * Update src/app.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * chore: peer review * chore: fix build errors * fix(ui): make MessageBanner::set_global truly idempotent set_global() no longer resets timestamps, auto-dismiss timer, or logged flag when a banner with identical text already exists. This makes it safe to call every frame without log spam or timer restarts. Cherry-picked from origin/fix/spv-peer-timeout (08e3b3b). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): address code review findings R01, R03, R06, R09, R10 - R01: Replace expect("No key selected") with graceful match + error banner in 11 token screens to prevent panics on missing signing key - R03: Remove dead backend_message field from AddExistingIdentityScreen - R06: Replace is_some() + unwrap() with idiomatic if-let-Some pattern in 10 token screens; use is_some_and() in structs.rs - R09: Add use imports for MessageBanner in 5 dashpay screens, replacing 22 fully-qualified crate::ui::components::MessageBanner:: calls - R10: Replace custom_dash_qt_error_message inline rendering with MessageBanner::set_global in network_chooser_screen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): address review findings SEC-08, SEC-09, RUST-015, SEC-05, SEC-07 - SEC-08: Restore safe if-let-Some pattern in WithdrawalScreen::refresh() to prevent double unwrap() panic on DB error or deleted identity - SEC-09: Restore original DB lookup in SendPaymentScreen::load_contact_info() replacing hardcoded "alice.dash" mock data - RUST-015: Revert unimplemented!() back to ui.label() in update_token_config MarketplaceTradeMode arm - SEC-05: Add success banners for contact request accept/reject in ContactRequests::display_task_result - SEC-07: Add MessageBanner::clear_all_global() and call it from AppState::change_network() to prevent stale banners leaking across network switches Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update coding conventions and message display guidance Add fallible constructor rule (Result<Self, ...> when they can fail), rename section to "General rules", and document MessageBanner idempotency (no guard needed for set_global). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): replace expect/panic with graceful error handling (SEC-10) Replace all expect() calls in token screen constructors and confirmation handlers with MessageBanner error display. Constructors handle errors internally and return Self with degraded state, keeping create_screen() clean. refresh() methods now show errors via MessageBanner instead of tracing-only logging. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(ui): accept impl Display/Debug in MessageBanner API Change MessageBanner public methods to accept `impl Display` for message text and `impl Debug` for details, instead of `&str`. Remove needless `&format!(...)` borrows across 27 call sites. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): remove error Failed to get best chain lock for mainnet, testnet, devnet, and local Fixes #633 * feat(ui): add automatic connection status banners Display persistent MessageBanner notifications based on network connection state transitions. Mode-aware messages guide users toward the right recovery action (RPC vs SPV). - Disconnected (RPC): "Disconnected — check that Dash Core is running" - Disconnected (SPV): "Disconnected — check your internet connection" - Syncing (RPC): "Syncing with Dash Core…" - Syncing (SPV): "SPV sync in progress…" - Synced: banner cleared Uses Option<OverallConnectionState> for change detection, with None as initial/post-network-switch sentinel to force first evaluation. Closes #667 (partial — action links deferred to follow-up) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): pass TaskError directly to with_details() to avoid double-formatting The previous code used `format!("{err:?}")` which produced a String, then `with_details()` applied `{:#?}` again — wrapping the output in quotes and escaping inner characters. Passing `&err` directly lets Debug format once. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): correct copy-paste error messages in token screens Replace "Burning" error messages that were copy-pasted from burn screen into freeze, destroy, and resume token screens with contextually correct messages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): restore lost success/error messages in 5 screens Replace display_message() calls with MessageBanner::set_global() in screens where display_message() is now a side-effect-only handler and no longer displays messages directly. Affected screens: create_asset_lock_screen, wallets_screen (MineBlocks), address_table (export error), profile_screen (validation), contact_details. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): replace unwrap/expect with graceful error handling Replace double unwrap in transfer_screen refresh() with unwrap_or_else + MessageBanner error display, matching the pattern from withdraw_screen. SEC-002 tokens_screen skipped: the .expect() calls are only on compile-time embedded image data (include_bytes!) which is safe. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(ui): migrate masternode_list_diff_screen to global MessageBanner Replace ~15 local ui_state.message assignments and custom render_message_banner() with MessageBanner::set_global() via the display_message() trait method. Remove the message field from UiState and the unused Color32 import. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): improve banner eviction logging and atomics - Upgrade BANNER_KEY_COUNTER from Relaxed to SeqCst ordering for future-proofing against multi-threaded usage - Log evicted banners at warn level in set_global() and replace_global() - Add comment explaining why show_global() always writes back Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove resolved TODO.md All items tracked in the unified message display TODO have been addressed or moved to the review findings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(ui): add INTENTIONAL markers and API documentation - Document why with_details() accepts Debug (not Display): structured error context is more useful in diagnostic details panes - Document replace_global() fallback-to-add behavior as intentional - Add INTENTIONAL(SEC-003) marker for developer mode error details - Add INTENTIONAL(SEC-004) marker for BannerHandle Send+Sync safety Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(ui): extract shared token validation and fix ordering - Add validate_signing_key() helper in tokens/mod.rs to eliminate duplicated signing key validation across 12 token screens - Move signing key validation BEFORE WaitingForResult state transition so users see immediate errors instead of loading spinner then error - Replace is_err()/unwrap() anti-pattern with idiomatic let-else blocks in freeze, mint, transfer, destroy_frozen_funds, unfreeze screens Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(ui): return Result from get_selected_wallet Replace &mut Option<String> error out-parameter with idiomatic Result<Option<Arc<RwLock<Wallet>>>, String>. Update 26+ callsites across identity, token, DashPay, and contract screens. Callsite patterns: unwrap_or_else with MessageBanner for user-visible errors, unwrap_or(None) where errors were previously silently ignored. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): resolve duplicate imports and clippy warnings Remove duplicate MessageBanner imports in create_asset_lock_screen and wallets_screen/mod. Fix needless_borrows_for_generic_args clippy lints in profile_screen, transfer_screen, and wallets_screen. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style: reorder imports in masternode_list_diff_screen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): address review findings from MessageBanner migration audit Apply 13 triaged review fixes plus 1 bug fix across 22 files: - Remove dead error state fields (backend_message, error_message, Error variant) - Replace .expect() panics with graceful fallback + MessageBanner in token screens - Fix missing MessageBanner::show_global() on contracts documents screen - Migrate DocumentActionScreen inline errors to MessageBanner - Replace unwrap_or(None) with error-reporting fallback in DashPay screens - Fix replace_global idempotency and use relaxed atomic ordering in banner - Extract shared set_error_banner helper for 8 token screens - Restore correct Some(0) wallet index semantics - Document BannerHandle lifecycle in CLAUDE.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: validate token description length before sending to Platform (#530) * fix: validate token description length before sending to Platform Descriptions must be either empty or 3-100 characters long. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(ui): validate token description by char count, not byte length String::len() counts UTF-8 bytes, causing multi-byte characters (CJK, emoji) to be miscounted against the 3–100 limit. Switch to chars().count() and update all UI labels to surface the minimum. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Lukasz Klimek <842586+lklimek@users.noreply.github.com> * refactor(ui): consolidate banner extension traits into message_banner Move BannerHandleExt and ResultBannerExt from banner_ext.rs into message_banner.rs where they belong. Merge take_and_clear() into OptionBannerExt trait (impl for Option<BannerHandle>) alongside or_show_error() for Option. Remove the separate banner_ext module and Clearable helper trait for simplicity. Apply review findings: DRY patterns (take_and_clear, or_show_error, load_identities_with_banner), fix .expect() panics in constructors, restore known_identities in refresh(), narrow pub field visibility, add ScreenLike doc comments, and update CLAUDE.md conventions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * doc(tmp): review guide for pr 604 * fix(ui): address review findings from grumpy-review iteration 1 - Replace .expect() panics in TransferTokensScreen and ClaimTokensScreen constructors with graceful degradation via Option<QualifiedIdentity> and MessageBanner error display (PROJ-001 HIGH) - Fix CLAUDE.md referencing non-existent BannerHandleExt trait name, corrected to OptionBannerExt (PROJ-002 MEDIUM) - Update set_global to preserve message_type when same text appears with different severity (RUST-001 MEDIUM) - Standardize display_message to handle both Error and Warning across all 11 token screens (RUST-002 MEDIUM) - Replace 21 manual take().clear() patterns with take_and_clear() across 6 files (RUST-003 MEDIUM) - Remove unused OptionBannerExt::or_show_error method (RUST-004 MEDIUM) - Migrate update_token_config from old error_message pattern to set_error_banner closure (RUST-005 MEDIUM) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * perf(ui): replace per-frame QualifiedIdentity clone with borrow Use .as_ref() instead of .clone() in the ui() identity guard of TransferTokensScreen and ClaimTokensScreen. QualifiedIdentity (Identity + KeyStorage + BTreeMap + Vec) was being cloned 60x/sec; now only borrowed for display, with clones deferred to button-click paths that actually need ownership. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(ui): add OptionResultExt::or_show_error for Option<T> Mirrors ResultBannerExt::or_show_error but for Option<T>: if None, displays a global error banner with the given message. Enables concise patterns like: identities.first().cloned().or_show_error(ctx, "No identities loaded") Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): address review findings from grumpy-review iteration 2 - Standardize display_message to handle both Error and Warning across 13 non-token screens that were missed in iteration 1 (PROJ-001 MEDIUM) - Replace .expect() panic in AddKeyScreen::refresh() with graceful or_show_error() + unwrap_or_default() (PROJ-002 MEDIUM) - Rename OptionResultExt to OptionBannerShowExt to avoid confusion with ResultBannerExt (RUST-001 MEDIUM) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): handle Warning in add_new_identity_screen display_message Missed in the previous sweep — standardize display_message to handle both Error and Warning, matching all other screens. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): standardize display_message side-effect patterns across screens Guard side effects with Error|Warning match, use take_and_clear(), and remove redundant MessageBanner::set_global() call in 4 screens. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(deps): update dashpay/platform to rev 570e3af0 Adapt to breaking changes in rust-dashcore (a05d256f → 2824e52a): - Replace removed FeeLevel enum with FeeRate::normal() direct calls - Replace removed WalletManager::create_unsigned_payment_transaction() with TransactionBuilder + WalletManager::get_change_address() - Replace removed DashSpvClientInterface/DashSpvClientCommand with direct Arc<SpvClient> for quorum lookups via get_quorum_at_height() - Replace removed start()+monitor_network() with client.run(token) - Add .await to now-async subscribe_sync/network/progress methods - Replace removed SyncState::Initializing with SyncState::WaitForEvents Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: log backtrace on panic * fix: panic in asset locks * fix(ui): address PR #604 review comments (CMT-001, CMT-002, CMT-003) - Fix QR scanner form reset matching wrong result type (CMT-001) - Remove dangerous identity fallback in token transfer screen (CMT-002) - Add fee-aware validation before credit transfers (CMT-003) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(spv): replace AsyncRwLock with ArcSwapOption for SPV client reference The SPV client reference only needs atomic set/clear (on start/stop) and wait-free reads (quorum lookups). ArcSwapOption is a better fit than AsyncRwLock<Option<Arc<...>>> — no lock contention, no async in blocking context, and consistent with how AppContext already uses ArcSwap for the SDK. Also fixes stale doc comment referencing removed DashSpvClientInterface. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): address PR #604 review comments iteration 2 (transfer_tokens_screen) - Remove duplicate conflicting banner on missing identity (CMT-003) - Use generic banner messages with with_details() for errors (CMT-002) - Fix refresh to match specific token by contract+position (CMT-001) - Document error banner pattern in CLAUDE.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [claudesquad] update from 'testing research' on 02 Mar 26 13:25 CET (paused) * test: add backend E2E test harness and SPV wallet test Add test-only accessors (db(), wallets()) on AppContext gated behind cfg(test/testing), fix compilation errors in the backend-e2e test (private field access, unused import), and apply nightly rustfmt. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add DASH_EVO_DATA_DIR env var to override app data directory Allows tests and CI to redirect all app data (database, SPV chain state, .env config) to a temp directory. The backend-e2e test harness now uses this to achieve full data isolation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add backend E2E test framework with shared state and funded wallets Evolve the prototype backend E2E test harness into a reusable framework: - LazyLock shared BackendTestContext with persistent workdir, SPV, and framework wallet (funded via E2E_WALLET_MNEMONIC or testnet faucet) - Task runner wrapper, polling wait helpers, faucet HTTP client - Identity key derivation helpers for wallet-funded registration - Six test scenarios: SPV wallet, identity create, identity withdraw, send/receive funds, fetch contracts, register DPNS name - Move default_identity_key_specs() from UI to backend_task::identity (domain logic, not UI concern) and make IdentityKeys fields pub - Add dashpay_contract_id() test accessor to AppContext Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): use tokio::sync::OnceCell instead of LazyLock for async init LazyLock triggers synchronously inside the #[tokio::test] runtime, causing "Cannot start a runtime from within a runtime" when the init function calls block_on(). Switch to tokio::sync::OnceCell with an async init() method so shared state initialization runs cooperatively within the existing tokio runtime. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): handle persistent DB and SPV balance sync in E2E harness - Wait for SPV to sync existing wallet balance before checking if faucet funding is needed (pre-funded wallets need time to discover on-chain UTXOs) - Handle "already imported" error gracefully when framework wallet exists in persistent DB from a previous run Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): wait for spendable balance and retry sends in E2E harness The SPV wallet reports total balance (including unconfirmed) but only confirmed/IS-locked UTXOs are available for transaction building. This caused "Insufficient funds" errors when tests tried to spend immediately after receiving funds. - Add wait_for_spendable_balance() that checks confirmed_balance_duffs() and triggers reconcile_spv_wallets() on each poll iteration - Add retry logic (5 attempts, 10s backoff) to create_funded_test_wallet() for sends that fail with InsufficientFunds - Wait for framework wallet change output to become spendable after each send so subsequent calls don't fail - Add wait_for_spendable_balance() before identity registration in all identity/DPNS tests - Add send_with_retry() helper in send_funds test - Add developer-facing README.md for the test framework Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): use tokio-shared-rt for shared runtime and sweep orphaned wallets Replace per-test tokio runtimes with tokio-shared-rt's global shared runtime to prevent SPV background tasks from dying between test functions. Add automatic orphaned wallet fund recovery during setup — wallets persist in DB, so on next run the harness sweeps funds back to the framework wallet. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: handle OverallConnectionState::Error in connection banner Semantic merge conflict from v1.0-dev: PR #650 added an Error variant to OverallConnectionState, which our connection banner match didn't cover. Show an error banner when SPV sync enters the error state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(test): improve backend-e2e documentation and funding UX - Add backend E2E section to CLAUDE.md pointing to the full README - Document .env file handling (project root vs workdir) and precedence - Fix test attribute in README: tokio::test → tokio_shared_rt::test(shared) - Update init sequence to reflect current code (spendable wait, orphan sweep) - Document automatic cleanup-on-init of orphaned test wallets - Raise minimum balance threshold from 1 to 10 tDASH - Always panic with receive address when faucet fails and balance is below minimum (previously only panicked on zero balance) - Show receive address in spendable-balance timeout warning Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: patch SPV UTXO spendability flags before coin selection Upstream key-wallet-manager (rust-dashcore) never sets is_confirmed or is_instantlocked on UTXOs, but CoinSelector requires one of them. This caused "No UTXOs available for selection" errors despite having balance. Workaround infers status from block inclusion (height > 0 → confirmed, height == 0 → IS-locked). Ref: dashpay/rust-dashcore#514 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: harden backend E2E tests with retry logic and calibrated amounts - Increase funding amounts to avoid insufficient-funds flakes - Add 3-attempt retry for identity registration (chain height sync) - Retry on "No UTXOs" in send_with_retry alongside "Insufficient" - Wait for spendable balance in create_funded_test_wallet before return - Add CI workflow for backend E2E tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update test command in backend E2E workflow * ci: merge backend-e2e workflow into tests workflow (#727) * Initial plan * ci: merge backend-e2e workflow into tests workflow as an additional step Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com> * test: add cleanup_only noop test to sweep orphaned wallets Add a standalone test that triggers BackendTestContext initialization, which runs cleanup_test_wallets() as its final step. This can be run as a dedicated CI step after the E2E suite to sweep orphaned wallet funds back to the framework wallet. Run with: cargo test --test backend-e2e --all-features -- --ignored --nocapture cleanup_only Co-authored-by: lklimek <lklimek@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add cleanup step for E2E test wallets Added a cleanup step for E2E test wallets in the workflow. * Simplify E2E test workflow conditions Removed conditional checks for E2E_WALLET_MNEMONIC in test steps. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: lklimek <lklimek@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR #673 review comments from triage Production code: - Extract patch_utxo_spendability_flags() helper to deduplicate workaround in estimate_fallback_amount and build_unsigned_payment_tx - Add IdentityKeys::new() constructor - Make wallet+address persistence atomic via store_wallet_with_addresses() - Add pending_wallet_selection after wallet creation Test code: - Add DPNS registration retry for identity propagation delay - Use u64 hex for DPNS name uniqueness (CMT-023) - Calibrate test funding amounts per reviewer feedback - Add fragility note on string-match wallet detection (CMT-006) - Add TODO for identity fund withdrawal in cleanup (CMT-032) - Add INTENTIONAL comment for bounded channel design (CMT-017) - Add balance assertion for return leg in send_funds test - Log reconcile_spv_wallets errors in wait helpers - Use is_ok_and for clarity in spv_wallet test Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: remove UTXO spendability workaround, update platform dep Remove `patch_utxo_spendability_flags()` that faked IS-locked status on mempool UTXOs. Wait for upstream fix (dashpay/rust-dashcore#514) to properly set is_confirmed/is_instantlocked flags on UTXOs. Also: - Update dashpay/platform rev to aa86b74f7e2 - Adapt to upstream API: FeeLevel→FeeRate, remove NetworkExt import - Fix retry to catch "No UTXOs" errors in test harness Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: deduplicate default_identity_key_specs Move the single canonical copy to backend_task::identity::mod and have the UI screen import it, eliminating ~240 lines of duplicated function and tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ci: temporarily disable backend E2E tests in CI The backend E2E tests need updates after the TaskError migration (#739) changed AppContext field visibility. Commenting out the CI steps until the tests are adapted. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: type-safe wallet creation and identity key visibility - Restrict IdentityKeys fields to pub(crate) to prevent private key exposure outside the crate - Change register_wallet() to return TaskError instead of String, using proper rusqlite error matching via is_unique_constraint_violation() and a new WalletAlreadyImported variant - Change Wallet::new_from_seed() to accept Option<&Secret> for password instead of Option<&str>, keeping sensitive data in the Secret wrapper - Change Wallet::new_from_seed() to return TaskError instead of String, with a new WalletKeyDerivationFailed variant for derivation errors - Move build_identity_registration() and get_receive_address() from test helpers to production code in src/backend_task/identity/mod.rs - Extract is_unique_constraint_violation() to src/database/mod.rs as a shared pub(crate) utility, removing the duplicate in import_mnemonic_screen - Update all callers in add_new_wallet_screen and import_mnemonic_screen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: address PR #673 review comments for E2E test framework - Move framework modules (harness, task_runner, wait, funding, cleanup, identity_helpers) into tests/backend-e2e/framework/ subdirectory - Make E2E_WALLET_MNEMONIC required (panic with instructions if unset) - Remove auto-faucet from initialization flow (keep as helper) - Remove retry loops in identity_create and identity_withdraw tests - Remove unnecessary wait_for_spendable_balance calls (already done by create_funded_test_wallet) - Replace all println!/eprintln! with tracing macros - Initialize tracing subscriber in harness init - Add "No spendable funds" and "spendable" to send retry conditions - Remove stale "other agent" NOTE comments from identity_helpers - Consolidate funding logic (harness delegates to funding module) - Update README for required mnemonic and new directory structure Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: reconcile production and test code after type-safe refactor Adapt test framework to production API changes: - Use IdentityKeys::new() constructor (fields now pub(crate)) - Match TaskError::WalletAlreadyImported variant instead of string - Allow dead_code on faucet helpers (kept but not auto-called) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: thread data_dir as explicit parameter to eliminate env var dependency Add `data_dir: PathBuf` field to `AppContext` and thread it through `Config::load_from()`, `Config::save()`, `SpvManager::new()`, `start_dash_qt()`, and `create_dash_core_config_if_not_exists()`. This enables E2E tests to specify their data directory without mutating process-wide environment variables, making parallel test execution safe. The `DASH_EVO_DATA_DIR` env var is still checked in production via `app_user_data_dir_path()`, but the resolved path is threaded through as a value rather than re-read from env on every call. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): simplify funded wallet creation and make spendable balance check reliable - Remove retry loop from create_funded_test_wallet; wait for full amount_duffs instead of 1 duff in spendable balance check - Add Wallet::spv_confirmed_balance() that returns None when SPV hasn't synced yet (no max_balance fallback) - Use spv_confirmed_balance() in wait_for_spendable_balance so the wait never gets false positives from the fallback - Remove --test-threads=1 requirement from README (unsafe set_var was the only reason; data_dir is now threaded explicitly) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove send retry loop, handle PRIMARY KEY constraints, isolate SPV test dirs - Remove send_with_retry() from send_funds.rs; use wait_for_spendable_balance before each send instead - Add SQLITE_CONSTRAINT_PRIMARYKEY (1555) to uniqueness check alongside SQLITE_CONSTRAINT_UNIQUE (2067) - Use tempfile::TempDir in SPV tests instead of fixed /tmp/spv-test path to prevent state leaks and support concurrent test runs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Pasta Lil Claw <pasta+claw@dashboost.org> Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Paul DeLucia <69597248+pauldelucia@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: lklimek <lklimek@users.noreply.github.com>
…770) * ci: add binary dependency verification for Linux, macOS, and Flatpak Add post-build sanity checks to catch missing or unexpected shared library dependencies before release artifacts are published: - Linux: ldd check with allowlist, fail on "not found" - macOS (ARM64 + x86): otool -L check, fail on non-system libs - Flatpak: ldd check inside build-dir, fail on "not found" Complements the Windows PE verification from #769. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ci): prevent grep exit code 1 from failing dependency checks When all binary dependencies are valid system libraries, `grep -Ev` filters out all lines and returns exit code 1 (no matches). Under `bash -e` (GitHub Actions default), this kills the script before it can report success. Add `|| true` to all grep commands inside command substitutions across Linux and macOS verification steps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ci): run Flatpak ldd check inside sandbox, not on host The ldd check was running against the host runner's libraries, which don't match the Flatpak runtime. Use `flatpak build build-dir` to execute ldd inside the sandbox where libraries resolve against the Flatpak runtime (/usr) and app (/app) directories. Addresses review comment from copilot-pull-request-reviewer on PR #770. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
#692) (#752) * fix: display balances for key-only addresses and load RPC transactions (#692) - Remove N/A display for Identity System addresses in Balance, UTXOs, and Total Received columns so actual values are shown - Load transaction history from Core RPC via list_transactions when SPV transactions are not available - Remove duplicate address_total_received insert that used non-canonical address, causing inconsistent DB data - Update empty transactions message to be backend-agnostic Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address QA findings for wallet display and RPC transactions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(wallet): address 4 review findings in refresh_wallet_info - RUST-001: Pass block_hash to get_raw_transaction so confirmed txs resolve without -txindex - RUST-002: Use Option<Vec> for rpc_transactions to distinguish skip (SPV/failure) from successful-empty, clearing stale data - RUST-003: Surface truncation warning to the user when list_transactions hits the 200-entry limit - RUST-004: Document why Send-category entries are safe to include without address check (wallet-scoped RPC endpoint) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): respect zero-balance filter for key-only addresses Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): show transactions in non-developer mode and track provider addresses in SPV Remove the stale developer-mode gate around the transactions section (transactions are now loaded via RPC, not just SPV). Add the four Provider account types (VotingKeys, OwnerKeys, OperatorKeys, PlatformKeys) to spv_account_metadata() so their addresses are registered with SPV for balance tracking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: return None on partial RPC transaction fetch failure When get_raw_transaction fails for some txids, the partial result set would replace the full DB/in-memory history, silently dropping previously-known transactions. Now tracks fetch failures and returns None (preserving existing data) instead of Some(partial_set). Addresses PR #752 review comment from coderabbitai. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review comments — truncation warning and wording - Reset tx_truncated when raw_fetch_failed to avoid misleading truncation warning when history was not actually updated (CMT-003) - Reword warning to drop specific "200 entries" count that refers to RPC entries, not displayed transactions (CMT-004) - Add INTENTIONAL comment documenting key-only address filter behavior as accepted design decision (CMT-002) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): include platform balance in wallet total and rename transactions section - Wallet list dropdown and balance display now sum Core + Platform balances instead of showing Core-only - Add "Total:" line in wallet overview when platform balance > 0 - Rename "Transactions" heading to "Dash Core Transactions" - Add TODO for future account-type transaction filtering Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: spinner stuck --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…#735) * fix(ui): prevent stacking banners during batch wallet identity search Add BackendTaskSuccessResult::Progress variant for progress updates during long-running operations. Unlike Message, Progress does NOT create global banners via MessageBanner::set_global — it only updates the originating screen's existing banner handle in-place. Root cause: load_user_identities_up_to_index() sent progress as Message('Searching index X of Y...'), and app.rs's Message handler calls MessageBanner::set_global() for every Message. Since each progress text is unique, set_global() created a new banner each time, causing the stacking visible in DET#713. Fix: Change batch search progress to use the new Progress variant, which bypasses global banner creation and only flows through display_task_result() to update the screen's existing banner handle. Closes #713 * fix(ui): address review feedback on Progress variant - Reword Progress doc comment to clarify behavior is a routing convention enforced by app.rs, not inherent to the enum variant (Copilot review) - Add fallback banner creation in Progress handler when refresh_banner is None, ensuring progress is always visible (Copilot review) - Use named placeholders in progress format string for localizability (CodeRabbit nitpick) - Add TODO about visible_screen_mut() task-to-screen affinity limitation (CodeRabbit major: architectural change deferred) * fix(ui): address review — use structured Progress type with current/total Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com> Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: integrate platform mempool support Bump dash-sdk to platform commit 6d9efa97b which includes rust-dashcore mempool support (MempoolManager with bloom filter monitoring) and SDK RPITIT refactor. Key changes: - Configure SPV client with BloomFilter mempool strategy - Update process_mempool_transaction() call signature - Adapt to Network::Dash -> Network::Mainnet rename - Add DB migration 29 to update stored "dash" network strings - Add Send-asserting wrappers for backend task futures to work around RPITIT HRTB Send inference limitations in the SDK Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style: fix formatting in settings.rs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: remove unsafe assert_send workaround Rebase platform dependency onto aa86b74f (before PR #3376 which paradoxically broke Send inference) so the compiler can natively prove the backend task future is Send. Changes: - Update dash-sdk rev to 2414799b7 (mempool bump on clean base) - Remove assert_send() transmute and all _send() wrapper methods - Revert app.rs to call run_backend_task() directly This commit is self-contained — revert it to restore the workaround if a future platform update reintroduces the HRTB regression. See: dashpay/platform#3376 See: rust-lang/rust#100013 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add trace-level timing instrumentation to E2E harness Add structured tracing to wait helpers and create_funded_test_wallet() for diagnosing IS lock timing issues. All new logs are trace-level (enable with RUST_LOG=backend_e2e=trace) except the final setup summary which logs at info. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: bump platform rev for bloom filter rebuild API Update dash-sdk to platform commit d9de0fd2 which includes rust-dashcore c451a1c6 adding notify_wallet_addresses_changed() for triggering mempool bloom filter rebuilds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: trigger bloom filter rebuild on wallet/address changes Call notify_wallet_addresses_changed() after loading a new wallet into SPV and after registering DashPay contact addresses. This ensures the mempool bloom filter includes newly added addresses so incoming transactions are detected immediately rather than waiting for the next block or SPV reconnect. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): let RUST_LOG override default trace level in E2E harness The hardcoded .add_directive("backend_e2e=info") was overriding RUST_LOG=backend_e2e=trace. Change to use RUST_LOG as primary source with info as fallback when the env var is not set. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * revert: use original rust-dashcore 1af96dd without filter rebuild additions Reverts bloom filter rebuild API additions (notify_wallet_addresses_changed) that were added in commit eb948ae. The upstream MempoolManager has bugs (masternode sync cascade blocking activation) that make these calls ineffective. Failing tests proving these bugs are tracked in dashpay/rust-dashcore#567. Platform rev pinned to 2af8cac6 which uses rust-dashcore 1af96dd (PR #558). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: update Cargo.lock for platform rev 2af8cac6 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: bump platform rev to be2082b3c (rust-dashcore b03252af) Updates platform to be2082b3c which pins rust-dashcore b03252af, bringing bloom filter staleness detection via monitor_revision polling. Adapts imports for key-wallet-manager → key-wallet crate merge (upstream rust-dashcore #503). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): wait for SPV sync completion before broadcasting in E2E harness The E2E harness was broadcasting funding transactions before the MempoolManager's bloom filter was loaded to peers. Peers never relayed IS locks for those txs because the filter didn't exist yet. - Add wait_for_spv_running() that polls ConnectionStatus::spv_status() until SpvStatus::Running (fires after SyncComplete + bloom filter built) - Call it in BackendTestContext::init() after spendable balance is confirmed - Add 200ms sleep in create_funded_test_wallet() after wallet appears in SPV to let the mempool manager tick rebuild the bloom filter Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): use SpvManager status directly instead of ConnectionStatus ConnectionStatus.spv_status is only updated from the UI frame loop, which doesn't run in the E2E test harness. Read from SpvManager's internal RwLock instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: bump platform rev to 93556aa4b (rust-dashcore 450f72f) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: push SPV status to ConnectionStatus from event handlers Eliminate polling of SpvManager.status() from the UI frame loop. SpvManager event handlers now push status, peer count, and error updates directly to ConnectionStatus via new setter methods, making SPV status visible to headless/test code without a UI polling cycle. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: document ConnectionStatus as single source of truth for connection health Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): replace wallet_addresses with identity_token_balances in migration 29 wallet_addresses table has no `network` column, causing migration 29 (rename "dash" → "mainnet") to fail with "no such column: network". Replace with identity_token_balances which does have the column. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): include table name in migration 29 error messages If a table update fails during the dash→mainnet rename, the error now identifies which table caused the failure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(spv): push Starting/Stopping status to ConnectionStatus immediately write_status() updated the internal RwLock but did not push to ConnectionStatus. The UI reads ConnectionStatus.spv_status() to show the SPV Sync Status section, so it stayed hidden until spawn_progress_watcher fired its first async tick. Now start() and stop() push status to ConnectionStatus immediately, matching the pattern used in all other status transitions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(spv): centralize ConnectionStatus push in write_status() Move the ConnectionStatus push into write_status() so every status transition automatically propagates to the UI. Remove redundant manual pushes from start(), stop(), run_spv_loop(), and run_client(). Async event handlers (spawn_progress_watcher, spawn_sync_event_handler) still push directly because they write to the RwLock without going through write_status() (they hold Arc<RwLock>, not &self). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address remaining PR review comments - fix(ui): testnet MNListDiff fallback now uses Network::Testnet (was incorrectly Network::Mainnet in file-not-found path) - fix(spv): progress watcher no longer clears last_error when a second error arrives with error_msg=None (preserves first error) - fix(spv): clear spv_no_peers_since when SPV transitions to non-active state, preventing stale "no peers" tooltip warnings - docs: clarify ConnectionStatus scope — health is push-based, detailed sync progress may still be read from SpvManager Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: add key_wallet and mempool_filter to default tracing filter These crates were renamed/added upstream in rust-dashcore 450f72f and were missing from the default EnvFilter, hiding debug logs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): show actionable SPV error details in banner and Settings - Error banner now says "Go to Settings" and attaches the actual error message via with_details() for the collapsible details panel - Settings SPV status label shows the error message instead of just "Error" - Add public spv_last_error() getter on ConnectionStatus (DRY) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): deduplicate wallet transactions by txid before insert With mempool support enabled, upstream wallet_transaction_history() can return the same txid twice — as both a mempool entry (no height) and a confirmed entry (with height). This caused a UNIQUE constraint violation on (seed_hash, txid, network). Deduplicate in replace_wallet_transactions(), preferring the confirmed version over unconfirmed when both exist for the same txid. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(wallet): add TransactionStatus enum for transaction lifecycle tracking Add TransactionStatus (Unconfirmed/InstantSendLocked/Confirmed/ChainLocked) to WalletTransaction model and database schema. - New TransactionStatus enum with from_height() heuristic for inferring status from block height (confirmed vs unconfirmed) - Migration 30: adds `status` column to wallet_transactions table (default 2=Confirmed for existing rows) - INSERT OR REPLACE handles duplicate txids from mempool + block - UI shows status labels: "Pending", "⚡ InstantSend", "Confirmed @n", "🔒 ChainLocked @n" InstantSendLocked and ChainLocked require upstream support (rust-dashcore#569) — currently only Unconfirmed/Confirmed are inferred. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(db): consolidate migrations 29+30 into single migration 29 Both migrations were introduced in this PR — no need for users to run them separately. Migration 29 now does both: rename network "dash" to "mainnet" AND add the transaction status column. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): throttle system theme detection to prevent white flash during sync When using the "System" color theme, dark_light::detect() was called every frame. During SPV sync (high repaint frequency), transient wrong values caused brief theme flips visible as white flashes. Changes: - Cache the resolved theme in AppState and only re-poll the OS every 2s - Guard font initialization with AtomicBool so set_fonts runs once - Immediately resolve and apply theme on preference change Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Revert "fix(ui): throttle system theme detection to prevent white flash during sync" This reverts commit 7f822bb. * fix(db): default transaction status to Unconfirmed for new inserts The table-creation DEFAULT was Confirmed (2), which is wrong — transactions should be unconfirmed until proven otherwise. The migration ALTER TABLE correctly keeps DEFAULT 2 for pre-existing rows (all from confirmed RPC). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review comments (migration idempotency, status field, poison handling) - Guard ALTER TABLE in migration 30 with pragma_table_info check to prevent duplicate column crash on DBs upgrading from version <14 - Make is_confirmed() authoritative via TransactionStatus instead of height - Consistent recover-from-poison in ConnectionStatus setters - Add TODO for write_last_error() ConnectionStatus push consolidation - Add maintenance comments for progress watcher write_status bypass - Add tracing::debug for unknown Network variant fallback Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): throttle system theme detection to prevent white flash during sync When using the "System" color theme, dark_light::detect() was called every frame. During SPV sync (high repaint frequency), transient wrong values caused brief theme flips visible as white flashes. Changes: - Cache the resolved theme in AppState and only re-poll the OS every 2s - Guard font initialization with AtomicBool so set_fonts runs once - Immediately resolve and apply theme on preference change Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): address review feedback on theme throttling - Use cached resolved_theme in shutdown path instead of theme_preference - Only call apply_theme() when resolved theme actually changes - Replace process-global FONTS_INITIALIZED with per-Context guard Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): address review feedback — persisted font guard, >= throttle, remove dead assignment Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): use temp storage for font-init flag to prevent cross-restart persistence The font initialization guard in apply_theme() used get_persisted_mut_or_default, which serializes the flag to app.ron. On restart, configure_fonts() was skipped entirely, leaving users with default egui fonts instead of custom Dash fonts. Switch to get_temp_mut_or_default — temp data survives across frames within a session but is cleared on Context drop, so fonts are correctly re-initialized on each app launch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(ui): remove duplicate font init from apply_theme Font initialization was happening in two places: `new_inner()` (comprehensive i18n font set via `bundled::fonts()`) and `apply_theme()` (basic NotoSans variable font via `configure_fonts()`). The latter overwrote the i18n fonts on first frame, losing support for CJK, Thai, Arabic, Hebrew, and Devanagari scripts. Since fonts don't change with themes, remove the font init block and the now-unused `configure_fonts()` function from `apply_theme()`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): preserve previous theme on ambiguous OS detection The throttled theme polling called resolve_theme_mode which falls back to Light on both detection errors and Unspecified results from dark_light::detect(). This caused dark-mode users to flash to light when the OS returned a transient error or ambiguous result. Add try_detect_system_theme() that returns None on error/Unspecified, and use it in the polling path so the previous resolved theme is preserved when detection is inconclusive. The preference-change and initialization paths still use resolve_theme_mode with its Light fallback, which is correct for those contexts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): remove stale doc comment and harden preference-change detection - Remove leftover "Configure fonts" doc comment from apply_theme - Use try_detect_system_theme in UpdatedThemePreference handler to avoid falling back to Light on transient detection errors when switching to System mode Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…nto zk-fixes # Conflicts: # Cargo.lock # Cargo.toml # src/app.rs # src/backend_task/core/mod.rs # src/backend_task/error.rs # src/database/initialization.rs # src/spv/manager.rs # src/ui/wallets/wallets_screen/mod.rs
…ge (#783) * fix(rpc): detect 401 auth errors and show actionable credential message When Dash Core RPC rejects with HTTP 401 (wrong password), the app previously showed generic "Disconnected" or "Could not communicate with Dash Core." Now it detects the 401 via downcast on the transport error and shows: "Dash Core rejected your credentials. Check your RPC username and password in settings." - Add `is_rpc_auth_error()` helper that downcasts through `jsonrpc::error::Error::Transport` to `simple_http::Error::HttpErrorCode(401)` - Add `TaskError::CoreRpcAuthFailed` variant with user-friendly message - Check auth before wallet-not-specified in `From<dashcore_rpc::Error>` - Refactor `get_best_chain_lock()` to return typed error instead of String, enabling auth detection in the `GetBestChainLocks` handler - Add 5 unit tests covering detection, display, and edge cases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(rpc): refine auth error message and cap password field width - Drop "username" from error message — only password is user-configurable - Add `with_char_limit()` builder to `PasswordInput` component - Set 40-char limit and 280px width on network settings RPC password field Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Resolves version numbering collision between zk and v1.0-dev branches: the zk branch used v28 for shielded tables while v1.0-dev used v28 for contacts. After merging, users migrating from either branch could end up with missing tables depending on which version their DB was at. v33 runs all sub-migrations idempotently in one step, ensuring all tables exist regardless of prior migration history. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (2)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
…igrations-v33-zk # Conflicts: # Cargo.lock
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…coverage - propagate SQL error in add_nullifier_sync_timestamp_column instead of swallowing it with unwrap_or(false) - add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already present on this branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by all other per-wallet tables in the codebase. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(db): consolidate migrations v28-v32 into v33 Resolves version numbering collision between zk and v1.0-dev branches: the zk branch used v28 for shielded tables while v1.0-dev used v28 for contacts. After merging, users migrating from either branch could end up with missing tables depending on which version their DB was at. v33 runs all sub-migrations idempotently in one step, ensuring all tables exist regardless of prior migration history. Shielded pool tables are created but unused on v1.0-dev — they prepare the schema for the upcoming zk merge. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): address PR review — fresh schema, error propagation, rename coverage - Add `last_nullifier_sync_timestamp` to CREATE TABLE in `create_shielded_wallet_meta_table` so fresh installs at v33 get the full schema without relying on the migration column-add path - Propagate SQL errors from the column-existence check in `add_nullifier_sync_timestamp_column` instead of silently swallowing them with `unwrap_or(false)` - Add `shielded_notes` and `shielded_wallet_meta` to the tables array in `rename_network_dash_to_mainnet` so the 'dash'→'mainnet' rename covers all network-scoped tables Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): add foreign key constraints to shielded tables Add FOREIGN KEY (wallet_seed_hash) REFERENCES wallet(seed_hash) ON DELETE CASCADE to both shielded_notes and shielded_wallet_meta CREATE TABLE statements, matching the pattern used throughout initialization.rs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…igrations-v33-zk # Conflicts: # src/database/initialization.rs
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
The merge of v1.0-dev into the zk branch introduces duplicate method definitions for Database that would cause a compile error — create_shielded_tables, create_shielded_wallet_meta_table, and add_nullifier_sync_timestamp_column are defined in both initialization.rs and the new shielded.rs. Beyond this critical merge issue, the migration consolidation design is sound with correct idempotency guards. Several agent findings about missing shielded table coverage turned out to be false positives.
Reviewed commit: 9d4d65a
🔴 1 blocking | 🟡 4 suggestion(s) | 💬 3 nitpick(s)
🤖 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/database/initialization.rs`:
- [BLOCKING] lines 941-997: Duplicate method definitions for Database will cause compile error
The merge of v1.0-dev brought `create_shielded_tables` (line 941), `create_shielded_wallet_meta_table` (line 968), and `add_nullifier_sync_timestamp_column` (line 984) into `initialization.rs` as private methods. However, `shielded.rs` (new in this PR) already defines identical `pub(crate)` methods on `impl Database`. Rust does not allow duplicate method names across `impl` blocks for the same type, so this will not compile. Remove the three duplicate private methods from `initialization.rs` — the v33 migration at line 65 will resolve to the `pub(crate)` versions in `shielded.rs`.
- [SUGGESTION] line 933: Schema DEFAULT for status column differs between migration and fresh install
The migration adds `status INTEGER NOT NULL DEFAULT 2` (Confirmed) to existing rows, which is correct. But the fresh `CREATE TABLE` in `wallet.rs:391` uses `DEFAULT 0` (Unconfirmed). The migration default persists in the column schema, so any future INSERT that omits `status` on a migrated database would get Confirmed (2) instead of Unconfirmed (0). This is harmless if all INSERT paths always specify `status` explicitly, but it's a latent inconsistency worth a brief comment.
- [SUGGESTION] lines 58-70: No test exercises the v33 consolidated migration path
The consolidated v33 migration is the most complex migration in the codebase (7 idempotent sub-steps covering multiple branch-collision scenarios), but no test starts from a v27/v28/v31 database shape and verifies the final schema. Given the branch-collision motivation, a regression test would add confidence.
In `src/model/wallet/mod.rs`:
- [SUGGESTION] lines 757-763: spv_confirmed_balance returns None for a genuine zero-balance wallet
When all three balance fields (total, confirmed, unconfirmed) are 0, the function returns `None` (not-yet-synced) instead of `Some(0)`. This makes it impossible for callers to distinguish a synced zero-balance wallet from an unsynced one. Test waiters that retry on `None` would loop forever on a funded-then-drained wallet. Consider tracking a `has_synced` flag or similar sentinel.
In `src/backend_task/error.rs`:
- [SUGGESTION] lines 608-609: WalletKeyDerivationFailed uses String field instead of typed #[source]
Per project conventions in CLAUDE.md, error variants should use typed `#[source]` fields rather than `String` fields. `WalletKeyDerivationFailed { detail: String }` stores the stringified BIP32 error. Consider wrapping the upstream error type with `#[source]` instead.
src/database/initialization.rs
Outdated
| /// Migration 29: rename network value "dash" to "mainnet" in all tables. | ||
| /// | ||
| /// Upstream `dashcore` renamed `Network::Dash` to `Network::Mainnet`, | ||
| /// which changes `Display`/`FromStr` representations from `"dash"` to | ||
| /// `"mainnet"`. This migration updates every table that stores the | ||
| /// network as a string. | ||
| /// Migration 30: add `status` column to `wallet_transactions`. | ||
| /// Default 2 (Confirmed) — all pre-existing rows were confirmed transactions. |
There was a problem hiding this comment.
💬 Nitpick: Doc comment for migration 29 (rename) is attached to the migration 30 function
The doc comment block at lines 917–924 describes both migration 29 (rename network) and migration 30 (add status column), but it's attached to add_wallet_transaction_status_column. The actual rename_network_dash_to_mainnet function at line 999 has no doc comment. Move the migration 29 doc comment to rename_network_dash_to_mainnet.
source: ['claude']
| )?; | ||
| if !has_status { | ||
| conn.execute( | ||
| "ALTER TABLE wallet_transactions ADD COLUMN status INTEGER NOT NULL DEFAULT 2", |
There was a problem hiding this comment.
🟡 Suggestion: Schema DEFAULT for status column differs between migration and fresh install
The migration adds status INTEGER NOT NULL DEFAULT 2 (Confirmed) to existing rows, which is correct. But the fresh CREATE TABLE in wallet.rs:391 uses DEFAULT 0 (Unconfirmed). The migration default persists in the column schema, so any future INSERT that omits status on a migrated database would get Confirmed (2) instead of Unconfirmed (0). This is harmless if all INSERT paths always specify status explicitly, but it's a latent inconsistency worth a brief comment.
source: ['claude']
🤖 Fix this 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/database/initialization.rs`:
- [SUGGESTION] line 933: Schema DEFAULT for status column differs between migration and fresh install
The migration adds `status INTEGER NOT NULL DEFAULT 2` (Confirmed) to existing rows, which is correct. But the fresh `CREATE TABLE` in `wallet.rs:391` uses `DEFAULT 0` (Unconfirmed). The migration default persists in the column schema, so any future INSERT that omits `status` on a migrated database would get Confirmed (2) instead of Unconfirmed (0). This is harmless if all INSERT paths always specify `status` explicitly, but it's a latent inconsistency worth a brief comment.
| pub fn spv_confirmed_balance(&self) -> Option<u64> { | ||
| if self.total_balance > 0 || self.confirmed_balance > 0 || self.unconfirmed_balance > 0 { | ||
| Some(self.confirmed_balance) | ||
| } else { | ||
| None | ||
| } | ||
| } |
There was a problem hiding this comment.
🟡 Suggestion: spv_confirmed_balance returns None for a genuine zero-balance wallet
When all three balance fields (total, confirmed, unconfirmed) are 0, the function returns None (not-yet-synced) instead of Some(0). This makes it impossible for callers to distinguish a synced zero-balance wallet from an unsynced one. Test waiters that retry on None would loop forever on a funded-then-drained wallet. Consider tracking a has_synced flag or similar sentinel.
source: ['claude']
🤖 Fix this 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/model/wallet/mod.rs`:
- [SUGGESTION] lines 757-763: spv_confirmed_balance returns None for a genuine zero-balance wallet
When all three balance fields (total, confirmed, unconfirmed) are 0, the function returns `None` (not-yet-synced) instead of `Some(0)`. This makes it impossible for callers to distinguish a synced zero-balance wallet from an unsynced one. Test waiters that retry on `None` would loop forever on a funded-then-drained wallet. Consider tracking a `has_synced` flag or similar sentinel.
| #[error("Could not create the wallet. Key derivation failed — please try again.")] | ||
| WalletKeyDerivationFailed { detail: String }, |
There was a problem hiding this comment.
🟡 Suggestion: WalletKeyDerivationFailed uses String field instead of typed #[source]
Per project conventions in CLAUDE.md, error variants should use typed #[source] fields rather than String fields. WalletKeyDerivationFailed { detail: String } stores the stringified BIP32 error. Consider wrapping the upstream error type with #[source] instead.
source: ['claude']
🤖 Fix this 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/error.rs`:
- [SUGGESTION] lines 608-609: WalletKeyDerivationFailed uses String field instead of typed #[source]
Per project conventions in CLAUDE.md, error variants should use typed `#[source]` fields rather than `String` fields. `WalletKeyDerivationFailed { detail: String }` stores the stringified BIP32 error. Consider wrapping the upstream error type with `#[source]` instead.
| .map_err(|e| { | ||
| rusqlite::Error::InvalidParameterName(format!( | ||
| "migration 29: failed to update network in table `{table}`: {e}" | ||
| )) | ||
| })?; |
There was a problem hiding this comment.
💬 Nitpick: rename_network_dash_to_mainnet wraps errors in semantically wrong rusqlite variant
map_err converts the real error into rusqlite::Error::InvalidParameterName, which is semantically unrelated to a migration UPDATE failure. This is a code smell — consider a more appropriate error variant or propagating the original error with additional context.
source: ['claude']
| pub fn from_u8(v: u8) -> Self { | ||
| match v { | ||
| 0 => Self::Unconfirmed, | ||
| 1 => Self::InstantSendLocked, | ||
| 2 => Self::Confirmed, | ||
| 3 => Self::ChainLocked, | ||
| _ => Self::Unconfirmed, | ||
| } |
There was a problem hiding this comment.
💬 Nitpick: TransactionStatus::from_u8 silently maps unknown values to Unconfirmed
Unknown u8 values (≥4) silently become Unconfirmed with no diagnostic. In a financial application, a debug! or warn! log for unexpected values would help catch schema evolution mismatches early.
source: ['claude']
| 33 => { | ||
| // Consolidated migration: all changes from v28-v32 in one step. | ||
| // Every sub-migration is idempotent (IF NOT EXISTS / column checks), | ||
| // so this is safe to run on any DB that already applied some or all | ||
| // of the individual steps. | ||
| self.add_core_wallet_name_column(tx)?; | ||
| self.init_contacts_tables(tx)?; | ||
| self.create_shielded_tables(tx)?; | ||
| self.create_shielded_wallet_meta_table(tx)?; | ||
| self.add_nullifier_sync_timestamp_column(tx)?; | ||
| self.rename_network_dash_to_mainnet(tx)?; | ||
| self.add_wallet_transaction_status_column(tx)?; | ||
| } |
There was a problem hiding this comment.
🟡 Suggestion: No test exercises the v33 consolidated migration path
The consolidated v33 migration is the most complex migration in the codebase (7 idempotent sub-steps covering multiple branch-collision scenarios), but no test starts from a v27/v28/v31 database shape and verifies the final schema. Given the branch-collision motivation, a regression test would add confidence.
source: ['claude', 'codex']
🤖 Fix this 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/database/initialization.rs`:
- [SUGGESTION] lines 58-70: No test exercises the v33 consolidated migration path
The consolidated v33 migration is the most complex migration in the codebase (7 idempotent sub-steps covering multiple branch-collision scenarios), but no test starts from a v27/v28/v31 database shape and verifies the final schema. Given the branch-collision motivation, a regression test would add confidence.
- Split misplaced doc comment: `add_wallet_transaction_status_column` now has its own Migration 30 doc; `rename_network_dash_to_mainnet` gets its own Migration 29 doc. - Add inline comment explaining why the migration uses DEFAULT 2 (Confirmed) while fresh CREATE TABLE in wallet.rs uses DEFAULT 0. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev backport inlined them in initialization.rs (no shielded.rs on that branch). Merging v1.0-dev back caused E0592 duplicate definitions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PR #789 review thread resolution statuslklimek asked me to check which of my review comments here are already fixed in PR #786 and resolve them. I've done the cross-check (see PR #786 comment), but I don't have permission to resolve review threads on this PR. Already fixed in PR #786 (please resolve these threads):
Not applicable to PR #786 (only exist in #789's code): Still present in PR #786 (suggestions, non-blocking): @lklimek could you resolve threads #1 and #2? I don't have the permission to do so. |
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
Trivial incremental addressing the blocking duplicate-method finding. The 3 duplicate shielded helper methods (create_shielded_tables, create_shielded_wallet_meta_table, add_nullifier_sync_timestamp_column) removed from initialization.rs — they already exist in shielded.rs. Doc comments reorganized to correct methods. Inline comment added explaining migration DEFAULT 2 vs fresh install DEFAULT 0.
Reviewed commit: 0d4e359
QuantumExplorer
left a comment
There was a problem hiding this comment.
Approved, but tests need to pass
register_test_address called db.store_wallet redundantly — callers already store the wallet before calling it, causing UNIQUE constraint violations when tests run in parallel on CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* more work * more work * fix(spv): zero out stale per-address balances during reconciliation (#627) During SPV reconciliation, per_address_sum only contains addresses with current UTXOs. Addresses whose funds were fully spent never had their balance reset to zero, causing the address table to display stale non-zero balances even though UTXO count correctly showed 0. Now explicitly zeroes address_balances for any known address absent from the refreshed UTXO map before applying current sums. Closes #571 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml (#613) * fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml Replace .expect() with match expression to avoid panicking when .testnet_nodes.yml contains malformed YAML. Instead, logs the error with tracing::error and returns None, allowing the application to continue without crashing. Closes #557 Co-authored-by: lklimek <lklimek@users.noreply.github.com> * fix: propagate YAML parse errors to UI and remove unwrap calls - Change load_testnet_nodes_from_yml to return Result<Option<TestnetNodes>, String> so parse errors display in the UI error banner instead of only logging - Use explicit type annotation on serde_yaml_ng::from_str::<TestnetNodes> - Replace .unwrap() with .and_then() in fill_random_hpmn/fill_random_masternode Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: lklimek <lklimek@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * chore: let Claude write manual test scenarios for PRs (#634) * chore: move doc/ contents into docs/ and update references Consolidate documentation under a single docs/ directory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: CLAUDE.md should write manual test scenarios for PRs --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * build(flatpak): use only-arches for dynamic protoc architecture selection (#603) * build(flatpak): use only-arches for dynamic protoc architecture selection Replace the fragile sed-based CI patching of the Flatpak manifest with Flatpak's native `only-arches` source selector. The protoc module now declares both x86_64 and aarch64 sources inline, and build-commands use a glob pattern (`protoc-*.zip`) so no per-arch fixup is needed. Changes: - flatpak manifest: add aarch64 protoc source with `only-arches`, use glob in unzip commands, remove stale CI-patching comment - CI workflow: remove `protoc-zip`/`protoc-sha256` matrix variables and the "Patch manifest for architecture" step https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG * fix(flatpak): use dest-filename for deterministic protoc extraction Use dest-filename to normalize both arch-specific protoc zips to a common name, avoiding glob expansion in build-commands. This ensures the unzip target is deterministic regardless of shell behavior in the Flatpak sandbox. https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG * build: update platform to b445b6f0 and remove rust-dashcore patches Update dashpay/platform dependency from d6f4eb9a to b445b6f0e0bd4863 (3.0.1 → 3.1.0-dev.1). Remove the [patch] section that pinned rust-dashcore crates to a separate rev, as the new platform commit resolves them correctly on its own. https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG --------- Co-authored-by: Claude <noreply@anthropic.com> * fix(ci): remove local path patches, use git deps for platform crates The [patch] section referenced ../platform local paths that don't exist in CI. Since dash-sdk already depends on the feat/zk branch, all transitive platform crates resolve correctly without patches. Also fixes a formatting issue in address_table.rs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): store wallet in DB before registering addresses The remove_utxos tests were failing because `register_test_address` inserts into `wallet_addresses` which has a foreign key constraint on `wallet(seed_hash)`. Added the missing `store_wallet` call so the parent row exists before inserting child address rows. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(build): restore shielded module declaration removed during merge The `pub mod shielded;` declaration was accidentally removed from `src/model/wallet/mod.rs` during the v1.0-dev merge, causing build failures since the shielded.rs file still exists. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): restore store_wallet calls lost in merge (#663) * Initial plan * fix(test): restore store_wallet calls lost in merge Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com> * Merge remote-tracking branch 'origin/v1.0-dev' into zk-extract/all-merged Resolve conflicts in Cargo.toml (keep feat/zk branch), Cargo.lock (regenerate with pinned platform rev 4d7b9be5), and backend_task/mod.rs (combine TaskError wrapping with ShieldedTask). Fix post-merge integration issues: - SPV manager: remove stale .await on subscribe methods, add command_receiver channel for updated DashSpvClient::run() API - send_screen: update SendStatus::WaitingForResult to unit variant - network_chooser_screen: handle new SyncState::Initializing variant Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: update platform dependency to 3.1-dev branch Migrate from feat/zk to 3.1-dev branch of dashpay/platform, adapting to breaking API changes in dash-spv, key-wallet, and dpp: - FeeLevel removed; use FeeRate::normal() directly - DashSpvClientInterface/Command removed; use DashSpvClient directly - SyncState::Initializing removed; replaced with WaitForEvents - NetworkExt trait inlined into Network impl - OrchardProver now requires wrapper struct around ProvingKey - OrchardAddress::from_raw_bytes now returns Result - Builder functions gain fee/platform_version params - NullifierSyncConfig API uses NullifierSyncCheckpoint - WalletManager.create_unsigned_payment_transaction removed; use TransactionBuilder directly - Work around Send lifetime issues with spawn_blocking Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: migrate shielded module from Result<T, String> to typed TaskError Replace all Result<T, String> error patterns in the shielded pool module with typed TaskError variants, aligning with the codebase-wide typed error migration (PR #739). New TaskError variants: ShieldedNoUnspentNotes, ShieldedInsufficientBalance, PlatformAddressNotFound, ShieldedMerkleWitnessUnavailable, ShieldedTransitionBuildFailed, ShieldedBroadcastFailed, ShieldedInvalidRecipientAddress, ShieldedAssetLockTimeout, ShieldedSyncFailed, ShieldedTreeUpdateFailed, ShieldedNullifierSyncFailed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): prevent settings password row from clipping right edge Reserve width for Save and Auto Update buttons so the password input doesn't consume all available space, pushing buttons off-screen. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(db): consolidate migrations v28-v32 into v33 Resolves version numbering collision between zk and v1.0-dev branches: the zk branch used v28 for shielded tables while v1.0-dev used v28 for contacts. After merging, users migrating from either branch could end up with missing tables depending on which version their DB was at. v33 runs all sub-migrations idempotently in one step, ensuring all tables exist regardless of prior migration history. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: pin platform dependency to zk-fixes revision Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): address PR review — fresh schema, error propagation, rename coverage - propagate SQL error in add_nullifier_sync_timestamp_column instead of swallowing it with unwrap_or(false) - add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already present on this branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): add foreign key constraints to shielded tables shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by all other per-wallet tables in the codebase. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): address PR #789 review — doc comments, migration default - Split misplaced doc comment: `add_wallet_transaction_status_column` now has its own Migration 30 doc; `rename_network_dash_to_mainnet` gets its own Migration 29 doc. - Add inline comment explaining why the migration uses DEFAULT 2 (Confirmed) while fresh CREATE TABLE in wallet.rs uses DEFAULT 0. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): remove duplicate shielded methods after v1.0-dev merge On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev backport inlined them in initialization.rs (no shielded.rs on that branch). Merging v1.0-dev back caused E0592 duplicate definitions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: simplify shielded helpers comment in initialization.rs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): remove duplicate wallet store in register_test_address register_test_address called db.store_wallet redundantly — callers already store the wallet before calling it, causing UNIQUE constraint violations when tests run in parallel on CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(mcp): resolve async lifetime errors for Rust 2024 edition Use spawn_blocking + block_on in dispatch_task to avoid Send bound issues with platform SDK types (DataContract/Sdk references across await points). Same pattern already used by AppState::handle_backend_task. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: fix mcp dispatch * doc: remove obsolete manual teting docs --------- Co-authored-by: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: lklimek <lklimek@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
… improvements (#786) * more work * more work * fix(spv): zero out stale per-address balances during reconciliation (#627) During SPV reconciliation, per_address_sum only contains addresses with current UTXOs. Addresses whose funds were fully spent never had their balance reset to zero, causing the address table to display stale non-zero balances even though UTXO count correctly showed 0. Now explicitly zeroes address_balances for any known address absent from the refreshed UTXO map before applying current sums. Closes #571 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml (#613) * fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml Replace .expect() with match expression to avoid panicking when .testnet_nodes.yml contains malformed YAML. Instead, logs the error with tracing::error and returns None, allowing the application to continue without crashing. Closes #557 Co-authored-by: lklimek <lklimek@users.noreply.github.com> * fix: propagate YAML parse errors to UI and remove unwrap calls - Change load_testnet_nodes_from_yml to return Result<Option<TestnetNodes>, String> so parse errors display in the UI error banner instead of only logging - Use explicit type annotation on serde_yaml_ng::from_str::<TestnetNodes> - Replace .unwrap() with .and_then() in fill_random_hpmn/fill_random_masternode Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: lklimek <lklimek@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * chore: let Claude write manual test scenarios for PRs (#634) * chore: move doc/ contents into docs/ and update references Consolidate documentation under a single docs/ directory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: CLAUDE.md should write manual test scenarios for PRs --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * build(flatpak): use only-arches for dynamic protoc architecture selection (#603) * build(flatpak): use only-arches for dynamic protoc architecture selection Replace the fragile sed-based CI patching of the Flatpak manifest with Flatpak's native `only-arches` source selector. The protoc module now declares both x86_64 and aarch64 sources inline, and build-commands use a glob pattern (`protoc-*.zip`) so no per-arch fixup is needed. Changes: - flatpak manifest: add aarch64 protoc source with `only-arches`, use glob in unzip commands, remove stale CI-patching comment - CI workflow: remove `protoc-zip`/`protoc-sha256` matrix variables and the "Patch manifest for architecture" step https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG * fix(flatpak): use dest-filename for deterministic protoc extraction Use dest-filename to normalize both arch-specific protoc zips to a common name, avoiding glob expansion in build-commands. This ensures the unzip target is deterministic regardless of shell behavior in the Flatpak sandbox. https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG * build: update platform to b445b6f0 and remove rust-dashcore patches Update dashpay/platform dependency from d6f4eb9a to b445b6f0e0bd4863 (3.0.1 → 3.1.0-dev.1). Remove the [patch] section that pinned rust-dashcore crates to a separate rev, as the new platform commit resolves them correctly on its own. https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG --------- Co-authored-by: Claude <noreply@anthropic.com> * fix(ci): remove local path patches, use git deps for platform crates The [patch] section referenced ../platform local paths that don't exist in CI. Since dash-sdk already depends on the feat/zk branch, all transitive platform crates resolve correctly without patches. Also fixes a formatting issue in address_table.rs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): store wallet in DB before registering addresses The remove_utxos tests were failing because `register_test_address` inserts into `wallet_addresses` which has a foreign key constraint on `wallet(seed_hash)`. Added the missing `store_wallet` call so the parent row exists before inserting child address rows. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(build): restore shielded module declaration removed during merge The `pub mod shielded;` declaration was accidentally removed from `src/model/wallet/mod.rs` during the v1.0-dev merge, causing build failures since the shielded.rs file still exists. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): restore store_wallet calls lost in merge (#663) * Initial plan * fix(test): restore store_wallet calls lost in merge Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com> * Merge remote-tracking branch 'origin/v1.0-dev' into zk-extract/all-merged Resolve conflicts in Cargo.toml (keep feat/zk branch), Cargo.lock (regenerate with pinned platform rev 4d7b9be5), and backend_task/mod.rs (combine TaskError wrapping with ShieldedTask). Fix post-merge integration issues: - SPV manager: remove stale .await on subscribe methods, add command_receiver channel for updated DashSpvClient::run() API - send_screen: update SendStatus::WaitingForResult to unit variant - network_chooser_screen: handle new SyncState::Initializing variant Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: update platform dependency to 3.1-dev branch Migrate from feat/zk to 3.1-dev branch of dashpay/platform, adapting to breaking API changes in dash-spv, key-wallet, and dpp: - FeeLevel removed; use FeeRate::normal() directly - DashSpvClientInterface/Command removed; use DashSpvClient directly - SyncState::Initializing removed; replaced with WaitForEvents - NetworkExt trait inlined into Network impl - OrchardProver now requires wrapper struct around ProvingKey - OrchardAddress::from_raw_bytes now returns Result - Builder functions gain fee/platform_version params - NullifierSyncConfig API uses NullifierSyncCheckpoint - WalletManager.create_unsigned_payment_transaction removed; use TransactionBuilder directly - Work around Send lifetime issues with spawn_blocking Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: migrate shielded module from Result<T, String> to typed TaskError Replace all Result<T, String> error patterns in the shielded pool module with typed TaskError variants, aligning with the codebase-wide typed error migration (PR #739). New TaskError variants: ShieldedNoUnspentNotes, ShieldedInsufficientBalance, PlatformAddressNotFound, ShieldedMerkleWitnessUnavailable, ShieldedTransitionBuildFailed, ShieldedBroadcastFailed, ShieldedInvalidRecipientAddress, ShieldedAssetLockTimeout, ShieldedSyncFailed, ShieldedTreeUpdateFailed, ShieldedNullifierSyncFailed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): prevent settings password row from clipping right edge Reserve width for Save and Auto Update buttons so the password input doesn't consume all available space, pushing buttons off-screen. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(rpc): include host:port in connection-refused errors and always show details Add CoreRpcConnectionFailed variant to TaskError that includes the configured address in the user-facing message. Connection-refused errors are now detected via is_rpc_connection_error() and enriched with host:port at every RPC call site where the URL is known (AppContext::rpc_error_with_url helper). Details panel is now shown for all RPC-related errors regardless of developer mode, so users can always see the technical information they need to diagnose connectivity issues. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): save RPC password for active network instead of hardcoded Regtest The Network Chooser screen had three bugs related to RPC password handling: 1. Password input was initialized from Regtest config only, ignoring the current network selection. 2. Password UI was hidden for all networks except Regtest, even though Mainnet/Testnet/Devnet also use RPC mode. 3. Save logic was hardcoded to update Regtest config and triggered a SwitchNetwork(Regtest) action, which disconnected the active network's ZMQ listener unnecessarily. Now the password input shows for any network in RPC mode, reads/writes the correct network config, reinits the RPC client in-place without triggering a network switch, and reloads the stored password when the user switches network tabs. The "Auto Update" (dashmate) button remains Regtest-only since dashmate is only relevant for local networks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): ensure funding method dropdown fits all items without scrollbar Add explicit .height(200.0) to the funding method ComboBox in both top_up_identity_screen and add_new_identity_screen. The add_enabled_ui wrappers inflate item height via frame overhead, causing the popup to clip to a single row at the default height. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(error): show actionable message for insufficient identity balance IdentityInsufficientBalanceError from the SDK now maps to a dedicated TaskError::IdentityInsufficientBalance variant instead of falling through to "An unexpected error occurred." The user sees the available and required credit amounts along with a clear "top up your identity" action. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): clear stale error banners when saving RPC password Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use network-compatible comparison for platform address lookups Regtest and Testnet share the `tdash` bech32m prefix, but `PlatformAddress::from_bech32m_string()` always returns `Network::Testnet` for `tdash` addresses. The strict `!=` comparison in the fund-platform dialog rejected valid Regtest addresses with "Address network mismatch". - Make `networks_address_compatible()` `pub(crate)` in `model::wallet` so all modules can reuse the canonical check - Remove the duplicate private copy in `backend_task::core` and import from the single source - Replace `network != self.app_context.network` in `dialogs.rs` with `networks_address_compatible()` so Testnet/Devnet/Regtest addresses are accepted interchangeably Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): add actionable messages for shielded fee and pool-size errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): add actionable message for shielded anchor mismatch Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): auto-resync notes and retry on anchor mismatch When unshield_credits, shielded_transfer, or shielded_withdrawal fails with ShieldedAnchorMismatch (stale commitment tree witnesses), automatically sync notes once to update the tree and retry the operation. Only one resync attempt is made — if the retry also fails, the error propagates as-is. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): ensure shielded tables exist and log DB errors during init Three changes to fix empty shielded balance after app restart: 1. Defensive table creation in initialize(): after migrations complete, ensure shielded_notes and shielded_wallet_meta tables exist even if the DB version was already past v29/v30 from a prior build. Both methods use CREATE TABLE IF NOT EXISTS, so this is safe. 2. Log DB errors during shielded init: change silent if-let-Ok pattern to match/Err with tracing::warn, so missing-table errors are visible instead of silently producing empty note lists. 3. Safety net resync: when the commitment tree has been synced but no unspent notes were loaded from DB, clear the tree and reset last_synced_index to 0 so the auto-sync rediscovers all notes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): consolidate migrations v28-v32 into v33 Resolves version numbering collision between zk and v1.0-dev branches: the zk branch used v28 for shielded tables while v1.0-dev used v28 for contacts. After merging, users migrating from either branch could end up with missing tables depending on which version their DB was at. v33 runs all sub-migrations idempotently in one step, ensuring all tables exist regardless of prior migration history. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): consolidate migrations v28-v32 into v33 Resolves version numbering collision between zk and v1.0-dev branches: the zk branch used v28 for shielded tables while v1.0-dev used v28 for contacts. After merging, users migrating from either branch could end up with missing tables depending on which version their DB was at. v33 runs all sub-migrations idempotently in one step, ensuring all tables exist regardless of prior migration history. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: pin platform dependency to zk-fixes revision Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): address PR review — fresh schema, error propagation, rename coverage - propagate SQL error in add_nullifier_sync_timestamp_column instead of swallowing it with unwrap_or(false) - add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already present on this branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): address PR review — fresh schema, error propagation, rename coverage - propagate SQL error in add_nullifier_sync_timestamp_column instead of swallowing it with unwrap_or(false) - add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already present on this branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): add foreign key constraints to shielded tables shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by all other per-wallet tables in the codebase. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): add foreign key constraints to shielded tables shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by all other per-wallet tables in the codebase. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): remove duplicate shielded methods after v1.0-dev merge On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev backport inlined them in initialization.rs (no shielded.rs on that branch). Merging v1.0-dev back caused E0592 duplicate definitions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): remove duplicate wallet store in register_test_address register_test_address called db.store_wallet redundantly — callers already store the wallet before calling it, causing UNIQUE constraint violations when tests run in parallel on CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(shielded): address PR review — error propagation, retry helper, safety net - Propagate DB error from get_unspent_shielded_notes instead of swallowing - Extract with_anchor_retry() helper to deduplicate ~27 lines across shielded_transfer_task, unshield_credits_task, shielded_withdrawal_task - Fix safety net false positive: check all notes (spent + unspent) to distinguish "never had notes" from "all spent" - Propagate error from clear_commitment_tree_tables instead of discarding Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): address PR review — Amount formatting, Display completeness - Add Amount::dash_from_credits() constructor to model/amount.rs - Replace manual format_credits_as_dash() logic with Amount::dash_from_credits().to_string(), so credit amounts use Amount's Display (with DASH unit, trimmed trailing zeros) - Update ShieldedFeeExceedsBalance #[error] message: remove redundant "Dash" literal (Amount's Display already includes the "DASH" unit suffix) - Update format_credits_as_dash tests to match new output format ("1 DASH", "2.5 DASH", etc.) - Remove special-casing that showed with_details() for all RPC errors unconditionally: all required user-facing info is already in each variant's Display string; technical details are now shown only in developer mode (aligns with CLAUDE.md §error-messages rule 4) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(rpc): address PR review — context fallback, success banner, error source - Guard in-memory config update + reinit behind a check that the target network's AppContext actually exists; skip both when it doesn't so we don't accidentally overwrite mainnet config via the fallback path. - Show success banner only when reinit succeeds; show a warning when it fails so the user knows the connection may not reflect the new password. - Make CoreRpcConnectionFailed.source Option<dashcore_rpc::Error> so chain_lock_rpc_error can pass None instead of fabricating a fake ConnectionRefused error from a borrowed reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: simplify shielded helpers comment in initialization.rs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): address PR #789 review — doc comments, migration default - Split misplaced doc comment: `add_wallet_transaction_status_column` now has its own Migration 30 doc; `rename_network_dash_to_mainnet` gets its own Migration 29 doc. - Add inline comment explaining why the migration uses DEFAULT 2 (Confirmed) while fresh CREATE TABLE in wallet.rs uses DEFAULT 0. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): remove duplicate shielded methods after v1.0-dev merge On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev backport inlined them in initialization.rs (no shielded.rs on that branch). Merging v1.0-dev back caused E0592 duplicate definitions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: simplify shielded helpers comment in initialization.rs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(db): document migration DEFAULT 2 vs fresh DEFAULT 0 Existing transactions predate status tracking and are assumed confirmed (DEFAULT 2). Fresh installs use DEFAULT 0 (Unconfirmed). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(wallet): address review — zero-balance detection, status warning Add `spv_balance_known: bool` to `Wallet` to distinguish a synced zero-balance wallet from an unsynced one. `spv_confirmed_balance()` now returns `None` only before the first SPV report, and `Some(0)` after SPV confirms an empty wallet. Add `tracing::warn!` in `TransactionStatus::from_u8` when an unknown discriminant is encountered, avoiding silent data coercion in a financial context. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): address review — typed BIP32 source, migration error variant Replace WalletKeyDerivationFailed { detail: String } with a typed #[source] field (Box<dyn Error + Send + Sync>) so the error chain is preserved and Display/Debug separation is explicit. Update all callsites in wallet/mod.rs and identity/mod.rs accordingly. Replace the semantically wrong InvalidParameterName map_err in rename_network_dash_to_mainnet with a plain ? — execute() already returns rusqlite::Result so no conversion is needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test(db): add v33 consolidated migration regression tests Two test scenarios verify the v33 consolidated migration that merges sub-migrations v28-v32 (core_wallet_name, shielded tables, contacts, wallet transaction status, network rename): 1. Fresh install creates all v33 tables/columns directly via create_tables() 2. Upgrade from v27 runs the full migration path and produces identical schema Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): show warning when config save fails instead of success When config.save() fails, stop early with a warning banner instead of continuing to reinit and showing a misleading success message. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): consolidate format_credits_as_dash and remove jargon from user messages - fee_estimation::format_credits_as_dash now delegates to Amount::dash_from_credits() instead of doing its own f64 math with fixed 8 decimals; output is now trimmed (e.g. "1 DASH" instead of "1.00000000 DASH") - Remove private format_credits_as_dash from error.rs; import the pub version from fee_estimation instead - IdentityInsufficientBalance: show amounts in DASH rather than raw credit integers - ShieldedAnchorMismatch: replace "out of sync" with plain-language retry guidance - ShieldedFeeExceedsBalance: remove "shielded balance" / "shield more credits" jargon - ShieldedInsufficientPoolNotes: remove count interpolations and ZK pool jargon - Update tests throughout to match new message content and format Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(test): remove duplicate wallet store in register_test_address register_test_address called db.store_wallet redundantly — callers already store the wallet before calling it, causing UNIQUE constraint violations when tests run in parallel on CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(core): log chain lock RPC errors that aren't auth/connection failures Silent swallowing of errors like "Unable to find any ChainLock" made it impossible to diagnose why the active network showed as Disconnected. Now warns via tracing when chain_lock_rpc_error returns None. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ui): surface chain lock RPC errors in Networks tab Previously, non-auth/non-connection RPC errors (e.g. "Unable to find any ChainLock") were silently swallowed — the UI showed "Disconnected" with no explanation. Now the error message is carried through the ChainLocks result, stored in ConnectionStatus.rpc_last_error, and displayed as "Error: ..." in both developer and non-developer RPC status labels on the Networks tab. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(ui): unified AddressInput component with autocomplete (#787) * feat(ui): add unified AddressInput component with autocomplete Introduce a reusable AddressInput component that handles text input with real-time address type detection, autocomplete from wallet data, balance display, type filtering, and network-aware validation. Supports Core, Platform, Shielded, and Identity address kinds. New files: - src/model/address.rs: AddressKind enum and ValidatedAddress enum - src/ui/components/address_input.rs: full component with 33 unit tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): integrate AddressInput component into send and unshield screens Replace inline address parsing and validation with the unified AddressInput component across 3 proof-of-concept sites: - UnshieldCreditsScreen: full migration, removes local Destination enum and parse_destination(), gains autocomplete and type-restricted input - WalletSendScreen simple mode: full migration, removes AddressType enum, replaces detect_address_type/is_shielded_address with AddressKind-based detection, eliminates double-parsing in send handlers - WalletSendScreen advanced mode: minimal migration, updates type detection to use AddressKind, keeps CoreAddressInput/PlatformAddressInput structs unchanged Net reduction of ~47 lines per screen. All send flows preserved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(address-input): fix misleading test and add missing coverage The existing `disabled_type_rejected_with_correct_error` test was testing empty input (which returns no error) rather than actually verifying type restriction. Fixed the test and added coverage for: - Selection-only mode rejection of manual input - Identity validation with valid/invalid identifiers - Truncate boundary at exactly 16/17 characters - Empty input in selection-only and restricted-type modes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): address QA findings for AddressInput component QA-001: Extract duplicated address detection logic from send_screen.rs into AddressKind::detect() on the model type. Both send_screen and address_input now delegate to the single canonical implementation. QA-002: Fix autocomplete "...and N more" count using unfiltered total. filtered_entries() now returns the pre-truncation match count so the overflow label shows the correct number of remaining matches. QA-003: Add minimum length check (>= 60 chars) to shielded address validation. Previously any string with the correct prefix was accepted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): address bot review findings for AddressInput component - Validate shielded addresses via OrchardAddress::from_bech32m_string() instead of prefix+length check only - Use char-aware truncation in truncate_address() to prevent panics on multi-byte UTF-8 input (DPNS labels, emoji) - Reset AddressInput when source selection changes in send screen, and configure allowed destination kinds per source type - Store bech32m string in ValidatedAddress::Platform variant so to_address_string() returns canonical encoding instead of debug hex Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): address remaining review findings for AddressInput component - Fix manual entry not propagating validated address on blur (HIGH) - Fix selected_from_autocomplete causing repeated change signals - Remove protocol jargon from unshield screen messages - Reject mixed-case bech32m platform addresses per BIP-350 - Use per-instance ComboBox ID to prevent state collision - Clear cached_detection after autocomplete selection - Fix doc comments and reuse filtered_entries() computation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): support multi-wallet autocomplete in AddressInput Replace with_wallet/set_wallet (single wallet) with with_wallets/set_wallets (slice of wallets). When multiple wallets are loaded, each autocomplete entry is prefixed with the wallet alias so the user can tell which wallet owns the address. send_screen.rs now passes all loaded wallets to AddressInput instead of only the selected one. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ui): restore missing "Show zero-balance addresses" checkbox The checkbox was accidentally dropped during a branch merge. Restored the horizontal layout with heading on the left and checkbox right-aligned, matching the v1.0-dev implementation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): improve AddressInput autocomplete UX - Fix click selection: keep popup rendered when text field loses focus so the click handler fires (was gated on has_focus only) - Show dropdown on focus with any input length (removed 3-char minimum) - Add type suffix (Core), (Platform), (Identity), (Shielded) to dropdown entries when multiple address types are enabled - Validate immediately on paste (text >3 chars) instead of requiring blur first, so "Fund Platform Address" button activates without needing to click away Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): make entire autocomplete row clickable in AddressInput Previously only the address label was clickable — clicking the balance text on the right side of the row did nothing. Now the click handler uses the horizontal row response, so the entire row triggers selection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): fix autocomplete click handling and format balances with 4dp - Capture selectable_label click response instead of discarding it; combine with row-level click for full-row clickability - Format all DASH balances in dropdown with exactly 4 decimal places Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): use single interaction rect for full-row clickable autocomplete Replace selectable_label + horizontal layout with allocate_exact_size and manual painting — no child widgets steal clicks, the entire row (address, balance, dead space) is clickable with hover feedback. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(ui): add wallet address autocomplete to unshield screen Populate AddressInput with wallet addresses via .with_wallets() so users can select a destination from the dropdown instead of manually entering addresses. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(error): handle asset lock and shielded insufficient funds errors with user-friendly messages Add two new TaskError variants to replace raw SDK errors with actionable, jargon-free messages: - AssetLockOutPointInsufficientBalance: shown when an asset lock outpoint has been partially consumed and lacks credits for the operation. Extracts credits_left/credits_required and displays DASH amounts. - ShieldedAddressInsufficientFunds: shown when a shielded broadcast fails because the address balance is too low. Detected in shielded_broadcast_error() before falling through to ShieldedBroadcastFailed. Both follow the IdentityInsufficientBalance pattern with format_credits_as_dash. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): use AmountInput component for all amount inputs in wallet screens Replace raw text_edit_singleline + custom parse_amount_*() methods with the AmountInput component in 4 shielded screens (unshield, shield, shield-from-asset-lock, shielded-send). This fixes a bug where entering "1" would send 1 credit instead of 1 DASH because the raw input had ambiguous parsing. Also adds "Amount (DASH):" label to SendScreen's AmountInput and removes its redundant manual label. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add UI components reference and teach CLAUDE.md to use it Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): use AddressInput component in Mine dialog for core address selection Replace the manual ComboBox address selector in the Mine Blocks dialog with the unified AddressInput component, configured for Core-only addresses with selection-only mode from the current wallet. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): trigger shielded balance refresh after all shielding operations After Shield, Shield from Core, Send Private, Unshield, and Send Dash (with shielded source) complete successfully, automatically dispatch a SyncNotes task so the shielded tab shows updated balances without manual refresh. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: demote cookie auth fallback log to trace level Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): prevent transaction list showing wrong wallet data Add defensive checks in render_transactions_section to prevent cross-wallet transaction leakage: 1. Verify the selected wallet Arc matches the canonical one in app_context.wallets (guards against stale references). 2. Filter displayed transactions to only those with at least one output matching the wallet's known addresses (prevents showing transactions from other wallets that leaked in via a non-wallet-scoped RPC endpoint). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): auto-show zero-balance addresses when wallet is empty When all addresses have zero balance and there are fewer than 5 addresses total, bypass the zero-balance filter so new/empty wallets show their addresses instead of a blank list. The checkbox remains functional for wallets with balances. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): trigger shielded sync on wallet refresh and wallet switch The wallet Refresh button now dispatches a SyncNotes task alongside the core wallet refresh when the shielded wallet has been initialized. Switching HD wallets also triggers a full refresh (core + shielded) on the next frame for unlocked wallets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(log): add diagnostic logging for shielded transfer operations Add tracing::info! and tracing::debug! logs throughout the shielded transfer pipeline to enable post-mortem diagnosis when balances don't update after broadcast. Key additions: - bundle.rs: log amount, input notes, change before building each shielded operation (transfer, shield, unshield, withdrawal, shield-from-asset-lock); log broadcast success with note that balance updates after next block - sync.rs: warn when next_start_index is 0 (full rescan); log post-sync balance with unspent note count - context/shielded.rs: log note spend marking with before/after unspent counts in with_anchor_retry - shielded_send_screen.rs: log task result variant received and post-transfer sync completion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): improve shielded transfer UX messaging for balance update delays After a shielded transfer, the sender's change notes and the recipient's new notes won't appear until the next block is mined and synced. Users see their balance temporarily drop to 0 and think the transfer failed. Add informational messages on all three shielded transfer screens (ShieldedSendScreen, UnshieldCreditsScreen, WalletSendScreen) explaining that balances will update after the next block is confirmed. For shielded-to-shielded transfers, also note that the recipient needs to sync after the next block. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): scope commitment tree per wallet for multi-wallet correctness Each wallet's ClientPersistentCommitmentTree now uses its own dedicated SQLite file under <data_dir>/shielded/<hex>.db instead of sharing the main database's global commitment_tree_* tables. This prevents wallets from stepping on each other's Merkle tree state, which caused invalid witnesses and silently rejected transactions. Changes: - SHLD-001: Per-wallet commitment tree DB files via ClientPersistentCommitmentTree::open() - SHLD-002: clear_commitment_tree_for_wallet() scoped to a single wallet - SHLD-007: Safety-net resync guard now logs wallet seed hash before clearing - Migration v34: drops orphaned global commitment_tree_* tables - clear_network_data() deletes per-wallet tree files for the target network Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): prevent permanent state leak on sync_notes failure in with_anchor_retry When sync_notes() failed after an anchor mismatch, the early `?` return skipped re-inserting the shielded state back into the HashMap, permanently orphaning it until app restart. Now the sync result is captured without early return, ensuring the state is always re-inserted regardless of success or failure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): apply password change for current session even when config save fails The in-memory config update and SDK reinit only ran on the save-success path, silently discarding the password change on save failure despite the banner promising session-level application. Now the in-memory update and reinit run unconditionally; only the banner text varies across the four (save x reinit) outcome combinations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(error): use dedicated TaskError variants for non-build wallet errors reload_utxos() and recalculate_affected_address_balances() errors were routed through shielded_build_error() which pattern-matches for build- specific patterns like "AnchorMismatch". These are wallet operations, not shielded builds. Added WalletUtxoReloadFailed and WalletBalanceRecalculationFailed variants with appropriate user-facing messages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(error): preserve error chain in CoreRpcConnectionFailed chain_lock_rpc_error() took the RPC error by reference and created CoreRpcConnectionFailed with source: None, dropping all diagnostic info. Added a detail: Option<String> field to the variant to carry the formatted error. Boxed the source field to keep the enum size under the clippy threshold. Updated all constructors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(test): rename test_v33_migration_fresh_install to match DB version 34 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert(shielded): use shared main DB for commitment tree instead of per-wallet files Reverts the per-wallet SQLite file approach (63ce0e1). Multiple wallets share the same commitment_tree_* tables in the main database. This is accepted behavior until the SDK adds proper wallet-scoping support (dashpay/grovedb#653). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): harden anchor retry, commitment tree clearing, and state guard SEC-002: Verify shielded balance after anchor retry sync before retrying the operation — prevents doomed retries with zero balance. SEC-003: Handle missing commitment tree tables in clear_commitment_tree_tables (grovedb creates them lazily, so DELETEs fail on fresh installs). Fix clear_network_data to log-and-continue when commitment tree clearing fails — these tables are optional and shouldn't block network data reset. Add SEC-001 INTENTIONAL annotation for panic safety in with_anchor_retry. Document shield_from_asset_lock_task: anchor retry is not applicable since it only reads the payment address and doesn't use the commitment tree. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(core): sanitize RPC errors and simplify CoreRpcConnectionFailed SEC-006: Store user-friendly message instead of raw RPC error string in network status when chain lock query fails with a non-auth, non-connection error. CODE-002: Remove redundant detail field from CoreRpcConnectionFailed -- format diagnostic info into the url field instead, keeping source for the error chain. Add SEC-005 INTENTIONAL annotation for RPC errors in status tooltip. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): config permissions, address detection, and per-frame caching SEC-007: Set config file permissions to 0600 on Unix after save (contains RPC credentials). CODE-003: Deduplicate Amount::dash_from_duffs by delegating to dash_from_credits. CODE-006: Remove unused network parameter from AddressKind::detect -- detection is format-based, network validation happens separately. CODE-004/CODE-005: Cache filtered transaction indices per wallet to avoid rebuilding HashSet and filtering on every frame. Invalidated on wallet switch and refresh. CODE-008: Reset AddressInput widgets on network switch so they pick up the new network for validation. Add invalidate_address_input methods to SendScreen, UnshieldCreditsScreen, and WalletsBalancesScreen. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(logging): correct log levels per coding best practices - debug! -> trace!: "state transition built, broadcasting" messages in bundle.rs (5 occurrences) — primary-path step-by-step progress - trace! -> debug!: cookie auth fallback in core/mod.rs and context/mod.rs (2 occurrences) — secondary/fallback execution path, not primary flow - info! -> debug!: anchor mismatch retry in context/shielded.rs — error handling branch, not a business event - info! -> debug!: "marked N note(s) spent" in context/shielded.rs — internal bookkeeping, not a user-visible business event - info! -> debug!: post-transfer sync complete in shielded_send_screen.rs — internal plumbing at screen level Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(mcp): resolve async lifetime errors for Rust 2024 edition Use spawn_blocking + block_on in dispatch_task to avoid Send bound issues with platform SDK types (DataContract/Sdk references across await points). Same pattern already used by AppState::handle_backend_task. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: fix mcp dispatch * doc: remove obsolete manual teting docs --------- Co-authored-by: Quantum Explorer <quantum@dash.org> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: lklimek <lklimek@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
* more work
* more work
* fix(spv): zero out stale per-address balances during reconciliation (#627)
During SPV reconciliation, per_address_sum only contains addresses with
current UTXOs. Addresses whose funds were fully spent never had their
balance reset to zero, causing the address table to display stale
non-zero balances even though UTXO count correctly showed 0.
Now explicitly zeroes address_balances for any known address absent from
the refreshed UTXO map before applying current sums.
Closes #571
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml (#613)
* fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml
Replace .expect() with match expression to avoid panicking when
.testnet_nodes.yml contains malformed YAML. Instead, logs the error
with tracing::error and returns None, allowing the application to
continue without crashing.
Closes #557
Co-authored-by: lklimek <lklimek@users.noreply.github.com>
* fix: propagate YAML parse errors to UI and remove unwrap calls
- Change load_testnet_nodes_from_yml to return Result<Option<TestnetNodes>, String>
so parse errors display in the UI error banner instead of only logging
- Use explicit type annotation on serde_yaml_ng::from_str::<TestnetNodes>
- Replace .unwrap() with .and_then() in fill_random_hpmn/fill_random_masternode
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: lklimek <lklimek@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* chore: let Claude write manual test scenarios for PRs (#634)
* chore: move doc/ contents into docs/ and update references
Consolidate documentation under a single docs/ directory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: CLAUDE.md should write manual test scenarios for PRs
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* build(flatpak): use only-arches for dynamic protoc architecture selection (#603)
* build(flatpak): use only-arches for dynamic protoc architecture selection
Replace the fragile sed-based CI patching of the Flatpak manifest with
Flatpak's native `only-arches` source selector. The protoc module now
declares both x86_64 and aarch64 sources inline, and build-commands use
a glob pattern (`protoc-*.zip`) so no per-arch fixup is needed.
Changes:
- flatpak manifest: add aarch64 protoc source with `only-arches`,
use glob in unzip commands, remove stale CI-patching comment
- CI workflow: remove `protoc-zip`/`protoc-sha256` matrix variables
and the "Patch manifest for architecture" step
https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG
* fix(flatpak): use dest-filename for deterministic protoc extraction
Use dest-filename to normalize both arch-specific protoc zips to a
common name, avoiding glob expansion in build-commands. This ensures
the unzip target is deterministic regardless of shell behavior in the
Flatpak sandbox.
https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG
* build: update platform to b445b6f0 and remove rust-dashcore patches
Update dashpay/platform dependency from d6f4eb9a to b445b6f0e0bd4863
(3.0.1 → 3.1.0-dev.1). Remove the [patch] section that pinned
rust-dashcore crates to a separate rev, as the new platform commit
resolves them correctly on its own.
https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ci): remove local path patches, use git deps for platform crates
The [patch] section referenced ../platform local paths that don't exist
in CI. Since dash-sdk already depends on the feat/zk branch, all
transitive platform crates resolve correctly without patches. Also fixes
a formatting issue in address_table.rs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(test): store wallet in DB before registering addresses
The remove_utxos tests were failing because `register_test_address`
inserts into `wallet_addresses` which has a foreign key constraint on
`wallet(seed_hash)`. Added the missing `store_wallet` call so the
parent row exists before inserting child address rows.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(build): restore shielded module declaration removed during merge
The `pub mod shielded;` declaration was accidentally removed from
`src/model/wallet/mod.rs` during the v1.0-dev merge, causing build
failures since the shielded.rs file still exists.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(test): restore store_wallet calls lost in merge (#663)
* Initial plan
* fix(test): restore store_wallet calls lost in merge
Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com>
* Merge remote-tracking branch 'origin/v1.0-dev' into zk-extract/all-merged
Resolve conflicts in Cargo.toml (keep feat/zk branch), Cargo.lock
(regenerate with pinned platform rev 4d7b9be5), and
backend_task/mod.rs (combine TaskError wrapping with ShieldedTask).
Fix post-merge integration issues:
- SPV manager: remove stale .await on subscribe methods, add
command_receiver channel for updated DashSpvClient::run() API
- send_screen: update SendStatus::WaitingForResult to unit variant
- network_chooser_screen: handle new SyncState::Initializing variant
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: update platform dependency to 3.1-dev branch
Migrate from feat/zk to 3.1-dev branch of dashpay/platform,
adapting to breaking API changes in dash-spv, key-wallet, and dpp:
- FeeLevel removed; use FeeRate::normal() directly
- DashSpvClientInterface/Command removed; use DashSpvClient directly
- SyncState::Initializing removed; replaced with WaitForEvents
- NetworkExt trait inlined into Network impl
- OrchardProver now requires wrapper struct around ProvingKey
- OrchardAddress::from_raw_bytes now returns Result
- Builder functions gain fee/platform_version params
- NullifierSyncConfig API uses NullifierSyncCheckpoint
- WalletManager.create_unsigned_payment_transaction removed;
use TransactionBuilder directly
- Work around Send lifetime issues with spawn_blocking
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: migrate shielded module from Result<T, String> to typed TaskError
Replace all Result<T, String> error patterns in the shielded pool module
with typed TaskError variants, aligning with the codebase-wide typed error
migration (PR #739).
New TaskError variants: ShieldedNoUnspentNotes, ShieldedInsufficientBalance,
PlatformAddressNotFound, ShieldedMerkleWitnessUnavailable,
ShieldedTransitionBuildFailed, ShieldedBroadcastFailed,
ShieldedInvalidRecipientAddress, ShieldedAssetLockTimeout,
ShieldedSyncFailed, ShieldedTreeUpdateFailed, ShieldedNullifierSyncFailed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): prevent settings password row from clipping right edge
Reserve width for Save and Auto Update buttons so the password input
doesn't consume all available space, pushing buttons off-screen.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(rpc): include host:port in connection-refused errors and always show details
Add CoreRpcConnectionFailed variant to TaskError that includes the
configured address in the user-facing message. Connection-refused
errors are now detected via is_rpc_connection_error() and enriched
with host:port at every RPC call site where the URL is known
(AppContext::rpc_error_with_url helper).
Details panel is now shown for all RPC-related errors regardless of
developer mode, so users can always see the technical information
they need to diagnose connectivity issues.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): save RPC password for active network instead of hardcoded Regtest
The Network Chooser screen had three bugs related to RPC password handling:
1. Password input was initialized from Regtest config only, ignoring the
current network selection.
2. Password UI was hidden for all networks except Regtest, even though
Mainnet/Testnet/Devnet also use RPC mode.
3. Save logic was hardcoded to update Regtest config and triggered a
SwitchNetwork(Regtest) action, which disconnected the active network's
ZMQ listener unnecessarily.
Now the password input shows for any network in RPC mode, reads/writes
the correct network config, reinits the RPC client in-place without
triggering a network switch, and reloads the stored password when the
user switches network tabs. The "Auto Update" (dashmate) button remains
Regtest-only since dashmate is only relevant for local networks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): ensure funding method dropdown fits all items without scrollbar
Add explicit .height(200.0) to the funding method ComboBox in both
top_up_identity_screen and add_new_identity_screen. The add_enabled_ui
wrappers inflate item height via frame overhead, causing the popup to
clip to a single row at the default height.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(error): show actionable message for insufficient identity balance
IdentityInsufficientBalanceError from the SDK now maps to a dedicated
TaskError::IdentityInsufficientBalance variant instead of falling through
to "An unexpected error occurred." The user sees the available and required
credit amounts along with a clear "top up your identity" action.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): clear stale error banners when saving RPC password
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use network-compatible comparison for platform address lookups
Regtest and Testnet share the `tdash` bech32m prefix, but
`PlatformAddress::from_bech32m_string()` always returns `Network::Testnet`
for `tdash` addresses. The strict `!=` comparison in the fund-platform
dialog rejected valid Regtest addresses with "Address network mismatch".
- Make `networks_address_compatible()` `pub(crate)` in `model::wallet`
so all modules can reuse the canonical check
- Remove the duplicate private copy in `backend_task::core` and import
from the single source
- Replace `network != self.app_context.network` in `dialogs.rs` with
`networks_address_compatible()` so Testnet/Devnet/Regtest addresses
are accepted interchangeably
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(error): add actionable messages for shielded fee and pool-size errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(error): add actionable message for shielded anchor mismatch
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(shielded): auto-resync notes and retry on anchor mismatch
When unshield_credits, shielded_transfer, or shielded_withdrawal fails
with ShieldedAnchorMismatch (stale commitment tree witnesses), automatically
sync notes once to update the tree and retry the operation. Only one resync
attempt is made — if the retry also fails, the error propagates as-is.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(shielded): ensure shielded tables exist and log DB errors during init
Three changes to fix empty shielded balance after app restart:
1. Defensive table creation in initialize(): after migrations complete,
ensure shielded_notes and shielded_wallet_meta tables exist even if
the DB version was already past v29/v30 from a prior build. Both
methods use CREATE TABLE IF NOT EXISTS, so this is safe.
2. Log DB errors during shielded init: change silent if-let-Ok pattern
to match/Err with tracing::warn, so missing-table errors are visible
instead of silently producing empty note lists.
3. Safety net resync: when the commitment tree has been synced but no
unspent notes were loaded from DB, clear the tree and reset
last_synced_index to 0 so the auto-sync rediscovers all notes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(db): consolidate migrations v28-v32 into v33
Resolves version numbering collision between zk and v1.0-dev branches:
the zk branch used v28 for shielded tables while v1.0-dev used v28 for
contacts. After merging, users migrating from either branch could end up
with missing tables depending on which version their DB was at.
v33 runs all sub-migrations idempotently in one step, ensuring all
tables exist regardless of prior migration history.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(db): consolidate migrations v28-v32 into v33
Resolves version numbering collision between zk and v1.0-dev branches:
the zk branch used v28 for shielded tables while v1.0-dev used v28 for
contacts. After merging, users migrating from either branch could end up
with missing tables depending on which version their DB was at.
v33 runs all sub-migrations idempotently in one step, ensuring all
tables exist regardless of prior migration history.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: pin platform dependency to zk-fixes revision
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(db): address PR review — fresh schema, error propagation, rename coverage
- propagate SQL error in add_nullifier_sync_timestamp_column instead of
swallowing it with unwrap_or(false)
- add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet
Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already
present on this branch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(db): address PR review — fresh schema, error propagation, rename coverage
- propagate SQL error in add_nullifier_sync_timestamp_column instead of
swallowing it with unwrap_or(false)
- add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet
Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already
present on this branch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(db): add foreign key constraints to shielded tables
shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns
with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES
wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by
all other per-wallet tables in the codebase.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(db): add foreign key constraints to shielded tables
shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns
with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES
wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by
all other per-wallet tables in the codebase.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(db): remove duplicate shielded methods after v1.0-dev merge
On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev
backport inlined them in initialization.rs (no shielded.rs on that
branch). Merging v1.0-dev back caused E0592 duplicate definitions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(test): remove duplicate wallet store in register_test_address
register_test_address called db.store_wallet redundantly — callers
already store the wallet before calling it, causing UNIQUE constraint
violations when tests run in parallel on CI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(shielded): address PR review — error propagation, retry helper, safety net
- Propagate DB error from get_unspent_shielded_notes instead of swallowing
- Extract with_anchor_retry() helper to deduplicate ~27 lines across
shielded_transfer_task, unshield_credits_task, shielded_withdrawal_task
- Fix safety net false positive: check all notes (spent + unspent) to
distinguish "never had notes" from "all spent"
- Propagate error from clear_commitment_tree_tables instead of discarding
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(error): address PR review — Amount formatting, Display completeness
- Add Amount::dash_from_credits() constructor to model/amount.rs
- Replace manual format_credits_as_dash() logic with Amount::dash_from_credits().to_string(),
so credit amounts use Amount's Display (with DASH unit, trimmed trailing zeros)
- Update ShieldedFeeExceedsBalance #[error] message: remove redundant "Dash" literal
(Amount's Display already includes the "DASH" unit suffix)
- Update format_credits_as_dash tests to match new output format ("1 DASH", "2.5 DASH", etc.)
- Remove special-casing that showed with_details() for all RPC errors unconditionally:
all required user-facing info is already in each variant's Display string;
technical details are now shown only in developer mode (aligns with CLAUDE.md §error-messages rule 4)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(rpc): address PR review — context fallback, success banner, error source
- Guard in-memory config update + reinit behind a check that the target
network's AppContext actually exists; skip both when it doesn't so we
don't accidentally overwrite mainnet config via the fallback path.
- Show success banner only when reinit succeeds; show a warning when it
fails so the user knows the connection may not reflect the new password.
- Make CoreRpcConnectionFailed.source Option<dashcore_rpc::Error> so
chain_lock_rpc_error can pass None instead of fabricating a fake
ConnectionRefused error from a borrowed reference.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: simplify shielded helpers comment in initialization.rs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(db): address PR #789 review — doc comments, migration default
- Split misplaced doc comment: `add_wallet_transaction_status_column`
now has its own Migration 30 doc; `rename_network_dash_to_mainnet`
gets its own Migration 29 doc.
- Add inline comment explaining why the migration uses DEFAULT 2
(Confirmed) while fresh CREATE TABLE in wallet.rs uses DEFAULT 0.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(db): remove duplicate shielded methods after v1.0-dev merge
On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev
backport inlined them in initialization.rs (no shielded.rs on that
branch). Merging v1.0-dev back caused E0592 duplicate definitions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: simplify shielded helpers comment in initialization.rs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore(db): document migration DEFAULT 2 vs fresh DEFAULT 0
Existing transactions predate status tracking and are assumed
confirmed (DEFAULT 2). Fresh installs use DEFAULT 0 (Unconfirmed).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(wallet): address review — zero-balance detection, status warning
Add `spv_balance_known: bool` to `Wallet` to distinguish a synced
zero-balance wallet from an unsynced one. `spv_confirmed_balance()`
now returns `None` only before the first SPV report, and `Some(0)`
after SPV confirms an empty wallet.
Add `tracing::warn!` in `TransactionStatus::from_u8` when an unknown
discriminant is encountered, avoiding silent data coercion in a
financial context.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(error): address review — typed BIP32 source, migration error variant
Replace WalletKeyDerivationFailed { detail: String } with a typed
#[source] field (Box<dyn Error + Send + Sync>) so the error chain is
preserved and Display/Debug separation is explicit. Update all callsites
in wallet/mod.rs and identity/mod.rs accordingly.
Replace the semantically wrong InvalidParameterName map_err in
rename_network_dash_to_mainnet with a plain ? — execute() already
returns rusqlite::Result so no conversion is needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test(db): add v33 consolidated migration regression tests
Two test scenarios verify the v33 consolidated migration that merges
sub-migrations v28-v32 (core_wallet_name, shielded tables, contacts,
wallet transaction status, network rename):
1. Fresh install creates all v33 tables/columns directly via create_tables()
2. Upgrade from v27 runs the full migration path and produces identical schema
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): show warning when config save fails instead of success
When config.save() fails, stop early with a warning banner instead
of continuing to reinit and showing a misleading success message.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(error): consolidate format_credits_as_dash and remove jargon from user messages
- fee_estimation::format_credits_as_dash now delegates to Amount::dash_from_credits()
instead of doing its own f64 math with fixed 8 decimals; output is now trimmed
(e.g. "1 DASH" instead of "1.00000000 DASH")
- Remove private format_credits_as_dash from error.rs; import the pub version from
fee_estimation instead
- IdentityInsufficientBalance: show amounts in DASH rather than raw credit integers
- ShieldedAnchorMismatch: replace "out of sync" with plain-language retry guidance
- ShieldedFeeExceedsBalance: remove "shielded balance" / "shield more credits" jargon
- ShieldedInsufficientPoolNotes: remove count interpolations and ZK pool jargon
- Update tests throughout to match new message content and format
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(test): remove duplicate wallet store in register_test_address
register_test_address called db.store_wallet redundantly — callers
already store the wallet before calling it, causing UNIQUE constraint
violations when tests run in parallel on CI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(core): log chain lock RPC errors that aren't auth/connection failures
Silent swallowing of errors like "Unable to find any ChainLock" made it
impossible to diagnose why the active network showed as Disconnected.
Now warns via tracing when chain_lock_rpc_error returns None.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ui): surface chain lock RPC errors in Networks tab
Previously, non-auth/non-connection RPC errors (e.g. "Unable to find
any ChainLock") were silently swallowed — the UI showed "Disconnected"
with no explanation. Now the error message is carried through the
ChainLocks result, stored in ConnectionStatus.rpc_last_error, and
displayed as "Error: ..." in both developer and non-developer RPC
status labels on the Networks tab.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(ui): unified AddressInput component with autocomplete (#787)
* feat(ui): add unified AddressInput component with autocomplete
Introduce a reusable AddressInput component that handles text input with
real-time address type detection, autocomplete from wallet data, balance
display, type filtering, and network-aware validation. Supports Core,
Platform, Shielded, and Identity address kinds.
New files:
- src/model/address.rs: AddressKind enum and ValidatedAddress enum
- src/ui/components/address_input.rs: full component with 33 unit tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ui): integrate AddressInput component into send and unshield screens
Replace inline address parsing and validation with the unified
AddressInput component across 3 proof-of-concept sites:
- UnshieldCreditsScreen: full migration, removes local Destination enum
and parse_destination(), gains autocomplete and type-restricted input
- WalletSendScreen simple mode: full migration, removes AddressType enum,
replaces detect_address_type/is_shielded_address with AddressKind-based
detection, eliminates double-parsing in send handlers
- WalletSendScreen advanced mode: minimal migration, updates type
detection to use AddressKind, keeps CoreAddressInput/PlatformAddressInput
structs unchanged
Net reduction of ~47 lines per screen. All send flows preserved.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(address-input): fix misleading test and add missing coverage
The existing `disabled_type_rejected_with_correct_error` test was testing
empty input (which returns no error) rather than actually verifying type
restriction. Fixed the test and added coverage for:
- Selection-only mode rejection of manual input
- Identity validation with valid/invalid identifiers
- Truncate boundary at exactly 16/17 characters
- Empty input in selection-only and restricted-type modes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): address QA findings for AddressInput component
QA-001: Extract duplicated address detection logic from send_screen.rs
into AddressKind::detect() on the model type. Both send_screen and
address_input now delegate to the single canonical implementation.
QA-002: Fix autocomplete "...and N more" count using unfiltered total.
filtered_entries() now returns the pre-truncation match count so the
overflow label shows the correct number of remaining matches.
QA-003: Add minimum length check (>= 60 chars) to shielded address
validation. Previously any string with the correct prefix was accepted.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): address bot review findings for AddressInput component
- Validate shielded addresses via OrchardAddress::from_bech32m_string()
instead of prefix+length check only
- Use char-aware truncation in truncate_address() to prevent panics
on multi-byte UTF-8 input (DPNS labels, emoji)
- Reset AddressInput when source selection changes in send screen,
and configure allowed destination kinds per source type
- Store bech32m string in ValidatedAddress::Platform variant so
to_address_string() returns canonical encoding instead of debug hex
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): address remaining review findings for AddressInput component
- Fix manual entry not propagating validated address on blur (HIGH)
- Fix selected_from_autocomplete causing repeated change signals
- Remove protocol jargon from unshield screen messages
- Reject mixed-case bech32m platform addresses per BIP-350
- Use per-instance ComboBox ID to prevent state collision
- Clear cached_detection after autocomplete selection
- Fix doc comments and reuse filtered_entries() computation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ui): support multi-wallet autocomplete in AddressInput
Replace with_wallet/set_wallet (single wallet) with with_wallets/set_wallets
(slice of wallets). When multiple wallets are loaded, each autocomplete entry
is prefixed with the wallet alias so the user can tell which wallet owns the
address.
send_screen.rs now passes all loaded wallets to AddressInput instead of only
the selected one.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ui): restore missing "Show zero-balance addresses" checkbox
The checkbox was accidentally dropped during a branch merge. Restored
the horizontal layout with heading on the left and checkbox
right-aligned, matching the v1.0-dev implementation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): improve AddressInput autocomplete UX
- Fix click selection: keep popup rendered when text field loses focus
so the click handler fires (was gated on has_focus only)
- Show dropdown on focus with any input length (removed 3-char minimum)
- Add type suffix (Core), (Platform), (Identity), (Shielded) to
dropdown entries when multiple address types are enabled
- Validate immediately on paste (text >3 chars) instead of requiring
blur first, so "Fund Platform Address" button activates without
needing to click away
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): make entire autocomplete row clickable in AddressInput
Previously only the address label was clickable — clicking the balance
text on the right side of the row did nothing. Now the click handler
uses the horizontal row response, so the entire row triggers selection.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): fix autocomplete click handling and format balances with 4dp
- Capture selectable_label click response instead of discarding it;
combine with row-level click for full-row clickability
- Format all DASH balances in dropdown with exactly 4 decimal places
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): use single interaction rect for full-row clickable autocomplete
Replace selectable_label + horizontal layout with allocate_exact_size
and manual painting — no child widgets steal clicks, the entire row
(address, balance, dead space) is clickable with hover feedback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(ui): add wallet address autocomplete to unshield screen
Populate AddressInput with wallet addresses via .with_wallets() so
users can select a destination from the dropdown instead of manually
entering addresses.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(error): handle asset lock and shielded insufficient funds errors with user-friendly messages
Add two new TaskError variants to replace raw SDK errors with actionable,
jargon-free messages:
- AssetLockOutPointInsufficientBalance: shown when an asset lock outpoint
has been partially consumed and lacks credits for the operation. Extracts
credits_left/credits_required and displays DASH amounts.
- ShieldedAddressInsufficientFunds: shown when a shielded broadcast fails
because the address balance is too low. Detected in shielded_broadcast_error()
before falling through to ShieldedBroadcastFailed.
Both follow the IdentityInsufficientBalance pattern with format_credits_as_dash.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): use AmountInput component for all amount inputs in wallet screens
Replace raw text_edit_singleline + custom parse_amount_*() methods with
the AmountInput component in 4 shielded screens (unshield, shield,
shield-from-asset-lock, shielded-send). This fixes a bug where entering
"1" would send 1 credit instead of 1 DASH because the raw input had
ambiguous parsing. Also adds "Amount (DASH):" label to SendScreen's
AmountInput and removes its redundant manual label.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add UI components reference and teach CLAUDE.md to use it
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ui): redesign wallet screen information architecture
Replace the old Balances|Shielded top-level tabs and account dropdown
with a unified layout that better matches how users think about their
wallet:
- Balance section with collapsible breakdown (Core/Platform split)
- Dev Tools expandable button replaces scattered dev-mode controls
- Transaction History shown as collapsible section (visible in all modes)
- Accounts & Addresses use tabs instead of a dropdown selector
- Shielded view moved from top-level tab to an account tab
- Asset Locks restricted to the Dash Core tab only
- "Main Account" renamed to "Dash Core" throughout
- Fee column added to transaction table (dev mode only)
- "Get Test Dash" button opens testnet faucet in dev tools
Tab visibility follows progressive disclosure: default mode shows only
Dash Core, Platform, and Shielded tabs. Developer mode reveals all
account types that have addresses.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): use AddressInput component in Mine dialog for core address selection
Replace the manual ComboBox address selector in the Mine Blocks dialog
with the unified AddressInput component, configured for Core-only
addresses with selection-only mode from the current wallet.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): trigger shielded balance refresh after all shielding operations
After Shield, Shield from Core, Send Private, Unshield, and Send Dash
(with shielded source) complete successfully, automatically dispatch a
SyncNotes task so the shielded tab shows updated balances without
manual refresh.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: demote cookie auth fallback log to trace level
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): prevent transaction list showing wrong wallet data
Add defensive checks in render_transactions_section to prevent
cross-wallet transaction leakage:
1. Verify the selected wallet Arc matches the canonical one in
app_context.wallets (guards against stale references).
2. Filter displayed transactions to only those with at least one
output matching the wallet's known addresses (prevents showing
transactions from other wallets that leaked in via a
non-wallet-scoped RPC endpoint).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): auto-show zero-balance addresses when wallet is empty
When all addresses have zero balance and there are fewer than 5
addresses total, bypass the zero-balance filter so new/empty wallets
show their addresses instead of a blank list. The checkbox remains
functional for wallets with balances.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): address PR review — shielded balance, dev tools layout
1. Include shielded balance in the collapsible balance breakdown
2. Add shielded balance to the total wallet balance everywhere
3. Right-align the Dev Tools button in the action row
4. Always show downward arrow on Dev Tools button
5. Dev Tools opens as a vertical dropdown popup instead of
a horizontal inline row
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): rename transaction heading, add explorer link, merge zk-fixes
- Rename "Transaction History" to "Dash Core Transaction History"
- Add "View" button on transactions for Mainnet/Testnet (opens Insight explorer)
- Merge zk-fixes (mine dialog, shielded refresh, wallet bug fixes, log level)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): trigger shielded sync on wallet refresh and wallet switch
The wallet Refresh button now dispatches a SyncNotes task alongside the
core wallet refresh when the shielded wallet has been initialized.
Switching HD wallets also triggers a full refresh (core + shielded) on
the next frame for unlocked wallets.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(log): add diagnostic logging for shielded transfer operations
Add tracing::info! and tracing::debug! logs throughout the shielded
transfer pipeline to enable post-mortem diagnosis when balances don't
update after broadcast. Key additions:
- bundle.rs: log amount, input notes, change before building each
shielded operation (transfer, shield, unshield, withdrawal,
shield-from-asset-lock); log broadcast success with note that
balance updates after next block
- sync.rs: warn when next_start_index is 0 (full rescan); log
post-sync balance with unspent note count
- context/shielded.rs: log note spend marking with before/after
unspent counts in with_anchor_retry
- shielded_send_screen.rs: log task result variant received and
post-transfer sync completion
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): improve Dev Tools dropdown layout and refresh mode cycling
Right-align popup content to match the Dev Tools button position.
Replace ComboBox with a cycle-on-click button to avoid nested popup
conflicts in egui, where the inner ComboBox dropdown would close the
parent popup.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): fix shielded balance conversion, add tab balances, reorder sync status
- Fix shielded balance inflating total: divide credits by CREDITS_PER_DUFF
before summing with duffs-denominated balances (was showing 5000 DASH
instead of 5 DASH)
- Show balances in account tab labels: Dash Core (0.9980) | Platform (0.1000)
| Shielded (5.0000), with (empty) for zero-balance tabs
- Move Sync Status section inside detail panel between balance breakdown
and action buttons for better visual flow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ui): collapsible sections, move tx history to Dash Core tab, restore account types
- Wrap Addresses, Transaction History, Asset Locks, and Shielded Notes
in collapsible headers (default open, tx history default closed)
- Move Dash Core Transaction History from top-level into the Dash Core
tab, between Addresses and Asset Locks
- In developer mode, show ALL account category tabs (Bip44, Platform,
Bip32, CoinJoin, Identity Registration/System/Top-up/Invitation,
Provider) even when no addresses exist for that type
- Add TODO for shielded tab layout redesign
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): improve shielded transfer UX messaging for balance update delays
After a shielded transfer, the sender's change notes and the recipient's
new notes won't appear until the next block is mined and synced. Users
see their balance temporarily drop to 0 and think the transfer failed.
Add informational messages on all three shielded transfer screens
(ShieldedSendScreen, UnshieldCreditsScreen, WalletSendScreen) explaining
that balances will update after the next block is confirmed. For
shielded-to-shielded transfers, also note that the recipient needs to
sync after the next block.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ui): consolidate dev-mode accounts into System tab, limit balance decimals
Replace all individual dev-mode-only account type tabs (Identity Registration,
Identity System, Identity Top-up, Identity Invitation, CoinJoin, Provider,
Legacy BIP32) with a single "System" tab. Inside the System tab, each account
type appears as a collapsible section (collapsed by default) showing address
count and balance.
Tab order: Dash Core | Platform | Shielded | System (dev-mode only, always last)
Additional changes:
- Limit tab balance display to max 4 decimal places (was 8)
- Rename "Dev Tools" button to "Tools"
- Simplify refresh mode button: single button with "Refresh mode: X" text,
no separate label or arrow indicator
- Remove "Accounts & Addresses" section heading
- Style active tab with underline for visual distinction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(shielded): scope commitment tree per wallet for multi-wallet correctness
Each wallet's ClientPersistentCommitmentTree now uses its own dedicated
SQLite file under <data_dir>/shielded/<hex>.db instead of sharing the
main database's global commitment_tree_* tables. This prevents wallets
from stepping on each other's Merkle tree state, which caused invalid
witnesses and silently rejected transactions.
Changes:
- SHLD-001: Per-wallet commitment tree DB files via ClientPersistentCommitmentTree::open()
- SHLD-002: clear_commitment_tree_for_wallet() scoped to a single wallet
- SHLD-007: Safety-net resync guard now logs wallet seed hash before clearing
- Migration v34: drops orphaned global commitment_tree_* tables
- clear_network_data() deletes per-wallet tree files for the target network
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(shielded): prevent permanent state leak on sync_notes failure in with_anchor_retry
When sync_notes() failed after an anchor mismatch, the early `?` return
skipped re-inserting the shielded state back into the HashMap, permanently
orphaning it until app restart. Now the sync result is captured without
early return, ensuring the state is always re-inserted regardless of
success or failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): apply password change for current session even when config save fails
The in-memory config update and SDK reinit only ran on the save-success
path, silently discarding the password change on save failure despite
the banner promising session-level application. Now the in-memory update
and reinit run unconditionally; only the banner text varies across the
four (save x reinit) outcome combinations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(error): use dedicated TaskError variants for non-build wallet errors
reload_utxos() and recalculate_affected_address_balances() errors were
routed through shielded_build_error() which pattern-matches for build-
specific patterns like "AnchorMismatch". These are wallet operations,
not shielded builds. Added WalletUtxoReloadFailed and
WalletBalanceRecalculationFailed variants with appropriate user-facing
messages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(error): preserve error chain in CoreRpcConnectionFailed
chain_lock_rpc_error() took the RPC error by reference and created
CoreRpcConnectionFailed with source: None, dropping all diagnostic
info. Added a detail: Option<String> field to the variant to carry
the formatted error. Boxed the source field to keep the enum size
under the clippy threshold. Updated all constructors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ui): two-column wallet header layout, rename Tools to Advanced
Split the wallet detail panel header into left (name, total balance,
action buttons) and right (collapsible balance breakdown, sync status)
columns. Renamed the "Tools" dropdown to "Advanced" and moved it into
the action buttons row instead of right-aligning it separately.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(test): rename test_v33_migration_fresh_install to match DB version 34
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert(shielded): use shared main DB for commitment tree instead of per-wallet files
Reverts the per-wallet SQLite file approach (63ce0e18). Multiple wallets
share the same commitment_tree_* tables in the main database. This is
accepted behavior until the SDK adds proper wallet-scoping support
(dashpay/grovedb#653).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(shielded): fix stale clear_commitment_tree_for_wallet reference after merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(shielded): harden anchor retry, commitment tree clearing, and state guard
SEC-002: Verify shielded balance after anchor retry sync before retrying
the operation — prevents doomed retries with zero balance.
SEC-003: Handle missing commitment tree tables in clear_commitment_tree_tables
(grovedb creates them lazily, so DELETEs fail on fresh installs).
Fix clear_network_data to log-and-continue when commitment tree clearing
fails — these tables are optional and shouldn't block network data reset.
Add SEC-001 INTENTIONAL annotation for panic safety in with_anchor_retry.
Document shield_from_asset_lock_task: anchor retry is not applicable since
it only reads the payment address and doesn't use the commitment tree.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(core): sanitize RPC errors and simplify CoreRpcConnectionFailed
SEC-006: Store user-friendly message instead of raw RPC error string
in network status when chain lock query fails with a non-auth,
non-connection error.
CODE-002: Remove redundant detail field from CoreRpcConnectionFailed --
format diagnostic info into the url field instead, keeping source for
the error chain.
Add SEC-005 INTENTIONAL annotation for RPC errors in status tooltip.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): config permissions, address detection, and per-frame caching
SEC-007: Set config file permissions to 0600 on Unix after save (contains
RPC credentials).
CODE-003: Deduplicate Amount::dash_from_duffs by delegating to
dash_from_credits.
CODE-006: Remove unused network parameter from AddressKind::detect --
detection is format-based, network validation happens separately.
CODE-004/CODE-005: Cache filtered transaction indices per wallet to
avoid rebuilding HashSet and filtering on every frame. Invalidated
on wallet switch and refresh.
CODE-008: Reset AddressInput widgets on network switch so they pick up
the new network for validation. Add invalidate_address_input methods
to SendScreen, UnshieldCreditsScreen, and WalletsBalancesScreen.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(logging): correct log levels per coding best practices
- debug! -> trace!: "state transition built, broadcasting" messages in
bundle.rs (5 occurrences) — primary-path step-by-step progress
- trace! -> debug!: cookie auth fallback in core/mod.rs and context/mod.rs
(2 occurrences) — secondary/fallback execution path, not primary flow
- info! -> debug!: anchor mismatch retry in context/shielded.rs — error
handling branch, not a business event
- info! -> debug!: "marked N note(s) spent" in context/shielded.rs —
internal bookkeeping, not a user-visible business event
- info! -> debug!: post-transfer sync complete in shielded_send_screen.rs —
internal plumbing at screen level
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: minor ui fixes
* feat(ui): replace Advanced dropdown with inline right-aligned buttons
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): use allocate_ui_with_layout to right-align dev buttons
The right_to_left layout inside with_layout only got the remaining
space after left buttons, centering the dev buttons instead of
pushing them to the right edge. Using allocate_ui_with_layout with
the full remaining width forces the right-to-left block to span
the entire remaining area.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): eagerly initialize shielded balance on wallet screen open
The shielded tab balance only recalculated when the user clicked the
Shielded tab because ShieldedTabView was lazily initialized on tab
selection. This meant the wallet list and tab header showed zero balance
until the user manually visited the tab.
Two-layer fix:
- Backend: call initialize_shielded_wallet() in handle_wallet_unlocked()
so persisted shielded balance is loaded into shielded_states at wallet
load time, making it available to all UI screens immediately.
- UI: eagerly create ShieldedTabView on wallet screen construction and
wallet switch, and tick() it every frame so the init/sync chain
(InitializeShieldedWallet -> SyncNotes -> CheckNullifiers) runs even
when the Shielded tab is not the active tab.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove stale balance_breakdown_expanded field from cherry-pick
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): move action buttons to full-width row below header columns
The button bar was inside the left column (55% width), so the
right-aligned dev buttons ended up in the middle of the screen.
Move render_action_buttons() below the two-column header so it
spans the full available width.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(ui): change sync status to bullet-point layout
Replace the wide horizontal Platform line (pipe-separated) with
individual bullet-point items: Core, Addresses, Notes, Nullifiers
each on their own line. Removes the Frame wrapper for a lighter
appearance.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(shielded): prevent double balance from redundant init + sync chain
When eager init (wallet_lifecycle) populates shielded_states and then
ShieldedTabView.tick() dispatches another InitializeShieldedWallet,
the idempotency check returns early but handle_result() chains
SyncNotes which can re-append notes already loaded from DB.
Fix: tick() now checks if shielded_states already contains the
wallet's state. If so, it adopts the cached balance and marks
itself initialized without dispatching a backend task, preventing
the redundant sync chain.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(shielded): move initialization entirely to backend, remove UI init path
The shielded wallet had two initialization paths: the backend
(handle_wallet_unlocked -> initialize_shielded_wallet) and the UI
(ShieldedTabView::tick dispatching InitializeShieldedWallet). This
created redundant init attempts and blurred the responsibility boundary.
Now:
- handle_wallet_unlocked initializes the shielded state synchronously,
then queues SyncNotes -> CheckNullifiers as a background task
- ShieldedTabView reads its state from AppContext::shielded_states
via refresh_from_backend_state(), never dispatching init itself
- The "Initialize Shielded Wallet" button and WalletUnlockPopup are
removed from ShieldedTabView (wallet unlock already calls
handle_wallet_unlocked which handles everything)
- Resync Notes (developer mode) still dispatches InitializeShieldedWallet
as an explicit user action
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(shielded): deduplicate notes in sync to prevent double balance
When the commitment tree resets (empty/corrupt) but persisted notes
remain in the DB, initialize_shielded_wallet loads notes from DB
while last_synced_index falls to 0. The subsequent sync rescans
from position 0 and re-appends all decrypted notes — duplicating
the ones already in memory from DB, doubling the balance.
Fix: sync_notes() now checks if a note at the same position already
exists in shielded_state.notes before appending, preventing
duplicates regardless of the last_synced_index value.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(ui): add shielded diversified address table
Replace the single-address display with prev/next buttons in the
Shielded tab with a collapsible address table showing all generated
diversified addresses. Each row shows the index, truncated bech32m
address (click or Copy button to copy full address), and status
(Default for index 0). The section is collapsed by default in normal
mode and expanded in developer mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address review findings — duplicate controls, perf, tab visibility
- Remove duplicate sync status/buttons block in shielded_tab.rs
- Fix BIP44 accounts with index > 0 being invisible (regression)
- Fix early return in render_account_tabs blocking Shielded tab for new wallets
- Fix double-tick of ShieldedTabView per frame when tab is active
- Fix system_tab_sections O(n*m) perf by precomputing address counts
- Track queue_shielded_sync via subtasks for graceful shutdown
- Use HashSet for O(1) duplicate note check in sync (was O(n^2))
- Fix balance_breakdown default_open to respect developer mode
- Fix is_system_category to delegate to is_visible_in_default_mode
- Fix doc comment for system_tab_sections sort order claim
- Narrow #[allow(dead_code)] to specific field on AccountSummary
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): replace mutex unwrap() with graceful error handling in shielded tab
Replace .lock().unwrap() calls on shielded_states with .lock().ok()
patterns throughout ShieldedTabView to prevent UI panics on poisoned
mutex. Shows fallback messages or skips updates gracefully.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(mcp): resolve async lifetime errors for Rust 2024 edition
Use spawn_blocking + block_on in dispatch_task to avoid Send bound
issues with platform SDK types (DataContract/Sdk references across
await points). Same pattern already used by AppState::handle_backend_task.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: fix mcp dispatch
* doc: remove obsolete manual teting docs
* fix(ui): include zero-balance addresses in AddressInput wallet entries
Pull Core addresses from known_addresses (all derived) instead of
address_balances (only funded). Balance is looked up from
address_balances, defaulting to 0. Callers use with_balance_range(1..)
when they need funded-only addresses.
Fixes Mine dialog showing empty dropdown for fresh/zero-balance wallets.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): sort AddressInput wallet entries alphabetically
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: remove redundant sort in extract_wallet_entries
filtered_entries() already sorts alphabetically by display_label.
The sort added in extract_wallet_entries was redundant.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): clarify shielded network validation logic
Replace double-negation with explicit mainnet-class comparison.
Documents that testnet/devnet/local share the same HRP ("tdash1z")
and cannot be distinguished at the address level.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): include send-all transactions in wallet history
Check both outputs and inputs when filtering transactions. Build an
OutPoint→Address lookup from the wallet's own transactions to resolve
input addresses. Send-all transactions (no change output) were
previously dropped because no output matched known_addresses.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): use is_ours flag for transaction filtering
Simplify transaction filter to use the is_ours flag instead of
address matching. Fix SPV path to set is_ours=true for all wallet
transactions (upstream only sets it for sends). SPV history is
per-wallet, so all entries are ours by definition.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(e2e): verify is_ours flag for SPV send and receive transactions
Sends funds between two wallets via SPV and asserts that both the
sender (negative net_amount) and receiver (positive net_amount) have
is_ours=true. Validates the fix to the SPV reconcile path that
overrides the upstream library's send-only is_ours logic.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(core): clear RPC error state on successful chain lock fetch
When get_best_chain_lock() succeeds on the active network, explicitly
clear any lingering RPC error in ConnectionStatus so the connection
indicator recovers after a transient outage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(model): move is_platform_address_string from UI helpers to model layer
AddressKind::detect() in the model layer depended on crate::ui::helpers
for platform address detection — wrong layering. Move the function to
src/model/address.rs and keep a re-export in helpers for existing callers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): add actionable guidance to address validation error messages
Each validation error now tells the user what to do, not just what went
wrong: "check for typos", "check you are using the correct network",
"use all lowercase". Messages that already had guidance are unchanged.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): invalidate address inputs on all screens during context switch
TransferScreen, AddressBalanceScreen, and ShieldedSendScreen retained
stale address text from the previous network after a context switch.
Add invalidate_address_input() to each and call it from change_context().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): clear password field on network switch when no config exists
When switching networks, if no config entry exists for the new network
(or the password is empty), the password input field now clears instead
of retaining the previous network's password.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): clear validated_destination in invalidate_address_input
WalletSendScreen and UnshieldCreditsScreen cleared the AddressInput
widget but left validated_destination with stale validation from the
previous network. Now both are cleared together.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): consume ShieldedNotesSynced to update shielded send screen state
After a shielded transfer, the post-transfer note sync result was only
logged but never used to update the UI. Now ShieldedNotesSynced updates
the balance display, clears the pending-update banner, and appends the
remaining balance to the success message.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: apply cargo +nightly fmt formatting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(model): disambiguate Core vs Identity address detection by prefix
Core addresses on Dash start with X/Y (mainnet) or y/8/7 (testnet).
When the input starts with a known Core prefix, try Core first.
Otherwise try Identity first, since Identity IDs use the same Base58
alphabet but don't have Core address prefixes.
This fixes identity-only AddressInput mode rejecting valid Identity
IDs that happened to parse as Core addresses, and eliminates the
flaky test that had to skip Core-looking random identifiers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(core): show actual RPC error on Networks page instead of generic message
The Networks page is a debugging tool — users need the real error text
to diagnose Dash Core issues. Replace sanitized "RPC error — check
Dash Core status" with the actual error from dashcore_rpc.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(db): resolve deadlock in clear_network_data
clear_network_data() held self.conn.lock() for the transaction, then
called clear_commitment_tree_tables() which tried to acquire the same
mutex — classic non-reentrant mutex deadlock. UI froze on "Delete Data".
Fix: scope the connection lock so it's released before the commitment
tree clearing acquires it again.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): replace mutex unwrap() with graceful .ok() in shielded_sync_task
The shielded_states mutex lock used .unwrap() which would panic on a
poisoned mutex. Replace with .ok()? to return None gracefully.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): replace RwLock unwrap() with graceful error handling in shielded tab
Replace wallets.read().unwrap() with .read().ok() and show an error
label instead of panicking on a poisoned RwLock.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(spv): add debug log when overriding is_ours for receive transactions
Log at debug level when SPV reports is_ours=false for a receive
transaction (net_amount >= 0) to detect upstream API behavior changes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(shielded): log warning on note value divergence during position dedup
Use a HashMap<position, value> instead of HashSet<position> so we can
detect when a re-synced note at an existing position has a different
value — indicating potential data integrity issues.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* perf(model): avoid allocation in is_platform_address_string
Replace to_lowercase() with eq_ignore_ascii_case() for the HRP prefix
check. The HRP constants are ASCII-only so this is safe and avoids a
heap allocation on every call.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(ui): remove unused label field and is_key_only from AccountSummary
The label field was constructed but never read outside the builder.
is_key_only() had no callers. Remove both instead of suppressing
dead_code warnings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(ui): avoid cloning full AccountTab enum in tab content match
Extract only the category data (clone of AccountCategory + index) before
the match instead of cloning the entire enum. This avoids unnecessary
allocation for the Shielded and System variants.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): clear validated_address on network switch in mine dialog
invalidate_address_inputs() cleared the AddressInput widget but left
the validated_address stale, which could reference an address from the
previous network.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(model): move truncate_address to model layer and document ASCII precondition
Move truncate_address from address_input.rs and shielded_tab.rs into
src/model/address.rs as a parameterized public function. Both callers
now delegate to the shared implementation with their own prefix/suffix
lengths.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(shielded): document why spawn_blocking trampoline is needed in queue_shielded_sync
The spawn_blocking(block_on(...)) pattern is required because async
methods on Arc<Self> produce non-'static futures due to a known Rust
compiler limitation (rust-lang/rust#100013). Document this to prevent
future removal attempts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: apply nightly rustfmt formatting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(wallet): bootstrap platform addresses on wallet creation
bootstrap_wallet_addresses only ran when known_addresses was empty,
but new_from_seed already derives one Core address. This meant
platform payment addresses were never bootstrapped for new wallets
— only populated later by network sync (which returns nothing for
fresh wallets with no on-chain activity).
Now checks for the presence of PlatformPayment addresses in
watched_addresses and runs bootstrap if missing. Bootstrap functions
are idempotent (add_address_if_not_exists).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): show bootstrapped platform addresses in AddressInput
Platform addresses were only populated from platform_address_info
(network sync results). Fresh wallets with no on-chain activity had
empty platform_address_info, so no platform addresses appeared in
the Send screen autocomplete.
Now derives platform addresses from watched_addresses (which contains
all bootstrapped platform payment addresses), with balance looked up
from platform_address_info (defaulting to 0).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(ui): pass account filter directly to render_address_table
Remove the `selected_account` field that was mutated as a side-effect
during rendering, causing the last-rendered collapsible section in the
System tab to overwrite previous sections' state. Now each render call
receives its `(AccountCategory, Option<u32>)` as a direct parameter.
Additionally, `system_tab_sections` now builds per-(category, index)
pair so multi-index accounts show correct per-index balance and address
counts instead of aggregating across all indices.
Fixes CODE-005, CODE-011, PROJ-004.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* perf(shielded): move wallet initialization to background thread
ZIP32 key derivation and DB reads in `initialize_shielded_wallet` ran
synchronously on the UI thread during `handle_wallet_unlocked()`.
Move the call to a `spawn_blocking` task tracked by `subtasks` so the
UI remains responsive. The shielded tab already handles the
"initializing" state gracefully via its `is_initialized` check.
Fixes CODE-010.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(spv): add unit tests for is_ours override logic
Extract the SPV is_ours override into a named function and add four
unit tests covering: outgoing already-ours, incoming not-ours (the
main override case), zero-amount edge case, and outgoing not-ours.
A comment explains why bloom filter false positive testing requires
mocking the SPV layer and is out of scope for unit tests.
Fixes PROJ-002.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(user-stories): add stories for wallet tab redesign
Add WAL-021 through WAL-024 covering the tab-based navigation,
developer-mode System tab, collapsible transaction history, and
collapsible balance breakdown sections introduced in PR #791.
Fixes PROJ-007.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: apply nightly rustfmt formatting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ui): distinguish change addresses in AddressInput autocomplete
Core addresses now show "(change)" suffix for BIP44 change addresses
(m/44'/5'/0'/1/x). New with_exclude_change(true) builder method
filters them out — send inputs should not display change addresses
since users don't share them with others.
Change detection uses the existing DerivationPathHelpers::is_bip44_change
trait method from the wallet…
…on (#797) (#800) Regtest and Testnet share the `tdash` bech32m prefix, but `PlatformAddress::from_bech32m_string()` always returns `Network::Testnet` for `tdash` addresses. The strict `!=` comparison in the fund-platform dialog rejected valid Regtest addresses with "Address network mismatch". - Make `networks_address_compatible()` `pub(crate)` in `model::wallet` so all modules can reuse the canonical check - Remove the duplicate private copy in `backend_task::core` and import from the single source - Replace `network != self.app_context.network` in `dialogs.rs` with `networks_address_compatible()` so Testnet/Devnet/Regtest addresses are accepted interchangeably Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): add actionable messages for shielded fee and pool-size errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): add actionable message for shielded anchor mismatch Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): auto-resync notes and retry on anchor mismatch When unshield_credits, shielded_transfer, or shielded_withdrawal fails with ShieldedAnchorMismatch (stale commitment tree witnesses), automatically sync notes once to update the tree and retry the operation. Only one resync attempt is made — if the retry also fails, the error propagates as-is. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): ensure shielded tables exist and log DB errors during init Three changes to fix empty shielded balance after app restart: 1. Defensive table creation in initialize(): after migrations complete, ensure shielded_notes and shielded_wallet_meta tables exist even if the DB version was already past v29/v30 from a prior build. Both methods use CREATE TABLE IF NOT EXISTS, so this is safe. 2. Log DB errors during shielded init: change silent if-let-Ok pattern to match/Err with tracing::warn, so missing-table errors are visible instead of silently producing empty note lists. 3. Safety net resync: when the commitment tree has been synced but no unspent notes were loaded from DB, clear the tree and reset last_synced_index to 0 so the auto-sync rediscovers all notes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): consolidate migrations v28-v32 into v33 Resolves version numbering collision between zk and v1.0-dev branches: the zk branch used v28 for shielded tables while v1.0-dev used v28 for contacts. After merging, users migrating from either branch could end up with missing tables depending on which version their DB was at. v33 runs all sub-migrations idempotently in one step, ensuring all tables exist regardless of prior migration history. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): consolidate migrations v28-v32 into v33 Resolves version numbering collision between zk and v1.0-dev branches: the zk branch used v28 for shielded tables while v1.0-dev used v28 for contacts. After merging, users migrating from either branch could end up with missing tables depending on which version their DB was at. v33 runs all sub-migrations idempotently in one step, ensuring all tables exist regardless of prior migration history. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: pin platform dependency to zk-fixes revision Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): address PR review — fresh schema, error propagation, rename coverage - propagate SQL error in add_nullifier_sync_timestamp_column instead of swallowing it with unwrap_or(false) - add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already present on this branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): address PR review — fresh schema, error propagation, rename coverage - propagate SQL error in add_nullifier_sync_timestamp_column instead of swallowing it with unwrap_or(false) - add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already present on this branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): add foreign key constraints to shielded tables shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by all other per-wallet tables in the codebase. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): add foreign key constraints to shielded tables shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by all other per-wallet tables in the codebase. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): remove duplicate shielded methods after v1.0-dev merge On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev backport inlined them in initialization.rs (no shielded.rs on that branch). Merging v1.0-dev back caused E0592 duplicate definitions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): remove duplicate wallet store in register_test_address register_test_address called db.store_wallet redundantly — callers already store the wallet before calling it, causing UNIQUE constraint violations when tests run in parallel on CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(shielded): address PR review — error propagation, retry helper, safety net - Propagate DB error from get_unspent_shielded_notes instead of swallowing - Extract with_anchor_retry() helper to deduplicate ~27 lines across shielded_transfer_task, unshield_credits_task, shielded_withdrawal_task - Fix safety net false positive: check all notes (spent + unspent) to distinguish "never had notes" from "all spent" - Propagate error from clear_commitment_tree_tables instead of discarding Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): address PR review — Amount formatting, Display completeness - Add Amount::dash_from_credits() constructor to model/amount.rs - Replace manual format_credits_as_dash() logic with Amount::dash_from_credits().to_string(), so credit amounts use Amount's Display (with DASH unit, trimmed trailing zeros) - Update ShieldedFeeExceedsBalance #[error] message: remove redundant "Dash" literal (Amount's Display already includes the "DASH" unit suffix) - Update format_credits_as_dash tests to match new output format ("1 DASH", "2.5 DASH", etc.) - Remove special-casing that showed with_details() for all RPC errors unconditionally: all required user-facing info is already in each variant's Display string; technical details are now shown only in developer mode (aligns with CLAUDE.md §error-messages rule 4) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(rpc): address PR review — context fallback, success banner, error source - Guard in-memory config update + reinit behind a check that the target network's AppContext actually exists; skip both when it doesn't so we don't accidentally overwrite mainnet config via the fallback path. - Show success banner only when reinit succeeds; show a warning when it fails so the user knows the connection may not reflect the new password. - Make CoreRpcConnectionFailed.source Option<dashcore_rpc::Error> so chain_lock_rpc_error can pass None instead of fabricating a fake ConnectionRefused error from a borrowed reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: simplify shielded helpers comment in initialization.rs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): address PR #789 review — doc comments, migration default - Split misplaced doc comment: `add_wallet_transaction_status_column` now has its own Migration 30 doc; `rename_network_dash_to_mainnet` gets its own Migration 29 doc. - Add inline comment explaining why the migration uses DEFAULT 2 (Confirmed) while fresh CREATE TABLE in wallet.rs uses DEFAULT 0. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): remove duplicate shielded methods after v1.0-dev merge On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev backport inlined them in initialization.rs (no shielded.rs on that branch). Merging v1.0-dev back caused E0592 duplicate definitions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: simplify shielded helpers comment in initialization.rs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(db): document migration DEFAULT 2 vs fresh DEFAULT 0 Existing transactions predate status tracking and are assumed confirmed (DEFAULT 2). Fresh installs use DEFAULT 0 (Unconfirmed). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(wallet): address review — zero-balance detection, status warning Add `spv_balance_known: bool` to `Wallet` to distinguish a synced zero-balance wallet from an unsynced one. `spv_confirmed_balance()` now returns `None` only before the first SPV report, and `Some(0)` after SPV confirms an empty wallet. Add `tracing::warn!` in `TransactionStatus::from_u8` when an unknown discriminant is encountered, avoiding silent data coercion in a financial context. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): address review — typed BIP32 source, migration error variant Replace WalletKeyDerivationFailed { detail: String } with a typed #[source] field (Box<dyn Error + Send + Sync>) so the error chain is preserved and Display/Debug separation is explicit. Update all callsites in wallet/mod.rs and identity/mod.rs accordingly. Replace the semantically wrong InvalidParameterName map_err in rename_network_dash_to_mainnet with a plain ? — execute() already returns rusqlite::Result so no conversion is needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test(db): add v33 consolidated migration regression tests Two test scenarios verify the v33 consolidated migration that merges sub-migrations v28-v32 (core_wallet_name, shielded tables, contacts, wallet transaction status, network rename): 1. Fresh install creates all v33 tables/columns directly via create_tables() 2. Upgrade from v27 runs the full migration path and produces identical schema Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): show warning when config save fails instead of success When config.save() fails, stop early with a warning banner instead of continuing to reinit and showing a misleading success message. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): consolidate format_credits_as_dash and remove jargon from user messages - fee_estimation::format_credits_as_dash now delegates to Amount::dash_from_credits() instead of doing its own f64 math with fixed 8 decimals; output is now trimmed (e.g. "1 DASH" instead of "1.00000000 DASH") - Remove private format_credits_as_dash from error.rs; import the pub version from fee_estimation instead - IdentityInsufficientBalance: show amounts in DASH rather than raw credit integers - ShieldedAnchorMismatch: replace "out of sync" with plain-language retry guidance - ShieldedFeeExceedsBalance: remove "shielded balance" / "shield more credits" jargon - ShieldedInsufficientPoolNotes: remove count interpolations and ZK pool jargon - Update tests throughout to match new message content and format Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(test): remove duplicate wallet store in register_test_address register_test_address called db.store_wallet redundantly — callers already store the wallet before calling it, causing UNIQUE constraint violations when tests run in parallel on CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(core): log chain lock RPC errors that aren't auth/connection failures Silent swallowing of errors like "Unable to find any ChainLock" made it impossible to diagnose why the active network showed as Disconnected. Now warns via tracing when chain_lock_rpc_error returns None. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ui): surface chain lock RPC errors in Networks tab Previously, non-auth/non-connection RPC errors (e.g. "Unable to find any ChainLock") were silently swallowed — the UI showed "Disconnected" with no explanation. Now the error message is carried through the ChainLocks result, stored in ConnectionStatus.rpc_last_error, and displayed as "Error: ..." in both developer and non-developer RPC status labels on the Networks tab. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(ui): unified AddressInput component with autocomplete (#787) * feat(ui): add unified AddressInput component with autocomplete Introduce a reusable AddressInput component that handles text input with real-time address type detection, autocomplete from wallet data, balance display, type filtering, and network-aware validation. Supports Core, Platform, Shielded, and Identity address kinds. New files: - src/model/address.rs: AddressKind enum and ValidatedAddress enum - src/ui/components/address_input.rs: full component with 33 unit tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): integrate AddressInput component into send and unshield screens Replace inline address parsing and validation with the unified AddressInput component across 3 proof-of-concept sites: - UnshieldCreditsScreen: full migration, removes local Destination enum and parse_destination(), gains autocomplete and type-restricted input - WalletSendScreen simple mode: full migration, removes AddressType enum, replaces detect_address_type/is_shielded_address with AddressKind-based detection, eliminates double-parsing in send handlers - WalletSendScreen advanced mode: minimal migration, updates type detection to use AddressKind, keeps CoreAddressInput/PlatformAddressInput structs unchanged Net reduction of ~47 lines per screen. All send flows preserved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(address-input): fix misleading test and add missing coverage The existing `disabled_type_rejected_with_correct_error` test was testing empty input (which returns no error) rather than actually verifying type restriction. Fixed the test and added coverage for: - Selection-only mode rejection of manual input - Identity validation with valid/invalid identifiers - Truncate boundary at exactly 16/17 characters - Empty input in selection-only and restricted-type modes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): address QA findings for AddressInput component QA-001: Extract duplicated address detection logic from send_screen.rs into AddressKind::detect() on the model type. Both send_screen and address_input now delegate to the single canonical implementation. QA-002: Fix autocomplete "...and N more" count using unfiltered total. filtered_entries() now returns the pre-truncation match count so the overflow label shows the correct number of remaining matches. QA-003: Add minimum length check (>= 60 chars) to shielded address validation. Previously any string with the correct prefix was accepted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): address bot review findings for AddressInput component - Validate shielded addresses via OrchardAddress::from_bech32m_string() instead of prefix+length check only - Use char-aware truncation in truncate_address() to prevent panics on multi-byte UTF-8 input (DPNS labels, emoji) - Reset AddressInput when source selection changes in send screen, and configure allowed destination kinds per source type - Store bech32m string in ValidatedAddress::Platform variant so to_address_string() returns canonical encoding instead of debug hex Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): address remaining review findings for AddressInput component - Fix manual entry not propagating validated address on blur (HIGH) - Fix selected_from_autocomplete causing repeated change signals - Remove protocol jargon from unshield screen messages - Reject mixed-case bech32m platform addresses per BIP-350 - Use per-instance ComboBox ID to prevent state collision - Clear cached_detection after autocomplete selection - Fix doc comments and reuse filtered_entries() computation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): support multi-wallet autocomplete in AddressInput Replace with_wallet/set_wallet (single wallet) with with_wallets/set_wallets (slice of wallets). When multiple wallets are loaded, each autocomplete entry is prefixed with the wallet alias so the user can tell which wallet owns the address. send_screen.rs now passes all loaded wallets to AddressInput instead of only the selected one. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ui): restore missing "Show zero-balance addresses" checkbox The checkbox was accidentally dropped during a branch merge. Restored the horizontal layout with heading on the left and checkbox right-aligned, matching the v1.0-dev implementation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): improve AddressInput autocomplete UX - Fix click selection: keep popup rendered when text field loses focus so the click handler fires (was gated on has_focus only) - Show dropdown on focus with any input length (removed 3-char minimum) - Add type suffix (Core), (Platform), (Identity), (Shielded) to dropdown entries when multiple address types are enabled - Validate immediately on paste (text >3 chars) instead of requiring blur first, so "Fund Platform Address" button activates without needing to click away Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): make entire autocomplete row clickable in AddressInput Previously only the address label was clickable — clicking the balance text on the right side of the row did nothing. Now the click handler uses the horizontal row response, so the entire row triggers selection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): fix autocomplete click handling and format balances with 4dp - Capture selectable_label click response instead of discarding it; combine with row-level click for full-row clickability - Format all DASH balances in dropdown with exactly 4 decimal places Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): use single interaction rect for full-row clickable autocomplete Replace selectable_label + horizontal layout with allocate_exact_size and manual painting — no child widgets steal clicks, the entire row (address, balance, dead space) is clickable with hover feedback. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(ui): add wallet address autocomplete to unshield screen Populate AddressInput with wallet addresses via .with_wallets() so users can select a destination from the dropdown instead of manually entering addresses. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(error): handle asset lock and shielded insufficient funds errors with user-friendly messages Add two new TaskError variants to replace raw SDK errors with actionable, jargon-free messages: - AssetLockOutPointInsufficientBalance: shown when an asset lock outpoint has been partially consumed and lacks credits for the operation. Extracts credits_left/credits_required and displays DASH amounts. - ShieldedAddressInsufficientFunds: shown when a shielded broadcast fails because the address balance is too low. Detected in shielded_broadcast_error() before falling through to ShieldedBroadcastFailed. Both follow the IdentityInsufficientBalance pattern with format_credits_as_dash. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): use AmountInput component for all amount inputs in wallet screens Replace raw text_edit_singleline + custom parse_amount_*() methods with the AmountInput component in 4 shielded screens (unshield, shield, shield-from-asset-lock, shielded-send). This fixes a bug where entering "1" would send 1 credit instead of 1 DASH because the raw input had ambiguous parsing. Also adds "Amount (DASH):" label to SendScreen's AmountInput and removes its redundant manual label. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add UI components reference and teach CLAUDE.md to use it Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): redesign wallet screen information architecture Replace the old Balances|Shielded top-level tabs and account dropdown with a unified layout that better matches how users think about their wallet: - Balance section with collapsible breakdown (Core/Platform split) - Dev Tools expandable button replaces scattered dev-mode controls - Transaction History shown as collapsible section (visible in all modes) - Accounts & Addresses use tabs instead of a dropdown selector - Shielded view moved from top-level tab to an account tab - Asset Locks restricted to the Dash Core tab only - "Main Account" renamed to "Dash Core" throughout - Fee column added to transaction table (dev mode only) - "Get Test Dash" button opens testnet faucet in dev tools Tab visibility follows progressive disclosure: default mode shows only Dash Core, Platform, and Shielded tabs. Developer mode reveals all account types that have addresses. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): use AddressInput component in Mine dialog for core address selection Replace the manual ComboBox address selector in the Mine Blocks dialog with the unified AddressInput component, configured for Core-only addresses with selection-only mode from the current wallet. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): trigger shielded balance refresh after all shielding operations After Shield, Shield from Core, Send Private, Unshield, and Send Dash (with shielded source) complete successfully, automatically dispatch a SyncNotes task so the shielded tab shows updated balances without manual refresh. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: demote cookie auth fallback log to trace level Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): prevent transaction list showing wrong wallet data Add defensive checks in render_transactions_section to prevent cross-wallet transaction leakage: 1. Verify the selected wallet Arc matches the canonical one in app_context.wallets (guards against stale references). 2. Filter displayed transactions to only those with at least one output matching the wallet's known addresses (prevents showing transactions from other wallets that leaked in via a non-wallet-scoped RPC endpoint). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): auto-show zero-balance addresses when wallet is empty When all addresses have zero balance and there are fewer than 5 addresses total, bypass the zero-balance filter so new/empty wallets show their addresses instead of a blank list. The checkbox remains functional for wallets with balances. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): address PR review — shielded balance, dev tools layout 1. Include shielded balance in the collapsible balance breakdown 2. Add shielded balance to the total wallet balance everywhere 3. Right-align the Dev Tools button in the action row 4. Always show downward arrow on Dev Tools button 5. Dev Tools opens as a vertical dropdown popup instead of a horizontal inline row Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): rename transaction heading, add explorer link, merge zk-fixes - Rename "Transaction History" to "Dash Core Transaction History" - Add "View" button on transactions for Mainnet/Testnet (opens Insight explorer) - Merge zk-fixes (mine dialog, shielded refresh, wallet bug fixes, log level) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): trigger shielded sync on wallet refresh and wallet switch The wallet Refresh button now dispatches a SyncNotes task alongside the core wallet refresh when the shielded wallet has been initialized. Switching HD wallets also triggers a full refresh (core + shielded) on the next frame for unlocked wallets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(log): add diagnostic logging for shielded transfer operations Add tracing::info! and tracing::debug! logs throughout the shielded transfer pipeline to enable post-mortem diagnosis when balances don't update after broadcast. Key additions: - bundle.rs: log amount, input notes, change before building each shielded operation (transfer, shield, unshield, withdrawal, shield-from-asset-lock); log broadcast success with note that balance updates after next block - sync.rs: warn when next_start_index is 0 (full rescan); log post-sync balance with unspent note count - context/shielded.rs: log note spend marking with before/after unspent counts in with_anchor_retry - shielded_send_screen.rs: log task result variant received and post-transfer sync completion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): improve Dev Tools dropdown layout and refresh mode cycling Right-align popup content to match the Dev Tools button position. Replace ComboBox with a cycle-on-click button to avoid nested popup conflicts in egui, where the inner ComboBox dropdown would close the parent popup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): fix shielded balance conversion, add tab balances, reorder sync status - Fix shielded balance inflating total: divide credits by CREDITS_PER_DUFF before summing with duffs-denominated balances (was showing 5000 DASH instead of 5 DASH) - Show balances in account tab labels: Dash Core (0.9980) | Platform (0.1000) | Shielded (5.0000), with (empty) for zero-balance tabs - Move Sync Status section inside detail panel between balance breakdown and action buttons for better visual flow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): collapsible sections, move tx history to Dash Core tab, restore account types - Wrap Addresses, Transaction History, Asset Locks, and Shielded Notes in collapsible headers (default open, tx history default closed) - Move Dash Core Transaction History from top-level into the Dash Core tab, between Addresses and Asset Locks - In developer mode, show ALL account category tabs (Bip44, Platform, Bip32, CoinJoin, Identity Registration/System/Top-up/Invitation, Provider) even when no addresses exist for that type - Add TODO for shielded tab layout redesign Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): improve shielded transfer UX messaging for balance update delays After a shielded transfer, the sender's change notes and the recipient's new notes won't appear until the next block is mined and synced. Users see their balance temporarily drop to 0 and think the transfer failed. Add informational messages on all three shielded transfer screens (ShieldedSendScreen, UnshieldCreditsScreen, WalletSendScreen) explaining that balances will update after the next block is confirmed. For shielded-to-shielded transfers, also note that the recipient needs to sync after the next block. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): consolidate dev-mode accounts into System tab, limit balance decimals Replace all individual dev-mode-only account type tabs (Identity Registration, Identity System, Identity Top-up, Identity Invitation, CoinJoin, Provider, Legacy BIP32) with a single "System" tab. Inside the System tab, each account type appears as a collapsible section (collapsed by default) showing address count and balance. Tab order: Dash Core | Platform | Shielded | System (dev-mode only, always last) Additional changes: - Limit tab balance display to max 4 decimal places (was 8) - Rename "Dev Tools" button to "Tools" - Simplify refresh mode button: single button with "Refresh mode: X" text, no separate label or arrow indicator - Remove "Accounts & Addresses" section heading - Style active tab with underline for visual distinction Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): scope commitment tree per wallet for multi-wallet correctness Each wallet's ClientPersistentCommitmentTree now uses its own dedicated SQLite file under <data_dir>/shielded/<hex>.db instead of sharing the main database's global commitment_tree_* tables. This prevents wallets from stepping on each other's Merkle tree state, which caused invalid witnesses and silently rejected transactions. Changes: - SHLD-001: Per-wallet commitment tree DB files via ClientPersistentCommitmentTree::open() - SHLD-002: clear_commitment_tree_for_wallet() scoped to a single wallet - SHLD-007: Safety-net resync guard now logs wallet seed hash before clearing - Migration v34: drops orphaned global commitment_tree_* tables - clear_network_data() deletes per-wallet tree files for the target network Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): prevent permanent state leak on sync_notes failure in with_anchor_retry When sync_notes() failed after an anchor mismatch, the early `?` return skipped re-inserting the shielded state back into the HashMap, permanently orphaning it until app restart. Now the sync result is captured without early return, ensuring the state is always re-inserted regardless of success or failure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): apply password change for current session even when config save fails The in-memory config update and SDK reinit only ran on the save-success path, silently discarding the password change on save failure despite the banner promising session-level application. Now the in-memory update and reinit run unconditionally; only the banner text varies across the four (save x reinit) outcome combinations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(error): use dedicated TaskError variants for non-build wallet errors reload_utxos() and recalculate_affected_address_balances() errors were routed through shielded_build_error() which pattern-matches for build- specific patterns like "AnchorMismatch". These are wallet operations, not shielded builds. Added WalletUtxoReloadFailed and WalletBalanceRecalculationFailed variants with appropriate user-facing messages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(error): preserve error chain in CoreRpcConnectionFailed chain_lock_rpc_error() took the RPC error by reference and created CoreRpcConnectionFailed with source: None, dropping all diagnostic info. Added a detail: Option<String> field to the variant to carry the formatted error. Boxed the source field to keep the enum size under the clippy threshold. Updated all constructors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): two-column wallet header layout, rename Tools to Advanced Split the wallet detail panel header into left (name, total balance, action buttons) and right (collapsible balance breakdown, sync status) columns. Renamed the "Tools" dropdown to "Advanced" and moved it into the action buttons row instead of right-aligning it separately. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(test): rename test_v33_migration_fresh_install to match DB version 34 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert(shielded): use shared main DB for commitment tree instead of per-wallet files Reverts the per-wallet SQLite file approach (63ce0e18). Multiple wallets share the same commitment_tree_* tables in the main database. This is accepted behavior until the SDK adds proper wallet-scoping support (dashpay/grovedb#653). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): fix stale clear_commitment_tree_for_wallet reference after merge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): harden anchor retry, commitment tree clearing, and state guard SEC-002: Verify shielded balance after anchor retry sync before retrying the operation — prevents doomed retries with zero balance. SEC-003: Handle missing commitment tree tables in clear_commitment_tree_tables (grovedb creates them lazily, so DELETEs fail on fresh installs). Fix clear_network_data to log-and-continue when commitment tree clearing fails — these tables are optional and shouldn't block network data reset. Add SEC-001 INTENTIONAL annotation for panic safety in with_anchor_retry. Document shield_from_asset_lock_task: anchor retry is not applicable since it only reads the payment address and doesn't use the commitment tree. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(core): sanitize RPC errors and simplify CoreRpcConnectionFailed SEC-006: Store user-friendly message instead of raw RPC error string in network status when chain lock query fails with a non-auth, non-connection error. CODE-002: Remove redundant detail field from CoreRpcConnectionFailed -- format diagnostic info into the url field instead, keeping source for the error chain. Add SEC-005 INTENTIONAL annotation for RPC errors in status tooltip. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): config permissions, address detection, and per-frame caching SEC-007: Set config file permissions to 0600 on Unix after save (contains RPC credentials). CODE-003: Deduplicate Amount::dash_from_duffs by delegating to dash_from_credits. CODE-006: Remove unused network parameter from AddressKind::detect -- detection is format-based, network validation happens separately. CODE-004/CODE-005: Cache filtered transaction indices per wallet to avoid rebuilding HashSet and filtering on every frame. Invalidated on wallet switch and refresh. CODE-008: Reset AddressInput widgets on network switch so they pick up the new network for validation. Add invalidate_address_input methods to SendScreen, UnshieldCreditsScreen, and WalletsBalancesScreen. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(logging): correct log levels per coding best practices - debug! -> trace!: "state transition built, broadcasting" messages in bundle.rs (5 occurrences) — primary-path step-by-step progress - trace! -> debug!: cookie auth fallback in core/mod.rs and context/mod.rs (2 occurrences) — secondary/fallback execution path, not primary flow - info! -> debug!: anchor mismatch retry in context/shielded.rs — error handling branch, not a business event - info! -> debug!: "marked N note(s) spent" in context/shielded.rs — internal bookkeeping, not a user-visible business event - info! -> debug!: post-transfer sync complete in shielded_send_screen.rs — internal plumbing at screen level Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: minor ui fixes * feat(ui): replace Advanced dropdown with inline right-aligned buttons Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): use allocate_ui_with_layout to right-align dev buttons The right_to_left layout inside with_layout only got the remaining space after left buttons, centering the dev buttons instead of pushing them to the right edge. Using allocate_ui_with_layout with the full remaining width forces the right-to-left block to span the entire remaining area. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): eagerly initialize shielded balance on wallet screen open The shielded tab balance only recalculated when the user clicked the Shielded tab because ShieldedTabView was lazily initialized on tab selection. This meant the wallet list and tab header showed zero balance until the user manually visited the tab. Two-layer fix: - Backend: call initialize_shielded_wallet() in handle_wallet_unlocked() so persisted shielded balance is loaded into shielded_states at wallet load time, making it available to all UI screens immediately. - UI: eagerly create ShieldedTabView on wallet screen construction and wallet switch, and tick() it every frame so the init/sync chain (InitializeShieldedWallet -> SyncNotes -> CheckNullifiers) runs even when the Shielded tab is not the active tab. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove stale balance_breakdown_expanded field from cherry-pick Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): move action buttons to full-width row below header columns The button bar was inside the left column (55% width), so the right-aligned dev buttons ended up in the middle of the screen. Move render_action_buttons() below the two-column header so it spans the full available width. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(ui): change sync status to bullet-point layout Replace the wide horizontal Platform line (pipe-separated) with individual bullet-point items: Core, Addresses, Notes, Nullifiers each on their own line. Removes the Frame wrapper for a lighter appearance. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(shielded): prevent double balance from redundant init + sync chain When eager init (wallet_lifecycle) populates shielded_states and then ShieldedTabView.tick() dispatches another InitializeShieldedWallet, the idempotency check returns early but handle_result() chains SyncNotes which can re-append notes already loaded from DB. Fix: tick() now checks if shielded_states already contains the wallet's state. If so, it adopts the cached balance and marks itself initialized without dispatching a backend task, preventing the redundant sync chain. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(shielded): move initialization entirely to backend, remove UI init path The shielded wallet had two initialization paths: the backend (handle_wallet_unlocked -> initialize_shielded_wallet) and the UI (ShieldedTabView::tick dispatching InitializeShieldedWallet). This created redundant init attempts and blurred the responsibility boundary. Now: - handle_wallet_unlocked initializes the shielded state synchronously, then queues SyncNotes -> CheckNullifiers as a background task - ShieldedTabView reads its state from AppContext::shielded_states via refresh_from_backend_state(), never dispatching init itself - The "Initialize Shielded Wallet" button and WalletUnlockPopup are removed from ShieldedTabView (wallet unlock already calls handle_wallet_unlocked which handles everything) - Resync Notes (developer mode) still dispatches InitializeShieldedWallet as an explicit user action Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): deduplicate notes in sync to prevent double balance When the commitment tree resets (empty/corrupt) but persisted notes remain in the DB, initialize_shielded_wallet loads notes from DB while last_synced_index falls to 0. The subsequent sync rescans from position 0 and re-appends all decrypted notes — duplicating the ones already in memory from DB, doubling the balance. Fix: sync_notes() now checks if a note at the same position already exists in shielded_state.notes before appending, preventing duplicates regardless of the last_synced_index value. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(ui): add shielded diversified address table Replace the single-address display with prev/next buttons in the Shielded tab with a collapsible address table showing all generated diversified addresses. Each row shows the index, truncated bech32m address (click or Copy button to copy full address), and status (Default for index 0). The section is collapsed by default in normal mode and expanded in developer mode. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address review findings — duplicate controls, perf, tab visibility - Remove duplicate sync status/buttons block in shielded_tab.rs - Fix BIP44 accounts with index > 0 being invisible (regression) - Fix early return in render_account_tabs blocking Shielded tab for new wallets - Fix double-tick of ShieldedTabView per frame when tab is active - Fix system_tab_sections O(n*m) perf by precomputing address counts - Track queue_shielded_sync via subtasks for graceful shutdown - Use HashSet for O(1) duplicate note check in sync (was O(n^2)) - Fix balance_breakdown default_open to respect developer mode - Fix is_system_category to delegate to is_visible_in_default_mode - Fix doc comment for system_tab_sections sort order claim - Narrow #[allow(dead_code)] to specific field on AccountSummary Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): replace mutex unwrap() with graceful error handling in shielded tab Replace .lock().unwrap() calls on shielded_states with .lock().ok() patterns throughout ShieldedTabView to prevent UI panics on poisoned mutex. Shows fallback messages or skips updates gracefully. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(mcp): resolve async lifetime errors for Rust 2024 edition Use spawn_blocking + block_on in dispatch_task to avoid Send bound issues with platform SDK types (DataContract/Sdk references across await points). Same pattern already used by AppState::handle_backend_task. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: fix mcp dispatch * doc: remove obsolete manual teting docs * fix(ui): include zero-balance addresses in AddressInput wallet entries Pull Core addresses from known_addresses (all derived) instead of address_balances (only funded). Balance is looked up from address_balances, defaulting to 0. Callers use with_balance_range(1..) when they need funded-only addresses. Fixes Mine dialog showing empty dropdown for fresh/zero-balance wallets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): sort AddressInput wallet entries alphabetically Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert: remove redundant sort in extract_wallet_entries filtered_entries() already sorts alphabetically by display_label. The sort added in extract_wallet_entries was redundant. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): clarify shielded network validation logic Replace double-negation with explicit mainnet-class comparison. Documents that testnet/devnet/local share the same HRP ("tdash1z") and cannot be distinguished at the address level. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): include send-all transactions in wallet history Check both outputs and inputs when filtering transactions. Build an OutPoint→Address lookup from the wallet's own transactions to resolve input addresses. Send-all transactions (no change output) were previously dropped because no output matched known_addresses. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): use is_ours flag for transaction filtering Simplify transaction filter to use the is_ours flag instead of address matching. Fix SPV path to set is_ours=true for all wallet transactions (upstream only sets it for sends). SPV history is per-wallet, so all entries are ours by definition. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(e2e): verify is_ours flag for SPV send and receive transactions Sends funds between two wallets via SPV and asserts that both the sender (negative net_amount) and receiver (positive net_amount) have is_ours=true. Validates the fix to the SPV reconcile path that overrides the upstream library's send-only is_ours logic. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(core): clear RPC error state on successful chain lock fetch When get_best_chain_lock() succeeds on the active network, explicitly clear any lingering RPC error in ConnectionStatus so the connection indicator recovers after a transient outage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(model): move is_platform_address_string from UI helpers to model layer AddressKind::detect() in the model layer depended on crate::ui::helpers for platform address detection — wrong layering. Move the function to src/model/address.rs and keep a re-export in helpers for existing callers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): add actionable guidance to address validation error messages Each validation error now tells the user what to do, not just what went wrong: "check for typos", "check you are using the correct network", "use all lowercase". Messages that already had guidance are unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): invalidate address inputs on all screens during context switch TransferScreen, AddressBalanceScreen, and ShieldedSendScreen retained stale address text from the previous network after a context switch. Add invalidate_address_input() to each and call it from change_context(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): clear password field on network switch when no config exists When switching networks, if no config entry exists for the new network (or the password is empty), the password input field now clears instead of retaining the previous network's password. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): clear validated_destination in invalidate_address_input WalletSendScreen and UnshieldCreditsScreen cleared the AddressInput widget but left validated_destination with stale validation from the previous network. Now both are cleared together. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): consume ShieldedNotesSynced to update shielded send screen state After a shielded transfer, the post-transfer note sync result was only logged but never used to update the UI. Now ShieldedNotesSynced updates the balance display, clears the pending-update banner, and appends the remaining balance to the success message. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: apply cargo +nightly fmt formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(model): disambiguate Core vs Identity address detection by prefix Core addresses on Dash start with X/Y (mainnet) or y/8/7 (testnet). When the input starts with a known Core prefix, try Core first. Otherwise try Identity first, since Identity IDs use the same Base58 alphabet but don't have Core address prefixes. This fixes identity-only AddressInput mode rejecting valid Identity IDs that happened to parse as Core addresses, and eliminates the flaky test that had to skip Core-looking random identifiers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(core): show actual RPC error on Networks page instead of generic message The Networks page is a debugging tool — users need the real error text to diagnose Dash Core issues. Replace sanitized "RPC error — check Dash Core status" with the actual error from dashcore_rpc. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(db): resolve deadlock in clear_network_data clear_network_data() held self.conn.lock() for the transaction, then called clear_commitment_tree_tables() which tried to acquire the same mutex — classic non-reentrant mutex deadlock. UI froze on "Delete Data". Fix: scope the connection lock so it's released before the commitment tree clearing acquires it again. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): replace mutex unwrap() with graceful .ok() in shielded_sync_task The shielded_states mutex lock used .unwrap() which would panic on a poisoned mutex. Replace with .ok()? to return None gracefully. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): replace RwLock unwrap() with graceful error handling in shielded tab Replace wallets.read().unwrap() with .read().ok() and show an error label instead of panicking on a poisoned RwLock. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(spv): add debug log when overriding is_ours for receive transactions Log at debug level when SPV reports is_ours=false for a receive transaction (net_amount >= 0) to detect upstream API behavior changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): log warning on note value divergence during position dedup Use a HashMap<position, value> instead of HashSet<position> so we can detect when a re-synced note at an existing position has a different value — indicating potential data integrity issues. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * perf(model): avoid allocation in is_platform_address_string Replace to_lowercase() with eq_ignore_ascii_case() for the HRP prefix check. The HRP constants are ASCII-only so this is safe and avoids a heap allocation on every call. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(ui): remove unused label field and is_key_only from AccountSummary The label field was constructed but never read outside the builder. is_key_only() had no callers. Remove both instead of suppressing dead_code warnings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(ui): avoid cloning full AccountTab enum in tab content match Extract only the category data (clone of AccountCategory + index) before the match instead of cloning the entire enum. This avoids unnecessary allocation for the Shielded and System variants. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): clear validated_address on network switch in mine dialog invalidate_address_inputs() cleared the AddressInput widget but left the validated_address stale, which could reference an address from the previous network. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(model): move truncate_address to model layer and document ASCII precondition Move truncate_address from address_input.rs and shielded_tab.rs into src/model/address.rs as a parameterized public function. Both callers now delegate to the shared implementation with their own prefix/suffix lengths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(shielded): document why spawn_blocking trampoline is needed in queue_shielded_sync The spawn_blocking(block_on(...)) pattern is required because async methods on Arc<Self> produce non-'static futures due to a known Rust compiler limitation (rust-lang/rust#100013). Document this to prevent future removal attempts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: apply nightly rustfmt formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(wallet): bootstrap platform addresses on wallet creation bootstrap_wallet_addresses only ran when known_addresses was empty, but new_from_seed already derives one Core address. This meant platform payment addresses were never bootstrapped for new wallets — only populated later by network sync (which returns nothing for fresh wallets with no on-chain activity). Now checks for the presence of PlatformPayment addresses in watched_addresses and runs bootstrap if missing. Bootstrap functions are idempotent (add_address_if_not_exists). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): show bootstrapped platform addresses in AddressInput Platform addresses were only populated from platform_address_info (network sync results). Fresh wallets with no on-chain activity had empty platform_address_info, so no platform addresses appeared in the Send screen autocomplete. Now derives platform addresses from watched_addresses (which contains all bootstrapped platform payment addresses), with balance looked up from platform_address_info (defaulting to 0). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(ui): pass account filter directly to render_address_table Remove the `selected_account` field that was mutated as a side-effect during rendering, causing the last-rendered collapsible section in the System tab to overwrite previous sections' state. Now each render call receives its `(AccountCategory, Option<u32>)` as a direct parameter. Additionally, `system_tab_sections` now builds per-(category, index) pair so multi-index accounts show correct per-index balance and address counts instead of aggregating across all indices. Fixes CODE-005, CODE-011, PROJ-004. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * perf(shielded): move wallet initialization to background thread ZIP32 key derivation and DB reads in `initialize_shielded_wallet` ran synchronously on the UI thread during `handle_wallet_unlocked()`. Move the call to a `spawn_blocking` task tracked by `subtasks` so the UI remains responsive. The shielded tab already handles the "initializing" state gracefully via its `is_initialized` check. Fixes CODE-010. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(spv): add unit tests for is_ours override logic Extract the SPV is_ours override into a named function and add four unit tests covering: outgoing already-ours, incoming not-ours (the main override case), zero-amount edge case, and outgoing not-ours. A comment explains why bloom filter false positive testing requires mocking the SPV layer and is out of scope for unit tests. Fixes PROJ-002. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(user-stories): add stories for wallet tab redesign Add WAL-021 through WAL-024 covering the tab-based navigation, developer-mode System tab, collapsible transaction history, and collapsible balance breakdown sections introduced in PR #791. Fixes PROJ-007. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: apply nightly rustfmt formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): distinguish change addresses in AddressInput autocomplete Core addresses now show "(change)" suffix for BIP44 change addresses (m/44'/5'/0'/1/x). New with_exclude_change(true) builder method filters them out — send inputs should not display change addresses since users don't share them with others. Change detection uses the existing DerivationPathHelpers::is_bip44_change trait method from the wallet model. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): exclude change addresses from Send screen destination input Change addresses are internal wallet addresses not meant to be shared. The Send screen's destination AddressInput now uses with_exclude_change(true) to filter them out of the autocomplete. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): add new address generation button to wallet tabs Add right-aligned compact buttons below the address table for generating new addresses on Core (BIP44) and Platform tabs. Core addresses use the async BackendTask path for SPV compatibility; Platform addresses derive synchronously. The button is disabled during generation to prevent double-clicks. Shielded tab already has its own button; System tab has no button (addresses are derived automatically). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): exclude system addresses from AddressInput autocomplete System addresses (Identity Registration, CoinJoin, Provider keys, etc.) are internal wallet infrastructure not meant for user-facing send/receive operations. Filter them out by checking each address against watched_addresses path_reference → AccountCategory. Only BIP44 (Core) and PlatformPayment addresses are shown. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): always show address type label, enable type-based filtering Address type suffix (Core/Platform/Shielded/Identity) is now always shown in autocomplete entries, not just when multiple types are enabled. Users can type "platform", "core", "plat", etc. to filter entries by type — the filter matches against both short_label() and display_name(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(error): add user-friendly message for shielded nonce mismatch Detect AddressInvalidNonceError from Platform and show an actionable message: "The transaction used an outdated sequence number. Please retry — the wallet will use the correct number automatically." Previously this fell through to the generic ShieldedBroadcastFailed message which gave no guidance. Upstream fix filed: dashpay/platform#3407 (SDK auto-retry on nonce mismatch). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: fix stable rustfmt formatting for address_input filter Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): compare WalletSeedHash in shielded ScreenType equality Wildcard matches caused all shielded screens to be considered equal regardless of which wallet they belonged to, breaking multi-wallet navigation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): reset all transient state in ShieldedTabView::update_seed_hash Switching wallets left stale initializing/syncing flags, pending tasks, and address count from the previous wallet, causing UI glitches. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(error): replace string-based SPV payment errors with typed TaskError variants Add WalletInfoUnavailable, MissingBip44Account, and ChangeAddressDerivation variants to TaskError, replacing ad-hoc WalletPaymentFailed { detail: String } usage for these three recurring error patterns in SPV payment code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(perf): skip Halo2 proving key warmup in test builds The ~30s background thread for proving key generation is unnecessary during tests and slows down the test suite. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): delegate is_platform_address and rewrite hints as full sentences is_platform_address now delegates to is_platform_address_string for consistent case-insensitive HRP matching. Address hint constants are rewritten as complete, i18n-extractable sentences. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): sanitize raw RPC error strings in connection status display Strip OS-level error details like "(os error 111)" and transport error wrappers before storing in ConnectionStatus, so users see clean messages instead of raw system error text. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(error): rewrite SPV error messages for everyday users - WalletInfoUnavailable: "wallet is still loading" instead of technical jargon - MissingBip44Account: "needs to be refreshed" instead of "BIP44 account" - ChangeAddressDerivation: "could not prepare transaction" instead of "derive change address" - sanitize_rpc_error: fix doc/impl mismatch, strip "RPC error:" prefix, remove redundant "RPC: " output prefix Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Quantum Explorer <quantum@dash.org> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: lklimek <lklimek@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
…798) * more work * more work * fix(spv): zero out stale per-address balances during reconciliation (#627) During SPV reconciliation, per_address_sum only contains addresses with current UTXOs. Addresses whose funds were fully spent never had their balance reset to zero, causing the address table to display stale non-zero balances even though UTXO count correctly showed 0. Now explicitly zeroes address_balances for any known address absent from the refreshed UTXO map before applying current sums. Closes #571 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml (#613) * fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml Replace .expect() with match expression to avoid panicking when .testnet_nodes.yml contains malformed YAML. Instead, logs the error with tracing::error and returns None, allowing the application to continue without crashing. Closes #557 Co-authored-by: lklimek <lklimek@users.noreply.github.com> * fix: propagate YAML parse errors to UI and remove unwrap calls - Change load_testnet_nodes_from_yml to return Result<Option<TestnetNodes>, String> so parse errors display in the UI error banner instead of only logging - Use explicit type annotation on serde_yaml_ng::from_str::<TestnetNodes> - Replace .unwrap() with .and_then() in fill_random_hpmn/fill_random_masternode Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: lklimek <lklimek@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * chore: let Claude write manual test scenarios for PRs (#634) * chore: move doc/ contents into docs/ and update references Consolidate documentation under a single docs/ directory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: CLAUDE.md should write manual test scenarios for PRs --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * build(flatpak): use only-arches for dynamic protoc architecture selection (#603) * build(flatpak): use only-arches for dynamic protoc architecture selection Replace the fragile sed-based CI patching of the Flatpak manifest with Flatpak's native `only-arches` source selector. The protoc module now declares both x86_64 and aarch64 sources inline, and build-commands use a glob pattern (`protoc-*.zip`) so no per-arch fixup is needed. Changes: - flatpak manifest: add aarch64 protoc source with `only-arches`, use glob in unzip commands, remove stale CI-patching comment - CI workflow: remove `protoc-zip`/`protoc-sha256` matrix variables and the "Patch manifest for architecture" step https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG * fix(flatpak): use dest-filename for deterministic protoc extraction Use dest-filename to normalize both arch-specific protoc zips to a common name, avoiding glob expansion in build-commands. This ensures the unzip target is deterministic regardless of shell behavior in the Flatpak sandbox. https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG * build: update platform to b445b6f0 and remove rust-dashcore patches Update dashpay/platform dependency from d6f4eb9a to b445b6f0e0bd4863 (3.0.1 → 3.1.0-dev.1). Remove the [patch] section that pinned rust-dashcore crates to a separate rev, as the new platform commit resolves them correctly on its own. https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG --------- Co-authored-by: Claude <noreply@anthropic.com> * fix(ci): remove local path patches, use git deps for platform crates The [patch] section referenced ../platform local paths that don't exist in CI. Since dash-sdk already depends on the feat/zk branch, all transitive platform crates resolve correctly without patches. Also fixes a formatting issue in address_table.rs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): store wallet in DB before registering addresses The remove_utxos tests were failing because `register_test_address` inserts into `wallet_addresses` which has a foreign key constraint on `wallet(seed_hash)`. Added the missing `store_wallet` call so the parent row exists before inserting child address rows. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(build): restore shielded module declaration removed during merge The `pub mod shielded;` declaration was accidentally removed from `src/model/wallet/mod.rs` during the v1.0-dev merge, causing build failures since the shielded.rs file still exists. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): restore store_wallet calls lost in merge (#663) * Initial plan * fix(test): restore store_wallet calls lost in merge Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com> * Merge remote-tracking branch 'origin/v1.0-dev' into zk-extract/all-merged Resolve conflicts in Cargo.toml (keep feat/zk branch), Cargo.lock (regenerate with pinned platform rev 4d7b9be5), and backend_task/mod.rs (combine TaskError wrapping with ShieldedTask). Fix post-merge integration issues: - SPV manager: remove stale .await on subscribe methods, add command_receiver channel for updated DashSpvClient::run() API - send_screen: update SendStatus::WaitingForResult to unit variant - network_chooser_screen: handle new SyncState::Initializing variant Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: update platform dependency to 3.1-dev branch Migrate from feat/zk to 3.1-dev branch of dashpay/platform, adapting to breaking API changes in dash-spv, key-wallet, and dpp: - FeeLevel removed; use FeeRate::normal() directly - DashSpvClientInterface/Command removed; use DashSpvClient directly - SyncState::Initializing removed; replaced with WaitForEvents - NetworkExt trait inlined into Network impl - OrchardProver now requires wrapper struct around ProvingKey - OrchardAddress::from_raw_bytes now returns Result - Builder functions gain fee/platform_version params - NullifierSyncConfig API uses NullifierSyncCheckpoint - WalletManager.create_unsigned_payment_transaction removed; use TransactionBuilder directly - Work around Send lifetime issues with spawn_blocking Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: migrate shielded module from Result<T, String> to typed TaskError Replace all Result<T, String> error patterns in the shielded pool module with typed TaskError variants, aligning with the codebase-wide typed error migration (PR #739). New TaskError variants: ShieldedNoUnspentNotes, ShieldedInsufficientBalance, PlatformAddressNotFound, ShieldedMerkleWitnessUnavailable, ShieldedTransitionBuildFailed, ShieldedBroadcastFailed, ShieldedInvalidRecipientAddress, ShieldedAssetLockTimeout, ShieldedSyncFailed, ShieldedTreeUpdateFailed, ShieldedNullifierSyncFailed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): prevent settings password row from clipping right edge Reserve width for Save and Auto Update buttons so the password input doesn't consume all available space, pushing buttons off-screen. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(rpc): include host:port in connection-refused errors and always show details Add CoreRpcConnectionFailed variant to TaskError that includes the configured address in the user-facing message. Connection-refused errors are now detected via is_rpc_connection_error() and enriched with host:port at every RPC call site where the URL is known (AppContext::rpc_error_with_url helper). Details panel is now shown for all RPC-related errors regardless of developer mode, so users can always see the technical information they need to diagnose connectivity issues. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): save RPC password for active network instead of hardcoded Regtest The Network Chooser screen had three bugs related to RPC password handling: 1. Password input was initialized from Regtest config only, ignoring the current network selection. 2. Password UI was hidden for all networks except Regtest, even though Mainnet/Testnet/Devnet also use RPC mode. 3. Save logic was hardcoded to update Regtest config and triggered a SwitchNetwork(Regtest) action, which disconnected the active network's ZMQ listener unnecessarily. Now the password input shows for any network in RPC mode, reads/writes the correct network config, reinits the RPC client in-place without triggering a network switch, and reloads the stored password when the user switches network tabs. The "Auto Update" (dashmate) button remains Regtest-only since dashmate is only relevant for local networks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): ensure funding method dropdown fits all items without scrollbar Add explicit .height(200.0) to the funding method ComboBox in both top_up_identity_screen and add_new_identity_screen. The add_enabled_ui wrappers inflate item height via frame overhead, causing the popup to clip to a single row at the default height. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(error): show actionable message for insufficient identity balance IdentityInsufficientBalanceError from the SDK now maps to a dedicated TaskError::IdentityInsufficientBalance variant instead of falling through to "An unexpected error occurred." The user sees the available and required credit amounts along with a clear "top up your identity" action. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): clear stale error banners when saving RPC password Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use network-compatible comparison for platform address lookups Regtest and Testnet share the `tdash` bech32m prefix, but `PlatformAddress::from_bech32m_string()` always returns `Network::Testnet` for `tdash` addresses. The strict `!=` comparison in the fund-platform dialog rejected valid Regtest addresses with "Address network mismatch". - Make `networks_address_compatible()` `pub(crate)` in `model::wallet` so all modules can reuse the canonical check - Remove the duplicate private copy in `backend_task::core` and import from the single source - Replace `network != self.app_context.network` in `dialogs.rs` with `networks_address_compatible()` so Testnet/Devnet/Regtest addresses are accepted interchangeably Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): add actionable messages for shielded fee and pool-size errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): add actionable message for shielded anchor mismatch Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): auto-resync notes and retry on anchor mismatch When unshield_credits, shielded_transfer, or shielded_withdrawal fails with ShieldedAnchorMismatch (stale commitment tree witnesses), automatically sync notes once to update the tree and retry the operation. Only one resync attempt is made — if the retry also fails, the error propagates as-is. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): ensure shielded tables exist and log DB errors during init Three changes to fix empty shielded balance after app restart: 1. Defensive table creation in initialize(): after migrations complete, ensure shielded_notes and shielded_wallet_meta tables exist even if the DB version was already past v29/v30 from a prior build. Both methods use CREATE TABLE IF NOT EXISTS, so this is safe. 2. Log DB errors during shielded init: change silent if-let-Ok pattern to match/Err with tracing::warn, so missing-table errors are visible instead of silently producing empty note lists. 3. Safety net resync: when the commitment tree has been synced but no unspent notes were loaded from DB, clear the tree and reset last_synced_index to 0 so the auto-sync rediscovers all notes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): consolidate migrations v28-v32 into v33 Resolves version numbering collision between zk and v1.0-dev branches: the zk branch used v28 for shielded tables while v1.0-dev used v28 for contacts. After merging, users migrating from either branch could end up with missing tables depending on which version their DB was at. v33 runs all sub-migrations idempotently in one step, ensuring all tables exist regardless of prior migration history. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): consolidate migrations v28-v32 into v33 Resolves version numbering collision between zk and v1.0-dev branches: the zk branch used v28 for shielded tables while v1.0-dev used v28 for contacts. After merging, users migrating from either branch could end up with missing tables depending on which version their DB was at. v33 runs all sub-migrations idempotently in one step, ensuring all tables exist regardless of prior migration history. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: pin platform dependency to zk-fixes revision Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): address PR review — fresh schema, error propagation, rename coverage - propagate SQL error in add_nullifier_sync_timestamp_column instead of swallowing it with unwrap_or(false) - add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already present on this branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): address PR review — fresh schema, error propagation, rename coverage - propagate SQL error in add_nullifier_sync_timestamp_column instead of swallowing it with unwrap_or(false) - add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already present on this branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): add foreign key constraints to shielded tables shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by all other per-wallet tables in the codebase. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): add foreign key constraints to shielded tables shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by all other per-wallet tables in the codebase. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): remove duplicate shielded methods after v1.0-dev merge On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev backport inlined them in initialization.rs (no shielded.rs on that branch). Merging v1.0-dev back caused E0592 duplicate definitions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): remove duplicate wallet store in register_test_address register_test_address called db.store_wallet redundantly — callers already store the wallet before calling it, causing UNIQUE constraint violations when tests run in parallel on CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(shielded): address PR review — error propagation, retry helper, safety net - Propagate DB error from get_unspent_shielded_notes instead of swallowing - Extract with_anchor_retry() helper to deduplicate ~27 lines across shielded_transfer_task, unshield_credits_task, shielded_withdrawal_task - Fix safety net false positive: check all notes (spent + unspent) to distinguish "never had notes" from "all spent" - Propagate error from clear_commitment_tree_tables instead of discarding Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): address PR review — Amount formatting, Display completeness - Add Amount::dash_from_credits() constructor to model/amount.rs - Replace manual format_credits_as_dash() logic with Amount::dash_from_credits().to_string(), so credit amounts use Amount's Display (with DASH unit, trimmed trailing zeros) - Update ShieldedFeeExceedsBalance #[error] message: remove redundant "Dash" literal (Amount's Display already includes the "DASH" unit suffix) - Update format_credits_as_dash tests to match new output format ("1 DASH", "2.5 DASH", etc.) - Remove special-casing that showed with_details() for all RPC errors unconditionally: all required user-facing info is already in each variant's Display string; technical details are now shown only in developer mode (aligns with CLAUDE.md §error-messages rule 4) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(rpc): address PR review — context fallback, success banner, error source - Guard in-memory config update + reinit behind a check that the target network's AppContext actually exists; skip both when it doesn't so we don't accidentally overwrite mainnet config via the fallback path. - Show success banner only when reinit succeeds; show a warning when it fails so the user knows the connection may not reflect the new password. - Make CoreRpcConnectionFailed.source Option<dashcore_rpc::Error> so chain_lock_rpc_error can pass None instead of fabricating a fake ConnectionRefused error from a borrowed reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: simplify shielded helpers comment in initialization.rs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): address PR #789 review — doc comments, migration default - Split misplaced doc comment: `add_wallet_transaction_status_column` now has its own Migration 30 doc; `rename_network_dash_to_mainnet` gets its own Migration 29 doc. - Add inline comment explaining why the migration uses DEFAULT 2 (Confirmed) while fresh CREATE TABLE in wallet.rs uses DEFAULT 0. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): remove duplicate shielded methods after v1.0-dev merge On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev backport inlined them in initialization.rs (no shielded.rs on that branch). Merging v1.0-dev back caused E0592 duplicate definitions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: simplify shielded helpers comment in initialization.rs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(db): document migration DEFAULT 2 vs fresh DEFAULT 0 Existing transactions predate status tracking and are assumed confirmed (DEFAULT 2). Fresh installs use DEFAULT 0 (Unconfirmed). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(wallet): address review — zero-balance detection, status warning Add `spv_balance_known: bool` to `Wallet` to distinguish a synced zero-balance wallet from an unsynced one. `spv_confirmed_balance()` now returns `None` only before the first SPV report, and `Some(0)` after SPV confirms an empty wallet. Add `tracing::warn!` in `TransactionStatus::from_u8` when an unknown discriminant is encountered, avoiding silent data coercion in a financial context. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): address review — typed BIP32 source, migration error variant Replace WalletKeyDerivationFailed { detail: String } with a typed #[source] field (Box<dyn Error + Send + Sync>) so the error chain is preserved and Display/Debug separation is explicit. Update all callsites in wallet/mod.rs and identity/mod.rs accordingly. Replace the semantically wrong InvalidParameterName map_err in rename_network_dash_to_mainnet with a plain ? — execute() already returns rusqlite::Result so no conversion is needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test(db): add v33 consolidated migration regression tests Two test scenarios verify the v33 consolidated migration that merges sub-migrations v28-v32 (core_wallet_name, shielded tables, contacts, wallet transaction status, network rename): 1. Fresh install creates all v33 tables/columns directly via create_tables() 2. Upgrade from v27 runs the full migration path and produces identical schema Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): show warning when config save fails instead of success When config.save() fails, stop early with a warning banner instead of continuing to reinit and showing a misleading success message. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(error): consolidate format_credits_as_dash and remove jargon from user messages - fee_estimation::format_credits_as_dash now delegates to Amount::dash_from_credits() instead of doing its own f64 math with fixed 8 decimals; output is now trimmed (e.g. "1 DASH" instead of "1.00000000 DASH") - Remove private format_credits_as_dash from error.rs; import the pub version from fee_estimation instead - IdentityInsufficientBalance: show amounts in DASH rather than raw credit integers - ShieldedAnchorMismatch: replace "out of sync" with plain-language retry guidance - ShieldedFeeExceedsBalance: remove "shielded balance" / "shield more credits" jargon - ShieldedInsufficientPoolNotes: remove count interpolations and ZK pool jargon - Update tests throughout to match new message content and format Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(test): remove duplicate wallet store in register_test_address register_test_address called db.store_wallet redundantly — callers already store the wallet before calling it, causing UNIQUE constraint violations when tests run in parallel on CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(core): log chain lock RPC errors that aren't auth/connection failures Silent swallowing of errors like "Unable to find any ChainLock" made it impossible to diagnose why the active network showed as Disconnected. Now warns via tracing when chain_lock_rpc_error returns None. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ui): surface chain lock RPC errors in Networks tab Previously, non-auth/non-connection RPC errors (e.g. "Unable to find any ChainLock") were silently swallowed — the UI showed "Disconnected" with no explanation. Now the error message is carried through the ChainLocks result, stored in ConnectionStatus.rpc_last_error, and displayed as "Error: ..." in both developer and non-developer RPC status labels on the Networks tab. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(ui): unified AddressInput component with autocomplete (#787) * feat(ui): add unified AddressInput component with autocomplete Introduce a reusable AddressInput component that handles text input with real-time address type detection, autocomplete from wallet data, balance display, type filtering, and network-aware validation. Supports Core, Platform, Shielded, and Identity address kinds. New files: - src/model/address.rs: AddressKind enum and ValidatedAddress enum - src/ui/components/address_input.rs: full component with 33 unit tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): integrate AddressInput component into send and unshield screens Replace inline address parsing and validation with the unified AddressInput component across 3 proof-of-concept sites: - UnshieldCreditsScreen: full migration, removes local Destination enum and parse_destination(), gains autocomplete and type-restricted input - WalletSendScreen simple mode: full migration, removes AddressType enum, replaces detect_address_type/is_shielded_address with AddressKind-based detection, eliminates double-parsing in send handlers - WalletSendScreen advanced mode: minimal migration, updates type detection to use AddressKind, keeps CoreAddressInput/PlatformAddressInput structs unchanged Net reduction of ~47 lines per screen. All send flows preserved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(address-input): fix misleading test and add missing coverage The existing `disabled_type_rejected_with_correct_error` test was testing empty input (which returns no error) rather than actually verifying type restriction. Fixed the test and added coverage for: - Selection-only mode rejection of manual input - Identity validation with valid/invalid identifiers - Truncate boundary at exactly 16/17 characters - Empty input in selection-only and restricted-type modes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): address QA findings for AddressInput component QA-001: Extract duplicated address detection logic from send_screen.rs into AddressKind::detect() on the model type. Both send_screen and address_input now delegate to the single canonical implementation. QA-002: Fix autocomplete "...and N more" count using unfiltered total. filtered_entries() now returns the pre-truncation match count so the overflow label shows the correct number of remaining matches. QA-003: Add minimum length check (>= 60 chars) to shielded address validation. Previously any string with the correct prefix was accepted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): address bot review findings for AddressInput component - Validate shielded addresses via OrchardAddress::from_bech32m_string() instead of prefix+length check only - Use char-aware truncation in truncate_address() to prevent panics on multi-byte UTF-8 input (DPNS labels, emoji) - Reset AddressInput when source selection changes in send screen, and configure allowed destination kinds per source type - Store bech32m string in ValidatedAddress::Platform variant so to_address_string() returns canonical encoding instead of debug hex Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): address remaining review findings for AddressInput component - Fix manual entry not propagating validated address on blur (HIGH) - Fix selected_from_autocomplete causing repeated change signals - Remove protocol jargon from unshield screen messages - Reject mixed-case bech32m platform addresses per BIP-350 - Use per-instance ComboBox ID to prevent state collision - Clear cached_detection after autocomplete selection - Fix doc comments and reuse filtered_entries() computation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): support multi-wallet autocomplete in AddressInput Replace with_wallet/set_wallet (single wallet) with with_wallets/set_wallets (slice of wallets). When multiple wallets are loaded, each autocomplete entry is prefixed with the wallet alias so the user can tell which wallet owns the address. send_screen.rs now passes all loaded wallets to AddressInput instead of only the selected one. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ui): restore missing "Show zero-balance addresses" checkbox The checkbox was accidentally dropped during a branch merge. Restored the horizontal layout with heading on the left and checkbox right-aligned, matching the v1.0-dev implementation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): improve AddressInput autocomplete UX - Fix click selection: keep popup rendered when text field loses focus so the click handler fires (was gated on has_focus only) - Show dropdown on focus with any input length (removed 3-char minimum) - Add type suffix (Core), (Platform), (Identity), (Shielded) to dropdown entries when multiple address types are enabled - Validate immediately on paste (text >3 chars) instead of requiring blur first, so "Fund Platform Address" button activates without needing to click away Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): make entire autocomplete row clickable in AddressInput Previously only the address label was clickable — clicking the balance text on the right side of the row did nothing. Now the click handler uses the horizontal row response, so the entire row triggers selection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): fix autocomplete click handling and format balances with 4dp - Capture selectable_label click response instead of discarding it; combine with row-level click for full-row clickability - Format all DASH balances in dropdown with exactly 4 decimal places Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): use single interaction rect for full-row clickable autocomplete Replace selectable_label + horizontal layout with allocate_exact_size and manual painting — no child widgets steal clicks, the entire row (address, balance, dead space) is clickable with hover feedback. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(ui): add wallet address autocomplete to unshield screen Populate AddressInput with wallet addresses via .with_wallets() so users can select a destination from the dropdown instead of manually entering addresses. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(error): handle asset lock and shielded insufficient funds errors with user-friendly messages Add two new TaskError variants to replace raw SDK errors with actionable, jargon-free messages: - AssetLockOutPointInsufficientBalance: shown when an asset lock outpoint has been partially consumed and lacks credits for the operation. Extracts credits_left/credits_required and displays DASH amounts. - ShieldedAddressInsufficientFunds: shown when a shielded broadcast fails because the address balance is too low. Detected in shielded_broadcast_error() before falling through to ShieldedBroadcastFailed. Both follow the IdentityInsufficientBalance pattern with format_credits_as_dash. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): use AmountInput component for all amount inputs in wallet screens Replace raw text_edit_singleline + custom parse_amount_*() methods with the AmountInput component in 4 shielded screens (unshield, shield, shield-from-asset-lock, shielded-send). This fixes a bug where entering "1" would send 1 credit instead of 1 DASH because the raw input had ambiguous parsing. Also adds "Amount (DASH):" label to SendScreen's AmountInput and removes its redundant manual label. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add UI components reference and teach CLAUDE.md to use it Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): redesign wallet screen information architecture Replace the old Balances|Shielded top-level tabs and account dropdown with a unified layout that better matches how users think about their wallet: - Balance section with collapsible breakdown (Core/Platform split) - Dev Tools expandable button replaces scattered dev-mode controls - Transaction History shown as collapsible section (visible in all modes) - Accounts & Addresses use tabs instead of a dropdown selector - Shielded view moved from top-level tab to an account tab - Asset Locks restricted to the Dash Core tab only - "Main Account" renamed to "Dash Core" throughout - Fee column added to transaction table (dev mode only) - "Get Test Dash" button opens testnet faucet in dev tools Tab visibility follows progressive disclosure: default mode shows only Dash Core, Platform, and Shielded tabs. Developer mode reveals all account types that have addresses. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): use AddressInput component in Mine dialog for core address selection Replace the manual ComboBox address selector in the Mine Blocks dialog with the unified AddressInput component, configured for Core-only addresses with selection-only mode from the current wallet. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): trigger shielded balance refresh after all shielding operations After Shield, Shield from Core, Send Private, Unshield, and Send Dash (with shielded source) complete successfully, automatically dispatch a SyncNotes task so the shielded tab shows updated balances without manual refresh. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: demote cookie auth fallback log to trace level Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): prevent transaction list showing wrong wallet data Add defensive checks in render_transactions_section to prevent cross-wallet transaction leakage: 1. Verify the selected wallet Arc matches the canonical one in app_context.wallets (guards against stale references). 2. Filter displayed transactions to only those with at least one output matching the wallet's known addresses (prevents showing transactions from other wallets that leaked in via a non-wallet-scoped RPC endpoint). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): auto-show zero-balance addresses when wallet is empty When all addresses have zero balance and there are fewer than 5 addresses total, bypass the zero-balance filter so new/empty wallets show their addresses instead of a blank list. The checkbox remains functional for wallets with balances. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): address PR review — shielded balance, dev tools layout 1. Include shielded balance in the collapsible balance breakdown 2. Add shielded balance to the total wallet balance everywhere 3. Right-align the Dev Tools button in the action row 4. Always show downward arrow on Dev Tools button 5. Dev Tools opens as a vertical dropdown popup instead of a horizontal inline row Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): rename transaction heading, add explorer link, merge zk-fixes - Rename "Transaction History" to "Dash Core Transaction History" - Add "View" button on transactions for Mainnet/Testnet (opens Insight explorer) - Merge zk-fixes (mine dialog, shielded refresh, wallet bug fixes, log level) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): trigger shielded sync on wallet refresh and wallet switch The wallet Refresh button now dispatches a SyncNotes task alongside the core wallet refresh when the shielded wallet has been initialized. Switching HD wallets also triggers a full refresh (core + shielded) on the next frame for unlocked wallets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(log): add diagnostic logging for shielded transfer operations Add tracing::info! and tracing::debug! logs throughout the shielded transfer pipeline to enable post-mortem diagnosis when balances don't update after broadcast. Key additions: - bundle.rs: log amount, input notes, change before building each shielded operation (transfer, shield, unshield, withdrawal, shield-from-asset-lock); log broadcast success with note that balance updates after next block - sync.rs: warn when next_start_index is 0 (full rescan); log post-sync balance with unspent note count - context/shielded.rs: log note spend marking with before/after unspent counts in with_anchor_retry - shielded_send_screen.rs: log task result variant received and post-transfer sync completion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): improve Dev Tools dropdown layout and refresh mode cycling Right-align popup content to match the Dev Tools button position. Replace ComboBox with a cycle-on-click button to avoid nested popup conflicts in egui, where the inner ComboBox dropdown would close the parent popup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): fix shielded balance conversion, add tab balances, reorder sync status - Fix shielded balance inflating total: divide credits by CREDITS_PER_DUFF before summing with duffs-denominated balances (was showing 5000 DASH instead of 5 DASH) - Show balances in account tab labels: Dash Core (0.9980) | Platform (0.1000) | Shielded (5.0000), with (empty) for zero-balance tabs - Move Sync Status section inside detail panel between balance breakdown and action buttons for better visual flow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): collapsible sections, move tx history to Dash Core tab, restore account types - Wrap Addresses, Transaction History, Asset Locks, and Shielded Notes in collapsible headers (default open, tx history default closed) - Move Dash Core Transaction History from top-level into the Dash Core tab, between Addresses and Asset Locks - In developer mode, show ALL account category tabs (Bip44, Platform, Bip32, CoinJoin, Identity Registration/System/Top-up/Invitation, Provider) even when no addresses exist for that type - Add TODO for shielded tab layout redesign Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): improve shielded transfer UX messaging for balance update delays After a shielded transfer, the sender's change notes and the recipient's new notes won't appear until the next block is mined and synced. Users see their balance temporarily drop to 0 and think the transfer failed. Add informational messages on all three shielded transfer screens (ShieldedSendScreen, UnshieldCreditsScreen, WalletSendScreen) explaining that balances will update after the next block is confirmed. For shielded-to-shielded transfers, also note that the recipient needs to sync after the next block. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): consolidate dev-mode accounts into System tab, limit balance decimals Replace all individual dev-mode-only account type tabs (Identity Registration, Identity System, Identity Top-up, Identity Invitation, CoinJoin, Provider, Legacy BIP32) with a single "System" tab. Inside the System tab, each account type appears as a collapsible section (collapsed by default) showing address count and balance. Tab order: Dash Core | Platform | Shielded | System (dev-mode only, always last) Additional changes: - Limit tab balance display to max 4 decimal places (was 8) - Rename "Dev Tools" button to "Tools" - Simplify refresh mode button: single button with "Refresh mode: X" text, no separate label or arrow indicator - Remove "Accounts & Addresses" section heading - Style active tab with underline for visual distinction Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): scope commitment tree per wallet for multi-wallet correctness Each wallet's ClientPersistentCommitmentTree now uses its own dedicated SQLite file under <data_dir>/shielded/<hex>.db instead of sharing the main database's global commitment_tree_* tables. This prevents wallets from stepping on each other's Merkle tree state, which caused invalid witnesses and silently rejected transactions. Changes: - SHLD-001: Per-wallet commitment tree DB files via ClientPersistentCommitmentTree::open() - SHLD-002: clear_commitment_tree_for_wallet() scoped to a single wallet - SHLD-007: Safety-net resync guard now logs wallet seed hash before clearing - Migration v34: drops orphaned global commitment_tree_* tables - clear_network_data() deletes per-wallet tree files for the target network Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): prevent permanent state leak on sync_notes failure in with_anchor_retry When sync_notes() failed after an anchor mismatch, the early `?` return skipped re-inserting the shielded state back into the HashMap, permanently orphaning it until app restart. Now the sync result is captured without early return, ensuring the state is always re-inserted regardless of success or failure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): apply password change for current session even when config save fails The in-memory config update and SDK reinit only ran on the save-success path, silently discarding the password change on save failure despite the banner promising session-level application. Now the in-memory update and reinit run unconditionally; only the banner text varies across the four (save x reinit) outcome combinations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(error): use dedicated TaskError variants for non-build wallet errors reload_utxos() and recalculate_affected_address_balances() errors were routed through shielded_build_error() which pattern-matches for build- specific patterns like "AnchorMismatch". These are wallet operations, not shielded builds. Added WalletUtxoReloadFailed and WalletBalanceRecalculationFailed variants with appropriate user-facing messages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(error): preserve error chain in CoreRpcConnectionFailed chain_lock_rpc_error() took the RPC error by reference and created CoreRpcConnectionFailed with source: None, dropping all diagnostic info. Added a detail: Option<String> field to the variant to carry the formatted error. Boxed the source field to keep the enum size under the clippy threshold. Updated all constructors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): two-column wallet header layout, rename Tools to Advanced Split the wallet detail panel header into left (name, total balance, action buttons) and right (collapsible balance breakdown, sync status) columns. Renamed the "Tools" dropdown to "Advanced" and moved it into the action buttons row instead of right-aligning it separately. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(test): rename test_v33_migration_fresh_install to match DB version 34 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert(shielded): use shared main DB for commitment tree instead of per-wallet files Reverts the per-wallet SQLite file approach (63ce0e18). Multiple wallets share the same commitment_tree_* tables in the main database. This is accepted behavior until the SDK adds proper wallet-scoping support (dashpay/grovedb#653). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): fix stale clear_commitment_tree_for_wallet reference after merge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): harden anchor retry, commitment tree clearing, and state guard SEC-002: Verify shielded balance after anchor retry sync before retrying the operation — prevents doomed retries with zero balance. SEC-003: Handle missing commitment tree tables in clear_commitment_tree_tables (grovedb creates them lazily, so DELETEs fail on fresh installs). Fix clear_network_data to log-and-continue when commitment tree clearing fails — these tables are optional and shouldn't block network data reset. Add SEC-001 INTENTIONAL annotation for panic safety in with_anchor_retry. Document shield_from_asset_lock_task: anchor retry is not applicable since it only reads the payment address and doesn't use the commitment tree. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(core): sanitize RPC errors and simplify CoreRpcConnectionFailed SEC-006: Store user-friendly message instead of raw RPC error string in network status when chain lock query fails with a non-auth, non-connection error. CODE-002: Remove redundant detail field from CoreRpcConnectionFailed -- format diagnostic info into the url field instead, keeping source for the error chain. Add SEC-005 INTENTIONAL annotation for RPC errors in status tooltip. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): config permissions, address detection, and per-frame caching SEC-007: Set config file permissions to 0600 on Unix after save (contains RPC credentials). CODE-003: Deduplicate Amount::dash_from_duffs by delegating to dash_from_credits. CODE-006: Remove unused network parameter from AddressKind::detect -- detection is format-based, network validation happens separately. CODE-004/CODE-005: Cache filtered transaction indices per wallet to avoid rebuilding HashSet and filtering on every frame. Invalidated on wallet switch and refresh. CODE-008: Reset AddressInput widgets on network switch so they pick up the new network for validation. Add invalidate_address_input methods to SendScreen, UnshieldCreditsScreen, and WalletsBalancesScreen. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(logging): correct log levels per coding best practices - debug! -> trace!: "state transition built, broadcasting" messages in bundle.rs (5 occurrences) — primary-path step-by-step progress - trace! -> debug!: cookie auth fallback in core/mod.rs and context/mod.rs (2 occurrences) — secondary/fallback execution path, not primary flow - info! -> debug!: anchor mismatch retry in context/shielded.rs — error handling branch, not a business event - info! -> debug!: "marked N note(s) spent" in context/shielded.rs — internal bookkeeping, not a user-visible business event - info! -> debug!: post-transfer sync complete in shielded_send_screen.rs — internal plumbing at screen level Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: minor ui fixes * feat(ui): replace Advanced dropdown with inline right-aligned buttons Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): use allocate_ui_with_layout to right-align dev buttons The right_to_left layout inside with_layout only got the remaining space after left buttons, centering the dev buttons instead of pushing them to the right edge. Using allocate_ui_with_layout with the full remaining width forces the right-to-left block to span the entire remaining area. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): eagerly initialize shielded balance on wallet screen open The shielded tab balance only recalculated when the user clicked the Shielded tab because ShieldedTabView was lazily initialized on tab selection. This meant the wallet list and tab header showed zero balance until the user manually visited the tab. Two-layer fix: - Backend: call initialize_shielded_wallet() in handle_wallet_unlocked() so persisted shielded balance is loaded into shielded_states at wallet load time, making it available to all UI screens immediately. - UI: eagerly create ShieldedTabView on wallet screen construction and wallet switch, and tick() it every frame so the init/sync chain (InitializeShieldedWallet -> SyncNotes -> CheckNullifiers) runs even when the Shielded tab is not the active tab. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove stale balance_breakdown_expanded field from cherry-pick Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): move action buttons to full-width row below header columns The button bar was inside the left column (55% width), so the right-aligned dev buttons ended up in the middle of the screen. Move render_action_buttons() below the two-column header so it spans the full available width. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(ui): change sync status to bullet-point layout Replace the wide horizontal Platform line (pipe-separated) with individual bullet-point items: Core, Addresses, Notes, Nullifiers each on their own line. Removes the Frame wrapper for a lighter appearance. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(shielded): prevent double balance from redundant init + sync chain When eager init (wallet_lifecycle) populates shielded_states and then ShieldedTabView.tick() dispatches another InitializeShieldedWallet, the idempotency check returns early but handle_result() chains SyncNotes which can re-append notes already loaded from DB. Fix: tick() now checks if shielded_states already contains the wallet's state. If so, it adopts the cached balance and marks itself initialized without dispatching a backend task, preventing the redundant sync chain. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(shielded): move initialization entirely to backend, remove UI init path The shielded wallet had two initialization paths: the backend (handle_wallet_unlocked -> initialize_shielded_wallet) and the UI (ShieldedTabView::tick dispatching InitializeShieldedWallet). This created redundant init attempts and blurred the responsibility boundary. Now: - handle_wallet_unlocked initializes the shielded state synchronously, then queues SyncNotes -> CheckNullifiers as a background task - ShieldedTabView reads its state from AppContext::shielded_states via refresh_from_backend_state(), never dispatching init itself - The "Initialize Shielded Wallet" button and WalletUnlockPopup are removed from ShieldedTabView (wallet unlock already calls handle_wallet_unlocked which handles everything) - Resync Notes (developer mode) still dispatches InitializeShieldedWallet as an explicit user action Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): deduplicate notes in sync to prevent double balance When the commitment tree resets (empty/corrupt) but persisted notes remain in the DB, initialize_shielded_wallet loads notes from DB while last_synced_index falls to 0. The subsequent sync rescans from position 0 and re-appends all decrypted notes — duplicating the ones already in memory from DB, doubling the balance. Fix: sync_notes() now checks if a note at the same position already exists in shielded_state.notes before appending, preventing duplicates regardless of the last_synced_index value. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(ui): add shielded diversified address table Replace the single-address display with prev/next buttons in the Shielded tab with a collapsible address table showing all generated diversified addresses. Each row shows the index, truncated bech32m address (click or Copy button to copy full address), and status (Default for index 0). The section is collapsed by default in normal mode and expanded in developer mode. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address review findings — duplicate controls, perf, tab visibility - Remove duplicate sync status/buttons block in shielded_tab.rs - Fix BIP44 accounts with index > 0 being invisible (regression) - Fix early return in render_account_tabs blocking Shielded tab for new wallets - Fix double-tick of ShieldedTabView per frame when tab is active - Fix system_tab_sections O(n*m) perf by precomputing address counts - Track queue_shielded_sync via subtasks for graceful shutdown - Use HashSet for O(1) duplicate note check in sync (was O(n^2)) - Fix balance_breakdown default_open to respect developer mode - Fix is_system_category to delegate to is_visible_in_default_mode - Fix doc comment for system_tab_sections sort order claim - Narrow #[allow(dead_code)] to specific field on AccountSummary Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ui): replace mutex unwrap() with graceful error handling in shielded tab Replace .lock().unwrap() calls on shielded_states with .lock().ok() patterns throughout ShieldedTabView to prevent UI panics on poisoned mutex. Shows fallback messages or skips updates gracefully. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(mcp): resolve async lifetime errors for Rust 2024 edition Use spawn_blocking + block_on in dispatch_task to avoid Send bound issues with platform SDK types (DataContract/Sdk references across await points). Same pattern already used by AppState::handle_backend_task. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: fix mcp dispatch * doc: remove obsolete manual teting docs * fix(ui): include zero-balance addresses in AddressInput wallet entries Pull Core addresses from known_addresses (all derived) instead of address_balances (only funded). Balance is looked up from address_balances, defaulting to 0. Callers use with_balance_range(1..) when they need funded-only addresses. Fixes Mine dialog showing empty dropdown for fresh/zero-balance wallets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): sort AddressInput wallet entries alphabetically Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert: remove redundant sort in extract_wallet_entries filtered_entries() already sorts alphabetically by display_label. The sort added in extract_wallet_entries was redundant. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): clarify shielded network validation logic Replace double-negation with explicit mainnet-class comparison. Documents that testnet/devnet/local share the same HRP ("tdash1z") and cannot be distinguished at the address level. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): include send-all transactions in wallet history Check both outputs and inputs when filtering transactions. Build an OutPoint→Address lookup from the wallet's own transactions to resolve input addresses. Send-all transactions (no change output) were previously dropped because no output matched known_addresses. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): use is_ours flag for transaction filtering Simplify transaction filter to use the is_ours flag instead of address matching. Fix SPV path to set is_ours=true for all wallet transactions (upstream only sets it for sends). SPV history is per-wallet, so all entries are ours by definition. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(e2e): verify is_ours flag for SPV send and receive transactions Sends funds between two wallets via SPV and asserts that both the sender (negative net_amount) and receiver (positive net_amount) have is_ours=true. Validates the fix to the SPV reconcile path that overrides the upstream library's send-only is_ours logic. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(core): clear RPC error state on successful chain lock fetch When get_best_chain_lock() succeeds on the active network, explicitly clear any lingering RPC error in ConnectionStatus so the connection indicator recovers after a transient outage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(model): move is_platform_address_string from UI helpers to model layer AddressKind::detect() in the model layer depended on crate::ui::helpers for platform address detection — wrong layering. Move the function to src/model/address.rs and keep a re-export in helpers for existing callers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): add actionable guidance to address validation error messages Each validation error now tells the user what to do, not just what went wrong: "check for typos", "check you are using the correct network", "use all lowercase". Messages that already had guidance are unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): invalidate address inputs on all screens during context switch TransferScreen, AddressBalanceScreen, and ShieldedSendScreen retained stale address text from the previous network after a context switch. Add invalidate_address_input() to each and call it from change_context(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): clear password field on network switch when no config exists When switching networks, if no config entry exists for the new network (or the password is empty), the password input field now clears instead of retaining the previous network's password. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): clear validated_destination in invalidate_address_input WalletSendScreen and UnshieldCreditsScreen cleared the AddressInput widget but left validated_destination with stale validation from the previous network. Now both are cleared together. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): consume ShieldedNotesSynced to update shielded send screen state After a shielded transfer, the post-transfer note sync result was only logged but never used to update the UI. Now ShieldedNotesSynced updates the balance display, clears the pending-update banner, and appends the remaining balance to the success message. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: apply cargo +nightly fmt formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(model): disambiguate Core vs Identity address detection by prefix Core addresses on Dash start with X/Y (mainnet) or y/8/7 (testnet). When the input starts with a known Core prefix, try Core first. Otherwise try Identity first, since Identity IDs use the same Base58 alphabet but don't have Core address prefixes. This fixes identity-only AddressInput mode rejecting valid Identity IDs that happened to parse as Core addresses, and eliminates the flaky test that had to skip Core-looking random identifiers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(core): show actual RPC error on Networks page instead of generic message The Networks page is a debugging tool — users need the real error text to diagnose Dash Core issues. Replace sanitized "RPC error — check Dash Core status" with the actual error from dashcore_rpc. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(db): resolve deadlock in clear_network_data clear_network_data() held self.conn.lock() for the transaction, then called clear_commitment_tree_tables() which tried to acquire the same mutex — classic non-reentrant mutex deadlock. UI froze on "Delete Data". Fix: scope the connection lock so it's released before the commitment tree clearing acquires it again. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): replace mutex unwrap() with graceful .ok() in shielded_sync_task The shielded_states mutex lock used .unwrap() which would panic on a poisoned mutex. Replace with .ok()? to return None gracefully. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): replace RwLock unwrap() with graceful error handling in shielded tab Replace wallets.read().unwrap() with .read().ok() and show an error label instead of panicking on a poisoned RwLock. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(spv): add debug log when overriding is_ours for receive transactions Log at debug level when SPV reports is_ours=false for a receive transaction (net_amount >= 0) to detect upstream API behavior changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(shielded): log warning on note value divergence during position dedup Use a HashMap<position, value> instead of HashSet<position> so we can detect when a re-synced note at an existing position has a different value — indicating potential data integrity issues. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * perf(model): avoid allocation in is_platform_address_string Replace to_lowercase() with eq_ignore_ascii_case() for the HRP prefix check. The HRP constants are ASCII-only so this is safe and avoids a heap allocation on every call. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(ui): remove unused label field and is_key_only from AccountSummary The label field was constructed but never read outside the builder. is_key_only() had no callers. Remove both instead of suppressing dead_code warnings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(ui): avoid cloning full AccountTab enum in tab content match Extract only the category data (clone of AccountCategory + index) before the match instead of cloning the entire enum. This avoids unnecessary allocation for the Shielded and System variants. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ui): clear validated_address on network switch in mine dialog invalidate_address_inputs() cleared the AddressInput widget but left the validated_address stale, which could reference an address from the previous network. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(model): move truncate_address to model layer and document ASCII precondition Move truncate_address from address_input.rs and shielded_tab.rs into src/model/address.rs as a parameterized public function. Both callers now delegate to the shared implementation with their own prefix/suffix lengths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(shielded): document why spawn_blocking trampoline is needed in queue_shielded_sync The spawn_blocking(block_on(...)) pattern is required because async methods on Arc<Self> produce non-'static futures due to a known Rust compiler limitation (rust-lang/rust#100013). Document this to prevent future removal attempts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: apply nightly rustfmt formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add unified send screen requirements Full requirements for extending the Send screen to support all 14 viable Source×Destination combinations plus 13 new MCP/CLI tools. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add unified send screen architecture System layer traces for all 8 new combinations, code placement plan, send_screen decomposition strategy, MCP tools architecture (13 tools across 3 files), and 8-task work decomposition with dependency graph. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): add unified send screen with all 14 source-destination combinations Wire 8 new send combinations into the Send screen routing: - Core->Shielded, Core->Identity, Platform->Shielded, Platform->Identity - Shielded->Core, Identity->Core, Identity->Platform, Identity->Identity Add Identity as 4th source type with selector dropdown, progressive disclosure (developer mode gates shielded, identities shown when loaded), and display_task_result handlers for all new backend result types. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(mcp): add 10 identity and shielded MCP tools Add identity tools: topup (core/platform), transfer, withdraw, to-address. Add shielded tools: shield-from-core, shield-from-platform, transfer, unshield, withdraw. Add resolve::qualified_identity() helper and validate_credits() for shared parameter validation. Box QualifiedIdentity in SourceSelection::Identity to fix clippy large_enum_variant warning. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add user stories and MCP tool reference for unified send Add SND-007 through SND-013 user stories for new send combinations. Add 10 new tools to MCP.md tool reference table. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): add unified send screen with all 14 source-destination combinations Wire 8 new send combinations into the Send screen routing: - Core->Shielded, Core->Identity, Platform->Shielded, Platform->Identity - Shielded->Core, Identity->Core, Identity->Platform, Identity->Identity Add Identity as 4th source type with selector dropdown, progressive disclosure (developer mode gates shielded, identities shown when loaded), and display_task_result handlers for all new backend result types. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(mcp): add 10 identity and shielded MCP tools Add identity tools: topup (core/platform), transfer, withdraw, to-address. Add shielded tools: shield-from-core, shield-from-platform, transfer, unshield, withdraw. Add resolve::qualified_identity() helper and validate_credits() for shared parameter validation. Box QualifiedIdentity in SourceSelection::Identity to fix clippy large_enum_variant warning. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve merge conflicts with v1.0-dev Restore address_input.rs from v1.0-dev (includes change/system address filtering). Fix render_address_table calls to pass account_filter parameter. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR #798 review findings across send screen and MCP tools Send screen (C01/C02/C03/C05): - Validate shielded destination before dispatching shield routes - Check highest-address platform balance for Platform->Shielded - Subtract estimated fee from identity source available balance - Improve identity-not-found error to be actionable without navigation MCP tools (C09/C10/C15): - Add INTENTIONAL comments for tools that skip SPV …
Summary
v1.0-dev,zk, andzk-fixesbranches (all used v28 for different migrations)IF NOT EXISTS/ column-existence checks, safe to re-run on databases that already applied some or all stepsMigrations included in v33
add_core_wallet_name_column— addscore_wallet_nametowalletsinit_contacts_tables— creates contact request tablescreate_shielded_tables— createsshielded_notestablecreate_shielded_wallet_meta_table— createsshielded_wallet_metatablerename_network_dash_to_mainnet— renames "dash" network to "mainnet"add_wallet_transaction_status_column— addsstatustowallet_transactionsadd_nullifier_sync_timestamp_column— addslast_nullifier_sync_timestamptoshielded_wallet_metaCompanion PRs: #788 (v1.0-dev backport), same commit on
zk-fixes.Test plan
create_tables()sets version to 33, all tables exist🤖 Co-authored by Claudius the Magnificent AI Agent