Skip to content

Conversation

@PastaPastaPasta
Copy link
Member

@PastaPastaPasta PastaPastaPasta commented Aug 16, 2025

Summary by CodeRabbit

  • New Features

    • Added key-wallet FFI conveniences (DerivationPath, initialize) and new public fields in chain/SPV status types.
  • Bug Fixes

    • Safer quorum public-key handling.
    • Preserve QRInfo payloads when marking messages unhandled.
    • Prevented sync progress counters from overshooting; reduced spurious updates.
    • Apply LLMQ activation per window.
    • Improved error reporting by resolving block heights.
  • Configuration

    • qr_info_extra_share is now disabled by default.
  • Chores

    • Expanded storage mock surface and updated tests, removed QRInfo plan docs, adjusted helper script, added fuzz dependency and updated fuzz target.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 16, 2025

Walkthrough

Safer FFI pubkey byte copying, preservation of GetQRInfo payloads when unhandled, explicit validation errors for missing quorum data, tighter progress accounting, MnListDiff height resolution, test and mock storage surface updates, per-window LLMQ activation logic, PSBT/type refactors, key-wallet-ffi address/derivation refactor, and removal of QRInfo plan docs.

Changes

Cohort / File(s) Summary
FFI: quorum pubkey copy safety
dash-spv-ffi/src/platform_integration.rs
Copy source switched to a typed 48-byte slice (public_key.as_ref()pubkey_bytes.as_ptr()) instead of a raw pointer cast; signature unchanged.
FFI tests & types
dash-spv-ffi/tests/test_platform_integration_safety.rs, dash-spv-ffi/tests/unit/test_type_conversions.rs
Test updated to pass a FFIDashSpvClient to ffi_dash_spv_get_core_handle; FFI conversion tests add new ChainState/SpvStats fields.
Message handling: GetQRInfo
dash-spv/src/network/message_handler.rs
Match binds inner req (GetQRInfo(req)) and returns Unhandled(NetworkMessage::GetQRInfo(req)), preserving payload.
Chainlock quorum lookup
dash-spv/src/sync/chainlock_validation.rs
Replace Ok(None) missing-data returns with explicit SyncError::Validation errors at multiple failure points; successful path unchanged.
Sequential sync: comments & progress gating
dash-spv/src/sync/sequential/mod.rs, dash-spv/src/sync/sequential/phases.rs
Comment clarified; progress increments gated (QR info / MN diff) to avoid overshoot, clamp totals, update last_progress only on real progress.
Validation: MnListDiff height resolution
dash-spv/src/sync/validation.rs
Resolve block_height from diff.block_hash (via engine.block_container) for error messages and cache keys; rename _engineengine params.
Validation tests: storage API async
dash-spv/src/sync/validation_test.rs
Tests use MemoryStorageManager::new().await (async) instead of MemoryStorage::new().
Tests: chainlock storage mutability
dash-spv/tests/chainlock_validation_test.rs
Make storage bindings mut, remove redundant retrievals, pass &mut *storage for mut APIs.
Tests: expanded StorageManager mock
dash-spv/tests/error_handling_test.rs
MockStorageManager expanded/rewritten to match redesigned StorageManager trait: many added/renamed async methods (filters, chain state, sync state, locks, mempool, headers/UTXO helpers) and expanded error injection.
QRInfo tests: defaults & quorum sizing
dash-spv/tests/qrinfo_integration_test.rs, dash-spv/tests/smart_fetch_integration_test.rs
Default qr_info_extra_share expectation set to false; QuorumEntry signers/valid_members sized from llmq_type.size() instead of hard-coded 50.
LLMQ DKG window activation
dash/src/sml/llmq_type/network.rs
Add NetworkLLMQExt::should_skip_quorum_type(&self, llmq_type, height) and apply per-window skip in get_all_dkg_windows; activation thresholds per network/type introduced.
Fixtures: genesis hash parsing
test-utils/src/fixtures.rs
Replace string comparisons with BlockHash constructed from decoded constants (bytes reversed) for assertions.
Script: smart algo start-height
test_smart_algo.sh
Replace checkpoint trigger with --start-height 1100000; update comment.
Docs removed
qr_info_spv_plan/PHASE_1.md, .../PHASE_2.md, .../PHASE_3.md, .../PHASE_4.md
Remove QRInfo planning/design documents (PHASE_1–PHASE_4).
Fuzz: key-wallet dependency & PSBT target
fuzz/Cargo.toml, fuzz/fuzz_targets/dash/deserialize_psbt.rs
Add local key-wallet dependency; switch PSBT deserialize/serialize round-trip to key_wallet::psbt types.
Key-wallet FFI refactor
key-wallet-ffi/src/lib.rs
Replace kw_address usage with KwAddress/KwAddressType, add DerivationPath and initialize(), refactor Address/AddressGenerator internals, expand error conversions and mapping.

Sequence Diagram(s)

sequenceDiagram
  participant Engine
  participant Validator
  participant MNLists
  participant Quorums

  Engine->>Validator: find_chain_lock_quorum(height, llmq_type)
  Validator->>MNLists: get_list_at_or_before(height)
  alt no list found
    Validator-->>Engine: Err(SyncError::Validation: no masternode list)
  else list found
    Validator->>Quorums: get_quorums_at_height(list_height, llmq_type)
    alt none found
      Validator-->>Engine: Err(SyncError::Validation: no quorums)
    else quorums exist
      Validator->>Quorums: select most recent quorum
      Validator-->>Engine: Ok(Some(quorum_hash, quorum_pubkey))
    end
  end
Loading
sequenceDiagram
  participant Network
  participant LLMQ as LLMQType
  participant DKG as DKGWindows

  Network->>DKG: get_all_dkg_windows()
  loop for each enabled LLMQ type
    DKG->>DKG: compute type_windows
    loop for each window
      DKG->>Network: should_skip_quorum_type(LLMQ, window.mining_start)
      alt skip
        DKG-->>DKG: continue (skip window)
      else include
        DKG-->>DKG: include window in result
      end
    end
  end
  DKG-->>Network: return windows
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • QuantumExplorer
  • pankcuf

Poem

In burrows of bytes I hop and peek,
I nibble keys and guard each tweak.
Heights resolved and mocks refined,
Windows checked, no bytes misaligned.
Hooray — safe hops down the code-lined creek. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

Support

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

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Status, Documentation and Community

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🔭 Outside diff range comments (2)
dash-spv/src/sync/masternodes.rs (1)

731-742: Replace lingering GetMnListD variants with GetMnListDiff in SPV code

To ensure consistency across the SPV implementation, update all remaining uses of the old NetworkMessage::GetMnListD(...) variant to the new NetworkMessage::GetMnListDiff(...):

• dash-spv/src/sync/sequential/request_control.rs: line 287
• dash-spv/src/sync/sequential/mod.rs: lines 1789, 1844
• dash-spv/src/network/multi_peer.rs: line 1035

Definitions in dash/src/network/message.rs (enum variants, serializers/deserializers) can remain unchanged for backward-compatibility.

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

1788-1852: Fix incorrect NetworkMessage variant in masternodes sync

The dashcore::network::message::NetworkMessage enum defines the request variant as GetMnListD, not GetMnListDiff. You must use the same variant name everywhere to compile.

• File: dash-spv/src/sync/masternodes.rs
Location: around line 736

Apply this change:

- let network_message = NetworkMessage::GetMnListDiff(get_mnlist_diff);
+ let network_message = NetworkMessage::GetMnListD(get_mnlist_diff);
🧹 Nitpick comments (25)
swift-dash-core-sdk/Package.swift (2)

24-27: Documented target removal looks good; add a short deprecation note in README for downstreams.

The manifest comments are clear. Consider a brief deprecation note in the package’s README pointing integrators to the unified SDK path (dashpay-ios) to avoid confusion for teams pinning older revisions.

I can draft a short README snippet/changelog entry if helpful.


31-33: Confirm StrictConcurrency flag compatibility; consider aligning tests and future-proofing.

Using .enableExperimentalFeature("StrictConcurrency") is fine if your CI/Xcode toolchain (Swift 5.9) supports it. Two follow-ups:

  • Apply the same setting to the test target to avoid differing concurrency diagnostics between library and tests.
  • If your toolchain supports it, prefer .enableUpcomingFeature("StrictConcurrency") (as some toolchains deprecate the experimental spelling). Keep whichever your CI accepts.

Proposed diffs:

Option A: keep experimental flag, propagate to tests

         .testTarget(
             name: "SwiftDashCoreSDKTests",
             dependencies: ["SwiftDashCoreSDK"],
-            path: "Tests/SwiftDashCoreSDKTests"
+            path: "Tests/SwiftDashCoreSDKTests",
+            swiftSettings: [
+                .enableExperimentalFeature("StrictConcurrency")
+            ]
         ),

Option B: switch to upcoming feature and propagate to tests

         .target(
             name: "SwiftDashCoreSDK",
             dependencies: [],
             path: "Sources/SwiftDashCoreSDK",
             swiftSettings: [
-                .enableExperimentalFeature("StrictConcurrency")
+                .enableUpcomingFeature("StrictConcurrency")
             ]
         ),
         .testTarget(
             name: "SwiftDashCoreSDKTests",
             dependencies: ["SwiftDashCoreSDK"],
-            path: "Tests/SwiftDashCoreSDKTests"
+            path: "Tests/SwiftDashCoreSDKTests",
+            swiftSettings: [
+                .enableUpcomingFeature("StrictConcurrency")
+            ]
         ),

If the alternative spelling isn’t recognized by your current toolchain, keep the existing one.

To verify which spelling your CI supports, try a quick dry run locally or in CI. If you want, I can add a minimal matrix job that compiles the package with both settings to auto-select the supported one.

test-utils/src/fixtures.rs (3)

103-111: Simplify expected hash construction using parse()

You can avoid manual hex decoding and byte reversal by parsing the canonical hex directly into BlockHash. This reduces duplication and the chance of mirrored mistakes between code-under-test and expected values.

Apply this diff:

-        // Create expected BlockHash instances from the constants for proper comparison
-        let expected_mainnet = {
-            let bytes = decode(MAINNET_GENESIS_HASH).unwrap();
-            let mut reversed = [0u8; 32];
-            reversed.copy_from_slice(&bytes);
-            reversed.reverse();
-            BlockHash::from_slice(&reversed).unwrap()
-        };
+        // Parse expected BlockHash instances from the canonical hex (display) form
+        let expected_mainnet: BlockHash = MAINNET_GENESIS_HASH.parse().unwrap();

112-118: Ditto for testnet: use parse() instead of manual reverse

Keeps the test short, expressive, and independent from the implementation details of the fixture helpers.

Apply this diff:

-        let expected_testnet = {
-            let bytes = decode(TESTNET_GENESIS_HASH).unwrap();
-            let mut reversed = [0u8; 32];
-            reversed.copy_from_slice(&bytes);
-            reversed.reverse();
-            BlockHash::from_slice(&reversed).unwrap()
-        };
+        let expected_testnet: BlockHash = TESTNET_GENESIS_HASH.parse().unwrap();

120-121: Optional: also assert the display (hex) form

Asserting the hex formatting matches the canonical constants guards against endianness regressions in Display/LowerHex implementations.

Apply this diff:

         assert_eq!(mainnet, expected_mainnet);
         assert_eq!(testnet, expected_testnet);
+        // Additionally ensure the human-readable hex form matches the canonical constants
+        assert_eq!(format!("{:x}", mainnet), MAINNET_GENESIS_HASH);
+        assert_eq!(format!("{:x}", testnet), TESTNET_GENESIS_HASH);
test_smart_algo.sh (1)

7-12: Confirm CLI flag and verify smart-algo window coverage

  • The CLI parser in dash-spv/src/main.rs (lines 114–115) defines and exposes --start-height.

  • Manually run the smart-algo script and inspect the debug log to ensure the smart algorithm is triggered over heights 1260302–1290302 when starting from 1,100,000 (without relying on checkpoint hashes).

  • For CI/scripts robustness, prepend your test script with:

    set -euo pipefail
dash-spv/src/network/message_handler.rs (1)

95-100: Preserving GetQRInfo payload in Unhandled is correct (LGTM).

The arm now returns Unhandled(NetworkMessage::GetQRInfo(req)), keeping the original payload intact for upper layers or test harnesses.

If inbound GetQRInfo requests are common in testnets, consider tracking a dedicated counter (e.g., getqrinfo_request_messages) instead of incrementing other_messages for better observability.

qr_info_spv_plan/PHASE_1.md (1)

340-345: Rename in StorageManager snippet conflicts with earlier examples

You’ve documented get_header_height_by_hash(&BlockHash) here, but earlier “Create block height fetcher” examples still call storage.get_header_height(block_hash) and self.storage.get_header_height(block_hash).await. This inconsistency will confuse implementers and readers.

  • Align all earlier snippets to use get_header_height_by_hash.
  • Also, state clearly that the method name was renamed (and that call sites must update), rather than “NO NEW METHODS NEEDED”.

Would you like me to sweep the doc and update all occurrences of get_header_height(...) to get_header_height_by_hash(...) for consistency?

dash-spv/src/sync/sequential/phases.rs (2)

549-565: Good gating on QRInfo progress to prevent overshoot

The guard if *qr_info_completed < *qr_info_requests and clamping requests_completed via .min(*requests_total) resolves overshoot and duplicate-accounting issues. Updating last_progress only on actual increments is also correct.

Consider adding a trace log when a duplicate completion is ignored to aid diagnostics:

 if *qr_info_completed < *qr_info_requests {
   *qr_info_completed += 1;
   *requests_completed = (*requests_completed + 1).min(*requests_total);
   *last_progress = Instant::now();
+} else {
+  tracing::trace!("Ignoring duplicate QRInfo completion (completed={} of {})",
+                  qr_info_completed, qr_info_requests);
 }

573-591: MnListDiff gating mirrors QRInfo fix; back-compat counter retained

Same positive notes as above. Retaining *diffs_processed += 1 preserves prior metrics while keeping the new request-based progress sane.

Add a trace for ignored duplicates (see the QRInfo comment) for parity and easier debugging.

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

1821-1825: Per-block MnListDiff requests: confirm rate-limiting and backpressure

Requesting a diff for each new block keeps a rolling validation window, but can be chatty. Ensure the request controller enforces per-peer limits and retries so we don’t overload peers during bursts.

If bursts are common, consider batching or coalescing adjacent requests (e.g., only request for the latest tip if prior ones are still in flight).

dash/src/sml/llmq_type/network.rs (2)

108-118: Per-window activation gating is a solid improvement

Skipping per-window based on window.mining_start instead of blanket type-level skipping enables post-activation windows while correctly excluding pre-activation ones. Logging at trace is appropriate.

Consider promoting the skip decision to debug level temporarily while validating on testnet/mainnet to help spot misconfigurations in the field.


135-142: Avoid magic activation heights; centralize as constants

The hardcoded activation heights are opaque. Define constants (or place them in a config/params module) and reference them here to improve discoverability and maintenance.

Apply this refactor pattern:

impl Network {
    const MAINNET_PLATFORM_ACTIVATION: u32 = 1_888_888;
    const TESTNET_PLATFORM_ACTIVATION: u32 = 1_289_520;
}

fn should_skip_quorum_type(&self, llmq_type: &LLMQType, height: u32) -> bool {
    match (self, llmq_type) {
        (Network::Dash, LLMQType::Llmqtype100_67) => height < Self::MAINNET_PLATFORM_ACTIVATION,
        (Network::Testnet, LLMQType::Llmqtype25_67) => height < Self::TESTNET_PLATFORM_ACTIVATION,
        _ => false,
    }
}

Do any other LLMQ types on your networks have activation thresholds (e.g., 400_85)? If yes, extend the matcher accordingly.

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

205-219: Mocks expanded correctly; consider re-enabling the test module and tightening failure modes

The broadened mock surface aligns with the expanded StorageManager trait and covers read/write failure injections. To get value from this suite:

  • Drop the cfg gate once method signatures are aligned and types imported.
  • Consider exercising the new “error-injection points” in more paths (e.g., get_headers_batch failures during recovery).
  • For methods returning Option, return Some(...) in a couple of spots to simulate realistic non-empty cases (e.g., get_tip_height -> Some(1234), get_header_height_by_hash -> Some(height)) for specific test scenarios.

If you share the target behaviors, I can contribute a set of granular tests that validate error propagation through the new storage paths.

Also applies to: 221-227, 241-247, 248-254, 255-261, 262-267, 269-275, 277-285, 286-294, 295-301, 302-308, 309-315, 316-321, 323-329, 330-336, 337-343, 344-356, 357-365, 367-376, 392-397, 406-411, 413-419, 420-425, 427-436, 438-447, 449-458, 460-465, 467-476, 478-487, 489-497, 510-515, 517-525, 527-534, 536-541, 543-548, 550-555

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

127-128: Defaults flip to qr_info_extra_share=false looks right; consider un-ignoring this test

This test has no network or I/O and would be valuable to run in CI. Recommend removing #[ignore] so regressions in defaults are caught early.

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

365-365: Redundant storage initialization earlier in the test; prefer a single handle

At Line 365 you obtain a storage handle from the client right after previously creating a DiskStorageManager at Lines 347–349. The earlier handle is never used and is dropped immediately upon shadowing, which is wasteful and potentially confusing.

  • Remove the earlier DiskStorageManager creation and rely on client.storage().
  • Alternatively, reuse the first handle and avoid calling client.storage() here.

I recommend deleting the earlier creation (Lines 347–349) to keep a single storage source in this test.

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

226-257: Use next_back() to select the most recent quorum entry

Since quorums is a BTreeMap<QuorumHash, QualifiedQuorumEntry> (ordered by key), iter().next() picks the smallest hash, not the most recent. To deterministically select the last (largest) key, replace:

-        let (quorum_hash, entry) = quorums.iter().next().ok_or_else(|| {
+        let (quorum_hash, entry) = quorums.iter().next_back().ok_or_else(|| {
             SyncError::Validation(format!(
                 "No quorum entries found for LLMQ type {:?}",
                 self.config.required_llmq_type
             ))
         })?;

Also, this method no longer returns Ok(None), so you may eventually simplify its signature from SyncResult<Option<…>> to SyncResult<(BlockHash, BLSPublicKey)>.
[optional_refactors_recommended]

dash-spv/src/sync/validation.rs (3)

236-247: Better error context with resolved heights; watch out for “unknown height” = 0 collisions

Resolving block_height from engine.block_container.get_height and bubbling it into InvalidMnListDiff improves diagnostics.

Caveat: unwrap_or(0) conflates unknown heights with genesis (0), which can mislead error reporting for diffs whose heights aren’t yet known. Consider either:

  • Emitting a distinct “unknown height” message (don’t coerce to 0), or
  • Using a cache key that incorporates block hash when height is unknown (see comment on Lines 281–283).

This is optional, but it avoids subtle cache and logging confusion.


281-283: Cache key collision risk when height is unknown

Using ValidationCacheKey::MasternodeList(block_height) with unwrap_or(0) can cause different diffs with unknown heights to collide under height 0. Two safer options:

  • Skip caching when height cannot be resolved.
  • Introduce a cache key variant for hash-based addressing (e.g., MasternodeListByHash(BlockHash)) and use it when height is None.

Either avoids false cache hits/misses for unknown heights without impacting the common path.


302-307: Remove unused local variable to keep Clippy quiet

block_height is computed but not used here. Drop it until needed to avoid warnings and noise.

-        // We can resolve block height from the diff's block hash using the engine's block container
-        let block_height = engine.block_container.get_height(&diff.block_hash).unwrap_or(0);
+        // Height resolution available via engine.block_container if needed
qr_info_spv_plan/PHASE_3.md (5)

68-84: Priority enum and request spec look good.

The priority enum deriving Ord enables sorting; spec fields cover base/tip hash and priority. Consider documenting expected dedup behavior when multiple specs target the same tip.


175-206: Recovery strategy math is sane; consider making thresholds configurable.

The failure-rate cutoffs and step-down logic are reasonable. Making thresholds tunable via config would help A/B and network variability without code changes.


420-432: Remove timeouts on successful correlation to avoid growth.

Entries are only dropped on timeout. Successful responses leave request_timeouts to grow until timeout, increasing memory usage and churn.

Quick fix: remove the corresponding entry on success:

-    if let Some(sender) = self.pending_requests.remove(&tip_hash) {
+    if let Some(sender) = self.pending_requests.remove(&tip_hash) {
         if sender.send(qr_info).is_err() {
             tracing::warn!("Failed to send QRInfo response - receiver dropped");
         }
+        // remove from timeouts queue
+        if let Some(pos) = self.request_timeouts.iter().position(|(h, _)| *h == tip_hash) {
+            self.request_timeouts.remove(pos);
+        }
         Ok(())

If you adopt (base, tip) keys, adjust the predicate accordingly.


501-520: Minor grammar/formatting polish in Success Criteria.

Nitpicks to improve readability:

  • Consider adding a blank line after “## Success Criteria” before “### Integration Requirements.”
  • Rephrase checklist items to read as complete statements, e.g.:
    • “Maintain existing sequential performance when parallel features are disabled.”
    • “Gracefully degrade to sequential on high failure rates.”

These are stylistic; content is clear.


523-543: Risk wording nitpicks.

  • “All parallel features behind …” → “All parallel features are behind …”
  • “Adaptive concurrency reduction on failures” → “Adaptive concurrency reduction on failures is enabled.”

Purely editorial.

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1c133ef and e9de17a.

📒 Files selected for processing (18)
  • dash-spv-ffi/src/platform_integration.rs (1 hunks)
  • dash-spv/src/network/message_handler.rs (1 hunks)
  • dash-spv/src/sync/chainlock_validation.rs (1 hunks)
  • dash-spv/src/sync/masternodes.rs (1 hunks)
  • dash-spv/src/sync/sequential/mod.rs (3 hunks)
  • dash-spv/src/sync/sequential/phases.rs (2 hunks)
  • dash-spv/src/sync/validation.rs (3 hunks)
  • dash-spv/src/sync/validation_test.rs (2 hunks)
  • dash-spv/tests/chainlock_validation_test.rs (2 hunks)
  • dash-spv/tests/error_handling_test.rs (6 hunks)
  • dash-spv/tests/qrinfo_integration_test.rs (1 hunks)
  • dash-spv/tests/smart_fetch_integration_test.rs (1 hunks)
  • dash/src/sml/llmq_type/network.rs (1 hunks)
  • qr_info_spv_plan/PHASE_1.md (1 hunks)
  • qr_info_spv_plan/PHASE_3.md (1 hunks)
  • swift-dash-core-sdk/Package.swift (1 hunks)
  • test-utils/src/fixtures.rs (1 hunks)
  • test_smart_algo.sh (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-26T15:54:02.509Z
Learnt from: DCG-Claude
PR: dashpay/rust-dashcore#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/sync/validation_test.rs
  • dash-spv/tests/chainlock_validation_test.rs
  • dash-spv/tests/error_handling_test.rs
📚 Learning: 2025-06-26T16:02:42.390Z
Learnt from: DCG-Claude
PR: dashpay/rust-dashcore#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/tests/chainlock_validation_test.rs
🧬 Code Graph Analysis (6)
dash-spv-ffi/src/platform_integration.rs (1)
dash/src/crypto/key.rs (1)
  • public_key (374-379)
dash-spv/src/sync/chainlock_validation.rs (1)
dash-spv/src/sync/masternodes.rs (1)
  • engine (456-458)
dash-spv/tests/smart_fetch_integration_test.rs (1)
dash/src/sml/llmq_type/mod.rs (4)
  • size (363-365)
  • from (377-394)
  • from (398-416)
  • from (419-437)
dash-spv/src/sync/validation_test.rs (1)
dash-spv/src/client/mod.rs (3)
  • storage (693-693)
  • storage (3186-3188)
  • new (248-339)
dash-spv/src/sync/validation.rs (2)
dash-spv/src/sync/masternodes.rs (1)
  • engine (456-458)
dash/src/sml/masternode_list/from_diff.rs (1)
  • diff (89-93)
dash-spv/tests/error_handling_test.rs (1)
dash-spv/src/storage/mod.rs (29)
  • store_headers (108-108)
  • load_headers (111-111)
  • get_tip_height (122-122)
  • store_filter_headers (125-125)
  • load_filter_headers (128-128)
  • store_filter (149-149)
  • load_filter (152-152)
  • clear (161-161)
  • get_header_height_by_hash (167-170)
  • get_headers_batch (174-178)
  • remove_utxo (184-184)
  • get_utxos_for_address (187-187)
  • store_sync_state (193-193)
  • load_sync_state (196-196)
  • clear_sync_state (199-199)
  • store_sync_checkpoint (202-206)
  • get_sync_checkpoints (209-213)
  • store_chain_lock (216-220)
  • load_chain_lock (223-223)
  • get_chain_locks (226-230)
  • store_instant_lock (233-237)
  • load_instant_lock (240-243)
  • store_mempool_transaction (247-251)
  • remove_mempool_transaction (254-254)
  • get_mempool_transaction (257-260)
  • get_all_mempool_transactions (263-265)
  • store_mempool_state (268-268)
  • load_mempool_state (271-271)
  • clear_mempool (274-274)
🪛 LanguageTool
qr_info_spv_plan/PHASE_3.md

[grammar] ~501-~501: There might be a mistake here.
Context: ...s Criteria ### Integration Requirements - [ ] Parallel processing uses existing Ne...

(QB_NEW_EN)


[grammar] ~509-~509: There might be a mistake here.
Context: ...Performance Requirements (feature-gated) - [ ] >50% reduction in QRInfo sync time w...

(QB_NEW_EN)


[grammar] ~510-~510: There might be a mistake here.
Context: ...nc time with parallel processing enabled - [ ] Maintain existing sequential perform...

(QB_NEW_EN)


[grammar] ~511-~511: There might be a mistake here.
Context: ...formance when parallel features disabled - [ ] Memory usage increases <20% during p...

(QB_NEW_EN)


[grammar] ~512-~512: There might be a mistake here.
Context: ...ncreases <20% during parallel operations - [ ] Graceful degradation to sequential o...

(QB_NEW_EN)


[grammar] ~515-~515: There might be a mistake here.
Context: ...re rates ### Compatibility Requirements - [ ] All existing sync tests pass without...

(QB_NEW_EN)


[grammar] ~523-~523: There might be a mistake here.
Context: ...gation ### High Risk: API Compatibility Risk: Changes might break existing das...

(QB_NEW_EN)


[grammar] ~525-~525: There might be a mistake here.
Context: ...ng dash-spv integrations Mitigation: - All parallel features behind `#[cfg(feat...

(QB_NEW_EN)


[grammar] ~530-~530: There might be a mistake here.
Context: ...uential ### Medium Risk: Resource Usage Risk: Parallel processing might overwh...

(QB_NEW_EN)


[grammar] ~532-~532: There might be a mistake here.
Context: ...rwhelm network or memory Mitigation: - Conservative default concurrency limits ...

(QB_NEW_EN)


[grammar] ~533-~533: There might be a mistake here.
Context: ...ncurrency limits (3 concurrent requests) - Reuse existing rate limiting and circuit...

(QB_NEW_EN)


[grammar] ~534-~534: There might be a mistake here.
Context: ...te limiting and circuit breaker patterns - Adaptive concurrency reduction on failur...

(QB_NEW_EN)


[grammar] ~537-~537: There might be a mistake here.
Context: ...lures ### Low Risk: Request Correlation Risk: QRInfo responses might be mismat...

(QB_NEW_EN)


[grammar] ~539-~539: There might be a mistake here.
Context: ...matched in parallel mode Mitigation: - Simple tip_hash-based correlation with f...

(QB_NEW_EN)


[grammar] ~540-~540: There might be a mistake here.
Context: ...based correlation with fallback matching - Timeout-based cleanup of stale requests ...

(QB_NEW_EN)


[grammar] ~541-~541: There might be a mistake here.
Context: ... Timeout-based cleanup of stale requests - Comprehensive correlation testing ## In...

(QB_NEW_EN)


[grammar] ~546-~546: There might be a mistake here.
Context: ...nts ### Existing Components Integration - MasternodeSyncManager: Extend with opt...

(QB_NEW_EN)


[grammar] ~553-~553: There might be a mistake here.
Context: ...unctionality ### Backward Compatibility - All parallel features are opt-in via fea...

(QB_NEW_EN)

⏰ 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). (15)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (hashes_sha512)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (dash_deser_net_msg)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (dash_outpoint_string)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (dash_deserialize_amount)
🔇 Additional comments (13)
test-utils/src/fixtures.rs (1)

102-121: Good shift to type-safe BlockHash comparisons

Moving away from string comparisons to actual BlockHash instances improves correctness and avoids endianness/display-format pitfalls.

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

94-110: Quorum size now derived from LLMQ type (LGTM).

Using llmq_type.size() to size signers/valid_members removes the hard-coded 50 and makes this test resilient across LLMQ types.

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

174-177: Safe, canonical public key copy (LGTM).
Verified that BLSPublicKey is a [u8; 48] newtype implementing AsRef<[u8; 48]>, and that QUORUM_PUBKEY_SIZE is defined as 48. The size check and std::ptr::copy_nonoverlapping call are correctly aligned. No further changes needed.

dash-spv/src/sync/sequential/mod.rs (2)

410-413: Explicitly ignoring blocks during MnList phase is fine; verify downstream block processing won’t starve

The rationale is clear and the log level is appropriate. Ensure that:

  • Blocks requested due to filter matches are still processed by the dedicated block processor path while MnList is running, and
  • Ignored blocks here won’t be dropped on the floor (e.g., are queued or re-requested later by the block processor).

If helpful, I can scan the codebase to confirm that block processing in non-DownloadingBlocks phases is indeed handled elsewhere.


837-845: Message gating comment aligns with behavior

The comment clarifies that blocks are intentionally not expected during MnList. This matches the early-return ignore path.

dash-spv/src/sync/validation_test.rs (2)

6-6: Migration to MemoryStorageManager aligns with async storage API

Import change matches the new async constructor usage throughout the codebase. Looks good.


139-139: Async MemoryStorageManager creation is correct and resilient

Using .await and .expect is appropriate here in a test; the leading underscore silences unused warnings cleanly.

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

291-291: Making storage mutable is necessary for &mut StorageManager call sites

Required to pass &mut *storage into validate_pending_chainlocks/process_chain_lock. Change is correct.

qr_info_spv_plan/PHASE_3.md (5)

5-5: Clear overview and scope.

The phase description is concise and correctly frames parallel QRInfo as an opt-in extension to the existing sequential path.


9-13: Objectives read well and align with non-breaking ethos.

No gaps spotted in the stated objectives. They map to the subsequent sections cleanly.


223-238: Enum extensions look good.

Variants are self-explanatory and dovetail with the earlier strategy function.


546-558: Integration points read well.

The integration list is coherent and consistent with earlier sections. No further action needed.


561-568: Implementation plan is incremental and safe.

Phased deployment with backward compatibility maintained—good plan.

Comment on lines +385 to 390
async fn remove_utxo(&mut self, _outpoint: &OutPoint) -> StorageResult<()> {
if self.fail_on_write {
return Err(StorageError::WriteFailed("Mock write failure".to_string()));
}
Ok(None)
Ok(())
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

remove_utxo signature mismatch with trait; will block re-enabling tests

The StorageManager trait typically returns the removed UTXO: StorageResult<Option<Utxo>>. This impl returns StorageResult<()>, which won’t match the trait when these tests are enabled.

Fix the signature and return value:

- async fn remove_utxo(&mut self, _outpoint: &OutPoint) -> StorageResult<()> {
+ async fn remove_utxo(&mut self, _outpoint: &OutPoint) -> StorageResult<Option<Utxo>> {
     if self.fail_on_write {
         return Err(StorageError::WriteFailed("Mock write failure".to_string()));
     }
-    Ok(())
+    Ok(None)
 }
📝 Committable suggestion

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

Suggested change
async fn remove_utxo(&mut self, _outpoint: &OutPoint) -> StorageResult<()> {
if self.fail_on_write {
return Err(StorageError::WriteFailed("Mock write failure".to_string()));
}
Ok(None)
Ok(())
}
async fn remove_utxo(&mut self, _outpoint: &OutPoint) -> StorageResult<Option<Utxo>> {
if self.fail_on_write {
return Err(StorageError::WriteFailed("Mock write failure".to_string()));
}
Ok(None)
}
🤖 Prompt for AI Agents
In dash-spv/tests/error_handling_test.rs around lines 385 to 390, the mock
remove_utxo currently has the wrong signature returning StorageResult<()>, but
the StorageManager trait expects StorageResult<Option<Utxo>>; change the
function signature to async fn remove_utxo(&mut self, _outpoint: &OutPoint) ->
StorageResult<Option<Utxo>> and on success return Ok(None) (or
Ok(Some(dummy_utxo) if the test requires a removed UTXO), preserving the
existing failure path that returns Err(StorageError::WriteFailed(...))). Ensure
any necessary Utxo type references are in scope.

Comment on lines +499 to +508
async fn store_mempool_transaction(
&mut self,
_txid: &Txid,
_tx: &UnconfirmedTransaction,
) -> StorageResult<()> {
if self.fail_on_write {
return Err(StorageError::WriteFailed("Mock write failure".to_string()));
}
Ok(())
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Type import likely needed for UnconfirmedTransaction

This impl references UnconfirmedTransaction but the file doesn’t import it. When you remove the cfg gate and compile this test, it will fail to resolve the type unless it’s brought into scope (or fully qualified in the trait).

Add:

use dash_spv::wallet::UnconfirmedTransaction;

right after the other use statements, or fully qualify the type in the signature to match the trait definition exactly.

🤖 Prompt for AI Agents
In dash-spv/tests/error_handling_test.rs around lines 499 to 508, the impl uses
UnconfirmedTransaction but the type is not imported, so compilation will fail
when the cfg gate is removed; fix by adding the missing use statement (use
dash_spv::wallet::UnconfirmedTransaction;) alongside the other use imports near
the top of the test file, or alternatively fully qualify the parameter type in
the function signature to dash_spv::wallet::UnconfirmedTransaction to match the
trait definition.

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

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

451-451: Address inner migration to dashcore is a good move

Switching to dashcore::Address simplifies downstream interop. One small follow-up: in get_network(), a panic via unreachable! on unknown variants is brittle across FFI. Consider a safe fallback (e.g., Testnet) or an error-returning API instead of panicking.

Suggested change for get_network to avoid panics across FFI boundaries:

-            unknown => unreachable!("Unhandled network variant: {:?}", unknown),
+            // Avoid panicking across FFI; default to Testnet to match team preference for fallbacks.
+            _ => Network::Testnet,

529-597: Replace the commented-out AddressGenerator with minimal helpers for BIP44 P2PKH derivation from xpub

Rather than keeping a large commented stub, add small, self-contained helpers that derive P2PKH addresses from an account xpub using standard BIP44 paths (change 0/1, index). This keeps functionality available without a dashcore::AddressGenerator type and keeps the public surface optional until you expose it via UDL.

Proposed replacement within this block:

-// NOTE: AddressGenerator doesn't exist in dashcore, commented out for now
-// pub struct AddressGenerator {
-//     inner: dashcore::address::AddressGenerator,
-// }
-
-// impl AddressGenerator {
-//     pub fn new(network: Network) -> Self {
-//         Self {
-//             inner: dashcore::address::AddressGenerator::new(network.into()),
-//         }
-//     }
-//
-// AddressGenerator methods commented out since AddressGenerator doesn't exist in dashcore
-// TODO: Implement address generation functionality without AddressGenerator
-//     pub fn generate(
-//         &self,
-//         account_xpub: AccountXPub,
-//         external: bool,
-//         index: u32,
-//     ) -> Result<Arc<Address>, KeyWalletError> {
-//         // Parse the extended public key from string
-//         let xpub =
-//             ExtendedPubKey::from_str(&account_xpub.xpub).map_err(|e| KeyWalletError::KeyError {
-//                 message: e.to_string(),
-//             })?;
-//
-//         // Generate addresses for a single index
-//         let addrs = self
-//             .inner
-//             .generate_range(&xpub, external, index, 1)
-//             .map_err(|e| KeyWalletError::from(e))?;
-//
-//         let addr = addrs.into_iter().next().ok_or_else(|| KeyWalletError::KeyError {
-//             message: "Failed to generate address".into(),
-//         })?;
-//
-//         Ok(Arc::new(Address {
-//             inner: addr,
-//         }))
-//     }
-//
-//     pub fn generate_range(
-//         &self,
-//         account_xpub: AccountXPub,
-//         external: bool,
-//         start: u32,
-//         count: u32,
-//     ) -> Result<Vec<Arc<Address>>, KeyWalletError> {
-//         // Parse the extended public key from string
-//         let xpub =
-//             ExtendedPubKey::from_str(&account_xpub.xpub).map_err(|e| KeyWalletError::KeyError {
-//                 message: e.to_string(),
-//             })?;
-//
-//         let addrs = self
-//             .inner
-//             .generate_range(&xpub, external, start, count)
-//             .map_err(|e| KeyWalletError::from(e))?;
-//
-//         Ok(addrs
-//             .into_iter()
-//             .map(|addr| {
-//                 Arc::new(Address {
-//                     inner: addr,
-//                 })
-//             })
-//             .collect())
-//     }
-// }
+// Replacement for the removed AddressGenerator: minimal helpers to derive P2PKH addresses from an account xpub.
+pub fn generate_p2pkh_from_xpub(
+    account_xpub: &AccountXPub,
+    external: bool,
+    index: u32,
+    network: Network,
+) -> Result<Arc<Address>, KeyWalletError> {
+    let xpub = ExtendedPubKey::from_str(&account_xpub.xpub).map_err(|e| KeyWalletError::KeyError {
+        message: e.to_string(),
+    })?;
+    let secp = Secp256k1::new();
+    let change = kw::ChildNumber::from_normal_idx(if external { 0 } else { 1 })
+        .map_err(|e| KeyWalletError::InvalidDerivationPath { message: e.to_string() })?;
+    let chain = xpub
+        .ckd_pub(&secp, change)
+        .map_err(|e| KeyWalletError::KeyError { message: e.to_string() })?;
+    let idx = kw::ChildNumber::from_normal_idx(index)
+        .map_err(|e| KeyWalletError::InvalidDerivationPath { message: e.to_string() })?;
+    let derived = chain
+        .ckd_pub(&secp, idx)
+        .map_err(|e| KeyWalletError::KeyError { message: e.to_string() })?;
+    let addr = dashcore::Address::p2pkh(&derived.public_key, network.into());
+    Ok(Arc::new(Address { inner: addr }))
+}
+
+pub fn generate_p2pkh_range_from_xpub(
+    account_xpub: &AccountXPub,
+    external: bool,
+    start: u32,
+    count: u32,
+    network: Network,
+) -> Result<Vec<Arc<Address>>, KeyWalletError> {
+    (start..start.saturating_add(count))
+        .map(|i| generate_p2pkh_from_xpub(account_xpub, external, i, network))
+        .collect()
+}

If you want these helpers exposed over FFI, I can mirror them in the UDL with throws annotations.

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e9de17a and 5ac49df.

📒 Files selected for processing (2)
  • key-wallet-ffi/src/key_wallet.udl (1 hunks)
  • key-wallet-ffi/src/lib.rs (6 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: DCG-Claude
PR: dashpay/rust-dashcore#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-06-15T15:31:44.136Z
Learnt from: QuantumExplorer
PR: dashpay/rust-dashcore#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-ffi/src/lib.rs
📚 Learning: 2025-06-15T16:42:18.187Z
Learnt from: QuantumExplorer
PR: dashpay/rust-dashcore#74
File: key-wallet-ffi/src/lib_tests.rs:41-48
Timestamp: 2025-06-15T16:42:18.187Z
Learning: In key-wallet-ffi, the HDWallet::derive_xpriv method returns Result<String, KeyWalletError>, not an ExtPrivKey wrapper. When unwrapped, it yields a String that can have .is_empty() called on it.

Applied to files:

  • key-wallet-ffi/src/lib.rs
🧬 Code Graph Analysis (1)
key-wallet-ffi/src/lib.rs (1)
dash/src/address.rs (11)
  • from (173-175)
  • from (180-182)
  • from (407-409)
  • from (414-419)
  • from (1301-1303)
  • network (936-938)
  • from_str (216-225)
  • from_str (283-286)
  • from_str (1330-1392)
  • p2pkh (536-538)
  • p2pkh (1034-1036)
⏰ 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). (15)
  • GitHub Check: fuzz (hashes_sha512)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (dash_deserialize_amount)
  • GitHub Check: fuzz (dash_deser_net_msg)
  • GitHub Check: fuzz (dash_outpoint_string)
🔇 Additional comments (5)
key-wallet-ffi/src/lib.rs (5)

7-7: Import changes LGTM

Switching address handling away from kw_address and aliasing key_wallet as kw aligns with the migration to dashcore types.


87-94: FFI -> dashcore AddressType conversion looks correct

P2PKH and P2SH are mapped as expected. Given the limited FFI enum, this is appropriate.


192-200: Expanded error mappings are consistent

Mapping CoinJoinNotEnabled, Serialization, and InvalidParameter to existing FFI error variants is reasonable for now and preserves the error surface.


456-458: Parse error wrapping LGTM

Parsing via dashcore::Address::from_str and returning AddressError preserves the original error context cleanly.


499-499: Only P2PKH generation from public key is supported

Using dashcore::Address::p2pkh is correct and matches the UDL signature (no AddressType parameter). Just noting that generating P2SH from a pubkey is intentionally not supported here.

dcg and others added 26 commits August 15, 2025 21:13
…tD to GetMnListDiff

Fix compile error by using the correct enum variant GetMnListDiff instead of GetMnListD
in masternodes sync code.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…function

Replace silent None returns with descriptive SyncError::Validation errors for:
- Missing masternode list at or before height
- Missing quorums for required LLMQ type
- Missing quorum entries for LLMQ type

This addresses CodeRabbitAI review comment to improve error visibility
and debugging when chain lock validation fails due to missing data.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
… in MnList phase

- Remove block message expectation during DownloadingMnList phase in is_message_expected_in_phase
- Clarify comment to explicitly state blocks are intentionally ignored during MnList sync
- Ensures expectation check matches actual runtime behavior where blocks are ignored

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…on test

Remove shadowing of storage handle by using the previously created mutable
Box<DiskStorageManager> instead of calling client.storage(). This ensures
proper borrowing and avoids variable shadowing.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Replace hardcoded placeholder height value of 0 with actual block height
resolved from MnListDiff's block_hash via the MasternodeListEngine's
block_container.get_height() method. This provides more accurate error
reporting and improves debugging capabilities.

Changes:
- Resolve block height from diff.block_hash in validate_qr_info method
- Update ValidationCacheKey to use actual block height instead of 0
- Update validation function comments to reflect height resolution capability

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…idation tests

Replace MemoryStorage::new() with MemoryStorageManager::new().await in validation_test.rs
to use the correct async constructor and storage manager implementation that matches
the expected StorageManager trait interface.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Update test in qrinfo_integration_test.rs to assert that
config.qr_info_extra_share is false by default, matching
ClientConfig::default() implementation.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Add bounds checking to complete_qr_info_request and complete_mn_diff_request
methods in phases.rs to prevent qr_info_completed and mn_diff_completed
counters from exceeding their planned totals. The methods now only increment
counters when below the planned limits and clamp requests_completed to not
exceed requests_total.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Move the should_skip_quorum_type check inside the per-window loop and use
window.mining_start instead of the outer range start. This prevents
incorrectly skipping all windows when a range straddles activation height.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…tation

Replace placeholder get_header_height with actual get_header_height_by_hash
API name and update behavior description to match the real implementation
in dash-spv/src/storage/mod.rs, memory.rs, and disk.rs.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Replace hardcoded array size of 50 in smart_fetch_integration_test.rs with
dynamic quorum size from LLMQ type parameters. This ensures the signers
and valid_members vectors match the actual quorum size and prevent panics
if LLMQ type parameters change.

The fix extracts the quorum size using `llmq_type.size()` and uses it to
create vectors of the correct length, making the test more robust and
maintainable.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…ger trait

Update MockStorageManager implementation in error_handling_test.rs to match
the current StorageManager trait methods:
- Replace get_headers_range -> load_headers
- Replace get_header_by_hash -> get_header_height_by_hash (returns Option<u32>)
- Replace store_filter_header -> store_filter_headers (&[FilterHeader])
- Replace store_header -> store_headers (&[BlockHeader])
- Replace get_stats -> stats (async)
- Replace get_utxos_by_address -> get_utxos_for_address
- Replace get_chain_state -> load_chain_state
- Replace get_masternode_state -> load_masternode_state
- Replace get_mempool_state -> load_mempool_state
- Remove impl-only methods (compact_storage, get_utxo)
- Add missing trait methods (clear, clear_mempool, clear_sync_state,
  get_headers_batch, get_all_mempool_transactions, get_chain_locks,
  load_filter*, store_filter, store_metadata, load_metadata, etc.)
- Update remove_utxo signature to return () instead of Option<Utxo>

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Implement wait_for_qr_info_response with proper async channel handling
- Add request/response correlation using QRInfoCorrelationManager
- Include configurable timeout (30s) with proper error mapping
- Add response validation to ensure tip_hash and base_hash match request
- Implement cleanup_request method for resource management
- Add global correlation manager accessor for dependency injection
- Handle all error paths: timeout, channel closure, validation failures
- Ensure cleanup occurs on both success and error paths

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…ad of instant

Fix processing_time field in QRInfoResult to properly capture and store elapsed
duration from start to completion of QRInfo request processing. Changed field
type from Instant to Duration and updated all usage sites to calculate elapsed
time from captured start_time.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Implement wait_for_qr_info_response with proper async channel handling
- Add request/response correlation using QRInfoCorrelationManager
- Include configurable timeout (30s) with proper error mapping
- Add response validation to ensure tip_hash and base_hash match request
- Implement cleanup_request method and global manager accessor
- Handle all error paths: timeout, channel closure, validation failures
- Ensure cleanup occurs on both success and error paths
- Fix QRInfoResult processing_time field to use Duration instead of Instant

Addresses CodeRabbitAI review comment by replacing placeholder todo!()
with complete implementation that correlates responses to requests and
returns appropriate errors instead of panicking.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…dling

Replace oneshot::Sender<QRInfo> with oneshot::Sender<Result<QRInfo, CorrelationError>>
to enable proper error propagation in timeout cleanup. This fixes the invalid
send_error method call by using standard Result error handling patterns.

Changes:
- Update PendingQRInfoRequest.response_sender type to Result<QRInfo, CorrelationError>
- Update register_request return type to match
- Replace send_error call with send(Err(CorrelationError::Timeout))
- Update successful response handling to send(Ok(qr_info))

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…ery strategies

- Replace Vec<Box<dyn RecoveryStrategy>> with Vec<RecoveryStrategyEnum>
- Define RecoveryStrategyEnum with variants for all strategy types
- Implement forwarding execute method on enum to avoid trait object issues
- Add concrete implementations for all strategy types
- Resolves async method safety problems with trait objects

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Wrap pending_requests HashMap in Mutex<HashMap<RequestId, PendingQRInfoRequest>>
- Update all methods to acquire mutex lock for thread safety
- Make methods async and change &mut self to &self where appropriate
- Update test methods to use await for new async method signatures
- Minimize locked sections to necessary operations only

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…uplicating

Rewrite PHASE_3.md to follow integration rather than replacement approach:

- Extend existing MasternodeSyncManager with feature-gated parallel QRInfo support
- Enhance RequestController to add parallel scheduling while preserving validation
- Augment RecoveryManager with parallel-aware strategies using existing patterns
- Consolidate parallel config with existing ClientConfig structure and validation
- Integrate QRInfo correlation into existing message handler routing
- Reuse existing MockNetworkManager and storage for testing

Key integration principles:
- All parallel features behind #[cfg(feature = "parallel-qrinfo")]
- Sequential sync remains unchanged and default
- Existing APIs only extended, never replaced
- Full backward compatibility maintained
- Conservative defaults and adaptive concurrency limits

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
The NetworkMessage enum uses GetMnListD not GetMnListDiff as the variant name.
This fixes the compilation error that was preventing the build from completing.
- Fixed NetworkMessage enum variant from GetMnListDiff to GetMnListD
- Removed non-existent address module import from key-wallet
- Fixed AddressType conversion to use dashcore types
- Commented out AddressGenerator as it doesn't exist in dashcore
- Added missing Error enum pattern matches
- Fixed InvalidAddress error variant usage

The build now succeeds for all main packages except dash-fuzz and key-wallet-ffi
which have deeper issues that need separate attention.
…m commit 6be28eb

Reverted the unintended changes to sequential/mod.rs that were included in
commit 6be28eb. These changes modified:
- Comment text about blocks during MnList phase
- Removed the Block(_) pattern match for DownloadingMnList phase

The original behavior has been restored where blocks are allowed during
masternode sync phase.
…6b78

Reverted the unintended removal of KeyWalletFFI targets that were
removed in commit 5786b78. Restored:
- KeyWalletFFISwift product library
- KeyWalletFFI target with its linker settings
- KeyWalletFFISwift target

These targets are needed for the key wallet FFI functionality.
Removed the qr_info_spv_plan planning documentation folder as requested.
This folder contained phase planning documents that are no longer needed.
Restored the AddressGenerator interface definition that was commented out.
This interface is needed for the FFI bindings to work properly.
Reverted all recent changes that replaced kw_address with dashcore types:
- Restored 'address as kw_address' import
- Reverted AddressType conversions back to kw_address types
- Removed extra Error enum match cases (CoinJoinNotEnabled, Serialization, InvalidParameter)
- Restored Address struct to use kw_address::Address
- Uncommented AddressGenerator struct and all its methods
- Restored generate() and generate_range() method implementations

This brings the file back to its original working state with proper
key-wallet address handling.
@DCG-Claude DCG-Claude force-pushed the fix/cr-review-fixes branch from fe9e0bc to 9ec2793 Compare August 16, 2025 04:13
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

🔭 Outside diff range comments (1)
dash/src/sml/llmq_type/network.rs (1)

135-141: Avoid hardcoding activation heights; centralize network params.

Hardcoded activation thresholds are network parameters. Move them into named constants or a shared chain-params source to prevent drift and ease maintenance. This also adheres to the guideline to not hardcode network parameters.

Example refactor:

// Near the top of the module (or import from a central chain-params module)
const DASH_PLATFORM_QUORUM_ACTIVATION_HEIGHT: u32 = 1_888_888;
const TESTNET_PLATFORM_QUORUM_ACTIVATION_HEIGHT: u32 = 1_289_520;

// ...
fn should_skip_quorum_type(&self, llmq_type: &LLMQType, height: u32) -> bool {
    match (self, llmq_type) {
        (Network::Dash, LLMQType::Llmqtype100_67) => height < DASH_PLATFORM_QUORUM_ACTIVATION_HEIGHT,
        (Network::Testnet, LLMQType::Llmqtype25_67) => height < TESTNET_PLATFORM_QUORUM_ACTIVATION_HEIGHT,
        _ => false,
    }
}

If a central chain-params crate/module exists, prefer sourcing these from there instead of duplicating constants in this file.

♻️ Duplicate comments (2)
dash-spv/tests/error_handling_test.rs (2)

385-390: Fix: remove_utxo signature mismatch with StorageManager trait

This still returns StorageResult<()>. The trait expects StorageResult<Option> (returning the removed UTXO when available). This will block re-enabling the tests.

Apply this diff:

-    async fn remove_utxo(&mut self, _outpoint: &OutPoint) -> StorageResult<()> {
+    async fn remove_utxo(&mut self, _outpoint: &OutPoint) -> StorageResult<Option<Utxo>> {
         if self.fail_on_write {
             return Err(StorageError::WriteFailed("Mock write failure".to_string()));
         }
-        Ok(())
+        Ok(None)
     }

499-508: Fix: missing UnconfirmedTransaction type import (or fully qualify it)

These methods reference UnconfirmedTransaction without importing it, which will fail when this cfg gate is removed.

Add the missing import near other use statements:

use dash_spv::wallet::UnconfirmedTransaction;

Alternatively, fully qualify the type in all signatures as dash_spv::wallet::UnconfirmedTransaction.

Also applies to: 517-525, 527-534

🧹 Nitpick comments (7)
dash-spv/tests/smart_fetch_integration_test.rs (2)

94-110: Add assertions to validate vector lengths against the LLMQ size.

Small enhancement: assert that signers/valid_members lengths match llmq_type.size() to catch regressions in struct construction.

Consider adding the following assertions after constructing diff (e.g., after Line 118):

let entry = &diff.new_quorums[0];
let expected = entry.llmq_type.size() as usize;
assert_eq!(entry.signers.len(), expected, "signers length must equal LLMQ size");
assert_eq!(entry.valid_members.len(), expected, "valid_members length must equal LLMQ size");

94-110: Nit: Prefer scoping the block around the vector expression, not inside vec![…].

Readability can improve by moving the block out of the vec! macro:

For example, instead of wrapping the QuorumEntry in a block inside vec![…], write:

new_quorums: {
    let llmq_type = LLMQType::Llmqtype50_60;
    let quorum_size = llmq_type.size() as usize;
    vec![QuorumEntry {
        version: 1,
        llmq_type,
        quorum_hash: dashcore::QuorumHash::all_zeros(),
        quorum_index: None,
        signers: vec![true; quorum_size],
        valid_members: vec![true; quorum_size],
        quorum_public_key: dashcore::bls_sig_utils::BLSPublicKey::from([0; 48]),
        quorum_vvec_hash: dashcore::hash_types::QuorumVVecHash::all_zeros(),
        threshold_sig: dashcore::bls_sig_utils::BLSSignature::from([0; 96]),
        all_commitment_aggregated_signature: dashcore::bls_sig_utils::BLSSignature::from([0; 96]),
    }]
},
dash/src/sml/llmq_type/network.rs (1)

109-117: Nit: Make the comment/log message generic (not platform-specific).

The skip logic is generic and may apply to more types later. Tweak the comment and message to avoid implying it only targets platform quorums.

Apply this diff:

-                // Skip platform quorums before activation if needed
+                // Skip quorums before activation if needed
                 if self.should_skip_quorum_type(&llmq_type, window.mining_start) {
-                    log::trace!(
-                        "Skipping {:?} for height {} (activation threshold not met)",
-                        llmq_type,
-                        window.mining_start
-                    );
+                    log::trace!(
+                        "Skipping quorum type {:?} at mining_start {} (activation threshold not met)",
+                        llmq_type,
+                        window.mining_start
+                    );
                     continue;
                 }
dash-spv/src/sync/chainlock_validation.rs (3)

249-254: “Most recent quorum” selection via iter().next() is not deterministic

The comment says “Get the most recent quorum”, but quorums.iter().next() does not guarantee recency:

  • For HashMap-like containers, order is arbitrary.
  • For BTreeMap keyed by BlockHash, ordering by hash is unrelated to recency.
  • For insertion-ordered types, “first” might not be the newest.

Please ensure deterministic selection based on a concrete notion of “recency” (e.g., creation height or quorum index), or verify that the underlying container guarantees “newest-first” iteration.

If the container is ordered and newest is last:

-        let (quorum_hash, entry) = quorums.iter().next().ok_or_else(|| {
+        let (quorum_hash, entry) = quorums.iter().next_back().ok_or_else(|| {
             SyncError::Validation(format!(
                 "No quorum entries found for LLMQ type {:?}",
                 self.config.required_llmq_type
             ))
         })?;

If “recency” is captured by a field (example: quorum_entry.quorum_index), select explicitly:

-        let (quorum_hash, entry) = quorums.iter().next().ok_or_else(|| {
+        let (quorum_hash, entry) = quorums
+            .iter()
+            .max_by_key(|(_, e)| e.quorum_entry.quorum_index)
+            .ok_or_else(|| {
             SyncError::Validation(format!(
                 "No quorum entries found for LLMQ type {:?}",
                 self.config.required_llmq_type
             ))
         })?;

Follow-up: since None is no longer returned anywhere in this function, consider in a future (breaking) change simplifying the signature to SyncResult<(BlockHash, BLSPublicKey)>.


257-257: Consider avoiding potential move-from-borrow of quorum_public_key

This returns entry.quorum_entry.quorum_public_key by value. If BLSPublicKey is not Copy, moving out of a borrowed entry won’t compile; if it is Copy, this is fine. If not Copy, ensure Clone is used:

-        Ok(Some((*quorum_hash, entry.quorum_entry.quorum_public_key)))
+        Ok(Some((*quorum_hash, entry.quorum_entry.quorum_public_key.clone())))

43-44: Avoid hardcoding network parameters in defaults

Hardcoding LLMQType::Llmqtype400_60 in Default may not fit all networks (testnet/regtest/devnet). It’s good that the field is configurable, but consider deriving the default from the active network/chain params (e.g., via a dedicated constructor like ChainLockValidationConfig::for_network(params)) to align with the “no hardcoded network params” guideline.

I can sketch a for_network(params) constructor that selects the appropriate default LLMQ type per network if you share where chain params are sourced in this crate.

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

205-219: Optional: factor repeated fail_on_write checks into a helper

Many store/clear methods share identical write-failure checks. A small helper would reduce duplication and make future test-surface changes cheaper.

Example:

impl MockStorageManager {
    fn guard_write(&self) -> StorageResult<()> {
        if self.lock_poisoned {
            return Err(StorageError::LockPoisoned("Mock lock poisoned".into()));
        }
        if self.permission_denied {
            return Err(StorageError::WriteFailed("Permission denied".into()));
        }
        if self.disk_full {
            return Err(StorageError::WriteFailed("No space left on device".into()));
        }
        if self.fail_on_write {
            return Err(StorageError::WriteFailed("Mock write failure".into()));
        }
        Ok(())
    }
}

Then invoke self.guard_write()?; at the top of each write path.

Also applies to: 248-254, 309-315, 323-328, 337-342, 406-411, 427-436, 449-458, 478-487, 536-541, 550-555

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between dc50de8 and 9ec2793.

📒 Files selected for processing (18)
  • dash-spv-ffi/src/platform_integration.rs (1 hunks)
  • dash-spv/src/network/message_handler.rs (1 hunks)
  • dash-spv/src/sync/chainlock_validation.rs (1 hunks)
  • dash-spv/src/sync/sequential/mod.rs (1 hunks)
  • dash-spv/src/sync/sequential/phases.rs (2 hunks)
  • dash-spv/src/sync/validation.rs (3 hunks)
  • dash-spv/src/sync/validation_test.rs (2 hunks)
  • dash-spv/tests/chainlock_validation_test.rs (2 hunks)
  • dash-spv/tests/error_handling_test.rs (6 hunks)
  • dash-spv/tests/qrinfo_integration_test.rs (1 hunks)
  • dash-spv/tests/smart_fetch_integration_test.rs (1 hunks)
  • dash/src/sml/llmq_type/network.rs (1 hunks)
  • qr_info_spv_plan/PHASE_1.md (0 hunks)
  • qr_info_spv_plan/PHASE_2.md (0 hunks)
  • qr_info_spv_plan/PHASE_3.md (0 hunks)
  • qr_info_spv_plan/PHASE_4.md (0 hunks)
  • test-utils/src/fixtures.rs (1 hunks)
  • test_smart_algo.sh (1 hunks)
💤 Files with no reviewable changes (4)
  • qr_info_spv_plan/PHASE_3.md
  • qr_info_spv_plan/PHASE_2.md
  • qr_info_spv_plan/PHASE_1.md
  • qr_info_spv_plan/PHASE_4.md
🚧 Files skipped from review as they are similar to previous changes (10)
  • dash-spv/tests/qrinfo_integration_test.rs
  • dash-spv-ffi/src/platform_integration.rs
  • dash-spv/src/sync/sequential/mod.rs
  • dash-spv/tests/chainlock_validation_test.rs
  • test_smart_algo.sh
  • dash-spv/src/sync/validation_test.rs
  • test-utils/src/fixtures.rs
  • dash-spv/src/network/message_handler.rs
  • dash-spv/src/sync/validation.rs
  • dash-spv/src/sync/sequential/phases.rs
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{rs,swift}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Do not hardcode network parameters, addresses, or keys

Files:

  • dash/src/sml/llmq_type/network.rs
  • dash-spv/src/sync/chainlock_validation.rs
  • dash-spv/tests/smart_fetch_integration_test.rs
  • dash-spv/tests/error_handling_test.rs
**/*.rs

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.rs: Use proper error types with thiserror and propagate errors appropriately
Use the tokio runtime for async operations
Use conditional compilation feature flags for optional features
Code must pass clippy with -D warnings (no clippy warnings allowed)
Format code with rustfmt and keep formatting clean
Maintain MSRV 1.80 (code must compile on Rust 1.80)

Files:

  • dash/src/sml/llmq_type/network.rs
  • dash-spv/src/sync/chainlock_validation.rs
  • dash-spv/tests/smart_fetch_integration_test.rs
  • dash-spv/tests/error_handling_test.rs
{dash-network/**,dash-spv/**,rpc-client/**,rpc-json/**}/**/*.rs

📄 CodeRabbit Inference Engine (CLAUDE.md)

Always validate inputs from untrusted sources

Files:

  • dash-spv/src/sync/chainlock_validation.rs
  • dash-spv/tests/smart_fetch_integration_test.rs
  • dash-spv/tests/error_handling_test.rs
dash-spv/**/*.rs

📄 CodeRabbit Inference Engine (dash-spv/CLAUDE.md)

dash-spv/**/*.rs: Code must be rustfmt-formatted (cargo fmt --check must pass)
Clippy warnings must be treated as errors (cargo clippy ... -D warnings must pass)

Files:

  • dash-spv/src/sync/chainlock_validation.rs
  • dash-spv/tests/smart_fetch_integration_test.rs
  • dash-spv/tests/error_handling_test.rs
{rpc-integration-test/**,dash-spv/tests/**}/**/*.rs

📄 CodeRabbit Inference Engine (CLAUDE.md)

{rpc-integration-test/**,dash-spv/tests/**}/**/*.rs: Provide integration tests for network operations
Ensure tests cover both mainnet and testnet configurations

Files:

  • dash-spv/tests/smart_fetch_integration_test.rs
  • dash-spv/tests/error_handling_test.rs
**/tests/**/*.rs

📄 CodeRabbit Inference Engine (CLAUDE.md)

Use proptest for property-based testing where appropriate

Files:

  • dash-spv/tests/smart_fetch_integration_test.rs
  • dash-spv/tests/error_handling_test.rs
dash-spv/tests/**/*.rs

📄 CodeRabbit Inference Engine (dash-spv/CLAUDE.md)

Place integration tests under the tests/ directory and organize them as standalone test files

Files:

  • dash-spv/tests/smart_fetch_integration_test.rs
  • dash-spv/tests/error_handling_test.rs
🧠 Learnings (8)
📚 Learning: 2025-08-16T04:14:57.778Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:57.778Z
Learning: Applies to dash-spv/**/storage/**/*.rs : Respect storage paths: headers in headers/, filters in filters/, and state in state/; persist chain state and sync progress between runs

Applied to files:

  • dash-spv/tests/error_handling_test.rs
📚 Learning: 2025-06-26T15:54:02.509Z
Learnt from: DCG-Claude
PR: dashpay/rust-dashcore#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/tests/error_handling_test.rs
📚 Learning: 2025-08-16T04:14:57.778Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:57.778Z
Learning: Applies to dash-spv/**/*.rs : Clippy warnings must be treated as errors (cargo clippy ... -D warnings must pass)

Applied to files:

  • dash-spv/tests/error_handling_test.rs
📚 Learning: 2025-08-16T04:13:21.857Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-16T04:13:21.857Z
Learning: Applies to {rpc-integration-test/**,dash-spv/tests/**}/**/*.rs : Provide integration tests for network operations

Applied to files:

  • dash-spv/tests/error_handling_test.rs
📚 Learning: 2025-08-16T04:14:57.778Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:57.778Z
Learning: Applies to dash-spv/**/{network,storage}/**/*.rs : Expose swappable implementations behind trait objects (e.g., Arc<dyn NetworkManager>, Arc<dyn StorageManager>) for runtime polymorphism

Applied to files:

  • dash-spv/tests/error_handling_test.rs
📚 Learning: 2025-08-16T04:14:57.778Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:57.778Z
Learning: Applies to dash-spv/**/error.rs : Define and maintain domain-specific error types (NetworkError, StorageError, SyncError, ValidationError, SpvError) in the central error module

Applied to files:

  • dash-spv/tests/error_handling_test.rs
📚 Learning: 2025-08-16T04:14:57.778Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:57.778Z
Learning: Applies to dash-spv/tests/integration_real_node_test.rs : Integration tests that connect to a real Dash Core node (127.0.0.1:9999) must gracefully skip when the node is unavailable

Applied to files:

  • dash-spv/tests/error_handling_test.rs
📚 Learning: 2025-08-16T04:13:21.857Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-16T04:13:21.857Z
Learning: Applies to {rpc-integration-test/**,dash-spv/tests/**}/**/*.rs : Ensure tests cover both mainnet and testnet configurations

Applied to files:

  • dash-spv/tests/error_handling_test.rs
🧬 Code Graph Analysis (2)
dash-spv/src/sync/chainlock_validation.rs (1)
dash-spv/src/sync/masternodes.rs (1)
  • engine (456-458)
dash-spv/tests/smart_fetch_integration_test.rs (1)
dash/src/sml/llmq_type/mod.rs (1)
  • size (363-365)
⏰ 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). (15)
  • GitHub Check: fuzz (hashes_sha512)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (dash_deserialize_amount)
  • GitHub Check: fuzz (dash_deser_net_msg)
  • GitHub Check: fuzz (dash_outpoint_string)
🔇 Additional comments (19)
dash-spv/tests/smart_fetch_integration_test.rs (1)

94-110: Dynamic quorum sizing via llmq_type.size() is correct and future-proof.

Switching signers/valid_members to derive their length from llmq_type.size() removes the previous hardcoded assumption and aligns the test with parameterized LLMQ sizes. Nice improvement.

dash/src/sml/llmq_type/network.rs (1)

109-117: Per-window activation gating is correct and aligns with the new API.

Evaluating should_skip_quorum_type per window using window.mining_start prevents pre-activation windows from leaking in while allowing post-activation windows. Good move.

dash-spv/src/sync/chainlock_validation.rs (2)

226-238: Good move: explicit Validation errors instead of silent None paths

Switching to SyncError::Validation for missing masternode lists and list retrieval makes failure modes actionable and surfaces root causes early. This aligns with the PR’s error-handling goals.


241-247: Clear errors when no quorums exist for the required LLMQ type

Returning descriptive SyncError::Validation messages here is a solid improvement over Ok(None), helping operators diagnose configuration/data gaps quickly.

dash-spv/tests/error_handling_test.rs (15)

205-219: LGTM: store_headers error-injection paths

The layered error checks (lock_poisoned, permission_denied, disk_full, fail_on_write) look good and deterministic for testing.


221-226: LGTM: load_headers happy-path and read-failure simulation

Returns an empty set by default and properly surfaces read failures.


248-254: LGTM: filter headers APIs mirror header storage semantics

Consistent error handling and defaults across store/load/get and tip height.

Also applies to: 255-261, 262-267, 269-274


276-285: LGTM: masternode state store/load shims

Consistent with the expanded trait surface and error paths.

Also applies to: 286-293


295-300: LGTM: chain state store/load coverage

Matches expected semantics for exercising error propagation in tests.

Also applies to: 302-307


309-315: LGTM: filters store/load shims

Error paths are consistent; default returns None for missing filters.

Also applies to: 316-321


323-328: LGTM: metadata store/load

Thin wrappers with consistent error injection.

Also applies to: 330-335


337-342: LGTM: clear and stats

Clear uses write-failure guard. Stats returns a sensible zeroed struct for tests.

Also applies to: 344-355


357-365: LGTM: header lookup utilities

Height-by-hash and batch retrieval stubs behave as expected for error testing.

Also applies to: 367-376


392-397: LGTM: UTXO getters

Return empty collections by default; surface read failures when toggled.

Also applies to: 399-404


406-411: LGTM: sync state and checkpoints APIs

Good coverage of store/load/clear/checkpoint flows with failure injection.

Also applies to: 413-418, 420-425, 427-436, 438-447


449-458: LGTM: chain lock storage APIs

Interfaces for store/load/range retrieval look consistent with trait evolution.

Also applies to: 460-465, 467-476


478-487: LGTM: instant lock storage APIs

Matches the pattern used for chain locks and mempool.

Also applies to: 489-497


510-515: LGTM: remove_mempool_transaction

Simple write-failure guard is sufficient for exercising error paths.


536-541: LGTM: mempool state store/load/clear

Consistent failure toggles and defaults.

Also applies to: 543-548, 550-555

Comment on lines +241 to +246
async fn get_tip_height(&self) -> StorageResult<Option<u32>> {
if self.fail_on_read {
return Err(StorageError::ReadFailed("Mock read failure".to_string()));
}
Ok(Some(0))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify: get_tip_height may be obsolete (trait likely replaced with get_filter_tip_height)

The enriched summary notes get_tip_height was replaced by get_filter_tip_height. If the trait no longer contains get_tip_height, this impl won’t compile. Please confirm the StorageManager trait’s current surface and remove/rename accordingly.

Run this script to compare trait vs. impl and list signature deltas:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

trait_file="$(rg -nPl 'trait\s+StorageManager\b' | head -n1)"
impl_file="dash-spv/tests/error_handling_test.rs"

echo "Trait file: $trait_file"
echo "Impl file : $impl_file"
echo

python - << 'PY'
import re, sys, pathlib, json

trait_path = pathlib.Path(sys.argv[1])
impl_path  = pathlib.Path(sys.argv[2])

trait_src = trait_path.read_text(encoding='utf-8')
impl_src  = impl_path.read_text(encoding='utf-8')

def extract_trait_methods(src):
    m = re.search(r'trait\s+StorageManager\s*{(?P<body>.*?)}', src, re.S)
    if not m:
        return {}
    body = m.group('body')
    sigs = re.findall(r'(?m)^\s*(?:async\s+)?fn\s+([a-zA-Z0-9_]+)\s*\((.*?)\)\s*->\s*([^;{]+)\s*;', body)
    return {name: ret.strip() for name, _, ret in sigs}

def extract_impl_methods(src):
    m = re.search(r'impl\s+(?:[\w:]+::)?StorageManager\s+for\s+MockStorageManager\s*{(?P<body>.*?)}\s*$', src, re.S)
    if not m:
        return {}
    body = m.group('body')
    sigs = re.findall(r'(?m)^\s*(?:async\s+)?fn\s+([a-zA-Z0-9_]+)\s*\((.*?)\)\s*->\s*([^{\n]+)\s*{', body)
    return {name: ret.strip() for name, _, ret in sigs}

trait = extract_trait_methods(trait_src)
impl  = extract_impl_methods(impl_src)

missing_in_impl = sorted(set(trait) - set(impl))
extra_in_impl   = sorted(set(impl) - set(trait))
ret_mismatch    = sorted([name for name in trait.keys() & impl.keys() if trait[name] != impl[name]])

print("Methods missing in impl (trait -> impl):")
for n in missing_in_impl:
    print(f"  - {n}: expected return {trait[n]}")

print("\nExtra methods in impl (not in trait):")
for n in extra_in_impl:
    print(f"  - {n}: impl return {impl[n]}")

print("\nReturn type mismatches:")
for n in ret_mismatch:
    print(f"  - {n}: trait {trait[n]} vs impl {impl[n]}")
PY
"$trait_file" "$impl_file"

Length of output: 486


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the StorageManager trait definition
trait_file="$(rg -nPl 'trait\s+StorageManager\b' | head -n1)"
echo "Trait file: $trait_file"
echo

# Search for get_tip_height in the trait
echo "Searching for get_tip_height in trait:"
rg -n 'fn\s\+get_tip_height' -C2 "$trait_file" || echo "  → get_tip_height: not found"

echo
# Search for get_filter_tip_height in the trait
echo "Searching for get_filter_tip_height in trait:"
rg -n 'fn\s\+get_filter_tip_height' -C2 "$trait_file" || echo "  → get_filter_tip_height: not found"

Length of output: 688


Remove obsolete get_tip_height from MockStorageManager
The current StorageManager trait in dash-spv/src/storage/mod.rs no longer declares get_tip_height (or get_filter_tip_height). As a result, the MockStorageManager impl in dash-spv/tests/error_handling_test.rs defines an extra method that won’t match the trait. Please update this impl to mirror the trait’s actual tip‐height API or remove it if it’s no longer needed.

• dash-spv/tests/error_handling_test.rs (lines 241–246):

-    async fn get_tip_height(&self) -> StorageResult<Option<u32>> {
-        if self.fail_on_read {
-            return Err(StorageError::ReadFailed("Mock read failure".to_string()));
-        }
-        Ok(Some(0))
-    }

– Remove or rename this block to the correct method signature as defined in dash-spv/src/storage/mod.rs.
– Verify the trait’s current tip‐height method name and return type before implementing.

📝 Committable suggestion

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

Suggested change
async fn get_tip_height(&self) -> StorageResult<Option<u32>> {
if self.fail_on_read {
return Err(StorageError::ReadFailed("Mock read failure".to_string()));
}
Ok(Some(0))
}
🤖 Prompt for AI Agents
In dash-spv/tests/error_handling_test.rs around lines 241–246 the
MockStorageManager impl defines a now-obsolete get_tip_height method that no
longer matches the StorageManager trait; remove this method or replace it with
the exact tip-height API declared in dash-spv/src/storage/mod.rs (confirm the
current method name and signature there — e.g., get_filter_tip_height or other
name and its return type), then implement the correct method signature with
matching return type and error handling or delete the mock method entirely if
the tests no longer require it.

…mpilation

- Restored key-wallet-ffi/src/lib.rs with correct imports (Address/AddressType from key_wallet)
- Restored fuzz/Cargo.toml with required key-wallet dependency
- Restored fuzz/fuzz_targets/dash/deserialize_psbt.rs to use key_wallet::psbt
- Restored dash-spv-ffi test files with correct struct field initializations
  - Added sync_base_height and synced_from_checkpoint to ChainState tests
  - Added connected_peers, total_peers, header_height, filter_height to SpvStats tests

These files were incorrectly modified in previous commits, causing build failures.
Restoring them from origin/v0.40-dev resolves all compilation issues.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@PastaPastaPasta
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 16, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🔭 Outside diff range comments (2)
fuzz/Cargo.toml (1)

3-3: MSRV 1.80 incompatibility: edition "2024" will fail to compile

Project guidelines require MSRV 1.80. Rust 1.80 does not support the 2024 edition. Please switch this crate’s edition to 2021.

Apply this diff:

- edition = "2024"
+ edition = "2021"
key-wallet-ffi/src/lib.rs (1)

502-509: Remove unreachable!() in FFI-facing code path; provide a safe fallback

Panics at FFI boundaries are dangerous. Also, per the recorded preference, unknown networks should conservatively default to Testnet rather than panic.

Apply this diff:

         match *self.inner.network() {
             KwNetwork::Dash => Network::Dash,
             KwNetwork::Testnet => Network::Testnet,
             KwNetwork::Regtest => Network::Regtest,
             KwNetwork::Devnet => Network::Devnet,
-            unknown => unreachable!("Unhandled network variant: {:?}", unknown),
+            _ => Network::Testnet, // Fallback for future/unknown variants
         }
♻️ Duplicate comments (1)
key-wallet-ffi/src/lib.rs (1)

79-87: Don’t silently coerce unknown address types to P2PKH

Defaulting unknown KwAddressType variants to P2PKH can misrepresent addresses and surprise callers. Prefer an explicit Unknown variant or return an error. This mirrors a past comment on non-exhaustive AddressType mappings.

Apply this diff within the selected range:

 impl From<KwAddressType> for AddressType {
     fn from(t: KwAddressType) -> Self {
         match t {
             KwAddressType::P2pkh => AddressType::P2PKH,
             KwAddressType::P2sh => AddressType::P2SH,
-            _ => AddressType::P2PKH, // Default to P2PKH for unknown types
+            _ => AddressType::Unknown,
         }
     }
 }

And outside this range, add the Unknown variant and adjust get_type accordingly:

// Add Unknown to the FFI enum
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddressType {
    P2PKH,
    P2SH,
    Unknown,
}
@@ pub fn get_type(&self) -> AddressType {
-    self.inner.address_type().unwrap_or(KwAddressType::P2pkh).into()
+    self.inner
+        .address_type()
+        .map(Into::into)
+        .unwrap_or(AddressType::Unknown)
}
🧹 Nitpick comments (2)
dash-spv-ffi/tests/unit/test_type_conversions.rs (1)

175-177: FFIChainState doesn’t expose checkpoint sync fields
The FFI struct and its From<ChainState> impl only map:

  • header_height
  • filter_header_height
  • masternode_height
  • last_chainlock_height
  • last_chainlock_hash
  • current_filter_tip

The new ChainState fields sync_base_height and synced_from_checkpoint are never exposed over FFI.
If you intend to surface checkpoint sync state through the FFI API:

  • Add sync_base_height: u32 and synced_from_checkpoint: bool to FFIChainState
  • Populate them in impl From<ChainState> for FFIChainState
  • Add assertions in test_type_conversions.rs for those fields

Otherwise, document in FFIChainState (and its From impl) that checkpoint-sync fields are intentionally omitted.
[optional_refactors_recommended]

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

591-599: Prevent potential u32 overflow when computing start + i

start + i can overflow for large ranges (debug panic, release wrap). Use checked_add and return an error on overflow.

Apply this diff:

-        for i in 0..count {
-            let addr = self.generate(account_xpub.clone(), external, start + i)?;
-            addresses.push(addr);
-        }
+        for i in 0..count {
+            let index = start
+                .checked_add(i)
+                .ok_or_else(|| KeyWalletError::KeyError { message: "address index overflow".into() })?;
+            let addr = self.generate(account_xpub.clone(), external, index)?;
+            addresses.push(addr);
+        }
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9ec2793 and dea2559.

📒 Files selected for processing (5)
  • dash-spv-ffi/tests/test_platform_integration_safety.rs (1 hunks)
  • dash-spv-ffi/tests/unit/test_type_conversions.rs (2 hunks)
  • fuzz/Cargo.toml (1 hunks)
  • fuzz/fuzz_targets/dash/deserialize_psbt.rs (1 hunks)
  • key-wallet-ffi/src/lib.rs (9 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
fuzz/**

📄 CodeRabbit Inference Engine (CLAUDE.md)

Maintain fuzzing tests under fuzz/ for security testing

Files:

  • fuzz/Cargo.toml
  • fuzz/fuzz_targets/dash/deserialize_psbt.rs
**/*.{rs,swift}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Do not hardcode network parameters, addresses, or keys

Files:

  • fuzz/fuzz_targets/dash/deserialize_psbt.rs
  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv-ffi/tests/test_platform_integration_safety.rs
  • key-wallet-ffi/src/lib.rs
**/*.rs

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.rs: Use proper error types with thiserror and propagate errors appropriately
Use the tokio runtime for async operations
Use conditional compilation feature flags for optional features
Code must pass clippy with -D warnings (no clippy warnings allowed)
Format code with rustfmt and keep formatting clean
Maintain MSRV 1.80 (code must compile on Rust 1.80)

Files:

  • fuzz/fuzz_targets/dash/deserialize_psbt.rs
  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv-ffi/tests/test_platform_integration_safety.rs
  • key-wallet-ffi/src/lib.rs
{dash-network-ffi/**,dash-spv-ffi/**,key-wallet-ffi/**,swift-dash-core-sdk/**}/**/*.{rs,udl,h,swift}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Be careful with memory management at FFI boundaries

Files:

  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv-ffi/tests/test_platform_integration_safety.rs
  • key-wallet-ffi/src/lib.rs
**/tests/**/*.rs

📄 CodeRabbit Inference Engine (CLAUDE.md)

Use proptest for property-based testing where appropriate

Files:

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

📄 CodeRabbit Inference Engine (dash-spv-ffi/CLAUDE.md)

Add a corresponding Rust unit test for each new FFI function

Files:

  • dash-spv-ffi/tests/unit/test_type_conversions.rs
{key-wallet/**,key-wallet-ffi/**,swift-dash-core-sdk/**}/**/*.{rs,swift}

📄 CodeRabbit Inference Engine (CLAUDE.md)

{key-wallet/**,key-wallet-ffi/**,swift-dash-core-sdk/**}/**/*.{rs,swift}: Use secure random number generation for keys
Never log or expose private keys

Files:

  • key-wallet-ffi/src/lib.rs
🧠 Learnings (9)
📚 Learning: 2025-08-16T04:14:07.667Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:07.667Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Expose complex Rust types to C as opaque pointers (e.g., FFIDashSpvClient*)

Applied to files:

  • fuzz/Cargo.toml
  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv-ffi/tests/test_platform_integration_safety.rs
📚 Learning: 2025-08-16T04:13:21.857Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-16T04:13:21.857Z
Learning: Applies to {rpc-integration-test/**,dash-spv/tests/**}/**/*.rs : Provide integration tests for network operations

Applied to files:

  • fuzz/fuzz_targets/dash/deserialize_psbt.rs
  • dash-spv-ffi/tests/unit/test_type_conversions.rs
📚 Learning: 2025-08-16T04:14:07.667Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:07.667Z
Learning: Applies to dash-spv-ffi/tests/unit/**/*.rs : Add a corresponding Rust unit test for each new FFI function

Applied to files:

  • dash-spv-ffi/tests/unit/test_type_conversions.rs
  • dash-spv-ffi/tests/test_platform_integration_safety.rs
📚 Learning: 2025-08-16T04:14:57.778Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:57.778Z
Learning: Applies to dash-spv/tests/**/*.rs : Place integration tests under the tests/ directory and organize them as standalone test files

Applied to files:

  • dash-spv-ffi/tests/unit/test_type_conversions.rs
📚 Learning: 2025-08-16T04:13:21.857Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-16T04:13:21.857Z
Learning: Applies to {rpc-integration-test/**,dash-spv/tests/**}/**/*.rs : Ensure tests cover both mainnet and testnet configurations

Applied to files:

  • dash-spv-ffi/tests/unit/test_type_conversions.rs
📚 Learning: 2025-08-16T04:14:07.667Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:07.667Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Add cbindgen annotations for complex types so headers are generated correctly

Applied to files:

  • dash-spv-ffi/tests/unit/test_type_conversions.rs
📚 Learning: 2025-08-16T04:13:21.857Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-16T04:13:21.857Z
Learning: Applies to {dash-network-ffi/**,dash-spv-ffi/**,key-wallet-ffi/**,swift-dash-core-sdk/**}/**/*.{rs,udl,h,swift} : Be careful with memory management at FFI boundaries

Applied to files:

  • dash-spv-ffi/tests/test_platform_integration_safety.rs
  • key-wallet-ffi/src/lib.rs
📚 Learning: 2025-06-15T15:31:44.136Z
Learnt from: QuantumExplorer
PR: dashpay/rust-dashcore#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-ffi/src/lib.rs
📚 Learning: 2025-06-15T16:42:18.187Z
Learnt from: QuantumExplorer
PR: dashpay/rust-dashcore#74
File: key-wallet-ffi/src/lib_tests.rs:41-48
Timestamp: 2025-06-15T16:42:18.187Z
Learning: In key-wallet-ffi, the HDWallet::derive_xpriv method returns Result<String, KeyWalletError>, not an ExtPrivKey wrapper. When unwrapped, it yields a String that can have .is_empty() called on it.

Applied to files:

  • key-wallet-ffi/src/lib.rs
🧬 Code Graph Analysis (3)
fuzz/fuzz_targets/dash/deserialize_psbt.rs (1)
dash/examples/taproot-psbt.rs (1)
  • psbt (289-289)
dash-spv-ffi/tests/test_platform_integration_safety.rs (1)
dash-spv-ffi/src/platform_integration.rs (1)
  • ffi_dash_spv_get_core_handle (39-50)
key-wallet-ffi/src/lib.rs (3)
dash/src/address.rs (10)
  • from (173-175)
  • from (180-182)
  • from (407-409)
  • from (414-419)
  • network (936-938)
  • from_str (216-225)
  • from_str (283-286)
  • from_str (1330-1392)
  • new (453-473)
  • new (1017-1025)
dash-network/src/lib.rs (1)
  • from_str (97-105)
dash-network-ffi/src/lib.rs (1)
  • new (58-62)
⏰ 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_sha512)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (dash_deserialize_amount)
  • GitHub Check: fuzz (dash_outpoint_string)
🔇 Additional comments (7)
fuzz/Cargo.toml (1)

14-14: LGTM: adding key-wallet dependency enables the PSBT fuzz target transition

The local path dep is appropriate for fuzz-only usage. No concerns.

dash-spv-ffi/tests/test_platform_integration_safety.rs (1)

360-365: Correct FFI call target: passing client ptr to get_core_handle

Using null_client with ffi_dash_spv_get_core_handle matches the updated signature and the test intent. Good fix.

fuzz/fuzz_targets/dash/deserialize_psbt.rs (1)

4-13: LGTM: PSBT fuzz target migrated to key_wallet types

The round-trip (deserialize → serialize → deserialize) semantics are preserved, and the type path updates are consistent with the new dependency.

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

89-96: LGTM: FFI <-> key-wallet address type bridging

The direct mapping of P2PKH/P2SH to KwAddressType is correct.


194-203: LGTM: broader kw::Error mapping improves diagnosability

Adding CoinJoinNotEnabled, Serialization, and InvalidParameter mappings provides clearer error surfaces.


215-221: LGTM: dashcore::address::Error -> KeyWalletError::AddressError mapping

This ensures parsing failures are surfaced cleanly through FFI.


466-475: LGTM: require_network enforces network correctness for parsed addresses

Parsing as unchecked then requiring the expected network is the right safety guard for FFI inputs.

Comment on lines +218 to 222
connected_peers: 0,
total_peers: 0,
header_height: 0,
filter_height: 0,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

SpvStats new fields are not validated in FFISpvStats

connected_peers/total_peers/header_height/filter_height are set in the Rust struct but not asserted post-conversion. Either add FFISpvStats assertions (if supported) or note they’re intentionally ignored.

Try locating these fields in the FFI:


🏁 Script executed:

#!/bin/bash
# Search for new SpvStats fields in FFI conversion code
rg -n -C3 -P '\b(connected_peers|total_peers|header_height|filter_height)\b'

Length of output: 70575


🏁 Script executed:

#!/bin/bash
# Display the FFISpvStats conversion test and following assertions
sed -n '200,260p' dash-spv-ffi/tests/unit/test_type_conversions.rs

Length of output: 2442


Add missing FFISpvStats assertions for peer and height fields
The SpvStats conversion test in dash-spv-ffi/tests/unit/test_type_conversions.rs currently checks most metrics but omits the four new fields (connected_peers, total_peers, header_height, filter_height). Please update the test to include assertions for these fields:

diff --git a/dash-spv-ffi/tests/unit/test_type_conversions.rs b/dash-spv-ffi/tests/unit/test_type_conversions.rs
index abc1234..def5678 100644
--- a/dash-spv-ffi/tests/unit/test_type_conversions.rs
+++ b/dash-spv-ffi/tests/unit/test_type_conversions.rs
@@ fn test_spv_stats_conversion() {
     assert_eq!(ffi_stats.uptime, u64::MAX);
+    // Validate peer counts and heights
+    assert_eq!(ffi_stats.connected_peers, 0);
+    assert_eq!(ffi_stats.total_peers,     0);
+    assert_eq!(ffi_stats.header_height,    0);
+    assert_eq!(ffi_stats.filter_height,    0);
 }

This ensures all fields introduced on the Rust side are properly round-tripped to the FFI struct.

📝 Committable suggestion

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

Suggested change
connected_peers: 0,
total_peers: 0,
header_height: 0,
filter_height: 0,
};
assert_eq!(ffi_stats.uptime, u64::MAX);
// Validate peer counts and heights
assert_eq!(ffi_stats.connected_peers, 0);
assert_eq!(ffi_stats.total_peers, 0);
assert_eq!(ffi_stats.header_height, 0);
assert_eq!(ffi_stats.filter_height, 0);
}
🤖 Prompt for AI Agents
In dash-spv-ffi/tests/unit/test_type_conversions.rs around lines 218 to 222, the
SpvStats round-trip test is missing assertions for the four new fields
(connected_peers, total_peers, header_height, filter_height); update the
expected Rust SpvStats instance and/or the assertions on the converted FFI
struct to include checks that connected_peers and total_peers equal the original
values and that header_height and filter_height equal the original heights,
adding those four assertions alongside the existing assertions so the test
validates all fields introduced on the Rust side.

Comment on lines 497 to 499
pub fn get_type(&self) -> AddressType {
self.inner.address_type.into()
self.inner.address_type().unwrap_or(KwAddressType::P2pkh).into()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid defaulting unknown address types to P2PKH in get_type

For the same reason as above, defaulting to P2PKH can mislead callers. Prefer returning AddressType::Unknown when the type is None/unknown.

Apply this diff:

-        self.inner.address_type().unwrap_or(KwAddressType::P2pkh).into()
+        self.inner
+            .address_type()
+            .map(Into::into)
+            .unwrap_or(AddressType::Unknown)
📝 Committable suggestion

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

Suggested change
pub fn get_type(&self) -> AddressType {
self.inner.address_type.into()
self.inner.address_type().unwrap_or(KwAddressType::P2pkh).into()
}
pub fn get_type(&self) -> AddressType {
self.inner
.address_type()
.map(Into::into)
.unwrap_or(AddressType::Unknown)
}
🤖 Prompt for AI Agents
In key-wallet-ffi/src/lib.rs around lines 497 to 499, get_type currently
defaults unknown/None address types to P2PKH which can mislead callers; change
the implementation to return AddressType::Unknown when self.inner.address_type()
is None or unrecognized instead of defaulting to P2PKH — e.g. match or map the
Option from address_type() and convert the present KwAddressType into
AddressType, otherwise return AddressType::Unknown (ensure the conversion path
supports the Unknown variant).

Added the missing import for UnconfirmedTransaction type which is used in the
StorageManager trait implementation. This prevents compilation failure when
the cfg gate is eventually removed.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
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 (1)
dash-spv/tests/error_handling_test.rs (1)

39-39: Import fixes the earlier unresolved UnconfirmedTransaction type

Brings UnconfirmedTransaction into scope for the mock mempool methods, addressing the prior compilation blocker.

🧹 Nitpick comments (4)
dash-spv/tests/error_handling_test.rs (4)

344-355: Add read-failure injection to stats() for consistency

Most read paths honor fail_on_read; stats() should, too, so tests can exercise error propagation uniformly.

Apply this diff:

 async fn stats(&self) -> StorageResult<dash_spv::storage::StorageStats> {
+    if self.fail_on_read {
+        return Err(StorageError::ReadFailed("Mock read failure".to_string()));
+    }
     Ok(dash_spv::storage::StorageStats {
         headers_count: 0,
         filter_headers_count: 0,
         filters_count: 0,
         headers_size_bytes: 0,
         filter_headers_size_bytes: 0,
         filters_size_bytes: 0,
         total_size_bytes: 0,
         last_compaction: None,
     })
 }

276-284: State, filter, and metadata mocks are consistent; consider lock_poisoned symmetry for reads

These methods correctly gate on write/read failures. Only get_header checks lock_poisoned; if you want uniform behavior, optionally mirror that check in other read methods too (non-essential).

Also applies to: 286-293, 295-300, 302-307, 309-314, 316-321, 323-328, 330-335, 337-342


560-573: Make test_network_connection_failure deterministic (avoid relying on local node state)

This tries a real TCP connect to 127.0.0.1:9999 and asserts failure. If a local node is running, the test will flake. Add a quick preflight and skip when the port is open.

Apply this diff:

 #[tokio::test]
 async fn test_network_connection_failure() {
     let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9999);
 
+    // If a local Dash node is running, skip this test to avoid flakes.
+    if std::net::TcpStream::connect_timeout(&addr, Duration::from_millis(50)).is_ok() {
+        eprintln!("Skipping test_network_connection_failure: local node appears to be running on {}", addr);
+        return;
+    }
+
     // Test connection timeout
     let result = TcpConnection::connect(addr, 1, Duration::from_millis(100), Network::Dash).await;
 
     match result {
         Err(NetworkError::ConnectionFailed(msg)) => {
             assert!(msg.contains("Failed to connect"));
         }
         _ => panic!("Expected ConnectionFailed error"),
     }
 }

6-6: Plan to re-enable this test module once the mock matches the trait surface

The cfg gate is appropriate for now. After resolving any trait/impl deltas (notably tip-height and UTXO removal), please drop the cfg to get these error-path tests into CI.

I can generate a script to diff all StorageManager methods vs. the mock impl and produce a checklist to close before re-enabling. Want me to add it?

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between dea2559 and 0ecb5ad.

📒 Files selected for processing (1)
  • dash-spv/tests/error_handling_test.rs (7 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{rs,swift}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Do not hardcode network parameters, addresses, or keys

Files:

  • dash-spv/tests/error_handling_test.rs
**/*.rs

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.rs: Use proper error types with thiserror and propagate errors appropriately
Use the tokio runtime for async operations
Use conditional compilation feature flags for optional features
Code must pass clippy with -D warnings (no clippy warnings allowed)
Format code with rustfmt and keep formatting clean
Maintain MSRV 1.80 (code must compile on Rust 1.80)

Files:

  • dash-spv/tests/error_handling_test.rs
{rpc-integration-test/**,dash-spv/tests/**}/**/*.rs

📄 CodeRabbit Inference Engine (CLAUDE.md)

{rpc-integration-test/**,dash-spv/tests/**}/**/*.rs: Provide integration tests for network operations
Ensure tests cover both mainnet and testnet configurations

Files:

  • dash-spv/tests/error_handling_test.rs
**/tests/**/*.rs

📄 CodeRabbit Inference Engine (CLAUDE.md)

Use proptest for property-based testing where appropriate

Files:

  • dash-spv/tests/error_handling_test.rs
{dash-network/**,dash-spv/**,rpc-client/**,rpc-json/**}/**/*.rs

📄 CodeRabbit Inference Engine (CLAUDE.md)

Always validate inputs from untrusted sources

Files:

  • dash-spv/tests/error_handling_test.rs
dash-spv/**/*.rs

📄 CodeRabbit Inference Engine (dash-spv/CLAUDE.md)

dash-spv/**/*.rs: Code must be rustfmt-formatted (cargo fmt --check must pass)
Clippy warnings must be treated as errors (cargo clippy ... -D warnings must pass)

Files:

  • dash-spv/tests/error_handling_test.rs
dash-spv/tests/**/*.rs

📄 CodeRabbit Inference Engine (dash-spv/CLAUDE.md)

Place integration tests under the tests/ directory and organize them as standalone test files

Files:

  • dash-spv/tests/error_handling_test.rs
🧠 Learnings (9)
📚 Learning: 2025-06-26T15:54:02.509Z
Learnt from: DCG-Claude
PR: dashpay/rust-dashcore#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/tests/error_handling_test.rs
📚 Learning: 2025-08-16T04:14:57.778Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:57.778Z
Learning: Applies to dash-spv/**/storage/**/*.rs : Respect storage paths: headers in headers/, filters in filters/, and state in state/; persist chain state and sync progress between runs

Applied to files:

  • dash-spv/tests/error_handling_test.rs
📚 Learning: 2025-08-16T04:14:57.778Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:57.778Z
Learning: Applies to dash-spv/**/{network,storage}/**/*.rs : Expose swappable implementations behind trait objects (e.g., Arc<dyn NetworkManager>, Arc<dyn StorageManager>) for runtime polymorphism

Applied to files:

  • dash-spv/tests/error_handling_test.rs
📚 Learning: 2025-08-16T04:14:57.778Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:57.778Z
Learning: Applies to dash-spv/**/*.rs : Clippy warnings must be treated as errors (cargo clippy ... -D warnings must pass)

Applied to files:

  • dash-spv/tests/error_handling_test.rs
📚 Learning: 2025-08-16T04:14:57.778Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:57.778Z
Learning: Applies to dash-spv/**/error.rs : Define and maintain domain-specific error types (NetworkError, StorageError, SyncError, ValidationError, SpvError) in the central error module

Applied to files:

  • dash-spv/tests/error_handling_test.rs
📚 Learning: 2025-06-26T16:01:37.609Z
Learnt from: DCG-Claude
PR: dashpay/rust-dashcore#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/tests/error_handling_test.rs
📚 Learning: 2025-08-16T04:13:21.857Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-16T04:13:21.857Z
Learning: Applies to {rpc-integration-test/**,dash-spv/tests/**}/**/*.rs : Provide integration tests for network operations

Applied to files:

  • dash-spv/tests/error_handling_test.rs
📚 Learning: 2025-08-16T04:14:57.778Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-08-16T04:14:57.778Z
Learning: Applies to dash-spv/tests/integration_real_node_test.rs : Integration tests that connect to a real Dash Core node (127.0.0.1:9999) must gracefully skip when the node is unavailable

Applied to files:

  • dash-spv/tests/error_handling_test.rs
📚 Learning: 2025-08-16T04:13:21.857Z
Learnt from: CR
PR: dashpay/rust-dashcore#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-16T04:13:21.857Z
Learning: Applies to {rpc-integration-test/**,dash-spv/tests/**}/**/*.rs : Ensure tests cover both mainnet and testnet configurations

Applied to files:

  • dash-spv/tests/error_handling_test.rs
🧬 Code Graph Analysis (1)
dash-spv/tests/error_handling_test.rs (2)
dash-spv/src/storage/mod.rs (38)
  • store_headers (108-108)
  • load_headers (111-111)
  • get_tip_height (122-122)
  • store_filter_headers (125-125)
  • load_filter_headers (128-128)
  • get_filter_header (131-131)
  • get_filter_tip_height (134-134)
  • store_masternode_state (137-137)
  • load_masternode_state (140-140)
  • store_chain_state (143-143)
  • load_chain_state (146-146)
  • store_filter (149-149)
  • load_filter (152-152)
  • store_metadata (155-155)
  • load_metadata (158-158)
  • clear (161-161)
  • stats (164-164)
  • get_header_height_by_hash (167-170)
  • get_headers_batch (174-178)
  • remove_utxo (184-184)
  • get_utxos_for_address (187-187)
  • store_sync_state (193-193)
  • load_sync_state (196-196)
  • clear_sync_state (199-199)
  • store_sync_checkpoint (202-206)
  • get_sync_checkpoints (209-213)
  • store_chain_lock (216-220)
  • load_chain_lock (223-223)
  • get_chain_locks (226-230)
  • store_instant_lock (233-237)
  • load_instant_lock (240-243)
  • store_mempool_transaction (247-251)
  • remove_mempool_transaction (254-254)
  • get_mempool_transaction (257-260)
  • get_all_mempool_transactions (263-265)
  • store_mempool_state (268-268)
  • load_mempool_state (271-271)
  • clear_mempool (274-274)
dash-spv/src/storage/memory.rs (38)
  • store_headers (60-98)
  • load_headers (100-109)
  • get_tip_height (115-121)
  • store_filter_headers (123-128)
  • load_filter_headers (130-139)
  • get_filter_header (141-144)
  • get_filter_tip_height (146-153)
  • store_masternode_state (155-158)
  • load_masternode_state (160-162)
  • store_chain_state (164-167)
  • load_chain_state (169-171)
  • store_filter (173-176)
  • load_filter (178-180)
  • store_metadata (182-185)
  • load_metadata (187-189)
  • clear (191-202)
  • stats (204-273)
  • get_header_height_by_hash (275-277)
  • get_headers_batch (279-297)
  • remove_utxo (313-325)
  • get_utxos_for_address (327-339)
  • store_sync_state (345-358)
  • load_sync_state (360-370)
  • clear_sync_state (372-377)
  • store_sync_checkpoint (379-392)
  • get_sync_checkpoints (394-419)
  • store_chain_lock (421-434)
  • load_chain_lock (436-446)
  • get_chain_locks (448-470)
  • store_instant_lock (472-485)
  • load_instant_lock (487-500)
  • store_mempool_transaction (503-510)
  • remove_mempool_transaction (512-515)
  • get_mempool_transaction (517-522)
  • get_all_mempool_transactions (524-528)
  • store_mempool_state (530-533)
  • load_mempool_state (535-537)
  • clear_mempool (539-543)
⏰ 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 (hashes_ripemd160)
  • GitHub Check: fuzz (hashes_sha512)
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (dash_outpoint_string)
  • GitHub Check: fuzz (dash_deserialize_amount)
🔇 Additional comments (9)
dash-spv/tests/error_handling_test.rs (9)

248-254: Filter header/load/tip mock behavior looks sound

Read/write error injection is consistent and the stubs are fine for tests that only care about error paths.

Also applies to: 255-261, 262-267, 269-274


357-365: Header index/batch stubs align with MemoryStorage semantics

Returning None and vec![] while honoring read failures matches expectations for error-path tests.

Also applies to: 367-376


406-447: Sync-state and checkpoint mocks: good coverage of error paths

Write/read error injection is consistent across store/load/clear and ranged getters. No issues spotted.


449-476: Chain/instant lock mocks look correct

APIs are stubbed coherently and honor read/write failure flags. LGTM.

Also applies to: 478-498


499-534: Mempool transaction/state mocks align with earlier feedback

With UnconfirmedTransaction now in scope, these look build-ready once the cfg gate is dropped.

Also applies to: 536-555


1147-1184: Concurrency test exercises read/write error paths under contention—nice coverage

Using RwLock with mixed read/write operations ensures both error modes are hit. Cleanly structured.


385-390: No signature mismatch: mock and trait both use StorageResult<()>
The StorageManager::remove_utxo method in dash-spv/src/storage/mod.rs is declared as

async fn remove_utxo(&mut self, outpoint: &OutPoint) -> StorageResult<()>;

which exactly matches the mock in dash-spv/tests/error_handling_test.rs. No changes are required.


392-397: Trait signature matches mock implementation
The mock’s get_utxos_for_address(&self, _address: &Address) -> StorageResult<Vec<Utxo>> exactly matches the trait declaration in dash-spv/src/storage/mod.rs:187. No changes needed.


241-246: Verify trait surface
I wasn’t able to locate the trait declaration automatically—please confirm that the trait your mock implements (e.g. in dash-spv/src/storage/mod.rs or wherever it’s declared) includes both of these methods:

  • fn get_tip_height(&self) → StorageResult<Option>;
  • fn get_filter_tip_height(&self) → StorageResult<Option>;

That way, when the cfg gate is removed the mock impl will still match the trait.

@QuantumExplorer QuantumExplorer merged commit 97e9e37 into dashpay:v0.40-dev Aug 16, 2025
16 of 17 checks passed
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.

2 participants