1use async_trait::async_trait;
2
3use crate::dht::types::Chord;
4use crate::dht::PeerRingAction;
5use crate::dht::TopoInfo;
6use crate::error::Error;
7use crate::error::Result;
8use crate::message::effects::CoreEffect;
9use crate::message::types::ConnectNodeReport;
10use crate::message::types::ConnectNodeSend;
11use crate::message::types::FindSuccessorReport;
12use crate::message::types::FindSuccessorSend;
13use crate::message::types::Message;
14use crate::message::types::PeerLivenessProbe;
15use crate::message::types::PeerLivenessReport;
16use crate::message::types::QueryForTopoInfoReport;
17use crate::message::types::QueryForTopoInfoSend;
18use crate::message::types::Then;
19use crate::message::FindSuccessorReportHandler;
20use crate::message::FindSuccessorThen;
21use crate::message::HandleMsg;
22use crate::message::MessageHandler;
23use crate::message::MessagePayload;
24
25mod topology_view;
26
27#[cfg(all(test, not(target_family = "wasm")))]
28use topology_view::confirmed_topology;
29use topology_view::connect_successor_hint;
30#[cfg(all(test, not(target_family = "wasm")))]
31use topology_view::topology_has_confirmed_peer;
32
33#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
35#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
36impl HandleMsg<PeerLivenessProbe> for MessageHandler {
37 async fn handle(&self, ctx: &MessagePayload, msg: &PeerLivenessProbe) -> Result<()> {
38 if ctx.should_forward_from(self.dht.did) {
39 return self
40 .run_effects([CoreEffect::forward_payload(ctx, None)])
41 .await;
42 }
43
44 self.run_effects([CoreEffect::send_report_message(
45 ctx,
46 Message::PeerLivenessReport(msg.resp()),
47 )])
48 .await
49 }
50}
51
52#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
54#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
55impl HandleMsg<PeerLivenessReport> for MessageHandler {
56 async fn handle(&self, _ctx: &MessagePayload, _msg: &PeerLivenessReport) -> Result<()> {
57 Ok(())
58 }
59}
60
61#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
63#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
64impl HandleMsg<QueryForTopoInfoSend> for MessageHandler {
65 async fn handle(&self, ctx: &MessagePayload, msg: &QueryForTopoInfoSend) -> Result<()> {
66 let info: TopoInfo = TopoInfo::try_from(self.dht.as_ref())?;
67 if msg.targets(self.dht.did) {
68 self.run_effects([CoreEffect::send_report_message(
69 ctx,
70 Message::QueryForTopoInfoReport(msg.resp(info)),
71 )])
72 .await?
73 }
74 Ok(())
75 }
76}
77
78#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
80#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
81impl HandleMsg<QueryForTopoInfoReport> for MessageHandler {
82 async fn handle(&self, _ctx: &MessagePayload, msg: &QueryForTopoInfoReport) -> Result<()> {
83 match msg.then {
84 <QueryForTopoInfoReport as Then>::Then::SyncSuccessor => {
85 let successors = msg.info.successors.clone();
86 self.connect_dht_peers(successors.iter().copied()).await?;
87 for peer in successors {
88 if self.transport.get_connection(peer).is_some() {
89 self.join_dht(peer).await?;
90 }
91 }
92 }
93 <QueryForTopoInfoReport as Then>::Then::Stabilization => {
94 let candidates = msg
97 .info
98 .predecessor
99 .into_iter()
100 .chain(msg.info.successors.iter().copied());
101 self.connect_dht_peers(candidates).await?;
102
103 if let Some(ev) = self.transport.stabilize_routable_topology(&msg.info)? {
104 self.handle_dht_events(&ev).await?;
105 }
106 }
107 }
108 Ok(())
109 }
110}
111
112#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
113#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
114impl HandleMsg<ConnectNodeSend> for MessageHandler {
115 async fn handle(&self, ctx: &MessagePayload, msg: &ConnectNodeSend) -> Result<()> {
116 if !self.transport.accepts_connection_offer(msg) {
117 tracing::warn!(
118 local = %self.dht.did,
119 tx_id = %ctx.transaction.tx_id,
120 origin = ?ctx.relay.try_origin_sender().ok(),
121 relay_destination = %ctx.relay.destination,
122 transaction_destination = %ctx.transaction.destination,
123 mode = ?msg.dht_protocol_mode(),
124 "CONNECT_NODE offer rejected by DHT protocol mismatch"
125 );
126 return Ok(());
127 }
128
129 if ctx.should_forward_from(self.dht.did) {
130 tracing::trace!(
131 local = %self.dht.did,
132 tx_id = %ctx.transaction.tx_id,
133 origin = ?ctx.relay.try_origin_sender().ok(),
134 next_hop = %ctx.relay.next_hop,
135 relay_destination = %ctx.relay.destination,
136 transaction_destination = %ctx.transaction.destination,
137 sdp_bytes = msg.sdp.len(),
138 "CONNECT_NODE offer forward"
139 );
140 self.run_effects([CoreEffect::forward_payload(ctx, None)])
141 .await
142 } else {
143 let peer = ctx.relay.try_origin_sender()?;
144 tracing::trace!(
145 local = %self.dht.did,
146 peer = %peer,
147 tx_id = %ctx.transaction.tx_id,
148 sdp_bytes = msg.sdp.len(),
149 "CONNECT_NODE offer answer start"
150 );
151 let answer = match self
152 .transport
153 .answer_remote_connection(peer, self.inner_callback(), msg)
154 .await
155 {
156 Ok(answer) => {
157 tracing::trace!(
158 local = %self.dht.did,
159 peer = %peer,
160 tx_id = %ctx.transaction.tx_id,
161 sdp_bytes = answer.sdp.len(),
162 "CONNECT_NODE offer answer complete"
163 );
164 answer
165 }
166 Err(error) => {
167 tracing::warn!(
168 local = %self.dht.did,
169 peer = %peer,
170 tx_id = %ctx.transaction.tx_id,
171 error = ?error,
172 "CONNECT_NODE offer answer failed"
173 );
174 return Err(error);
175 }
176 };
177 self.run_effects([CoreEffect::send_report_message(
178 ctx,
179 Message::ConnectNodeReport(answer),
180 )])
181 .await
182 }
183 }
184}
185
186#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
187#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
188impl HandleMsg<ConnectNodeReport> for MessageHandler {
189 async fn handle(&self, ctx: &MessagePayload, msg: &ConnectNodeReport) -> Result<()> {
190 if ctx.should_forward_from(self.dht.did) {
191 tracing::trace!(
192 local = %self.dht.did,
193 tx_id = %ctx.transaction.tx_id,
194 origin = ?ctx.relay.try_origin_sender().ok(),
195 next_hop = %ctx.relay.next_hop,
196 relay_destination = %ctx.relay.destination,
197 transaction_destination = %ctx.transaction.destination,
198 sdp_bytes = msg.sdp.len(),
199 "CONNECT_NODE answer forward"
200 );
201 self.run_effects([CoreEffect::forward_payload(ctx, None)])
202 .await
203 } else {
204 let peer = ctx.relay.try_origin_sender()?;
205 tracing::trace!(
206 local = %self.dht.did,
207 peer = %peer,
208 tx_id = %ctx.transaction.tx_id,
209 sdp_bytes = msg.sdp.len(),
210 "CONNECT_NODE answer accept start"
211 );
212 match self.transport.accept_remote_connection(peer, msg).await {
213 Ok(()) => {
214 tracing::trace!(
215 local = %self.dht.did,
216 peer = %peer,
217 tx_id = %ctx.transaction.tx_id,
218 "CONNECT_NODE answer accept complete"
219 );
220 Ok(())
221 }
222 Err(error) => {
223 tracing::warn!(
224 local = %self.dht.did,
225 peer = %peer,
226 tx_id = %ctx.transaction.tx_id,
227 error = ?error,
228 "CONNECT_NODE answer accept failed"
229 );
230 Err(error)
231 }
232 }
233 }
234 }
235}
236
237#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
238#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
239impl HandleMsg<FindSuccessorSend> for MessageHandler {
240 async fn handle(&self, ctx: &MessagePayload, msg: &FindSuccessorSend) -> Result<()> {
241 match self.dht.find_successor(msg.did)? {
242 PeerRingAction::Some(did) => {
243 if msg.accepts_local_successor(self.dht.did) {
244 match &msg.then {
245 FindSuccessorThen::Report(handler) => {
246 let did = match handler {
247 FindSuccessorReportHandler::Connect => connect_successor_hint(
248 self.dht.as_ref(),
249 ctx.relay.try_origin_sender()?,
250 did,
251 )?,
252 _ => did,
253 };
254 self.run_effects([CoreEffect::send_report_message(
255 ctx,
256 Message::FindSuccessorReport(FindSuccessorReport {
257 did,
258 handler: handler.clone(),
259 }),
260 )])
261 .await
262 }
263 }
264 } else {
265 self.run_effects([CoreEffect::forward_payload(ctx, Some(did))])
266 .await
267 }
268 }
269 PeerRingAction::RemoteAction(next, _) => {
270 self.run_effects([CoreEffect::reset_destination(ctx, next)])
271 .await
272 }
273 act => Err(Error::unexpected_peer_ring_action(act)),
274 }
275 }
276}
277
278#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
279#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
280impl HandleMsg<FindSuccessorReport> for MessageHandler {
281 async fn handle(&self, ctx: &MessagePayload, msg: &FindSuccessorReport) -> Result<()> {
282 if ctx.should_forward_from(self.dht.did) {
283 return self
284 .run_effects([CoreEffect::forward_payload(ctx, None)])
285 .await;
286 }
287
288 match &msg.handler {
289 FindSuccessorReportHandler::FixFingerTable { index } => {
290 let disposition = self.transport.record_finger_candidate(msg.did, *index)?;
291 if disposition.needs_connection() && msg.reports_remote_successor(self.dht.did) {
292 self.connect_dht_peer(msg.did).await?;
293 let _ = self.transport.record_finger_candidate(msg.did, *index)?;
294 }
295 }
296 FindSuccessorReportHandler::Connect if msg.reports_remote_successor(self.dht.did) => {
297 self.connect_dht_peer(msg.did).await?;
298 }
299 _ => {}
300 }
301
302 Ok(())
303 }
304}
305
306#[cfg(not(all(feature = "wasm", target_family = "wasm")))]
307#[cfg(test)]
308pub mod tests {
309 use rings_transport::core::transport::WebrtcConnectionState;
311 use tokio::time::sleep;
312 use tokio::time::Duration;
313
314 use super::*;
315 use crate::dht::successor::SuccessorReader;
316 use crate::ecc::tests::gen_ordered_keys;
317 use crate::ecc::SecretKey;
318 use crate::tests::default::assert_no_more_msg;
319 use crate::tests::default::gen_pure_dht;
320 use crate::tests::default::prepare_node;
321 use crate::tests::default::wait_for_connection_state;
322 use crate::tests::default::wait_for_msgs;
323 use crate::tests::default::wait_for_successor;
324 use crate::tests::default::Node;
325 use crate::tests::manually_establish_connection;
326
327 #[test]
328 fn test_topology_report_keeps_only_confirmed_peers() {
329 let active = SecretKey::random().address().into();
330 let pending_successor = SecretKey::random().address().into();
331 let pending_predecessor = SecretKey::random().address().into();
332 let confirmed = confirmed_topology(
333 &TopoInfo {
334 successors: vec![active, pending_successor],
335 predecessor: Some(pending_predecessor),
336 },
337 |peer| peer == active,
338 );
339
340 assert_eq!(confirmed.successors, vec![active]);
341 assert_eq!(confirmed.predecessor, None);
342 assert!(topology_has_confirmed_peer(&confirmed));
343 }
344
345 #[test]
346 fn test_connect_successor_hint_skips_requester_self_report() -> Result<()> {
347 let keys = gen_ordered_keys(4);
348 let local = keys[0].address().into();
349 let requester = keys[1].address().into();
350 let next = keys[2].address().into();
351 let tail = keys[3].address().into();
352 let dht = gen_pure_dht(local);
353
354 dht.join(next)?;
355 dht.join(tail)?;
356 dht.join(requester)?;
357
358 assert_eq!(dht.successors().list()?, vec![requester, next, tail]);
359 assert_eq!(connect_successor_hint(&dht, requester, requester)?, next);
360 Ok(())
361 }
362
363 #[tokio::test]
364 async fn test_sync_successor_report_connects_advertised_successor() -> Result<()> {
365 let keys = gen_ordered_keys(3);
366 let node1 = prepare_node(keys[0]).await;
367 let node2 = prepare_node(keys[1]).await;
368 let node3 = prepare_node(keys[2]).await;
369
370 manually_establish_connection(&node1.swarm, &node2.swarm).await;
371 wait_for_msgs([&node1, &node2, &node3]).await;
372 manually_establish_connection(&node2.swarm, &node3.swarm).await;
373 wait_for_msgs([&node1, &node2, &node3]).await;
374
375 if node1.swarm.transport.get_connection(node3.did()).is_some() {
376 node1.swarm.disconnect(node3.did()).await?;
377 wait_for_msgs([&node1, &node2, &node3]).await;
378 }
379 assert!(node1.swarm.transport.get_connection(node3.did()).is_none());
380 assert!(!node1.dht().successors().contains(&node3.did())?);
381
382 node2
383 .swarm
384 .send_direct_message(
385 Message::QueryForTopoInfoReport(QueryForTopoInfoReport {
386 info: TopoInfo {
387 successors: vec![node3.did()],
388 predecessor: None,
389 },
390 then: <QueryForTopoInfoReport as Then>::Then::SyncSuccessor,
391 }),
392 node1.did(),
393 )
394 .await?;
395
396 wait_for_connection_state(&node1, node3.did(), WebrtcConnectionState::Connected).await?;
397 wait_for_successor(&node1, node3.did()).await?;
398 wait_for_msgs([&node1, &node2, &node3]).await;
399 assert_no_more_msg([&node1, &node2, &node3]).await;
400 Ok(())
401 }
402
403 #[tokio::test]
445 async fn test_triple_nodes_connection_1_2_3() -> Result<()> {
446 let keys = gen_ordered_keys(3);
447 let (key1, key2, key3) = (keys[0], keys[1], keys[2]);
448 test_triple_ordered_nodes_connection(key1, key2, key3).await?;
449 Ok(())
450 }
451
452 #[tokio::test]
454 async fn test_triple_nodes_connection_2_3_1() -> Result<()> {
455 let keys = gen_ordered_keys(3);
456 let (key1, key2, key3) = (keys[0], keys[1], keys[2]);
457 test_triple_ordered_nodes_connection(key2, key3, key1).await?;
458 Ok(())
459 }
460
461 #[tokio::test]
463 async fn test_triple_nodes_connection_3_1_2() -> Result<()> {
464 let keys = gen_ordered_keys(3);
465 let (key1, key2, key3) = (keys[0], keys[1], keys[2]);
466 test_triple_ordered_nodes_connection(key3, key1, key2).await?;
467 Ok(())
468 }
469
470 #[tokio::test]
490 async fn test_triple_nodes_connection_3_2_1() -> Result<()> {
491 let keys = gen_ordered_keys(3);
492 let (key1, key2, key3) = (keys[0], keys[1], keys[2]);
493 test_triple_desc_ordered_nodes_connection(key3, key2, key1).await?;
494 Ok(())
495 }
496
497 #[tokio::test]
499 async fn test_triple_nodes_connection_2_1_3() -> Result<()> {
500 let keys = gen_ordered_keys(3);
501 let (key1, key2, key3) = (keys[0], keys[1], keys[2]);
502 test_triple_desc_ordered_nodes_connection(key2, key1, key3).await?;
503 Ok(())
504 }
505
506 #[tokio::test]
508 async fn test_triple_nodes_connection_1_3_2() -> Result<()> {
509 let keys = gen_ordered_keys(3);
510 let (key1, key2, key3) = (keys[0], keys[1], keys[2]);
511 test_triple_desc_ordered_nodes_connection(key1, key3, key2).await?;
512 Ok(())
513 }
514
515 async fn test_triple_ordered_nodes_connection(
516 key1: SecretKey,
517 key2: SecretKey,
518 key3: SecretKey,
519 ) -> Result<(Node, Node, Node)> {
520 let node1 = prepare_node(key1).await;
521 let node2 = prepare_node(key2).await;
522 let node3 = prepare_node(key3).await;
523
524 println!("========================================");
525 println!("|| now we connect node1 and node2 ||");
526 println!("========================================");
527
528 manually_establish_connection(&node1.swarm, &node2.swarm).await;
529 wait_for_msgs([&node1, &node2, &node3]).await;
530 assert_no_more_msg([&node1, &node2, &node3]).await;
531
532 node1.assert_transports(vec![node2.did()]);
533 node2.assert_transports(vec![node1.did()]);
534 node3.assert_transports(vec![]);
535 assert_eq!(node1.dht().successors().list()?, vec![node2.did()]);
536 assert_eq!(node2.dht().successors().list()?, vec![node1.did()]);
537 assert_eq!(node3.dht().successors().list()?, vec![]);
538
539 println!("========================================");
540 println!("|| now we start join node3 to node2 ||");
541 println!("========================================");
542
543 manually_establish_connection(&node3.swarm, &node2.swarm).await;
544 wait_for_msgs([&node1, &node2, &node3]).await;
545 assert_no_more_msg([&node1, &node2, &node3]).await;
546
547 println!("=== Check state before connect via DHT ===");
548 if node1.swarm.transport.get_connection(node3.did()).is_some() {
549 node1.assert_transports(vec![node2.did(), node3.did()]);
550 node2.assert_transports(vec![node1.did(), node3.did()]);
551 node3.assert_transports(vec![node1.did(), node2.did()]);
552 assert_eq!(node1.dht().successors().list()?, vec![
553 node2.did(),
554 node3.did()
555 ]);
556 assert_eq!(node2.dht().successors().list()?, vec![
557 node3.did(),
558 node1.did()
559 ]);
560 assert_eq!(node3.dht().successors().list()?, vec![
561 node1.did(),
562 node2.did()
563 ]);
564 } else {
565 node1.assert_transports(vec![node2.did()]);
566 node2.assert_transports(vec![node1.did(), node3.did()]);
567 node3.assert_transports(vec![node2.did()]);
568 assert_eq!(node1.dht().successors().list()?, vec![node2.did(),]);
569 assert_eq!(node2.dht().successors().list()?, vec![
570 node3.did(),
571 node1.did()
572 ]);
573 assert_eq!(node3.dht().successors().list()?, vec![node2.did()]);
574 }
575
576 println!("=============================================");
577 println!("|| now we connect node1 to node3 via DHT ||");
578 println!("=============================================");
579
580 if node1.swarm.transport.get_connection(node3.did()).is_none() {
581 node1.swarm.connect(node3.did()).await?;
582 }
583 wait_for_msgs([&node1, &node2, &node3]).await;
584 assert_no_more_msg([&node1, &node2, &node3]).await;
585
586 println!("=== Check state after connect via DHT ===");
587 node1.assert_transports(vec![node2.did(), node3.did()]);
588 node2.assert_transports(vec![node1.did(), node3.did()]);
589 node3.assert_transports(vec![node1.did(), node2.did()]);
590 assert_eq!(node1.dht().successors().list()?, vec![
591 node2.did(),
592 node3.did()
593 ]);
594 assert_eq!(node2.dht().successors().list()?, vec![
595 node3.did(),
596 node1.did()
597 ]);
598 assert_eq!(node3.dht().successors().list()?, vec![
599 node1.did(),
600 node2.did()
601 ]);
602
603 Ok((node1, node2, node3))
604 }
605
606 async fn test_triple_desc_ordered_nodes_connection(
607 key1: SecretKey,
608 key2: SecretKey,
609 key3: SecretKey,
610 ) -> Result<(Node, Node, Node)> {
611 let node1 = prepare_node(key1).await;
612 let node2 = prepare_node(key2).await;
613 let node3 = prepare_node(key3).await;
614
615 println!("========================================");
616 println!("|| now we connect node1 and node2 ||");
617 println!("========================================");
618
619 manually_establish_connection(&node1.swarm, &node2.swarm).await;
620 wait_for_msgs([&node1, &node2, &node3]).await;
621 assert_no_more_msg([&node1, &node2, &node3]).await;
622
623 assert_eq!(node1.dht().successors().list()?, vec![node2.did()]);
624 assert_eq!(node2.dht().successors().list()?, vec![node1.did()]);
625 assert_eq!(node3.dht().successors().list()?, vec![]);
626
627 println!("========================================");
628 println!("|| now we start join node3 to node2 ||");
629 println!("========================================");
630
631 manually_establish_connection(&node3.swarm, &node2.swarm).await;
632 wait_for_msgs([&node1, &node2, &node3]).await;
633 assert_no_more_msg([&node1, &node2, &node3]).await;
634
635 println!("=== Check state before connect via DHT ===");
636 if node1.swarm.transport.get_connection(node3.did()).is_some() {
637 node1.assert_transports(vec![node2.did(), node3.did()]);
638 node2.assert_transports(vec![node1.did(), node3.did()]);
639 node3.assert_transports(vec![node1.did(), node2.did()]);
640 assert_eq!(node1.dht().successors().list()?, vec![
641 node3.did(),
642 node2.did()
643 ]);
644 assert_eq!(node2.dht().successors().list()?, vec![
645 node1.did(),
646 node3.did()
647 ]);
648 assert_eq!(node3.dht().successors().list()?, vec![
649 node2.did(),
650 node1.did()
651 ]);
652 } else {
653 node1.assert_transports(vec![node2.did()]);
654 node2.assert_transports(vec![node1.did(), node3.did()]);
655 node3.assert_transports(vec![node2.did()]);
656 assert_eq!(node1.dht().successors().list()?, vec![node2.did()]);
657 assert_eq!(node2.dht().successors().list()?, vec![
658 node1.did(),
659 node3.did()
660 ]);
661 assert_eq!(node3.dht().successors().list()?, vec![node2.did()]);
662 }
663
664 println!("=============================================");
665 println!("|| now we connect node1 to node3 via DHT ||");
666 println!("=============================================");
667
668 if node1.swarm.transport.get_connection(node3.did()).is_none() {
669 node1.swarm.connect(node3.did()).await?;
670 }
671 wait_for_msgs([&node1, &node2, &node3]).await;
672 assert_no_more_msg([&node1, &node2, &node3]).await;
673
674 println!("=== Check state after connect via DHT ===");
675 node1.assert_transports(vec![node2.did(), node3.did()]);
676 node2.assert_transports(vec![node1.did(), node3.did()]);
677 node3.assert_transports(vec![node1.did(), node2.did()]);
678 assert_eq!(node1.dht().successors().list()?, vec![
679 node3.did(),
680 node2.did()
681 ]);
682 assert_eq!(node2.dht().successors().list()?, vec![
683 node1.did(),
684 node3.did()
685 ]);
686 assert_eq!(node3.dht().successors().list()?, vec![
687 node2.did(),
688 node1.did()
689 ]);
690
691 Ok((node1, node2, node3))
692 }
693
694 #[tokio::test]
695 async fn test_fourth_node_connection() -> Result<()> {
696 let keys = gen_ordered_keys(4);
697 let (key1, key2, key3, key4) = (keys[0], keys[1], keys[2], keys[3]);
698 let (node1, node2, node3) = test_triple_ordered_nodes_connection(key1, key2, key3).await?;
699 let node4 = prepare_node(key4).await;
704
705 manually_establish_connection(&node4.swarm, &node2.swarm).await;
714 wait_until_with_state(
718 "node4 joined: DHT successors converged",
719 || {
720 Ok(
721 node1.dht().successors().list()? == vec![node2.did(), node3.did(), node4.did()]
722 && node2.dht().successors().list()?
723 == vec![node3.did(), node4.did(), node1.did()]
724 && node3.dht().successors().list()?
725 == vec![node4.did(), node1.did(), node2.did()]
726 && node4.dht().successors().list()?
727 == vec![node1.did(), node2.did(), node3.did()],
728 )
729 },
730 || describe_nodes([&node1, &node2, &node3, &node4]),
731 )
732 .await?;
733
734 println!("=== Check state before connect via DHT ===");
735 node1.assert_transports(vec![node2.did(), node3.did(), node4.did()]);
736 node2.assert_transports(vec![node3.did(), node4.did(), node1.did()]);
737 node3.assert_transports(vec![node4.did(), node1.did(), node2.did()]);
738 node4.assert_transports(vec![node1.did(), node2.did(), node3.did()]);
739 assert_eq!(node1.dht().successors().list()?, vec![
740 node2.did(),
741 node3.did(),
742 node4.did(),
743 ]);
744 assert_eq!(node2.dht().successors().list()?, vec![
745 node3.did(),
746 node4.did(),
747 node1.did(),
748 ]);
749 assert_eq!(node3.dht().successors().list()?, vec![
750 node4.did(),
751 node1.did(),
752 node2.did(),
753 ]);
754 assert_eq!(node4.dht().successors().list()?, vec![
755 node1.did(),
756 node2.did(),
757 node3.did(),
758 ]);
759
760 println!("========================================");
761 println!("| test node4 connect node3 via dht |");
762 println!("========================================");
763 println!(
764 "node1.did(): {:?}, node2.did(): {:?}, node3.did(): {:?}, node4.did(): {:?}",
765 node1.did(),
766 node2.did(),
767 node3.did(),
768 node4.did(),
769 );
770 println!("==================================================");
771
772 if node4.swarm.transport.get_connection(node3.did()).is_none() {
773 node4.swarm.connect(node3.did()).await?;
774 }
775 wait_until_with_state(
778 "node4 connected node3: DHT successors converged",
779 || {
780 Ok(
781 node1.dht().successors().list()? == vec![node2.did(), node3.did(), node4.did()]
782 && node2.dht().successors().list()?
783 == vec![node3.did(), node4.did(), node1.did()]
784 && node3.dht().successors().list()?
785 == vec![node4.did(), node1.did(), node2.did()]
786 && node4.dht().successors().list()?
787 == vec![node1.did(), node2.did(), node3.did()],
788 )
789 },
790 || describe_nodes([&node1, &node2, &node3, &node4]),
791 )
792 .await?;
793
794 println!("=== Check state after connect via DHT ===");
795 node1.assert_transports(vec![node2.did(), node3.did(), node4.did()]);
796 node2.assert_transports(vec![node3.did(), node4.did(), node1.did()]);
797 node3.assert_transports(vec![node4.did(), node1.did(), node2.did()]);
798 node4.assert_transports(vec![node1.did(), node2.did(), node3.did()]);
799 assert_eq!(node1.dht().successors().list()?, vec![
800 node2.did(),
801 node3.did(),
802 node4.did()
803 ]);
804 assert_eq!(node2.dht().successors().list()?, vec![
805 node3.did(),
806 node4.did(),
807 node1.did(),
808 ]);
809 assert_eq!(node3.dht().successors().list()?, vec![
810 node4.did(),
811 node1.did(),
812 node2.did(),
813 ]);
814 assert_eq!(node4.dht().successors().list()?, vec![
815 node1.did(),
816 node2.did(),
817 node3.did(),
818 ]);
819
820 Ok(())
821 }
822
823 #[cfg(feature = "dummy")]
824 #[tokio::test]
825 async fn test_joining_between_bootstrap_and_successor_connects_successor_hint() -> Result<()> {
826 let keys = gen_ordered_keys(4);
827 let (node1, node2, node3) =
828 test_triple_ordered_nodes_connection(keys[0], keys[2], keys[3]).await?;
829 let joining = prepare_node(keys[1]).await;
830
831 manually_establish_connection(&joining.swarm, &node1.swarm).await;
832 wait_until(
833 "joining peer connects past bootstrap successor self-report",
834 || {
835 Ok(joining
836 .swarm
837 .transport
838 .get_connection(node2.did())
839 .is_some())
840 },
841 )
842 .await?;
843
844 wait_for_msgs([&node1, &node2, &node3, &joining]).await;
845 assert_no_more_msg([&node1, &node2, &node3, &joining]).await;
846
847 joining.assert_transports(vec![node1.did(), node2.did(), node3.did()]);
848 assert_eq!(node1.dht().successors().list()?, vec![
849 joining.did(),
850 node2.did(),
851 node3.did(),
852 ]);
853 assert_eq!(joining.dht().successors().list()?, vec![
854 node2.did(),
855 node3.did(),
856 node1.did(),
857 ]);
858
859 Ok(())
860 }
861
862 async fn wait_until(msg: &str, mut cond: impl FnMut() -> Result<bool>) -> Result<()> {
870 wait_until_with_state(msg, &mut cond, String::new).await
871 }
872
873 async fn wait_until_with_state(
874 msg: &str,
875 mut cond: impl FnMut() -> Result<bool>,
876 state: impl Fn() -> String,
877 ) -> Result<()> {
878 for _ in 0..300 {
879 if cond()? {
880 return Ok(());
881 }
882 sleep(Duration::from_millis(200)).await;
883 }
884 let state = state();
885 if state.is_empty() {
886 Err(Error::InvalidMessage(format!("timeout waiting for: {msg}")))
887 } else {
888 Err(Error::InvalidMessage(format!(
889 "timeout waiting for: {msg}\n{state}"
890 )))
891 }
892 }
893
894 fn describe_nodes<'a>(nodes: impl IntoIterator<Item = &'a Node>) -> String {
895 nodes
896 .into_iter()
897 .map(|node| {
898 format!(
899 "{:?}: successors={:?}, transports={:?}",
900 node.did(),
901 node.dht().successors().list().unwrap_or_default(),
902 node.swarm.transport.get_connection_ids(),
903 )
904 })
905 .collect::<Vec<_>>()
906 .join("\n")
907 }
908
909 #[tokio::test]
910 async fn test_finger_when_disconnect() -> Result<()> {
911 let key1 = SecretKey::random();
912 let key2 = SecretKey::random();
913
914 let node1 = prepare_node(key1).await;
915 let node2 = prepare_node(key2).await;
916
917 {
918 assert!(node1.dht().lock_finger()?.is_empty());
919 assert!(node1.dht().lock_finger()?.is_empty());
920 }
921
922 manually_establish_connection(&node1.swarm, &node2.swarm).await;
923
924 wait_until("node1 and node2 to join each other's DHT", || {
928 let finger1 = node1.dht().lock_finger()?.clone().clone_finger();
929 let finger2 = node2.dht().lock_finger()?.clone().clone_finger();
930 Ok(finger1.into_iter().any(|x| x == Some(node2.did()))
931 && finger2.into_iter().any(|x| x == Some(node1.did())))
932 })
933 .await?;
934
935 node1.assert_transports(vec![node2.did()]);
936 node2.assert_transports(vec![node1.did()]);
937
938 println!("===================================");
939 println!("| test disconnect node1 and node2 |");
940 println!("===================================");
941 node1.swarm.disconnect(node2.did()).await?;
942
943 wait_until("both sides to drop the connection", || {
947 Ok(node1.swarm.transport.get_connection(node2.did()).is_none()
948 && node2.swarm.transport.get_connection(node1.did()).is_none())
949 })
950 .await?;
951
952 node1.assert_transports(vec![]);
953 node2.assert_transports(vec![]);
954
955 wait_until("both sides to remove each other from DHT fingers", || {
956 let finger1 = node1.dht().lock_finger()?.clone().clone_finger();
957 let finger2 = node2.dht().lock_finger()?.clone().clone_finger();
958 Ok(
959 finger1.into_iter().all(|x| x.is_none())
960 && finger2.into_iter().all(|x| x.is_none()),
961 )
962 })
963 .await?;
964
965 Ok(())
966 }
967}