hashtree_cli/
p2p_common.rs1use std::path::PathBuf;
2use std::sync::Arc;
3
4use crate::config::Config;
5use crate::socialgraph;
6use crate::webrtc::{
7 BluetoothConfig, MulticastConfig, PeerClassifier, PeerPool, WebRTCConfig, WifiAwareConfig,
8};
9
10fn relay_is_loopback(relay: &str) -> bool {
11 relay.contains("://127.0.0.1") || relay.contains("://localhost") || relay.contains("://[::1]")
12}
13
14pub fn peer_router_enabled(config: &Config) -> bool {
15 config.server.enable_webrtc
16 || (config.server.enable_multicast && config.server.max_multicast_peers > 0)
17 || (config.server.enable_wifi_aware && config.server.max_wifi_aware_peers > 0)
18 || (config.server.enable_bluetooth && config.server.max_bluetooth_peers > 0)
19}
20
21pub fn should_start_stun_server(config: &Config) -> bool {
22 config.server.enable_webrtc && config.server.stun_port > 0
23}
24
25pub fn default_webrtc_config(config: &Config) -> WebRTCConfig {
27 let active_relays = config.nostr.active_relays();
28 let local_only_relays =
29 !active_relays.is_empty() && active_relays.iter().all(|relay| relay_is_loopback(relay));
30 let relays = if config.server.enable_webrtc {
31 active_relays
32 } else {
33 Vec::new()
34 };
35 let stun_servers =
36 if !config.server.enable_webrtc || (config.server.enable_multicast && local_only_relays) {
37 Vec::new()
38 } else {
39 WebRTCConfig::default().stun_servers
40 };
41
42 WebRTCConfig {
43 relays,
44 signaling_enabled: config.server.enable_webrtc,
45 stun_servers,
46 multicast: MulticastConfig {
47 enabled: config.server.enable_multicast,
48 group: config.server.multicast_group.clone(),
49 port: config.server.multicast_port,
50 max_peers: config.server.max_multicast_peers,
51 ..Default::default()
52 },
53 wifi_aware: WifiAwareConfig {
54 enabled: config.server.enable_wifi_aware,
55 max_peers: config.server.max_wifi_aware_peers,
56 ..Default::default()
57 },
58 bluetooth: BluetoothConfig {
59 enabled: config.server.enable_bluetooth,
60 max_peers: config.server.max_bluetooth_peers,
61 },
62 ..Default::default()
63 }
64}
65
66pub fn build_peer_classifier(
68 data_dir: PathBuf,
69 store: Arc<dyn socialgraph::SocialGraphBackend>,
70) -> PeerClassifier {
71 let contacts_file = data_dir.join("contacts.json");
72 Arc::new(move |pubkey_hex: &str| {
73 if contacts_file.exists() {
74 if let Ok(data) = std::fs::read_to_string(&contacts_file) {
75 if let Ok(contacts) = serde_json::from_str::<Vec<String>>(&data) {
76 if contacts.contains(&pubkey_hex.to_string()) {
77 return PeerPool::Follows;
78 }
79 }
80 }
81 }
82 if let Ok(pk_bytes) = hex::decode(pubkey_hex) {
83 if pk_bytes.len() == 32 {
84 let pk: [u8; 32] = pk_bytes.try_into().unwrap();
85 if let Some(dist) = socialgraph::get_follow_distance(store.as_ref(), &pk) {
86 if dist <= 2 {
87 return PeerPool::Follows;
88 }
89 }
90 }
91 }
92 PeerPool::Other
93 })
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn default_webrtc_config_disables_stun_for_loopback_only_multicast() {
102 let mut config = Config::default();
103 config.server.enable_multicast = true;
104 config.server.max_multicast_peers = 4;
105 config.nostr.relays = vec!["ws://127.0.0.1:8080/ws".to_string()];
106
107 let webrtc = default_webrtc_config(&config);
108 assert!(webrtc.stun_servers.is_empty());
109 }
110
111 #[test]
112 fn default_webrtc_config_keeps_stun_for_non_loopback_relays() {
113 let mut config = Config::default();
114 config.server.enable_multicast = true;
115 config.server.max_multicast_peers = 4;
116 config.nostr.relays = vec!["wss://relay.example".to_string()];
117
118 let webrtc = default_webrtc_config(&config);
119 assert!(!webrtc.stun_servers.is_empty());
120 }
121
122 #[test]
123 fn default_webrtc_config_maps_bluetooth_limits() {
124 let mut config = Config::default();
125 config.server.enable_bluetooth = true;
126 config.server.max_bluetooth_peers = 3;
127
128 let webrtc = default_webrtc_config(&config);
129 assert!(webrtc.signaling_enabled);
130 assert!(webrtc.bluetooth.enabled);
131 assert_eq!(webrtc.bluetooth.max_peers, 3);
132 }
133
134 #[test]
135 fn default_webrtc_config_maps_wifi_aware_limits() {
136 let mut config = Config::default();
137 config.server.enable_wifi_aware = true;
138 config.server.max_wifi_aware_peers = 4;
139
140 let webrtc = default_webrtc_config(&config);
141 assert!(webrtc.signaling_enabled);
142 assert!(webrtc.wifi_aware.enabled);
143 assert_eq!(webrtc.wifi_aware.max_peers, 4);
144 }
145
146 #[test]
147 fn default_webrtc_config_strips_relays_and_stun_when_webrtc_disabled() {
148 let mut config = Config::default();
149 config.server.enable_webrtc = false;
150 config.server.enable_bluetooth = true;
151 config.server.max_bluetooth_peers = 2;
152 config.server.stun_port = 3478;
153 config.nostr.relays = vec!["wss://relay.example".to_string()];
154
155 let webrtc = default_webrtc_config(&config);
156 assert!(!webrtc.signaling_enabled);
157 assert!(webrtc.relays.is_empty());
158 assert!(webrtc.stun_servers.is_empty());
159 }
160
161 #[test]
162 fn stun_server_only_starts_when_webrtc_is_enabled() {
163 let mut config = Config::default();
164 config.server.stun_port = 3478;
165
166 assert!(should_start_stun_server(&config));
167
168 config.server.enable_webrtc = false;
169 assert!(!should_start_stun_server(&config));
170 }
171
172 #[test]
173 fn peer_router_enabled_for_wifi_aware_only() {
174 let mut config = Config::default();
175 config.server.enable_webrtc = false;
176 config.server.enable_multicast = false;
177 config.server.max_multicast_peers = 0;
178 config.server.enable_bluetooth = false;
179 config.server.max_bluetooth_peers = 0;
180 config.server.enable_wifi_aware = true;
181 config.server.max_wifi_aware_peers = 2;
182
183 assert!(peer_router_enabled(&config));
184
185 config.server.max_wifi_aware_peers = 0;
186 assert!(!peer_router_enabled(&config));
187 }
188}