Skip to content

refactor: extract key-wallet-manager into separate crate#590

Closed
QuantumExplorer wants to merge 2 commits intov0.42-devfrom
refactor/extract-key-wallet-manager
Closed

refactor: extract key-wallet-manager into separate crate#590
QuantumExplorer wants to merge 2 commits intov0.42-devfrom
refactor/extract-key-wallet-manager

Conversation

@QuantumExplorer
Copy link
Copy Markdown
Member

@QuantumExplorer QuantumExplorer commented Mar 27, 2026

Summary

  • Extracts the manager module from key-wallet into a new key-wallet-manager crate
  • Removes tokio and rayon from key-wallet's dependency tree entirely
  • Updates all downstream consumers (dash-spv, key-wallet-ffi, dash-spv-ffi, fuzz) to import from key-wallet-manager directly
  • Re-exports key_wallet from key-wallet-manager for convenience

Motivation

The merged crate approach (PR #503) with feature flags (PR #584) was fragile due to Cargo's feature unification semantics: if any crate in the dependency tree enables the manager feature (even transitively), tokio gets pulled in for all consumers. This caused:

  • WASM binary bloat (tokio cannot run in WASM)
  • Days of debugging wasted tracking down where tokio was being pulled in
  • default-features = false discipline that inevitably breaks ("in a few months, we will forget why we have this thing" — Ivan)

With separate crates, the boundary is compiler-enforced: key-wallet literally cannot bring in tokio because it's not in its dependency tree.

What moved

From To
key-wallet/src/manager/ (7 files) key-wallet-manager/src/
key-wallet/src/test_utils/mock_wallet.rs key-wallet-manager/src/test_utils/

Verification

$ cargo tree -p key-wallet -e no-dev | grep tokio
(no output — tokio is gone from key-wallet)

$ cargo tree -p key-wallet-manager | grep tokio
├── tokio v1.47.1
  • cargo check --workspace passes
  • All 35 key-wallet-manager tests pass
  • All key-wallet tests pass
  • All key-wallet-ffi, dash-spv, dash-spv-ffi compile cleanly

Migration for consumers

- use key_wallet::manager::{WalletManager, WalletInterface, WalletEvent};
+ use key_wallet_manager::{WalletManager, WalletInterface, WalletEvent};

Lightweight consumers (WASM, embedded, RPC types) just use key-wallet — no tokio, no feature juggling.

Test plan

  • cargo check --workspace passes
  • cargo test -p key-wallet-manager — 35/35 pass
  • cargo test -p key-wallet — all pass
  • cargo tree -p key-wallet -e no-dev confirms no tokio
  • CI passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a dedicated key-wallet-manager crate providing wallet-management APIs and test utilities.
  • Breaking Changes

    • Wallet management types moved to the new crate — update imports and builds accordingly.
    • The previous in-crate "manager" feature has been removed from key-wallet; wallet-manager functionality is now delivered by key-wallet-manager.
  • Chores

    • Updated dependency and feature configurations across crates and examples to use the new crate.

@github-actions github-actions bot added the merge-conflict The PR conflicts with the target branch. label Mar 27, 2026
@github-actions
Copy link
Copy Markdown

This PR has merge conflicts with the base branch. Please rebase or merge the base branch into your branch to resolve them.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e03b981a-de3b-4ef0-8e9b-0b99d88e0b4f

📥 Commits

Reviewing files that changed from the base of the PR and between f10064c and a1b2116.

📒 Files selected for processing (65)
  • .github/ci-groups.yml
  • Cargo.toml
  • dash-spv-ffi/Cargo.toml
  • dash-spv-ffi/src/callbacks.rs
  • dash-spv-ffi/src/client.rs
  • dash-spv-ffi/tests/test_wallet_manager.rs
  • dash-spv/Cargo.toml
  • dash-spv/examples/filter_sync.rs
  • dash-spv/examples/simple_sync.rs
  • dash-spv/examples/spv_with_wallet.rs
  • dash-spv/src/client/core.rs
  • dash-spv/src/client/event_handler.rs
  • dash-spv/src/client/events.rs
  • dash-spv/src/client/lifecycle.rs
  • dash-spv/src/client/mempool.rs
  • dash-spv/src/client/mod.rs
  • dash-spv/src/client/queries.rs
  • dash-spv/src/client/sync_coordinator.rs
  • dash-spv/src/client/transactions.rs
  • dash-spv/src/lib.rs
  • dash-spv/src/main.rs
  • dash-spv/src/sync/blocks/manager.rs
  • dash-spv/src/sync/blocks/pipeline.rs
  • dash-spv/src/sync/blocks/sync_manager.rs
  • dash-spv/src/sync/events.rs
  • dash-spv/src/sync/filters/batch.rs
  • dash-spv/src/sync/filters/batch_tracker.rs
  • dash-spv/src/sync/filters/manager.rs
  • dash-spv/src/sync/filters/pipeline.rs
  • dash-spv/src/sync/filters/sync_manager.rs
  • dash-spv/src/sync/mempool/manager.rs
  • dash-spv/src/sync/mempool/sync_manager.rs
  • dash-spv/src/sync/sync_coordinator.rs
  • dash-spv/src/test_utils/event_handler.rs
  • dash-spv/src/validation/filter.rs
  • dash-spv/tests/dashd_sync/helpers.rs
  • dash-spv/tests/dashd_sync/setup.rs
  • dash-spv/tests/dashd_sync/tests_basic.rs
  • dash-spv/tests/peer_test.rs
  • dash-spv/tests/wallet_integration_test.rs
  • fuzz/Cargo.toml
  • key-wallet-ffi/Cargo.toml
  • key-wallet-ffi/src/error.rs
  • key-wallet-ffi/src/wallet_manager.rs
  • key-wallet-ffi/src/wallet_manager_tests.rs
  • key-wallet-ffi/tests/test_error_conversions.rs
  • key-wallet-manager/Cargo.toml
  • key-wallet-manager/src/accessors.rs
  • key-wallet-manager/src/error.rs
  • key-wallet-manager/src/event_tests.rs
  • key-wallet-manager/src/events.rs
  • key-wallet-manager/src/lib.rs
  • key-wallet-manager/src/matching.rs
  • key-wallet-manager/src/process_block.rs
  • key-wallet-manager/src/test_helpers.rs
  • key-wallet-manager/src/test_utils/mock_wallet.rs
  • key-wallet-manager/src/test_utils/mod.rs
  • key-wallet-manager/src/wallet_interface.rs
  • key-wallet/Cargo.toml
  • key-wallet/examples/wallet_creation.rs
  • key-wallet/src/lib.rs
  • key-wallet/src/test_utils/mod.rs
  • key-wallet/tests/integration_test.rs
  • key-wallet/tests/spv_integration_tests.rs
  • key-wallet/tests/test_serialized_wallets.rs
💤 Files with no reviewable changes (2)
  • key-wallet/src/lib.rs
  • key-wallet/src/test_utils/mod.rs
✅ Files skipped from review due to trivial changes (41)
  • .github/ci-groups.yml
  • key-wallet/examples/wallet_creation.rs
  • key-wallet-manager/src/wallet_interface.rs
  • dash-spv-ffi/tests/test_wallet_manager.rs
  • dash-spv/src/sync/filters/sync_manager.rs
  • key-wallet-manager/src/matching.rs
  • dash-spv/examples/simple_sync.rs
  • dash-spv/src/client/queries.rs
  • Cargo.toml
  • key-wallet/tests/integration_test.rs
  • dash-spv/src/validation/filter.rs
  • dash-spv/src/client/core.rs
  • dash-spv-ffi/Cargo.toml
  • dash-spv/examples/filter_sync.rs
  • dash-spv/src/sync/filters/pipeline.rs
  • dash-spv/src/test_utils/event_handler.rs
  • dash-spv/src/sync/blocks/sync_manager.rs
  • key-wallet-manager/src/test_helpers.rs
  • dash-spv/src/lib.rs
  • dash-spv/src/sync/filters/batch_tracker.rs
  • dash-spv/src/client/events.rs
  • key-wallet-manager/src/test_utils/mock_wallet.rs
  • dash-spv/src/client/transactions.rs
  • dash-spv/tests/dashd_sync/tests_basic.rs
  • fuzz/Cargo.toml
  • key-wallet-manager/src/test_utils/mod.rs
  • key-wallet-ffi/tests/test_error_conversions.rs
  • dash-spv/src/client/mod.rs
  • key-wallet/tests/test_serialized_wallets.rs
  • key-wallet-ffi/src/wallet_manager_tests.rs
  • dash-spv/src/sync/blocks/pipeline.rs
  • dash-spv/src/sync/mempool/manager.rs
  • dash-spv/src/client/mempool.rs
  • key-wallet-manager/src/event_tests.rs
  • dash-spv/src/sync/events.rs
  • key-wallet/tests/spv_integration_tests.rs
  • dash-spv/src/main.rs
  • dash-spv/src/sync/sync_coordinator.rs
  • dash-spv/tests/dashd_sync/setup.rs
  • key-wallet-manager/Cargo.toml
  • key-wallet-manager/src/events.rs
🚧 Files skipped from review as they are similar to previous changes (13)
  • dash-spv/tests/peer_test.rs
  • dash-spv/examples/spv_with_wallet.rs
  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/tests/dashd_sync/helpers.rs
  • dash-spv/src/client/sync_coordinator.rs
  • key-wallet-manager/src/process_block.rs
  • dash-spv/Cargo.toml
  • dash-spv-ffi/src/callbacks.rs
  • key-wallet-ffi/src/wallet_manager.rs
  • key-wallet/Cargo.toml
  • key-wallet-ffi/src/error.rs
  • dash-spv/src/client/lifecycle.rs
  • key-wallet-ffi/Cargo.toml

📝 Walkthrough

Walkthrough

Adds a new crate key-wallet-manager to the workspace, moves wallet-manager functionality and APIs into it, updates Cargo manifests and consumers to import manager types from key_wallet_manager, and adds manager accessors, error types, and test-utils. No runtime control-flow changes outside of type/path updates.

Changes

Cohort / File(s) Summary
Workspace & top-level Cargo
Cargo.toml, key-wallet/Cargo.toml, fuzz/Cargo.toml
Added key-wallet-manager to workspace; removed/adjusted manager/parallel-filters features from key-wallet; tweaked fuzz dependency features.
New crate: key-wallet-manager
key-wallet-manager/Cargo.toml, key-wallet-manager/src/lib.rs, key-wallet-manager/src/error.rs, key-wallet-manager/src/accessors.rs, key-wallet-manager/src/process_block.rs, key-wallet-manager/src/events.rs, key-wallet-manager/src/wallet_interface.rs, key-wallet-manager/src/test_helpers.rs, key-wallet-manager/src/event_tests.rs, key-wallet-manager/src/matching.rs, key-wallet-manager/src/test_utils/...
Introduced new crate manifest, public re-exports, new WalletError module, large accessors API (many WalletManager methods), test-utils module and mock wallet, and refactored internals to use key_wallet primitives where appropriate.
dash-spv: clients, sync, examples, tests
dash-spv/src/client/..., dash-spv/src/sync/..., dash-spv/src/validation/filter.rs, dash-spv/examples/*, dash-spv/src/lib.rs, dash-spv/src/main.rs, dash-spv/tests/..., dash-spv/src/test_utils/event_handler.rs
Repointed imports to key_wallet_manager for WalletManager, WalletInterface, WalletEvent, FilterMatchKey, MockWallet, etc.; no functional/control-flow changes.
dash-spv-ffi
dash-spv-ffi/Cargo.toml, dash-spv-ffi/src/callbacks.rs, dash-spv-ffi/src/client.rs, dash-spv-ffi/tests/*
Added key-wallet-manager dependency; updated FFI code and tests to use key_wallet_manager types for manager and events.
key-wallet-ffi
key-wallet-ffi/Cargo.toml, key-wallet-ffi/src/error.rs, key-wallet-ffi/src/wallet_manager.rs, key-wallet-ffi/src/wallet_manager_tests.rs, key-wallet-ffi/tests/*
Adjusted feature wiring and added key-wallet-manager dependency; updated imports and error conversions/matching to key_wallet_manager::WalletError.
key-wallet: API surface & tests
key-wallet/src/lib.rs, key-wallet/src/test_utils/mod.rs, key-wallet/examples/*, key-wallet/tests/*
Removed public manager module export and manager-gated test re-exports; tests/examples updated to import manager types from key_wallet_manager.
Sync / filters / mempool / blocks modules
dash-spv/src/sync/..., dash-spv/src/client/..., dash-spv/src/validation/...
Switched WalletInterface, FilterMatchKey, and related imports to key_wallet_manager; generic bounds and signatures unchanged.
CI config
.github/ci-groups.yml
Added key-wallet-manager to CI wallet group.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as "dash-spv CLI"
    participant Client as "DashSpvClient"
    participant Manager as "key-wallet-manager::WalletManager"
    participant Core as "key_wallet (core wallet)"

    rect rgba(200,200,255,0.5)
    CLI->>Client: create with WalletManager reference
    Client->>Manager: subscribe / call APIs (sync, tx, balance)
    Manager->>Core: delegate wallet primitives (create, scan, utxos, tx)
    Core-->>Manager: results / events
    Manager-->>Client: emit WalletEvent / results
    Client-->>CLI: progress / event callbacks
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • xdustinface

Poem

🐰 I hopped a crate across the lawn,
Moved manager where it belongs at dawn.
Types realigned with tidy cheer,
Tests and examples follow near.
🥕 New burrow—code hops on!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the primary change: extracting the key-wallet-manager module into a separate crate, which is the core objective of this PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/extract-key-wallet-manager

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

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
key-wallet-manager/src/process_block.rs (1)

264-264: Minor typo in comment.

"Inrease" should be "Increase".

📝 Suggested fix
-        //Increase synced height
+        // Increase synced height
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-manager/src/process_block.rs` at line 264, Locate the inline
comment that reads "// Inrease synced height" (the comment above the
synced-height increment) and correct the typo to "// Increase synced height" so
the comment is spelled properly and clear.
key-wallet-manager/Cargo.toml (1)

21-24: Use consistent version precision in Cargo.toml dependencies.

The dependencies mix loose specs ("0.1", "1.11") with precise ones ("2.0.1"). For better reproducibility, align all versions to the same precision level. Note that "1.11" means >=1.11.0, <1.12.0, which allows patch versions to drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-manager/Cargo.toml` around lines 21 - 24, The Cargo.toml
dependencies use mixed version precision (async-trait "0.1", rayon "1.11",
tracing "0.1" vs bincode "2.0.1"); make them consistent by pinning or matching
precision across all entries (e.g., change async-trait and tracing to "0.1.51"
or similar exact patch versions, or change bincode to "2.0" if you prefer
caret-style), update the entries for async-trait, rayon, bincode, and tracing so
they all follow the same precision scheme (either exact patch versions or
caret/tilde ranges) to ensure reproducible builds.
key-wallet-manager/src/lib.rs (2)

1188-1223: Preserve key_wallet::Error as a source instead of flattening it into strings.

This remapping discards the original variant/source information, so callers can no longer match or chain the underlying error. A wrapped WalletError variant is easier to propagate and removes the need to keep a manual mapping table in sync.

As per coding guidelines, Use proper error types (thiserror) and propagate errors appropriately in Rust code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-manager/src/lib.rs` around lines 1188 - 1223, The current impl
From<key_wallet::Error> for WalletError flattens and stringifies the original
key_wallet::Error variants; instead add a wrapped variant on WalletError (e.g.,
KeyWallet(#[from] key_wallet::Error) or KeyWalletError(Box<key_wallet::Error>))
and change the From implementation to return that wrapper for unhandled cases
(or use #[derive(thiserror::Error)] with a #[from] field) so the original
key_wallet::Error value and its variant information are preserved for chaining
and matching; keep any special-case mappings you need (like InvalidMnemonic ->
InvalidMnemonic) but fallback to the new KeyWallet/KeyWalletError wrapper to
avoid losing the source error.

24-25: The full key_wallet re-export blurs the boundary this PR is trying to create.

One of the goals here is keeping lightweight consumers on key-wallet only. Re-exporting the entire primitive crate from key-wallet-manager makes it easy for new code to depend on the heavier crate for primitives again; narrower re-exports would keep that boundary clearer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-manager/src/lib.rs` around lines 24 - 25, The current top-level
full re-export `pub use key_wallet;` in lib.rs exposes the entire heavier
primitive crate and breaks the intended dependency boundary; remove that blanket
re-export and instead audit which specific types/functions from the key_wallet
crate this crate actually needs to expose to consumers, then add explicit,
narrow re-exports for only those symbols (replace the `pub use key_wallet;` line
with `pub use key_wallet::{...}` containing the audited identifiers) so
lightweight consumers remain limited to the lighter crate surface.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@key-wallet-manager/Cargo.toml`:
- Around line 21-24: The Cargo.toml dependencies use mixed version precision
(async-trait "0.1", rayon "1.11", tracing "0.1" vs bincode "2.0.1"); make them
consistent by pinning or matching precision across all entries (e.g., change
async-trait and tracing to "0.1.51" or similar exact patch versions, or change
bincode to "2.0" if you prefer caret-style), update the entries for async-trait,
rayon, bincode, and tracing so they all follow the same precision scheme (either
exact patch versions or caret/tilde ranges) to ensure reproducible builds.

In `@key-wallet-manager/src/lib.rs`:
- Around line 1188-1223: The current impl From<key_wallet::Error> for
WalletError flattens and stringifies the original key_wallet::Error variants;
instead add a wrapped variant on WalletError (e.g., KeyWallet(#[from]
key_wallet::Error) or KeyWalletError(Box<key_wallet::Error>)) and change the
From implementation to return that wrapper for unhandled cases (or use
#[derive(thiserror::Error)] with a #[from] field) so the original
key_wallet::Error value and its variant information are preserved for chaining
and matching; keep any special-case mappings you need (like InvalidMnemonic ->
InvalidMnemonic) but fallback to the new KeyWallet/KeyWalletError wrapper to
avoid losing the source error.
- Around line 24-25: The current top-level full re-export `pub use key_wallet;`
in lib.rs exposes the entire heavier primitive crate and breaks the intended
dependency boundary; remove that blanket re-export and instead audit which
specific types/functions from the key_wallet crate this crate actually needs to
expose to consumers, then add explicit, narrow re-exports for only those symbols
(replace the `pub use key_wallet;` line with `pub use key_wallet::{...}`
containing the audited identifiers) so lightweight consumers remain limited to
the lighter crate surface.

In `@key-wallet-manager/src/process_block.rs`:
- Line 264: Locate the inline comment that reads "// Inrease synced height" (the
comment above the synced-height increment) and correct the typo to "// Increase
synced height" so the comment is spelled properly and clear.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5444982c-194e-4d98-b826-e314eaf80538

📥 Commits

Reviewing files that changed from the base of the PR and between fcdffa7 and bca7ccc.

📒 Files selected for processing (63)
  • Cargo.toml
  • dash-spv-ffi/Cargo.toml
  • dash-spv-ffi/src/callbacks.rs
  • dash-spv-ffi/src/client.rs
  • dash-spv-ffi/tests/test_wallet_manager.rs
  • dash-spv/Cargo.toml
  • dash-spv/examples/filter_sync.rs
  • dash-spv/examples/simple_sync.rs
  • dash-spv/examples/spv_with_wallet.rs
  • dash-spv/peer_reputation.json
  • dash-spv/src/client/core.rs
  • dash-spv/src/client/event_handler.rs
  • dash-spv/src/client/events.rs
  • dash-spv/src/client/lifecycle.rs
  • dash-spv/src/client/mempool.rs
  • dash-spv/src/client/mod.rs
  • dash-spv/src/client/queries.rs
  • dash-spv/src/client/sync_coordinator.rs
  • dash-spv/src/client/transactions.rs
  • dash-spv/src/lib.rs
  • dash-spv/src/main.rs
  • dash-spv/src/sync/blocks/manager.rs
  • dash-spv/src/sync/blocks/pipeline.rs
  • dash-spv/src/sync/blocks/sync_manager.rs
  • dash-spv/src/sync/events.rs
  • dash-spv/src/sync/filters/batch.rs
  • dash-spv/src/sync/filters/batch_tracker.rs
  • dash-spv/src/sync/filters/manager.rs
  • dash-spv/src/sync/filters/pipeline.rs
  • dash-spv/src/sync/filters/sync_manager.rs
  • dash-spv/src/sync/mempool/manager.rs
  • dash-spv/src/sync/mempool/sync_manager.rs
  • dash-spv/src/sync/sync_coordinator.rs
  • dash-spv/src/test_utils/event_handler.rs
  • dash-spv/src/validation/filter.rs
  • dash-spv/tests/dashd_sync/helpers.rs
  • dash-spv/tests/dashd_sync/setup.rs
  • dash-spv/tests/dashd_sync/tests_basic.rs
  • dash-spv/tests/peer_test.rs
  • dash-spv/tests/wallet_integration_test.rs
  • fuzz/Cargo.toml
  • key-wallet-ffi/Cargo.toml
  • key-wallet-ffi/src/error.rs
  • key-wallet-ffi/src/wallet_manager.rs
  • key-wallet-ffi/src/wallet_manager_tests.rs
  • key-wallet-ffi/tests/test_error_conversions.rs
  • key-wallet-manager/Cargo.toml
  • key-wallet-manager/src/event_tests.rs
  • key-wallet-manager/src/events.rs
  • key-wallet-manager/src/lib.rs
  • key-wallet-manager/src/matching.rs
  • key-wallet-manager/src/process_block.rs
  • key-wallet-manager/src/test_helpers.rs
  • key-wallet-manager/src/test_utils/mock_wallet.rs
  • key-wallet-manager/src/test_utils/mod.rs
  • key-wallet-manager/src/wallet_interface.rs
  • key-wallet/Cargo.toml
  • key-wallet/examples/wallet_creation.rs
  • key-wallet/src/lib.rs
  • key-wallet/src/test_utils/mod.rs
  • key-wallet/tests/integration_test.rs
  • key-wallet/tests/spv_integration_tests.rs
  • key-wallet/tests/test_serialized_wallets.rs
💤 Files with no reviewable changes (2)
  • key-wallet/src/lib.rs
  • key-wallet/src/test_utils/mod.rs

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 27, 2026
@QuantumExplorer QuantumExplorer force-pushed the refactor/extract-key-wallet-manager branch from bca7ccc to 3c88605 Compare March 27, 2026 13:36
@github-actions github-actions bot removed the merge-conflict The PR conflicts with the target branch. label Mar 27, 2026
@QuantumExplorer QuantumExplorer force-pushed the refactor/extract-key-wallet-manager branch 2 times, most recently from 43cfe1b to f10064c Compare March 27, 2026 13:45
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 27, 2026

Codecov Report

❌ Patch coverage is 50.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.36%. Comparing base (472131a) to head (a1b2116).
⚠️ Report is 3 commits behind head on v0.42-dev.

Files with missing lines Patch % Lines
key-wallet-ffi/src/wallet_manager.rs 0.00% 2 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##           v0.42-dev     #590      +/-   ##
=============================================
+ Coverage      67.06%   67.36%   +0.29%     
=============================================
  Files            318      312       -6     
  Lines          67026    65947    -1079     
=============================================
- Hits           44950    44422     -528     
+ Misses         22076    21525     -551     
Flag Coverage Δ
core 75.21% <ø> (ø)
ffi 36.29% <50.00%> (+2.02%) ⬆️
rpc 19.92% <ø> (ø)
spv 83.82% <ø> (+0.03%) ⬆️
wallet 66.86% <ø> (-0.11%) ⬇️
Files with missing lines Coverage Δ
dash-spv-ffi/src/callbacks.rs 80.59% <ø> (ø)
dash-spv-ffi/src/client.rs 59.13% <100.00%> (+0.53%) ⬆️
dash-spv/src/client/core.rs 46.00% <ø> (ø)
dash-spv/src/client/event_handler.rs 95.71% <ø> (ø)
dash-spv/src/client/events.rs 100.00% <ø> (ø)
dash-spv/src/client/lifecycle.rs 65.31% <ø> (ø)
dash-spv/src/client/mempool.rs 63.63% <ø> (ø)
dash-spv/src/client/mod.rs 98.64% <ø> (ø)
dash-spv/src/client/queries.rs 14.28% <ø> (ø)
dash-spv/src/client/sync_coordinator.rs 82.45% <ø> (ø)
... and 17 more

... and 26 files with indirect coverage changes

Moves the manager module from key-wallet into its own key-wallet-manager
crate to enforce a clean dependency boundary between wallet primitives
and the async wallet runtime (tokio).

This solves the problem where Cargo's feature unification caused tokio
to leak into WASM and other lightweight consumers through the default
"manager" feature flag — a fragile boundary that relied on every
consumer remembering default-features = false.

With separate crates, key-wallet is tokio-free by construction.
Consumers that need WalletManager, WalletInterface, or event support
add key-wallet-manager as an explicit dependency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@QuantumExplorer QuantumExplorer force-pushed the refactor/extract-key-wallet-manager branch from f10064c to f92f114 Compare March 27, 2026 13:56
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
key-wallet-manager/src/error.rs (1)

5-62: Prefer thiserror for WalletError.

This new crate-level error type is hand-rolling both Display and std::error::Error, which makes future variant changes easy to desync. Please derive thiserror::Error instead and keep the formatting on the variants themselves.

As per coding guidelines "Use proper error types (thiserror) and propagate errors appropriately in Rust code".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-manager/src/error.rs` around lines 5 - 62, Replace the hand-rolled
Display and Error impls for WalletError with thiserror: add
#[derive(thiserror::Error, Debug)] on the WalletError enum, remove the manual
impl core::fmt::Display for WalletError and impl std::error::Error for
WalletError, and annotate each variant with a #[error("...")] string that
preserves the current messages (e.g., #[error("Wallet creation failed: {0}")]
for WalletCreation, #[error("Invalid mnemonic: {0}")] for InvalidMnemonic,
etc.). For WalletNotFound and WalletExists (the WalletId variants) use an
#[error] message that formats the id the same way as before (include a
formatting expression that renders the id as hex), and keep the existing variant
names (WalletError, WalletNotFound, WalletExists, InvalidMnemonic,
AccountCreation, AccountNotFound, AddressGeneration, InvalidNetwork,
InvalidParameter, TransactionBuild, InsufficientFunds) so callers/signatures
remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@key-wallet-manager/src/accessors.rs`:
- Around line 74-82: get_account currently only calls wallet.get_bip44_account
and therefore returns Ok(None) for BIP32-only accounts; update the accessor to
accept an explicit account-type/preference parameter (e.g., AccountType or enum)
or implement fallback logic: first try wallet.get_bip44_account(index) and if
None then call the BIP32 lookup/generation path used by the address-generation
helpers in lib.rs (or wallet.get_bip32_account/get_account_by_index if present),
returning the found &Account; adjust the get_account signature and callers to
pass the account type when needed and ensure the Result<Option<&Account>,
WalletError> semantics remain unchanged.
- Around line 37-45: remove_wallet currently removes from self.wallets before
ensuring a matching entry exists in self.wallet_infos, which can leave the
manager inconsistent if the second lookup fails; update remove_wallet to first
check presence of both entries (e.g., using self.wallets.get and
self.wallet_infos.get or by borrowing checks) and only perform the removals
together (remove from self.wallets and self.wallet_infos) once both are
confirmed present, then adjust self.structural_revision using the removed info
and return Ok((wallet, info)); preserve error return of
WalletError::WalletNotFound for missing WalletId.
- Around line 169-172: The public accessor event_sender(&self) exposes
broadcast::Sender<WalletEvent> to consumers and must be restricted: change the
method visibility to pub(crate) so only crate-local code (e.g., process_block.rs
and lib.rs) can call it, and keep subscribe_events() public for consumers;
alternatively remove the accessor entirely and add controlled emit helpers
(e.g., emit_event or emit_wallet_event on the manager) that validate invariants
and call the internal sender. Locate the event_sender method and replace its
signature with a crate-only accessor or remove it, then update internal call
sites (process_block.rs, lib.rs) to use the new pub(crate) accessor or the new
emit_X helper(s), ensuring external code cannot call .send() directly.

In `@key-wallet-manager/src/error.rs`:
- Around line 95-96: Replace the catch-all `_` arm that maps any
key_wallet::Error into WalletError::InvalidParameter (and remove the
#[allow(unreachable_patterns)] attribute) with explicit match arms for each
variant of key_wallet::Error so the original error types are preserved;
specifically add explicit arms for Slip10 and BLS (each behind their respective
feature gates) that convert to the appropriate WalletError variant instead of
collapsing to WalletError::InvalidParameter, so the compiler will force you to
handle new upstream variants of key_wallet::Error.

---

Nitpick comments:
In `@key-wallet-manager/src/error.rs`:
- Around line 5-62: Replace the hand-rolled Display and Error impls for
WalletError with thiserror: add #[derive(thiserror::Error, Debug)] on the
WalletError enum, remove the manual impl core::fmt::Display for WalletError and
impl std::error::Error for WalletError, and annotate each variant with a
#[error("...")] string that preserves the current messages (e.g.,
#[error("Wallet creation failed: {0}")] for WalletCreation, #[error("Invalid
mnemonic: {0}")] for InvalidMnemonic, etc.). For WalletNotFound and WalletExists
(the WalletId variants) use an #[error] message that formats the id the same way
as before (include a formatting expression that renders the id as hex), and keep
the existing variant names (WalletError, WalletNotFound, WalletExists,
InvalidMnemonic, AccountCreation, AccountNotFound, AddressGeneration,
InvalidNetwork, InvalidParameter, TransactionBuild, InsufficientFunds) so
callers/signatures remain unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2311ac35-6c84-4454-9f8e-e11452a8fab0

📥 Commits

Reviewing files that changed from the base of the PR and between 3c88605 and f10064c.

📒 Files selected for processing (64)
  • Cargo.toml
  • dash-spv-ffi/Cargo.toml
  • dash-spv-ffi/src/callbacks.rs
  • dash-spv-ffi/src/client.rs
  • dash-spv-ffi/tests/test_wallet_manager.rs
  • dash-spv/Cargo.toml
  • dash-spv/examples/filter_sync.rs
  • dash-spv/examples/simple_sync.rs
  • dash-spv/examples/spv_with_wallet.rs
  • dash-spv/src/client/core.rs
  • dash-spv/src/client/event_handler.rs
  • dash-spv/src/client/events.rs
  • dash-spv/src/client/lifecycle.rs
  • dash-spv/src/client/mempool.rs
  • dash-spv/src/client/mod.rs
  • dash-spv/src/client/queries.rs
  • dash-spv/src/client/sync_coordinator.rs
  • dash-spv/src/client/transactions.rs
  • dash-spv/src/lib.rs
  • dash-spv/src/main.rs
  • dash-spv/src/sync/blocks/manager.rs
  • dash-spv/src/sync/blocks/pipeline.rs
  • dash-spv/src/sync/blocks/sync_manager.rs
  • dash-spv/src/sync/events.rs
  • dash-spv/src/sync/filters/batch.rs
  • dash-spv/src/sync/filters/batch_tracker.rs
  • dash-spv/src/sync/filters/manager.rs
  • dash-spv/src/sync/filters/pipeline.rs
  • dash-spv/src/sync/filters/sync_manager.rs
  • dash-spv/src/sync/mempool/manager.rs
  • dash-spv/src/sync/mempool/sync_manager.rs
  • dash-spv/src/sync/sync_coordinator.rs
  • dash-spv/src/test_utils/event_handler.rs
  • dash-spv/src/validation/filter.rs
  • dash-spv/tests/dashd_sync/helpers.rs
  • dash-spv/tests/dashd_sync/setup.rs
  • dash-spv/tests/dashd_sync/tests_basic.rs
  • dash-spv/tests/peer_test.rs
  • dash-spv/tests/wallet_integration_test.rs
  • fuzz/Cargo.toml
  • key-wallet-ffi/Cargo.toml
  • key-wallet-ffi/src/error.rs
  • key-wallet-ffi/src/wallet_manager.rs
  • key-wallet-ffi/src/wallet_manager_tests.rs
  • key-wallet-ffi/tests/test_error_conversions.rs
  • key-wallet-manager/Cargo.toml
  • key-wallet-manager/src/accessors.rs
  • key-wallet-manager/src/error.rs
  • key-wallet-manager/src/event_tests.rs
  • key-wallet-manager/src/events.rs
  • key-wallet-manager/src/lib.rs
  • key-wallet-manager/src/matching.rs
  • key-wallet-manager/src/process_block.rs
  • key-wallet-manager/src/test_helpers.rs
  • key-wallet-manager/src/test_utils/mock_wallet.rs
  • key-wallet-manager/src/test_utils/mod.rs
  • key-wallet-manager/src/wallet_interface.rs
  • key-wallet/Cargo.toml
  • key-wallet/examples/wallet_creation.rs
  • key-wallet/src/lib.rs
  • key-wallet/src/test_utils/mod.rs
  • key-wallet/tests/integration_test.rs
  • key-wallet/tests/spv_integration_tests.rs
  • key-wallet/tests/test_serialized_wallets.rs
💤 Files with no reviewable changes (2)
  • key-wallet/src/lib.rs
  • key-wallet/src/test_utils/mod.rs
✅ Files skipped from review due to trivial changes (44)
  • dash-spv/src/lib.rs
  • key-wallet-manager/src/wallet_interface.rs
  • key-wallet-ffi/src/wallet_manager_tests.rs
  • fuzz/Cargo.toml
  • dash-spv/examples/simple_sync.rs
  • dash-spv/tests/wallet_integration_test.rs
  • key-wallet/examples/wallet_creation.rs
  • dash-spv/src/validation/filter.rs
  • key-wallet-manager/src/events.rs
  • dash-spv/examples/spv_with_wallet.rs
  • dash-spv/src/client/transactions.rs
  • dash-spv/src/client/events.rs
  • dash-spv/src/sync/filters/pipeline.rs
  • dash-spv/src/sync/sync_coordinator.rs
  • key-wallet-manager/src/test_helpers.rs
  • key-wallet-manager/src/event_tests.rs
  • dash-spv/src/client/lifecycle.rs
  • dash-spv-ffi/Cargo.toml
  • dash-spv/src/client/sync_coordinator.rs
  • dash-spv/tests/dashd_sync/tests_basic.rs
  • key-wallet-manager/src/test_utils/mock_wallet.rs
  • key-wallet/tests/test_serialized_wallets.rs
  • dash-spv/src/client/core.rs
  • dash-spv/src/client/mod.rs
  • key-wallet-ffi/Cargo.toml
  • dash-spv/examples/filter_sync.rs
  • key-wallet-manager/src/test_utils/mod.rs
  • dash-spv/src/main.rs
  • dash-spv/src/sync/blocks/manager.rs
  • dash-spv/src/client/mempool.rs
  • dash-spv/tests/peer_test.rs
  • Cargo.toml
  • dash-spv/src/client/queries.rs
  • dash-spv/src/sync/events.rs
  • key-wallet-ffi/tests/test_error_conversions.rs
  • dash-spv/src/sync/filters/batch_tracker.rs
  • dash-spv/src/sync/filters/sync_manager.rs
  • dash-spv/src/sync/mempool/manager.rs
  • key-wallet-manager/Cargo.toml
  • key-wallet-manager/src/process_block.rs
  • key-wallet-manager/src/matching.rs
  • dash-spv-ffi/tests/test_wallet_manager.rs
  • dash-spv/src/sync/blocks/pipeline.rs
  • key-wallet/tests/integration_test.rs
🚧 Files skipped from review as they are similar to previous changes (10)
  • dash-spv-ffi/src/client.rs
  • dash-spv/src/sync/filters/manager.rs
  • dash-spv/Cargo.toml
  • key-wallet-ffi/src/error.rs
  • key-wallet/tests/spv_integration_tests.rs
  • dash-spv/tests/dashd_sync/helpers.rs
  • dash-spv/src/client/event_handler.rs
  • key-wallet/Cargo.toml
  • key-wallet-ffi/src/wallet_manager.rs
  • dash-spv/src/test_utils/event_handler.rs

Comment on lines +37 to +45
pub fn remove_wallet(&mut self, wallet_id: &WalletId) -> Result<(Wallet, T), WalletError> {
let wallet =
self.wallets.remove(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?;
let info =
self.wallet_infos.remove(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?;
// Absorb the removed wallet's account-level revision so the total
// stays monotonically increasing even though we lost a contributor.
self.structural_revision += info.monitor_revision() + 1;
Ok((wallet, info))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Keep wallet/info removal atomic.

If the two maps ever drift, Line 39 removes the wallet before Line 41 errors, leaving the manager in a worse inconsistent state. Check both entries before mutating, then remove them together.

💡 Minimal fix
 pub fn remove_wallet(&mut self, wallet_id: &WalletId) -> Result<(Wallet, T), WalletError> {
+    if !self.wallets.contains_key(wallet_id) || !self.wallet_infos.contains_key(wallet_id) {
+        return Err(WalletError::WalletNotFound(*wallet_id));
+    }
     let wallet =
         self.wallets.remove(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?;
     let info =
         self.wallet_infos.remove(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn remove_wallet(&mut self, wallet_id: &WalletId) -> Result<(Wallet, T), WalletError> {
let wallet =
self.wallets.remove(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?;
let info =
self.wallet_infos.remove(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?;
// Absorb the removed wallet's account-level revision so the total
// stays monotonically increasing even though we lost a contributor.
self.structural_revision += info.monitor_revision() + 1;
Ok((wallet, info))
pub fn remove_wallet(&mut self, wallet_id: &WalletId) -> Result<(Wallet, T), WalletError> {
if !self.wallets.contains_key(wallet_id) || !self.wallet_infos.contains_key(wallet_id) {
return Err(WalletError::WalletNotFound(*wallet_id));
}
let wallet =
self.wallets.remove(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?;
let info =
self.wallet_infos.remove(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?;
// Absorb the removed wallet's account-level revision so the total
// stays monotonically increasing even though we lost a contributor.
self.structural_revision += info.monitor_revision() + 1;
Ok((wallet, info))
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-manager/src/accessors.rs` around lines 37 - 45, remove_wallet
currently removes from self.wallets before ensuring a matching entry exists in
self.wallet_infos, which can leave the manager inconsistent if the second lookup
fails; update remove_wallet to first check presence of both entries (e.g., using
self.wallets.get and self.wallet_infos.get or by borrowing checks) and only
perform the removals together (remove from self.wallets and self.wallet_infos)
once both are confirmed present, then adjust self.structural_revision using the
removed info and return Ok((wallet, info)); preserve error return of
WalletError::WalletNotFound for missing WalletId.

Comment on lines +74 to +82
/// Get account by index in a specific wallet
pub fn get_account(
&self,
wallet_id: &WalletId,
index: u32,
) -> Result<Option<&Account>, WalletError> {
let wallet = self.wallets.get(wallet_id).ok_or(WalletError::WalletNotFound(*wallet_id))?;
Ok(wallet.get_bip44_account(index))
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

get_account only sees BIP44 accounts right now.

This returns Ok(None) for a BIP32-only account at the same index, even though the wallet contains it. The accessor needs an explicit account type/preference parameter, or equivalent fallback logic to the address-generation methods in key-wallet-manager/src/lib.rs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-manager/src/accessors.rs` around lines 74 - 82, get_account
currently only calls wallet.get_bip44_account and therefore returns Ok(None) for
BIP32-only accounts; update the accessor to accept an explicit
account-type/preference parameter (e.g., AccountType or enum) or implement
fallback logic: first try wallet.get_bip44_account(index) and if None then call
the BIP32 lookup/generation path used by the address-generation helpers in
lib.rs (or wallet.get_bip32_account/get_account_by_index if present), returning
the found &Account; adjust the get_account signature and callers to pass the
account type when needed and ensure the Result<Option<&Account>, WalletError>
semantics remain unchanged.

Comment on lines +95 to +96
#[allow(unreachable_patterns)]
_ => WalletError::InvalidParameter(format!("Key wallet error: {}", err)),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and examine the error.rs file
find . -path "*key-wallet-manager/src/error.rs" -type f

Repository: dashpay/rust-dashcore

Length of output: 98


🏁 Script executed:

# Read the context around lines 95-96
cat -n key-wallet-manager/src/error.rs | sed -n '80,110p'

Repository: dashpay/rust-dashcore

Length of output: 1246


🏁 Script executed:

# Search for the key_wallet::Error definition and its variants
rg "enum Error|struct Error" --type rs -A 20 | grep -A 20 "key_wallet"

Repository: dashpay/rust-dashcore

Length of output: 91


🏁 Script executed:

# Check for feature gating related to Slip10 and BLS
rg "Slip10|BLS" --type rs -B 2 -A 2

Repository: dashpay/rust-dashcore

Length of output: 91


🏁 Script executed:

# Read broader context to see the full match statement
cat -n key-wallet-manager/src/error.rs | sed -n '50,105p'

Repository: dashpay/rust-dashcore

Length of output: 3017


🏁 Script executed:

# Search for key_wallet::Error definition with correct syntax
rg "key_wallet" -A 15 key-wallet-manager/src/error.rs

Repository: dashpay/rust-dashcore

Length of output: 981


🏁 Script executed:

# Look for the From implementation to understand the match structure
cat -n key-wallet-manager/src/error.rs | head -100

Repository: dashpay/rust-dashcore

Length of output: 4831


🏁 Script executed:

# Search for Slip10 and BLS to understand feature gating
rg "Slip10|BLS"

Repository: dashpay/rust-dashcore

Length of output: 50377


🏁 Script executed:

# Find the key_wallet error.rs file
find . -path "*key-wallet/src/error.rs" -type f

Repository: dashpay/rust-dashcore

Length of output: 90


🏁 Script executed:

# Read the full key_wallet::Error enum definition
cat -n key-wallet/src/error.rs | head -150

Repository: dashpay/rust-dashcore

Length of output: 4311


🏁 Script executed:

# Check for feature gates around Slip10 and BLS variants
grep -n "Slip10\|BLS\|#\[cfg" key-wallet/src/error.rs | head -50

Repository: dashpay/rust-dashcore

Length of output: 670


🏁 Script executed:

# Check for tests around the From impl or error conversion
grep -r "InvalidParameter.*Key wallet error" --include="*.rs"

Repository: dashpay/rust-dashcore

Length of output: 182


🏁 Script executed:

# Check if there are any explicit feature-gated tests for Slip10/BLS error conversion
grep -r "Slip10\|BLS" key-wallet-manager/ --include="*.rs" -A 2 -B 2

Repository: dashpay/rust-dashcore

Length of output: 47


Explicitly match all key_wallet::Error variants and remove the catch-all pattern.

The _ arm with #[allow(unreachable_patterns)] collapses feature-gated variants (Slip10, BLS) and any future upstream additions into InvalidParameter, causing callers and the FFI layer to lose the original error class. Replace the catch-all with explicit arms for Slip10 and BLS (guarded by their respective feature gates), allowing the compiler to enforce updates when key_wallet::Error changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-manager/src/error.rs` around lines 95 - 96, Replace the catch-all
`_` arm that maps any key_wallet::Error into WalletError::InvalidParameter (and
remove the #[allow(unreachable_patterns)] attribute) with explicit match arms
for each variant of key_wallet::Error so the original error types are preserved;
specifically add explicit arms for Slip10 and BLS (each behind their respective
feature gates) that convert to the appropriate WalletError variant instead of
collapsing to WalletError::InvalidParameter, so the compiler will force you to
handle new upstream variants of key_wallet::Error.

Copy link
Copy Markdown
Collaborator

@xdustinface xdustinface left a comment

Choose a reason for hiding this comment

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

I looked into the Cargo feature unification concern that motivates this split. A few observations:

Feature unification is real but more nuanced than described. Cargo unions features per-crate across a single build invocation. However, with resolver = "2" (which we already use), platform-specific dependencies are resolved per-target — they don't unify across targets.

The split doesn't fully solve the WASM problem either. If a WASM consumer lives in the same workspace alongside dash-spv (which pulls in key-wallet-manager → tokio), then cargo check --workspace will still try to compile tokio for WASM. The split only helps if the WASM crate is built with cargo build -p wasm-crate separately, or lives in a different workspace.

What actually solves it: target-gating tokio. This works whether the crates are split or merged:

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1", features = ["macros", "rt", "sync"] }

With resolver 2, this is compiler-enforced — WASM builds simply won't see tokio, even in --workspace builds. No feature discipline required.

Trade-off to consider: The split sacrifices our ability to tighten key-wallet's API surface. Types like AccountTypePreference, TransactionRouter, and AccountCollection's 17 public fields must stay pub for cross-crate access, when they could be pub(crate) if the manager lived inside key-wallet. We discussed this recently and it was a meaningful win.

The split still has value (lighter dep tree for consumers who don't need the manager), but the WASM/tokio argument specifically is better addressed with target-gating regardless of crate structure. Wanted to flag this before we commit to the direction.

@thepastaclaw
Copy link
Copy Markdown
Contributor

Updating my position here after dustinface's comment.

No, I wasn't aware tokio removal was planned when I weighed in. That's significant context that changes the trade-off.

My original argument was mostly about the structural guarantee — crate boundaries preventing tokio from leaking into lightweight/WASM consumers. If tokio is being removed from the picture entirely, that protection becomes theoretical rather than practical. You're defending against a dep that won't exist.

What's left in the "pro-split" column:

  • Lighter compile for consumers who don't need the manager
  • No feature discipline needed

What's left in the "pro-merged" column:

  • pub(crate) on types like AccountTypePreference, TransactionRouter, AccountCollection's 17 fields — that's a real API surface concern, not a nice-to-have
  • CI flag combination testing addresses the fragility concern (the 5edf71988eacdff92f114 chain happened because flag combos weren't tested, not because flags are fundamentally broken)

Revised take: with tokio removal planned and CI hardening for flag combos, keeping the manager inside key-wallet with proper target-gating is the better trade-off. The pub(crate) wins are concrete and immediate; the split's benefits are now mostly theoretical.

I got this one wrong. The tie-break should go the other way.

@thepastaclaw
Copy link
Copy Markdown
Contributor

Fair points, and to answer directly: no, I wasn't aware tokio removal from key-wallet-manager was planned. That does shift the weight of the argument.

A few thoughts on the specifics:

On target-gating: Technically correct that cfg(not(target_arch = "wasm32")) with resolver 2 would prevent tokio from compiling for WASM targets. But this is a defensive measure that requires someone to remember to gate correctly every time a new async dependency gets added. Crate boundaries make the wrong thing impossible rather than merely avoidable.

On pub(crate): This is the strongest counterargument. Widening the visibility of AccountTypePreference, TransactionRouter, AccountCollection fields etc. from pub(crate) to pub is a real API surface cost. Not fatal, but real.

On "the flag was fragile because it wasn't done right": That's kind of the point though — feature flags are easy to get wrong, and the fact that it took three commits to sort out (5edf71988eacdff92f114) illustrates the failure mode. CI flag combination testing would help, but that's additional infrastructure to build and maintain for a problem that crate boundaries solve structurally.

Where I land with the new info: If tokio is genuinely going away from the manager entirely, then the core motivation (preventing tokio from leaking into lightweight consumers) becomes moot. The remaining argument is "crate boundaries prevent future dependency creep" — which is real but speculative, vs the pub(crate) cost which is concrete and present.

I'm less confident in the split than I was. If the team has conviction that tokio removal is happening soon and the flag testing CI gets built, I could see the merged approach being the right call. But I'd want both of those to actually land, not just be planned.

@xdustinface
Copy link
Copy Markdown
Collaborator

This is not a pure move. I come up with a PR soon.

@github-actions github-actions bot added the merge-conflict The PR conflicts with the target branch. label Mar 30, 2026
@github-actions
Copy link
Copy Markdown

This PR has merge conflicts with the base branch. Please rebase or merge the base branch into your branch to resolve them.

@QuantumExplorer
Copy link
Copy Markdown
Member Author

Superseded by #599, which is rebased on top of #594.

QuantumExplorer added a commit that referenced this pull request Mar 30, 2026
This was removed in #592 but accidentally reintroduced when
cherry-picking from the old PR #590 branch. Hardcoded regtest
peers should not exist — peers are configured explicitly by
the caller via add_peer() or the FFI config functions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
QuantumExplorer added a commit that referenced this pull request Mar 30, 2026
These files were accidentally modified when cherry-picking from the
old branch:
- dash-spv/tests/dashd_sync/setup.rs: unnecessary peer clearing
- dash-spv/Cargo.toml: dropped key-wallet-manager/test-utils feature
- fuzz/Cargo.toml: unrelated dep change

Reverted to match v0.42-dev.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-conflict The PR conflicts with the target branch.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants