From 1c1ed0d93918a4b5edd9ee2c9c7bfddb5619ce13 Mon Sep 17 00:00:00 2001 From: Johnathan Corgan Date: Fri, 10 Jul 2026 16:52:18 +0000 Subject: [PATCH] Relocate PromotionResult into proto/fmp Move the PromotionResult enum (and its impl) out of peer::mod and into proto/fmp/core.rs, alongside the cross_connection_winner tie-break helper that was relocated the same way. This is FMP connection-lifecycle result vocabulary, so it belongs in the FMP subsystem home rather than the peer module. Behavior-neutral pure type relocation: consumers import it from crate::proto::fmp, and the crate-root public path crate::PromotionResult is preserved via a re-export in lib.rs (mirroring cross_connection_winner). Full lib suite green at baseline. --- src/lib.rs | 5 ++- src/node/handlers/handshake.rs | 4 +-- src/node/tests/session.rs | 2 +- src/node/tests/unit.rs | 2 +- src/peer/mod.rs | 61 +--------------------------------- src/proto/fmp/core.rs | 56 +++++++++++++++++++++++++++++++ src/proto/fmp/mod.rs | 2 +- 7 files changed, 64 insertions(+), 68 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 83ed616..47210f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,13 +88,12 @@ pub use proto::fmp::HandshakeMessageType; // Re-export cache types pub use cache::{CacheEntry, CacheError, CacheStats, CoordCache}; -// Re-export FMP tie-break helper (relocated from peer:: to proto::fmp) -pub use proto::fmp::cross_connection_winner; +// Re-export FMP tie-break helper and promotion result (relocated from peer:: to proto::fmp) +pub use proto::fmp::{PromotionResult, cross_connection_winner}; // Re-export peer types pub use peer::{ ActivePeer, ConnectivityState, HandshakeState, PeerConnection, PeerError, PeerSlot, - PromotionResult, }; // Re-export node types diff --git a/src/node/handlers/handshake.rs b/src/node/handlers/handshake.rs index 189dca1..da8a40b 100644 --- a/src/node/handlers/handshake.rs +++ b/src/node/handlers/handshake.rs @@ -5,11 +5,11 @@ use crate::PeerIdentity; use crate::node::acl::PeerAclContext; use crate::node::reject::{HandshakeReject, RejectReason}; use crate::node::{Node, NodeError}; -use crate::peer::{ActivePeer, PeerConnection, PromotionResult}; +use crate::peer::{ActivePeer, PeerConnection}; use crate::proto::fmp::wire::{Msg1Header, Msg2Header, build_msg2}; use crate::proto::fmp::{ ConnAction, EstablishSnapshot, EstablishView, InboundDecision, InboundReject, OutboundDecision, - OutboundSnapshot, WireOutcome, cross_connection_winner, + OutboundSnapshot, PromotionResult, WireOutcome, cross_connection_winner, }; use crate::transport::{Link, LinkDirection, LinkId, ReceivedPacket}; use std::time::Duration; diff --git a/src/node/tests/session.rs b/src/node/tests/session.rs index b45297c..3915ea2 100644 --- a/src/node/tests/session.rs +++ b/src/node/tests/session.rs @@ -1002,7 +1002,7 @@ fn build_ipv6_packet( #[test] fn test_identity_cache_populated_on_promote() { - use crate::peer::PromotionResult; + use crate::proto::fmp::PromotionResult; let mut node = make_node(); let transport_id = TransportId::new(1); diff --git a/src/node/tests/unit.rs b/src/node/tests/unit.rs index 36ca670..9fe75ec 100644 --- a/src/node/tests/unit.rs +++ b/src/node/tests/unit.rs @@ -1,6 +1,6 @@ use super::*; use crate::nostr::{BootstrapEvent, NostrRendezvous}; -use crate::peer::PromotionResult; +use crate::proto::fmp::PromotionResult; use crate::transport::udp::UdpTransport; use crate::transport::{TransportHandle, packet_channel}; use std::sync::Arc; diff --git a/src/peer/mod.rs b/src/peer/mod.rs index 77dd871..07bcea1 100644 --- a/src/peer/mod.rs +++ b/src/peer/mod.rs @@ -59,66 +59,6 @@ pub enum PeerError { MaxPeersExceeded { max: usize }, } -// ============================================================================ -// Cross-Connection Handling -// ============================================================================ - -/// Result of attempting to promote a connection to active peer. -/// -/// When a handshake completes, we may discover that we already have a -/// connection to this peer (cross-connection). The tie-breaker rule -/// determines which connection survives. -/// -/// Note: Returns NodeAddr instead of ActivePeer because ActivePeer cannot -/// be cloned (it contains NoiseSession which has cryptographic state). -/// Callers can look up the peer from the peers map using the NodeAddr. -#[derive(Debug, Clone, Copy)] -pub enum PromotionResult { - /// New peer created successfully. - Promoted(NodeAddr), - - /// Cross-connection detected. This connection lost the tie-breaker - /// and should be closed. - CrossConnectionLost { - /// The link that won (existing connection). - winner_link_id: LinkId, - }, - - /// Cross-connection detected. This connection won the tie-breaker. - /// The existing connection was replaced. - CrossConnectionWon { - /// The link that lost (previous connection, now closed). - loser_link_id: LinkId, - /// The node ID of the peer. - node_addr: NodeAddr, - }, -} - -impl PromotionResult { - /// Get the node ID if promotion succeeded. - pub fn node_addr(&self) -> Option { - match self { - PromotionResult::Promoted(node_addr) => Some(*node_addr), - PromotionResult::CrossConnectionWon { node_addr, .. } => Some(*node_addr), - PromotionResult::CrossConnectionLost { .. } => None, - } - } - - /// Check if this connection should be closed. - pub fn should_close_this_connection(&self) -> bool { - matches!(self, PromotionResult::CrossConnectionLost { .. }) - } - - /// Get the link that should be closed, if any. - pub fn link_to_close(&self) -> Option { - match self { - PromotionResult::CrossConnectionLost { .. } => None, // Caller's link - PromotionResult::CrossConnectionWon { loser_link_id, .. } => Some(*loser_link_id), - PromotionResult::Promoted(_) => None, - } - } -} - // ============================================================================ // PeerSlot // ============================================================================ @@ -240,6 +180,7 @@ impl fmt::Display for PeerSlot { #[cfg(test)] mod tests { use super::*; + use crate::proto::fmp::PromotionResult; use crate::transport::LinkId; use crate::{Identity, PeerIdentity}; diff --git a/src/proto/fmp/core.rs b/src/proto/fmp/core.rs index dd7af64..5fa27d9 100644 --- a/src/proto/fmp/core.rs +++ b/src/proto/fmp/core.rs @@ -51,6 +51,62 @@ pub fn cross_connection_winner( } } +/// Result of attempting to promote a connection to active peer. +/// +/// When a handshake completes, we may discover that we already have a +/// connection to this peer (cross-connection). The tie-breaker rule +/// determines which connection survives. +/// +/// Note: Returns NodeAddr instead of ActivePeer because ActivePeer cannot +/// be cloned (it contains NoiseSession which has cryptographic state). +/// Callers can look up the peer from the peers map using the NodeAddr. +#[derive(Debug, Clone, Copy)] +pub enum PromotionResult { + /// New peer created successfully. + Promoted(NodeAddr), + + /// Cross-connection detected. This connection lost the tie-breaker + /// and should be closed. + CrossConnectionLost { + /// The link that won (existing connection). + winner_link_id: LinkId, + }, + + /// Cross-connection detected. This connection won the tie-breaker. + /// The existing connection was replaced. + CrossConnectionWon { + /// The link that lost (previous connection, now closed). + loser_link_id: LinkId, + /// The node ID of the peer. + node_addr: NodeAddr, + }, +} + +impl PromotionResult { + /// Get the node ID if promotion succeeded. + pub fn node_addr(&self) -> Option { + match self { + PromotionResult::Promoted(node_addr) => Some(*node_addr), + PromotionResult::CrossConnectionWon { node_addr, .. } => Some(*node_addr), + PromotionResult::CrossConnectionLost { .. } => None, + } + } + + /// Check if this connection should be closed. + pub fn should_close_this_connection(&self) -> bool { + matches!(self, PromotionResult::CrossConnectionLost { .. }) + } + + /// Get the link that should be closed, if any. + pub fn link_to_close(&self) -> Option { + match self { + PromotionResult::CrossConnectionLost { .. } => None, // Caller's link + PromotionResult::CrossConnectionWon { loser_link_id, .. } => Some(*loser_link_id), + PromotionResult::Promoted(_) => None, + } + } +} + /// A snapshot of one handshake connection's lifecycle-relevant state, taken by /// the shell so the core decides without touching live `Node` state or reading /// a clock. diff --git a/src/proto/fmp/mod.rs b/src/proto/fmp/mod.rs index dc58f0f..c719921 100644 --- a/src/proto/fmp/mod.rs +++ b/src/proto/fmp/mod.rs @@ -33,12 +33,12 @@ pub(crate) mod wire; #[cfg(test)] mod tests; -pub use core::cross_connection_winner; pub(crate) use core::{ ConnAction, ConnSnapshot, EstablishSnapshot, EstablishView, InboundDecision, InboundReject, LifecycleView, OutboundDecision, OutboundSnapshot, PeerSnapshot, RekeyCfg, RekeyResendSnapshot, WireOutcome, }; +pub use core::{PromotionResult, cross_connection_winner}; pub(crate) use limits::backoff_ms; pub use state::HandshakeState; pub(crate) use state::{ConnectionState, Fmp};