Skip to main content

cuprated/config/
p2p.rs

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