1use crate::bloom::BloomFilter;
7use crate::mmp::MmpConfig;
8use crate::node::REKEY_JITTER_SECS;
9use crate::noise::{HandshakeState as NoiseHandshakeState, NoiseError, NoiseSession};
10use crate::transport::{LinkId, LinkStats, TransportAddr, TransportId};
11use crate::tree::{ParentDeclaration, TreeCoordinate};
12use crate::utils::index::SessionIndex;
13use crate::{FipsAddress, NodeAddr, PeerIdentity};
14use rand::RngExt;
15use secp256k1::XOnlyPublicKey;
16use std::fmt;
17use std::time::{Duration, Instant};
18
19fn draw_rekey_jitter() -> i64 {
20 rand::rng().random_range(-REKEY_JITTER_SECS..=REKEY_JITTER_SECS)
21}
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub enum ConnectivityState {
28 Connected,
30 Stale,
32 Reconnecting,
34 Disconnected,
36}
37
38impl ConnectivityState {
39 pub fn can_send(&self) -> bool {
41 matches!(
42 self,
43 ConnectivityState::Connected | ConnectivityState::Stale
44 )
45 }
46
47 pub fn is_terminal(&self) -> bool {
49 matches!(self, ConnectivityState::Disconnected)
50 }
51
52 pub fn is_healthy(&self) -> bool {
54 matches!(self, ConnectivityState::Connected)
55 }
56}
57
58impl fmt::Display for ConnectivityState {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 let s = match self {
61 ConnectivityState::Connected => "connected",
62 ConnectivityState::Stale => "stale",
63 ConnectivityState::Reconnecting => "reconnecting",
64 ConnectivityState::Disconnected => "disconnected",
65 };
66 write!(f, "{}", s)
67 }
68}
69
70#[derive(Debug)]
79pub struct ActivePeer {
80 identity: PeerIdentity,
83
84 link_id: LinkId,
87 connectivity: ConnectivityState,
89
90 noise_session: Option<NoiseSession>,
93 our_index: Option<SessionIndex>,
95 their_index: Option<SessionIndex>,
97 transport_id: Option<TransportId>,
99 current_addr: Option<TransportAddr>,
101
102 declaration: Option<ParentDeclaration>,
105 ancestry: Option<TreeCoordinate>,
107
108 tree_announce_min_interval_ms: u64,
111 last_tree_announce_sent_ms: u64,
113 pending_tree_announce: bool,
115
116 inbound_filter: Option<BloomFilter>,
119 filter_sequence: u64,
121 filter_received_at: u64,
123 pending_filter_update: bool,
125
126 session_start: Instant,
130 session_generation: u64,
132
133 link_stats: LinkStats,
136 authenticated_at: u64,
138 last_seen: u64,
140
141 remote_epoch: Option<[u8; 8]>,
144
145 fmp_mmp_is_initiator: bool,
148
149 last_heartbeat_sent: Option<Instant>,
152
153 handshake_msg2: Option<Vec<u8>>,
157
158 replay_suppressed_count: u32,
161 consecutive_decrypt_failures: u32,
163
164 session_established_at: Instant,
167 rekey_jitter_secs: i64,
169 current_k_bit: bool,
171 previous_session: Option<NoiseSession>,
173 previous_our_index: Option<SessionIndex>,
175 drain_started: Option<Instant>,
177 pending_new_session: Option<NoiseSession>,
179 pending_our_index: Option<SessionIndex>,
181 pending_their_index: Option<SessionIndex>,
183 pending_rekey_initiator: bool,
185 pending_rekey_completed_at: Option<Instant>,
187 rekey_in_progress: bool,
189 last_peer_rekey: Option<Instant>,
191 rekey_handshake: Option<NoiseHandshakeState>,
193 rekey_our_index: Option<SessionIndex>,
195 rekey_msg1: Option<Vec<u8>>,
197 rekey_msg1_next_resend: u64,
199 rekey_msg1_resend_count: u32,
201}
202
203impl ActivePeer {
204 pub fn new(identity: PeerIdentity, link_id: LinkId, authenticated_at: u64) -> Self {
209 let now = Instant::now();
210 Self {
211 identity,
212 link_id,
213 connectivity: ConnectivityState::Connected,
214 noise_session: None,
215 our_index: None,
216 their_index: None,
217 transport_id: None,
218 current_addr: None,
219 declaration: None,
220 ancestry: None,
221 tree_announce_min_interval_ms: 500,
222 last_tree_announce_sent_ms: 0,
223 pending_tree_announce: false,
224 inbound_filter: None,
225 filter_sequence: 0,
226 filter_received_at: 0,
227 pending_filter_update: true, session_start: now,
229 session_generation: authenticated_at.max(1),
230 link_stats: LinkStats::new(),
231 authenticated_at,
232 last_seen: authenticated_at,
233 remote_epoch: None,
234 fmp_mmp_is_initiator: false,
235 last_heartbeat_sent: None,
236 handshake_msg2: None,
237 replay_suppressed_count: 0,
238 consecutive_decrypt_failures: 0,
239 session_established_at: now,
240 rekey_jitter_secs: draw_rekey_jitter(),
241 current_k_bit: false,
242 previous_session: None,
243 previous_our_index: None,
244 drain_started: None,
245 pending_new_session: None,
246 pending_our_index: None,
247 pending_their_index: None,
248 pending_rekey_initiator: false,
249 pending_rekey_completed_at: None,
250 rekey_in_progress: false,
251 last_peer_rekey: None,
252 rekey_handshake: None,
253 rekey_our_index: None,
254 rekey_msg1: None,
255 rekey_msg1_next_resend: 0,
256 rekey_msg1_resend_count: 0,
257 }
258 }
259
260 pub fn with_stats(
265 identity: PeerIdentity,
266 link_id: LinkId,
267 authenticated_at: u64,
268 link_stats: LinkStats,
269 ) -> Self {
270 let mut peer = Self::new(identity, link_id, authenticated_at);
271 peer.link_stats = link_stats;
272 peer
273 }
274
275 #[allow(clippy::too_many_arguments)]
280 pub fn with_session(
281 identity: PeerIdentity,
282 link_id: LinkId,
283 authenticated_at: u64,
284 noise_session: NoiseSession,
285 our_index: SessionIndex,
286 their_index: SessionIndex,
287 transport_id: TransportId,
288 current_addr: TransportAddr,
289 link_stats: LinkStats,
290 is_initiator: bool,
291 _mmp_config: &MmpConfig,
292 remote_epoch: Option<[u8; 8]>,
293 ) -> Self {
294 let now = Instant::now();
295 Self {
296 identity,
297 link_id,
298 connectivity: ConnectivityState::Connected,
299 noise_session: Some(noise_session),
300 our_index: Some(our_index),
301 their_index: Some(their_index),
302 transport_id: Some(transport_id),
303 current_addr: Some(current_addr),
304 declaration: None,
305 ancestry: None,
306 tree_announce_min_interval_ms: 500,
307 last_tree_announce_sent_ms: 0,
308 pending_tree_announce: false,
309 inbound_filter: None,
310 filter_sequence: 0,
311 filter_received_at: 0,
312 pending_filter_update: true,
313 session_start: now,
314 session_generation: authenticated_at.max(1),
315 link_stats,
316 authenticated_at,
317 last_seen: authenticated_at,
318 remote_epoch,
319 fmp_mmp_is_initiator: is_initiator,
320 last_heartbeat_sent: None,
321 handshake_msg2: None,
322 replay_suppressed_count: 0,
323 consecutive_decrypt_failures: 0,
324 session_established_at: now,
325 rekey_jitter_secs: draw_rekey_jitter(),
326 current_k_bit: false,
327 previous_session: None,
328 previous_our_index: None,
329 drain_started: None,
330 pending_new_session: None,
331 pending_our_index: None,
332 pending_their_index: None,
333 pending_rekey_initiator: false,
334 pending_rekey_completed_at: None,
335 rekey_in_progress: false,
336 last_peer_rekey: None,
337 rekey_handshake: None,
338 rekey_our_index: None,
339 rekey_msg1: None,
340 rekey_msg1_next_resend: 0,
341 rekey_msg1_resend_count: 0,
342 }
343 }
344
345 pub fn identity(&self) -> &PeerIdentity {
349 &self.identity
350 }
351
352 pub fn node_addr(&self) -> &NodeAddr {
354 self.identity.node_addr()
355 }
356
357 pub fn address(&self) -> &FipsAddress {
359 self.identity.address()
360 }
361
362 pub fn pubkey(&self) -> XOnlyPublicKey {
364 self.identity.pubkey()
365 }
366
367 pub fn npub(&self) -> String {
369 self.identity.npub()
370 }
371
372 pub fn link_id(&self) -> LinkId {
376 self.link_id
377 }
378
379 pub fn connectivity(&self) -> ConnectivityState {
381 self.connectivity
382 }
383
384 pub fn can_send(&self) -> bool {
386 self.connectivity.can_send()
387 }
388
389 pub fn is_healthy(&self) -> bool {
391 self.connectivity.is_healthy()
392 }
393
394 pub fn is_disconnected(&self) -> bool {
396 self.connectivity.is_terminal()
397 }
398
399 pub fn has_session(&self) -> bool {
403 self.noise_session.is_some()
404 }
405
406 pub fn noise_session(&self) -> Option<&NoiseSession> {
408 self.noise_session.as_ref()
409 }
410
411 pub fn noise_session_mut(&mut self) -> Option<&mut NoiseSession> {
413 self.noise_session.as_mut()
414 }
415
416 pub fn our_index(&self) -> Option<SessionIndex> {
418 self.our_index
419 }
420
421 pub fn their_index(&self) -> Option<SessionIndex> {
423 self.their_index
424 }
425
426 pub fn set_their_index(&mut self, index: SessionIndex) {
430 self.their_index = Some(index);
431 }
432
433 pub fn replace_session(
443 &mut self,
444 new_session: NoiseSession,
445 new_our_index: SessionIndex,
446 new_their_index: SessionIndex,
447 ) -> Option<SessionIndex> {
448 self.reset_replay_suppressed();
449 let old_our_index = self.our_index;
450 self.noise_session = Some(new_session);
451 self.our_index = Some(new_our_index);
452 self.their_index = Some(new_their_index);
453 old_our_index
454 }
455
456 pub fn transport_id(&self) -> Option<TransportId> {
458 self.transport_id
459 }
460
461 pub fn current_addr(&self) -> Option<&TransportAddr> {
463 self.current_addr.as_ref()
464 }
465
466 pub fn set_current_addr(&mut self, transport_id: TransportId, addr: &TransportAddr) -> bool {
480 if self.transport_id == Some(transport_id) && self.current_addr.as_ref() == Some(addr) {
481 return false;
482 }
483 self.transport_id = Some(transport_id);
484 self.current_addr = Some(addr.clone());
485 true
486 }
487
488 pub fn set_handshake_msg2(&mut self, msg2: Vec<u8>) {
492 self.handshake_msg2 = Some(msg2);
493 }
494
495 pub fn handshake_msg2(&self) -> Option<&[u8]> {
497 self.handshake_msg2.as_deref()
498 }
499
500 pub fn clear_handshake_msg2(&mut self) {
502 self.handshake_msg2 = None;
503 }
504
505 pub fn increment_replay_suppressed(&mut self) -> u32 {
509 self.replay_suppressed_count += 1;
510 self.replay_suppressed_count
511 }
512
513 pub fn reset_replay_suppressed(&mut self) -> u32 {
515 let count = self.replay_suppressed_count;
516 self.replay_suppressed_count = 0;
517 count
518 }
519
520 pub fn replay_suppressed_count(&self) -> u32 {
522 self.replay_suppressed_count
523 }
524
525 pub fn increment_decrypt_failures(&mut self) -> u32 {
529 self.consecutive_decrypt_failures += 1;
530 self.consecutive_decrypt_failures
531 }
532
533 pub fn reset_decrypt_failures(&mut self) {
535 self.consecutive_decrypt_failures = 0;
536 }
537
538 pub fn consecutive_decrypt_failures(&self) -> u32 {
540 self.consecutive_decrypt_failures
541 }
542
543 pub fn remote_epoch(&self) -> Option<[u8; 8]> {
547 self.remote_epoch
548 }
549
550 pub(crate) fn set_remote_epoch(&mut self, remote_epoch: Option<[u8; 8]>) {
554 self.remote_epoch = remote_epoch;
555 }
556
557 pub fn coords(&self) -> Option<&TreeCoordinate> {
561 self.ancestry.as_ref()
562 }
563
564 pub fn declaration(&self) -> Option<&ParentDeclaration> {
566 self.declaration.as_ref()
567 }
568
569 pub fn has_tree_position(&self) -> bool {
571 self.declaration.is_some() && self.ancestry.is_some()
572 }
573
574 pub fn inbound_filter(&self) -> Option<&BloomFilter> {
578 self.inbound_filter.as_ref()
579 }
580
581 pub fn filter_sequence(&self) -> u64 {
583 self.filter_sequence
584 }
585
586 pub fn filter_is_stale(&self, current_time_ms: u64, stale_threshold_ms: u64) -> bool {
588 if self.filter_received_at == 0 {
589 return true;
590 }
591 current_time_ms.saturating_sub(self.filter_received_at) > stale_threshold_ms
592 }
593
594 pub fn may_reach(&self, node_addr: &NodeAddr) -> bool {
596 match &self.inbound_filter {
597 Some(filter) => filter.contains(node_addr),
598 None => false,
599 }
600 }
601
602 pub fn needs_filter_update(&self) -> bool {
604 self.pending_filter_update
605 }
606
607 pub fn link_stats(&self) -> &LinkStats {
611 &self.link_stats
612 }
613
614 pub fn link_stats_mut(&mut self) -> &mut LinkStats {
616 &mut self.link_stats
617 }
618
619 pub fn fmp_mmp_is_initiator(&self) -> bool {
621 self.fmp_mmp_is_initiator
622 }
623
624 pub fn authenticated_at(&self) -> u64 {
626 self.authenticated_at
627 }
628
629 pub fn last_seen(&self) -> u64 {
631 self.last_seen
632 }
633
634 pub fn idle_time(&self, current_time_ms: u64) -> u64 {
636 current_time_ms.saturating_sub(self.last_seen)
637 }
638
639 pub fn connection_duration(&self, current_time_ms: u64) -> u64 {
641 current_time_ms.saturating_sub(self.authenticated_at)
642 }
643
644 pub fn session_elapsed_ms(&self) -> u32 {
649 self.session_start.elapsed().as_millis() as u32
650 }
651
652 pub fn session_generation(&self) -> u64 {
654 self.session_generation
655 }
656
657 pub fn session_start(&self) -> Instant {
659 self.session_start
660 }
661
662 pub fn last_heartbeat_sent(&self) -> Option<Instant> {
666 self.last_heartbeat_sent
667 }
668
669 pub fn mark_heartbeat_sent(&mut self, now: Instant) {
671 self.last_heartbeat_sent = Some(now);
672 }
673
674 pub fn touch(&mut self, current_time_ms: u64) {
678 self.last_seen = current_time_ms;
679 if self.connectivity == ConnectivityState::Stale {
683 self.connectivity = ConnectivityState::Connected;
684 }
685 }
686
687 pub fn mark_stale(&mut self) {
689 if self.connectivity == ConnectivityState::Connected {
690 self.connectivity = ConnectivityState::Stale;
691 }
692 }
693
694 pub fn mark_reconnecting(&mut self) {
696 self.connectivity = ConnectivityState::Reconnecting;
697 }
698
699 pub fn mark_disconnected(&mut self) {
701 self.connectivity = ConnectivityState::Disconnected;
702 }
703
704 pub fn mark_connected(&mut self, current_time_ms: u64) {
706 self.connectivity = ConnectivityState::Connected;
707 self.last_seen = current_time_ms;
708 }
709
710 pub fn set_link_id(&mut self, link_id: LinkId) {
712 self.link_id = link_id;
713 }
714
715 pub fn update_tree_position(
723 &mut self,
724 declaration: ParentDeclaration,
725 ancestry: TreeCoordinate,
726 _current_time_ms: u64,
727 ) {
728 self.declaration = Some(declaration);
729 self.ancestry = Some(ancestry);
730 }
731
732 pub fn clear_tree_position(&mut self) {
734 self.declaration = None;
735 self.ancestry = None;
736 }
737
738 pub fn set_tree_announce_min_interval_ms(&mut self, ms: u64) {
742 self.tree_announce_min_interval_ms = ms;
743 }
744
745 pub fn last_tree_announce_sent_ms(&self) -> u64 {
747 self.last_tree_announce_sent_ms
748 }
749
750 pub fn set_last_tree_announce_sent_ms(&mut self, ms: u64) {
752 self.last_tree_announce_sent_ms = ms;
753 }
754
755 pub fn can_send_tree_announce(&self, now_ms: u64) -> bool {
757 now_ms.saturating_sub(self.last_tree_announce_sent_ms) >= self.tree_announce_min_interval_ms
758 }
759
760 pub fn record_tree_announce_sent(&mut self, now_ms: u64) {
762 self.last_tree_announce_sent_ms = now_ms;
763 self.pending_tree_announce = false;
764 }
765
766 pub fn mark_tree_announce_pending(&mut self) {
768 self.pending_tree_announce = true;
769 }
770
771 pub fn has_pending_tree_announce(&self) -> bool {
773 self.pending_tree_announce
774 }
775
776 pub fn update_filter(&mut self, filter: BloomFilter, sequence: u64, current_time_ms: u64) {
780 self.inbound_filter = Some(filter);
781 self.filter_sequence = sequence;
782 self.filter_received_at = current_time_ms;
783 self.last_seen = current_time_ms;
784 }
785
786 pub fn clear_filter(&mut self) {
788 self.inbound_filter = None;
789 self.filter_sequence = 0;
790 self.filter_received_at = 0;
791 }
792
793 pub fn mark_filter_update_needed(&mut self) {
795 self.pending_filter_update = true;
796 }
797
798 pub fn clear_filter_update_needed(&mut self) {
800 self.pending_filter_update = false;
801 }
802}
803
804mod rekey;
805
806#[cfg(test)]
807mod tests;