Skip to main content

cuprated/config/
p2p.rs

1use std::{
2    cmp::{max, min},
3    marker::PhantomData,
4    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
5    path::Path,
6    time::Duration,
7};
8
9use serde::{Deserialize, Serialize};
10
11use cuprate_helper::{cast::u64_to_usize, fs::address_book_path, network::Network};
12use cuprate_p2p::config::TransportConfig;
13use cuprate_p2p_core::{
14    transports::{Tcp, TcpServerConfig},
15    ClearNet, NetworkZone, Tor, Transport,
16};
17use cuprate_wire::OnionAddr;
18
19use super::{default::DefaultOrCustom, macros::config_struct};
20use crate::{p2p::ProxySettings, tor::TorMode};
21
22#[cfg(feature = "arti")]
23use {
24    arti_client::{
25        config::onion_service::{OnionServiceConfig, OnionServiceConfigBuilder},
26        TorClient, TorClientBuilder, TorClientConfig,
27    },
28    cuprate_p2p_transport::{Arti, ArtiClientConfig, ArtiServerConfig, Socks, SocksClientConfig},
29    tor_rtcompat::PreferredRuntime,
30};
31
32config_struct! {
33    /// P2P config.
34    #[derive(Debug, Default, Deserialize, Serialize, PartialEq)]
35    #[serde(deny_unknown_fields, default)]
36    pub struct P2PConfig {
37        #[child = true]
38        /// The clear-net P2P config.
39        pub clear_net: ClearNetConfig,
40
41        #[child = true]
42        /// The tor-net P2P config.
43        pub tor_net: TorNetConfig,
44
45        #[child = true]
46        /// Block downloader config.
47        ///
48        /// The block downloader handles downloading old blocks from peers when we are behind.
49        pub block_downloader: BlockDownloaderConfig,
50    }
51}
52
53config_struct! {
54    #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
55    #[serde(deny_unknown_fields, default)]
56    pub struct BlockDownloaderConfig {
57        #[comment_out = true]
58        /// The size in bytes of the buffer between the block downloader
59        /// and the place which is consuming the downloaded blocks (`cuprated`).
60        ///
61        /// This value is an absolute maximum,
62        /// once this is reached the block downloader will pause.
63        ///
64        /// Type         | Number
65        /// Valid values | >= 0
66        /// Examples     | 1_000_000_000, 5_500_000_000, 500_000_000
67        pub buffer_bytes: DefaultOrCustom<usize>,
68
69        #[comment_out = true]
70        /// The size of the in progress queue (in bytes)
71        /// at which cuprated stops requesting more blocks.
72        ///
73        /// The value is _NOT_ an absolute maximum,
74        /// the in-progress queue could get much larger.
75        /// This value is only the value cuprated stops requesting more blocks,
76        /// if cuprated still has requests in progress,
77        /// it will still accept the response and add the blocks to the queue.
78        ///
79        /// Type         | Number
80        /// Valid values | >= 0
81        /// Examples     | 500_000_000, 1_000_000_000,
82        pub in_progress_queue_bytes: DefaultOrCustom<usize>,
83
84        #[inline = true]
85        /// The duration between checking the client pool for free peers.
86        ///
87        /// Type     | Duration
88        /// Examples | { secs = 30, nanos = 0 }, { secs = 35, nano = 123 }
89        pub check_client_pool_interval: Duration,
90
91        #[comment_out = true]
92        /// The target size of a single batch of blocks (in bytes).
93        ///
94        /// This value must be below 100_000,000,
95        /// it is not recommended to set it above 30_000_000.
96        ///
97        /// Type         | Number
98        /// Valid values | 0..100_000,000
99        pub target_batch_bytes: usize,
100    }
101}
102
103impl BlockDownloaderConfig {
104    /// Constructs the config given to the p2p crate.
105    pub fn construct_inner(
106        &self,
107        total_memory: u64,
108    ) -> cuprate_p2p::block_downloader::BlockDownloaderConfig {
109        let buffer_mem = u64_to_usize(min(total_memory / 5, 1024 * 1024 * 1024));
110
111        cuprate_p2p::block_downloader::BlockDownloaderConfig {
112            buffer_bytes: *self.buffer_bytes.value(&buffer_mem),
113            in_progress_queue_bytes: *self.in_progress_queue_bytes.value(&(buffer_mem / 2)),
114            check_client_pool_interval: self.check_client_pool_interval,
115            target_batch_bytes: self.target_batch_bytes,
116            initial_batch_len: 1,
117        }
118    }
119}
120
121impl Default for BlockDownloaderConfig {
122    fn default() -> Self {
123        Self {
124            buffer_bytes: DefaultOrCustom::Default,
125            in_progress_queue_bytes: DefaultOrCustom::Default,
126            check_client_pool_interval: Duration::from_secs(30),
127            target_batch_bytes: 15_000_000,
128        }
129    }
130}
131
132config_struct! {
133    Shared {
134        #[comment_out = true]
135        /// The number of outbound connections to make and try keep.
136        ///
137        /// It's recommended to keep this value above 12.
138        ///
139        /// Type         | Number
140        /// Valid values | >= 0
141        /// Examples     | 12, 32, 64, 100, 500
142        pub outbound_connections: usize,
143
144        #[comment_out = true]
145        /// The amount of extra connections to make if cuprated is under load.
146        ///
147        /// Type         | Number
148        /// Valid values | >= 0
149        /// Examples     | 0, 12, 32, 64, 100, 500
150        pub extra_outbound_connections: usize,
151
152        #[comment_out = true]
153        /// The maximum amount of inbound connections to allow.
154        ///
155        /// Type         | Number
156        /// Valid values | >= 0
157        /// Examples     | 0, 12, 32, 64, 100, 500
158        pub max_inbound_connections: usize,
159
160        #[comment_out = true]
161        /// The percent of connections that should be
162        /// to peers that haven't connected to before.
163        ///
164        /// 0.0 is 0%.
165        /// 1.0 is 100%.
166        ///
167        /// Type         | Floating point number
168        /// Valid values | 0.0..1.0
169        /// Examples     | 0.0, 0.5, 0.123, 0.999, 1.0
170        pub gray_peers_percent: f64,
171
172        /// The port bind to this network zone.
173        ///
174        /// This port will be bind to if the incoming P2P
175        /// server for this zone has been enabled.
176        ///
177        /// Type         | Number or "Default"
178        /// Valid values | 0..65534, "Default"
179        /// Examples     | 18080, 9999, 5432
180        pub p2p_port: DefaultOrCustom<u16>,
181
182        #[child = true]
183        /// The address book config.
184        pub address_book_config: AddressBookConfig,
185    }
186
187    /// The config values for P2P clear-net.
188    #[derive(Debug, Deserialize, Serialize, PartialEq)]
189    #[serde(deny_unknown_fields, default)]
190    pub struct ClearNetConfig {
191
192        /// Enable IPv4 inbound server.
193        ///
194        /// The inbound server will listen on port `p2p.clear_net.p2p_port`.
195        /// Setting this to `false` will disable incoming IPv4 P2P connections.
196        ///
197        /// Type         | boolean
198        /// Valid values | false, true
199        /// Examples     | false
200        pub enable_inbound: bool,
201
202        /// The IPv4 address to bind and listen for connections on.
203        ///
204        /// Type     | IPv4 address
205        /// Examples | "0.0.0.0", "192.168.1.50"
206        pub listen_on: Ipv4Addr,
207
208        /// Enable IPv6 inbound server.
209        ///
210        /// The inbound server will listen on port `p2p.clear_net.p2p_port`.
211        /// Setting this to `false` will disable incoming IPv6 P2P connections.
212        ///
213        /// Type         | boolean
214        /// Valid values | false, true
215        /// Examples     | false
216        pub enable_inbound_v6: bool,
217
218        /// The IPv6 address to bind and listen for connections on.
219        ///
220        /// Type     | IPv6 address
221        /// Examples | "::", "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
222        pub listen_on_v6: Ipv6Addr,
223
224        #[comment_out = true]
225        /// The proxy to use for outgoing P2P connections
226        ///
227        /// Setting this to "Tor" will anonymise clearnet connections through Tor.
228        ///
229        /// Setting this to "" (an empty string) will disable the proxy.
230        ///
231        /// Enabling this setting will disable inbound connections.
232        ///
233        /// Type         | String
234        /// Valid values | "Tor", "socks5://ip:port", "socks5://user:pass@ip:port"
235        /// Examples     | "Tor", "socks5://127.0.0.1:9050"
236        pub proxy: ProxySettings,
237
238        #[comment_out = true]
239        /// Extra seed nodes to connect to on startup, in addition to the
240        /// network's built-in seeds. Given as "ip:port" socket addresses.
241        ///
242        /// FakeChain/regtest ships no built-in seeds, so a private or isolated
243        /// network relies entirely on this list to bootstrap.
244        ///
245        /// Type     | Array of socket addresses
246        /// Examples | "1.2.3.4:18080", "5.6.7.8:18080"
247        pub seed_nodes: Vec<SocketAddr>,
248    }
249
250    /// The config values for P2P tor.
251    #[derive(Debug, Deserialize, Serialize, PartialEq)]
252    #[serde(deny_unknown_fields, default)]
253    pub struct TorNetConfig {
254
255        #[comment_out = true]
256        /// Enable the Tor P2P network.
257        ///
258        /// Type         | boolean
259        /// Valid values | false, true
260        /// Examples     | false
261        pub enabled: bool,
262
263        #[comment_out = true]
264        /// Enable Tor inbound onion server.
265        ///
266        /// In Arti mode, setting this to `true` will enable Arti's onion service for accepting inbound
267        /// Tor P2P connections. The keypair and therefore onion address is generated randomly on first run.
268        ///
269        /// In Daemon mode, setting this to `true` will enable a TCP server listening for inbound connections
270        /// from your Tor daemon. Refer to the `tor.anonymous_inbound` and `tor.listening_addr` field for onion address
271        /// and listening configuration.
272        ///
273        /// The server will listen on port `p2p.tor_net.p2p_port`
274        ///
275        /// Type         | boolean
276        /// Valid values | false, true
277        /// Examples     | false
278        pub inbound_onion: bool,
279
280        #[comment_out = true]
281        /// Extra Tor seed nodes to connect to on startup, in addition to the
282        /// built-in seeds. Given as onion addresses.
283        ///
284        /// Type     | Array of onion addresses
285        /// Examples | "zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083"
286        pub seed_nodes: Vec<OnionAddr>,
287    }
288}
289
290/// Gets the port to listen on for p2p connections.
291pub const fn p2p_port(setting: DefaultOrCustom<u16>, network: Network) -> u16 {
292    match setting {
293        DefaultOrCustom::Default => match network {
294            Network::Mainnet | Network::FakeChain => 18080,
295            Network::Stagenet => 38080,
296            Network::Testnet => 28080,
297        },
298        DefaultOrCustom::Custom(port) => port,
299    }
300}
301
302impl ClearNetConfig {
303    /// Gets the transport config for [`ClearNet`] over [`Tcp`].
304    pub fn tcp_transport_config(&self, network: Network) -> TransportConfig<ClearNet, Tcp> {
305        let server_config = if self.enable_inbound {
306            let mut sc = TcpServerConfig::default();
307            sc.ipv4 = Some(self.listen_on);
308            sc.ipv6 = self.enable_inbound_v6.then_some(self.listen_on_v6);
309            sc.port = p2p_port(self.p2p_port, network);
310            Some(sc)
311        } else {
312            None
313        };
314
315        TransportConfig {
316            client_config: (),
317            server_config,
318        }
319    }
320}
321
322impl Default for ClearNetConfig {
323    fn default() -> Self {
324        Self {
325            p2p_port: DefaultOrCustom::Default,
326            enable_inbound: true,
327            listen_on: Ipv4Addr::UNSPECIFIED,
328            enable_inbound_v6: false,
329            listen_on_v6: Ipv6Addr::UNSPECIFIED,
330            proxy: ProxySettings::Disabled,
331            seed_nodes: Vec::new(),
332            outbound_connections: 32,
333            extra_outbound_connections: 8,
334            max_inbound_connections: 128,
335            gray_peers_percent: 0.3,
336            address_book_config: AddressBookConfig::default(),
337        }
338    }
339}
340
341impl Default for TorNetConfig {
342    fn default() -> Self {
343        Self {
344            enabled: false,
345            inbound_onion: false,
346            seed_nodes: Vec::new(),
347            p2p_port: DefaultOrCustom::Default,
348            outbound_connections: 12,
349            extra_outbound_connections: 2,
350            max_inbound_connections: 128,
351            gray_peers_percent: 0.3,
352            address_book_config: AddressBookConfig::default(),
353        }
354    }
355}
356
357config_struct! {
358    /// The addressbook config exposed to users.
359    #[derive(Debug, Deserialize, Serialize, Eq, PartialEq)]
360    #[serde(deny_unknown_fields, default)]
361    pub struct AddressBookConfig {
362        /// The size of the white peer list.
363        ///
364        /// The white list holds peers that have been connected to before.
365        ///
366        /// Type         | Number
367        /// Valid values | >= 0
368        /// Examples     | 1000, 500, 241
369        pub max_white_list_length: usize,
370
371        /// The size of the gray peer list.
372        ///
373        /// The gray peer list holds peers that have been
374        /// told about but not connected to cuprated.
375        ///
376        /// Type         | Number
377        /// Valid values | >= 0
378        /// Examples     | 1000, 500, 241
379        pub max_gray_list_length: usize,
380
381        #[inline = true]
382        /// The time period between address book saves.
383        ///
384        /// Type     | Duration
385        /// Examples | { secs = 90, nanos = 0 }, { secs = 100, nano = 123 }
386        pub peer_save_period: Duration,
387    }
388}
389
390impl Default for AddressBookConfig {
391    fn default() -> Self {
392        Self {
393            max_white_list_length: 1_000,
394            max_gray_list_length: 5_000,
395            peer_save_period: Duration::from_secs(90),
396        }
397    }
398}
399
400impl AddressBookConfig {
401    /// Returns the [`cuprate_address_book::AddressBookConfig`].
402    pub fn address_book_config<Z: NetworkZone>(
403        &self,
404        cache_dir: &Path,
405        network: Network,
406        our_own_address: Option<Z::Addr>,
407    ) -> cuprate_address_book::AddressBookConfig<Z> {
408        cuprate_address_book::AddressBookConfig {
409            max_white_list_length: self.max_white_list_length,
410            max_gray_list_length: self.max_gray_list_length,
411            peer_store_directory: address_book_path(cache_dir, network),
412            peer_save_period: self.peer_save_period,
413            our_own_address,
414        }
415    }
416}
417
418/// Seed nodes for [`ClearNet`].
419pub fn clear_net_seed_nodes(network: Network) -> Vec<SocketAddr> {
420    let seeds = match network {
421        Network::FakeChain => [].as_slice(),
422        Network::Mainnet => [
423            "176.9.0.187:18080",
424            "88.198.163.90:18080",
425            "66.85.74.134:18080",
426            "51.79.173.165:18080",
427            "192.99.8.110:18080",
428            "37.187.74.171:18080",
429            "77.172.183.193:18080",
430        ]
431        .as_slice(),
432        Network::Stagenet => [
433            "176.9.0.187:38080",
434            "51.79.173.165:38080",
435            "192.99.8.110:38080",
436            "37.187.74.171:38080",
437            "77.172.183.193:38080",
438        ]
439        .as_slice(),
440        Network::Testnet => [
441            "176.9.0.187:28080",
442            "51.79.173.165:28080",
443            "192.99.8.110:28080",
444            "37.187.74.171:28080",
445            "77.172.183.193:28080",
446        ]
447        .as_slice(),
448    };
449
450    seeds
451        .iter()
452        .map(|s| s.parse())
453        .collect::<Result<_, _>>()
454        .unwrap()
455}
456
457/// Seed nodes for `Tor`.
458pub fn tor_net_seed_nodes(network: Network) -> Vec<OnionAddr> {
459    let seeds = match network {
460        Network::Mainnet => [
461            "zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083",
462            "lykcas4tus7mkm4bhsgqe4drtd4awi7gja24goscc47xfgzj54yofyqd.onion:18083",
463            "plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:18083",
464            "plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:18083",
465            "plowsofe6cleftfmk2raiw5h2x66atrik3nja4bfd3zrfa2hdlgworad.onion:18083",
466            "aclc4e2jhhtr44guufbnwk5bzwhaecinax4yip4wr4tjn27sjsfg6zqd.onion:18083",
467        ]
468        .as_slice(),
469        Network::FakeChain | Network::Stagenet | Network::Testnet => [].as_slice(),
470    };
471
472    seeds
473        .iter()
474        .map(|s| s.parse())
475        .collect::<Result<_, _>>()
476        .unwrap()
477}