libp2p_swarm/dial_opts.rs
1// Copyright 2019 Parity Technologies (UK) Ltd.
2// Copyright 2021 Protocol Labs.
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20// DEALINGS IN THE SOFTWARE.
21
22use crate::ConnectionId;
23use libp2p_core::connection::Endpoint;
24use libp2p_core::multiaddr::Protocol;
25use libp2p_core::Multiaddr;
26use libp2p_identity::PeerId;
27use std::num::NonZeroU8;
28
29/// Options to configure a dial to a known or unknown peer.
30///
31/// Used in [`Swarm::dial`](crate::Swarm::dial) and
32/// [`ToSwarm::Dial`](crate::behaviour::ToSwarm::Dial).
33///
34/// To construct use either of:
35///
36/// - [`DialOpts::peer_id`] dialing a known peer
37///
38/// - [`DialOpts::unknown_peer_id`] dialing an unknown peer
39#[derive(Debug)]
40pub struct DialOpts {
41 peer_id: Option<PeerId>,
42 condition: PeerCondition,
43 addresses: Vec<Multiaddr>,
44 extend_addresses_through_behaviour: bool,
45 role_override: Endpoint,
46 dial_concurrency_factor_override: Option<NonZeroU8>,
47 connection_id: ConnectionId,
48}
49
50impl DialOpts {
51 /// Dial a known peer.
52 ///
53 /// ```
54 /// # use libp2p_swarm::dial_opts::{DialOpts, PeerCondition};
55 /// # use libp2p_identity::PeerId;
56 /// DialOpts::peer_id(PeerId::random())
57 /// .condition(PeerCondition::Disconnected)
58 /// .addresses(vec!["/ip6/::1/tcp/12345".parse().unwrap()])
59 /// .extend_addresses_through_behaviour()
60 /// .build();
61 /// ```
62 pub fn peer_id(peer_id: PeerId) -> WithPeerId {
63 WithPeerId {
64 peer_id,
65 condition: Default::default(),
66 role_override: Endpoint::Dialer,
67 dial_concurrency_factor_override: Default::default(),
68 }
69 }
70
71 /// Dial an unknown peer.
72 ///
73 /// ```
74 /// # use libp2p_swarm::dial_opts::DialOpts;
75 /// DialOpts::unknown_peer_id()
76 /// .address("/ip6/::1/tcp/12345".parse().unwrap())
77 /// .build();
78 /// ```
79 pub fn unknown_peer_id() -> WithoutPeerId {
80 WithoutPeerId {}
81 }
82
83 /// Retrieves the [`PeerId`] from the [`DialOpts`] if specified or otherwise tries to extract it
84 /// from the multihash in the `/p2p` part of the address, if present.
85 pub fn get_peer_id(&self) -> Option<PeerId> {
86 if let Some(peer_id) = self.peer_id {
87 return Some(peer_id);
88 }
89
90 let first_address = self.addresses.first()?;
91 let last_protocol = first_address.iter().last()?;
92
93 if let Protocol::P2p(p) = last_protocol {
94 return Some(p);
95 }
96
97 None
98 }
99
100 /// Get the [`ConnectionId`] of this dial attempt.
101 ///
102 /// All future events of this dial will be associated with this ID.
103 /// See [`DialFailure`](crate::DialFailure) and [`ConnectionEstablished`](crate::behaviour::ConnectionEstablished).
104 pub fn connection_id(&self) -> ConnectionId {
105 self.connection_id
106 }
107
108 pub(crate) fn get_addresses(&self) -> Vec<Multiaddr> {
109 self.addresses.clone()
110 }
111
112 pub(crate) fn extend_addresses_through_behaviour(&self) -> bool {
113 self.extend_addresses_through_behaviour
114 }
115
116 pub(crate) fn peer_condition(&self) -> PeerCondition {
117 self.condition
118 }
119
120 pub(crate) fn dial_concurrency_override(&self) -> Option<NonZeroU8> {
121 self.dial_concurrency_factor_override
122 }
123
124 pub(crate) fn role_override(&self) -> Endpoint {
125 self.role_override
126 }
127}
128
129impl From<Multiaddr> for DialOpts {
130 fn from(address: Multiaddr) -> Self {
131 DialOpts::unknown_peer_id().address(address).build()
132 }
133}
134
135impl From<PeerId> for DialOpts {
136 fn from(peer_id: PeerId) -> Self {
137 DialOpts::peer_id(peer_id).build()
138 }
139}
140
141#[derive(Debug)]
142pub struct WithPeerId {
143 peer_id: PeerId,
144 condition: PeerCondition,
145 role_override: Endpoint,
146 dial_concurrency_factor_override: Option<NonZeroU8>,
147}
148
149impl WithPeerId {
150 /// Specify a [`PeerCondition`] for the dial.
151 pub fn condition(mut self, condition: PeerCondition) -> Self {
152 self.condition = condition;
153 self
154 }
155
156 /// Override
157 /// Number of addresses concurrently dialed for a single outbound connection attempt.
158 pub fn override_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self {
159 self.dial_concurrency_factor_override = Some(factor);
160 self
161 }
162
163 /// Specify a set of addresses to be used to dial the known peer.
164 pub fn addresses(self, addresses: Vec<Multiaddr>) -> WithPeerIdWithAddresses {
165 WithPeerIdWithAddresses {
166 peer_id: self.peer_id,
167 condition: self.condition,
168 addresses,
169 extend_addresses_through_behaviour: false,
170 role_override: self.role_override,
171 dial_concurrency_factor_override: self.dial_concurrency_factor_override,
172 }
173 }
174
175 /// Override role of local node on connection. I.e. execute the dial _as a
176 /// listener_.
177 ///
178 /// See
179 /// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer)
180 /// for details.
181 pub fn override_role(mut self) -> Self {
182 self.role_override = Endpoint::Listener;
183 self
184 }
185
186 /// Build the final [`DialOpts`].
187 pub fn build(self) -> DialOpts {
188 DialOpts {
189 peer_id: Some(self.peer_id),
190 condition: self.condition,
191 addresses: vec![],
192 extend_addresses_through_behaviour: true,
193 role_override: self.role_override,
194 dial_concurrency_factor_override: self.dial_concurrency_factor_override,
195 connection_id: ConnectionId::next(),
196 }
197 }
198}
199
200#[derive(Debug)]
201pub struct WithPeerIdWithAddresses {
202 peer_id: PeerId,
203 condition: PeerCondition,
204 addresses: Vec<Multiaddr>,
205 extend_addresses_through_behaviour: bool,
206 role_override: Endpoint,
207 dial_concurrency_factor_override: Option<NonZeroU8>,
208}
209
210impl WithPeerIdWithAddresses {
211 /// Specify a [`PeerCondition`] for the dial.
212 pub fn condition(mut self, condition: PeerCondition) -> Self {
213 self.condition = condition;
214 self
215 }
216
217 /// In addition to the provided addresses, extend the set via
218 /// [`NetworkBehaviour::handle_pending_outbound_connection`](crate::behaviour::NetworkBehaviour::handle_pending_outbound_connection).
219 pub fn extend_addresses_through_behaviour(mut self) -> Self {
220 self.extend_addresses_through_behaviour = true;
221 self
222 }
223
224 /// Override role of local node on connection. I.e. execute the dial _as a
225 /// listener_.
226 ///
227 /// See
228 /// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer)
229 /// for details.
230 pub fn override_role(mut self) -> Self {
231 self.role_override = Endpoint::Listener;
232 self
233 }
234
235 /// Override
236 /// Number of addresses concurrently dialed for a single outbound connection attempt.
237 pub fn override_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self {
238 self.dial_concurrency_factor_override = Some(factor);
239 self
240 }
241
242 /// Build the final [`DialOpts`].
243 pub fn build(self) -> DialOpts {
244 DialOpts {
245 peer_id: Some(self.peer_id),
246 condition: self.condition,
247 addresses: self.addresses,
248 extend_addresses_through_behaviour: self.extend_addresses_through_behaviour,
249 role_override: self.role_override,
250 dial_concurrency_factor_override: self.dial_concurrency_factor_override,
251 connection_id: ConnectionId::next(),
252 }
253 }
254}
255
256#[derive(Debug)]
257pub struct WithoutPeerId {}
258
259impl WithoutPeerId {
260 /// Specify a single address to dial the unknown peer.
261 pub fn address(self, address: Multiaddr) -> WithoutPeerIdWithAddress {
262 WithoutPeerIdWithAddress {
263 address,
264 role_override: Endpoint::Dialer,
265 }
266 }
267}
268
269#[derive(Debug)]
270pub struct WithoutPeerIdWithAddress {
271 address: Multiaddr,
272 role_override: Endpoint,
273}
274
275impl WithoutPeerIdWithAddress {
276 /// Override role of local node on connection. I.e. execute the dial _as a
277 /// listener_.
278 ///
279 /// See
280 /// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer)
281 /// for details.
282 pub fn override_role(mut self) -> Self {
283 self.role_override = Endpoint::Listener;
284 self
285 }
286 /// Build the final [`DialOpts`].
287 pub fn build(self) -> DialOpts {
288 DialOpts {
289 peer_id: None,
290 condition: PeerCondition::Always,
291 addresses: vec![self.address],
292 extend_addresses_through_behaviour: false,
293 role_override: self.role_override,
294 dial_concurrency_factor_override: None,
295 connection_id: ConnectionId::next(),
296 }
297 }
298}
299
300/// The available conditions under which a new dialing attempt to
301/// a known peer is initiated.
302///
303/// ```
304/// # use libp2p_swarm::dial_opts::{DialOpts, PeerCondition};
305/// # use libp2p_identity::PeerId;
306/// #
307/// DialOpts::peer_id(PeerId::random())
308/// .condition(PeerCondition::Disconnected)
309/// .build();
310/// ```
311#[derive(Debug, Copy, Clone, Default)]
312pub enum PeerCondition {
313 /// A new dialing attempt is initiated _only if_ the peer is currently
314 /// considered disconnected, i.e. there is no established connection
315 /// and no ongoing dialing attempt.
316 #[default]
317 Disconnected,
318 /// A new dialing attempt is initiated _only if_ there is currently
319 /// no ongoing dialing attempt, i.e. the peer is either considered
320 /// disconnected or connected but without an ongoing dialing attempt.
321 NotDialing,
322 /// A new dialing attempt is always initiated, only subject to the
323 /// configured connection limits.
324 Always,
325}