Skip to content

Commit 73383b8

Browse files
authored
feat(peer-store): introduce libp2p-peer-store
Introduce `libp2p-peer-store` for a peer store implementation. Related: #4103 Pull-Request: #5724.
1 parent 9228dc2 commit 73383b8

File tree

11 files changed

+777
-0
lines changed

11 files changed

+777
-0
lines changed

Cargo.lock

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ members = [
2626
"misc/memory-connection-limits",
2727
"misc/metrics",
2828
"misc/multistream-select",
29+
"misc/peer-store",
2930
"misc/quick-protobuf-codec",
3031
"misc/quickcheck-ext",
3132
"misc/rw-stream-sink",
@@ -89,6 +90,7 @@ libp2p-memory-connection-limits = { version = "0.4.0", path = "misc/memory-conne
8990
libp2p-metrics = { version = "0.16.1", path = "misc/metrics" }
9091
libp2p-mplex = { version = "0.43.1", path = "muxers/mplex" }
9192
libp2p-noise = { version = "0.46.0", path = "transports/noise" }
93+
libp2p-peer-store = { version = "0.1.0", path = "misc/peer-store" }
9294
libp2p-perf = { version = "0.4.0", path = "protocols/perf" }
9395
libp2p-ping = { version = "0.46.0", path = "protocols/ping" }
9496
libp2p-plaintext = { version = "0.43.0", path = "transports/plaintext" }

libp2p/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
## 0.55.1
22
- Introduce `libp2p-webrtc-websys` behind `webrtc-websys` feature flag.
33
See [PR 5819](https://github.com/libp2p/rust-libp2p/pull/5819).
4+
5+
- Introduce `libp2p-peer-store`.
6+
See [PR 5724](https://github.com/libp2p/rust-libp2p/pull/5724).
47

58
- Make the `*-websys` variants (`libp2p-webrtc-websys`, `libp2p-websocket-websys`, `libp2p-webtransport-websys`) only available on wasm32 target architecture.
69
See [PR 5891](https://github.com/libp2p/rust-libp2p/pull/5891).

libp2p/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ full = [
2929
"memory-connection-limits",
3030
"metrics",
3131
"noise",
32+
"peer-store",
3233
"ping",
3334
"plaintext",
3435
"pnet",
@@ -69,6 +70,7 @@ mdns = ["dep:libp2p-mdns"]
6970
memory-connection-limits = ["dep:libp2p-memory-connection-limits"]
7071
metrics = ["dep:libp2p-metrics"]
7172
noise = ["dep:libp2p-noise"]
73+
peer-store = ["dep:libp2p-peer-store"]
7274
ping = ["dep:libp2p-ping", "libp2p-metrics?/ping"]
7375
plaintext = ["dep:libp2p-plaintext"]
7476
pnet = ["dep:libp2p-pnet"]
@@ -111,6 +113,7 @@ libp2p-identity = { workspace = true, features = ["rand"] }
111113
libp2p-kad = { workspace = true, optional = true }
112114
libp2p-metrics = { workspace = true, optional = true }
113115
libp2p-noise = { workspace = true, optional = true }
116+
libp2p-peer-store = { workspace = true, optional = true }
114117
libp2p-ping = { workspace = true, optional = true }
115118
libp2p-plaintext = { workspace = true, optional = true }
116119
libp2p-pnet = { workspace = true, optional = true }

libp2p/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ pub use libp2p_metrics as metrics;
8181
#[cfg(feature = "noise")]
8282
#[doc(inline)]
8383
pub use libp2p_noise as noise;
84+
#[cfg(feature = "peer-store")]
85+
#[doc(inline)]
86+
pub use libp2p_peer_store as peer_store;
8487
#[cfg(feature = "ping")]
8588
#[doc(inline)]
8689
pub use libp2p_ping as ping;

misc/peer-store/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## 0.1.0
2+
3+
- Introduce `libp2p-peer-store`.
4+
See [PR 5724](https://github.com/libp2p/rust-libp2p/pull/5724).

misc/peer-store/Cargo.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[package]
2+
name = "libp2p-peer-store"
3+
edition = "2021"
4+
version = "0.1.0"
5+
authors = ["drHuangMHT <[email protected]>"]
6+
license = "MIT"
7+
repository = "https://github.com/libp2p/rust-libp2p"
8+
publish = false
9+
rust-version.workspace = true
10+
11+
[dependencies]
12+
libp2p-core = { workspace = true }
13+
libp2p-swarm = { workspace = true }
14+
lru = "0.12.3"
15+
libp2p-identity = { workspace = true, optional = true }
16+
17+
[dev-dependencies]
18+
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
19+
libp2p-identity = { workspace = true, features = ["rand", "serde"] }
20+
libp2p = { workspace = true, features = ["macros", "identify"] }
21+
libp2p-swarm-test = { path = "../../swarm-test", features = ["tokio"] }
22+
serde_json = { version = "1.0.134" }
23+
24+
[lints]
25+
workspace = true

misc/peer-store/src/behaviour.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use std::{collections::VecDeque, task::Poll};
2+
3+
use libp2p_core::{Multiaddr, PeerId};
4+
use libp2p_swarm::{dummy, NetworkBehaviour};
5+
6+
use crate::store::Store;
7+
8+
/// Events generated by [`Behaviour`] and emitted back to [`Swarm`](libp2p_swarm::Swarm).
9+
#[derive(Debug, Clone)]
10+
pub enum Event<T> {
11+
/// The peer's record has been updated.
12+
/// Manually updating a record will always emit this event
13+
/// even if it provides no new information.
14+
RecordUpdated {
15+
/// The peer that has an update.
16+
peer: PeerId,
17+
},
18+
/// Event from the internal store.
19+
Store(T),
20+
}
21+
22+
/// Behaviour that maintains a peer address book.
23+
///
24+
/// Usage:
25+
/// ```
26+
/// use libp2p::swarm::NetworkBehaviour;
27+
/// use libp2p_peer_store::{memory_store::MemoryStore, Behaviour};
28+
///
29+
/// // `identify::Behaviour` broadcasts listen addresses of the peer,
30+
/// // `peer_store::Behaviour` will then capture the resulting
31+
/// // `FromSwarm::NewExternalAddrOfPeer` and add the addresses
32+
/// // to address book.
33+
/// #[derive(NetworkBehaviour)]
34+
/// struct ComposedBehaviour {
35+
/// peer_store: Behaviour<MemoryStore>,
36+
/// identify: libp2p::identify::Behaviour,
37+
/// }
38+
/// ```
39+
pub struct Behaviour<S: Store> {
40+
/// The internal store.
41+
store: S,
42+
/// Pending Events to be emitted back to [`Swarm`](libp2p_swarm::Swarm).
43+
pending_events: VecDeque<Event<S::FromStore>>,
44+
}
45+
46+
impl<'a, S> Behaviour<S>
47+
where
48+
S: Store + 'static,
49+
{
50+
/// Build a new [`Behaviour`] with the given store.
51+
pub fn new(store: S) -> Self {
52+
Self {
53+
store,
54+
pending_events: VecDeque::new(),
55+
}
56+
}
57+
58+
/// Try to get all observed address of the given peer.
59+
/// Returns `None` when the peer is not in the store.
60+
pub fn address_of_peer<'b>(
61+
&'a self,
62+
peer: &'b PeerId,
63+
) -> Option<impl Iterator<Item = &'a Multiaddr> + use<'a, 'b, S>> {
64+
self.store.addresses_of_peer(peer)
65+
}
66+
67+
/// Get an immutable reference to the internal store.
68+
pub fn store(&self) -> &S {
69+
&self.store
70+
}
71+
72+
/// Get a mutable reference to the internal store.
73+
pub fn store_mut(&mut self) -> &mut S {
74+
&mut self.store
75+
}
76+
77+
fn handle_store_event(&mut self, event: crate::store::Event<<S as Store>::FromStore>) {
78+
use crate::store::Event::*;
79+
match event {
80+
RecordUpdated(peer) => self.pending_events.push_back(Event::RecordUpdated { peer }),
81+
Store(ev) => self.pending_events.push_back(Event::Store(ev)),
82+
}
83+
}
84+
}
85+
86+
impl<S> NetworkBehaviour for Behaviour<S>
87+
where
88+
S: Store + 'static,
89+
<S as Store>::FromStore: Send + Sync,
90+
{
91+
type ConnectionHandler = dummy::ConnectionHandler;
92+
93+
type ToSwarm = Event<S::FromStore>;
94+
95+
fn handle_established_inbound_connection(
96+
&mut self,
97+
_connection_id: libp2p_swarm::ConnectionId,
98+
_peer: libp2p_core::PeerId,
99+
_local_addr: &libp2p_core::Multiaddr,
100+
_remote_addr: &libp2p_core::Multiaddr,
101+
) -> Result<libp2p_swarm::THandler<Self>, libp2p_swarm::ConnectionDenied> {
102+
Ok(dummy::ConnectionHandler)
103+
}
104+
105+
fn handle_pending_outbound_connection(
106+
&mut self,
107+
_connection_id: libp2p_swarm::ConnectionId,
108+
maybe_peer: Option<PeerId>,
109+
_addresses: &[Multiaddr],
110+
_effective_role: libp2p_core::Endpoint,
111+
) -> Result<Vec<Multiaddr>, libp2p_swarm::ConnectionDenied> {
112+
if maybe_peer.is_none() {
113+
return Ok(Vec::new());
114+
}
115+
let peer = maybe_peer.expect("already handled");
116+
Ok(self
117+
.store
118+
.addresses_of_peer(&peer)
119+
.map(|i| i.cloned().collect())
120+
.unwrap_or_default())
121+
}
122+
123+
fn handle_established_outbound_connection(
124+
&mut self,
125+
_connection_id: libp2p_swarm::ConnectionId,
126+
_peer: libp2p_core::PeerId,
127+
_addr: &libp2p_core::Multiaddr,
128+
_role_override: libp2p_core::Endpoint,
129+
_port_use: libp2p_core::transport::PortUse,
130+
) -> Result<libp2p_swarm::THandler<Self>, libp2p_swarm::ConnectionDenied> {
131+
Ok(dummy::ConnectionHandler)
132+
}
133+
134+
fn on_swarm_event(&mut self, event: libp2p_swarm::FromSwarm) {
135+
self.store.on_swarm_event(&event);
136+
}
137+
138+
fn on_connection_handler_event(
139+
&mut self,
140+
_peer_id: libp2p_core::PeerId,
141+
_connection_id: libp2p_swarm::ConnectionId,
142+
_event: libp2p_swarm::THandlerOutEvent<Self>,
143+
) {
144+
unreachable!("No event will be produced by a dummy handler.")
145+
}
146+
147+
fn poll(
148+
&mut self,
149+
cx: &mut std::task::Context<'_>,
150+
) -> std::task::Poll<libp2p_swarm::ToSwarm<Self::ToSwarm, libp2p_swarm::THandlerInEvent<Self>>>
151+
{
152+
if let Some(ev) = self.store.poll(cx) {
153+
self.handle_store_event(ev);
154+
};
155+
156+
if let Some(ev) = self.pending_events.pop_front() {
157+
return Poll::Ready(libp2p_swarm::ToSwarm::GenerateEvent(ev));
158+
}
159+
Poll::Pending
160+
}
161+
}

misc/peer-store/src/lib.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//! Implementation of peer store that stores address information
2+
//! about foreign peers.
3+
//!
4+
//! ## Important Discrepancies
5+
//! - **PeerStore is a local**: The peer store itself doesn't facilitate any information exchange
6+
//! between peers. You will need other protocols like `libp2p-kad` to share addresses you know
7+
//! across the network.
8+
//! - **PeerStore is a standalone**: Other protocols cannot expect the existence of PeerStore, and
9+
//! need to be manually hooked up to PeerStore in order to obtain information it provides.
10+
//!
11+
//! ## Usage
12+
//! Compose [`Behaviour`] with other [`NetworkBehaviour`](libp2p_swarm::NetworkBehaviour),
13+
//! and the PeerStore will automatically track addresses from
14+
//! [`FromSwarm::NewExternalAddrOfPeer`](libp2p_swarm::FromSwarm)
15+
//! and provide addresses when dialing peers(require `extend_addresses_through_behaviour` in
16+
//! [`DialOpts`](libp2p_swarm::dial_opts::DialOpts) when dialing).
17+
//! Other protocols need to be manually hooked up to obtain information from
18+
//! or provide information to PeerStore.
19+
20+
mod behaviour;
21+
pub mod memory_store;
22+
mod store;
23+
24+
pub use behaviour::{Behaviour, Event};
25+
pub use store::Store;

0 commit comments

Comments
 (0)