darkfi/net/
settings.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
/* This file is part of DarkFi (https://dark.fi)
 *
 * Copyright (C) 2020-2025 Dyne.org foundation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use structopt::StructOpt;
use url::Url;

type BlacklistEntry = (String, Vec<String>, Vec<u16>);

/// Ban policies definitions.
///
/// If the ban policy is set to `Relaxed` will not ban peers in case
/// they send a message without a corresponding MessageDispatcher.
/// This is useful for nodes that may not be subscribed to protocols,
/// such as Lilith. For most uses this should be set to `Strict`.
///
/// TODO: this will be deprecated when we introduce the p2p resource
/// mananger.
#[derive(Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "lowercase")]
pub enum BanPolicy {
    #[default]
    Strict,

    Relaxed,
}

/// P2P network settings. The scope of this is a P2P network instance
/// configured by the library user.
#[derive(Debug, Clone)]
pub struct Settings {
    /// Only used for debugging, compromises privacy when set
    pub node_id: String,
    /// P2P accept addresses the instance listens on for inbound connections
    pub inbound_addrs: Vec<Url>,
    /// P2P external addresses the instance advertises so other peers can
    /// reach us and connect to us, as long as inbound addrs are configured
    pub external_addrs: Vec<Url>,
    /// Peer nodes to manually connect to
    pub peers: Vec<Url>,
    /// Seed nodes to connect to for peer discovery and/or adversising our
    /// own external addresses
    pub seeds: Vec<Url>,
    /// Magic bytes should be unique per P2P network.
    /// Avoid bleeding of networks.
    pub magic_bytes: MagicBytes,
    /// Application version, used for convenient protocol matching
    pub app_version: semver::Version,
    /// Whitelisted network transports for outbound connections
    pub allowed_transports: Vec<String>,
    /// Allow transport mixing (e.g. Tor would be allowed to connect to `tcp://`)
    pub transport_mixing: bool,
    /// Outbound connection slots number, this many connections will be
    /// attempted. (This does not include manual connections)
    pub outbound_connections: usize,
    /// Inbound connection slots number, this many active listening connections
    /// will be allowed. (This does not include manual connections)
    pub inbound_connections: usize,
    /// Outbound connection timeout (in seconds)
    pub outbound_connect_timeout: u64,
    /// Exchange versions (handshake) timeout (in seconds)
    pub channel_handshake_timeout: u64,
    /// Ping-pong exchange execution interval (in seconds)
    pub channel_heartbeat_interval: u64,
    /// Allow localnet hosts
    pub localnet: bool,
    /// Cooling off time for peer discovery when unsuccessful
    pub outbound_peer_discovery_cooloff_time: u64,
    /// Time between peer discovery attempts
    pub outbound_peer_discovery_attempt_time: u64,
    /// P2P datastore path
    pub p2p_datastore: Option<String>,
    /// Hostlist storage path
    pub hostlist: Option<String>,
    /// Pause interval within greylist refinery process
    pub greylist_refinery_interval: u64,
    /// Percent of connections to come from the whitelist
    pub white_connect_percent: usize,
    /// Number of goldlist connections
    pub gold_connect_count: usize,
    /// If this is true, strictly follow the gold_connect_count and
    /// white_connect_percent settings. Otherwise, connect to greylist
    /// entries if we have no white or gold connections.
    pub slot_preference_strict: bool,
    /// Number of seconds with no connections after which refinery
    /// process is paused.
    pub time_with_no_connections: u64,
    /// Nodes to avoid interacting with for the duration of the program,
    /// in the format ["host", ["scheme", "scheme"], [port, port]]
    /// If scheme is left empty it will default to "tcp+tls".
    /// If ports are left empty all ports from this peer will be blocked.
    pub blacklist: Vec<BlacklistEntry>,
    /// Do not ban nodes that send messages without dispatchers if set
    /// to `Relaxed`. For most uses, should be set to `Strict`.
    pub ban_policy: BanPolicy,
}

impl Default for Settings {
    fn default() -> Self {
        let version = option_env!("CARGO_PKG_VERSION").unwrap_or("0.0.0");
        let app_version = semver::Version::parse(version).unwrap();

        Self {
            node_id: String::new(),
            inbound_addrs: vec![],
            external_addrs: vec![],
            magic_bytes: Default::default(),
            peers: vec![],
            seeds: vec![],
            app_version,
            allowed_transports: vec!["tcp+tls".to_string()],
            transport_mixing: true,
            outbound_connections: 8,
            inbound_connections: 8,
            outbound_connect_timeout: 15,
            channel_handshake_timeout: 10,
            channel_heartbeat_interval: 30,
            localnet: false,
            outbound_peer_discovery_cooloff_time: 30,
            outbound_peer_discovery_attempt_time: 5,
            p2p_datastore: None,
            hostlist: None,
            greylist_refinery_interval: 15,
            white_connect_percent: 70,
            gold_connect_count: 2,
            slot_preference_strict: false,
            time_with_no_connections: 30,
            blacklist: vec![],
            ban_policy: BanPolicy::Strict,
        }
    }
}

// The following is used so we can have P2P settings configurable
// from TOML files.

/// Distinguishes distinct P2P networks
#[derive(serde::Deserialize, Debug, Clone)]
pub struct MagicBytes(pub [u8; 4]);

impl Default for MagicBytes {
    fn default() -> Self {
        Self([0xd9, 0xef, 0xb6, 0x7d])
    }
}

/// Defines the network settings.
#[derive(Clone, Debug, serde::Deserialize, structopt::StructOpt, structopt_toml::StructOptToml)]
#[structopt()]
pub struct SettingsOpt {
    /// P2P accept address node listens to for inbound connections
    #[serde(default)]
    #[structopt(long = "accept")]
    pub inbound: Vec<Url>,

    /// Outbound connection slots number
    #[structopt(long = "outbound-slots")]
    pub outbound_connections: Option<usize>,

    /// Inbound connection slots number
    #[structopt(long = "inbound-slots")]
    pub inbound_connections: Option<usize>,

    #[serde(default)]
    #[structopt(skip)]
    /// Magic bytes used to distinguish P2P distinct networks and
    /// avoid nodes bleeding due to user config error.
    pub magic_bytes: MagicBytes,

    /// P2P external addresses node advertises so other peers can
    /// reach us and connect to us, as long as inbound addresses
    /// are also configured
    #[serde(default)]
    #[structopt(long)]
    pub external_addrs: Vec<Url>,

    /// Peer nodes to manually connect to
    #[serde(default)]
    #[structopt(long)]
    pub peers: Vec<Url>,

    /// Seed nodes to connect to for peers retrieval and/or
    /// advertising our own external addresses
    #[serde(default)]
    #[structopt(long)]
    pub seeds: Vec<Url>,

    /// Connection establishment timeout in seconds
    #[structopt(skip)]
    pub outbound_connect_timeout: Option<u64>,

    /// Exchange versions (handshake) timeout in seconds
    #[structopt(skip)]
    pub channel_handshake_timeout: Option<u64>,

    /// Ping-pong exchange execution interval in seconds
    #[structopt(skip)]
    pub channel_heartbeat_interval: Option<u64>,

    /// Only used for debugging. Compromises privacy when set.
    #[serde(default)]
    #[structopt(skip)]
    pub node_id: String,

    /// Preferred transports for outbound connections
    #[serde(default)]
    #[structopt(long = "transports")]
    pub allowed_transports: Option<Vec<String>>,

    /// Allow transport mixing (e.g. Tor would be allowed to connect to `tcp://`)
    #[structopt(long)]
    pub transport_mixing: Option<bool>,

    /// If this is true, strictly follow the gold_connect_count and
    /// white_connect_percent settings. Otherwise, connect to greylist
    /// entries if we have no white or gold connections.
    #[serde(default)]
    #[structopt(long)]
    pub localnet: bool,

    /// Cooling off time for peer discovery when unsuccessful
    #[structopt(skip)]
    pub outbound_peer_discovery_cooloff_time: Option<u64>,

    /// Time between peer discovery attempts
    #[structopt(skip)]
    pub outbound_peer_discovery_attempt_time: Option<u64>,

    /// P2P datastore path
    #[serde(default)]
    #[structopt(long)]
    pub p2p_datastore: Option<String>,

    /// Hosts .tsv file to use
    #[serde(default)]
    #[structopt(long)]
    pub hostlist: Option<String>,

    /// Pause interval within greylist refinery process
    #[structopt(skip)]
    pub greylist_refinery_interval: Option<u64>,

    /// Number of whitelist connections
    #[structopt(skip)]
    pub white_connect_percent: Option<usize>,

    /// Number of goldlist connections
    #[structopt(skip)]
    pub gold_connect_count: Option<usize>,

    /// Allow localnet hosts
    #[serde(default)]
    #[structopt(long)]
    pub slot_preference_strict: bool,

    /// Number of seconds with no connections after which refinery
    /// process is paused.
    #[structopt(skip)]
    pub time_with_no_connections: Option<u64>,

    /// Nodes to avoid interacting with for the duration of the program,
    /// in the format ["host", ["scheme", "scheme"], [port, port]]
    /// If scheme is left empty it will default to "tcp+tls".
    /// If ports are left empty all ports from this peer will be blocked.
    #[serde(default)]
    #[structopt(skip)]
    pub blacklist: Vec<BlacklistEntry>,

    /// Do not ban nodes that send messages without dispatchers if set
    /// to `Relaxed`. For most uses, should be set to `Strict`.
    #[serde(default)]
    #[structopt(skip)]
    pub ban_policy: BanPolicy,
}

impl From<SettingsOpt> for Settings {
    fn from(opt: SettingsOpt) -> Self {
        let def = Settings::default();

        Self {
            node_id: opt.node_id,
            inbound_addrs: opt.inbound,
            external_addrs: opt.external_addrs,
            magic_bytes: opt.magic_bytes,
            peers: opt.peers,
            seeds: opt.seeds,
            app_version: def.app_version,
            allowed_transports: opt.allowed_transports.unwrap_or(def.allowed_transports),
            transport_mixing: opt.transport_mixing.unwrap_or(def.transport_mixing),
            outbound_connections: opt.outbound_connections.unwrap_or(def.outbound_connections),
            inbound_connections: opt.inbound_connections.unwrap_or(def.inbound_connections),
            outbound_connect_timeout: opt
                .outbound_connect_timeout
                .unwrap_or(def.outbound_connect_timeout),
            channel_handshake_timeout: opt
                .channel_handshake_timeout
                .unwrap_or(def.channel_handshake_timeout),
            channel_heartbeat_interval: opt
                .channel_heartbeat_interval
                .unwrap_or(def.channel_heartbeat_interval),
            localnet: opt.localnet,
            outbound_peer_discovery_cooloff_time: opt
                .outbound_peer_discovery_cooloff_time
                .unwrap_or(def.outbound_peer_discovery_cooloff_time),
            outbound_peer_discovery_attempt_time: opt
                .outbound_peer_discovery_attempt_time
                .unwrap_or(def.outbound_peer_discovery_attempt_time),
            p2p_datastore: opt.p2p_datastore,
            hostlist: opt.hostlist,
            greylist_refinery_interval: opt
                .greylist_refinery_interval
                .unwrap_or(def.greylist_refinery_interval),
            white_connect_percent: opt.white_connect_percent.unwrap_or(def.white_connect_percent),
            gold_connect_count: opt.gold_connect_count.unwrap_or(def.gold_connect_count),
            slot_preference_strict: opt.slot_preference_strict,
            time_with_no_connections: opt
                .time_with_no_connections
                .unwrap_or(def.time_with_no_connections),
            blacklist: opt.blacklist,
            ban_policy: opt.ban_policy,
        }
    }
}