refactor(peer): stop storing peer connectivity

Removes the stored field, its four setters, the promotion in touch that
could not fire, and connectivity(). ConnectivityState is reduced to the two
values the control socket reports, which the node now derives from idle
time. ConnectivityState::is_terminal and ActivePeer::is_disconnected stay
and return false, as they always did in the daemon.

Adds the CHANGELOG Removed entry for the library-surface change.
This commit is contained in:
Johnathan Corgan
2026-09-14 15:49:13 +00:00
parent ad3fcc3081
commit e6b0725af1
2 changed files with 30 additions and 70 deletions
+16
View File
@@ -285,6 +285,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
binder tearing down and rebinding every second while teardown silently
declined to abort anything.
### Removed
- **Source-breaking for consumers of the library crate**: `ActivePeer` no
longer stores a connectivity state. `ActivePeer::connectivity`, `can_send`,
`is_healthy`, `mark_stale`, `mark_reconnecting`, `mark_disconnected` and
`mark_connected` are gone. `ConnectivityState` stays public with only
`Connected` and `Stale`, the values `show_peers` reports, and loses
`can_send` and `is_healthy`. `ConnectivityState::is_terminal`,
`ActivePeer::is_disconnected`, `Node::sendable_peers` and
`Node::sendable_peer_count` keep their signatures and the results they
always had in the daemon: the first two return `false`, and the last two
cover every peer. Nothing in the daemon changed the stored state after a
peer was promoted, so every removed check was already true and the shipped
binaries behave as before. The peer wire and the control-socket response
shape are unchanged.
### Fixed
#### Node lifecycle
+14 -70
View File
@@ -22,25 +22,24 @@ fn draw_rekey_jitter() -> i64 {
rand::rng().random_range(-REKEY_JITTER_SECS..=REKEY_JITTER_SECS)
}
/// Connectivity state for an active peer.
/// Connectivity of an active peer, as the control socket reports it.
///
/// This is simpler than the full PeerState since authentication is complete.
/// Not stored on the peer: the node derives it from how long the peer has
/// been silent, compared with the configured heartbeat interval.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ConnectivityState {
/// Peer is fully connected and responsive.
/// Heard from within the heartbeat interval.
Connected,
/// Peer hasn't been heard from recently (potential timeout).
/// Silent for longer than the heartbeat interval.
Stale,
/// Connection lost, attempting to reconnect.
Reconnecting,
/// Peer has been explicitly disconnected.
Disconnected,
}
impl ConnectivityState {
/// Check if this is a terminal state requiring cleanup.
///
/// Always false: neither derived state is terminal.
pub fn is_terminal(&self) -> bool {
matches!(self, ConnectivityState::Disconnected)
false
}
}
@@ -49,8 +48,6 @@ impl fmt::Display for ConnectivityState {
let s = match self {
ConnectivityState::Connected => "connected",
ConnectivityState::Stale => "stale",
ConnectivityState::Reconnecting => "reconnecting",
ConnectivityState::Disconnected => "disconnected",
};
write!(f, "{}", s)
}
@@ -199,10 +196,6 @@ pub struct ActivePeer {
/// Immutable for the same reason as [`ActivePeer::npub`].
short_npub: String,
// === Connection ===
/// Current connectivity state.
connectivity: ConnectivityState,
// === Spanning Tree ===
/// Their latest parent declaration.
declaration: Option<ParentDeclaration>,
@@ -293,7 +286,6 @@ impl ActivePeer {
npub: identity.npub(),
short_npub: identity.short_npub(),
identity,
connectivity: ConnectivityState::Connected,
declaration: None,
ancestry: None,
tree_announce_min_interval_ms: 500,
@@ -373,7 +365,6 @@ impl ActivePeer {
npub: identity.npub(),
short_npub: identity.short_npub(),
identity,
connectivity: ConnectivityState::Connected,
declaration: None,
ancestry: None,
tree_announce_min_interval_ms: 500,
@@ -488,14 +479,12 @@ impl ActivePeer {
self.send.link_id
}
/// Get the connectivity state.
pub fn connectivity(&self) -> ConnectivityState {
self.connectivity
}
/// Check if peer is disconnected.
///
/// Always false: the peer stores no connectivity state, and a peer that
/// goes away is removed from the node rather than marked.
pub fn is_disconnected(&self) -> bool {
self.connectivity.is_terminal()
false
}
// === Session Accessors ===
@@ -817,33 +806,6 @@ impl ActivePeer {
/// Update last seen timestamp.
pub fn touch(&mut self, current_time_ms: u64) {
self.send.last_seen = current_time_ms;
// If we were stale, receiving traffic makes us connected again
if self.connectivity == ConnectivityState::Stale {
self.connectivity = ConnectivityState::Connected;
}
}
/// Mark peer as stale (no recent traffic).
pub fn mark_stale(&mut self) {
if self.connectivity == ConnectivityState::Connected {
self.connectivity = ConnectivityState::Stale;
}
}
/// Mark peer as reconnecting.
pub fn mark_reconnecting(&mut self) {
self.connectivity = ConnectivityState::Reconnecting;
}
/// Mark peer as disconnected.
pub fn mark_disconnected(&mut self) {
self.connectivity = ConnectivityState::Disconnected;
}
/// Mark peer as connected (e.g., after successful reconnect).
pub fn mark_connected(&mut self, current_time_ms: u64) {
self.connectivity = ConnectivityState::Connected;
self.send.last_seen = current_time_ms;
}
/// Update the link ID (e.g., on reconnect).
@@ -1302,8 +1264,8 @@ mod tests {
#[test]
fn test_connectivity_state_properties() {
assert!(ConnectivityState::Disconnected.is_terminal());
assert!(!ConnectivityState::Connected.is_terminal());
assert!(!ConnectivityState::Stale.is_terminal());
}
#[test]
@@ -1313,6 +1275,7 @@ mod tests {
assert_eq!(peer.identity().node_addr(), identity.node_addr());
assert_eq!(peer.link_id(), LinkId::new(1));
assert!(!peer.is_disconnected());
assert_eq!(peer.authenticated_at(), 1000);
assert!(peer.needs_filter_update()); // New peers need filter
}
@@ -1371,25 +1334,6 @@ mod tests {
assert_eq!(short_first, short_second);
}
#[test]
fn test_connectivity_transitions() {
let identity = make_peer_identity();
let mut peer = ActivePeer::new(identity, LinkId::new(1), 1000);
peer.mark_stale();
assert_eq!(peer.connectivity(), ConnectivityState::Stale);
// Traffic received brings back to connected
peer.touch(2000);
peer.mark_reconnecting();
peer.mark_connected(3000);
peer.mark_disconnected();
assert!(peer.is_disconnected());
}
#[test]
fn test_tree_position() {
let identity = make_peer_identity();