Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9375a66
fix(spv): exit manager task loop on network errors and signal shutdow…
lklimek Feb 16, 2026
d8bc066
fix(dash-spv): shutdown token not checked when waiting for peer conne…
lklimek Feb 16, 2026
cdccab6
fix(spv): address review findings for network error handling
lklimek Feb 16, 2026
0a6dff1
refactor(spv): replace signal_shutdown with full shutdown in stop()
lklimek Feb 16, 2026
9f3970c
fix(spv): resolve deadlock in PeerNetworkManager shutdown
lklimek Feb 16, 2026
86ebbab
doc: document logging
lklimek Feb 16, 2026
85cf2bd
feat(spv): surface FatalNetwork errors to API consumers
lklimek Feb 16, 2026
8541836
fix(spv): self-recover on FatalNetwork instead of exiting manager loop
lklimek Feb 16, 2026
cb84ccc
fix(spv): remove take() in PeerNetworkManager::shutdown to close race
lklimek Feb 16, 2026
f4a52dd
fix(spv): add missing shutdown checks in maintenance loop
lklimek Feb 16, 2026
7356e2b
fix(spv): address PR #440 audit findings
lklimek Feb 16, 2026
f48a3a5
fmt
lklimek Feb 16, 2026
04a87a7
Merge remote-tracking branch 'origin/v0.42-dev' into fix/block-header…
lklimek Feb 17, 2026
8e22175
refactor(spv): extract network error cooldown into named constant
lklimek Feb 17, 2026
8872062
fmt
lklimek Feb 17, 2026
c3fa668
fix(spv): restore shutdown checks dropped during upstream refactor
lklimek Feb 17, 2026
ddc539f
fmt
lklimek Feb 17, 2026
531eb11
revert: CLAUDE.md logging info
lklimek Feb 17, 2026
6affdaa
revert logging changes
lklimek Feb 17, 2026
ebdfbe3
Update dash-spv/src/network/manager.rs
lklimek Feb 17, 2026
e6c01e8
chore: fmt
lklimek Feb 17, 2026
d3d0305
fix: don't set SyncState::WaitingForConnections) on network error
lklimek Feb 17, 2026
b239c4e
refactor: remove cooldown
lklimek Feb 17, 2026
9501aed
Merge branch 'v0.42-dev' into fix/block-header-error-retry-loop
lklimek Feb 18, 2026
8f7eacc
refactor: strip non-shutdown changes from PR 440
lklimek Feb 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions dash-spv/src/client/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
}
}

// Shut down sync coordinator: signals cancellation and waits for manager
// tasks to drain before we tear down the network and storage layers.
if let Err(e) = self.sync_coordinator.shutdown().await {
log::warn!("Error shutting down sync coordinator: {}", e);
}

// Disconnect from network
self.network.disconnect().await?;

Expand Down
29 changes: 25 additions & 4 deletions dash-spv/src/network/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,27 @@ impl PeerNetworkManager {
let message_dispatcher = self.message_dispatcher.clone();
let network_event_sender = self.network_event_sender.clone();

// Spawn connection task
let mut tasks = self.tasks.lock().await;
// Spawn connection task — use select to avoid blocking on the lock during shutdown
let mut tasks = tokio::select! {
guard = self.tasks.lock() => guard,
_ = self.shutdown_token.cancelled() => {
self.pool.remove_peer(&addr).await;
return;
}
};
tasks.spawn(async move {
log::debug!("Attempting to connect to {}", addr);

match Peer::connect(addr, CONNECTION_TIMEOUT.as_secs(), network).await {
let connect_result = tokio::select! {
result = Peer::connect(addr, CONNECTION_TIMEOUT.as_secs(), network) => result,
_ = shutdown_token.cancelled() => {
log::debug!("Connection to {} cancelled by shutdown", addr);
pool.remove_peer(&addr).await;
return;
}
};

match connect_result {
Ok(mut peer) => {
// Perform handshake
let mut handshake_manager =
Expand Down Expand Up @@ -841,6 +856,10 @@ impl PeerNetworkManager {
}
}

if self.shutdown_token.is_cancelled() {
return;
}

// Send ping to all peers if needed
for (addr, peer) in self.pool.get_all_peers().await {
let mut peer_guard = peer.write().await;
Expand Down Expand Up @@ -1295,7 +1314,9 @@ impl PeerNetworkManager {
log::warn!("Failed to save reputation data on shutdown: {}", e);
}

// Wait for tasks to complete
// Drain tasks while holding the lock. connect_to_peer() already uses
// `select!` with the cancellation token when acquiring this lock, so no
// deadlock can occur once the shutdown token is cancelled above.
let mut tasks = self.tasks.lock().await;
while let Some(result) = tasks.join_next().await {
if let Err(e) = result {
Expand Down
Loading