-
Notifications
You must be signed in to change notification settings - Fork 3
fix: coderabbit review fixes #104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: coderabbit review fixes #104
Conversation
WalkthroughSafer 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
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
Poem
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 unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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 lingeringGetMnListDvariants withGetMnListDiffin SPV codeTo ensure consistency across the SPV implementation, update all remaining uses of the old
NetworkMessage::GetMnListD(...)variant to the newNetworkMessage::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 1035Definitions 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 incorrectNetworkMessagevariant in masternodes syncThe
dashcore::network::message::NetworkMessageenum defines the request variant asGetMnListD, notGetMnListDiff. You must use the same variant name everywhere to compile.• File: dash-spv/src/sync/masternodes.rs
Location: around line 736Apply 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 reverseKeeps 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) formAsserting 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 pipefaildash-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 examplesYou’ve documented
get_header_height_by_hash(&BlockHash)here, but earlier “Create block height fetcher” examples still callstorage.get_header_height(block_hash)andself.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(...)toget_header_height_by_hash(...)for consistency?dash-spv/src/sync/sequential/phases.rs (2)
549-565: Good gating on QRInfo progress to prevent overshootThe guard
if *qr_info_completed < *qr_info_requestsand clampingrequests_completedvia.min(*requests_total)resolves overshoot and duplicate-accounting issues. Updatinglast_progressonly 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 retainedSame positive notes as above. Retaining
*diffs_processed += 1preserves 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 backpressureRequesting 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 improvementSkipping per-window based on
window.mining_startinstead 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 constantsThe 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 modesThe 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 testThis 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 handleAt 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: Usenext_back()to select the most recent quorum entrySince
quorumsis aBTreeMap<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 fromSyncResult<Option<…>>toSyncResult<(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 collisionsResolving 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 unknownUsing 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 quietblock_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 neededqr_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_timeoutsto 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.
📒 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.rsdash-spv/tests/chainlock_validation_test.rsdash-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 comparisonsMoving 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 thatBLSPublicKeyis a[u8; 48]newtype implementingAsRef<[u8; 48]>, and thatQUORUM_PUBKEY_SIZEis defined as48. The size check andstd::ptr::copy_nonoverlappingcall 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 starveThe 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 behaviorThe 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 APIImport change matches the new async constructor usage throughout the codebase. Looks good.
139-139: Async MemoryStorageManager creation is correct and resilientUsing .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 sitesRequired 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.
| 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(()) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| 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(()) | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this 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 moveSwitching 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 xpubRather 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.
📒 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 LGTMSwitching 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 correctP2PKH and P2SH are mapped as expected. Given the limited FFI enum, this is appropriate.
192-200: Expanded error mappings are consistentMapping CoinJoinNotEnabled, Serialization, and InvalidParameter to existing FFI error variants is reasonable for now and preserves the error surface.
456-458: Parse error wrapping LGTMParsing via dashcore::Address::from_str and returning AddressError preserves the original error context cleanly.
499-499: Only P2PKH generation from public key is supportedUsing 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.
…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.
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.
fe9e0bc to
9ec2793
Compare
There was a problem hiding this 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 traitThis 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 deterministicThe 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_keyThis 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 defaultsHardcoding 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 helperMany 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.
📒 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.rsdash-spv/src/sync/chainlock_validation.rsdash-spv/tests/smart_fetch_integration_test.rsdash-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.rsdash-spv/src/sync/chainlock_validation.rsdash-spv/tests/smart_fetch_integration_test.rsdash-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.rsdash-spv/tests/smart_fetch_integration_test.rsdash-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.rsdash-spv/tests/smart_fetch_integration_test.rsdash-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.rsdash-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.rsdash-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.rsdash-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 pathsSwitching 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 typeReturning 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 pathsThe 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 simulationReturns an empty set by default and properly surfaces read failures.
248-254: LGTM: filter headers APIs mirror header storage semanticsConsistent 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 shimsConsistent with the expanded trait surface and error paths.
Also applies to: 286-293
295-300: LGTM: chain state store/load coverageMatches expected semantics for exercising error propagation in tests.
Also applies to: 302-307
309-315: LGTM: filters store/load shimsError paths are consistent; default returns None for missing filters.
Also applies to: 316-321
323-328: LGTM: metadata store/loadThin wrappers with consistent error injection.
Also applies to: 330-335
337-342: LGTM: clear and statsClear uses write-failure guard. Stats returns a sensible zeroed struct for tests.
Also applies to: 344-355
357-365: LGTM: header lookup utilitiesHeight-by-hash and batch retrieval stubs behave as expected for error testing.
Also applies to: 367-376
392-397: LGTM: UTXO gettersReturn empty collections by default; surface read failures when toggled.
Also applies to: 399-404
406-411: LGTM: sync state and checkpoints APIsGood 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 APIsInterfaces for store/load/range retrieval look consistent with trait evolution.
Also applies to: 460-465, 467-476
478-487: LGTM: instant lock storage APIsMatches the pattern used for chain locks and mempool.
Also applies to: 489-497
510-515: LGTM: remove_mempool_transactionSimple write-failure guard is sufficient for exercising error paths.
536-541: LGTM: mempool state store/load/clearConsistent failure toggles and defaults.
Also applies to: 543-548, 550-555
| 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)) | ||
| } |
There was a problem hiding this comment.
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.
| 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>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this 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 compileProject 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 fallbackPanics 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 P2PKHDefaulting 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 itsFrom<ChainState>impl only map:
- header_height
- filter_header_height
- masternode_height
- last_chainlock_height
- last_chainlock_hash
- current_filter_tip
The new
ChainStatefieldssync_base_heightandsynced_from_checkpointare never exposed over FFI.
If you intend to surface checkpoint sync state through the FFI API:
- Add
sync_base_height: u32andsynced_from_checkpoint: booltoFFIChainState- Populate them in
impl From<ChainState> for FFIChainState- Add assertions in
test_type_conversions.rsfor those fieldsOtherwise, document in
FFIChainState(and itsFromimpl) 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 + istart + 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.
📒 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.tomlfuzz/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.rsdash-spv-ffi/tests/unit/test_type_conversions.rsdash-spv-ffi/tests/test_platform_integration_safety.rskey-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.rsdash-spv-ffi/tests/unit/test_type_conversions.rsdash-spv-ffi/tests/test_platform_integration_safety.rskey-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.rsdash-spv-ffi/tests/test_platform_integration_safety.rskey-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.rsdash-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.tomldash-spv-ffi/tests/unit/test_type_conversions.rsdash-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.rsdash-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.rsdash-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.rskey-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 transitionThe 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_handleUsing 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 typesThe 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 bridgingThe direct mapping of P2PKH/P2SH to KwAddressType is correct.
194-203: LGTM: broader kw::Error mapping improves diagnosabilityAdding CoinJoinNotEnabled, Serialization, and InvalidParameter mappings provides clearer error surfaces.
215-221: LGTM: dashcore::address::Error -> KeyWalletError::AddressError mappingThis ensures parsing failures are surfaced cleanly through FFI.
466-475: LGTM: require_network enforces network correctness for parsed addressesParsing as unchecked then requiring the expected network is the right safety guard for FFI inputs.
| connected_peers: 0, | ||
| total_peers: 0, | ||
| header_height: 0, | ||
| filter_height: 0, | ||
| }; |
There was a problem hiding this comment.
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.rsLength 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.
| 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.
| pub fn get_type(&self) -> AddressType { | ||
| self.inner.address_type.into() | ||
| self.inner.address_type().unwrap_or(KwAddressType::P2pkh).into() | ||
| } |
There was a problem hiding this comment.
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.
| 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>
There was a problem hiding this 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 unresolvedUnconfirmedTransactiontypeBrings
UnconfirmedTransactioninto 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 tostats()for consistencyMost 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; considerlock_poisonedsymmetry for readsThese methods correctly gate on write/read failures. Only
get_headercheckslock_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: Maketest_network_connection_failuredeterministic (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 surfaceThe 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
StorageManagermethods 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.
📒 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 soundRead/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 semanticsReturning
Noneandvec![]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 pathsWrite/read error injection is consistent across store/load/clear and ranged getters. No issues spotted.
449-476: Chain/instant lock mocks look correctAPIs are stubbed coherently and honor read/write failure flags. LGTM.
Also applies to: 478-498
499-534: Mempool transaction/state mocks align with earlier feedbackWith
UnconfirmedTransactionnow 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 coverageUsing RwLock with mixed read/write operations ensures both error modes are hit. Cleanly structured.
385-390: No signature mismatch: mock and trait both useStorageResult<()>
TheStorageManager::remove_utxomethod indash-spv/src/storage/mod.rsis declared asasync 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’sget_utxos_for_address(&self, _address: &Address) -> StorageResult<Vec<Utxo>>exactly matches the trait declaration indash-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.
Summary by CodeRabbit
New Features
Bug Fixes
Configuration
Chores