Skip to content

Conversation

@QuantumExplorer
Copy link
Member

@QuantumExplorer QuantumExplorer commented Nov 17, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Load compact filters for custom height ranges from the blockchain
    • Query filter-matched heights with wallet match information
    • Retrieve transaction counts and count of blocks containing transactions
  • Improvements

    • Filter matching now returns specific matched wallet identifiers instead of simple match status
    • Added persistent storage of filter match results for improved data handling

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 17, 2025

Walkthrough

The changes extend the Dash SPV client FFI and core library with comprehensive filter-matching capabilities. They replace boolean filter-match responses with vectors of matched wallet IDs, introduce ChainState persistence for filter matches, and add FFI APIs for loading and querying filters within configurable height ranges with 10,000-block limits.

Changes

Cohort / File(s) Summary
FFI Header & Type Definitions
dash-spv-ffi/include/dash_spv_ffi.h, dash-spv-ffi/src/types.rs
Added four new FFI-safe types: FFICompactFilter, FFICompactFilters, FFIFilterMatchEntry, FFIFilterMatches. Defined corresponding destroy functions for memory management. Types include height, filter data, and wallet ID lists with count metadata.
FFI Client API
dash-spv-ffi/src/client.rs
Implemented four new public FFI functions: load_filters, get_filter_matched_heights, get_transaction_count, get_blocks_with_transactions_count. Each enforces 10,000-block range limits, returns ownership-transferred structs or counts, and handles null clients gracefully.
Filter Matching Logic
dash-spv/src/sync/filters/matching.rs, key-wallet-manager/src/wallet_interface.rs, key-wallet-manager/src/wallet_manager/process_block.rs
Modified check_compact_filter signature from bool return to Vec<[u8; 32]> across trait and implementations. Updated wallet matching to collect and return matched wallet IDs instead of simple boolean hit, with per-wallet script evaluation and caching.
Block Processing & Tests
dash-spv/src/client/block_processor.rs, dash-spv/src/client/block_processor_test.rs
Replaced boolean match logic with matched wallet ID list handling. Updated all mock and test wallet implementations to return vectors. Adjusted logging to include matched wallet count. Updated control flow to derive has_matches from non-empty vector.
ChainState & Storage
dash-spv/src/types.rs, key-wallet-manager/src/wallet_manager/mod.rs
Added filter_matches: BTreeMap<u32, Vec<[u8; 32]>> field to ChainState. Implemented record_filter_matches() and get_filter_matched_heights(Range<u32>) methods with 10,000-block range enforcement. Updated WalletManager cache type from BTreeMap<BlockHash, bool> to Vec<[u8; 32]>.
Message Handling & Sync
dash-spv/src/sync/message_handlers.rs, dash-spv/src/sync/phase_execution.rs
Updated message handler to load/record filter matches to ChainState on successful match. Persists updated state and logs matched wallet count. Minimal formatting adjustment in phase execution.
Error Handling
dash-spv/src/error.rs
Added InvalidInput(String) error variant to StorageError with corresponding Clone implementation.
Tests
dash-spv-ffi/tests/unit/test_client_lifecycle.rs, dash-spv-ffi/tests/unit/test_type_conversions.rs, dash-spv/tests/storage_test.rs
Added four client lifecycle tests validating transaction/block counts for empty wallets and null clients. Extended type conversion test to include filter_matches field initialization. Added async storage test for filter range loading.
Documentation
dash-spv-ffi/FFI_API.md
Updated API documentation reflecting eight new functions and two new types, increasing total function count from 70 to 78.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • xdustinface

Pre-merge checks and finishing touches

✅ 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 'feat: expose filters' directly describes the main objective of the changeset, which adds FFI functions to expose compact filter loading and filter-match retrieval capabilities.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/exposeFilters

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b43a6e8 and dcbe06a.

📒 Files selected for processing (1)
  • dash-spv-ffi/src/client.rs
🧰 Additional context used
📓 Path-based instructions (4)
dash-spv-ffi/src/**/*.rs

📄 CodeRabbit inference engine (dash-spv-ffi/CLAUDE.md)

dash-spv-ffi/src/**/*.rs: Use #[no_mangle] extern "C" attribute when implementing new FFI functions in Rust
All FFI types must have corresponding _destroy() functions for explicit memory management
Rust strings must be returned as *const c_char with caller responsibility to free using dash_string_free
Input strings in FFI functions are *const c_char (borrowed, not freed by C caller)
Add cbindgen annotations for complex types in FFI functions
Use thread-local storage for error propagation via dash_spv_ffi_get_last_error() function

Files:

  • dash-spv-ffi/src/client.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (CLAUDE.md)

Never hardcode network parameters, addresses, or keys

Files:

  • dash-spv-ffi/src/client.rs
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rs: Use proper error types (thiserror) and propagate errors appropriately
Use tokio runtime for async operations
Use conditional compilation with feature flags for optional features
Write unit tests for new functionality
Use secure random number generation for keys
Never log or expose private keys
Code must target Rust 1.89 minimum supported version (MSRV)
Format code using cargo fmt
Pass clippy linting without warnings

**/*.rs: MSRV (Minimum Supported Rust Version) is 1.89; ensure compatibility with this version for all builds
Unit tests should live alongside code with #[cfg(test)] annotation; integration tests use the tests/ directory
Use snake_case for function and variable names
Use UpperCamelCase for types and traits
Use SCREAMING_SNAKE_CASE for constants
Format code with rustfmt before commits; ensure cargo fmt --all is run
Run cargo clippy --workspace --all-targets -- -D warnings for linting; avoid warnings in CI
Prefer async/await via tokio for asynchronous operations

Files:

  • dash-spv-ffi/src/client.rs
**/*-ffi/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*-ffi/**/*.rs: Exercise careful handling at FFI boundaries for memory safety
Be careful with FFI memory management

Files:

  • dash-spv-ffi/src/client.rs
🧠 Learnings (24)
📓 Common learnings
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:01:37.609Z
Learning: The mempool tracking infrastructure (UnconfirmedTransaction, MempoolState, configuration, and mempool_filter.rs) is fully implemented and integrated in the Dash SPV client as of this PR, including client logic, FFI APIs, and tests.
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/sync/**/*.rs : Implement sequential phase-based synchronization via SyncManager with phases progressing in order: Headers → Masternode List → Filter Headers → Filters → Blocks
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Prepare support for future Dash upgrades including Schnorr/Taproot support, descriptor wallets, multi-signature account types, and Lightning Network payment channels
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/client/**/*.rs : Use the DashSpvClient high-level API with proper configuration via ClientConfig for client initialization
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/storage/**/*.rs : Store headers in 10,000-header segments with index files in headers/ directory, filter headers and compact block filters in filters/ directory, and state in state/ directory
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:47:37.438Z
Learning: Transaction IDs (txids) and block hashes in Dash are always 32 bytes and should be passed as fixed-size byte arrays in FFI interfaces, not as C strings, to avoid inefficiency and encoding issues.
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/network/**/*.rs : Use DNS-first peer discovery with automatic DNS seeds (dnsseed.dash.org, testnet-seed.dashdot.io) when no explicit peers are configured; implement immediate startup with 10-second delay only for subsequent peer searches
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/validation/**/*.rs : Implement three validation modes: ValidationMode::None (no validation), ValidationMode::Basic (structure and timestamp validation), and ValidationMode::Full (complete PoW and chain validation)
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: When debugging FFI issues, check `dash_spv_ffi_get_last_error()` for error details
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-22T17:59:37.833Z
Learning: Applies to **/{dash-network,dash-spv,key-wallet}/**/*.rs : Use async/await for async operations in network and wallet modules
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/tests/unit/**/*.rs : Add corresponding unit tests in `tests/unit/` for each new FFI function

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-06-26T16:01:37.609Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:01:37.609Z
Learning: The mempool tracking infrastructure (UnconfirmedTransaction, MempoolState, configuration, and mempool_filter.rs) is fully implemented and integrated in the Dash SPV client as of this PR, including client logic, FFI APIs, and tests.

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Add cbindgen annotations for complex types in FFI functions

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Use `#[no_mangle] extern "C"` attribute when implementing new FFI functions in Rust

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Use thread-local storage for error propagation via `dash_spv_ffi_get_last_error()` function

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/client/**/*.rs : Use the DashSpvClient high-level API with proper configuration via ClientConfig for client initialization

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Input strings in FFI functions are `*const c_char` (borrowed, not freed by C caller)

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/tests/c_tests/**/*.{c,h} : Add corresponding C tests in `tests/c_tests/` for each new FFI function

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/sync/**/*.rs : Implement sequential phase-based synchronization via SyncManager with phases progressing in order: Headers → Masternode List → Filter Headers → Filters → Blocks

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Rust strings must be returned as `*const c_char` with caller responsibility to free using `dash_string_free`

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/managed_account/**/*.rs : Implement atomic state updates when processing transactions: update transactions, UTXOs, balances, and address usage state together

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/transaction_checking/**/*.rs : Implement transaction classification and routing through `TransactionRouter` to avoid checking all accounts for every transaction

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-22T17:59:37.833Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-22T17:59:37.833Z
Learning: Applies to **/{dash-network,dash-spv,key-wallet}/**/*.rs : Use async/await for async operations in network and wallet modules

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-06-26T15:54:02.509Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:54:02.509Z
Learning: The `StorageManager` trait in `dash-spv/src/storage/mod.rs` uses `&mut self` methods but is also `Send + Sync`, and implementations often use interior mutability for concurrency. This can be confusing, so explicit documentation should clarify thread-safety expectations and the rationale for the API design.

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-06-26T16:02:42.390Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:02:42.390Z
Learning: Passing exclusive mutable references to network and storage managers into the sync manager in async Rust can lead to borrow checker issues or runtime contention if concurrent access is needed elsewhere. It's advisable to document this architectural tradeoff and consider refactoring to interior mutability or message passing for shared access as the codebase evolves.

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/*.rs : Apply atomic state updates when managing watch-only wallets: validate that external signatures match expected pubkeys and never attempt signing operations

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/**/*.rs : Use async/await throughout the codebase, built on tokio runtime

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/*.rs : Use `BTreeMap` for ordered data (accounts, transactions) and `HashMap` for lookups (address mappings); apply memory management strategies for old transaction data

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/error.rs : Use domain-specific error types (NetworkError, StorageError, SyncError, ValidationError, SpvError) rather than generic error handling

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-08-21T05:01:58.949Z
Learnt from: QuantumExplorer
Repo: dashpay/rust-dashcore PR: 108
File: key-wallet-ffi/src/wallet_manager.rs:270-318
Timestamp: 2025-08-21T05:01:58.949Z
Learning: In the key-wallet-ffi design, wallets retrieved from the wallet manager via lookup functions should return const pointers (*const FFIWallet) to enforce read-only access and prevent unintended modifications. The wallet manager should control wallet lifecycle and mutations through specific APIs rather than allowing external mutation of retrieved wallet references.

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/**/*.rs : Use Arc<dyn TraitName> for trait objects to enable runtime polymorphism for NetworkManager and StorageManager

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : All FFI types must have corresponding `_destroy()` functions for explicit memory management

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-06-26T15:47:37.438Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:47:37.438Z
Learning: Transaction IDs (txids) and block hashes in Dash are always 32 bytes and should be passed as fixed-size byte arrays in FFI interfaces, not as C strings, to avoid inefficiency and encoding issues.

Applied to files:

  • dash-spv-ffi/src/client.rs
🧬 Code graph analysis (1)
dash-spv-ffi/src/client.rs (3)
dash-spv/src/client/core.rs (2)
  • storage (168-170)
  • chain_state (203-206)
dash-spv-ffi/src/error.rs (1)
  • set_last_error (26-31)
dash-spv/src/client/status_display.rs (1)
  • chain_state (146-149)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (dash_deserialize_amount)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: Core Components Tests
  • GitHub Check: fuzz (hashes_sha512)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (dash_deser_net_msg)
  • GitHub Check: fuzz (dash_outpoint_string)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: Key Wallet Components Tests
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: SPV Components Tests
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: RPC Tests (stable, true)
  • GitHub Check: Pre-commit (ubuntu-latest)
  • GitHub Check: Pre-commit (macos-latest)
🔇 Additional comments (4)
dash-spv-ffi/src/client.rs (4)

1254-1353: LGTM! Correct async mutex handling.

The function correctly obtains the storage Arc (line 1305) and releases the std::sync::Mutex guard before awaiting on the async storage.lock() at line 1314. Range validation and FFI memory transfer are properly implemented.


1643-1750: LGTM! Proper take/put pattern avoids mutex-across-await.

The function correctly takes the client out of the mutex (lines 1691-1700), performs the async operation without holding any std::sync::Mutex (line 1703), then puts the client back (lines 1705-1709). This is the correct pattern to avoid Clippy's await_holding_lock warning.


1768-1807: LGTM! Correct take/put pattern avoids mutex-across-await.

The function correctly takes the client out of the mutex (lines 1778-1787), accesses the wallet and awaits without holding any std::sync::Mutex on inner (line 1792), then puts the client back (lines 1798-1801). This addresses the Clippy await_holding_lock warning mentioned in the PR objectives.


1809-1875: LGTM! Correct take/put pattern avoids mutex-across-await.

The function correctly takes the client out of the mutex (lines 1835-1846), performs async wallet access without holding any std::sync::Mutex on inner (line 1851), then puts the client back (lines 1866-1869). This properly addresses the Clippy await_holding_lock warning mentioned in the PR objectives. The logic to count unique block heights is also correct.


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.

@github-actions
Copy link

⚠️ FFI Documentation Update Required

The FFI API documentation is out of date. Please regenerate it by running:

For key-wallet-ffi:

cd key-wallet-ffi
make update-docs

For dash-spv-ffi:

cd dash-spv-ffi
make update-docs

Then commit the changes:

git add */FFI_API.md
git commit -m "docs: update FFI API documentation"

This ensures the documentation stays in sync with the actual FFI functions.

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 (8)
dash-spv/src/storage/mod.rs (1)

130-133: Filter height/query APIs match backing implementations

The new get_stored_filter_height and load_filters methods, including the 10,000-block range cap and “skip missing filters” semantics, line up with both disk and memory implementations and give a clear abstraction for batch filter access.

As an optional refinement, you might briefly note that the Range<u32> end is exclusive (e.g., “heights in [range.start, range.end)”) in the docs to make the boundary behavior explicit for callers.

Also applies to: 152-159

dash-spv/tests/storage_test.rs (1)

319-352: Prefer matching on StorageError::InvalidInput over error string text

The limit checks in test_load_filters_range_limit currently assert on to_string() contents. That works, but it’s a bit brittle if the error message wording changes.

You can make the test more robust by matching the concrete error variant and only optionally asserting on the message, e.g.:

-use dash_spv::storage::{MemoryStorageManager, StorageManager};
+use dash_spv::storage::{MemoryStorageManager, StorageManager};
+use dash_spv::error::StorageError;

-    let result = storage.load_filters(0..10_001).await;
-    assert!(result.is_err(), "Should reject range exceeding 10,000 blocks");
-    let error_msg = result.unwrap_err().to_string();
-    assert!(
-        error_msg.contains("exceeds maximum") && error_msg.contains("10000"),
-        "Error message should mention the limit"
-    );
+    let result = storage.load_filters(0..10_001).await;
+    match result {
+        Err(StorageError::InvalidInput(msg)) => {
+            assert!(
+                msg.contains("exceeds maximum") && msg.contains("10000"),
+                "Error message should mention the limit"
+            );
+        }
+        other => panic!("Expected StorageError::InvalidInput, got {:?}", other),
+    }

This keeps the semantic check while tying the assertion to the intended error variant rather than its string formatting.

dash-spv/src/sync/sequential/message_handlers.rs (1)

607-612: Filter persistence and match recording logic looks correct; minor clone can be avoided

Persisting the CFilter after header verification and then recording matched_wallet_ids into ChainState gives you durable filter data plus per-height wallet match tracking, which fits the new filter_matches model and the surrounding sync/FFI changes. Error mapping to SyncError::Storage is consistent with existing patterns.

One small refinement: you currently clone matched_wallet_ids just to pass ownership into record_filter_matches:

chain_state.record_filter_matches(height, matched_wallet_ids.clone());

Since you only use the vector afterward for its length in logs, you could avoid the clone by capturing the count first and then moving the vector:

let match_count = matched_wallet_ids.len();
// ...
chain_state.record_filter_matches(height, matched_wallet_ids);
// use match_count for logging below

This keeps behavior identical while avoiding an extra allocation/copy on each matching filter.

Also applies to: 613-621, 625-655, 656-661

key-wallet-manager/src/wallet_interface.rs (1)

35-40: Trait docs and return type look good; consider a WalletId alias for clarity

The new documentation and Vec<[u8; 32]> return type clearly define the semantics (empty = no download, non-empty = download those wallet IDs) and align with the rest of the PR.

Given that this Vec<[u8; 32]> shape now appears in several places (here, chain state, filter matching), consider introducing a dedicated alias or newtype (e.g. type WalletId = [u8; 32];) and using Vec<WalletId> in the trait and downstream code to improve readability and reduce potential for mismatched array lengths.

Also applies to: 41-47

dash-spv/src/sync/sequential/phase_execution.rs (1)

153-186: Consider clamping start_height with sync_base_height to avoid redundant filter downloads

The resume logic from stored_filter_height is sensible, but there are a couple of edge cases worth tightening:

  • If stored_filter_height < self.header_sync.get_sync_base_height(), you’ll resume below the sync base and re-download filters you’ll never use. Clamping to max(stored_height + 1, sync_base_height.max(1)) would avoid this.
  • If stored_filter_height > filter_header_tip, the current code will log that all filters are downloaded and skip the phase, but silently accepts an inconsistent state (stored past tip). Emitting a warning (or falling back to a safer strategy) could make diagnosing storage corruption easier.

Refactoring the start_height calculation to incorporate sync_base_height in the Some(stored_height) branch would address the first point without changing the main happy-path behavior.

Also applies to: 188-200

dash-spv/src/storage/disk/filters.rs (1)

263-289: Range-bounded bulk filter loading is correct; consider de-duplicating MAX_RANGE

The implementation:

  • Uses saturating_sub to avoid underflow,
  • Enforces a 10,000-block range cap, and
  • Skips missing filters while returning (height, filter) pairs.

For maintainability, you might optionally extract MAX_RANGE to a shared constant used by both disk and memory implementations to prevent divergence over time.

dash-spv/src/types.rs (1)

428-448: Consider adding deduplication logic to prevent duplicate wallet IDs.

Both record_filter_match and record_filter_matches can insert duplicate wallet IDs at the same height if called multiple times. This could lead to memory bloat and incorrect counting.

If deduplication is needed, consider using a BTreeSet or checking for existence before inserting:

 pub fn record_filter_match(&mut self, height: u32, wallet_id: [u8; 32]) {
-    self.filter_matches.entry(height).or_insert_with(Vec::new).push(wallet_id);
+    let vec = self.filter_matches.entry(height).or_insert_with(Vec::new);
+    if !vec.contains(&wallet_id) {
+        vec.push(wallet_id);
+    }
 }

Alternatively, change the type to BTreeMap<u32, BTreeSet<[u8; 32]>> if order doesn't matter:

-pub filter_matches: BTreeMap<u32, Vec<[u8; 32]>>,
+pub filter_matches: BTreeMap<u32, BTreeSet<[u8; 32]>>,
dash-spv-ffi/src/types.rs (1)

609-628: Memory management is correct, but note the logic duplication.

The destroy function properly frees nested allocations: first each filter's data (lines 614-622), then the filters array itself (line 625). However, the filter data deallocation logic is duplicated from dash_spv_ffi_compact_filter_destroy (lines 595-597).

This follows the existing pattern in the codebase (similar to FFIUnconfirmedTransaction), so it's consistent, but if the FFICompactFilter structure changes, both functions must be updated.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da21b9d and c0fc4ca.

📒 Files selected for processing (20)
  • dash-spv-ffi/include/dash_spv_ffi.h (5 hunks)
  • dash-spv-ffi/src/client.rs (2 hunks)
  • dash-spv-ffi/src/types.rs (1 hunks)
  • dash-spv-ffi/tests/unit/test_client_lifecycle.rs (1 hunks)
  • dash-spv-ffi/tests/unit/test_type_conversions.rs (1 hunks)
  • dash-spv/src/client/block_processor.rs (2 hunks)
  • dash-spv/src/client/block_processor_test.rs (2 hunks)
  • dash-spv/src/error.rs (2 hunks)
  • dash-spv/src/storage/disk/filters.rs (3 hunks)
  • dash-spv/src/storage/disk/state.rs (3 hunks)
  • dash-spv/src/storage/memory.rs (2 hunks)
  • dash-spv/src/storage/mod.rs (2 hunks)
  • dash-spv/src/storage/sync_state.rs (4 hunks)
  • dash-spv/src/sync/filters/matching.rs (1 hunks)
  • dash-spv/src/sync/sequential/message_handlers.rs (2 hunks)
  • dash-spv/src/sync/sequential/phase_execution.rs (1 hunks)
  • dash-spv/src/types.rs (4 hunks)
  • dash-spv/tests/storage_test.rs (1 hunks)
  • key-wallet-manager/src/wallet_interface.rs (1 hunks)
  • key-wallet-manager/src/wallet_manager/process_block.rs (1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-06-26T16:01:37.609Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:01:37.609Z
Learning: The mempool tracking infrastructure (UnconfirmedTransaction, MempoolState, configuration, and mempool_filter.rs) is fully implemented and integrated in the Dash SPV client as of this PR, including client logic, FFI APIs, and tests.

Applied to files:

  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv/src/storage/mod.rs
  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv-ffi/tests/unit/test_client_lifecycle.rs
  • dash-spv/src/sync/sequential/phase_execution.rs
  • dash-spv/src/storage/disk/filters.rs
  • dash-spv/src/types.rs
  • dash-spv/src/sync/filters/matching.rs
  • dash-spv/src/storage/memory.rs
  • dash-spv/src/client/block_processor.rs
  • dash-spv-ffi/src/types.rs
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv/src/client/block_processor_test.rs
  • dash-spv-ffi/src/client.rs
  • dash-spv/src/storage/disk/state.rs
  • dash-spv/src/storage/sync_state.rs
📚 Learning: 2025-06-26T15:54:02.509Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:54:02.509Z
Learning: The `StorageManager` trait in `dash-spv/src/storage/mod.rs` uses `&mut self` methods but is also `Send + Sync`, and implementations often use interior mutability for concurrency. This can be confusing, so explicit documentation should clarify thread-safety expectations and the rationale for the API design.

Applied to files:

  • dash-spv/src/error.rs
  • dash-spv/src/storage/mod.rs
  • dash-spv/tests/storage_test.rs
  • dash-spv/src/storage/memory.rs
  • dash-spv-ffi/src/types.rs
  • dash-spv/src/storage/disk/state.rs
  • dash-spv/src/storage/sync_state.rs
📚 Learning: 2025-06-15T15:31:44.136Z
Learnt from: QuantumExplorer
Repo: dashpay/rust-dashcore PR: 74
File: key-wallet/src/address.rs:30-40
Timestamp: 2025-06-15T15:31:44.136Z
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.

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/client/block_processor_test.rs
📚 Learning: 2025-06-26T15:47:37.438Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:47:37.438Z
Learning: Transaction IDs (txids) and block hashes in Dash are always 32 bytes and should be passed as fixed-size byte arrays in FFI interfaces, not as C strings, to avoid inefficiency and encoding issues.

Applied to files:

  • dash-spv-ffi/src/types.rs
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-06-26T15:46:56.854Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:46:56.854Z
Learning: Transaction IDs (txids) in Dash are always 32 bytes and should be passed as fixed-size byte arrays in FFI interfaces, not as C strings, to avoid inefficiency and encoding issues.

Applied to files:

  • dash-spv-ffi/include/dash_spv_ffi.h
📚 Learning: 2025-08-21T05:01:58.949Z
Learnt from: QuantumExplorer
Repo: dashpay/rust-dashcore PR: 108
File: key-wallet-ffi/src/wallet_manager.rs:270-318
Timestamp: 2025-08-21T05:01:58.949Z
Learning: In the key-wallet-ffi design, wallets retrieved from the wallet manager via lookup functions should return const pointers (*const FFIWallet) to enforce read-only access and prevent unintended modifications. The wallet manager should control wallet lifecycle and mutations through specific APIs rather than allowing external mutation of retrieved wallet references.

Applied to files:

  • dash-spv-ffi/include/dash_spv_ffi.h
📚 Learning: 2025-02-25T06:13:52.858Z
Learnt from: QuantumExplorer
Repo: dashpay/rust-dashcore PR: 51
File: dash/src/sml/masternode_list/scores_for_quorum.rs:50-73
Timestamp: 2025-02-25T06:13:52.858Z
Learning: ScoreHash is a cryptographic hash in the rust-dashcore library, and therefore does not need collision handling when used as a key in collections like BTreeMap due to the extremely low probability of collisions.

Applied to files:

  • dash-spv/src/storage/disk/state.rs
  • dash-spv/src/storage/sync_state.rs
🧬 Code graph analysis (12)
dash-spv/src/storage/mod.rs (3)
dash-spv/src/storage/disk/filters.rs (2)
  • get_stored_filter_height (191-243)
  • load_filters (269-289)
dash-spv/src/storage/disk/state.rs (2)
  • get_stored_filter_height (550-552)
  • load_filters (578-580)
dash-spv/src/storage/memory.rs (2)
  • get_stored_filter_height (261-268)
  • load_filters (297-315)
dash-spv/src/sync/sequential/message_handlers.rs (3)
dash-spv/src/client/core.rs (2)
  • storage (171-173)
  • chain_state (206-209)
dash-spv/src/storage/disk/manager.rs (1)
  • new (79-129)
dash-spv/src/sync/sequential/lifecycle.rs (1)
  • new (31-62)
dash-spv-ffi/tests/unit/test_client_lifecycle.rs (2)
dash-spv-ffi/src/client.rs (4)
  • dash_spv_ffi_client_new (140-210)
  • dash_spv_ffi_client_get_transaction_count (1790-1817)
  • dash_spv_ffi_client_destroy (1417-1455)
  • dash_spv_ffi_client_get_blocks_with_transactions_count (1835-1873)
dash-spv-ffi/src/config.rs (1)
  • dash_spv_ffi_config_destroy (345-354)
key-wallet-manager/src/wallet_manager/process_block.rs (2)
key-wallet-manager/src/wallet_manager/mod.rs (2)
  • new (70-76)
  • new (107-114)
key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs (2)
  • wallet_id (30-30)
  • wallet_id (126-128)
dash-spv/src/sync/sequential/phase_execution.rs (1)
dash-spv/src/sync/headers_with_reorg.rs (1)
  • get_sync_base_height (1119-1121)
dash-spv/tests/storage_test.rs (1)
dash-spv/src/storage/memory.rs (1)
  • new (30-42)
dash-spv/src/storage/disk/filters.rs (4)
dash-spv/src/storage/disk/state.rs (2)
  • get_stored_filter_height (550-552)
  • load_filters (578-580)
dash-spv/src/storage/memory.rs (3)
  • get_stored_filter_height (261-268)
  • load_filters (297-315)
  • new (30-42)
dash-spv/src/storage/mod.rs (2)
  • get_stored_filter_height (132-132)
  • load_filters (158-158)
dash-spv/src/storage/disk/manager.rs (1)
  • new (79-129)
dash-spv/src/storage/memory.rs (3)
dash-spv/src/storage/disk/filters.rs (2)
  • get_stored_filter_height (191-243)
  • load_filters (269-289)
dash-spv/src/storage/disk/state.rs (2)
  • get_stored_filter_height (550-552)
  • load_filters (578-580)
dash-spv/src/storage/mod.rs (2)
  • get_stored_filter_height (132-132)
  • load_filters (158-158)
key-wallet-manager/src/wallet_interface.rs (2)
dash-spv/src/client/block_processor_test.rs (2)
  • check_compact_filter (66-74)
  • check_compact_filter (289-297)
key-wallet-manager/src/wallet_manager/process_block.rs (1)
  • check_compact_filter (87-123)
dash-spv-ffi/include/dash_spv_ffi.h (2)
dash-spv-ffi/src/client.rs (4)
  • dash_spv_ffi_client_load_filters (1310-1385)
  • dash_spv_ffi_client_get_filter_matched_heights (1697-1772)
  • dash_spv_ffi_client_get_transaction_count (1790-1817)
  • dash_spv_ffi_client_get_blocks_with_transactions_count (1835-1873)
dash-spv-ffi/src/types.rs (4)
  • dash_spv_ffi_compact_filter_destroy (592-599)
  • dash_spv_ffi_compact_filters_destroy (609-628)
  • dash_spv_ffi_filter_match_entry_destroy (663-674)
  • dash_spv_ffi_filter_matches_destroy (684-701)
dash-spv-ffi/src/client.rs (2)
dash-spv-ffi/src/error.rs (1)
  • set_last_error (26-31)
dash-spv/src/client/core.rs (1)
  • wallet (161-163)
dash-spv/src/storage/disk/state.rs (3)
dash-spv/src/storage/memory.rs (2)
  • get_stored_filter_height (261-268)
  • load_filters (297-315)
dash-spv/src/storage/disk/filters.rs (2)
  • get_stored_filter_height (191-243)
  • load_filters (269-289)
dash-spv/src/storage/mod.rs (2)
  • get_stored_filter_height (132-132)
  • load_filters (158-158)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
  • GitHub Check: update-docs-comment
  • GitHub Check: fuzz (hashes_sha512)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (dash_deserialize_amount)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (dash_outpoint_string)
  • GitHub Check: fuzz (dash_deser_net_msg)
  • GitHub Check: RPC Tests (stable, true)
  • GitHub Check: Core Components Tests
  • GitHub Check: Strict Warnings and Clippy Checks
🔇 Additional comments (26)
dash-spv-ffi/tests/unit/test_type_conversions.rs (1)

165-176: Initialize new filter_matches field consistently

Setting filter_matches to an empty BTreeMap keeps the test’s ChainState construction aligned with the updated struct and avoids uninitialized field issues. Looks correct and consistent with the new persistence model.

dash-spv/src/error.rs (1)

114-116: StorageError::InvalidInput variant is correctly integrated

The new InvalidInput(String) variant and its explicit arm in the manual Clone implementation are wired consistently with the existing string-based variants, so downstream storage code can distinguish invalid input cleanly without changing existing behavior.

Also applies to: 118-130

dash-spv-ffi/tests/unit/test_client_lifecycle.rs (1)

245-300: Nice coverage for zero-count and null-client FFI behavior

These tests validate the expected 0 counts for fresh wallets and ensure the new FFI query functions behave safely when passed a null client pointer, aligning with the null_check! behavior in client.rs. The lifecycle (create → query → destroy) mirrors existing patterns, so this fits cleanly into the test suite.

dash-spv/tests/storage_test.rs (1)

288-317: Filter range behavior is well covered

test_load_filters_range does a good job exercising the intended semantics of load_filters: skipping missing heights, confirming end-exclusive ranges (101..103 only returns 101 even though 103 exists), and returning an empty vector when no filters are present in the range. This aligns with the MemoryStorageManager implementation.

dash-spv/src/client/block_processor.rs (1)

180-190: Compact filter handling correctly adapted to per-wallet matches

Using matched_wallet_ids and deriving has_matches preserves the existing boolean contract for callers while enriching internal logging with the number of matching wallets. The control flow and locking remain consistent with prior behavior.

Also applies to: 195-205

dash-spv/src/sync/filters/matching.rs (1)

54-60: Filter–wallet integration cleanly exposes per-wallet matches

check_filter_for_matches now correctly wraps the wallet’s check_compact_filter, returning the Vec<[u8; 32]> of wallet IDs and logging only when matches exist. This aligns with the updated WalletInterface and the broader move toward per-wallet filter match tracking.

Also applies to: 61-69

dash-spv/src/storage/disk/filters.rs (2)

8-8: Importing StorageError for range validation is appropriate

The added import is necessary for the new load_filters range validation and is consistent with existing error handling in this module.


185-243: Directory-based stored filter height detection looks robust

The scan correctly:

  • Ignores non-files and non-.dat entries.
  • Restricts to purely numeric filenames and caps heights at 2,000,000 with a warning.
  • Returns None when no valid filter files are present.

This aligns with the documented intent to reflect actually persisted filters rather than headers.

key-wallet-manager/src/wallet_manager/process_block.rs (1)

87-123: Per-wallet compact filter matching and caching behavior looks correct

The new implementation:

  • Evaluates each wallet independently using its monitored_addresses(network),
  • Builds per-wallet script pubkeys and calls filter.match_any(..).unwrap_or(false),
  • Collects wallet IDs with hits, and
  • Caches any_match as a boolean in filter_matches, preserving the prior “any wallet matched” semantics.

This cleanly exposes matched wallet IDs while keeping the existing cache contract.

dash-spv/src/storage/memory.rs (2)

261-268: In-memory stored filter height computation is simple and correct

Using self.filters.keys().max().copied() to derive the highest stored filter height (or None when empty) matches the intended semantics and is consistent with how filters are stored in this manager.


297-315: In-memory bulk filter loading correctly mirrors disk behavior

The range-bounded load_filters:

  • Enforces the same 10,000-block maximum range as the disk implementation,
  • Handles inverted ranges safely via saturating_sub,
  • Iterates the range and returns (height, filter.clone()) only for existing entries.

This keeps memory and disk storage backends behaviorally aligned.

dash-spv/src/client/block_processor_test.rs (2)

66-74: MockWallet’s check_compact_filter adaptation matches new API

Returning a non-empty Vec<[u8; 32]> with a fixed test wallet ID for all filters is a straightforward way to signal “matched” in tests under the new signature.


289-297: NonMatchingWallet correctly models the “no match” case

Returning an empty Vec<[u8; 32]> cleanly represents a non-matching filter for tests and aligns with the updated WalletInterface contract.

dash-spv/src/storage/disk/state.rs (3)

5-5: Range import is appropriate for new trait method signature

The added use std::ops::Range; matches the new load_filters method signature and keeps the implementation concise.


550-552: Trait forwarding for get_stored_filter_height is consistent

Delegating StorageManager::get_stored_filter_height to DiskStorageManager::get_stored_filter_height matches the existing pattern used throughout this impl and correctly exposes the disk-level helper.


578-580: load_filters delegation cleanly exposes disk-backed bulk loading

Forwarding load_filters through the StorageManager impl ensures callers can use the new disk bulk filter loading API via the trait without duplicating logic.

dash-spv/src/types.rs (3)

285-287: Good choice of data structure.

Using BTreeMap<u32, Vec<[u8; 32]>> enables efficient range queries via the range() method, which is exactly what get_filter_matched_heights leverages. The 32-byte fixed arrays for wallet IDs align with FFI best practices.

Based on learnings


461-480: LGTM! Good range validation and error handling.

The 10,000 block limit prevents abuse, and the error messages are clear. The use of saturating_sub prevents underflow, and BTreeMap::range() provides efficient extraction.

Note: Line 477 clones each Vec, which is necessary for thread safety across the FFI boundary but could be expensive for ranges with many matches. Given the 10K block limit, this is acceptable.


1163-1245: Excellent test coverage!

The tests comprehensively validate:

  • Single and bulk filter match recording
  • Range queries with hits and misses
  • Boundary conditions (exactly 10,000 blocks passes, 10,001 fails)
  • Empty and partially overlapping ranges
dash-spv-ffi/src/types.rs (2)

554-599: LGTM! Memory management is correct.

The FFICompactFilter struct and its destroy function properly handle ownership transfer. The use of Vec::from_raw_parts(data, data_len, data_len) is correct because the Vec is forgotten immediately after creation (see dash-spv-ffi/src/client.rs:1356-1359), ensuring len equals capacity.


630-701: LGTM! FFIFilterMatches memory management mirrors FFICompactFilters pattern.

The destroy functions correctly handle the two-level allocation:

  1. Array of entries (line 698)
  2. Each entry's wallet_ids array (lines 688-696)

The logic properly reconstructs and drops Vec<[u8; 32]> with matching len/capacity, which is safe given how these are created in dash-spv-ffi/src/client.rs:1742-1757.

dash-spv-ffi/src/client.rs (3)

1310-1385: LGTM! Well-designed for concurrent access.

Lines 1335-1344: The function gets a storage reference without taking the client mutex, allowing other operations to proceed concurrently. This is excellent for responsiveness during sync.

The memory transfer pattern (lines 1354-1370) correctly uses std::mem::forget to transfer ownership to the FFI caller, who must call dash_spv_ffi_compact_filters_destroy.


1697-1772: LGTM! Consistent pattern with proper ownership transfer.

The function follows the same concurrent-access pattern as load_filters, obtaining chain state without blocking other operations (lines 1722-1731). The BTreeMap-to-FFI conversion correctly transfers ownership via std::mem::forget (lines 1742-1757).


1790-1873: LGTM! Both count functions are well-implemented.

get_transaction_count (lines 1790-1817): Simple and correct, returns the persisted transaction history length.

get_blocks_with_transactions_count (lines 1835-1873): Properly counts unique block heights using a HashSet (lines 1854-1859), filtering for confirmed transactions only. The warning logs (lines 1810, 1864-1866) help with debugging.

dash-spv-ffi/include/dash_spv_ffi.h (2)

112-749: LGTM! C declarations correctly match Rust implementations.

The auto-generated header properly declares all new FFI types and functions:

  • Struct layouts match the Rust definitions
  • Function signatures correctly translate Rust types to C types
  • Memory management requirements are clearly documented (e.g., lines 116-118, 558-560, 687-688)
  • Safety requirements are explicit (e.g., lines 576-577, 704-705)

1156-1198: LGTM! Destroy function declarations are comprehensive and well-documented.

All four destroy functions are properly declared with clear safety documentation emphasizing:

  • Pointer validity requirements
  • One-time-only calling constraint
  • Risk of use-after-free

This matches the Rust implementations and follows FFI best practices.

@github-actions
Copy link

⚠️ FFI Documentation Update Required

The FFI API documentation is out of date. Please regenerate it by running:

For key-wallet-ffi:

cd key-wallet-ffi
make update-docs

For dash-spv-ffi:

cd dash-spv-ffi
make update-docs

Then commit the changes:

git add */FFI_API.md
git commit -m "docs: update FFI API documentation"

This ensures the documentation stays in sync with the actual FFI functions.

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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
key-wallet-ffi/include/key_wallet_ffi.h (1)

896-900: Documentation inconsistency: wrong enum type referenced.

The doc comment at line 899 says "Returns FFINetwork::NoNetworks" but the function returns FFINetworks, and the correct value is NO_NETWORKS. Same issue appears at lines 953 and 1009 for bls_account_get_network and eddsa_account_get_network.

Since this file is auto-generated by cbindgen, the fix should be in the Rust source doc comments.

🧹 Nitpick comments (2)
dash-spv-ffi/include/dash_spv_ffi.h (2)

713-731: Doc wording for transaction count is slightly misleading

The function name and summary (“Get the total count of transactions across all wallets”) are clear, but the sentence:

“Use this to show how many blocks contained relevant transactions for the user's wallets.”

describes what the blocks‑with‑transactions metric does, not transaction count. I’d suggest tightening the doc to avoid confusion with dash_spv_ffi_client_get_blocks_with_transactions_count.

Example rewording:

- * not the ephemeral sync statistics. Use this to show how many
- * blocks contained relevant transactions for the user's wallets.
+ * not the ephemeral sync statistics. Use this to show how many
+ * wallet-related transactions have been seen across all wallets.

1156-1199: Destroy helpers for new types match existing FFI patterns

The four destroy functions (dash_spv_ffi_compact_filter_destroy, dash_spv_ffi_compact_filters_destroy, dash_spv_ffi_filter_match_entry_destroy, dash_spv_ffi_filter_matches_destroy) match the crate’s pattern of providing explicit free functions for heap-owned FFI structs. The docs make the single‑ownership requirement clear (“must not be used after this function” / “only be called once per allocation”). Just ensure the high-level docs/examples clearly indicate which destroy function to call for objects returned by each API (e.g., arrays from *_client_load_filters / *_get_filter_matched_heights should only go through the array-level destroy to avoid accidental double frees).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0fc4ca and ec771fd.

📒 Files selected for processing (2)
  • dash-spv-ffi/include/dash_spv_ffi.h (7 hunks)
  • key-wallet-ffi/include/key_wallet_ffi.h (5 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-08-21T05:01:58.949Z
Learnt from: QuantumExplorer
Repo: dashpay/rust-dashcore PR: 108
File: key-wallet-ffi/src/wallet_manager.rs:270-318
Timestamp: 2025-08-21T05:01:58.949Z
Learning: In the key-wallet-ffi design, wallets retrieved from the wallet manager via lookup functions should return const pointers (*const FFIWallet) to enforce read-only access and prevent unintended modifications. The wallet manager should control wallet lifecycle and mutations through specific APIs rather than allowing external mutation of retrieved wallet references.

Applied to files:

  • key-wallet-ffi/include/key_wallet_ffi.h
  • dash-spv-ffi/include/dash_spv_ffi.h
📚 Learning: 2025-06-26T15:47:37.438Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:47:37.438Z
Learning: Transaction IDs (txids) and block hashes in Dash are always 32 bytes and should be passed as fixed-size byte arrays in FFI interfaces, not as C strings, to avoid inefficiency and encoding issues.

Applied to files:

  • key-wallet-ffi/include/key_wallet_ffi.h
  • dash-spv-ffi/include/dash_spv_ffi.h
📚 Learning: 2025-06-26T15:46:56.854Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:46:56.854Z
Learning: Transaction IDs (txids) in Dash are always 32 bytes and should be passed as fixed-size byte arrays in FFI interfaces, not as C strings, to avoid inefficiency and encoding issues.

Applied to files:

  • key-wallet-ffi/include/key_wallet_ffi.h
  • dash-spv-ffi/include/dash_spv_ffi.h
📚 Learning: 2025-06-26T16:01:37.609Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:01:37.609Z
Learning: The mempool tracking infrastructure (UnconfirmedTransaction, MempoolState, configuration, and mempool_filter.rs) is fully implemented and integrated in the Dash SPV client as of this PR, including client logic, FFI APIs, and tests.

Applied to files:

  • dash-spv-ffi/include/dash_spv_ffi.h
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: update-docs-comment
  • GitHub Check: RPC Tests (stable, true)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (dash_deserialize_amount)
  • GitHub Check: Key Wallet Components Tests
🔇 Additional comments (12)
dash-spv-ffi/include/dash_spv_ffi.h (7)

6-6: cbindgen version bump is fine

Updating the generated-header marker to cbindgen:0.29.2 is consistent with a regenerated FFI surface; no issues here.


32-35: Explicit mempool strategy discriminants preserve ABI

Reintroducing FFIMempoolStrategy here with explicit numeric values (FetchAll = 0, BloomFilter = 1) keeps the C ABI stable for existing callers and resolves any forward-declaration ordering issues with the config APIs.


112-153: Compact filter structs and ownership semantics look correct

The FFICompactFilter / FFICompactFilters layout (height + uint8_t *data + uintptr_t data_len, plus array-of-filters) is FFI-safe and matches the existing FFIArray/usize pattern. The docs clearly direct callers to free results from dash_spv_ffi_client_load_filters via dash_spv_ffi_compact_filters_destroy, and dash_spv_ffi_compact_filter_destroy is available for single-filter allocations. I don’t see ABI or ownership issues here.


216-252: Filter match structs follow the established 32‑byte ID pattern

FFIFilterMatchEntry and FFIFilterMatches mirror the compact-filter pattern and use uint8_t (*wallet_ids)[32] for fixed-size 32‑byte identifiers, which aligns with the existing practice of passing binary IDs as fixed arrays instead of strings. The docs also make it clear that dash_spv_ffi_filter_matches_destroy is responsible for freeing both the entries array and each entry’s wallet_ids. Based on learnings, this is consistent with how txids/block hashes are handled.


555-583: Range-based filter loading API is well-specified; confirm inclusive/exclusive expectations

The dash_spv_ffi_client_load_filters API is clearly documented: [start_height, end_height) semantics, 10 000‑block max range, null on error with details via dash_spv_ffi_get_last_error, and ownership transferred to the caller via dash_spv_ffi_compact_filters_destroy. Functionally and ABI‑wise this looks sound; just ensure the Rust implementation enforces the same inclusive/exclusive bounds and range limit so C callers don’t hit off‑by‑one surprises.


683-711: Filter match query API pairs cleanly with the new match structs

dash_spv_ffi_client_get_filter_matched_heights mirrors client_load_filters in range semantics, error handling, and ownership rules, and returns the new FFIFilterMatches wrapper. This is a coherent extension of the FFI surface; as with filters, just confirm the Rust side enforces the documented [start_height, end_height) range and 10 000‑block limit.


732-749: Blocks-with-transactions count API is consistent and useful

dash_spv_ffi_client_get_blocks_with_transactions_count rounds out the metrics nicely: it returns a persistent count of unique block heights with relevant transactions and documents the “0 if client not initialized or wallet not available” behavior. Signature and semantics are consistent with dash_spv_ffi_client_get_transaction_count.

key-wallet-ffi/include/key_wallet_ffi.h (5)

27-35: LGTM: Well-designed single-network enum.

The FFINetwork enum correctly uses sequential values for single network selection, complementing the bit-flag FFINetworks enum for multi-network scenarios.


97-107: LGTM: Correct bit flag implementation.

The FFINetworks enum properly uses powers of 2 for individual flags, and ALL_NETWORKS = 15 correctly represents the bitwise OR of all network flags (1|2|4|8).


128-144: LGTM: Clear address pool type enumeration.

The enum appropriately uses sequential values for mutually exclusive pool types, with good documentation for each variant.


184-208: LGTM: Account creation options properly defined.

The enum provides clear, mutually exclusive options for wallet account creation with helpful documentation for each variant.


814-818: API consistency between FFINetwork and FFINetworks is correctly implemented.

The Rust implementation confirms the review comment's claims:

  • wallet_create_from_mnemonic_with_options accepts FFINetworks (bit flags) for multi-network wallet configuration
  • wallet_get_account accepts FFINetwork (single value) for network-specific operations
  • Proper From/TryFrom trait implementations exist in key-wallet-ffi/src/types.rs for bidirectional conversion between Network, FFINetwork, and FFINetworks

The API design correctly distinguishes between wallet-level multi-network configuration and operation-level single-network targeting.

Copy link
Collaborator

@ZocoLini ZocoLini left a comment

Choose a reason for hiding this comment

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

Changes found:

  • WalletInterface::check_compact_filter now returns a vector of 32-byte wallet IDs that matched the filter. This method is used by FilterSyncManager<S, N>::check_filter_for_matches that returns the vector of wallet IDs for later being stored in ChainState.filter_matches field. The function dash_spv_ffi_client_get_filter_matched_heights calls ChainState::get_filter_matched_heights to extract data for the previous mentioned field.
  • the function dash_spv_ffi_client_load_filters makes use of the new StorageManager::load_filters method. All the structs that implement the trait were updated to implement it.
  • The new method StorageManager::get_stored_filter_height doesn't look like it has any relation with the filters exposition. Is more an optimization that allows downloading from the last stored filter during SyncPhase::DownloadingFilters
  • The FilterSyncState::filter_matches doesn't look like it is being used
  • Missing get_stored_filter_height, load_filters implementation in MockStorageManager (The file where this issue exists is no longer in the v0.41-dev branch)

It took my sometime but here is what I found. Almost everything has something to do with the filters exposition. Made some notes on stuff that doesn't make sense to me and the only change I found that has nothing to do with what this PR tries to add is the method StorageManager::get_stored_filter_height used to avoid downloading all the filters in each sync.

@QuantumExplorer if this looks good to you I will push the changes suggested

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec771fd and 0c98598.

📒 Files selected for processing (7)
  • dash-spv/src/storage/disk/state.rs (2 hunks)
  • dash-spv/src/storage/memory.rs (1 hunks)
  • dash-spv/src/storage/mod.rs (1 hunks)
  • dash-spv/src/storage/sync_state.rs (0 hunks)
  • dash-spv/src/sync/sequential/message_handlers.rs (2 hunks)
  • dash-spv/src/sync/sequential/phase_execution.rs (1 hunks)
  • dash-spv/src/types.rs (4 hunks)
💤 Files with no reviewable changes (1)
  • dash-spv/src/storage/sync_state.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • dash-spv/src/sync/sequential/phase_execution.rs
🧰 Additional context used
📓 Path-based instructions (5)
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rs: Never hardcode network parameters, addresses, or keys in Rust code
Use proper error types with thiserror crate and propagate errors appropriately in Rust code
Use tokio runtime for async operations in Rust code
Use conditional compilation with feature flags for optional features
Write unit tests for new functionality in Rust
Format Rust code with cargo fmt before committing
Ensure clippy passes with all warnings as errors on all Rust code

**/*.rs: Each crate keeps sources in src/; unit tests live alongside code with #[cfg(test)]
Maintain MSRV of 1.89 for Rust code
Format Rust code with rustfmt (see rustfmt.toml); run cargo fmt --all before commits
Lint Rust code with clippy; avoid unwrap()/expect() in library code; use error types (e.g., thiserror)
Use snake_case for function and variable names in Rust
Use UpperCamelCase for types and traits in Rust
Use SCREAMING_SNAKE_CASE for constants in Rust
Follow mixed editions (2021/2024) and crate-specific idioms; prefer async via tokio where applicable
Unit tests should be placed near code with descriptive names (e.g., test_parse_address_mainnet)
Use #[ignore] for network-dependent or long-running tests; run with -- --ignored flag
Never commit secrets or real keys; avoid logging sensitive data in Rust code
Keep test vectors deterministic in Rust test code

Files:

  • dash-spv/src/storage/mod.rs
  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv/src/types.rs
  • dash-spv/src/storage/disk/state.rs
  • dash-spv/src/storage/memory.rs
dash-spv/src/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

dash-spv/src/**/*.rs: Use trait-based abstractions for swappable implementations (e.g., NetworkManager, StorageManager)
Use async/await throughout the codebase with tokio runtime
Use domain-specific error types (NetworkError, StorageError, SyncError, ValidationError, SpvError) for error handling
Use trait objects (Arc<dyn StorageManager>, Arc<dyn NetworkManager>) for runtime polymorphism
Use Tokio channels for inter-component message passing
Add comprehensive unit tests in-module for individual components

Files:

  • dash-spv/src/storage/mod.rs
  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv/src/types.rs
  • dash-spv/src/storage/disk/state.rs
  • dash-spv/src/storage/memory.rs
dash-spv/src/storage/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

dash-spv/src/storage/**/*.rs: Implement StorageManager trait with segmented storage: headers in 10,000-header segments with index files, filters in separate storage, and state persistence in dedicated paths
Use segmented storage with headers in 10,000-header chunks stored with index files for efficient retrieval

Files:

  • dash-spv/src/storage/mod.rs
  • dash-spv/src/storage/disk/state.rs
  • dash-spv/src/storage/memory.rs
dash-spv/src/sync/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

dash-spv/src/sync/**/*.rs: Use sequential phase-based synchronization with SequentialSyncManager for organized sync phases
Use state machines with SyncState enum to drive synchronization flow and state transitions

Files:

  • dash-spv/src/sync/sequential/message_handlers.rs
dash-spv/src/types.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

Define common data structures in types.rs including SyncProgress, ValidationMode, WatchItem, etc.

Files:

  • dash-spv/src/types.rs
🧠 Learnings (21)
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/storage/**/*.rs : Implement `StorageManager` trait with segmented storage: headers in 10,000-header segments with index files, filters in separate storage, and state persistence in dedicated paths

Applied to files:

  • dash-spv/src/storage/mod.rs
  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv/src/types.rs
  • dash-spv/src/storage/disk/state.rs
  • dash-spv/src/storage/memory.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/storage/**/*.rs : Use segmented storage with headers in 10,000-header chunks stored with index files for efficient retrieval

Applied to files:

  • dash-spv/src/storage/mod.rs
  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv/src/storage/disk/state.rs
  • dash-spv/src/storage/memory.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/**/*.rs : Use trait objects (`Arc<dyn StorageManager>`, `Arc<dyn NetworkManager>`) for runtime polymorphism

Applied to files:

  • dash-spv/src/storage/mod.rs
  • dash-spv/src/types.rs
  • dash-spv/src/storage/disk/state.rs
  • dash-spv/src/storage/memory.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/**/*.rs : Use trait-based abstractions for swappable implementations (e.g., `NetworkManager`, `StorageManager`)

Applied to files:

  • dash-spv/src/storage/mod.rs
  • dash-spv/src/types.rs
  • dash-spv/src/storage/disk/state.rs
  • dash-spv/src/storage/memory.rs
📚 Learning: 2025-06-26T15:54:02.509Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:54:02.509Z
Learning: The `StorageManager` trait in `dash-spv/src/storage/mod.rs` uses `&mut self` methods but is also `Send + Sync`, and implementations often use interior mutability for concurrency. This can be confusing, so explicit documentation should clarify thread-safety expectations and the rationale for the API design.

Applied to files:

  • dash-spv/src/storage/mod.rs
  • dash-spv/src/storage/disk/state.rs
  • dash-spv/src/storage/memory.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/**/*.rs : Use async/await throughout the codebase with tokio runtime

Applied to files:

  • dash-spv/src/storage/mod.rs
  • dash-spv/src/storage/disk/state.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/sync/**/*.rs : Use sequential phase-based synchronization with `SequentialSyncManager` for organized sync phases

Applied to files:

  • dash-spv/src/storage/mod.rs
  • dash-spv/src/sync/sequential/message_handlers.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/wallet/**/*.rs : Implement wallet functionality with UTXO tracking, balance calculation with confirmation states, and transaction processing

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv/src/types.rs
  • dash-spv/src/storage/disk/state.rs
📚 Learning: 2025-06-26T16:01:37.609Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:01:37.609Z
Learning: The mempool tracking infrastructure (UnconfirmedTransaction, MempoolState, configuration, and mempool_filter.rs) is fully implemented and integrated in the Dash SPV client as of this PR, including client logic, FFI APIs, and tests.

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv/src/types.rs
  • dash-spv/src/storage/memory.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/sync/**/*.rs : Use state machines with `SyncState` enum to drive synchronization flow and state transitions

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv/src/types.rs
  • dash-spv/src/storage/disk/state.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/transaction_checking/**/*.rs : In SPV sync integration, use `wallet_info.check_transaction()` with appropriate `TransactionContext` and update wallet state atomically only when `is_relevant` flag is true

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
📚 Learning: 2025-12-01T08:01:00.652Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: swift-dash-core-sdk/Examples/DashHDWalletExample/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:01:00.652Z
Learning: Applies to swift-dash-core-sdk/Examples/DashHDWalletExample/**/*.swift : Enable mempool tracking on SPV connection using the appropriate strategy (.fetchAll, .bloomFilter, .selective)

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/address_pool/**/*.rs : Implement staged gap limit management using `GapLimitStage` structure with tracking of last_used_index and used_indices HashSet to enable efficient address discovery without loading entire address chains into memory

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv/src/types.rs
  • dash-spv/src/storage/disk/state.rs
  • dash-spv/src/storage/memory.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/managed_account/**/*.rs : Implement atomic state updates in ManagedAccount: when processing transactions, update address usage state, UTXO set, transaction history, and balance calculations together to maintain consistency

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/**/*.rs : Add comprehensive unit tests in-module for individual components

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/tests/**/*.rs : Add integration tests in the `tests/` directory for comprehensive test suites

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/types.rs : Define common data structures in `types.rs` including `SyncProgress`, `ValidationMode`, `WatchItem`, etc.

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/{wallet,account,managed_account}/**/*.rs : Use BTreeMap for ordered data structures (accounts, transactions) and HashMap for quick lookups (address to index mapping) to optimize data structure performance

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/network/**/*.rs : Implement configurable timeouts with recovery mechanisms for network operations

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/**/*.rs : Use domain-specific error types (`NetworkError`, `StorageError`, `SyncError`, `ValidationError`, `SpvError`) for error handling

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-02-25T06:13:52.858Z
Learnt from: QuantumExplorer
Repo: dashpay/rust-dashcore PR: 51
File: dash/src/sml/masternode_list/scores_for_quorum.rs:50-73
Timestamp: 2025-02-25T06:13:52.858Z
Learning: ScoreHash is a cryptographic hash in the rust-dashcore library, and therefore does not need collision handling when used as a key in collections like BTreeMap due to the extremely low probability of collisions.

Applied to files:

  • dash-spv/src/storage/disk/state.rs
🧬 Code graph analysis (3)
dash-spv/src/sync/sequential/message_handlers.rs (1)
dash-spv/src/storage/disk/manager.rs (1)
  • new (79-129)
dash-spv/src/storage/disk/state.rs (3)
dash-spv/src/storage/memory.rs (1)
  • load_filters (288-306)
dash-spv/src/storage/mod.rs (1)
  • load_filters (154-154)
dash-spv/src/storage/disk/filters.rs (1)
  • load_filters (269-289)
dash-spv/src/storage/memory.rs (2)
dash-spv/src/storage/mod.rs (1)
  • load_filters (154-154)
dash-spv/src/storage/disk/filters.rs (1)
  • load_filters (269-289)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (dash_deser_net_msg)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (dash_outpoint_string)
  • GitHub Check: RPC Tests (stable, true)
  • GitHub Check: Core Components Tests
  • GitHub Check: Key Wallet Components Tests
  • GitHub Check: SPV Components Tests
  • GitHub Check: Pre-commit (ubuntu-latest)
  • GitHub Check: Pre-commit (macos-latest)
🔇 Additional comments (8)
dash-spv/src/types.rs (4)

18-18: LGTM! Well-chosen data structure for ordered filter matches.

Using BTreeMap<u32, Vec<[u8; 32]>> for filter_matches is appropriate as it provides efficient range-based queries (used in get_filter_matched_heights) and maintains height ordering naturally.

Also applies to: 284-287


428-443: LGTM! Efficient batch recording with proper merge handling.

The method correctly handles both new entries and merging with existing wallet IDs. The early return for empty wallet_ids avoids unnecessary BTreeMap operations.


445-475: LGTM! Range query with appropriate size limit.

The 10,000 block limit is consistent with the segmented storage architecture. The use of BTreeMap::range provides efficient O(log n + m) lookup where m is the number of entries in the range.


1154-1223: LGTM! Comprehensive test coverage for the new filter match API.

The tests cover critical scenarios including bulk recording, range queries, the 10,000-block limit enforcement, and edge cases (empty ranges, partial overlaps). Well done!

dash-spv/src/sync/sequential/message_handlers.rs (1)

607-625: LGTM! Correct ordering of filter persistence and match detection.

Storing the filter to disk (lines 607-611) before checking for matches ensures the filter data is persisted regardless of match results. The transition from boolean to Vec<[u8; 32]> for wallet IDs enables fine-grained tracking.

dash-spv/src/storage/mod.rs (1)

148-154: LGTM! Well-documented trait method with appropriate constraints.

The load_filters method signature and documentation are clear. The 10,000 block limit is consistent with the segmented storage architecture documented in the coding guidelines.

dash-spv/src/storage/disk/state.rs (1)

5-5: LGTM! Straightforward trait method delegation.

The Range import and trait method implementation follow the established pattern in this file of delegating to inherent implementation methods.

Also applies to: 574-576

dash-spv/src/storage/memory.rs (1)

288-306: LGTM! Consistent implementation with proper range validation.

The implementation correctly:

  • Enforces the 10,000 block limit using saturating_sub
  • Returns InvalidInput error for oversized ranges
  • Skips missing filters as documented
  • Matches the behavior of the disk storage implementation

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
dash-spv-ffi/FFI_API.md (1)

1393-1412: Add new FFI types to the Type Definitions section.

The Type Definitions section should include the new filter-related types introduced in this PR to maintain complete documentation coverage.

Add the following types to the "Core Types" subsection after the existing types:

 - `FFIEventCallbacks` - Event callback structure
 - `CoreSDKHandle` - Platform SDK integration handle
+- `FFICompactFilter` - Compact filter with height and data
+- `FFICompactFilters` - Array of compact filters with count
+- `FFIFilterMatchEntry` - Filter match entry with height and wallet IDs
+- `FFIFilterMatches` - Array of filter match entries with count
🧹 Nitpick comments (4)
dash-spv-ffi/FFI_API.md (2)

1414-1423: Update Memory Management section to document new filter types.

The Memory Management section should reflect the new types and their lifecycle, particularly the range-query limits mentioned in the function documentation.

Add a new subsection after the "Important Rules" to document filter-specific lifecycle requirements:

 5. **Shared Ownership**: `dash_spv_ffi_client_get_wallet_manager()` returns `FFIWalletManager*` that must be released with `dash_spv_ffi_wallet_manager_free()`
+
+### Filter API Usage
+
+1. **Range Limits**: Filter queries (`dash_spv_ffi_client_load_filters` and `dash_spv_ffi_client_get_filter_matched_heights`) are limited to 10,000-block ranges
+2. **Filter Cleanup**: `FFICompactFilters` must be freed with `dash_spv_ffi_compact_filters_destroy()`
+3. **Match Cleanup**: `FFIFilterMatches` must be freed with `dash_spv_ffi_filter_matches_destroy()`

1424-1474: Add example usage for new filter and transaction APIs.

The Usage Examples section should demonstrate the new filter loading and query capabilities introduced in this PR.

Add a new example section after the Event Callbacks example:

 dash_spv_ffi_client_set_event_callbacks(client, callbacks);

Then append:

+
+### Loading and Querying Filters
+
+```c
+// Load compact filters for a range
+FFICompactFilters* filters = dash_spv_ffi_client_load_filters(client, 100, 1100);
+if (filters != NULL) {
+    for (size_t i = 0; i < filters->count; i++) {
+        printf("Filter at height %u, data length: %zu\n", 
+               filters->filters[i].height, 
+               filters->filters[i].data_len);
+    }
+    dash_spv_ffi_compact_filters_destroy(filters);
+}
+
+// Get filter-matched heights with wallet IDs
+FFIFilterMatches* matches = dash_spv_ffi_client_get_filter_matched_heights(client, 100, 1100);
+if (matches != NULL) {
+    for (size_t i = 0; i < matches->count; i++) {
+        printf("Height %u matched %zu wallet(s)\n", 
+               matches->entries[i].height, 
+               matches->entries[i].wallet_ids_count);
+    }
+    dash_spv_ffi_filter_matches_destroy(matches);
+}
+
+// Get transaction statistics
+usize tx_count = dash_spv_ffi_client_get_transaction_count(client);
+usize block_count = dash_spv_ffi_client_get_blocks_with_transactions_count(client);
+printf("Transactions: %zu across %zu blocks\n", tx_count, block_count);
+```
key-wallet-manager/src/wallet_manager/mod.rs (1)

91-94: Use WalletId alias in filter_matches (and consider caching policy).

You already define pub type WalletId = [u8; 32]; above; here you hard‑code [u8; 32] instead of using WalletId. For consistency and easier refactors, it would be clearer to declare this as:

filter_matches: BTreeMap<Network, BTreeMap<BlockHash, Vec<WalletId>>>,

Also, you currently cache an entry even when the matched list is empty. If filters are processed for large ranges of heights, you may want to consider skipping insertion for the empty case to bound the cache size over long runs (the miss path just recomputes once). Not urgent, but worth thinking about given this is per‑block state.

key-wallet-manager/src/wallet_manager/process_block.rs (1)

87-132: Align return/cached types with WalletId alias.

check_compact_filter returns Vec<[u8; 32]> and pushes *wallet_id values, while WalletManager already defines WalletId = [u8; 32]. For clarity and to keep types in sync, it would be preferable to use the alias consistently here, e.g.:

async fn check_compact_filter(
    &mut self,
    filter: &BlockFilter,
    block_hash: &BlockHash,
    network: Network,
) -> Vec<WalletId> {
    // ...
    let mut matched_wallet_ids: Vec<WalletId> = Vec::new();
    // ...
}

and update the WalletInterface trait / callers accordingly if feasible. This keeps the concept of “wallet id” explicit throughout the API.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c98598 and 1463355.

📒 Files selected for processing (3)
  • dash-spv-ffi/FFI_API.md (9 hunks)
  • key-wallet-manager/src/wallet_manager/mod.rs (1 hunks)
  • key-wallet-manager/src/wallet_manager/process_block.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rs: Never hardcode network parameters, addresses, or keys in Rust code
Use proper error types with thiserror crate and propagate errors appropriately in Rust code
Use tokio runtime for async operations in Rust code
Use conditional compilation with feature flags for optional features
Write unit tests for new functionality in Rust
Format Rust code with cargo fmt before committing
Ensure clippy passes with all warnings as errors on all Rust code

**/*.rs: Each crate keeps sources in src/; unit tests live alongside code with #[cfg(test)]
Maintain MSRV of 1.89 for Rust code
Format Rust code with rustfmt (see rustfmt.toml); run cargo fmt --all before commits
Lint Rust code with clippy; avoid unwrap()/expect() in library code; use error types (e.g., thiserror)
Use snake_case for function and variable names in Rust
Use UpperCamelCase for types and traits in Rust
Use SCREAMING_SNAKE_CASE for constants in Rust
Follow mixed editions (2021/2024) and crate-specific idioms; prefer async via tokio where applicable
Unit tests should be placed near code with descriptive names (e.g., test_parse_address_mainnet)
Use #[ignore] for network-dependent or long-running tests; run with -- --ignored flag
Never commit secrets or real keys; avoid logging sensitive data in Rust code
Keep test vectors deterministic in Rust test code

Files:

  • key-wallet-manager/src/wallet_manager/mod.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
🧠 Learnings (35)
📓 Common learnings
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:01:37.609Z
Learning: The mempool tracking infrastructure (UnconfirmedTransaction, MempoolState, configuration, and mempool_filter.rs) is fully implemented and integrated in the Dash SPV client as of this PR, including client logic, FFI APIs, and tests.
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: swift-dash-core-sdk/Examples/DashHDWalletExample/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:01:00.652Z
Learning: Applies to swift-dash-core-sdk/Examples/DashHDWalletExample/**/*.swift : Enable mempool tracking on SPV connection using the appropriate strategy (.fetchAll, .bloomFilter, .selective)
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/wallet/**/*.rs : Implement wallet functionality with UTXO tracking, balance calculation with confirmation states, and transaction processing
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/storage/**/*.rs : Implement `StorageManager` trait with segmented storage: headers in 10,000-header segments with index files, filters in separate storage, and state persistence in dedicated paths
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/{wallet,account,managed_account}/**/*.rs : Use BTreeMap for ordered data structures (accounts, transactions) and HashMap for quick lookups (address to index mapping) to optimize data structure performance

Applied to files:

  • key-wallet-manager/src/wallet_manager/mod.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/{wallet,account,managed_account}/**/*.rs : Clearly separate watch-only wallets from full wallets using the wallet type system; never attempt signing operations on watch-only accounts and validate external signatures match expected pubkeys

Applied to files:

  • key-wallet-manager/src/wallet_manager/mod.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/{bip32,derivation}/**/*.rs : Cache intermediate derivation results and use hardened derivation only when required; batch derive child keys when possible to optimize derivation performance

Applied to files:

  • key-wallet-manager/src/wallet_manager/mod.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/address_pool/**/*.rs : Implement staged gap limit management using `GapLimitStage` structure with tracking of last_used_index and used_indices HashSet to enable efficient address discovery without loading entire address chains into memory

Applied to files:

  • key-wallet-manager/src/wallet_manager/mod.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/{account,managed_account}/**/*.rs : Separate immutable and mutable concerns: Use `Account` for immutable identity information (keys, derivation paths) and `ManagedAccount` for mutable state (address pools, metadata, balances)

Applied to files:

  • key-wallet-manager/src/wallet_manager/mod.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/{bip32,derivation,account}/**/*.rs : Use `ExtendedPubKey` and `ExtendedPrivKey` from the BIP32 implementation for key derivation; avoid direct private key exposure or serialization in logs

Applied to files:

  • key-wallet-manager/src/wallet_manager/mod.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/managed_account/**/*.rs : Implement atomic state updates in ManagedAccount: when processing transactions, update address usage state, UTXO set, transaction history, and balance calculations together to maintain consistency

Applied to files:

  • key-wallet-manager/src/wallet_manager/mod.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/address_pool/**/*.rs : Support multiple `KeySource` variants (Private, Public, NoKeySource) in address pool initialization to enable both full wallets and watch-only wallets with the same interface

Applied to files:

  • key-wallet-manager/src/wallet_manager/mod.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/{account,managed_account}/**/*.rs : Use strongly typed enum-based system for `AccountType` with specific variants (Standard, Identity, Masternode, etc.) to provide compile-time safety for different account purposes

Applied to files:

  • key-wallet-manager/src/wallet_manager/mod.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/storage/**/*.rs : Implement `StorageManager` trait with segmented storage: headers in 10,000-header segments with index files, filters in separate storage, and state persistence in dedicated paths

Applied to files:

  • key-wallet-manager/src/wallet_manager/mod.rs
📚 Learning: 2025-06-15T15:31:44.136Z
Learnt from: QuantumExplorer
Repo: dashpay/rust-dashcore PR: 74
File: key-wallet/src/address.rs:30-40
Timestamp: 2025-06-15T15:31:44.136Z
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.

Applied to files:

  • key-wallet-manager/src/wallet_manager/mod.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/{wallet,account,managed_account,address_pool}/**/*.rs : Always validate network consistency: use the account's network type when creating addresses and assert that operations use the expected network to prevent mainnet/testnet mixing

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/transaction_checking/**/*.rs : In SPV sync integration, use `wallet_info.check_transaction()` with appropriate `TransactionContext` and update wallet state atomically only when `is_relevant` flag is true

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/transaction_checking/**/*.rs : Use `TransactionRouter::classify_transaction()` to determine transaction type and `get_relevant_account_types()` to avoid checking all accounts for every transaction; route CoinJoin and other specialized transactions only to relevant account types

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/wallet/**/*.rs : Implement wallet functionality with UTXO tracking, balance calculation with confirmation states, and transaction processing

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/*.rs : Use `?` operator for error propagation in Rust code to maintain clean error handling patterns

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
📚 Learning: 2025-12-01T08:00:50.618Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: swift-dash-core-sdk/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:50.618Z
Learning: Applies to swift-dash-core-sdk/**/*.rs : Implement new FFI functions in Rust with `#[no_mangle] extern "C"` annotation in dash-spv-ffi

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/tests/unit/**/*.rs : Add corresponding unit tests in `tests/unit/` for each new FFI function

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Use `#[no_mangle] extern "C"` attribute when implementing new FFI functions in Rust

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Add cbindgen annotations for complex types in FFI functions

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/tests/c_tests/**/*.{c,h} : Add corresponding C tests in `tests/c_tests/` for each new FFI function

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : All FFI types must have corresponding `_destroy()` functions for explicit memory management

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Input strings in FFI functions are `*const c_char` (borrowed, not freed by C caller)

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-06-26T16:01:37.609Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:01:37.609Z
Learning: The mempool tracking infrastructure (UnconfirmedTransaction, MempoolState, configuration, and mempool_filter.rs) is fully implemented and integrated in the Dash SPV client as of this PR, including client logic, FFI APIs, and tests.

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T08:00:50.618Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: swift-dash-core-sdk/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:50.618Z
Learning: When making changes to Rust FFI in dash-spv-ffi, rebuild with `cargo build --release` and run Swift tests to verify integration

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T08:01:00.652Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: swift-dash-core-sdk/Examples/DashHDWalletExample/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:01:00.652Z
Learning: Applies to swift-dash-core-sdk/Examples/DashHDWalletExample/**/*.swift : Use DashSDK's public methods, not direct SPVClient access, when accessing SPV functionality in Swift code

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-06-26T15:47:37.438Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:47:37.438Z
Learning: Transaction IDs (txids) and block hashes in Dash are always 32 bytes and should be passed as fixed-size byte arrays in FFI interfaces, not as C strings, to avoid inefficiency and encoding issues.

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T08:01:18.174Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-01T08:01:18.174Z
Learning: Organize workspace with crates: `dash`, `hashes`, `internals`, `dash-network`, `dash-spv`, `key-wallet`, `rpc-*`, utilities (`fuzz`, `test-utils`), and FFI crates (`*-ffi`)

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: When debugging FFI issues, check `dash_spv_ffi_get_last_error()` for error details

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Rust strings must be returned as `*const c_char` with caller responsibility to free using `dash_string_free`

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/**/*.rs : Use domain-specific error types (`NetworkError`, `StorageError`, `SyncError`, `ValidationError`, `SpvError`) for error handling

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/**/*.rs : Add comprehensive unit tests in-module for individual components

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/tests/**/*.rs : Add integration tests in the `tests/` directory for comprehensive test suites

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Use thread-local storage for error propagation via `dash_spv_ffi_get_last_error()` function

Applied to files:

  • dash-spv-ffi/FFI_API.md
🧬 Code graph analysis (1)
key-wallet-manager/src/wallet_manager/process_block.rs (1)
key-wallet-manager/src/wallet_manager/mod.rs (2)
  • new (70-76)
  • new (107-114)
🪛 LanguageTool
dash-spv-ffi/FFI_API.md

[grammar] ~1117-~1117: Use a hyphen to join words.
Context: ...Matches ``` Description: Get filter matched heights with wallet IDs in a giv...

(QB_NEW_EN_HYPHEN)

🔇 Additional comments (3)
dash-spv-ffi/FFI_API.md (3)

99-100: LGTM!

The new transaction count functions (dash_spv_ffi_client_get_transaction_count and dash_spv_ffi_client_get_blocks_with_transactions_count) are well-documented with clear parameter descriptions and safety annotations. The distinction between persisted metrics and ephemeral sync statistics is appropriately explained.


1113-1124: LGTM!

The filter loading and matched-heights query functions are comprehensively documented with:

  • Clear range-limit constraints (10,000 blocks max)
  • Explicit caller ownership and cleanup requirements
  • Proper safety annotations and error handling guidance
  • Return value semantics

Also applies to: 1193-1204


1238-1310: LGTM!

All four destroy functions for the new filter types follow the established memory-management pattern consistently. Safety documentation is clear and comprehensive for each destruction operation.

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

♻️ Duplicate comments (2)
dash-spv/src/sync/sequential/message_handlers.rs (1)

641-656: ChainState per-match I/O concern remains unaddressed.

Loading and saving ChainState for every filter match (lines 644-656) could cause performance issues during bulk filter sync. Additionally, unwrap_or_else(|| ChainState::new()) on line 648 masks storage errors by silently creating a fresh state.

Consider:

  1. Batching filter match recording and persisting periodically (e.g., every N matches or at phase completion)
  2. Propagating the storage error rather than silently creating a new ChainState
-                let mut chain_state = storage
-                    .load_chain_state()
-                    .await
-                    .map_err(|e| SyncError::Storage(format!("Failed to load chain state: {}", e)))?
-                    .unwrap_or_else(|| crate::types::ChainState::new());
+                let mut chain_state = storage
+                    .load_chain_state()
+                    .await
+                    .map_err(|e| SyncError::Storage(format!("Failed to load chain state: {}", e)))?
+                    .ok_or_else(|| SyncError::Storage("Chain state not found".to_string()))?;
dash-spv-ffi/FFI_API.md (1)

1116-1117: Hyphenate “filter-matched heights” in the description.

The phrase “Get filter matched heights…” should be hyphenated as “filter-matched heights” to read as a compound adjective in the description.

-Get filter matched heights with wallet IDs in a given range.
+Get filter-matched heights with wallet IDs in a given range.
🧹 Nitpick comments (3)
dash-spv/src/types.rs (1)

428-443: Consider deduplicating wallet IDs on record.

If a filter at the same height is processed multiple times (e.g., due to retries), duplicates will accumulate since extend appends without checking for existing entries. If this is a concern for memory or query accuracy:

     pub fn record_filter_matches(&mut self, height: u32, wallet_ids: Vec<[u8; 32]>) {
         if wallet_ids.is_empty() {
             return;
         }

         if let Some(ids) = self.filter_matches.get_mut(&height) {
-            ids.extend(wallet_ids);
+            for id in wallet_ids {
+                if !ids.contains(&id) {
+                    ids.push(id);
+                }
+            }
         } else {
             self.filter_matches.insert(height, wallet_ids);
         }
     }

Alternatively, use a HashSet internally if uniqueness is required.

dash-spv-ffi/src/client.rs (2)

1669-1767: Avoid holding the client mutex across chain_state().await in get_filter_matched_heights.

dash_spv_ffi_client_get_filter_matched_heights currently keeps a MutexGuard on inner while awaiting client.chain_state().await. This is generally discouraged in async Rust because it:

  • Holds a blocking std::sync::Mutex across an .await, potentially stalling any other FFI call that needs the same mutex while the future is pending.
  • Diverges from the established “take, await, then put back” pattern used elsewhere in this file.

Functionality and FFI ownership transfer for FFIFilterMatches and FFIFilterMatchEntry otherwise look correct.

A more consistent pattern would be:

 pub unsafe extern "C" fn dash_spv_ffi_client_get_filter_matched_heights(
     client: *mut FFIDashSpvClient,
     start_height: u32,
     end_height: u32,
 ) -> *mut crate::types::FFIFilterMatches {
@@
-    let result = client.runtime.block_on(async {
-        // Get chain state without taking the client
-        let chain_state = {
-            let guard = inner.lock().unwrap();
-            match guard.as_ref() {
-                Some(client) => client.chain_state().await,
-                None => {
-                    set_last_error("Client not initialized");
-                    return None;
-                }
-            }
-        };
+    let result = client.runtime.block_on(async {
+        // Take the client out of the mutex while we await.
+        let mut spv_client = {
+            let mut guard = inner.lock().unwrap();
+            match guard.take() {
+                Some(c) => c,
+                None => {
+                    set_last_error("Client not initialized");
+                    return None;
+                }
+            }
+        };
+
+        let chain_state = spv_client.chain_state().await;
+
+        // Put the client back before doing further work.
+        let mut guard = inner.lock().unwrap();
+        *guard = Some(spv_client);
@@
-        // Get filter matches in range - works even during sync
-        let matches_result = chain_state.get_filter_matched_heights(start_height..end_height);
+        // Get filter matches in range.
+        let matches_result = chain_state.get_filter_matched_heights(start_height..end_height);

This keeps the blocking mutex out of the async wait while preserving existing error reporting and FFI conversion semantics. Please double-check against how DashSpvClient is used during long‑running sync to ensure this doesn’t conflict with your intended concurrency model.


1814-1867: Refine async + mutex usage in get_blocks_with_transactions_count.

Functionally this looks good: it derives a set of unique confirmed block heights from the wallet’s transaction history and returns the count, matching the documentation.

As with get_transaction_count, though, it keeps a std::sync::MutexGuard on inner across wallet.read().await, which can serialize any other caller needing the same mutex while that async lock is pending.

You can follow the same pattern of cloning the wallet handle before awaiting:

 pub unsafe extern "C" fn dash_spv_ffi_client_get_blocks_with_transactions_count(
     client: *mut FFIDashSpvClient,
 ) -> usize {
@@
-    let result = client.runtime.block_on(async {
-        // Get wallet without taking the client
-        let guard = inner.lock().unwrap();
-        match guard.as_ref() {
-            Some(spv_client) => {
-                // Access wallet and get unique block heights
-                let wallet = spv_client.wallet();
-                let wallet_guard = wallet.read().await;
-                let tx_history = wallet_guard.transaction_history();
-
-                // Count unique block heights (confirmed transactions only)
-                let mut unique_heights = std::collections::HashSet::new();
-                for tx in tx_history {
-                    if let Some(height) = tx.height {
-                        unique_heights.insert(height);
-                    }
-                }
-
-                unique_heights.len()
-            }
-            None => {
-                tracing::warn!(
-                    "Client not initialized when querying blocks with transactions count"
-                );
-                0
-            }
-        }
-    });
+    let result = client.runtime.block_on(async {
+        // Clone wallet handle without holding the mutex across .await
+        let wallet = {
+            let guard = inner.lock().unwrap();
+            match guard.as_ref() {
+                Some(spv_client) => spv_client.wallet().clone(),
+                None => {
+                    tracing::warn!(
+                        "Client not initialized when querying blocks with transactions count"
+                    );
+                    return 0;
+                }
+            }
+        };
+
+        let wallet_guard = wallet.read().await;
+        let tx_history = wallet_guard.transaction_history();
+
+        // Count unique block heights (confirmed transactions only)
+        let mut unique_heights = std::collections::HashSet::new();
+        for tx in tx_history {
+            if let Some(height) = tx.height {
+                unique_heights.insert(height);
+            }
+        }
+
+        unique_heights.len()
+    });

This keeps the FFI interface the same while making the async locking behaviour friendlier to other callers.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1463355 and 9d7b83d.

📒 Files selected for processing (7)
  • dash-spv-ffi/FFI_API.md (9 hunks)
  • dash-spv-ffi/src/client.rs (2 hunks)
  • dash-spv/src/error.rs (2 hunks)
  • dash-spv/src/storage/disk/state.rs (2 hunks)
  • dash-spv/src/sync/sequential/message_handlers.rs (2 hunks)
  • dash-spv/src/sync/sequential/phase_execution.rs (1 hunks)
  • dash-spv/src/types.rs (4 hunks)
✅ Files skipped from review due to trivial changes (1)
  • dash-spv/src/sync/sequential/phase_execution.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • dash-spv/src/error.rs
  • dash-spv/src/storage/disk/state.rs
🧰 Additional context used
📓 Path-based instructions (6)
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rs: Never hardcode network parameters, addresses, or keys in Rust code
Use proper error types with thiserror crate and propagate errors appropriately in Rust code
Use tokio runtime for async operations in Rust code
Use conditional compilation with feature flags for optional features
Write unit tests for new functionality in Rust
Format Rust code with cargo fmt before committing
Ensure clippy passes with all warnings as errors on all Rust code

**/*.rs: Each crate keeps sources in src/; unit tests live alongside code with #[cfg(test)]
Maintain MSRV of 1.89 for Rust code
Format Rust code with rustfmt (see rustfmt.toml); run cargo fmt --all before commits
Lint Rust code with clippy; avoid unwrap()/expect() in library code; use error types (e.g., thiserror)
Use snake_case for function and variable names in Rust
Use UpperCamelCase for types and traits in Rust
Use SCREAMING_SNAKE_CASE for constants in Rust
Follow mixed editions (2021/2024) and crate-specific idioms; prefer async via tokio where applicable
Unit tests should be placed near code with descriptive names (e.g., test_parse_address_mainnet)
Use #[ignore] for network-dependent or long-running tests; run with -- --ignored flag
Never commit secrets or real keys; avoid logging sensitive data in Rust code
Keep test vectors deterministic in Rust test code

Files:

  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv-ffi/src/client.rs
  • dash-spv/src/types.rs
dash-spv/src/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

dash-spv/src/**/*.rs: Use trait-based abstractions for swappable implementations (e.g., NetworkManager, StorageManager)
Use async/await throughout the codebase with tokio runtime
Use domain-specific error types (NetworkError, StorageError, SyncError, ValidationError, SpvError) for error handling
Use trait objects (Arc<dyn StorageManager>, Arc<dyn NetworkManager>) for runtime polymorphism
Use Tokio channels for inter-component message passing
Add comprehensive unit tests in-module for individual components

Files:

  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv/src/types.rs
dash-spv/src/sync/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

dash-spv/src/sync/**/*.rs: Use sequential phase-based synchronization with SequentialSyncManager for organized sync phases
Use state machines with SyncState enum to drive synchronization flow and state transitions

Files:

  • dash-spv/src/sync/sequential/message_handlers.rs
**/*-ffi/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

Careful handling of memory safety at FFI boundaries in Rust code

Files:

  • dash-spv-ffi/src/client.rs
dash-spv-ffi/src/**/*.rs

📄 CodeRabbit inference engine (dash-spv-ffi/CLAUDE.md)

dash-spv-ffi/src/**/*.rs: Use #[no_mangle] extern "C" attribute when implementing new FFI functions in Rust
All FFI types must have corresponding _destroy() functions for explicit memory management
Rust strings must be returned as *const c_char with caller responsibility to free using dash_string_free
Input strings in FFI functions are *const c_char (borrowed, not freed by C caller)
Add cbindgen annotations for complex types in FFI functions
Use thread-local storage for error propagation via dash_spv_ffi_get_last_error() function

Files:

  • dash-spv-ffi/src/client.rs
dash-spv/src/types.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

Define common data structures in types.rs including SyncProgress, ValidationMode, WatchItem, etc.

Files:

  • dash-spv/src/types.rs
🧠 Learnings (32)
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/storage/**/*.rs : Implement `StorageManager` trait with segmented storage: headers in 10,000-header segments with index files, filters in separate storage, and state persistence in dedicated paths

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv-ffi/src/client.rs
  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/wallet/**/*.rs : Implement wallet functionality with UTXO tracking, balance calculation with confirmation states, and transaction processing

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv-ffi/src/client.rs
  • dash-spv/src/types.rs
  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-06-26T16:01:37.609Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:01:37.609Z
Learning: The mempool tracking infrastructure (UnconfirmedTransaction, MempoolState, configuration, and mempool_filter.rs) is fully implemented and integrated in the Dash SPV client as of this PR, including client logic, FFI APIs, and tests.

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv-ffi/src/client.rs
  • dash-spv/src/types.rs
  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/transaction_checking/**/*.rs : In SPV sync integration, use `wallet_info.check_transaction()` with appropriate `TransactionContext` and update wallet state atomically only when `is_relevant` flag is true

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/sync/**/*.rs : Use state machines with `SyncState` enum to drive synchronization flow and state transitions

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:01:00.652Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: swift-dash-core-sdk/Examples/DashHDWalletExample/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:01:00.652Z
Learning: Applies to swift-dash-core-sdk/Examples/DashHDWalletExample/**/*.swift : Enable mempool tracking on SPV connection using the appropriate strategy (.fetchAll, .bloomFilter, .selective)

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/sync/**/*.rs : Use sequential phase-based synchronization with `SequentialSyncManager` for organized sync phases

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/managed_account/**/*.rs : Implement atomic state updates in ManagedAccount: when processing transactions, update address usage state, UTXO set, transaction history, and balance calculations together to maintain consistency

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/address_pool/**/*.rs : Implement staged gap limit management using `GapLimitStage` structure with tracking of last_used_index and used_indices HashSet to enable efficient address discovery without loading entire address chains into memory

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/storage/**/*.rs : Use segmented storage with headers in 10,000-header chunks stored with index files for efficient retrieval

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
📚 Learning: 2025-06-26T16:02:42.390Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:02:42.390Z
Learning: Passing exclusive mutable references to network and storage managers into the sync manager in async Rust can lead to borrow checker issues or runtime contention if concurrent access is needed elsewhere. It's advisable to document this architectural tradeoff and consider refactoring to interior mutability or message passing for shared access as the codebase evolves.

Applied to files:

  • dash-spv/src/sync/sequential/message_handlers.rs
📚 Learning: 2025-12-01T08:00:50.618Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: swift-dash-core-sdk/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:50.618Z
Learning: Applies to swift-dash-core-sdk/**/*.rs : Implement new FFI functions in Rust with `#[no_mangle] extern "C"` annotation in dash-spv-ffi

Applied to files:

  • dash-spv-ffi/src/client.rs
  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/tests/unit/**/*.rs : Add corresponding unit tests in `tests/unit/` for each new FFI function

Applied to files:

  • dash-spv-ffi/src/client.rs
  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Add cbindgen annotations for complex types in FFI functions

Applied to files:

  • dash-spv-ffi/src/client.rs
  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Use `#[no_mangle] extern "C"` attribute when implementing new FFI functions in Rust

Applied to files:

  • dash-spv-ffi/src/client.rs
  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Use thread-local storage for error propagation via `dash_spv_ffi_get_last_error()` function

Applied to files:

  • dash-spv-ffi/src/client.rs
  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Input strings in FFI functions are `*const c_char` (borrowed, not freed by C caller)

Applied to files:

  • dash-spv-ffi/src/client.rs
  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/**/*.rs : Add comprehensive unit tests in-module for individual components

Applied to files:

  • dash-spv-ffi/src/client.rs
  • dash-spv/src/types.rs
  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-06-26T15:47:37.438Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:47:37.438Z
Learning: Transaction IDs (txids) and block hashes in Dash are always 32 bytes and should be passed as fixed-size byte arrays in FFI interfaces, not as C strings, to avoid inefficiency and encoding issues.

Applied to files:

  • dash-spv-ffi/src/client.rs
  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/tests/**/*.rs : Add integration tests in the `tests/` directory for comprehensive test suites

Applied to files:

  • dash-spv/src/types.rs
  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/types.rs : Define common data structures in `types.rs` including `SyncProgress`, `ValidationMode`, `WatchItem`, etc.

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/**/*.rs : Use trait-based abstractions for swappable implementations (e.g., `NetworkManager`, `StorageManager`)

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/{wallet,account,managed_account}/**/*.rs : Use BTreeMap for ordered data structures (accounts, transactions) and HashMap for quick lookups (address to index mapping) to optimize data structure performance

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/**/*.rs : Use trait objects (`Arc<dyn StorageManager>`, `Arc<dyn NetworkManager>`) for runtime polymorphism

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/network/**/*.rs : Implement configurable timeouts with recovery mechanisms for network operations

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:00:17.379Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:17.379Z
Learning: Applies to dash-spv/src/**/*.rs : Use domain-specific error types (`NetworkError`, `StorageError`, `SyncError`, `ValidationError`, `SpvError`) for error handling

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T08:01:00.652Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: swift-dash-core-sdk/Examples/DashHDWalletExample/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:01:00.652Z
Learning: Applies to swift-dash-core-sdk/Examples/DashHDWalletExample/**/*.swift : Use DashSDK's public methods, not direct SPVClient access, when accessing SPV functionality in Swift code

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : All FFI types must have corresponding `_destroy()` functions for explicit memory management

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/tests/c_tests/**/*.{c,h} : Add corresponding C tests in `tests/c_tests/` for each new FFI function

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T08:01:18.174Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-01T08:01:18.174Z
Learning: Organize workspace with crates: `dash`, `hashes`, `internals`, `dash-network`, `dash-spv`, `key-wallet`, `rpc-*`, utilities (`fuzz`, `test-utils`), and FFI crates (`*-ffi`)

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: When debugging FFI issues, check `dash_spv_ffi_get_last_error()` for error details

Applied to files:

  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Rust strings must be returned as `*const c_char` with caller responsibility to free using `dash_string_free`

Applied to files:

  • dash-spv-ffi/FFI_API.md
🧬 Code graph analysis (2)
dash-spv/src/sync/sequential/message_handlers.rs (2)
dash-spv/src/storage/disk/state.rs (2)
  • stats (400-431)
  • stats (601-603)
dash-spv/src/storage/disk/manager.rs (1)
  • new (79-129)
dash-spv-ffi/src/client.rs (3)
dash-spv-ffi/src/error.rs (1)
  • set_last_error (26-31)
dash-spv/src/client/core.rs (2)
  • chain_state (206-209)
  • wallet (161-163)
dash-spv/src/client/status_display.rs (1)
  • chain_state (146-149)
🪛 LanguageTool
dash-spv-ffi/FFI_API.md

[grammar] ~1117-~1117: Use a hyphen to join words.
Context: ...Matches ``` Description: Get filter matched heights with wallet IDs in a giv...

(QB_NEW_EN_HYPHEN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Key Wallet Components Tests
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (dash_deserialize_amount)
  • GitHub Check: Pre-commit (ubuntu-latest)
🔇 Additional comments (8)
dash-spv/src/sync/sequential/message_handlers.rs (3)

614-618: Filter persistence before evaluation is the right approach.

Storing the filter to disk before evaluating matches ensures data is persisted even if processing fails later. This aligns with the segmented storage pattern for filters.


620-632: API change to return matched wallet IDs is well-integrated.

The shift from boolean to Vec<[u8; 32]> enables tracking which specific wallets matched, supporting richer diagnostics and the new ChainState persistence. The length check on line 634 correctly replaces the previous boolean condition.


658-669: Improved logging provides useful diagnostic context.

Including the matched wallet count and height in logs aids debugging filter sync issues. The log levels (debug for ChainState recording, info for block request) are appropriate.

dash-spv/src/types.rs (3)

284-288: Filter matches field uses appropriate data structure.

BTreeMap<u32, Vec<[u8; 32]>> provides ordered iteration by height and efficient range queries, which aligns well with the get_filter_matched_heights method. The documentation clearly explains the purpose.

Consider adding filter_matches to the Debug impl (lines 543-555) for completeness, though omitting it is reasonable if the map could be large.


456-475: Range query implementation is efficient and well-bounded.

The 10,000 block limit aligns with the storage segmentation pattern (headers in 10,000-header segments per coding guidelines). Using BTreeMap::range() provides efficient O(log n + k) iteration. The saturating_sub prevents underflow if range.start > range.end.


1153-1223: Unit tests provide good coverage of the new filter match API.

Tests cover bulk recording, range queries, range limit enforcement, and edge cases (empty/partial ranges). The test names follow the descriptive naming convention per coding guidelines.

Note: The past review comment "method only tested, never used" is now resolved—record_filter_matches is called in message_handlers.rs line 651.

dash-spv-ffi/FFI_API.md (1)

7-7: Docs for new filter and count APIs look consistent with the Rust FFI surface.

The updated function counts, table rows, and detailed sections for the new filter-loading, filter-match, and transaction/block-count APIs correctly mirror the extern "C" signatures, range limits, and destroy semantics from dash-spv-ffi/src/client.rs and dash-spv-ffi/src/types.rs. Nothing to change here.

Also applies to: 94-101, 143-165, 817-847, 1110-1123, 1190-1203, 1238-1267, 1280-1309

dash-spv-ffi/src/client.rs (1)

1281-1379: Filter-loading FFI looks sound and matches ownership expectations.

The dash_spv_ffi_client_load_filters implementation correctly:

  • Validates the height range with a hard limit of 10,000 blocks.
  • Avoids holding the client mutex across await by cloning the storage handle first.
  • Transfers ownership of filter byte buffers and the FFICompactFilter array to FFI via as_mut_ptr + mem::forget, relying on the corresponding destroy helpers.

No changes needed here.

If you want an extra safety check, you can grep for the definitions of FFICompactFilter, FFICompactFilters, and dash_spv_ffi_compact_filters_destroy to confirm the destroy path reconstructs and frees both the outer array and each filter’s data buffer symmetrically.

@ZocoLini
Copy link
Collaborator

ZocoLini commented Dec 3, 2025

Need to fix the following clippy errors:

warning: this `MutexGuard` is held across an await point
    --> dash-spv-ffi/src/client.rs:1718:17
     |
1718 |             let guard = inner.lock().unwrap();
     |                 ^^^^^
     |
     = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
note: these are all the await points this lock is held through
    --> dash-spv-ffi/src/client.rs:1720:54
     |
1720 |                 Some(client) => client.chain_state().await,
     |                                                      ^^^^^
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
     = note: `#[warn(clippy::await_holding_lock)]` on by default

warning: this `MutexGuard` is held across an await point
    --> dash-spv-ffi/src/client.rs:1795:13
     |
1795 |         let guard = inner.lock().unwrap();
     |             ^^^^^
     |
     = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
note: these are all the await points this lock is held through
    --> dash-spv-ffi/src/client.rs:1800:50
     |
1800 |                 let wallet_guard = wallet.read().await;
     |                                                  ^^^^^
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock

warning: this `MutexGuard` is held across an await point
    --> dash-spv-ffi/src/client.rs:1840:13
     |
1840 |         let guard = inner.lock().unwrap();
     |             ^^^^^
     |
     = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
note: these are all the await points this lock is held through
    --> dash-spv-ffi/src/client.rs:1845:50
     |
1845 |                 let wallet_guard = wallet.read().await;
     |                                                  ^^^^^
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock

warning: `dash-spv-ffi` (lib) generated 3 warnings
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.14s

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: 0

🧹 Nitpick comments (1)
dash-spv/src/storage/disk/filters.rs (1)

220-228: Consider parallel loading for better performance.

The sequential file I/O works correctly but could be slow for large ranges (up to 10,000 reads). Consider using futures::stream::iter with buffer_unordered or tokio::spawn to parallelize filter loading.

Example optimization:

use futures::stream::{self, StreamExt};

let tasks = range.map(|height| async move {
    self.load_filter(height).await.map(|opt| opt.map(|filter| (height, filter)))
});

let results: Vec<_> = stream::iter(tasks)
    .buffer_unordered(50)  // Load up to 50 filters concurrently
    .collect()
    .await;

let mut result = Vec::new();
for res in results {
    if let Some((height, filter)) = res? {
        result.push((height, filter));
    }
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 108d092 and 638dd20.

📒 Files selected for processing (1)
  • dash-spv/src/storage/disk/filters.rs (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rs: Never hardcode network parameters, addresses, or keys in Rust code
Use proper error types with thiserror crate and propagate errors appropriately in Rust code
Use tokio runtime for async operations in Rust code
Use conditional compilation with feature flags for optional features
Write unit tests for new functionality in Rust
Format Rust code with cargo fmt before committing
Ensure clippy passes with all warnings as errors on all Rust code

**/*.rs: Each crate keeps sources in src/; unit tests live alongside code with #[cfg(test)]
Maintain MSRV of 1.89 for Rust code
Format Rust code with rustfmt (see rustfmt.toml); run cargo fmt --all before commits
Lint Rust code with clippy; avoid unwrap()/expect() in library code; use error types (e.g., thiserror)
Use snake_case for function and variable names in Rust
Use UpperCamelCase for types and traits in Rust
Use SCREAMING_SNAKE_CASE for constants in Rust
Follow mixed editions (2021/2024) and crate-specific idioms; prefer async via tokio where applicable
Unit tests should be placed near code with descriptive names (e.g., test_parse_address_mainnet)
Use #[ignore] for network-dependent or long-running tests; run with -- --ignored flag
Never commit secrets or real keys; avoid logging sensitive data in Rust code
Keep test vectors deterministic in Rust test code

Files:

  • dash-spv/src/storage/disk/filters.rs
dash-spv/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

dash-spv/**/*.rs: Use trait-based abstractions for core components like NetworkManager and StorageManager to enable swappable implementations
Use async/await throughout the codebase with tokio runtime for all asynchronous operations
Use Minimum Rust Version (MSRV) 1.89 and ensure all code compiles with cargo check --all-features

Files:

  • dash-spv/src/storage/disk/filters.rs
dash-spv/src/storage/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

Storage module should provide abstraction via StorageManager trait with concrete implementations for MemoryStorageManager and DiskStorageManager

Files:

  • dash-spv/src/storage/disk/filters.rs
🧠 Learnings (5)
📓 Common learnings
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:01:37.609Z
Learning: The mempool tracking infrastructure (UnconfirmedTransaction, MempoolState, configuration, and mempool_filter.rs) is fully implemented and integrated in the Dash SPV client as of this PR, including client logic, FFI APIs, and tests.
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/address_pool/**/*.rs : Implement staged gap limit management using `GapLimitStage` structure with tracking of last_used_index and used_indices HashSet to enable efficient address discovery without loading entire address chains into memory
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/address_pool/**/*.rs : Pre-generate addresses in batches (typically 20-100) and store in address pools; only derive on-demand when pool is exhausted to optimize performance and memory usage
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/{wallet,account,managed_account}/**/*.rs : Use BTreeMap for ordered data structures (accounts, transactions) and HashMap for quick lookups (address to index mapping) to optimize data structure performance
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/{bip32,derivation}/**/*.rs : Cache intermediate derivation results and use hardened derivation only when required; batch derive child keys when possible to optimize derivation performance
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/*ffi*.rs : For FFI bindings, expose safe C interfaces with appropriate memory management and use #[no_mangle] for public FFI functions
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Storage should use segmented storage with headers stored in 10,000-header segments with index files, separate storage for filter headers and compact block filters
📚 Learning: 2025-12-04T15:51:59.632Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Applies to dash-spv/src/storage/**/*.rs : Storage module should provide abstraction via `StorageManager` trait with concrete implementations for `MemoryStorageManager` and `DiskStorageManager`

Applied to files:

  • dash-spv/src/storage/disk/filters.rs
📚 Learning: 2025-12-04T15:51:59.632Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Applies to dash-spv/**/*.rs : Use trait-based abstractions for core components like `NetworkManager` and `StorageManager` to enable swappable implementations

Applied to files:

  • dash-spv/src/storage/disk/filters.rs
📚 Learning: 2025-12-04T15:51:59.632Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Applies to dash-spv/src/error.rs : Maintain domain-specific error types in `error.rs` with variants for `NetworkError`, `StorageError`, `SyncError`, `ValidationError`, and `SpvError` (top-level wrapper)

Applied to files:

  • dash-spv/src/storage/disk/filters.rs
📚 Learning: 2025-12-04T15:51:59.632Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Storage should use segmented storage with headers stored in 10,000-header segments with index files, separate storage for filter headers and compact block filters

Applied to files:

  • dash-spv/src/storage/disk/filters.rs
🧬 Code graph analysis (1)
dash-spv/src/storage/disk/filters.rs (3)
dash-spv/src/storage/mod.rs (1)
  • load_filters (154-154)
dash-spv/src/storage/memory.rs (2)
  • load_filters (288-306)
  • new (30-42)
dash-spv/src/storage/disk/state.rs (1)
  • load_filters (581-583)
🔇 Additional comments (3)
dash-spv/src/storage/disk/filters.rs (3)

8-8: LGTM: Import addition supports new error handling.

The StorageError import is correctly added to support the range validation error in the new load_filters method.


203-209: LGTM: Clear documentation and consistent signature.

The method signature correctly implements the StorageManager trait, and the documentation clearly explains the behavior including the range limit and missing filter handling.


210-218: LGTM: Robust range validation with clear error messaging.

The range validation logic is correct and consistent with the MemoryStorageManager implementation. The use of saturating_sub prevents underflow, and the error message clearly communicates the constraint.

@github-actions github-actions bot added the merge-conflict The PR conflicts with the target branch. label Dec 11, 2025
@github-actions
Copy link

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

@github-actions github-actions bot removed the merge-conflict The PR conflicts with the target branch. label Dec 23, 2025
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: 0

♻️ Duplicate comments (6)
key-wallet-manager/src/wallet_manager/process_block.rs (1)

116-118: Handle match_any errors explicitly instead of silently treating them as "no match".

filter.match_any(...).unwrap_or(false) converts any error (e.g., IO errors, corrupted filter data) into a "no match" result. The subsequent cache insert permanently stores this incorrect empty result for the (network, block_hash) pair, causing the wallet manager to miss relevant blocks without indication of failure.

Consider:

  • Skip caching on error, allowing retry
  • Add logging/tracing when errors occur
  • Return Result<Vec<[u8; 32]>, Error> to let callers handle errors

As per coding guidelines, proper error propagation is expected.

dash-spv-ffi/FFI_API.md (2)

1105-1107: Optional: hyphenate “filter-matched heights” in the description.

For grammatical correctness, consider hyphenating the compound adjective:

-Get filter matched heights with wallet IDs in a given range.
+Get filter-matched heights with wallet IDs in a given range.

This keeps the meaning unchanged while fixing the wording; regenerate the docs from the updated Rust comments when convenient.


822-835: Fix description: this returns a transaction count, not “blocks with transactions”.

The description for dash_spv_ffi_client_get_transaction_count still says “Use this to show how many blocks contained relevant transactions”, but this API returns the total number of transactions across all wallets. Please update the sentence to avoid confusion, e.g.:

-This returns the persisted transaction count from the wallet, not the ephemeral sync statistics. Use this to show how many blocks contained relevant transactions for the user's wallets.
+This returns the persisted transaction count from the wallet, not the ephemeral sync statistics. Use this to show how many transactions have been recorded for the user's wallets.
dash-spv/src/sync/message_handlers.rs (1)

607-663: Filter-match persistence via ChainState looks correct; consider batching if it becomes hot.

The new logic to:

  • compute matched_wallet_ids,
  • bump filters_matched,
  • record matches with chain_state.record_filter_matches(height, matched_wallet_ids), and
  • persist via store_chain_state

is functionally sound and ties into ChainState::filter_matches as intended. If you later see many matches in quick succession, you might want to cache ChainState in memory during filter sync and flush periodically instead of load+store per match, but this is an optimization and not a blocker.

dash-spv-ffi/src/client.rs (2)

1666-1741: Fix await while holding inner mutex in dash_spv_ffi_client_get_filter_matched_heights.

Inside the async block you call client.chain_state().await while holding a std::sync::MutexGuard from inner.lock().unwrap(). This is exactly what Clippy’s await_holding_lock warns about and can cause unnecessary blocking or deadlock if chain_state() ever tries to acquire the same mutex or other blocking resources.

You can follow the existing pattern used elsewhere in this file: temporarily move the DashSpvClient out of the mutex, perform the async call, then put it back, so no std mutex is held across .await:

Proposed refactor for `dash_spv_ffi_client_get_filter_matched_heights`
-    let result = client.runtime.block_on(async {
-        // Get chain state without taking the client
-        let chain_state = {
-            let guard = inner.lock().unwrap();
-            match guard.as_ref() {
-                Some(client) => client.chain_state().await,
-                None => {
-                    set_last_error("Client not initialized");
-                    return None;
-                }
-            }
-        };
-
-        // Get filter matches in range - works even during sync
-        let matches_result = chain_state.get_filter_matched_heights(start_height..end_height);
+    let result = client.runtime.block_on(async {
+        // Take client out of the mutex so no std::sync::MutexGuard is held across .await
+        let mut spv_client = {
+            let mut guard = inner.lock().unwrap();
+            match guard.take() {
+                Some(client) => client,
+                None => {
+                    set_last_error("Client not initialized");
+                    return None;
+                }
+            }
+        };
+
+        // Query chain state without holding the mutex
+        let chain_state = spv_client.chain_state().await;
+
+        // Put client back
+        {
+            let mut guard = inner.lock().unwrap();
+            *guard = Some(spv_client);
+        }
+
+        // Get filter matches in range - works even during sync
+        let matches_result = chain_state.get_filter_matched_heights(start_height..end_height);

The rest of the function (conversion into FFIFilterMatches and error handling) can remain unchanged.


---

`1759-1786`: **Avoid holding `inner` mutex across `wallet.read().await` in both count helpers and fix doc text.**

Both `dash_spv_ffi_client_get_transaction_count` and
`dash_spv_ffi_client_get_blocks_with_transactions_count` currently:

- Lock `inner` with `inner.lock().unwrap()`,
- Call `spv_client.wallet()`,
- Then `await` on `wallet.read()` while the std mutex guard is still held.

This triggers Clippy’s `await_holding_lock` and can block other users of the client mutex while the async read is pending.

You can fix this by cloning the `Arc<RwLock<_>>` wallet handle out of `spv_client` while holding the mutex, then dropping the guard before you `await`:

<details>
<summary>Proposed refactor for both count functions</summary>

```diff
 #[no_mangle]
 pub unsafe extern "C" fn dash_spv_ffi_client_get_transaction_count(
     client: *mut FFIDashSpvClient,
 ) -> usize {
     null_check!(client, 0);

     let client = &(*client);
     let inner = client.inner.clone();

     let result = client.runtime.block_on(async {
-        // Get wallet without taking the client
-        let guard = inner.lock().unwrap();
-        match guard.as_ref() {
-            Some(spv_client) => {
-                // Access wallet and get transaction count
-                let wallet = spv_client.wallet();
-                let wallet_guard = wallet.read().await;
-                let tx_history = wallet_guard.transaction_history();
-                tx_history.len()
-            }
-            None => {
-                tracing::warn!("Client not initialized when querying transaction count");
-                0
-            }
-        }
+        // Clone wallet handle without holding the client mutex across .await
+        let wallet = {
+            let guard = inner.lock().unwrap();
+            match guard.as_ref() {
+                Some(spv_client) => spv_client.wallet().clone(),
+                None => {
+                    tracing::warn!("Client not initialized when querying transaction count");
+                    return 0;
+                }
+            }
+        };
+
+        let wallet_guard = wallet.read().await;
+        let tx_history = wallet_guard.transaction_history();
+        tx_history.len()
     });

     result
 }
@@
 pub unsafe extern "C" fn dash_spv_ffi_client_get_blocks_with_transactions_count(
     client: *mut FFIDashSpvClient,
 ) -> usize {
     null_check!(client, 0);

     let client = &(*client);
     let inner = client.inner.clone();

     let result = client.runtime.block_on(async {
-        // Get wallet without taking the client
-        let guard = inner.lock().unwrap();
-        match guard.as_ref() {
-            Some(spv_client) => {
-                // Access wallet and get unique block heights
-                let wallet = spv_client.wallet();
-                let wallet_guard = wallet.read().await;
-                let tx_history = wallet_guard.transaction_history();
-
-                // Count unique block heights (confirmed transactions only)
-                let mut unique_heights = std::collections::HashSet::new();
-                for tx in tx_history {
-                    if let Some(height) = tx.height {
-                        unique_heights.insert(height);
-                    }
-                }
-
-                unique_heights.len()
-            }
-            None => {
-                tracing::warn!(
-                    "Client not initialized when querying blocks with transactions count"
-                );
-                0
-            }
-        }
+        // Clone wallet handle without holding the client mutex across .await
+        let wallet = {
+            let guard = inner.lock().unwrap();
+            match guard.as_ref() {
+                Some(spv_client) => spv_client.wallet().clone(),
+                None => {
+                    tracing::warn!(
+                        "Client not initialized when querying blocks with transactions count"
+                    );
+                    return 0;
+                }
+            }
+        };
+
+        let wallet_guard = wallet.read().await;
+        let tx_history = wallet_guard.transaction_history();
+
+        // Count unique block heights (confirmed transactions only)
+        let mut unique_heights = std::collections::HashSet::new();
+        for tx in tx_history {
+            if let Some(height) = tx.height {
+                unique_heights.insert(height);
+            }
+        }
+
+        unique_heights.len()
     });

     result
 }

Also, the Rust doc comment for dash_spv_ffi_client_get_transaction_count (mirrored into the generated FFI_API) still says:

“Use this to show how many blocks contained relevant transactions…”

but this function returns a transaction count, not a block count. Please update that sentence accordingly (as suggested in the FFI_API.md comment) so the docs and behavior align.

Also applies to: 1804-1841

🧹 Nitpick comments (1)
dash-spv/src/types.rs (1)

18-18: ChainState filter-match tracking is well-structured and covered by tests.

  • filter_matches: BTreeMap<u32, Vec<[u8; 32]>> plus record_filter_matches and get_filter_matched_heights implement the per-height wallet-ID tracking cleanly, with a clear 10,000-block range limit.
  • The new tests exercise bulk recording, typical range queries, the range limit boundary, and empty/partial overlaps, which is good coverage.

If you ever need stricter semantics, you might consider:

  • deduplicating wallet IDs per height inside record_filter_matches, or
  • introducing a type WalletId = [u8; 32]; alias to make signatures more self-documenting,

but the current design is perfectly acceptable as-is.

Also applies to: 281-285, 429-476, 1159-1221

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 638dd20 and b43a6e8.

📒 Files selected for processing (14)
  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv/src/client/block_processor_test.rs
  • dash-spv/src/error.rs
  • dash-spv/src/sync/filters/matching.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv/src/sync/phase_execution.rs
  • dash-spv/src/types.rs
  • dash-spv/tests/storage_test.rs
  • key-wallet-manager/src/wallet_interface.rs
  • key-wallet-manager/src/wallet_manager/mod.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • dash-spv/src/error.rs
  • dash-spv/tests/storage_test.rs
  • key-wallet-manager/src/wallet_manager/mod.rs
🧰 Additional context used
📓 Path-based instructions (10)
dash-spv/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

dash-spv/**/*.rs: Use async/await throughout the codebase, built on tokio runtime
Use Arc for trait objects to enable runtime polymorphism for NetworkManager and StorageManager
Use Tokio channels for inter-component message passing between async tasks
Maintain minimum Rust version (MSRV) of 1.89 and use only compatible syntax and features

Files:

  • dash-spv/src/sync/filters/matching.rs
  • dash-spv/src/sync/phase_execution.rs
  • dash-spv/src/client/block_processor_test.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv/src/types.rs
dash-spv/src/sync/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

Implement sequential phase-based synchronization via SyncManager with phases progressing in order: Headers → Masternode List → Filter Headers → Filters → Blocks

Files:

  • dash-spv/src/sync/filters/matching.rs
  • dash-spv/src/sync/phase_execution.rs
  • dash-spv/src/sync/message_handlers.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (CLAUDE.md)

Never hardcode network parameters, addresses, or keys

Files:

  • dash-spv/src/sync/filters/matching.rs
  • dash-spv/src/sync/phase_execution.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/client/block_processor_test.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv/src/types.rs
  • key-wallet-manager/src/wallet_interface.rs
  • dash-spv-ffi/src/client.rs
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rs: Use proper error types (thiserror) and propagate errors appropriately
Use tokio runtime for async operations
Use conditional compilation with feature flags for optional features
Write unit tests for new functionality
Use secure random number generation for keys
Never log or expose private keys
Code must target Rust 1.89 minimum supported version (MSRV)
Format code using cargo fmt
Pass clippy linting without warnings

**/*.rs: MSRV (Minimum Supported Rust Version) is 1.89; ensure compatibility with this version for all builds
Unit tests should live alongside code with #[cfg(test)] annotation; integration tests use the tests/ directory
Use snake_case for function and variable names
Use UpperCamelCase for types and traits
Use SCREAMING_SNAKE_CASE for constants
Format code with rustfmt before commits; ensure cargo fmt --all is run
Run cargo clippy --workspace --all-targets -- -D warnings for linting; avoid warnings in CI
Prefer async/await via tokio for asynchronous operations

Files:

  • dash-spv/src/sync/filters/matching.rs
  • dash-spv/src/sync/phase_execution.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/client/block_processor_test.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv/src/types.rs
  • key-wallet-manager/src/wallet_interface.rs
  • dash-spv-ffi/src/client.rs
**/{dash-network,dash-spv,key-wallet}/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

Use async/await for async operations in network and wallet modules

Files:

  • dash-spv/src/sync/filters/matching.rs
  • dash-spv/src/sync/phase_execution.rs
  • dash-spv/src/client/block_processor_test.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv/src/types.rs
dash-spv/src/client/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

Use the DashSpvClient high-level API with proper configuration via ClientConfig for client initialization

Files:

  • dash-spv/src/client/block_processor_test.rs
dash-spv-ffi/tests/unit/**/*.rs

📄 CodeRabbit inference engine (dash-spv-ffi/CLAUDE.md)

Add corresponding unit tests in tests/unit/ for each new FFI function

Files:

  • dash-spv-ffi/tests/unit/test_type_conversions.rs
**/*-ffi/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*-ffi/**/*.rs: Exercise careful handling at FFI boundaries for memory safety
Be careful with FFI memory management

Files:

  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv-ffi/src/client.rs
**/tests/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/tests/**/*.rs: Create integration tests for network operations
Test both mainnet and testnet configurations
Use proptest for property-based testing where appropriate

**/tests/**/*.rs: Use descriptive test names (e.g., test_parse_address_mainnet)
Mark network-dependent or long-running tests with #[ignore] and run with -- --ignored

Files:

  • dash-spv-ffi/tests/unit/test_type_conversions.rs
dash-spv-ffi/src/**/*.rs

📄 CodeRabbit inference engine (dash-spv-ffi/CLAUDE.md)

dash-spv-ffi/src/**/*.rs: Use #[no_mangle] extern "C" attribute when implementing new FFI functions in Rust
All FFI types must have corresponding _destroy() functions for explicit memory management
Rust strings must be returned as *const c_char with caller responsibility to free using dash_string_free
Input strings in FFI functions are *const c_char (borrowed, not freed by C caller)
Add cbindgen annotations for complex types in FFI functions
Use thread-local storage for error propagation via dash_spv_ffi_get_last_error() function

Files:

  • dash-spv-ffi/src/client.rs
🧠 Learnings (40)
📓 Common learnings
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:01:37.609Z
Learning: The mempool tracking infrastructure (UnconfirmedTransaction, MempoolState, configuration, and mempool_filter.rs) is fully implemented and integrated in the Dash SPV client as of this PR, including client logic, FFI APIs, and tests.
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/sync/**/*.rs : Implement sequential phase-based synchronization via SyncManager with phases progressing in order: Headers → Masternode List → Filter Headers → Filters → Blocks

Applied to files:

  • dash-spv/src/sync/filters/matching.rs
  • dash-spv/src/sync/phase_execution.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv/src/types.rs
  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/*.rs : Apply atomic state updates when managing watch-only wallets: validate that external signatures match expected pubkeys and never attempt signing operations

Applied to files:

  • dash-spv/src/sync/filters/matching.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/client/block_processor_test.rs
  • dash-spv/src/sync/message_handlers.rs
  • key-wallet-manager/src/wallet_interface.rs
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-06-26T16:01:37.609Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:01:37.609Z
Learning: The mempool tracking infrastructure (UnconfirmedTransaction, MempoolState, configuration, and mempool_filter.rs) is fully implemented and integrated in the Dash SPV client as of this PR, including client logic, FFI APIs, and tests.

Applied to files:

  • dash-spv/src/sync/filters/matching.rs
  • dash-spv/src/client/block_processor_test.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv/src/types.rs
  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/transaction_checking/**/*.rs : Implement transaction classification and routing through `TransactionRouter` to avoid checking all accounts for every transaction

Applied to files:

  • dash-spv/src/sync/filters/matching.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/client/block_processor_test.rs
  • key-wallet-manager/src/wallet_interface.rs
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/validation/**/*.rs : Implement three validation modes: ValidationMode::None (no validation), ValidationMode::Basic (structure and timestamp validation), and ValidationMode::Full (complete PoW and chain validation)

Applied to files:

  • dash-spv/src/sync/filters/matching.rs
  • dash-spv/src/client/block_processor_test.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv/src/types.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/*.rs : Use `BTreeMap` for ordered data (accounts, transactions) and `HashMap` for lookups (address mappings); apply memory management strategies for old transaction data

Applied to files:

  • dash-spv/src/sync/filters/matching.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv/src/types.rs
  • key-wallet-manager/src/wallet_interface.rs
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-22T17:59:37.833Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-22T17:59:37.833Z
Learning: Applies to **/{dash-network,dash-spv,key-wallet}/**/*.rs : Use async/await for async operations in network and wallet modules

Applied to files:

  • dash-spv/src/sync/filters/matching.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/client/block_processor_test.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv/src/types.rs
  • dash-spv-ffi/FFI_API.md
  • key-wallet-manager/src/wallet_interface.rs
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/address_pool/**/*.rs : Support multiple `KeySource` variants (Private, Public, NoKeySource) to enable both full wallets and watch-only wallets with the same interface

Applied to files:

  • dash-spv/src/sync/filters/matching.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/client/block_processor_test.rs
  • key-wallet-manager/src/wallet_interface.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/storage/**/*.rs : Store headers in 10,000-header segments with index files in headers/ directory, filter headers and compact block filters in filters/ directory, and state in state/ directory

Applied to files:

  • dash-spv/src/sync/phase_execution.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv/src/types.rs
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/managed_account/**/*.rs : Implement atomic state updates when processing transactions: update transactions, UTXOs, balances, and address usage state together

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/sync/message_handlers.rs
  • key-wallet-manager/src/wallet_interface.rs
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/*.rs : Separate immutable structures (`Account`, `Wallet`) containing only identity information from mutable wrappers (`ManagedAccount`, `ManagedWalletInfo`) with state management

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv/src/types.rs
  • key-wallet-manager/src/wallet_interface.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/address_pool/**/*.rs : Use staged gap limit management with `GapLimitStage` tracking `last_used_index` and `used_indices` to enable efficient address discovery without loading entire chains into memory

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv/src/types.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/address_pool/**/*.rs : Generate addresses in batches using gap limit and staged generation instead of unbounded address generation to prevent memory and performance issues

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/*.rs : Use the `?` operator for error propagation, provide context in error messages, never panic in library code, and return `Result<T>` for all fallible operations

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
📚 Learning: 2025-06-15T15:31:44.136Z
Learnt from: QuantumExplorer
Repo: dashpay/rust-dashcore PR: 74
File: key-wallet/src/address.rs:30-40
Timestamp: 2025-06-15T15:31:44.136Z
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.

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/client/block_processor_test.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/*.rs : Always validate network consistency when deriving or validating addresses; never mix mainnet and testnet operations

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
📚 Learning: 2025-06-26T16:02:42.390Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:02:42.390Z
Learning: Passing exclusive mutable references to network and storage managers into the sync manager in async Rust can lead to borrow checker issues or runtime contention if concurrent access is needed elsewhere. It's advisable to document this architectural tradeoff and consider refactoring to interior mutability or message passing for shared access as the codebase evolves.

Applied to files:

  • key-wallet-manager/src/wallet_manager/process_block.rs
  • dash-spv/src/sync/message_handlers.rs
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/tests/**/*.rs : Organize tests into unit tests (in-module), integration tests (tests/ directory), real network tests (with live Dash Core nodes), and performance benchmarks

Applied to files:

  • dash-spv/src/client/block_processor_test.rs
  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv/src/types.rs
  • dash-spv-ffi/FFI_API.md
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/tests/unit/**/*.rs : Add corresponding unit tests in `tests/unit/` for each new FFI function

Applied to files:

  • dash-spv/src/client/block_processor_test.rs
  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv/src/types.rs
  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/tests/**/*.rs : Organize unit tests by functionality: separate test files for BIP32, mnemonics, addresses, derivation paths, and PSBT operations

Applied to files:

  • dash-spv/src/client/block_processor_test.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/tests/**/*.rs : Use property-based testing for complex invariants such as gap limit constraints

Applied to files:

  • dash-spv/src/client/block_processor_test.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Add cbindgen annotations for complex types in FFI functions

Applied to files:

  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/error.rs : Use domain-specific error types (NetworkError, StorageError, SyncError, ValidationError, SpvError) rather than generic error handling

Applied to files:

  • dash-spv/src/types.rs
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/**/*.rs : Use async/await throughout the codebase, built on tokio runtime

Applied to files:

  • dash-spv/src/types.rs
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/client/**/*.rs : Use the DashSpvClient high-level API with proper configuration via ClientConfig for client initialization

Applied to files:

  • dash-spv/src/types.rs
  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/network/**/*.rs : Implement configurable timeouts with recovery mechanisms for network operations

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/**/*.rs : Use Tokio channels for inter-component message passing between async tasks

Applied to files:

  • dash-spv/src/types.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Use `#[no_mangle] extern "C"` attribute when implementing new FFI functions in Rust

Applied to files:

  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/tests/c_tests/**/*.{c,h} : Add corresponding C tests in `tests/c_tests/` for each new FFI function

Applied to files:

  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Input strings in FFI functions are `*const c_char` (borrowed, not freed by C caller)

Applied to files:

  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : All FFI types must have corresponding `_destroy()` functions for explicit memory management

Applied to files:

  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-06-26T15:47:37.438Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:47:37.438Z
Learning: Transaction IDs (txids) and block hashes in Dash are always 32 bytes and should be passed as fixed-size byte arrays in FFI interfaces, not as C strings, to avoid inefficiency and encoding issues.

Applied to files:

  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: When debugging FFI issues, check `dash_spv_ffi_get_last_error()` for error details

Applied to files:

  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Rust strings must be returned as `*const c_char` with caller responsibility to free using `dash_string_free`

Applied to files:

  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Use thread-local storage for error propagation via `dash_spv_ffi_get_last_error()` function

Applied to files:

  • dash-spv-ffi/FFI_API.md
  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-06-26T15:46:56.854Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:46:56.854Z
Learning: Transaction IDs (txids) in Dash are always 32 bytes and should be passed as fixed-size byte arrays in FFI interfaces, not as C strings, to avoid inefficiency and encoding issues.

Applied to files:

  • dash-spv-ffi/include/dash_spv_ffi.h
📚 Learning: 2025-08-21T05:01:58.949Z
Learnt from: QuantumExplorer
Repo: dashpay/rust-dashcore PR: 108
File: key-wallet-ffi/src/wallet_manager.rs:270-318
Timestamp: 2025-08-21T05:01:58.949Z
Learning: In the key-wallet-ffi design, wallets retrieved from the wallet manager via lookup functions should return const pointers (*const FFIWallet) to enforce read-only access and prevent unintended modifications. The wallet manager should control wallet lifecycle and mutations through specific APIs rather than allowing external mutation of retrieved wallet references.

Applied to files:

  • dash-spv-ffi/include/dash_spv_ffi.h
  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-06-26T15:54:02.509Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:54:02.509Z
Learning: The `StorageManager` trait in `dash-spv/src/storage/mod.rs` uses `&mut self` methods but is also `Send + Sync`, and implementations often use interior mutability for concurrency. This can be confusing, so explicit documentation should clarify thread-safety expectations and the rationale for the API design.

Applied to files:

  • dash-spv-ffi/src/client.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/**/*.rs : Use Arc<dyn TraitName> for trait objects to enable runtime polymorphism for NetworkManager and StorageManager

Applied to files:

  • dash-spv-ffi/src/client.rs
🧬 Code graph analysis (3)
key-wallet-manager/src/wallet_manager/process_block.rs (3)
key-wallet/src/wallet/managed_wallet_info/mod.rs (2)
  • network (116-118)
  • new (56-67)
key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs (2)
  • network (32-32)
  • network (127-129)
key-wallet-manager/src/wallet_manager/mod.rs (2)
  • new (72-78)
  • new (109-116)
dash-spv/src/client/block_processor_test.rs (4)
dash-spv/tests/chainlock_validation_test.rs (1)
  • new (32-37)
dash-spv/tests/edge_case_filter_sync_test.rs (1)
  • new (51-55)
dash-spv/tests/block_download_test.rs (1)
  • new (39-45)
dash-spv/tests/filter_header_verification_test.rs (1)
  • new (46-50)
key-wallet-manager/src/wallet_interface.rs (2)
key-wallet-manager/src/wallet_manager/process_block.rs (1)
  • check_compact_filter (87-132)
dash-spv/src/client/block_processor_test.rs (2)
  • check_compact_filter (65-73)
  • check_compact_filter (290-298)
🪛 LanguageTool
dash-spv-ffi/FFI_API.md

[grammar] ~1106-~1106: Use a hyphen to join words.
Context: ...Matches ``` Description: Get filter matched heights with wallet IDs in a giv...

(QB_NEW_EN_HYPHEN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (dash_deserialize_amount)
  • GitHub Check: fuzz (hashes_sha512)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (dash_outpoint_string)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (dash_deser_net_msg)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: Pre-commit (macos-latest)
  • GitHub Check: RPC Tests (stable, true)
  • GitHub Check: Core Components Tests
  • GitHub Check: SPV Components Tests
🔇 Additional comments (15)
key-wallet-manager/src/wallet_manager/process_block.rs (3)

87-92: LGTM: Function signature correctly updated for per-wallet tracking.

The return type change from bool to Vec<[u8; 32]> enables tracking which specific wallets match the compact filter, aligning with the broader per-wallet matching architecture described in the PR.


94-98: LGTM: Cache lookup logic correctly handles new return type.

The early return with cloned vector is appropriate and avoids redundant filter evaluations.


125-132: Cache insertion logic is correct.

The nested BTreeMap update correctly stores the matched wallet IDs. Note that fixing the error handling in lines 116-118 will ensure only valid results are cached here.

dash-spv/src/sync/phase_execution.rs (1)

150-150: LGTM! Formatting improvement.

The blank line improves readability by adding visual separation between the variable declarations.


Note: The PR objectives mention Clippy warnings about await_holding_lock in dash-spv-ffi/src/client.rs (lines 1718, 1795, 1840). Those warnings are in a different file not included in this review.

dash-spv-ffi/FFI_API.md (1)

7-7: New FFI API surface and counts look consistent.

The updated totals, section counts, and new FFI entries for filter loading, filter matches, and the *_destroy helpers are coherent with the Rust implementations and ownership semantics. No issues from the docs side.

Also applies to: 93-101, 142-170, 152-164, 160-168

dash-spv/src/sync/filters/matching.rs (1)

25-49: check_filter_for_matches correctly adopts per-wallet ID semantics.

Returning SyncResult<Vec<[u8; 32]>> and delegating to wallet.check_compact_filter with a simple “non-empty => log and propagate” flow matches the new wallet interface and keeps the logic straightforward.

dash-spv-ffi/tests/unit/test_type_conversions.rs (1)

163-178: ChainState test updated correctly for new filter_matches field.

Initializing filter_matches with BTreeMap::new() keeps test_chain_state_none_values in sync with the struct definition and FFI conversion without changing the test’s intent.

key-wallet-manager/src/wallet_interface.rs (1)

35-47: WalletInterface::check_compact_filter trait update is clear and consistent.

Switching to Vec<[u8; 32]> with documented semantics (empty = no match, non-empty = matched wallet IDs) is well-specified and aligns with the new per-wallet filter matching pipeline.

dash-spv/src/client/block_processor_test.rs (1)

65-73: Test wallets correctly adapted to Vec-based check_compact_filter.

Both MockWallet (always returns one wallet ID) and NonMatchingWallet (always returns an empty vector) now match the updated trait while preserving the intended “match / no-match” behavior in the tests.

Also applies to: 290-297

dash-spv-ffi/src/client.rs (1)

1254-1353: FFI filter loading implementation looks correct and lock-safe.

dash_spv_ffi_client_load_filters:

  • Enforces the 10,000-block range limit up front.
  • Clones the storage handle while holding inner.lock() and drops the guard before any .await.
  • Converts the loaded filters into FFICompactFilters with ownership correctly transferred to FFI.

No concurrency or ownership issues stand out here.

dash-spv-ffi/include/dash_spv_ffi.h (5)

112-152: LGTM - Well-documented memory management.

The FFICompactFilter and FFICompactFilters structures follow proper FFI patterns with clear documentation about heap allocation and the required cleanup functions. The ownership transfer semantics are explicit.


216-251: LGTM - Proper use of fixed-size arrays for wallet IDs.

The structures correctly use fixed 32-byte arrays for wallet IDs, consistent with the established pattern for transaction IDs and block hashes in the FFI layer. Memory management is well-documented.

Based on learnings, fixed-size byte arrays are preferred over C strings for hash-like identifiers.


705-741: LGTM - Simple counter APIs with sensible defaults.

The transaction and block count functions return primitive types (no memory management needed) and sensibly return 0 when the client is not initialized or wallet is unavailable. Documentation clearly indicates these are persistent metrics from wallet storage, not ephemeral sync statistics.


1148-1190: LGTM - Complete memory management lifecycle.

All new heap-allocated types have corresponding destroyer functions with proper safety documentation. This satisfies the FFI requirement that all FFI types must have _destroy() functions for explicit memory management.

Based on learnings, proper cleanup functions are required for all FFI types.


547-575: API design looks solid with proper FFI safety patterns.

The new filter APIs follow correct FFI patterns with clear documentation about ownership transfer, safety requirements, error handling via thread-local storage, and range limits (10,000 blocks).

The implementation properly handles async/await by scoping MutexGuard locks and dropping them immediately before awaiting storage operations, avoiding any lock-across-await violations.

@xdustinface xdustinface marked this pull request as draft December 23, 2025 07:21
@xdustinface
Copy link
Collaborator

Just for the record, closing this for now because i don't really think we need it and it goes a bit in a different direction that i had in mind in regard to filter matching. Also im just stubborn and talked @QuantumExplorer out of it even though he wanted it. But i might be wrong here and we will end up re-opening this later if we run into trouble on iOS and potentially need more debug information there.

@ZocoLini
Copy link
Collaborator

There is always the option of exposing this kind of tools with a Rust feature named debug-utils for example.

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.

4 participants