Skip to main content

hashtree_cli/
p2p_common.rs

1use 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
25/// Build default WebRTC config from daemon/app config.
26pub 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        hash_get_enabled: config.server.mode.hash_get_enabled(),
46        stun_servers,
47        multicast: MulticastConfig {
48            enabled: config.server.enable_multicast,
49            group: config.server.multicast_group.clone(),
50            port: config.server.multicast_port,
51            max_peers: config.server.max_multicast_peers,
52            ..Default::default()
53        },
54        wifi_aware: WifiAwareConfig {
55            enabled: config.server.enable_wifi_aware,
56            max_peers: config.server.max_wifi_aware_peers,
57            ..Default::default()
58        },
59        bluetooth: BluetoothConfig {
60            enabled: config.server.enable_bluetooth,
61            max_peers: config.server.max_bluetooth_peers,
62        },
63        ..Default::default()
64    }
65}
66
67/// Build peer classifier used by daemon/runtime startup paths.
68pub fn build_peer_classifier(
69    data_dir: PathBuf,
70    store: Arc<dyn socialgraph::SocialGraphBackend>,
71) -> PeerClassifier {
72    let contacts_file = data_dir.join("contacts.json");
73    Arc::new(move |pubkey_hex: &str| {
74        if contacts_file.exists() {
75            if let Ok(data) = std::fs::read_to_string(&contacts_file) {
76                if let Ok(contacts) = serde_json::from_str::<Vec<String>>(&data) {
77                    if contacts.contains(&pubkey_hex.to_string()) {
78                        return PeerPool::Follows;
79                    }
80                }
81            }
82        }
83        if let Ok(pk_bytes) = hex::decode(pubkey_hex) {
84            if pk_bytes.len() == 32 {
85                let pk: [u8; 32] = pk_bytes.try_into().unwrap();
86                if let Some(dist) = socialgraph::get_follow_distance(store.as_ref(), &pk) {
87                    if dist <= 2 {
88                        return PeerPool::Follows;
89                    }
90                }
91            }
92        }
93        PeerPool::Other
94    })
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn default_webrtc_config_disables_stun_for_loopback_only_multicast() {
103        let mut config = Config::default();
104        config.server.enable_multicast = true;
105        config.server.max_multicast_peers = 4;
106        config.nostr.relays = vec!["ws://127.0.0.1:8080/ws".to_string()];
107
108        let webrtc = default_webrtc_config(&config);
109        assert!(webrtc.stun_servers.is_empty());
110    }
111
112    #[test]
113    fn default_webrtc_config_keeps_stun_for_non_loopback_relays() {
114        let mut config = Config::default();
115        config.server.enable_multicast = true;
116        config.server.max_multicast_peers = 4;
117        config.nostr.relays = vec!["wss://relay.example".to_string()];
118
119        let webrtc = default_webrtc_config(&config);
120        assert!(!webrtc.stun_servers.is_empty());
121    }
122
123    #[test]
124    fn default_webrtc_config_maps_bluetooth_limits() {
125        let mut config = Config::default();
126        config.server.enable_bluetooth = true;
127        config.server.max_bluetooth_peers = 3;
128
129        let webrtc = default_webrtc_config(&config);
130        assert!(webrtc.signaling_enabled);
131        assert!(webrtc.bluetooth.enabled);
132        assert_eq!(webrtc.bluetooth.max_peers, 3);
133    }
134
135    #[test]
136    fn default_webrtc_config_maps_wifi_aware_limits() {
137        let mut config = Config::default();
138        config.server.enable_wifi_aware = true;
139        config.server.max_wifi_aware_peers = 4;
140
141        let webrtc = default_webrtc_config(&config);
142        assert!(webrtc.signaling_enabled);
143        assert!(webrtc.wifi_aware.enabled);
144        assert_eq!(webrtc.wifi_aware.max_peers, 4);
145    }
146
147    #[test]
148    fn default_webrtc_config_strips_relays_and_stun_when_webrtc_disabled() {
149        let mut config = Config::default();
150        config.server.enable_webrtc = false;
151        config.server.enable_bluetooth = true;
152        config.server.max_bluetooth_peers = 2;
153        config.server.stun_port = 3478;
154        config.nostr.relays = vec!["wss://relay.example".to_string()];
155
156        let webrtc = default_webrtc_config(&config);
157        assert!(!webrtc.signaling_enabled);
158        assert!(webrtc.relays.is_empty());
159        assert!(webrtc.stun_servers.is_empty());
160    }
161
162    #[test]
163    fn stun_server_only_starts_when_webrtc_is_enabled() {
164        let mut config = Config::default();
165        config.server.stun_port = 3478;
166
167        assert!(should_start_stun_server(&config));
168
169        config.server.enable_webrtc = false;
170        assert!(!should_start_stun_server(&config));
171    }
172
173    #[test]
174    fn peer_router_enabled_for_wifi_aware_only() {
175        let mut config = Config::default();
176        config.server.enable_webrtc = false;
177        config.server.enable_multicast = false;
178        config.server.max_multicast_peers = 0;
179        config.server.enable_bluetooth = false;
180        config.server.max_bluetooth_peers = 0;
181        config.server.enable_wifi_aware = true;
182        config.server.max_wifi_aware_peers = 2;
183
184        assert!(peer_router_enabled(&config));
185
186        config.server.max_wifi_aware_peers = 0;
187        assert!(!peer_router_enabled(&config));
188    }
189}