Skip to content

Conversation

@QuantumExplorer
Copy link
Member

@QuantumExplorer QuantumExplorer commented Jun 12, 2025

Summary by CodeRabbit

  • New Features

    • Introduced the key-wallet Rust library for Dash key derivation and wallet management, supporting BIP32/BIP39, mnemonic handling, HD wallets, and Dash-specific derivation paths.
    • Added key-wallet-ffi crate providing FFI bindings for cross-language usage (Swift, Kotlin, Python, Ruby) with extensive API coverage.
    • Added dash-network crate centralizing Dash network type definitions, magic bytes, and protocol version activation info.
    • Implemented Dash P2PKH and P2SH address generation, parsing, and scriptPubKey creation with network inference.
    • Provided build scripts and tooling for FFI scaffolding generation and iOS universal library builds.
    • Re-exported key modules and types via public interfaces for improved modularity.
  • Bug Fixes

    • Replaced catch-all network match arms with explicit panics or unreachable calls to prevent silent misconfigurations.
    • Updated address parsing to infer network from version byte, improving correctness and backward compatibility.
    • Fixed FFI enum stability and error handling for network and base58 errors.
  • Refactor

    • Modularized code with explicit trait implementations and serialization logic for key types.
    • Externalized Network type to dash-network crate and extended it via traits for Dash-specific functionality.
    • Replaced local module implementations with public re-exports from key-wallet crate.
    • Consolidated imports and added default match arms for network variants to enhance robustness.
    • Updated dependencies and features to integrate new crates and unify serialization.
  • Tests

    • Added extensive unit tests covering mnemonic, BIP32, derivation, address generation, and FFI bindings.
    • Included tests verifying network compatibility, error cases, and serialization round-trips.
  • Documentation

    • Added comprehensive README files and API references for key-wallet, key-wallet-ffi, and dash-network crates.
    • Added detailed project documentation for AI-assisted Test-Driven Development workflow.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 12, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

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

Walkthrough

This update introduces the new key-wallet crate, providing standalone hierarchical deterministic wallet functionality for Dash, including BIP32, BIP39, and DIP9 derivations. The Dash library is refactored to use key-wallet for key management, replacing local modules. An FFI crate (key-wallet-ffi) is added, exposing key-wallet features to other languages via UniFFI. Comprehensive tests, documentation, and examples are included.

Changes

File(s) / Path(s) Change Summary
Cargo.toml Added key-wallet, key-wallet-ffi, dash-network, and dash-network-ffi to workspace members.
dash/Cargo.toml Added key-wallet and dash-network as dependencies; updated features; removed bip32 example.
dash/examples/bip32.rs Deleted BIP32 example.
dash/src/lib.rs Replaced local bip32 and dip9 modules with public re-exports from key-wallet; replaced local Network with dash-network::Network.
dash/src/psbt/map/global.rs Changed fingerprint serialization from .as_bytes() to .to_bytes().
dash/src/psbt/mod.rs Explicitly construct PrivateKey from ExtendedPrivKey; simplified GetKeyError derives; updated tests to use key_wallet::Network.
dash/tests/psbt.rs Updated key derivation and network usage to use key_wallet types.
dash/src/address.rs Changed Network import to dash_network::Network; added fallback arms in match expressions for address formatting and validation.
dash/src/blockdata/constants.rs Changed Network import to dash_network::Network; added catch-all arm in genesis_block for unknown networks.
dash/src/blockdata/transaction/mod.rs Changed test import of Network to dash_network::Network.
dash/src/blockdata/transaction/outpoint.rs Updated example import to use dashcore::Network.
dash/src/consensus/params.rs Changed Network import to dash_network::Network; added catch-all arm in Params::new for unknown networks.
dash/src/crypto/key.rs Changed Network import to dash_network::Network; added wildcard arm in WIF prefix match.
dash/src/crypto/sighash.rs Changed test import of Network to dash_network::Network.
dash/src/network/constants.rs Removed local Network enum and methods; imported Network from dash_network; added NetworkExt trait with known_genesis_block_hash method.
dash/src/sml/llmq_type/mod.rs Made network module public; changed Network import to dash_network::Network.
dash/src/sml/llmq_type/network.rs Replaced inherent Network methods with NetworkLLMQExt trait; added wildcard arms in match expressions; changed import to dash_network::Network.
dash/src/sml/masternode_list/from_diff.rs Changed Network import to dash_network::Network; added missing imports for BlockHash and NetworkExt.
dash/src/sml/masternode_list/quorum_helpers.rs Added import for NetworkLLMQExt.
dash/src/sml/masternode_list/scores_for_quorum.rs Added import for NetworkLLMQExt; used platform_type() method on network.
dash/src/sml/masternode_list_engine/message_request_verification.rs Added import for NetworkLLMQExt in main and test modules.
dash/src/sml/masternode_list_engine/mod.rs Added imports for NetworkExt, NetworkLLMQExt, and dash_network::Network; removed local Network import.
dash/examples/handshake.rs Consolidated imports of Network and secp256k1 from dashcore; replaced constants::Network::Dash.magic() with Network::Dash.magic().
key-wallet/Cargo.toml New crate manifest for key-wallet crate with dependencies and features for Dash wallet functionality.
key-wallet/README.md Added README describing key-wallet crate features and usage.
key-wallet/examples/basic_usage.rs Added example demonstrating mnemonic creation, wallet derivation, address generation, and parsing.
key-wallet/src/address.rs Implemented Dash address types (P2PKH, P2SH), encoding/decoding, script pubkey generation, and address generator.
key-wallet/src/bip32.rs Implemented BIP32 key types, serialization, error handling, and conversions; replaced macros with explicit structs.
key-wallet/src/derivation.rs Added HD wallet struct and key derivation logic including BIP44, CoinJoin, and identity keys; account address derivation.
key-wallet/src/dip9.rs Changed Network import to dash_network::Network.
key-wallet/src/error.rs Added unified error enum and result type for key-wallet operations.
key-wallet/src/lib.rs Main library module declaring submodules and re-exporting key types; imports Network from dash_network.
key-wallet/src/mnemonic.rs Added BIP39 mnemonic support with multiple languages, generation, validation, and seed derivation.
key-wallet/src/test_macros.rs Added serde_round_trip macro for serialization testing.
key-wallet/summary.md Added summary file describing the crate purpose.
key-wallet/tests/address_tests.rs Added comprehensive unit tests for address creation, parsing, and generation.
key-wallet/tests/bip32_tests.rs Added unit tests for extended key derivation, serialization, and fingerprinting.
key-wallet/tests/derivation_tests.rs Added tests for HD wallet creation, account derivation, and public key consistency.
key-wallet/tests/mnemonic_tests.rs Added tests for mnemonic validation, generation, and conversion to seed and extended keys.
key-wallet-ffi/Cargo.toml New crate manifest for key-wallet-ffi FFI bindings with dependencies and build configuration.
key-wallet-ffi/README.md Added README documenting FFI usage, supported languages, API, and examples.
key-wallet-ffi/build.rs Added build script generating UniFFI scaffolding from UDL file.
key-wallet-ffi/src/key_wallet.udl Added UniFFI interface definition for key-wallet FFI exposing wallet, mnemonic, keys, addresses, and errors.
key-wallet-ffi/src/lib.rs Implemented FFI bindings for key-wallet with error mapping, type wrappers, and conversion logic.
key-wallet-ffi/tests/ffi_tests.rs Added test verifying presence of FFI types and initialization.
key-wallet-ffi/src/lib_tests.rs Added comprehensive internal tests for FFI bindings covering mnemonic, HDWallet, address, and error handling.
key-wallet/src/utils.rs Added utility functions for hex digit and hex string parsing with tests.
dash-network/Cargo.toml New crate manifest for dash-network crate defining Dash network types and features.
dash-network/README.md Added README describing dash-network crate features and usage.
dash-network/src/lib.rs Added Network enum with magic bytes, core activation height, and string parsing; includes tests.
dash-network-ffi/Cargo.toml New crate manifest for dash-network-ffi FFI bindings with dependencies and build configuration.
dash-network-ffi/README.md Added README documenting dash-network-ffi usage, API, and examples.
dash-network-ffi/build.rs Added build script generating UniFFI scaffolding from UDL file.
dash-network-ffi/src/dash_network.udl Added UniFFI interface definition for Dash network FFI exposing network info and errors.
dash-network-ffi/src/lib.rs Implemented FFI bindings for Dash network with conversions, error handling, and tests.
dash-network-ffi/uniffi-bindgen.rs Added minimal executable to invoke UniFFI bindgen tool.
key-wallet-ffi/build-ios.sh Added build script for compiling key-wallet-ffi for iOS targets and generating Swift bindings.
.gitignore Extended ignore rules for AI assistant files, editor backups, and build artifacts.
CLAUDE.md Added detailed documentation on SPARC development environment and AI-assisted TDD workflow.
NETWORK_HANDLING_FIXES.md Added documentation summarizing fixes to network handling, including explicit panics on unknown networks and FFI enum stability.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant FFI (key-wallet-ffi)
    participant KeyWallet
    participant Dash

    User->>FFI (key-wallet-ffi): Call (e.g., create_mnemonic, derive_address)
    FFI (key-wallet-ffi)->>KeyWallet: Invoke Rust key-wallet API
    KeyWallet-->>FFI (key-wallet-ffi): Return result or error
    FFI (key-wallet-ffi)-->>User: Return value in target language

    User->>Dash: Use Dash API (e.g., derive key)
    Dash->>KeyWallet: Call re-exported bip32/dip9/address functions
    KeyWallet-->>Dash: Return key/address
    Dash-->>User: Return result
Loading

Possibly related PRs

  • feat: identity derivation path changes #48: Modifies identity-related derivation path methods within the local dash/src/bip32.rs. This is directly related, as the current PR removes/replaces the local bip32 module with key-wallet::bip32, superseding or integrating such changes.

  • Add DIP 14 derivation and DIP 9 known derivation Paths. #39: Adds DIP 14 and DIP 9 derivation paths in local dash/src/bip32.rs and dash/src/dip9.rs. Related because the current PR replaces these local modules with key-wallet crate versions.

Poem

In fields of code where Dash keys sprout,
A wallet emerges—no shadow of doubt.
Mnemonics and paths, now modular and neat,
With FFI bridges for languages to greet.
Addresses bloom, derivations align,
Rabbits rejoice—this wallet’s divine!
🐇✨


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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
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: 15

♻️ Duplicate comments (2)
dash/src/psbt/mod.rs (1)

560-569: Same conversion duplication here

Apply the From<key_wallet::Network> for Network refactor above to keep these two implementations in sync and avoid future drift.

key-wallet/src/mnemonic.rs (1)

122-124: Validation shares the same language-mapping pitfall

validate() relies on the broken From<Language> implementation and will wrongly accept/deny non-English phrases.

Fixing the mapping above will resolve this as well.

🧹 Nitpick comments (23)
key-wallet/summary.md (1)

1-2: Heading ends with a colon – drop it to satisfy markdownlint and conventional style
The MD026 hint is correct: headings should not trail punctuation. Simply remove the colon; the prose that follows can explain the bullet-list or sentence.

-# Summary of changes made to make bip32.rs work as a standalone crate:
+# Summary of changes made to make `bip32.rs` work as a standalone crate
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

1-1: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)

dash/src/psbt/map/global.rs (1)

58-62: Prefer extend_from_slice(&fingerprint.to_bytes()) for clarity/alloc hygiene
Vec<u8>::extend over an array consumes an iterator, creating an intermediate. A slice avoid that micro-overhead and is clearer.

-                    ret.extend(fingerprint.to_bytes());
+                    ret.extend_from_slice(&fingerprint.to_bytes());
key-wallet/src/test_macros.rs (1)

6-10: Drop the redundant use serde_json; inside the macro.

The item import is superfluous because the crate is already accessible with the absolute path. Removing it trims one statement from every macro expansion.

-        use serde_json;
-
-        let encoded = serde_json::to_value(&$var).unwrap();
+        let encoded = serde_json::to_value(&$var).unwrap();
key-wallet/README.md (1)

18-38: Docs compile-fail as shown.

The snippet uses ? in main-style code without returning a Result, so cargo test --doc will fail. Wrap it in fn main() -> anyhow::Result<()> { … } or add # fn main() -> Result<(), Box<dyn std::error::Error>> { … # Ok(()) # } guards to keep doctests green.

dash/src/psbt/mod.rs (1)

618-619: Typo in error message – “bip23” instead of “bip32”

-            Bip32(ref e) => write_err!(f, "a bip23 error"; e),
+            Bip32(ref e) => write_err!(f, "a bip32 error"; e),

Minor, but leaks into user-visible Display output.
Fix before publishing a release.

key-wallet-ffi/Cargo.toml (1)

18-24: Duplicate uniffi dependency declarations inflate build time

uniffi is declared three times (dependencies, build-dependencies, dev-dependencies).
If the versions/features ever diverge the crate will compile UniFFI thrice.

[workspace.dependencies]
uniffi = { version = "0.27", features = ["cli", "build", "bindgen-tests"] }

Then in this manifest just add uniffi = { workspace = true } to the three dependency sections.

key-wallet/tests/address_tests.rs (2)

24-27: Prefix-based assertions are fragile

assert!(addr_str.starts_with('X')); hard-codes the main-net prefix character.
If Dash ever changes its Base58 version byte (unlikely but possible in forks/devnets) the test fails for reasons unrelated to address logic.
Compare the actual version byte instead:

let version = base58ck::decode_check(&addr_str).unwrap()[0];
assert_eq!(version, Network::Dash.p2pkh_version());

88-93: Magic indices make the script-shape test brittle

Using fixed indices ([23], [24]) hides intent.
Recommend explicit slice pattern matching:

assert_eq!(
    &script_pubkey[..],
    &[
        0x76, 0xa9, 0x14,                // DUP HASH160 push20
        &address.hash[..],               // 20-byte hash
        0x88, 0xac                       // EQUALVERIFY CHECKSIG
    ]
    .concat()
);

Improves readability and prevents off-by-one errors if the script format changes.

key-wallet/tests/mnemonic_tests.rs (2)

3-5: Remove unused import to silence warnings

ExtendedPrivKey is imported but never used in this module – this will trigger a compiler warning (or a build failure if #![deny(warnings)] is enabled).

-use key_wallet::{ExtendedPrivKey, Network};
+use key_wallet::Network;

54-64: #[ignore] hides the most important happy-path test

The generation path is disabled to avoid the getrandom requirement at test-time.
Consider:

  1. Moving this test behind a separate cargo feature, e.g. use-entropy, so CI can still run it.
  2. Or replace the entropy source with a deterministic stub (rand_chacha::ChaCha20Rng::seed_from_u64(0)).

This keeps coverage while remaining deterministic.

key-wallet/tests/bip32_tests.rs (1)

26-38: Add negative test for 256-bit hex indices

ChildNumber::from_str has rather complex hex-parsing logic.
Including an invalid 256-bit component such as m/0xFF/1 would exercise this branch and catch regressions.

key-wallet/examples/basic_usage.rs (1)

14-19: Hard-coding the well-known BIP-39 test vector in examples invites copy-paste into production

Developers occasionally re-use example code verbatim.
Consider adding a conspicuous comment or generating a random mnemonic instead to avoid accidental fund loss.

key-wallet/src/error.rs (1)

24-27: Base58 variant discards the underlying cause

Returning just Base58 loses error context (checksum mismatch vs. invalid char, etc.).
Expose the original base58::Error (or at least its Display) to aid debugging.

-Base58,
+Base58(String),

and convert via Error::Base58(e.to_string()).

key-wallet/Cargo.toml (2)

11-18: getrandom pulled in unconditionally by the std feature

Because std is part of default, the getrandom crate is always compiled.
If the intention was to keep entropy generation optional (for deterministic builds or WASM), consider moving getrandom behind its own feature and keep std focused on allocation/std-io only.


1-9: Specify MSRV with rust-version

Add an explicit rust-version key (e.g. rust-version = "1.74").
This lets cargo refuse to compile on older compilers and documents the MSRV policy.

key-wallet/src/derivation.rs (1)

26-41: Method call relies on name collision – use UFCS for clarity

Inside impl KeyDerivation for ExtendedPrivKey, calling self.derive_priv depends on inherent-vs-trait resolution rules.
Use fully-qualified syntax to make intent explicit and future-proof:

bitcoin::bip32::ExtendedPrivKey::derive_priv(self, secp, path)

(or import the type alias).

key-wallet-ffi/src/lib.rs (1)

16-18: Consider documenting the initialization function's purpose

The initialize() function is empty. If no global initialization is needed, consider adding a comment explaining this, or remove the function if it's not required by the FFI interface.

-pub fn initialize() {
-    // Any global initialization if needed
-}
+/// Initialize the key-wallet FFI library.
+/// Currently no initialization is required, but this function is kept for future compatibility.
+pub fn initialize() {
+    // No initialization required at this time
+}
key-wallet-ffi/README.md (6)

1-4: Refine README title to match crate naming convention.

The title currently uses “Key Wallet FFI”—consider using the exact crate name key-wallet-ffi in backticks for clarity and consistency with other Rust README files (e.g., # key-wallet-ffi FFI Bindings).


30-39: Clean up trailing whitespace and align code block comments.

The Bash snippets under “Generate bindings” have inconsistent trailing spaces (e.g., line 35). Removing extra spaces and ensuring uniform indentation improves Markdown rendering and avoids lint warnings.


56-74: Add a Ruby usage example to match supported languages.

You list Ruby under “Supported Languages,” but there’s no Ruby snippet. Adding a minimal Ruby example showing mnemonic creation and wallet usage will align documentation with the feature list.


95-111: Include Python error-handling example.

The Python snippet demonstrates basic calls, but callers need to catch exceptions from FFI (e.g., InvalidMnemonic, InvalidDerivationPath). Consider showing a try/except block to handle these error variants.


115-122: Ensure proper Markdown list formatting under “Core Types.”

The bullets under Core Types lack a blank line after the heading, which can trigger Markdown lint warnings. Add an empty line between the heading and the list for better readability:

-### Core Types
-- `Mnemonic`: BIP39 mnemonic phrase handling
+### Core Types
+
+- `Mnemonic`: BIP39 mnemonic phrase handling
🧰 Tools
🪛 LanguageTool

[uncategorized] ~117-~117: Loose punctuation mark.
Context: ... Reference ### Core Types - Mnemonic: BIP39 mnemonic phrase handling - `HDWal...

(UNLIKELY_OPENING_PUNCTUATION)


123-128: Apply consistent list formatting in the “Enums” section.

Similarly, insert a blank line after Enums before listing the enum items to adhere to Markdown best practices and eliminate loose punctuation warnings:

-### Enums
- - `Network`: Dash, Testnet, Regtest, Devnet
+### Enums
+
+- `Network`: Dash, Testnet, Regtest, Devnet
🧰 Tools
🪛 LanguageTool

[uncategorized] ~125-~125: Loose punctuation mark.
Context: ...dress generation ### Enums - Network: Dash, Testnet, Regtest, Devnet - `Langu...

(UNLIKELY_OPENING_PUNCTUATION)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51df58f and 2348b2c.

📒 Files selected for processing (29)
  • Cargo.toml (1 hunks)
  • dash/Cargo.toml (4 hunks)
  • dash/examples/bip32.rs (0 hunks)
  • dash/src/lib.rs (1 hunks)
  • dash/src/psbt/map/global.rs (1 hunks)
  • dash/src/psbt/mod.rs (5 hunks)
  • dash/tests/psbt.rs (2 hunks)
  • key-wallet-ffi/Cargo.toml (1 hunks)
  • key-wallet-ffi/README.md (1 hunks)
  • key-wallet-ffi/build.rs (1 hunks)
  • key-wallet-ffi/src/key_wallet.udl (1 hunks)
  • key-wallet-ffi/src/lib.rs (1 hunks)
  • key-wallet-ffi/tests/ffi_tests.rs (1 hunks)
  • key-wallet/Cargo.toml (1 hunks)
  • key-wallet/README.md (1 hunks)
  • key-wallet/examples/basic_usage.rs (1 hunks)
  • key-wallet/src/address.rs (1 hunks)
  • key-wallet/src/bip32.rs (19 hunks)
  • key-wallet/src/derivation.rs (1 hunks)
  • key-wallet/src/dip9.rs (1 hunks)
  • key-wallet/src/error.rs (1 hunks)
  • key-wallet/src/lib.rs (1 hunks)
  • key-wallet/src/mnemonic.rs (1 hunks)
  • key-wallet/src/test_macros.rs (1 hunks)
  • key-wallet/summary.md (1 hunks)
  • key-wallet/tests/address_tests.rs (1 hunks)
  • key-wallet/tests/bip32_tests.rs (1 hunks)
  • key-wallet/tests/derivation_tests.rs (1 hunks)
  • key-wallet/tests/mnemonic_tests.rs (1 hunks)
💤 Files with no reviewable changes (1)
  • dash/examples/bip32.rs
🧰 Additional context used
🧬 Code Graph Analysis (8)
dash/src/psbt/map/global.rs (1)
key-wallet/src/bip32.rs (2)
  • fingerprint (1596-1598)
  • fingerprint (1884-1886)
dash/tests/psbt.rs (1)
key-wallet/src/bip32.rs (1)
  • new_master (1334-1347)
key-wallet/tests/address_tests.rs (4)
key-wallet/src/address.rs (7)
  • new (167-171)
  • p2pkh (69-76)
  • p2sh (79-85)
  • test_address_parsing (227-234)
  • from_str (102-127)
  • to_string (88-99)
  • script_pubkey (130-151)
key-wallet/src/derivation.rs (3)
  • new (52-57)
  • new (145-150)
  • from_seed (60-63)
key-wallet/src/bip32.rs (15)
  • from_str (281-315)
  • from_str (740-796)
  • from_str (1098-1107)
  • from_str (1898-1901)
  • from_str (1913-1916)
  • len (1141-1143)
  • from (78-80)
  • from (245-247)
  • from (658-668)
  • from (672-687)
  • from (1055-1057)
  • from (1061-1063)
  • from (1067-1069)
  • from (1321-1323)
  • from_priv (1612-1624)
key-wallet/src/error.rs (2)
  • from (59-61)
  • from (65-67)
key-wallet/tests/derivation_tests.rs (6)
key-wallet-ffi/src/lib.rs (4)
  • from_seed (251-257)
  • from_phrase (146-152)
  • new (138-144)
  • new (369-373)
key-wallet/src/derivation.rs (5)
  • from_seed (60-63)
  • master_key (66-68)
  • coinjoin_account (102-114)
  • new (52-57)
  • new (145-150)
key-wallet/src/mnemonic.rs (2)
  • from_phrase (85-92)
  • from_str (130-133)
key-wallet/src/address.rs (2)
  • from_str (102-127)
  • new (167-171)
key-wallet/src/bip32.rs (5)
  • from_str (281-315)
  • from_str (740-796)
  • from_str (1098-1107)
  • from_str (1898-1901)
  • from_str (1913-1916)
key-wallet/tests/bip32_tests.rs (1)
  • test_public_key_derivation (57-71)
key-wallet/tests/mnemonic_tests.rs (5)
key-wallet/src/mnemonic.rs (6)
  • validate (122-124)
  • phrase (95-97)
  • from_phrase (85-92)
  • word_count (100-102)
  • generate (44-67)
  • generate (71-82)
key-wallet-ffi/src/lib.rs (1)
  • from_phrase (146-152)
key-wallet/src/bip32.rs (1)
  • len (1141-1143)
dash/src/address.rs (1)
  • network (834-836)
dash/src/taproot.rs (1)
  • depth (1052-1055)
key-wallet/examples/basic_usage.rs (6)
key-wallet/src/mnemonic.rs (5)
  • from_phrase (85-92)
  • phrase (95-97)
  • word_count (100-102)
  • from_str (130-133)
  • from (27-33)
key-wallet/src/bip32.rs (17)
  • encode (1430-1436)
  • encode (1716-1722)
  • from_str (281-315)
  • from_str (740-796)
  • from_str (1098-1107)
  • from_str (1898-1901)
  • from_str (1913-1916)
  • from (78-80)
  • from (245-247)
  • from (658-668)
  • from (672-687)
  • from (1055-1057)
  • from (1061-1063)
  • from (1067-1069)
  • from (1321-1323)
  • from_hardened_idx (525-533)
  • len (1141-1143)
key-wallet/src/derivation.rs (4)
  • from_seed (60-63)
  • new (52-57)
  • new (145-150)
  • coinjoin_account (102-114)
key-wallet/src/address.rs (3)
  • new (167-171)
  • p2pkh (69-76)
  • from_str (102-127)
key-wallet/src/error.rs (2)
  • from (59-61)
  • from (65-67)
key-wallet/src/dip9.rs (1)
  • from (64-66)
key-wallet/tests/bip32_tests.rs (3)
key-wallet/src/bip32.rs (11)
  • master (1156-1158)
  • new_master (1334-1347)
  • child (1167-1171)
  • from_hardened_idx (525-533)
  • from_str (281-315)
  • from_str (740-796)
  • from_str (1098-1107)
  • from_str (1898-1901)
  • from_str (1913-1916)
  • from_priv (1612-1624)
  • from_normal_idx (511-519)
key-wallet/src/mnemonic.rs (1)
  • from_str (130-133)
key-wallet/tests/derivation_tests.rs (1)
  • test_public_key_derivation (96-109)
key-wallet/src/bip32.rs (5)
key-wallet-ffi/src/lib.rs (7)
  • derive (276-283)
  • from (30-37)
  • from (53-64)
  • from (74-79)
  • from (108-129)
  • to_string (234-242)
  • to_string (341-343)
key-wallet/src/derivation.rs (3)
  • derive (76-78)
  • format (154-155)
  • format (162-163)
key-wallet/src/error.rs (3)
  • from (59-61)
  • from (65-67)
  • fmt (33-44)
key-wallet/src/address.rs (4)
  • fmt (155-157)
  • from_str (102-127)
  • format (196-197)
  • to_string (88-99)
dash/src/base58.rs (2)
  • encode_check (388-391)
  • decode_check (308-328)
🪛 markdownlint-cli2 (0.17.2)
key-wallet/summary.md

1-1: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)

🪛 LanguageTool
key-wallet-ffi/README.md

[uncategorized] ~117-~117: Loose punctuation mark.
Context: ... Reference ### Core Types - Mnemonic: BIP39 mnemonic phrase handling - `HDWal...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~125-~125: Loose punctuation mark.
Context: ...dress generation ### Enums - Network: Dash, Testnet, Regtest, Devnet - `Langu...

(UNLIKELY_OPENING_PUNCTUATION)

⏰ Context from checks skipped due to timeout of 90000ms (15)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: fuzz (hashes_sha512)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (dash_outpoint_string)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (dash_deser_net_msg)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (dash_deserialize_amount)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: fuzz (dash_deserialize_witness)
🔇 Additional comments (12)
Cargo.toml (1)

1-3: Workspace addition looks correct – verify cargo xtask / CI scripts pick it up
Nothing blocking, but double-check any custom tooling that assumes a fixed member list.

key-wallet/src/dip9.rs (1)

25-27: Import path update LGTM
Network lives under address, and this avoids cyclic deps. No further action.

dash/Cargo.toml (1)

18-19: Edition 2024 is not yet stabilised.

Until the Rust 2024 edition is released on stable, crates published with edition = "2024" will fail to compile for the vast majority of users. Consider downgrading to 2021 (or gating the value on a nightly toolchain).

-edition = "2024"
+edition = "2021"
dash/tests/psbt.rs (1)

134-135: Seed size sanity-check.

ExtendedPrivKey::new_master follows BIP-32, which assumes a seed (normally 64 bytes from BIP-39), not a raw secret key. Passing secret_bytes() (32 bytes) happens to work for the specific test vector, but other vectors may diverge. Ensure this is intentional and documented, or use a full 64-byte seed for generality.

key-wallet/examples/basic_usage.rs (1)

88-97: Inconsistent path and is_change flag

is_change = true implies change chain, yet the derivation path supplied (44'/5'/0') points to the account node, not the external/change branch (…/0 or …/1).
Either:

  1. Append /1 before feeding the path to generate_range, or
  2. Pass false for receive addresses.

Clarifying this keeps the example aligned with BIP-44 semantics.

key-wallet/src/lib.rs (1)

14-17: Missing test_macros.rs will break cargo test

#[cfg(test)] #[macro_use] mod test_macros; expects a sibling test_macros.rs.
If that file is not committed, tests won’t compile.

key-wallet-ffi/src/lib.rs (1)

107-130: LGTM! Comprehensive error mapping with forward compatibility

The error conversion implementation properly maps all specific error types and includes a catch-all pattern for future error variants.

key-wallet-ffi/src/key_wallet.udl (1)

1-158: Well-structured UniFFI interface definition

The UDL file properly defines all interfaces, enums, and error types matching the Rust implementation. The use of named constructors and error annotations is correct.

key-wallet/src/bip32.rs (3)

707-735: LGTM! Clear formatting for 256-bit child numbers

The Display implementation correctly formats 256-bit child numbers with the 0x prefix for clarity.


1150-1154: LGTM! Useful method for building derivation paths

The push method is a valuable addition that enables incremental construction of derivation paths.


1083-1083: Good improvement for no_std compatibility

Using core::slice::Iter instead of std::slice::Iter improves compatibility with no_std environments.

key-wallet-ffi/README.md (1)

43-52:

❓ Verification inconclusive

Verify Android build command correctness and platform support.

The cargo ndk invocation might require flags like --platform or a different syntax depending on the cargo-ndk version. Also consider documenting Linux/Windows build steps if users target those OSes.


🏁 Script executed:

#!/bin/bash
# Skip cloning: only checking installed cargo-ndk syntax
cargo ndk --help

Length of output: 265


Verify Android build command syntax and expand OS build instructions

Please confirm that the cargo ndk invocation in your README matches the syntax required by the installed cargo-ndk version (e.g., you may need a --platform <API_LEVEL> flag or updated subcommands). Additionally, consider adding brief build instructions for Linux and Windows targets to cover all supported platforms.

• Ensure the Android build line works as intended:

cargo ndk -t arm64-v8a -t armeabi-v7a -t x86_64 -t x86 --platform <API_LEVEL> -o ./jniLibs build --release

• Add examples for:

  • Linux: cargo build --release --target x86_64-unknown-linux-gnu
  • Windows: cargo build --release --target x86_64-pc-windows-msvc

Comment on lines 330 to 339
let derived_priv = PrivateKey {
compressed: true,
network: match ext_derived.network {
key_wallet::Network::Dash => Network::Dash,
key_wallet::Network::Testnet => Network::Testnet,
key_wallet::Network::Regtest => Network::Regtest,
key_wallet::Network::Devnet => Network::Devnet,
},
inner: ext_derived.private_key,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid hand-rolled network mapping – implement From once.

Pattern-matching every time a conversion is needed is brittle and error-prone. Add a simple From impl (or TryFrom if ever fallible) inside dashcore and use ext_derived.network.into() here.

-let derived_priv = PrivateKey {
-    compressed: true,
-    network: match ext_derived.network {
-        key_wallet::Network::Dash => Network::Dash,
-        key_wallet::Network::Testnet => Network::Testnet,
-        key_wallet::Network::Regtest => Network::Regtest,
-        key_wallet::Network::Devnet => Network::Devnet,
-    },
-    inner: ext_derived.private_key,
-};
+let derived_priv = PrivateKey {
+    compressed: true,
+    network: ext_derived.network.into(),
+    inner: ext_derived.private_key,
+};

This keeps the test lean and centralises future maintenance to a single location.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In dash/tests/psbt.rs around lines 330 to 339, the network conversion is done
via manual pattern matching, which is error-prone. To fix this, implement a From
trait for converting key_wallet::Network to Network inside the dashcore module,
then replace the match expression with a simple call to
ext_derived.network.into() to centralize and simplify the conversion logic.

Comment on lines 3 to 49
/* Temporarily disabled due to uniffi build issues
use key_wallet_ffi::{Mnemonic, Language, Network, HDWallet, AddressGenerator};
use std::sync::Arc;
#[test]
fn test_mnemonic_ffi() {
// Test mnemonic validation
let valid_phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string();
let is_valid = Mnemonic::validate(valid_phrase.clone(), Language::English).unwrap();
assert!(is_valid);
// Test creating from phrase
let mnemonic = Mnemonic::from_phrase(valid_phrase, Language::English).unwrap();
assert_eq!(mnemonic.get_word_count(), 12);
// Test seed generation
let seed = mnemonic.to_seed("".to_string());
assert_eq!(seed.len(), 64);
}
#[test]
fn test_hd_wallet_ffi() {
// Create wallet from seed
let seed = vec![0u8; 64];
let wallet = HDWallet::from_seed(seed, Network::Testnet).unwrap();
// Test deriving keys
let path = "m/44'/1'/0'/0/0".to_string();
let privkey = wallet.derive_priv_key(path.clone()).unwrap();
let pubkey = wallet.derive_pub_key(path).unwrap();
assert!(!privkey.is_empty());
assert!(!pubkey.is_empty());
}
#[test]
fn test_address_generator_ffi() {
let seed = vec![0u8; 64];
let wallet = Arc::new(HDWallet::from_seed(seed, Network::Testnet).unwrap());
let generator = AddressGenerator::new(wallet, 0, 0, false).unwrap();
// Test address generation
let addresses = generator.generate_addresses(5).unwrap();
assert_eq!(addresses.len(), 5);
}
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider #[ignore]/conditional compilation instead of commenting-out the real tests

Commenting-out the entire test suite disables all compile-time checking and bit-rots quickly.
Prefer one of:

#[cfg(feature = "uniffi")]
#[test]
fn test_mnemonic_ffi() { /* … */ }

#[test]
#[ignore = "UniFFI scaffolding not generated on CI yet"]
fn test_hd_wallet_ffi() { /* … */ }

This keeps the code in the build, catches syntax breakage early, and still lets CI pass while UniFFI integration is stabilised.
The placeholder test can then be dropped.

🤖 Prompt for AI Agents
In key-wallet-ffi/tests/ffi_tests.rs from lines 3 to 49, the entire test suite
is commented out, which disables compile-time checks and risks bit rot. Instead,
re-enable the tests by using conditional compilation attributes like
#[cfg(feature = "uniffi")] on tests that require UniFFI, and apply #[ignore]
with a descriptive reason on tests that should not run yet. This approach keeps
the tests in the build for syntax validation while allowing CI to pass, avoiding
the need for placeholder tests.

Copy link
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: 2

🔭 Outside diff range comments (1)
key-wallet/src/bip32.rs (1)

799-817: ⚠️ Potential issue

serde (de)serialisation loses 256-bit child numbers.

Serialising a ChildNumber via

u32::from(*self).serialize(serializer)

forces every variant into a u32. For Normal256/Hardened256 this collapses the real 256-bit index to u32::MAX; deserialisation cannot recover the original value.

At minimum this is data-loss; at worst it silently corrupts keys.

Suggested direction:

  1. Encode to/from the canonical string form used by Display/FromStr (e.g. "0x…"), or
  2. Add a dedicated struct with explicit fields for serde support.

Fail serialisation if the variant cannot be represented losslessly rather than mangling it.

♻️ Duplicate comments (1)
key-wallet/src/bip32.rs (1)

184-218: 🛠️ Refactor suggestion

Hex-decoding code is still duplicated – refactor into a shared helper.

The manual two-nibble dance appears both in ChainCode (lines 184-218) and Fingerprint (lines 286-314) and again later in ChildNumber::from_str (lines 752-773). The previous review already pointed this out. Consolidate it in one utility to avoid maintenance headaches and reduce attack surface for parser bugs.

Also applies to: 286-314

🧹 Nitpick comments (2)
key-wallet/src/bip32.rs (1)

748-773: Re-implementing ad-hoc hex parsing – use existing crates.

The manual byte-wise parsing in ChildNumber::from_str is error-prone and repeats logic already present elsewhere. Prefer bitcoin_hashes::hex::FromHex, hex crate, or the shared helper suggested earlier.

Cleaner, safer, and removes ~40 lines of brittle code.

key-wallet-ffi/src/lib_tests.rs (1)

60-66: Avoid brittle prefix assertions for address tests.

assert!(address_str.starts_with('y')) ties the test to the current testnet version byte.
If Dash changes its testnet P2PKH prefix (or you swap to another network), this unit test will break even though Address is correct.

Prefer property-based checks already available:

assert_eq!(address.get_network(), Network::Testnet);

or round-trip via Address::from_string as you do below.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2348b2c and d8f05ec.

📒 Files selected for processing (9)
  • dash/src/lib.rs (1 hunks)
  • dash/src/psbt/mod.rs (5 hunks)
  • dash/tests/psbt.rs (2 hunks)
  • key-wallet-ffi/Cargo.toml (1 hunks)
  • key-wallet-ffi/src/lib.rs (1 hunks)
  • key-wallet-ffi/src/lib_tests.rs (1 hunks)
  • key-wallet-ffi/tests/ffi_tests.rs (1 hunks)
  • key-wallet/src/bip32.rs (19 hunks)
  • key-wallet/src/test_macros.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • dash/tests/psbt.rs
  • key-wallet/src/test_macros.rs
  • key-wallet-ffi/tests/ffi_tests.rs
  • dash/src/lib.rs
  • dash/src/psbt/mod.rs
  • key-wallet-ffi/Cargo.toml
  • key-wallet-ffi/src/lib.rs
⏰ Context from checks skipped due to timeout of 90000ms (15)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (dash_outpoint_string)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (hashes_sha512)
  • GitHub Check: fuzz (dash_deser_net_msg)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (dash_deserialize_amount)
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: fuzz (hashes_ripemd160)

Comment on lines +272 to +276
impl fmt::Debug for Fingerprint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Fingerprint({}))", self)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Same formatting bug for Fingerprint::Debug.

Identical duplicated-parenthesis issue here.

-        write!(f, "Fingerprint({}))", self)
+        write!(f, "Fingerprint({})", self)
📝 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
impl fmt::Debug for Fingerprint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Fingerprint({}))", self)
}
}
impl fmt::Debug for Fingerprint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Fingerprint({})", self)
}
}
🤖 Prompt for AI Agents
In key-wallet/src/bip32.rs around lines 272 to 276, the Debug implementation for
Fingerprint has an extra closing parenthesis in the format string. Remove the
duplicated closing parenthesis in the write! macro so the output string
correctly formats as "Fingerprint({})" without the extra parenthesis.

Comment on lines +105 to +109
impl fmt::Debug for ChainCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ChainCode({}))", self)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix the extra parenthesis in Debug implementation for ChainCode.

fmt::Debug currently renders "ChainCode())", note the doubled ).

-        write!(f, "ChainCode({}))", self)
+        write!(f, "ChainCode({})", self)
🤖 Prompt for AI Agents
In key-wallet/src/bip32.rs around lines 105 to 109, the Debug implementation for
ChainCode has an extra closing parenthesis in the format string causing output
like "ChainCode())". Remove the extra parenthesis from the write! macro format
string so it correctly outputs "ChainCode()".

uniffi = { version = "0.27", features = ["build"] }

[dev-dependencies]
uniffi = { version = "0.27", features = ["bindgen-tests"] } No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
uniffi = { version = "0.27", features = ["bindgen-tests"] }
uniffi = { version = "0.27", features = ["bindgen-tests"] }

Copy link
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: 10

🔭 Outside diff range comments (2)
key-wallet/src/bip32.rs (2)

689-716: 🛠️ Refactor suggestion

Reuse the existing hex-parsing helper instead of ad-hoc code

Manual nibble parsing is duplicated here and elsewhere; it is error-prone and misses validations (e.g. odd-length strings are silently padded with ‘0’).
Call crate::utils::parse_hex_bytes instead:

-// Simple hex decoder
-let hex_bytes = hex_str
-    .as_bytes()
-    ...
-    .collect::<Result<Vec<u8>, Error>>()?;
-if hex_bytes.len() != 32 {
-    return Err(Error::InvalidChildNumberFormat);
-}
-let mut index_bytes = [0u8; 32];
-index_bytes[32 - hex_bytes.len()..].copy_from_slice(&hex_bytes);
+let mut index_bytes = [0u8; 32];
+crate::utils::parse_hex_bytes(hex_str, &mut index_bytes)
+    .map_err(|_| Error::InvalidChildNumberFormat)?;

1038-1046: ⚠️ Potential issue

from_str("") panics

parts.next().unwrap() will panic for an empty string.
Return InvalidDerivationPathFormat instead of panicking.

-let mut parts = path.split('/');
-// First parts must be `m`.
-if parts.next().unwrap() != "m" {
+let mut parts = path.split('/');
+let first = parts.next().ok_or(Error::InvalidDerivationPathFormat)?;
+if first != "m" {
♻️ Duplicate comments (6)
dash/src/sml/llmq_type/network.rs (3)

23-30: Duplicate of previous concern – see above.


33-40: Duplicate of previous concern – see above.


43-50: Duplicate of previous concern – see above.

key-wallet/src/derivation.rs (1)

132-136: Avoid unwrap() – propagate the error instead

Both pushes use ChildNumber::from_hardened_idx(..).unwrap().
If either index ≥ 0x8000_0000 the call will error and unwrap() will panic, crashing the process.

-full_path.push(crate::bip32::ChildNumber::from_hardened_idx(identity_index).unwrap());
-full_path.push(crate::bip32::ChildNumber::from_hardened_idx(key_index).unwrap());
+full_path.push(
+    crate::bip32::ChildNumber::from_hardened_idx(identity_index)
+        .map_err(|e| Error::InvalidDerivationPath(e.to_string()))?,
+);
+full_path.push(
+    crate::bip32::ChildNumber::from_hardened_idx(key_index)
+        .map_err(|e| Error::InvalidDerivationPath(e.to_string()))?,
+);
key-wallet/src/bip32.rs (2)

106-108: Extra ) in Debug output

fmt::Debug prints ChainCode(())).
Remove the duplicated parenthesis.

-write!(f, "ChainCode({}))", self)
+write!(f, "ChainCode({})", self)

242-244: Same double-parenthesis bug in Fingerprint::Debug

-write!(f, "Fingerprint({}))", self)
+write!(f, "Fingerprint({})", self)
🧹 Nitpick comments (11)
dash-network-ffi/build.rs (1)

1-3: Missing cargo:rerun-if-changed hampers incremental builds

Add the directive so Cargo regenerates scaffolding when the UDL changes.

 fn main() {
+    println!("cargo:rerun-if-changed=src/dash_network.udl");
     uniffi::generate_scaffolding("src/dash_network.udl").unwrap();
 }
dash/src/crypto/key.rs (1)

401-405: Defaulting unknown networks to Testnet WIF prefix can cause fund loss

Unknown/future networks will generate WIFs with prefix 239, indistinguishable from Testnet/Devnet/Regtest keys.
Better: refuse to format or emit an explicit error for unrecognised variants so users don’t import a key on the wrong chain.

-            _ => 239,
+            _ => return Err(fmt::Error), // or choose a more descriptive error path
key-wallet/src/utils.rs (2)

4-11: Hex-digit helper duplicates existing logic

There is an almost identical conversion routine in
key-wallet/src/bip32.rs (see lines 692-712).
Consider re-exporting parse_hex_digit from this module and reusing it
there to keep the implementation in one place (DRY).


14-25: Return a typed error instead of &'static str

parse_hex_bytes currently bubbles up string literals.
Using a small enum (e.g. HexError { InvalidLen, InvalidChar })
will give callers the ability to pattern-match without string
comparisons and makes future localisation possible.

-pub(crate) fn parse_hex_bytes(hex_str: &str, output: &mut [u8]) -> Result<(), &'static str> {
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum HexError { InvalidLen, InvalidChar }
+
+pub(crate) fn parse_hex_bytes(hex_str: &str, output: &mut [u8]) -> Result<(), HexError> {
@@
-        return Err("invalid hex length");
+        return Err(HexError::InvalidLen);
@@
-        let high = parse_hex_digit(chunk[0]).ok_or("invalid hex character")?;
-        let low  = parse_hex_digit(chunk[1]).ok_or("invalid hex character")?;
+        let high = parse_hex_digit(chunk[0]).ok_or(HexError::InvalidChar)?;
+        let low  = parse_hex_digit(chunk[1]).ok_or(HexError::InvalidChar)?;
dash-network/README.md (1)

22-23: Hard-coded version in docs

dash-network = "0.39.6" is unlikely to exist on crates.io yet.
Consider using "0.39" or "{ path = \"../dash-network\" }" in early
releases to avoid copy-paste confusion.

dash-network-ffi/src/dash_network.udl (1)

30-35: Method name might cause ambiguity

boolean is_core_v20_active(u32 block_height); does not encode what height is expected (core vs chain). Consider renaming to is_core_v20_active_at_height or documenting the unit explicitly to avoid confusion in FFI consumers.

dash-network/src/lib.rs (1)

91-103: FromStr should not use String as an error type

type Err = String; forces callers to allocate and pattern-match on free-form text.
Introduce a small enum instead so downstream code can handle errors robustly:

+#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
+pub enum ParseNetworkError {
+    #[error("unknown network type")]
+    Unknown,
+}

 impl std::str::FromStr for Network {
-    type Err = String;
+    type Err = ParseNetworkError;
 ...
-            _ => Err(format!("Unknown network type: {}", s)),
+            _ => Err(ParseNetworkError::Unknown),
dash/src/network/constants.rs (1)

71-99: Name collision with another NetworkExt

key-wallet/src/address.rs also defines a NetworkExt trait.
Although they live in different crates, shared users importing both crates may face ambiguous-import errors.
Consider renaming this trait (e.g., DashNetworkExt) or pre-re-exporting it under a distinct module.

key-wallet/src/address.rs (1)

30-40: Hard-coding default versions masks future networks

The fallback branch (_ =>) repeats the testnet bytes.
If dash_network grows new variants the address versions will be wrong.
Prefer an explicit unreachable!() to detect missing mappings at compile-time.

key-wallet-ffi/src/lib.rs (1)

459-466: Defaulting unknown networks to Testnet hides bugs

If a future network variant is added, returning Testnet will silently mis-report. Prefer an explicit error or an Unknown enum value.

key-wallet-ffi/src/key_wallet.udl (1)

10-17: Document enum ordering or add explicit values

FFI consumers will assume a fixed numeric value for each Network variant; changes in Rust ordering will break ABI. Add a comment in UDL or explicit numeric tags to mirror the repr(u8) change in Rust.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d8f05ec and 93d1bfa.

📒 Files selected for processing (44)
  • Cargo.toml (1 hunks)
  • dash-network-ffi/Cargo.toml (1 hunks)
  • dash-network-ffi/README.md (1 hunks)
  • dash-network-ffi/build.rs (1 hunks)
  • dash-network-ffi/src/dash_network.udl (1 hunks)
  • dash-network-ffi/src/lib.rs (1 hunks)
  • dash-network-ffi/uniffi-bindgen.rs (1 hunks)
  • dash-network/Cargo.toml (1 hunks)
  • dash-network/README.md (1 hunks)
  • dash-network/src/lib.rs (1 hunks)
  • dash/Cargo.toml (4 hunks)
  • dash/examples/handshake.rs (3 hunks)
  • dash/src/address.rs (4 hunks)
  • dash/src/blockdata/constants.rs (3 hunks)
  • dash/src/blockdata/transaction/mod.rs (1 hunks)
  • dash/src/blockdata/transaction/outpoint.rs (1 hunks)
  • dash/src/consensus/params.rs (2 hunks)
  • dash/src/crypto/key.rs (3 hunks)
  • dash/src/crypto/sighash.rs (1 hunks)
  • dash/src/lib.rs (2 hunks)
  • dash/src/network/constants.rs (5 hunks)
  • dash/src/psbt/mod.rs (5 hunks)
  • dash/src/sml/llmq_type/mod.rs (2 hunks)
  • dash/src/sml/llmq_type/network.rs (1 hunks)
  • dash/src/sml/masternode_list/from_diff.rs (2 hunks)
  • dash/src/sml/masternode_list/quorum_helpers.rs (1 hunks)
  • dash/src/sml/masternode_list/scores_for_quorum.rs (1 hunks)
  • dash/src/sml/masternode_list_engine/message_request_verification.rs (2 hunks)
  • dash/src/sml/masternode_list_engine/mod.rs (1 hunks)
  • dash/tests/psbt.rs (2 hunks)
  • fuzz/fuzz_targets/dash/deserialize_script.rs (1 hunks)
  • key-wallet-ffi/Cargo.toml (1 hunks)
  • key-wallet-ffi/build.rs (1 hunks)
  • key-wallet-ffi/src/key_wallet.udl (1 hunks)
  • key-wallet-ffi/src/lib.rs (1 hunks)
  • key-wallet/Cargo.toml (1 hunks)
  • key-wallet/src/address.rs (1 hunks)
  • key-wallet/src/bip32.rs (23 hunks)
  • key-wallet/src/derivation.rs (1 hunks)
  • key-wallet/src/dip9.rs (1 hunks)
  • key-wallet/src/lib.rs (1 hunks)
  • key-wallet/src/mnemonic.rs (1 hunks)
  • key-wallet/src/utils.rs (1 hunks)
  • key-wallet/tests/mnemonic_tests.rs (1 hunks)
✅ Files skipped from review due to trivial changes (15)
  • key-wallet/src/dip9.rs
  • fuzz/fuzz_targets/dash/deserialize_script.rs
  • dash/src/blockdata/transaction/outpoint.rs
  • dash/examples/handshake.rs
  • dash/src/sml/masternode_list/scores_for_quorum.rs
  • dash/src/crypto/sighash.rs
  • dash-network-ffi/uniffi-bindgen.rs
  • dash/src/blockdata/transaction/mod.rs
  • dash/src/sml/masternode_list/from_diff.rs
  • dash/src/sml/masternode_list/quorum_helpers.rs
  • dash/src/sml/masternode_list_engine/message_request_verification.rs
  • dash-network-ffi/Cargo.toml
  • dash-network/Cargo.toml
  • dash-network-ffi/README.md
  • dash/src/sml/masternode_list_engine/mod.rs
🚧 Files skipped from review as they are similar to previous changes (10)
  • Cargo.toml
  • dash/tests/psbt.rs
  • dash/Cargo.toml
  • key-wallet-ffi/Cargo.toml
  • key-wallet/tests/mnemonic_tests.rs
  • dash/src/psbt/mod.rs
  • dash/src/lib.rs
  • key-wallet/Cargo.toml
  • key-wallet/src/lib.rs
  • key-wallet/src/mnemonic.rs
🧰 Additional context used
🧬 Code Graph Analysis (5)
dash/src/sml/llmq_type/mod.rs (1)
dash/src/address.rs (1)
  • network (834-836)
key-wallet-ffi/build.rs (2)
dash-network-ffi/uniffi-bindgen.rs (1)
  • main (1-3)
dash-network-ffi/build.rs (1)
  • main (1-3)
key-wallet/src/utils.rs (1)
key-wallet/src/bip32.rs (1)
  • hex_str (692-712)
key-wallet/src/derivation.rs (3)
key-wallet/src/bip32.rs (24)
  • derive_priv (1298-1308)
  • derive_pub (1575-1585)
  • into (778-783)
  • new_master (1274-1287)
  • from (78-80)
  • from (214-216)
  • from (598-608)
  • from (612-627)
  • from (995-997)
  • from (1001-1003)
  • from (1007-1009)
  • from (1261-1263)
  • from_hardened_idx (465-473)
  • index (123-125)
  • index (131-133)
  • index (139-141)
  • index (147-149)
  • index (155-157)
  • index (270-272)
  • index (278-280)
  • index (286-288)
  • index (294-296)
  • index (302-304)
  • index (962-964)
key-wallet/src/dip9.rs (1)
  • from (64-66)
key-wallet/src/mnemonic.rs (2)
  • from (28-40)
  • from_phrase (92-99)
key-wallet-ffi/src/lib.rs (6)
dash-network-ffi/src/lib.rs (4)
  • initialize (9-11)
  • from (23-30)
  • new (58-62)
  • to_string (76-78)
key-wallet/src/mnemonic.rs (10)
  • from (28-40)
  • from_str (147-150)
  • phrase (102-104)
  • validate (139-141)
  • inner (103-103)
  • from_phrase (92-99)
  • generate (51-74)
  • generate (78-89)
  • word_count (107-109)
  • to_seed (122-126)
key-wallet/src/error.rs (2)
  • from (59-61)
  • from (65-67)
key-wallet/src/bip32.rs (29)
  • from (78-80)
  • from (214-216)
  • from (598-608)
  • from (612-627)
  • from (995-997)
  • from (1001-1003)
  • from (1007-1009)
  • from (1261-1263)
  • from (1267-1269)
  • from_str (250-255)
  • from_str (680-736)
  • from_str (1038-1047)
  • from_str (1838-1841)
  • from_str (1853-1856)
  • from_priv (1552-1564)
  • index (123-125)
  • index (131-133)
  • index (139-141)
  • index (147-149)
  • index (155-157)
  • index (270-272)
  • index (278-280)
  • index (286-288)
  • index (294-296)
  • index (302-304)
  • index (962-964)
  • from_hardened_idx (465-473)
  • from_normal_idx (451-459)
  • child (1107-1111)
key-wallet/src/address.rs (5)
  • new (165-169)
  • from_str (100-125)
  • to_string (86-97)
  • p2pkh (67-74)
  • generate_range (177-209)
dash-network/src/lib.rs (1)
  • from_str (94-102)
🪛 LanguageTool
dash-network/README.md

[uncategorized] ~54-~54: Loose punctuation mark.
Context: ... test network ### Features - default: Enables std - std: Standard library...

(UNLIKELY_OPENING_PUNCTUATION)

⏰ Context from checks skipped due to timeout of 90000ms (15)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (hashes_sha512)
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (dash_deserialize_amount)
  • GitHub Check: fuzz (dash_outpoint_string)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (dash_deser_net_msg)
🔇 Additional comments (3)
dash/src/sml/llmq_type/mod.rs (1)

1-1: Publicly re-exporting network seems unnecessary – consider pub(crate)

Making the sub-module public widens the crate’s public API and locks you into a semver commitment. If the intent is purely internal reuse, prefer pub(crate) mod network; (or keep it private) to avoid accidental external dependence.

key-wallet-ffi/build.rs (1)

1-4: 👍 cargo:rerun-if-changed added – incremental rebuilds now work

The build script now properly invalidates when key_wallet.udl changes.
Looks good.

dash/src/address.rs (1)

1142-1147: is_valid_for_network returns false positives on future variants

The final wildcard branch (_ => false) is good, but the preceding
pattern-matching logic assumes the set of variants is closed,
otherwise the early arms may accept an unsupported pair.
After switching to exhaustive matches (see previous comment) this
function will also be safer.

Comment on lines +181 to +186
kw::Error::Base58 => KeyWalletError::AddressError {
message: "Base58 encoding error".into(),
},
kw::Error::InvalidNetwork => KeyWalletError::AddressError {
message: "Invalid network".into(),
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Pattern-matching wrong Base58 variant – won’t compile

kw::Error::Base58 carries the underlying error (Base58(err)); matching it without binding is a type mismatch.

-kw::Error::Base58 => KeyWalletError::AddressError {
-    message: "Base58 encoding error".into(),
-},
+kw::Error::Base58(err) => KeyWalletError::AddressError {
+    message: err.to_string(),
+},
📝 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
kw::Error::Base58 => KeyWalletError::AddressError {
message: "Base58 encoding error".into(),
},
kw::Error::InvalidNetwork => KeyWalletError::AddressError {
message: "Invalid network".into(),
},
kw::Error::Base58(err) => KeyWalletError::AddressError {
message: err.to_string(),
},
kw::Error::InvalidNetwork => KeyWalletError::AddressError {
message: "Invalid network".into(),
},
🤖 Prompt for AI Agents
In key-wallet-ffi/src/lib.rs around lines 181 to 186, the pattern match on
kw::Error::Base58 is incorrect because it does not bind the inner error value,
causing a type mismatch. Fix this by changing the pattern to
kw::Error::Base58(err) to capture the inner error, then use err as needed when
constructing the KeyWalletError::AddressError variant.

@QuantumExplorer
Copy link
Member Author

@CodeRabbit rereview

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 15, 2025

Tip

For best results, initiate chat on the files or code changes.

@QuantumExplorer I'll provide a comprehensive re-review of this key wallet refactoring. This is a significant architectural change that deserves careful analysis.

🏗️ Architecture & Design

Excellent modularization strategy! The split into separate crates is well-designed:

  • key-wallet: Core wallet functionality (BIP32, BIP39, DIP9)
  • key-wallet-ffi: UniFFI bindings for cross-language support
  • dash-network: Network type definitions and constants
  • dash-network-ffi: Network FFI bindings

The dependency graph is clean and logical, with proper separation of concerns.

🔍 Code Quality Analysis

Strengths:

  1. Comprehensive error handling with unified Error enum in key-wallet/src/error.rs
  2. Proper no-std support with feature flags
  3. Extensive test coverage across all new crates
  4. Clean FFI design using UniFFI for automatic binding generation
  5. Backward compatibility maintained through public re-exports

Areas of Interest:

Network handling improvements:

// Good: Added fallback arms for unknown network variants
match self.network {
    Network::Dash => /* ... */,
    Network::Testnet => /* ... */,
    _ => /* fallback */  // Prevents panics on new variants
}

Serialization consistency:

// Changed from .as_bytes() to .to_bytes() for explicit conversion
fingerprint.to_bytes()  // More explicit and consistent

🧪 Testing & Documentation

Outstanding test coverage:

  • Unit tests for all core functionality
  • Integration tests for FFI bindings
  • Example code demonstrating usage
  • Comprehensive README files

Documentation quality:

  • Clear API documentation
  • Usage examples in multiple languages (Swift, Kotlin, Python)
  • Migration guidance

🔒 Security Considerations

Positive security aspects:

  1. Memory safety maintained through Rust's ownership system
  2. Secure key handling with proper zeroization patterns
  3. Input validation in FFI layer
  4. Error propagation without leaking sensitive information

🚀 Performance & Efficiency

The refactoring appears performance-neutral:

  • No significant algorithmic changes
  • FFI overhead minimal for intended use cases
  • Lazy initialization patterns preserved

🔄 Migration Path

Smooth migration strategy:

  • Public re-exports maintain API compatibility
  • Gradual migration possible
  • Clear deprecation path for removed examples

📋 Minor Recommendations

  1. Consider adding benchmarks for key operations to track performance
  2. FFI error mapping could benefit from more specific error types
  3. Network extension traits are well-designed but could use more documentation

✅ Verdict

This is a high-quality refactoring that:

  • ✅ Improves code organization and reusability
  • ✅ Adds valuable FFI capabilities
  • ✅ Maintains backward compatibility
  • ✅ Includes comprehensive testing
  • ✅ Follows Rust best practices
  • ✅ Provides excellent documentation

The architectural decisions are sound, implementation is robust, and the migration path is well-planned. This refactoring positions the codebase well for future development and cross-language integration.

Recommendation: Approve 🎉

The modularization effort successfully achieves its goals while maintaining code quality and adding significant value through FFI capabilities.

Copy link
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

♻️ Duplicate comments (2)
key-wallet-ffi/src/lib.rs (1)

165-185: ⚠️ Potential issue

kw::Error::Base58 mapping loses error context

kw::Error::Base58 carries the underlying base58ck::Error.
Dropping it replaces useful details (“checksum mismatch”, “invalid character”) with a generic string.

-            kw::Error::Base58 => KeyWalletError::AddressError {
-                message: "Base58 encoding error".into(),
-            },
+            kw::Error::Base58(err) => KeyWalletError::AddressError {
+                message: err.to_string(),
+            },

Keeps FFI consumers informed and eases debugging.

CLAUDE.md (1)

120-126: Grammar nit: “Prefer to composition over inheritance”

-Use async/await for asynchronous operations
-Prefer composition over inheritance
+Use async/await for asynchronous operations
+Prefer composition to inheritance
🧰 Tools
🪛 LanguageTool

[grammar] ~125-~125: Consider using “to” with “prefer”.
Context: ...ync/await for asynchronous operations - Prefer composition over inheritance ### Memory and State Management - Use ...

(PREFER_OVER_TO)

🧹 Nitpick comments (5)
key-wallet-ffi/src/lib.rs (1)

268-273: Coin-type hard-code covers Testnet but still wrong for Devnet / Regtest

SLIP-44 assigns coin-type 1 for all test networks, but some software (e.g. Dash Core) expects:

  • Main-net – 5
  • Testnet & Regtest – 1
  • Devnet1 + devnetIndex (configurable)

At minimum, document the limitation or expose coin_type as a parameter so SDK users can set the correct value for their dev-net instance.

Also applies to: 288-293

key-wallet-ffi/build-ios.sh (2)

5-11: Add set -u and -o pipefail for safer bash execution

-set -e
+set -euo pipefail

Prevents the script from continuing on unset variables or failed pipeline commands.
Minor but avoids hard-to-diagnose build artefacts.


24-33: Use lipo -output <dest> -create … for clarity

While the current order works, the more conventional flag order improves readability:

-lipo -create \
-    target/x86_64-apple-ios/release/libkey_wallet_ffi.a \
-    target/aarch64-apple-ios-sim/release/libkey_wallet_ffi.a \
-    -output target/universal/release/libkey_wallet_ffi_sim.a
+lipo -output target/universal/release/libkey_wallet_ffi_sim.a -create \
+    target/x86_64-apple-ios/release/libkey_wallet_ffi.a \
+    target/aarch64-apple-ios-sim/release/libkey_wallet_ffi.a
CLAUDE.md (1)

268-268: Markdownlint: convert bare URL to link

Replace the raw URL with markdown link syntax to satisfy MD034.

For more information about SPARC methodology, see [SPARC docs](https://github.com/ruvnet/claude-code-flow/docs/sparc.md).
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

268-268: Bare URL used
null

(MD034, no-bare-urls)

NETWORK_HANDLING_FIXES.md (1)

98-98: Excessive exclamation marks

Tone down to keep docs professional.

- now explicitly panic or use unreachable!, preventing silent misconfigurations
+ now explicitly panic or use `unreachable!()`, preventing silent misconfigurations
🧰 Tools
🪛 LanguageTool

[style] ~98-~98: Using many exclamation marks might seem excessive (in this case: 4 exclamation marks for a text that’s 2366 characters long)
Context: ... now explicitly panic or use unreachable!, preventing silent misconfigurations - ...

(EN_EXCESSIVE_EXCLAMATION)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93d1bfa and 18b98c0.

📒 Files selected for processing (16)
  • .gitignore (1 hunks)
  • CLAUDE.md (1 hunks)
  • NETWORK_HANDLING_FIXES.md (1 hunks)
  • dash-network-ffi/src/lib.rs (1 hunks)
  • dash-network/src/lib.rs (1 hunks)
  • dash/src/address.rs (4 hunks)
  • dash/src/blockdata/constants.rs (3 hunks)
  • dash/src/consensus/params.rs (2 hunks)
  • dash/src/sml/llmq_type/network.rs (1 hunks)
  • key-wallet-ffi/build-ios.sh (1 hunks)
  • key-wallet-ffi/src/lib.rs (1 hunks)
  • key-wallet-ffi/src/lib_tests.rs (1 hunks)
  • key-wallet-ffi/tests/ffi_tests.rs (1 hunks)
  • key-wallet/examples/basic_usage.rs (1 hunks)
  • key-wallet/src/address.rs (1 hunks)
  • key-wallet/tests/address_tests.rs (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • .gitignore
  • dash-network-ffi/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (9)
  • dash/src/consensus/params.rs
  • dash/src/blockdata/constants.rs
  • key-wallet-ffi/tests/ffi_tests.rs
  • key-wallet/tests/address_tests.rs
  • dash/src/sml/llmq_type/network.rs
  • key-wallet/examples/basic_usage.rs
  • key-wallet-ffi/src/lib_tests.rs
  • dash/src/address.rs
  • dash-network/src/lib.rs
🧰 Additional context used
🧬 Code Graph Analysis (1)
key-wallet-ffi/src/lib.rs (5)
dash-network-ffi/src/lib.rs (4)
  • from (23-30)
  • from (34-42)
  • new (58-62)
  • to_string (76-78)
key-wallet/src/derivation.rs (6)
  • derive (76-78)
  • new (52-57)
  • new (149-154)
  • from_seed (60-63)
  • format (158-159)
  • format (166-167)
key-wallet/src/bip32.rs (29)
  • from (78-80)
  • from (214-216)
  • from (598-608)
  • from (612-627)
  • from (995-997)
  • from (1001-1003)
  • from (1007-1009)
  • from (1261-1263)
  • from (1267-1269)
  • from_str (250-255)
  • from_str (680-736)
  • from_str (1038-1047)
  • from_str (1838-1841)
  • from_str (1853-1856)
  • from_priv (1552-1564)
  • index (123-125)
  • index (131-133)
  • index (139-141)
  • index (147-149)
  • index (155-157)
  • index (270-272)
  • index (278-280)
  • index (286-288)
  • index (294-296)
  • index (302-304)
  • index (962-964)
  • from_hardened_idx (465-473)
  • from_normal_idx (451-459)
  • child (1107-1111)
key-wallet/src/address.rs (5)
  • new (174-178)
  • from_str (100-134)
  • to_string (86-97)
  • p2pkh (67-74)
  • generate_range (186-218)
key-wallet/src/mnemonic.rs (8)
  • from_str (147-150)
  • phrase (102-104)
  • inner (103-103)
  • from_phrase (92-99)
  • generate (51-74)
  • generate (78-89)
  • word_count (107-109)
  • to_seed (122-126)
🪛 LanguageTool
NETWORK_HANDLING_FIXES.md

[style] ~98-~98: Using many exclamation marks might seem excessive (in this case: 4 exclamation marks for a text that’s 2366 characters long)
Context: ... now explicitly panic or use unreachable!, preventing silent misconfigurations - ...

(EN_EXCESSIVE_EXCLAMATION)

CLAUDE.md

[grammar] ~125-~125: Consider using “to” with “prefer”.
Context: ...ync/await for asynchronous operations - Prefer composition over inheritance ### Memory and State Management - Use ...

(PREFER_OVER_TO)

🪛 markdownlint-cli2 (0.17.2)
CLAUDE.md

268-268: Bare URL used
null

(MD034, no-bare-urls)

⏰ Context from checks skipped due to timeout of 90000ms (15)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: fuzz (hashes_sha512)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: fuzz (dash_deser_net_msg)
  • GitHub Check: fuzz (dash_outpoint_string)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (dash_deserialize_amount)

Comment on lines +30 to +40
impl NetworkExt for Network {
/// Get P2PKH version byte
fn p2pkh_version(&self) -> u8 {
match self {
Network::Dash => 76, // 'X' prefix
Network::Testnet => 140, // 'y' prefix
Network::Devnet => 140, // 'y' prefix
Network::Regtest => 140, // 'y' prefix
_ => 140, // default to testnet version
}
}
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 15, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove silent fall-backs for unknown Network variants

Both p2pkh_version() and p2sh_version() use a wildcard arm that silently maps every future / unknown Network to the test-net prefix.
That re-introduces the very bug this PR tries to eradicate elsewhere and may route main-net funds to a test-net address.

-            Network::Regtest => 140,
-            _ => 140, // default to testnet version
+            Network::Regtest => 140,
+            other => unreachable!("Unhandled network variant {other:?}")

Make the match exhaustive (or panic) so problems are caught early.
Same change applies to p2sh_version().

Also applies to: 42-50

🤖 Prompt for AI Agents
In key-wallet/src/address.rs around lines 30 to 40 and 42 to 50, the match
statements in p2pkh_version() and p2sh_version() use a wildcard arm that
silently defaults unknown Network variants to the testnet prefix, which can
cause incorrect address generation. Remove the wildcard arm and make the match
exhaustive by explicitly handling all known Network variants. For any unknown
variants, either add a panic or return an error to catch issues early and
prevent silent fallback to testnet prefixes.

Copy link
Member Author

Choose a reason for hiding this comment

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

this is fine

Copy link
Contributor

Choose a reason for hiding this comment

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

@QuantumExplorer Understood, I'll respect your decision to keep the fallback behavior for unknown network variants. Thank you for the clarification.


✏️ Learnings added
Learnt from: QuantumExplorer
PR: dashpay/rust-dashcore#74
File: key-wallet/src/address.rs:30-40
Timestamp: 2025-06-15T15:31:44.115Z
Learning: In the key-wallet crate, QuantumExplorer prefers keeping wildcard arms that default unknown Network variants to testnet prefixes rather than using exhaustive matches or panics. This fallback behavior is intentional.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +112 to +126
// Infer network and address type from version byte
let (network, address_type) = match version {
76 => (Network::Dash, AddressType::P2PKH), // Dash mainnet P2PKH
16 => (Network::Dash, AddressType::P2SH), // Dash mainnet P2SH
140 => {
// Could be testnet, devnet, or regtest P2PKH
// Default to testnet, but this is ambiguous
(Network::Testnet, AddressType::P2PKH)
}
19 => {
// Could be testnet, devnet, or regtest P2SH
// Default to testnet, but this is ambiguous
(Network::Testnet, AddressType::P2SH)
}
_ => return Err(Error::InvalidAddress(format!("Unknown version byte: {}", version))),
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 15, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

Ambiguous version-byte ⇒ wrong network for Devnet / Regtest

from_str hard-codes
140 → Testnet and 19 → Testnet (P2SH) even though the same prefixes are used by Devnet and Regtest.
Address::from_str("8…") coming from a dev-net wallet would therefore be parsed as Testnet and later rejected by FFI when the caller specifies Devnet.

Consider:

  • Returning Err(…) when the version byte is shared by multiple networks and no disambiguation is possible; or
  • Adding a parameter / overload that lets the caller supply the expected network for validation.

Either way, avoid silently guessing.


Fail fast on ambiguous version bytes or require explicit network

Currently, version bytes 140 (P2PKH) and 19 (P2SH) are unconditionally mapped to Network::Testnet, but those same prefixes are also used by Devnet and Regtest. As a result, parsing an address from Devnet or Regtest will silently default to Testnet and later be rejected by FFI when the caller expects Devnet/Regtest.

Action items:

  • In key-wallet/src/address.rs (lines 112–126), replace the hard-coded Testnet fallback for 140 and 19 with one of:
    • Returning Err(Error::InvalidAddress(...)) for these ambiguous version bytes, or
    • Adding an overload/parameter that lets callers supply the expected Network for disambiguation.
  • Add or update tests to cover Devnet and Regtest address parsing paths.
🤖 Prompt for AI Agents
In key-wallet/src/address.rs around lines 112 to 126, the code currently maps
version bytes 140 and 19 directly to Network::Testnet, but these bytes are
ambiguous as they are also used by Devnet and Regtest. To fix this, modify the
match logic to return an Err(Error::InvalidAddress(...)) when encountering these
ambiguous version bytes instead of defaulting to Testnet, or alternatively,
refactor the function to accept an additional parameter specifying the expected
Network to disambiguate the version byte. Also, add or update tests to verify
correct behavior for Devnet and Regtest addresses.

Copy link
Member Author

Choose a reason for hiding this comment

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

this is fine

Copy link
Contributor

Choose a reason for hiding this comment

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

@QuantumExplorer Understood! I'll acknowledge your decision to keep the current address parsing implementation as-is. The ambiguous version byte handling will remain with the testnet default for shared prefixes.

🐰
╱|、
(˚ˎ 。7
|、˜〵
じしˍ,)ノ

Copy link
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: 1

🧹 Nitpick comments (2)
key-wallet-ffi/src/lib_tests.rs (2)

16-18: Prefer expect to make failures self-describing

unwrap() hides the context if validate_mnemonic unexpectedly returns Err.
Using expect surfaces the precise test section that failed.

-let is_valid = validate_mnemonic(valid_phrase.clone(), Language::English).unwrap();
+let is_valid = validate_mnemonic(valid_phrase.clone(), Language::English)
+    .expect("Mnemonic validation unexpectedly failed");

32-34: Zero-filled seed is not representative and may mask edge-cases

Feeding vec![0u8; 64] to HDWallet::from_seed meets the length requirement but does not mirror
real BIP-39 output.
Consider generating the seed from the mnemonic used in the previous test or at least a
non-trivial byte pattern to improve coverage.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18b98c0 and ba7e02e.

📒 Files selected for processing (3)
  • key-wallet-ffi/src/lib.rs (1 hunks)
  • key-wallet-ffi/src/lib_tests.rs (1 hunks)
  • key-wallet-ffi/tests/ffi_tests.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • key-wallet-ffi/tests/ffi_tests.rs
  • key-wallet-ffi/src/lib.rs
⏰ Context from checks skipped due to timeout of 90000ms (15)
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (hashes_sha512)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (dash_deser_net_msg)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (dash_deserialize_amount)
  • GitHub Check: fuzz (dash_outpoint_string)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
🔇 Additional comments (1)
key-wallet-ffi/src/lib_tests.rs (1)

152-157: Cross-network address acceptance could hide real regressions

The test asserts that parsing a Testnet address succeeds for Devnet and Regtest as well.
If the prefix mapping ever diverges, this will silently pass and fail in production.

Recommend adding a negative assertion when/if distinct prefixes are introduced, or
document explicitly that the same prefix across these networks is a deliberate rule.

@QuantumExplorer QuantumExplorer changed the base branch from master to v0.40-dev June 15, 2025 16:52
Copy link
Member Author

@QuantumExplorer QuantumExplorer left a comment

Choose a reason for hiding this comment

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

Self Reviewed

@QuantumExplorer QuantumExplorer deleted the refactor/splitKeyWallet branch October 20, 2025 08:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants