1use crate::bloom::BloomFilter;
7use crate::node::REKEY_JITTER_SECS;
8use crate::noise::{HandshakeState as NoiseHandshakeState, NoiseError, NoiseSession};
9use crate::transport::{LinkId, LinkStats, TransportAddr, TransportId};
10use crate::tree::{ParentDeclaration, TreeCoordinate};
11use crate::utils::index::SessionIndex;
12use crate::{FipsAddress, NodeAddr, PeerIdentity};
13use rand::RngExt;
14use secp256k1::XOnlyPublicKey;
15use std::fmt;
16use std::time::{Duration, Instant};
17
18fn draw_rekey_jitter() -> i64 {
19 rand::rng().random_range(-REKEY_JITTER_SECS..=REKEY_JITTER_SECS)
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum ConnectivityState {
27 Connected,
29 Stale,
31 Reconnecting,
33 Disconnected,
35}
36
37impl ConnectivityState {
38 pub fn can_send(&self) -> bool {
40 matches!(
41 self,
42 ConnectivityState::Connected | ConnectivityState::Stale
43 )
44 }
45
46 pub fn is_terminal(&self) -> bool {
48 matches!(self, ConnectivityState::Disconnected)
49 }
50
51 pub fn is_healthy(&self) -> bool {
53 matches!(self, ConnectivityState::Connected)
54 }
55}
56
57impl fmt::Display for ConnectivityState {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 let s = match self {
60 ConnectivityState::Connected => "connected",
61 ConnectivityState::Stale => "stale",
62 ConnectivityState::Reconnecting => "reconnecting",
63 ConnectivityState::Disconnected => "disconnected",
64 };
65 write!(f, "{}", s)
66 }
67}
68
69pub struct ActivePeerSession {
71 pub session: NoiseSession,
72 pub our_index: SessionIndex,
73 pub their_index: SessionIndex,
74 pub transport_id: TransportId,
75 pub current_addr: TransportAddr,
76 pub link_stats: LinkStats,
77 pub is_initiator: bool,
78 pub remote_epoch: Option<[u8; 8]>,
79}
80
81#[derive(Debug)]
90pub struct ActivePeer {
91 identity: PeerIdentity,
94
95 link_id: LinkId,
98 connectivity: ConnectivityState,
100
101 noise_session: Option<NoiseSession>,
104 our_index: Option<SessionIndex>,
106 their_index: Option<SessionIndex>,
108 transport_id: Option<TransportId>,
110 current_addr: Option<TransportAddr>,
112
113 declaration: Option<ParentDeclaration>,
116 ancestry: Option<TreeCoordinate>,
118
119 tree_announce_min_interval_ms: u64,
122 last_tree_announce_sent_ms: u64,
124 pending_tree_announce: bool,
126
127 inbound_filter: Option<BloomFilter>,
130 filter_sequence: u64,
132 filter_received_at: u64,
134 pending_filter_update: bool,
136
137 session_start: Instant,
141 session_generation: u64,
143
144 link_stats: LinkStats,
147 authenticated_at: u64,
149 last_seen: u64,
151
152 remote_epoch: Option<[u8; 8]>,
155
156 fmp_mmp_is_initiator: bool,
159
160 last_heartbeat_sent: Option<Instant>,
163
164 handshake_msg2: Option<Vec<u8>>,
168
169 replay_suppressed_count: u32,
172 consecutive_decrypt_failures: u32,
174
175 session_established_at: Instant,
178 rekey_jitter_secs: i64,
180 current_k_bit: bool,
182 previous_session: Option<NoiseSession>,
184 previous_our_index: Option<SessionIndex>,
186 previous_transport_id: Option<TransportId>,
188 drain_started: Option<Instant>,
190 pending_new_session: Option<NoiseSession>,
192 pending_our_index: Option<SessionIndex>,
194 pending_their_index: Option<SessionIndex>,
196 pending_rekey_initiator: bool,
198 pending_rekey_completed_at: Option<Instant>,
200 rekey_in_progress: bool,
202 last_peer_rekey: Option<Instant>,
204 rekey_handshake: Option<NoiseHandshakeState>,
206 rekey_our_index: Option<SessionIndex>,
208 rekey_msg1: Option<Vec<u8>>,
210 rekey_msg1_next_resend: u64,
212 rekey_msg1_resend_count: u32,
214}
215
216impl ActivePeer {
217 pub fn new(identity: PeerIdentity, link_id: LinkId, authenticated_at: u64) -> Self {
222 let now = Instant::now();
223 Self {
224 identity,
225 link_id,
226 connectivity: ConnectivityState::Connected,
227 noise_session: None,
228 our_index: None,
229 their_index: None,
230 transport_id: None,
231 current_addr: None,
232 declaration: None,
233 ancestry: None,
234 tree_announce_min_interval_ms: 500,
235 last_tree_announce_sent_ms: 0,
236 pending_tree_announce: false,
237 inbound_filter: None,
238 filter_sequence: 0,
239 filter_received_at: 0,
240 pending_filter_update: true, session_start: now,
242 session_generation: authenticated_at.max(1),
243 link_stats: LinkStats::new(),
244 authenticated_at,
245 last_seen: authenticated_at,
246 remote_epoch: None,
247 fmp_mmp_is_initiator: false,
248 last_heartbeat_sent: None,
249 handshake_msg2: None,
250 replay_suppressed_count: 0,
251 consecutive_decrypt_failures: 0,
252 session_established_at: now,
253 rekey_jitter_secs: draw_rekey_jitter(),
254 current_k_bit: false,
255 previous_session: None,
256 previous_our_index: None,
257 previous_transport_id: None,
258 drain_started: None,
259 pending_new_session: None,
260 pending_our_index: None,
261 pending_their_index: None,
262 pending_rekey_initiator: false,
263 pending_rekey_completed_at: None,
264 rekey_in_progress: false,
265 last_peer_rekey: None,
266 rekey_handshake: None,
267 rekey_our_index: None,
268 rekey_msg1: None,
269 rekey_msg1_next_resend: 0,
270 rekey_msg1_resend_count: 0,
271 }
272 }
273
274 pub fn with_stats(
279 identity: PeerIdentity,
280 link_id: LinkId,
281 authenticated_at: u64,
282 link_stats: LinkStats,
283 ) -> Self {
284 let mut peer = Self::new(identity, link_id, authenticated_at);
285 peer.link_stats = link_stats;
286 peer
287 }
288
289 pub fn with_session(
294 identity: PeerIdentity,
295 link_id: LinkId,
296 authenticated_at: u64,
297 session: ActivePeerSession,
298 ) -> Self {
299 let now = Instant::now();
300 Self {
301 identity,
302 link_id,
303 connectivity: ConnectivityState::Connected,
304 noise_session: Some(session.session),
305 our_index: Some(session.our_index),
306 their_index: Some(session.their_index),
307 transport_id: Some(session.transport_id),
308 current_addr: Some(session.current_addr),
309 declaration: None,
310 ancestry: None,
311 tree_announce_min_interval_ms: 500,
312 last_tree_announce_sent_ms: 0,
313 pending_tree_announce: false,
314 inbound_filter: None,
315 filter_sequence: 0,
316 filter_received_at: 0,
317 pending_filter_update: true,
318 session_start: now,
319 session_generation: authenticated_at.max(1),
320 link_stats: session.link_stats,
321 authenticated_at,
322 last_seen: authenticated_at,
323 remote_epoch: session.remote_epoch,
324 fmp_mmp_is_initiator: session.is_initiator,
325 last_heartbeat_sent: None,
326 handshake_msg2: None,
327 replay_suppressed_count: 0,
328 consecutive_decrypt_failures: 0,
329 session_established_at: now,
330 rekey_jitter_secs: draw_rekey_jitter(),
331 current_k_bit: false,
332 previous_session: None,
333 previous_our_index: None,
334 previous_transport_id: None,
335 drain_started: None,
336 pending_new_session: None,
337 pending_our_index: None,
338 pending_their_index: None,
339 pending_rekey_initiator: false,
340 pending_rekey_completed_at: None,
341 rekey_in_progress: false,
342 last_peer_rekey: None,
343 rekey_handshake: None,
344 rekey_our_index: None,
345 rekey_msg1: None,
346 rekey_msg1_next_resend: 0,
347 rekey_msg1_resend_count: 0,
348 }
349 }
350
351 pub fn identity(&self) -> &PeerIdentity {
355 &self.identity
356 }
357
358 pub fn node_addr(&self) -> &NodeAddr {
360 self.identity.node_addr()
361 }
362
363 pub fn address(&self) -> &FipsAddress {
365 self.identity.address()
366 }
367
368 pub fn pubkey(&self) -> XOnlyPublicKey {
370 self.identity.pubkey()
371 }
372
373 pub fn npub(&self) -> String {
375 self.identity.npub()
376 }
377
378 pub fn link_id(&self) -> LinkId {
382 self.link_id
383 }
384
385 pub fn connectivity(&self) -> ConnectivityState {
387 self.connectivity
388 }
389
390 pub fn can_send(&self) -> bool {
392 self.connectivity.can_send()
393 }
394
395 pub fn is_healthy(&self) -> bool {
397 self.connectivity.is_healthy()
398 }
399
400 pub fn is_disconnected(&self) -> bool {
402 self.connectivity.is_terminal()
403 }
404
405 pub fn has_session(&self) -> bool {
409 self.noise_session.is_some()
410 }
411
412 pub fn noise_session(&self) -> Option<&NoiseSession> {
414 self.noise_session.as_ref()
415 }
416
417 pub fn noise_session_mut(&mut self) -> Option<&mut NoiseSession> {
419 self.noise_session.as_mut()
420 }
421
422 pub fn our_index(&self) -> Option<SessionIndex> {
424 self.our_index
425 }
426
427 pub fn their_index(&self) -> Option<SessionIndex> {
429 self.their_index
430 }
431
432 pub fn set_their_index(&mut self, index: SessionIndex) {
436 self.their_index = Some(index);
437 }
438
439 pub fn replace_session(
449 &mut self,
450 new_session: NoiseSession,
451 new_our_index: SessionIndex,
452 new_their_index: SessionIndex,
453 ) -> Option<SessionIndex> {
454 self.reset_replay_suppressed();
455 let old_our_index = self.our_index;
456 self.previous_session = self.noise_session.take();
457 self.previous_our_index = old_our_index.filter(|old| *old != new_our_index);
458 self.previous_transport_id = self.transport_id;
459 self.drain_started = self.previous_our_index.map(|_| Instant::now());
460 self.noise_session = Some(new_session);
461 self.our_index = Some(new_our_index);
462 self.their_index = Some(new_their_index);
463 self.session_established_at = Instant::now();
464 self.session_start = Instant::now();
465 self.session_generation = self.session_generation.wrapping_add(1).max(1);
466 self.rekey_in_progress = false;
467 self.rekey_msg1_resend_count = 0;
468 self.rekey_jitter_secs = draw_rekey_jitter();
469 self.last_heartbeat_sent = None;
470 old_our_index
471 }
472
473 pub fn transport_id(&self) -> Option<TransportId> {
475 self.transport_id
476 }
477
478 pub fn current_addr(&self) -> Option<&TransportAddr> {
480 self.current_addr.as_ref()
481 }
482
483 pub fn set_current_addr(&mut self, transport_id: TransportId, addr: &TransportAddr) -> bool {
497 if self.transport_id == Some(transport_id) && self.current_addr.as_ref() == Some(addr) {
498 return false;
499 }
500 self.transport_id = Some(transport_id);
501 self.current_addr = Some(addr.clone());
502 true
503 }
504
505 pub fn set_handshake_msg2(&mut self, msg2: Vec<u8>) {
509 self.handshake_msg2 = Some(msg2);
510 }
511
512 pub fn handshake_msg2(&self) -> Option<&[u8]> {
514 self.handshake_msg2.as_deref()
515 }
516
517 pub fn clear_handshake_msg2(&mut self) {
519 self.handshake_msg2 = None;
520 }
521
522 pub fn increment_replay_suppressed(&mut self) -> u32 {
526 self.replay_suppressed_count += 1;
527 self.replay_suppressed_count
528 }
529
530 pub fn reset_replay_suppressed(&mut self) -> u32 {
532 let count = self.replay_suppressed_count;
533 self.replay_suppressed_count = 0;
534 count
535 }
536
537 pub fn replay_suppressed_count(&self) -> u32 {
539 self.replay_suppressed_count
540 }
541
542 pub fn increment_decrypt_failures(&mut self) -> u32 {
546 self.consecutive_decrypt_failures += 1;
547 self.consecutive_decrypt_failures
548 }
549
550 pub fn reset_decrypt_failures(&mut self) {
552 self.consecutive_decrypt_failures = 0;
553 }
554
555 pub fn consecutive_decrypt_failures(&self) -> u32 {
557 self.consecutive_decrypt_failures
558 }
559
560 pub fn remote_epoch(&self) -> Option<[u8; 8]> {
564 self.remote_epoch
565 }
566
567 pub(crate) fn set_remote_epoch(&mut self, remote_epoch: Option<[u8; 8]>) {
571 self.remote_epoch = remote_epoch;
572 }
573
574 pub fn coords(&self) -> Option<&TreeCoordinate> {
578 self.ancestry.as_ref()
579 }
580
581 pub fn declaration(&self) -> Option<&ParentDeclaration> {
583 self.declaration.as_ref()
584 }
585
586 pub fn has_tree_position(&self) -> bool {
588 self.declaration.is_some() && self.ancestry.is_some()
589 }
590
591 pub fn inbound_filter(&self) -> Option<&BloomFilter> {
595 self.inbound_filter.as_ref()
596 }
597
598 pub fn filter_sequence(&self) -> u64 {
600 self.filter_sequence
601 }
602
603 pub fn filter_is_stale(&self, current_time_ms: u64, stale_threshold_ms: u64) -> bool {
605 if self.filter_received_at == 0 {
606 return true;
607 }
608 current_time_ms.saturating_sub(self.filter_received_at) > stale_threshold_ms
609 }
610
611 pub fn may_reach(&self, node_addr: &NodeAddr) -> bool {
613 match &self.inbound_filter {
614 Some(filter) => filter.contains(node_addr),
615 None => false,
616 }
617 }
618
619 pub fn needs_filter_update(&self) -> bool {
621 self.pending_filter_update
622 }
623
624 pub fn link_stats(&self) -> &LinkStats {
628 &self.link_stats
629 }
630
631 pub fn link_stats_mut(&mut self) -> &mut LinkStats {
633 &mut self.link_stats
634 }
635
636 pub fn fmp_mmp_is_initiator(&self) -> bool {
638 self.fmp_mmp_is_initiator
639 }
640
641 pub(crate) fn set_fmp_mmp_is_initiator(&mut self, is_initiator: bool) {
642 self.fmp_mmp_is_initiator = is_initiator;
643 }
644
645 pub fn authenticated_at(&self) -> u64 {
647 self.authenticated_at
648 }
649
650 pub fn last_seen(&self) -> u64 {
652 self.last_seen
653 }
654
655 pub fn idle_time(&self, current_time_ms: u64) -> u64 {
657 current_time_ms.saturating_sub(self.last_seen)
658 }
659
660 pub fn connection_duration(&self, current_time_ms: u64) -> u64 {
662 current_time_ms.saturating_sub(self.authenticated_at)
663 }
664
665 pub fn session_elapsed_ms(&self) -> u32 {
670 self.session_start.elapsed().as_millis() as u32
671 }
672
673 pub fn session_generation(&self) -> u64 {
675 self.session_generation
676 }
677
678 pub fn session_start(&self) -> Instant {
680 self.session_start
681 }
682
683 pub fn last_heartbeat_sent(&self) -> Option<Instant> {
687 self.last_heartbeat_sent
688 }
689
690 pub fn mark_heartbeat_sent(&mut self, now: Instant) {
692 self.last_heartbeat_sent = Some(now);
693 }
694
695 pub fn touch(&mut self, current_time_ms: u64) {
699 self.last_seen = current_time_ms;
700 if self.connectivity == ConnectivityState::Stale {
704 self.connectivity = ConnectivityState::Connected;
705 }
706 }
707
708 pub fn mark_stale(&mut self) {
710 if self.connectivity == ConnectivityState::Connected {
711 self.connectivity = ConnectivityState::Stale;
712 }
713 }
714
715 pub fn mark_reconnecting(&mut self) {
717 self.connectivity = ConnectivityState::Reconnecting;
718 }
719
720 pub fn mark_disconnected(&mut self) {
722 self.connectivity = ConnectivityState::Disconnected;
723 }
724
725 pub fn mark_connected(&mut self, current_time_ms: u64) {
727 self.connectivity = ConnectivityState::Connected;
728 self.last_seen = current_time_ms;
729 }
730
731 pub fn set_link_id(&mut self, link_id: LinkId) {
733 self.link_id = link_id;
734 }
735
736 pub fn update_tree_position(
744 &mut self,
745 declaration: ParentDeclaration,
746 ancestry: TreeCoordinate,
747 ) {
748 self.declaration = Some(declaration);
749 self.ancestry = Some(ancestry);
750 }
751
752 pub fn clear_tree_position(&mut self) {
754 self.declaration = None;
755 self.ancestry = None;
756 }
757
758 pub fn set_tree_announce_min_interval_ms(&mut self, ms: u64) {
762 self.tree_announce_min_interval_ms = ms;
763 }
764
765 pub fn last_tree_announce_sent_ms(&self) -> u64 {
767 self.last_tree_announce_sent_ms
768 }
769
770 pub fn set_last_tree_announce_sent_ms(&mut self, ms: u64) {
772 self.last_tree_announce_sent_ms = ms;
773 }
774
775 pub fn can_send_tree_announce(&self, now_ms: u64) -> bool {
777 now_ms.saturating_sub(self.last_tree_announce_sent_ms) >= self.tree_announce_min_interval_ms
778 }
779
780 pub fn record_tree_announce_sent(&mut self, now_ms: u64) {
782 self.last_tree_announce_sent_ms = now_ms;
783 self.pending_tree_announce = false;
784 }
785
786 pub fn mark_tree_announce_pending(&mut self) {
788 self.pending_tree_announce = true;
789 }
790
791 pub fn has_pending_tree_announce(&self) -> bool {
793 self.pending_tree_announce
794 }
795
796 pub fn update_filter(&mut self, filter: BloomFilter, sequence: u64, current_time_ms: u64) {
800 self.inbound_filter = Some(filter);
801 self.filter_sequence = sequence;
802 self.filter_received_at = current_time_ms;
803 self.last_seen = current_time_ms;
804 }
805
806 pub fn clear_filter(&mut self) {
808 self.inbound_filter = None;
809 self.filter_sequence = 0;
810 self.filter_received_at = 0;
811 }
812
813 pub fn mark_filter_update_needed(&mut self) {
815 self.pending_filter_update = true;
816 }
817
818 pub fn clear_filter_update_needed(&mut self) {
820 self.pending_filter_update = false;
821 }
822}
823
824mod rekey;
825
826#[cfg(test)]
827mod tests;