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 drain_started: Option<Instant>,
188 pending_new_session: Option<NoiseSession>,
190 pending_our_index: Option<SessionIndex>,
192 pending_their_index: Option<SessionIndex>,
194 pending_rekey_initiator: bool,
196 pending_rekey_completed_at: Option<Instant>,
198 rekey_in_progress: bool,
200 last_peer_rekey: Option<Instant>,
202 rekey_handshake: Option<NoiseHandshakeState>,
204 rekey_our_index: Option<SessionIndex>,
206 rekey_msg1: Option<Vec<u8>>,
208 rekey_msg1_next_resend: u64,
210 rekey_msg1_resend_count: u32,
212}
213
214impl ActivePeer {
215 pub fn new(identity: PeerIdentity, link_id: LinkId, authenticated_at: u64) -> Self {
220 let now = Instant::now();
221 Self {
222 identity,
223 link_id,
224 connectivity: ConnectivityState::Connected,
225 noise_session: None,
226 our_index: None,
227 their_index: None,
228 transport_id: None,
229 current_addr: None,
230 declaration: None,
231 ancestry: None,
232 tree_announce_min_interval_ms: 500,
233 last_tree_announce_sent_ms: 0,
234 pending_tree_announce: false,
235 inbound_filter: None,
236 filter_sequence: 0,
237 filter_received_at: 0,
238 pending_filter_update: true, session_start: now,
240 session_generation: authenticated_at.max(1),
241 link_stats: LinkStats::new(),
242 authenticated_at,
243 last_seen: authenticated_at,
244 remote_epoch: None,
245 fmp_mmp_is_initiator: false,
246 last_heartbeat_sent: None,
247 handshake_msg2: None,
248 replay_suppressed_count: 0,
249 consecutive_decrypt_failures: 0,
250 session_established_at: now,
251 rekey_jitter_secs: draw_rekey_jitter(),
252 current_k_bit: false,
253 previous_session: None,
254 previous_our_index: None,
255 drain_started: None,
256 pending_new_session: None,
257 pending_our_index: None,
258 pending_their_index: None,
259 pending_rekey_initiator: false,
260 pending_rekey_completed_at: None,
261 rekey_in_progress: false,
262 last_peer_rekey: None,
263 rekey_handshake: None,
264 rekey_our_index: None,
265 rekey_msg1: None,
266 rekey_msg1_next_resend: 0,
267 rekey_msg1_resend_count: 0,
268 }
269 }
270
271 pub fn with_stats(
276 identity: PeerIdentity,
277 link_id: LinkId,
278 authenticated_at: u64,
279 link_stats: LinkStats,
280 ) -> Self {
281 let mut peer = Self::new(identity, link_id, authenticated_at);
282 peer.link_stats = link_stats;
283 peer
284 }
285
286 pub fn with_session(
291 identity: PeerIdentity,
292 link_id: LinkId,
293 authenticated_at: u64,
294 session: ActivePeerSession,
295 ) -> Self {
296 let now = Instant::now();
297 Self {
298 identity,
299 link_id,
300 connectivity: ConnectivityState::Connected,
301 noise_session: Some(session.session),
302 our_index: Some(session.our_index),
303 their_index: Some(session.their_index),
304 transport_id: Some(session.transport_id),
305 current_addr: Some(session.current_addr),
306 declaration: None,
307 ancestry: None,
308 tree_announce_min_interval_ms: 500,
309 last_tree_announce_sent_ms: 0,
310 pending_tree_announce: false,
311 inbound_filter: None,
312 filter_sequence: 0,
313 filter_received_at: 0,
314 pending_filter_update: true,
315 session_start: now,
316 session_generation: authenticated_at.max(1),
317 link_stats: session.link_stats,
318 authenticated_at,
319 last_seen: authenticated_at,
320 remote_epoch: session.remote_epoch,
321 fmp_mmp_is_initiator: session.is_initiator,
322 last_heartbeat_sent: None,
323 handshake_msg2: None,
324 replay_suppressed_count: 0,
325 consecutive_decrypt_failures: 0,
326 session_established_at: now,
327 rekey_jitter_secs: draw_rekey_jitter(),
328 current_k_bit: false,
329 previous_session: None,
330 previous_our_index: None,
331 drain_started: None,
332 pending_new_session: None,
333 pending_our_index: None,
334 pending_their_index: None,
335 pending_rekey_initiator: false,
336 pending_rekey_completed_at: None,
337 rekey_in_progress: false,
338 last_peer_rekey: None,
339 rekey_handshake: None,
340 rekey_our_index: None,
341 rekey_msg1: None,
342 rekey_msg1_next_resend: 0,
343 rekey_msg1_resend_count: 0,
344 }
345 }
346
347 pub fn identity(&self) -> &PeerIdentity {
351 &self.identity
352 }
353
354 pub fn node_addr(&self) -> &NodeAddr {
356 self.identity.node_addr()
357 }
358
359 pub fn address(&self) -> &FipsAddress {
361 self.identity.address()
362 }
363
364 pub fn pubkey(&self) -> XOnlyPublicKey {
366 self.identity.pubkey()
367 }
368
369 pub fn npub(&self) -> String {
371 self.identity.npub()
372 }
373
374 pub fn link_id(&self) -> LinkId {
378 self.link_id
379 }
380
381 pub fn connectivity(&self) -> ConnectivityState {
383 self.connectivity
384 }
385
386 pub fn can_send(&self) -> bool {
388 self.connectivity.can_send()
389 }
390
391 pub fn is_healthy(&self) -> bool {
393 self.connectivity.is_healthy()
394 }
395
396 pub fn is_disconnected(&self) -> bool {
398 self.connectivity.is_terminal()
399 }
400
401 pub fn has_session(&self) -> bool {
405 self.noise_session.is_some()
406 }
407
408 pub fn noise_session(&self) -> Option<&NoiseSession> {
410 self.noise_session.as_ref()
411 }
412
413 pub fn noise_session_mut(&mut self) -> Option<&mut NoiseSession> {
415 self.noise_session.as_mut()
416 }
417
418 pub fn our_index(&self) -> Option<SessionIndex> {
420 self.our_index
421 }
422
423 pub fn their_index(&self) -> Option<SessionIndex> {
425 self.their_index
426 }
427
428 pub fn set_their_index(&mut self, index: SessionIndex) {
432 self.their_index = Some(index);
433 }
434
435 pub fn replace_session(
445 &mut self,
446 new_session: NoiseSession,
447 new_our_index: SessionIndex,
448 new_their_index: SessionIndex,
449 ) -> Option<SessionIndex> {
450 self.reset_replay_suppressed();
451 let old_our_index = self.our_index;
452 self.noise_session = Some(new_session);
453 self.our_index = Some(new_our_index);
454 self.their_index = Some(new_their_index);
455 old_our_index
456 }
457
458 pub fn transport_id(&self) -> Option<TransportId> {
460 self.transport_id
461 }
462
463 pub fn current_addr(&self) -> Option<&TransportAddr> {
465 self.current_addr.as_ref()
466 }
467
468 pub fn set_current_addr(&mut self, transport_id: TransportId, addr: &TransportAddr) -> bool {
482 if self.transport_id == Some(transport_id) && self.current_addr.as_ref() == Some(addr) {
483 return false;
484 }
485 self.transport_id = Some(transport_id);
486 self.current_addr = Some(addr.clone());
487 true
488 }
489
490 pub fn set_handshake_msg2(&mut self, msg2: Vec<u8>) {
494 self.handshake_msg2 = Some(msg2);
495 }
496
497 pub fn handshake_msg2(&self) -> Option<&[u8]> {
499 self.handshake_msg2.as_deref()
500 }
501
502 pub fn clear_handshake_msg2(&mut self) {
504 self.handshake_msg2 = None;
505 }
506
507 pub fn increment_replay_suppressed(&mut self) -> u32 {
511 self.replay_suppressed_count += 1;
512 self.replay_suppressed_count
513 }
514
515 pub fn reset_replay_suppressed(&mut self) -> u32 {
517 let count = self.replay_suppressed_count;
518 self.replay_suppressed_count = 0;
519 count
520 }
521
522 pub fn replay_suppressed_count(&self) -> u32 {
524 self.replay_suppressed_count
525 }
526
527 pub fn increment_decrypt_failures(&mut self) -> u32 {
531 self.consecutive_decrypt_failures += 1;
532 self.consecutive_decrypt_failures
533 }
534
535 pub fn reset_decrypt_failures(&mut self) {
537 self.consecutive_decrypt_failures = 0;
538 }
539
540 pub fn consecutive_decrypt_failures(&self) -> u32 {
542 self.consecutive_decrypt_failures
543 }
544
545 pub fn remote_epoch(&self) -> Option<[u8; 8]> {
549 self.remote_epoch
550 }
551
552 pub(crate) fn set_remote_epoch(&mut self, remote_epoch: Option<[u8; 8]>) {
556 self.remote_epoch = remote_epoch;
557 }
558
559 pub fn coords(&self) -> Option<&TreeCoordinate> {
563 self.ancestry.as_ref()
564 }
565
566 pub fn declaration(&self) -> Option<&ParentDeclaration> {
568 self.declaration.as_ref()
569 }
570
571 pub fn has_tree_position(&self) -> bool {
573 self.declaration.is_some() && self.ancestry.is_some()
574 }
575
576 pub fn inbound_filter(&self) -> Option<&BloomFilter> {
580 self.inbound_filter.as_ref()
581 }
582
583 pub fn filter_sequence(&self) -> u64 {
585 self.filter_sequence
586 }
587
588 pub fn filter_is_stale(&self, current_time_ms: u64, stale_threshold_ms: u64) -> bool {
590 if self.filter_received_at == 0 {
591 return true;
592 }
593 current_time_ms.saturating_sub(self.filter_received_at) > stale_threshold_ms
594 }
595
596 pub fn may_reach(&self, node_addr: &NodeAddr) -> bool {
598 match &self.inbound_filter {
599 Some(filter) => filter.contains(node_addr),
600 None => false,
601 }
602 }
603
604 pub fn needs_filter_update(&self) -> bool {
606 self.pending_filter_update
607 }
608
609 pub fn link_stats(&self) -> &LinkStats {
613 &self.link_stats
614 }
615
616 pub fn link_stats_mut(&mut self) -> &mut LinkStats {
618 &mut self.link_stats
619 }
620
621 pub fn fmp_mmp_is_initiator(&self) -> bool {
623 self.fmp_mmp_is_initiator
624 }
625
626 pub(crate) fn set_fmp_mmp_is_initiator(&mut self, is_initiator: bool) {
627 self.fmp_mmp_is_initiator = is_initiator;
628 }
629
630 pub fn authenticated_at(&self) -> u64 {
632 self.authenticated_at
633 }
634
635 pub fn last_seen(&self) -> u64 {
637 self.last_seen
638 }
639
640 pub fn idle_time(&self, current_time_ms: u64) -> u64 {
642 current_time_ms.saturating_sub(self.last_seen)
643 }
644
645 pub fn connection_duration(&self, current_time_ms: u64) -> u64 {
647 current_time_ms.saturating_sub(self.authenticated_at)
648 }
649
650 pub fn session_elapsed_ms(&self) -> u32 {
655 self.session_start.elapsed().as_millis() as u32
656 }
657
658 pub fn session_generation(&self) -> u64 {
660 self.session_generation
661 }
662
663 pub fn session_start(&self) -> Instant {
665 self.session_start
666 }
667
668 pub fn last_heartbeat_sent(&self) -> Option<Instant> {
672 self.last_heartbeat_sent
673 }
674
675 pub fn mark_heartbeat_sent(&mut self, now: Instant) {
677 self.last_heartbeat_sent = Some(now);
678 }
679
680 pub fn touch(&mut self, current_time_ms: u64) {
684 self.last_seen = current_time_ms;
685 if self.connectivity == ConnectivityState::Stale {
689 self.connectivity = ConnectivityState::Connected;
690 }
691 }
692
693 pub fn mark_stale(&mut self) {
695 if self.connectivity == ConnectivityState::Connected {
696 self.connectivity = ConnectivityState::Stale;
697 }
698 }
699
700 pub fn mark_reconnecting(&mut self) {
702 self.connectivity = ConnectivityState::Reconnecting;
703 }
704
705 pub fn mark_disconnected(&mut self) {
707 self.connectivity = ConnectivityState::Disconnected;
708 }
709
710 pub fn mark_connected(&mut self, current_time_ms: u64) {
712 self.connectivity = ConnectivityState::Connected;
713 self.last_seen = current_time_ms;
714 }
715
716 pub fn set_link_id(&mut self, link_id: LinkId) {
718 self.link_id = link_id;
719 }
720
721 pub fn update_tree_position(
729 &mut self,
730 declaration: ParentDeclaration,
731 ancestry: TreeCoordinate,
732 ) {
733 self.declaration = Some(declaration);
734 self.ancestry = Some(ancestry);
735 }
736
737 pub fn clear_tree_position(&mut self) {
739 self.declaration = None;
740 self.ancestry = None;
741 }
742
743 pub fn set_tree_announce_min_interval_ms(&mut self, ms: u64) {
747 self.tree_announce_min_interval_ms = ms;
748 }
749
750 pub fn last_tree_announce_sent_ms(&self) -> u64 {
752 self.last_tree_announce_sent_ms
753 }
754
755 pub fn set_last_tree_announce_sent_ms(&mut self, ms: u64) {
757 self.last_tree_announce_sent_ms = ms;
758 }
759
760 pub fn can_send_tree_announce(&self, now_ms: u64) -> bool {
762 now_ms.saturating_sub(self.last_tree_announce_sent_ms) >= self.tree_announce_min_interval_ms
763 }
764
765 pub fn record_tree_announce_sent(&mut self, now_ms: u64) {
767 self.last_tree_announce_sent_ms = now_ms;
768 self.pending_tree_announce = false;
769 }
770
771 pub fn mark_tree_announce_pending(&mut self) {
773 self.pending_tree_announce = true;
774 }
775
776 pub fn has_pending_tree_announce(&self) -> bool {
778 self.pending_tree_announce
779 }
780
781 pub fn update_filter(&mut self, filter: BloomFilter, sequence: u64, current_time_ms: u64) {
785 self.inbound_filter = Some(filter);
786 self.filter_sequence = sequence;
787 self.filter_received_at = current_time_ms;
788 self.last_seen = current_time_ms;
789 }
790
791 pub fn clear_filter(&mut self) {
793 self.inbound_filter = None;
794 self.filter_sequence = 0;
795 self.filter_received_at = 0;
796 }
797
798 pub fn mark_filter_update_needed(&mut self) {
800 self.pending_filter_update = true;
801 }
802
803 pub fn clear_filter_update_needed(&mut self) {
805 self.pending_filter_update = false;
806 }
807}
808
809mod rekey;
810
811#[cfg(test)]
812mod tests;