Skip to content

Commit 674c54e

Browse files
committed
Merge branch 'dm-get-providers' into dm-stop-providing
2 parents c90861f + aeb59cf commit 674c54e

File tree

35 files changed

+670
-254
lines changed

35 files changed

+670
-254
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ prost-build = "0.13"
1010

1111
[dependencies]
1212
async-trait = "0.1.81"
13-
bs58 = "0.4.0"
13+
bs58 = "0.5.1"
1414
bytes = "1.6.1"
1515
cid = "0.10.1"
1616
ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }

src/addresses.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Copyright 2024 litep2p developers
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the "Software"),
5+
// to deal in the Software without restriction, including without limitation
6+
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7+
// and/or sell copies of the Software, and to permit persons to whom the
8+
// Software is furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19+
// DEALINGS IN THE SOFTWARE.
20+
21+
use std::{collections::HashSet, sync::Arc};
22+
23+
use multiaddr::{Multiaddr, Protocol};
24+
use parking_lot::RwLock;
25+
26+
use crate::PeerId;
27+
28+
/// Set of the public addresses of the local node.
29+
///
30+
/// The format of the addresses stored in the set contain the local peer ID.
31+
/// This requirement is enforced by the [`PublicAddresses::add_address`] method,
32+
/// that will add the local peer ID to the address if it is missing.
33+
///
34+
/// # Note
35+
///
36+
/// - The addresses are reported to the identify protocol and are used by other nodes
37+
/// to establish a connection with the local node.
38+
///
39+
/// - Users must ensure that the addresses are reachable from the network.
40+
#[derive(Debug, Clone)]
41+
pub struct PublicAddresses {
42+
pub(crate) inner: Arc<RwLock<HashSet<Multiaddr>>>,
43+
local_peer_id: PeerId,
44+
}
45+
46+
impl PublicAddresses {
47+
/// Creates new [`PublicAddresses`] from the given peer ID.
48+
pub(crate) fn new(local_peer_id: PeerId) -> Self {
49+
Self {
50+
inner: Arc::new(RwLock::new(HashSet::new())),
51+
local_peer_id,
52+
}
53+
}
54+
55+
/// Add a public address to the list of addresses.
56+
///
57+
/// The address must contain the local peer ID, otherwise an error is returned.
58+
/// In case the address does not contain any peer ID, it will be added.
59+
///
60+
/// Returns true if the address was added, false if it was already present.
61+
pub fn add_address(&self, address: Multiaddr) -> Result<bool, InsertionError> {
62+
let address = ensure_local_peer(address, self.local_peer_id)?;
63+
Ok(self.inner.write().insert(address))
64+
}
65+
66+
/// Remove the exact public address.
67+
///
68+
/// The provided address must contain the local peer ID.
69+
pub fn remove_address(&self, address: &Multiaddr) -> bool {
70+
self.inner.write().remove(address)
71+
}
72+
73+
/// Returns a vector of the available listen addresses.
74+
pub fn get_addresses(&self) -> Vec<Multiaddr> {
75+
self.inner.read().iter().cloned().collect()
76+
}
77+
}
78+
79+
/// Check if the address contains the local peer ID.
80+
///
81+
/// If the address does not contain any peer ID, it will be added.
82+
fn ensure_local_peer(
83+
mut address: Multiaddr,
84+
local_peer_id: PeerId,
85+
) -> Result<Multiaddr, InsertionError> {
86+
if address.is_empty() {
87+
return Err(InsertionError::EmptyAddress);
88+
}
89+
90+
// Verify the peer ID from the address corresponds to the local peer ID.
91+
if let Some(peer_id) = PeerId::try_from_multiaddr(&address) {
92+
if peer_id != local_peer_id {
93+
return Err(InsertionError::DifferentPeerId);
94+
}
95+
} else {
96+
address.push(Protocol::P2p(local_peer_id.into()));
97+
}
98+
99+
Ok(address)
100+
}
101+
102+
/// The error returned when an address cannot be inserted.
103+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104+
pub enum InsertionError {
105+
/// The address is empty.
106+
EmptyAddress,
107+
/// The address contains a different peer ID than the local peer ID.
108+
DifferentPeerId,
109+
}
110+
111+
#[cfg(test)]
112+
mod tests {
113+
use super::*;
114+
use std::str::FromStr;
115+
116+
#[test]
117+
fn add_remove_contains() {
118+
let peer_id = PeerId::random();
119+
let addresses = PublicAddresses::new(peer_id);
120+
let address = Multiaddr::from_str("/dns/domain1.com/tcp/30333").unwrap();
121+
let peer_address = Multiaddr::from_str("/dns/domain1.com/tcp/30333")
122+
.unwrap()
123+
.with(Protocol::P2p(peer_id.into()));
124+
125+
assert!(!addresses.get_addresses().contains(&address));
126+
127+
assert!(addresses.add_address(address.clone()).unwrap());
128+
// Adding the address a second time returns Ok(false).
129+
assert!(!addresses.add_address(address.clone()).unwrap());
130+
131+
assert!(!addresses.get_addresses().contains(&address));
132+
assert!(addresses.get_addresses().contains(&peer_address));
133+
134+
addresses.remove_address(&peer_address);
135+
assert!(!addresses.get_addresses().contains(&peer_address));
136+
}
137+
138+
#[test]
139+
fn get_addresses() {
140+
let peer_id = PeerId::random();
141+
let addresses = PublicAddresses::new(peer_id);
142+
let address1 = Multiaddr::from_str("/dns/domain1.com/tcp/30333").unwrap();
143+
let address2 = Multiaddr::from_str("/dns/domain2.com/tcp/30333").unwrap();
144+
// Addresses different than the local peer ID are ignored.
145+
let address3 = Multiaddr::from_str(
146+
"/dns/domain2.com/tcp/30333/p2p/12D3KooWSueCPH3puP2PcvqPJdNaDNF3jMZjtJtDiSy35pWrbt5h",
147+
)
148+
.unwrap();
149+
150+
assert!(addresses.add_address(address1.clone()).unwrap());
151+
assert!(addresses.add_address(address2.clone()).unwrap());
152+
addresses.add_address(address3.clone()).unwrap_err();
153+
154+
let addresses = addresses.get_addresses();
155+
assert_eq!(addresses.len(), 2);
156+
assert!(addresses.contains(&address1.with(Protocol::P2p(peer_id.into()))));
157+
assert!(addresses.contains(&address2.with(Protocol::P2p(peer_id.into()))));
158+
}
159+
}

src/error.rs

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ pub enum Error {
125125
ConnectionDoesntExist(ConnectionId),
126126
#[error("Exceeded connection limits `{0:?}`")]
127127
ConnectionLimit(ConnectionLimitsError),
128+
#[error("Failed to dial peer immediately")]
129+
ImmediateDialError(#[from] ImmediateDialError),
128130
}
129131

130132
/// Error type for address parsing.
@@ -150,7 +152,7 @@ pub enum AddressError {
150152
InvalidPeerId(Multihash),
151153
}
152154

153-
#[derive(Debug, thiserror::Error)]
155+
#[derive(Debug, thiserror::Error, PartialEq)]
154156
pub enum ParseError {
155157
/// The provided probuf message cannot be decoded.
156158
#[error("Failed to decode protobuf message: `{0:?}`")]
@@ -180,10 +182,16 @@ pub enum ParseError {
180182
InvalidData,
181183
}
182184

183-
#[derive(Debug, thiserror::Error)]
185+
#[derive(Debug, thiserror::Error, PartialEq)]
184186
pub enum SubstreamError {
185187
#[error("Connection closed")]
186188
ConnectionClosed,
189+
#[error("Connection channel clogged")]
190+
ChannelClogged,
191+
#[error("Connection to peer does not exist: `{0}`")]
192+
PeerDoesNotExist(PeerId),
193+
#[error("I/O error: `{0}`")]
194+
IoError(ErrorKind),
187195
#[error("yamux error: `{0}`")]
188196
YamuxError(crate::yamux::ConnectionError, Direction),
189197
#[error("Failed to read from substream, substream id `{0:?}`")]
@@ -232,6 +240,25 @@ pub enum NegotiationError {
232240
WebSocket(#[from] tokio_tungstenite::tungstenite::error::Error),
233241
}
234242

243+
impl PartialEq for NegotiationError {
244+
fn eq(&self, other: &Self) -> bool {
245+
match (self, other) {
246+
(Self::MultistreamSelectError(lhs), Self::MultistreamSelectError(rhs)) => lhs == rhs,
247+
(Self::SnowError(lhs), Self::SnowError(rhs)) => lhs == rhs,
248+
(Self::ParseError(lhs), Self::ParseError(rhs)) => lhs == rhs,
249+
(Self::IoError(lhs), Self::IoError(rhs)) => lhs == rhs,
250+
(Self::PeerIdMismatch(lhs, lhs_1), Self::PeerIdMismatch(rhs, rhs_1)) =>
251+
lhs == rhs && lhs_1 == rhs_1,
252+
#[cfg(feature = "quic")]
253+
(Self::Quic(lhs), Self::Quic(rhs)) => lhs == rhs,
254+
#[cfg(feature = "websocket")]
255+
(Self::WebSocket(lhs), Self::WebSocket(rhs)) =>
256+
core::mem::discriminant(lhs) == core::mem::discriminant(rhs),
257+
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
258+
}
259+
}
260+
}
261+
235262
#[derive(Debug, thiserror::Error)]
236263
pub enum NotificationError {
237264
#[error("Peer already exists")]
@@ -246,7 +273,8 @@ pub enum NotificationError {
246273

247274
/// The error type for dialing a peer.
248275
///
249-
/// This error is reported via the litep2p events.
276+
/// This error is reported via the litep2p events after performing
277+
/// a network dialing operation.
250278
#[derive(Debug, thiserror::Error)]
251279
pub enum DialError {
252280
/// The dialing operation timed out.
@@ -269,9 +297,32 @@ pub enum DialError {
269297
NegotiationError(#[from] NegotiationError),
270298
}
271299

300+
/// Dialing resulted in an immediate error before performing any network operations.
301+
#[derive(Debug, thiserror::Error, Copy, Clone, Eq, PartialEq)]
302+
pub enum ImmediateDialError {
303+
/// The provided address does not include a peer ID.
304+
#[error("`PeerId` missing from the address")]
305+
PeerIdMissing,
306+
/// The peer ID provided in the address is the same as the local peer ID.
307+
#[error("Tried to dial self")]
308+
TriedToDialSelf,
309+
/// Cannot dial an already connected peer.
310+
#[error("Already connected to peer")]
311+
AlreadyConnected,
312+
/// Cannot dial a peer that does not have any address available.
313+
#[error("No address available for peer")]
314+
NoAddressAvailable,
315+
/// The essential task was closed.
316+
#[error("TaskClosed")]
317+
TaskClosed,
318+
/// The channel is clogged.
319+
#[error("Connection channel clogged")]
320+
ChannelClogged,
321+
}
322+
272323
/// Error during the QUIC transport negotiation.
273324
#[cfg(feature = "quic")]
274-
#[derive(Debug, thiserror::Error)]
325+
#[derive(Debug, thiserror::Error, PartialEq)]
275326
pub enum QuicError {
276327
/// The provided certificate is invalid.
277328
#[error("Invalid certificate")]
@@ -285,7 +336,7 @@ pub enum QuicError {
285336
}
286337

287338
/// Error during DNS resolution.
288-
#[derive(Debug, thiserror::Error)]
339+
#[derive(Debug, thiserror::Error, PartialEq)]
289340
pub enum DnsError {
290341
/// The DNS resolution failed to resolve the provided URL.
291342
#[error("DNS failed to resolve url `{0}`")]
@@ -309,6 +360,12 @@ impl From<io::Error> for Error {
309360
}
310361
}
311362

363+
impl From<io::Error> for SubstreamError {
364+
fn from(error: io::Error) -> SubstreamError {
365+
SubstreamError::IoError(error.kind())
366+
}
367+
}
368+
312369
impl From<io::Error> for DialError {
313370
fn from(error: io::Error) -> Self {
314371
DialError::NegotiationError(NegotiationError::IoError(error.kind()))

src/lib.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
#![allow(clippy::match_like_matches_macro)]
3030

3131
use crate::{
32+
addresses::PublicAddresses,
3233
config::Litep2pConfig,
34+
error::DialError,
3335
protocol::{
3436
libp2p::{bitswap::Bitswap, identify::Identify, kademlia::Kademlia, ping::Ping},
3537
mdns::Mdns,
@@ -50,7 +52,6 @@ use crate::transport::webrtc::WebRtcTransport;
5052
#[cfg(feature = "websocket")]
5153
use crate::transport::websocket::WebSocketTransport;
5254

53-
use error::DialError;
5455
use multiaddr::{Multiaddr, Protocol};
5556
use multihash::Multihash;
5657
use transport::Endpoint;
@@ -65,6 +66,7 @@ pub use types::protocol::ProtocolName;
6566

6667
pub(crate) mod peer_id;
6768

69+
pub mod addresses;
6870
pub mod codec;
6971
pub mod config;
7072
pub mod crypto;
@@ -387,7 +389,7 @@ impl Litep2p {
387389
// if identify was enabled, give it the enabled protocols and listen addresses and start it
388390
if let Some((service, mut identify_config)) = identify_info.take() {
389391
identify_config.protocols = transport_manager.protocols().cloned().collect();
390-
let identify = Identify::new(service, identify_config, listen_addresses.clone());
392+
let identify = Identify::new(service, identify_config);
391393

392394
litep2p_config.executor.run(Box::pin(async move {
393395
let _ = identify.run().await;
@@ -450,7 +452,12 @@ impl Litep2p {
450452
&self.local_peer_id
451453
}
452454

453-
/// Get listen address of litep2p.
455+
/// Get the list of public addresses of the node.
456+
pub fn public_addresses(&self) -> PublicAddresses {
457+
self.transport_manager.public_addresses()
458+
}
459+
460+
/// Get the list of listen addresses of the node.
454461
pub fn listen_addresses(&self) -> impl Iterator<Item = &Multiaddr> {
455462
self.listen_addresses.iter()
456463
}
@@ -473,7 +480,7 @@ impl Litep2p {
473480
/// Add one ore more known addresses for peer.
474481
///
475482
/// Return value denotes how many addresses were added for the peer.
476-
// Addresses belonging to disabled/unsupported transports will be ignored.
483+
/// Addresses belonging to disabled/unsupported transports will be ignored.
477484
pub fn add_known_address(
478485
&mut self,
479486
peer: PeerId,

0 commit comments

Comments
 (0)