-
Notifications
You must be signed in to change notification settings - Fork 8
refactor: Less cloning in SPV message handling #268
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
Conversation
We currently clone all network messages in `DashSpvClient::handle_network_message` when passed to `MessageHandler::handle_network_message`. Also some get cloned further down the track again. So this PR changes it to pass references.
WalkthroughThis PR refactors message and block handling throughout the sync pipeline to use borrowed references instead of owned values. Method signatures across multiple sync components (message handlers, block processors, filter managers) are updated to accept references, reducing unnecessary clones and allocations while preserving control flow logic. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
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
🧹 Nitpick comments (4)
dash-spv/src/client/sync_coordinator.rs (1)
607-669: Good: delegate with&NetworkMessage(but consider borrowing at the coordinator boundary too)Using
handler.handle_network_message(&message)removes the need to clone for routing. If the broader pipeline is moving to borrowed messages, it may be worth also changingDashSpvClient::handle_network_messageto accept&NetworkMessagefor consistency.dash-spv/src/sync/message_handlers.rs (2)
452-509: Leftover clone:QRInfois still cloned in the hot pathIf feasible, prefer updating
MasternodeSyncManager::handle_qrinfo_messageto take&QRInfo(and only clone the pieces you truly need to cache). That would remove the unconditionalqr_info.clone()here.
510-562: Leftover clone:CFHeadersis still cloned before entering filter sync
self.filter_sync.handle_cfheaders_message(cfheaders.clone(), ...)negates some of the benefit of borrowing upstream. Consider changingFilterSyncManager::handle_cfheaders_messageto accept&CFHeaders, and only clone when you must buffer/store out-of-order batches.dash-spv/src/client/message_handler.rs (1)
246-256: Avoidable clone: inventory is cloned just to callhandle_inventoryIf you want to squeeze more cloning out, change
handle_inventoryto accept&[Inventory](orimpl IntoIterator<Item=&Inventory>) and only allocate new vectors for theGetDatasubsets you actually send.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
dash-spv/src/client/message_handler.rs(12 hunks)dash-spv/src/client/sync_coordinator.rs(1 hunks)dash-spv/src/sync/filters/download.rs(4 hunks)dash-spv/src/sync/filters/headers.rs(1 hunks)dash-spv/src/sync/headers/manager.rs(5 hunks)dash-spv/src/sync/masternodes/manager.rs(1 hunks)dash-spv/src/sync/message_handlers.rs(10 hunks)dash-spv/src/sync/post_sync.rs(6 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.rs
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.rs: Never hardcode network parameters, addresses, or keys in Rust code
Use proper error types with thiserror crate and propagate errors appropriately in Rust code
Use tokio runtime for async operations in Rust code
Use conditional compilation with feature flags for optional features
Write unit tests for new functionality in Rust
Format Rust code with cargo fmt before committing
Ensure clippy passes with all warnings as errors on all Rust code
**/*.rs: Each crate keeps sources insrc/; unit tests live alongside code with#[cfg(test)]
Maintain MSRV of 1.89 for Rust code
Format Rust code withrustfmt(seerustfmt.toml); runcargo fmt --allbefore commits
Lint Rust code withclippy; avoidunwrap()/expect()in library code; use error types (e.g.,thiserror)
Usesnake_casefor function and variable names in Rust
UseUpperCamelCasefor types and traits in Rust
UseSCREAMING_SNAKE_CASEfor constants in Rust
Follow mixed editions (2021/2024) and crate-specific idioms; prefer async viatokiowhere applicable
Unit tests should be placed near code with descriptive names (e.g.,test_parse_address_mainnet)
Use#[ignore]for network-dependent or long-running tests; run with-- --ignoredflag
Never commit secrets or real keys; avoid logging sensitive data in Rust code
Keep test vectors deterministic in Rust test code
Files:
dash-spv/src/sync/filters/headers.rsdash-spv/src/client/sync_coordinator.rsdash-spv/src/sync/filters/download.rsdash-spv/src/sync/headers/manager.rsdash-spv/src/sync/masternodes/manager.rsdash-spv/src/sync/post_sync.rsdash-spv/src/sync/message_handlers.rsdash-spv/src/client/message_handler.rs
dash-spv/**/*.rs
📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)
dash-spv/**/*.rs: Use trait-based abstractions for core components likeNetworkManagerandStorageManagerto enable swappable implementations
Use async/await throughout the codebase with tokio runtime for all asynchronous operations
Use Minimum Rust Version (MSRV) 1.89 and ensure all code compiles withcargo check --all-features
Files:
dash-spv/src/sync/filters/headers.rsdash-spv/src/client/sync_coordinator.rsdash-spv/src/sync/filters/download.rsdash-spv/src/sync/headers/manager.rsdash-spv/src/sync/masternodes/manager.rsdash-spv/src/sync/post_sync.rsdash-spv/src/sync/message_handlers.rsdash-spv/src/client/message_handler.rs
dash-spv/src/sync/**/*.rs
📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)
Synchronization should use
SyncManagerfor phase-based coordination: Phase 1 (Headers), Phase 2 (Masternode List), Phase 3 (Filter Headers), Phase 4 (Filters), Phase 5 (Blocks)
Files:
dash-spv/src/sync/filters/headers.rsdash-spv/src/sync/filters/download.rsdash-spv/src/sync/headers/manager.rsdash-spv/src/sync/masternodes/manager.rsdash-spv/src/sync/post_sync.rsdash-spv/src/sync/message_handlers.rs
dash-spv/src/client/**/*.rs
📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)
Client module should provide high-level API through
DashSpvClientand configuration viaClientConfig
Files:
dash-spv/src/client/sync_coordinator.rsdash-spv/src/client/message_handler.rs
🧠 Learnings (20)
📓 Common learnings
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Applies to dash-spv/src/network/**/*.rs : Network module should handle TCP connections, handshake management, message routing, and peer management following Dash protocol implementation
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:02:42.390Z
Learning: Passing exclusive mutable references to network and storage managers into the sync manager in async Rust can lead to borrow checker issues or runtime contention if concurrent access is needed elsewhere. It's advisable to document this architectural tradeoff and consider refactoring to interior mutability or message passing for shared access as the codebase evolves.
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Applies to dash-spv/src/sync/**/*.rs : Synchronization should use `SyncManager` for phase-based coordination: Phase 1 (Headers), Phase 2 (Masternode List), Phase 3 (Filter Headers), Phase 4 (Filters), Phase 5 (Blocks)
📚 Learning: 2025-06-26T15:52:45.327Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:52:45.327Z
Learning: In Rust, calling `headers.last().unwrap()` or `headers[headers.len() - 1]` is only safe if the vector is guaranteed non-empty; placing such code outside a tightly-coupled empty check is fragile and risks panics if future refactoring bypasses the check. Prefer pattern matching or moving logic inside the non-empty branch for robustness.
Applied to files:
dash-spv/src/sync/filters/headers.rsdash-spv/src/sync/filters/download.rsdash-spv/src/sync/post_sync.rs
📚 Learning: 2025-12-04T15:51:59.632Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Storage should use segmented storage with headers stored in 10,000-header segments with index files, separate storage for filter headers and compact block filters
Applied to files:
dash-spv/src/sync/filters/headers.rsdash-spv/src/sync/filters/download.rsdash-spv/src/sync/post_sync.rs
📚 Learning: 2025-12-04T15:51:59.632Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Applies to dash-spv/src/network/**/*.rs : Network module should handle TCP connections, handshake management, message routing, and peer management following Dash protocol implementation
Applied to files:
dash-spv/src/client/sync_coordinator.rsdash-spv/src/sync/headers/manager.rsdash-spv/src/sync/masternodes/manager.rsdash-spv/src/sync/post_sync.rsdash-spv/src/sync/message_handlers.rsdash-spv/src/client/message_handler.rs
📚 Learning: 2025-12-04T15:51:59.632Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Applies to dash-spv/src/sync/**/*.rs : Synchronization should use `SyncManager` for phase-based coordination: Phase 1 (Headers), Phase 2 (Masternode List), Phase 3 (Filter Headers), Phase 4 (Filters), Phase 5 (Blocks)
Applied to files:
dash-spv/src/client/sync_coordinator.rsdash-spv/src/sync/headers/manager.rsdash-spv/src/sync/masternodes/manager.rsdash-spv/src/sync/post_sync.rsdash-spv/src/sync/message_handlers.rsdash-spv/src/client/message_handler.rs
📚 Learning: 2025-06-26T16:02:42.390Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:02:42.390Z
Learning: Passing exclusive mutable references to network and storage managers into the sync manager in async Rust can lead to borrow checker issues or runtime contention if concurrent access is needed elsewhere. It's advisable to document this architectural tradeoff and consider refactoring to interior mutability or message passing for shared access as the codebase evolves.
Applied to files:
dash-spv/src/client/sync_coordinator.rsdash-spv/src/sync/masternodes/manager.rsdash-spv/src/sync/post_sync.rsdash-spv/src/sync/message_handlers.rsdash-spv/src/client/message_handler.rs
📚 Learning: 2025-12-04T15:51:59.632Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Applies to dash-spv/**/*.rs : Use trait-based abstractions for core components like `NetworkManager` and `StorageManager` to enable swappable implementations
Applied to files:
dash-spv/src/client/sync_coordinator.rsdash-spv/src/sync/headers/manager.rsdash-spv/src/sync/masternodes/manager.rsdash-spv/src/sync/post_sync.rsdash-spv/src/sync/message_handlers.rsdash-spv/src/client/message_handler.rs
📚 Learning: 2025-12-01T08:01:00.652Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: swift-dash-core-sdk/Examples/DashHDWalletExample/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:01:00.652Z
Learning: Applies to swift-dash-core-sdk/Examples/DashHDWalletExample/**/*.swift : Use DashSDK's public methods, not direct SPVClient access, when accessing SPV functionality in Swift code
Applied to files:
dash-spv/src/client/sync_coordinator.rsdash-spv/src/client/message_handler.rs
📚 Learning: 2025-12-04T15:51:59.632Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Applies to dash-spv/src/client/**/*.rs : Client module should provide high-level API through `DashSpvClient` and configuration via `ClientConfig`
Applied to files:
dash-spv/src/client/sync_coordinator.rsdash-spv/src/sync/headers/manager.rsdash-spv/src/client/message_handler.rs
📚 Learning: 2025-12-01T08:00:50.618Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: swift-dash-core-sdk/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:50.618Z
Learning: Applies to swift-dash-core-sdk/**/*.rs : Implement new FFI functions in Rust with `#[no_mangle] extern "C"` annotation in dash-spv-ffi
Applied to files:
dash-spv/src/client/sync_coordinator.rsdash-spv/src/sync/headers/manager.rsdash-spv/src/sync/post_sync.rsdash-spv/src/client/message_handler.rs
📚 Learning: 2025-12-01T08:00:50.618Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: swift-dash-core-sdk/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:50.618Z
Learning: Run `./sync-headers.sh` to copy updated headers to Swift SDK after rebuilding dash-spv-ffi
Applied to files:
dash-spv/src/sync/headers/manager.rsdash-spv/src/sync/post_sync.rs
📚 Learning: 2025-12-04T15:51:59.632Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Applies to dash-spv/**/*.rs : Use Minimum Rust Version (MSRV) 1.89 and ensure all code compiles with `cargo check --all-features`
Applied to files:
dash-spv/src/sync/headers/manager.rsdash-spv/src/client/message_handler.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Rust strings must be returned as `*const c_char` with caller responsibility to free using `dash_string_free`
Applied to files:
dash-spv/src/sync/headers/manager.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Input strings in FFI functions are `*const c_char` (borrowed, not freed by C caller)
Applied to files:
dash-spv/src/sync/headers/manager.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Use `#[no_mangle] extern "C"` attribute when implementing new FFI functions in Rust
Applied to files:
dash-spv/src/sync/headers/manager.rs
📚 Learning: 2025-06-26T15:54:02.509Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:54:02.509Z
Learning: The `StorageManager` trait in `dash-spv/src/storage/mod.rs` uses `&mut self` methods but is also `Send + Sync`, and implementations often use interior mutability for concurrency. This can be confusing, so explicit documentation should clarify thread-safety expectations and the rationale for the API design.
Applied to files:
dash-spv/src/sync/masternodes/manager.rsdash-spv/src/sync/post_sync.rsdash-spv/src/sync/message_handlers.rsdash-spv/src/client/message_handler.rs
📚 Learning: 2025-12-04T15:51:59.632Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Applies to dash-spv/**/*.rs : Use async/await throughout the codebase with tokio runtime for all asynchronous operations
Applied to files:
dash-spv/src/sync/post_sync.rs
📚 Learning: 2025-12-04T15:51:59.632Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-04T15:51:59.632Z
Learning: Applies to dash-spv/src/error.rs : Maintain domain-specific error types in `error.rs` with variants for `NetworkError`, `StorageError`, `SyncError`, `ValidationError`, and `SpvError` (top-level wrapper)
Applied to files:
dash-spv/src/sync/message_handlers.rsdash-spv/src/client/message_handler.rs
📚 Learning: 2025-12-01T08:00:37.919Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-01T08:00:37.919Z
Learning: Applies to key-wallet/**/transaction_checking/**/*.rs : In SPV sync integration, use `wallet_info.check_transaction()` with appropriate `TransactionContext` and update wallet state atomically only when `is_relevant` flag is true
Applied to files:
dash-spv/src/sync/message_handlers.rs
📚 Learning: 2025-12-01T08:01:18.174Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-01T08:01:18.174Z
Learning: Organize workspace with crates: `dash`, `hashes`, `internals`, `dash-network`, `dash-spv`, `key-wallet`, `rpc-*`, utilities (`fuzz`, `test-utils`), and FFI crates (`*-ffi`)
Applied to files:
dash-spv/src/client/message_handler.rs
🧬 Code graph analysis (3)
dash-spv/src/sync/masternodes/manager.rs (3)
dash/src/sml/masternode_list/from_diff.rs (1)
diff(90-94)dash-spv/src/sync/headers/manager.rs (1)
storage(261-261)dash-spv/src/client/core.rs (1)
storage(169-171)
dash-spv/src/sync/message_handlers.rs (5)
dash-spv-ffi/src/broadcast.rs (1)
dashcore(29-29)dash-spv/src/client/core.rs (3)
network(164-166)storage(169-171)wallet(159-161)dash-spv/src/client/sync_coordinator.rs (1)
headers(948-948)dash-spv/src/sync/headers/manager.rs (1)
storage(261-261)dash/src/sml/masternode_list/from_diff.rs (1)
diff(90-94)
dash-spv/src/client/message_handler.rs (1)
dash-spv/src/client/sync_coordinator.rs (1)
process_new_block(673-693)
⏰ 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). (16)
- GitHub Check: RPC Tests (stable, true)
- GitHub Check: fuzz (hashes_sha512)
- GitHub Check: fuzz (hashes_cbor)
- GitHub Check: fuzz (hashes_sha1)
- GitHub Check: fuzz (dash_outpoint_string)
- GitHub Check: fuzz (hashes_sha512_256)
- GitHub Check: fuzz (hashes_json)
- GitHub Check: fuzz (hashes_sha256)
- GitHub Check: fuzz (dash_deserialize_script)
- GitHub Check: fuzz (dash_deserialize_witness)
- GitHub Check: fuzz (dash_deserialize_block)
- GitHub Check: fuzz (dash_deserialize_address)
- GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
- GitHub Check: fuzz (dash_deser_net_msg)
- GitHub Check: Pre-commit (ubuntu-latest)
- GitHub Check: Pre-commit (macos-latest)
🔇 Additional comments (12)
dash-spv/src/sync/filters/headers.rs (1)
655-685: Good: borrowed CFHeaders when storingPassing
&cf_headersintostore_filter_headersis the right direction and should prevent unnecessary cloning at this call site.dash-spv/src/sync/filters/download.rs (1)
247-394: Nice:store_filter_headersnow borrowsCFHeadersend-to-endThis is consistent with the “less cloning” goal and keeps ownership local to the few places that actually need allocation (computed header vectors, etc.).
dash-spv/src/sync/masternodes/manager.rs (1)
443-501: Good:MnListDiffis now borrowed in the handlerThis matches the PR objective (avoid ownership churn). The internal cache clone remains reasonable given it stores the diff.
dash-spv/src/sync/headers/manager.rs (2)
139-273: Slice-basedheadersplumbing looks right; verify storage trait/impls matchMoving to
headers: &[BlockHeader]is aligned with the refactor and simplifies call sites. Please double-check that allStorageManager::store_headersimplementations (andDiskStorageManager::store_headers) accept&[Header]consistently.
451-558: Good:Headers2Messagenow borrowed throughout the handler pathThe borrowed signature avoids unnecessary ownership transfers and keeps the decompression-disabled behavior unchanged.
dash-spv/src/sync/post_sync.rs (3)
119-155: Good: post-sync headers now use slices and store via&[Header]
238-464: Good: post-sync CF payload handlers now borrow (&CFHeaders,&CFilter)
467-510: Handle undefined method calls and clarify height conversion semanticsThe code calls
self.header_sync.is_synced_from_checkpoint()andself.header_sync.get_sync_base_height()but these methods are not defined in the codebase. Additionally, determine whetherMasternodeState::last_heightis persisted as an absolute blockchain height or as a value relative to a checkpoint. If it's absolute (persisted fromtip_height), then the conversion logicsync_base + mn_state.last_heightwill incorrectly double-add thesync_baseoffset and should be:- let mn_blockchain_height = if is_ckpt && sync_base > 0 { - sync_base + mn_state.last_height - } else { - mn_state.last_height - }; + let mn_blockchain_height = mn_state.last_height;If
last_heightis stored relative to a checkpoint, then the persistence site should use clearly-documented "relative" height semantics and validate the conversion againstget_header_height_by_hash()which returns absolute blockchain heights.dash-spv/src/sync/message_handlers.rs (2)
25-193: Good:handle_messagenow borrows&NetworkMessageand routes borrowed payloads
747-812: Good:handle_block_messagenow processes borrowed&Blockdash-spv/src/client/message_handler.rs (2)
60-199: Good: client message handling now borrows&NetworkMessagewhen dispatching
513-534: Reasonable:process_new_block(&Block)clones once for async task ownershipThe clone is justified if the background processor needs
'staticownership; looks like the right tradeoff here.
We currently clone all network messages in
DashSpvClient::handle_network_messagewhen passed toMessageHandler::handle_network_message. Then also some get cloned again further down the track. This PR changes it to pass references where possible and only clone whats needed .Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.