diff --git a/crates/vnidrop/src/approval.rs b/crates/vnidrop/src/approval.rs index 936c30a..8407e5e 100644 --- a/crates/vnidrop/src/approval.rs +++ b/crates/vnidrop/src/approval.rs @@ -183,7 +183,7 @@ impl ApprovalService { .contacts() .is_blocked(&remote_endpoint_id) .await - .unwrap_or(false) + .unwrap_or(true) { // Indistinguishable from other refusals so probing cannot detect blocks. return self diff --git a/crates/vnidrop/src/device_relationship/mod.rs b/crates/vnidrop/src/device_relationship/mod.rs index df1a1c2..7c20ace 100644 --- a/crates/vnidrop/src/device_relationship/mod.rs +++ b/crates/vnidrop/src/device_relationship/mod.rs @@ -129,10 +129,11 @@ impl DeviceRelationshipService { } pub(super) async fn is_blocked(&self, endpoint_id: &str) -> bool { + // Fail closed: a store error must not admit blocked traffic. self.contacts() .is_blocked(endpoint_id) .await - .unwrap_or(false) + .unwrap_or(true) } /// Drop orphaned relationship grant secrets and disable rows whose secrets are gone. diff --git a/crates/vnidrop/src/runtime/contacts.rs b/crates/vnidrop/src/runtime/contacts.rs index 35b7fd6..2b48c15 100644 --- a/crates/vnidrop/src/runtime/contacts.rs +++ b/crates/vnidrop/src/runtime/contacts.rs @@ -86,7 +86,7 @@ impl CoreInner { .contacts() .is_blocked(&peer_endpoint_id) .await - .unwrap_or(false) + .unwrap_or(true) { return Ok(false); } @@ -103,7 +103,7 @@ impl CoreInner { .device_relationships .forget(peer_endpoint_id.clone()) .await?; - // Targeted transfers for this relationship only (ticket 10 fills in). + // Targeted transfers for this relationship only. // Invitation-domain shares are deliberately not cancelled here. self.cancel_targeted_transfers_for_peer(&peer_endpoint_id) .await?; diff --git a/crates/vnidrop/src/runtime/targeted.rs b/crates/vnidrop/src/runtime/targeted.rs index 007ae6b..e2ebe86 100644 --- a/crates/vnidrop/src/runtime/targeted.rs +++ b/crates/vnidrop/src/runtime/targeted.rs @@ -62,6 +62,17 @@ impl CoreInner { .push(peer_endpoint_id.to_string()); } self.targeted_offers.discard_from(peer_endpoint_id).await; + let protocol_ids = self + .targeted_store() + .protocol_ids_for_peer(peer_endpoint_id) + .await?; + // Signal active transfers synchronously before awaiting share teardown. + for protocol_transfer_id in &protocol_ids { + let _ = self.take_active_transfer(*protocol_transfer_id); + } + for protocol_transfer_id in &protocol_ids { + let _ = self.cancel_idle_or_share(*protocol_transfer_id).await; + } self.targeted_store().cancel_by_peer(peer_endpoint_id).await } diff --git a/crates/vnidrop/src/targeted_transfer/inbox.rs b/crates/vnidrop/src/targeted_transfer/inbox.rs index dbed28a..2b4ab57 100644 --- a/crates/vnidrop/src/targeted_transfer/inbox.rs +++ b/crates/vnidrop/src/targeted_transfer/inbox.rs @@ -187,10 +187,6 @@ impl TargetedOfferInbox { } } - #[allow( - dead_code, - reason = "called via cancel_targeted_transfers_for_peer for ticket 09" - )] pub(crate) async fn discard_from(&self, endpoint_id: &str) { let ids: Vec = { let pending = self.pending.lock().await; diff --git a/crates/vnidrop/src/targeted_transfer/mod.rs b/crates/vnidrop/src/targeted_transfer/mod.rs index 59883e8..7732447 100644 --- a/crates/vnidrop/src/targeted_transfer/mod.rs +++ b/crates/vnidrop/src/targeted_transfer/mod.rs @@ -159,10 +159,6 @@ impl TargetedTransferStore { rows.into_iter().map(row_to_transfer).collect() } - #[allow( - dead_code, - reason = "called via cancel_targeted_transfers_for_peer for ticket 09" - )] pub(crate) async fn cancel_by_peer(&self, peer_endpoint_id: &str) -> Result { let now = now_ms(); let result = sqlx::query( @@ -180,6 +176,26 @@ impl TargetedTransferStore { .map_err(VnidropError::repository)?; Ok(result.rows_affected()) } + + pub(crate) async fn protocol_ids_for_peer( + &self, + peer_endpoint_id: &str, + ) -> Result, VnidropError> { + let rows = sqlx::query( + r#" + SELECT protocol_transfer_id FROM targeted_transfers + WHERE sender_endpoint_id = ?1 OR receiver_endpoint_id = ?1 + "#, + ) + .bind(peer_endpoint_id) + .fetch_all(&self.pool) + .await + .map_err(VnidropError::repository)?; + Ok(rows + .into_iter() + .map(|row| row.get::(0) as u64) + .collect()) + } } #[derive(Debug, Clone)]