Skip to content

Commit

Permalink
feat(peer-store): introduce libp2p-peer-store
Browse files Browse the repository at this point in the history
Introduce `libp2p-peer-store` for a peer store implementation.

Related: #4103

Pull-Request: #5724.
  • Loading branch information
drHuangMHT authored Mar 4, 2025
1 parent 9228dc2 commit 73383b8
Show file tree
Hide file tree
Showing 11 changed files with 777 additions and 0 deletions.
15 changes: 15 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ members = [
"misc/memory-connection-limits",
"misc/metrics",
"misc/multistream-select",
"misc/peer-store",
"misc/quick-protobuf-codec",
"misc/quickcheck-ext",
"misc/rw-stream-sink",
Expand Down Expand Up @@ -89,6 +90,7 @@ libp2p-memory-connection-limits = { version = "0.4.0", path = "misc/memory-conne
libp2p-metrics = { version = "0.16.1", path = "misc/metrics" }
libp2p-mplex = { version = "0.43.1", path = "muxers/mplex" }
libp2p-noise = { version = "0.46.0", path = "transports/noise" }
libp2p-peer-store = { version = "0.1.0", path = "misc/peer-store" }
libp2p-perf = { version = "0.4.0", path = "protocols/perf" }
libp2p-ping = { version = "0.46.0", path = "protocols/ping" }
libp2p-plaintext = { version = "0.43.0", path = "transports/plaintext" }
Expand Down
3 changes: 3 additions & 0 deletions libp2p/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
## 0.55.1
- Introduce `libp2p-webrtc-websys` behind `webrtc-websys` feature flag.
See [PR 5819](https://github.com/libp2p/rust-libp2p/pull/5819).

- Introduce `libp2p-peer-store`.
See [PR 5724](https://github.com/libp2p/rust-libp2p/pull/5724).

- Make the `*-websys` variants (`libp2p-webrtc-websys`, `libp2p-websocket-websys`, `libp2p-webtransport-websys`) only available on wasm32 target architecture.
See [PR 5891](https://github.com/libp2p/rust-libp2p/pull/5891).
Expand Down
3 changes: 3 additions & 0 deletions libp2p/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ full = [
"memory-connection-limits",
"metrics",
"noise",
"peer-store",
"ping",
"plaintext",
"pnet",
Expand Down Expand Up @@ -69,6 +70,7 @@ mdns = ["dep:libp2p-mdns"]
memory-connection-limits = ["dep:libp2p-memory-connection-limits"]
metrics = ["dep:libp2p-metrics"]
noise = ["dep:libp2p-noise"]
peer-store = ["dep:libp2p-peer-store"]
ping = ["dep:libp2p-ping", "libp2p-metrics?/ping"]
plaintext = ["dep:libp2p-plaintext"]
pnet = ["dep:libp2p-pnet"]
Expand Down Expand Up @@ -111,6 +113,7 @@ libp2p-identity = { workspace = true, features = ["rand"] }
libp2p-kad = { workspace = true, optional = true }
libp2p-metrics = { workspace = true, optional = true }
libp2p-noise = { workspace = true, optional = true }
libp2p-peer-store = { workspace = true, optional = true }
libp2p-ping = { workspace = true, optional = true }
libp2p-plaintext = { workspace = true, optional = true }
libp2p-pnet = { workspace = true, optional = true }
Expand Down
3 changes: 3 additions & 0 deletions libp2p/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ pub use libp2p_metrics as metrics;
#[cfg(feature = "noise")]
#[doc(inline)]
pub use libp2p_noise as noise;
#[cfg(feature = "peer-store")]
#[doc(inline)]
pub use libp2p_peer_store as peer_store;
#[cfg(feature = "ping")]
#[doc(inline)]
pub use libp2p_ping as ping;
Expand Down
4 changes: 4 additions & 0 deletions misc/peer-store/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## 0.1.0

- Introduce `libp2p-peer-store`.
See [PR 5724](https://github.com/libp2p/rust-libp2p/pull/5724).
25 changes: 25 additions & 0 deletions misc/peer-store/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "libp2p-peer-store"
edition = "2021"
version = "0.1.0"
authors = ["drHuangMHT <[email protected]>"]
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
publish = false
rust-version.workspace = true

[dependencies]
libp2p-core = { workspace = true }
libp2p-swarm = { workspace = true }
lru = "0.12.3"
libp2p-identity = { workspace = true, optional = true }

[dev-dependencies]
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
libp2p-identity = { workspace = true, features = ["rand", "serde"] }
libp2p = { workspace = true, features = ["macros", "identify"] }
libp2p-swarm-test = { path = "../../swarm-test", features = ["tokio"] }
serde_json = { version = "1.0.134" }

[lints]
workspace = true
161 changes: 161 additions & 0 deletions misc/peer-store/src/behaviour.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use std::{collections::VecDeque, task::Poll};

use libp2p_core::{Multiaddr, PeerId};
use libp2p_swarm::{dummy, NetworkBehaviour};

use crate::store::Store;

/// Events generated by [`Behaviour`] and emitted back to [`Swarm`](libp2p_swarm::Swarm).
#[derive(Debug, Clone)]
pub enum Event<T> {
/// The peer's record has been updated.
/// Manually updating a record will always emit this event
/// even if it provides no new information.
RecordUpdated {
/// The peer that has an update.
peer: PeerId,
},
/// Event from the internal store.
Store(T),
}

/// Behaviour that maintains a peer address book.
///
/// Usage:
/// ```
/// use libp2p::swarm::NetworkBehaviour;
/// use libp2p_peer_store::{memory_store::MemoryStore, Behaviour};
///
/// // `identify::Behaviour` broadcasts listen addresses of the peer,
/// // `peer_store::Behaviour` will then capture the resulting
/// // `FromSwarm::NewExternalAddrOfPeer` and add the addresses
/// // to address book.
/// #[derive(NetworkBehaviour)]
/// struct ComposedBehaviour {
/// peer_store: Behaviour<MemoryStore>,
/// identify: libp2p::identify::Behaviour,
/// }
/// ```
pub struct Behaviour<S: Store> {
/// The internal store.
store: S,
/// Pending Events to be emitted back to [`Swarm`](libp2p_swarm::Swarm).
pending_events: VecDeque<Event<S::FromStore>>,
}

impl<'a, S> Behaviour<S>
where
S: Store + 'static,
{
/// Build a new [`Behaviour`] with the given store.
pub fn new(store: S) -> Self {
Self {
store,
pending_events: VecDeque::new(),
}
}

/// Try to get all observed address of the given peer.
/// Returns `None` when the peer is not in the store.
pub fn address_of_peer<'b>(
&'a self,
peer: &'b PeerId,
) -> Option<impl Iterator<Item = &'a Multiaddr> + use<'a, 'b, S>> {
self.store.addresses_of_peer(peer)
}

/// Get an immutable reference to the internal store.
pub fn store(&self) -> &S {
&self.store
}

/// Get a mutable reference to the internal store.
pub fn store_mut(&mut self) -> &mut S {
&mut self.store
}

fn handle_store_event(&mut self, event: crate::store::Event<<S as Store>::FromStore>) {
use crate::store::Event::*;
match event {
RecordUpdated(peer) => self.pending_events.push_back(Event::RecordUpdated { peer }),
Store(ev) => self.pending_events.push_back(Event::Store(ev)),
}
}
}

impl<S> NetworkBehaviour for Behaviour<S>
where
S: Store + 'static,
<S as Store>::FromStore: Send + Sync,
{
type ConnectionHandler = dummy::ConnectionHandler;

type ToSwarm = Event<S::FromStore>;

fn handle_established_inbound_connection(
&mut self,
_connection_id: libp2p_swarm::ConnectionId,
_peer: libp2p_core::PeerId,
_local_addr: &libp2p_core::Multiaddr,
_remote_addr: &libp2p_core::Multiaddr,
) -> Result<libp2p_swarm::THandler<Self>, libp2p_swarm::ConnectionDenied> {
Ok(dummy::ConnectionHandler)
}

fn handle_pending_outbound_connection(
&mut self,
_connection_id: libp2p_swarm::ConnectionId,
maybe_peer: Option<PeerId>,
_addresses: &[Multiaddr],
_effective_role: libp2p_core::Endpoint,
) -> Result<Vec<Multiaddr>, libp2p_swarm::ConnectionDenied> {
if maybe_peer.is_none() {
return Ok(Vec::new());
}
let peer = maybe_peer.expect("already handled");
Ok(self
.store
.addresses_of_peer(&peer)
.map(|i| i.cloned().collect())
.unwrap_or_default())
}

fn handle_established_outbound_connection(
&mut self,
_connection_id: libp2p_swarm::ConnectionId,
_peer: libp2p_core::PeerId,
_addr: &libp2p_core::Multiaddr,
_role_override: libp2p_core::Endpoint,
_port_use: libp2p_core::transport::PortUse,
) -> Result<libp2p_swarm::THandler<Self>, libp2p_swarm::ConnectionDenied> {
Ok(dummy::ConnectionHandler)
}

fn on_swarm_event(&mut self, event: libp2p_swarm::FromSwarm) {
self.store.on_swarm_event(&event);
}

fn on_connection_handler_event(
&mut self,
_peer_id: libp2p_core::PeerId,
_connection_id: libp2p_swarm::ConnectionId,
_event: libp2p_swarm::THandlerOutEvent<Self>,
) {
unreachable!("No event will be produced by a dummy handler.")
}

fn poll(
&mut self,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<libp2p_swarm::ToSwarm<Self::ToSwarm, libp2p_swarm::THandlerInEvent<Self>>>
{
if let Some(ev) = self.store.poll(cx) {
self.handle_store_event(ev);
};

if let Some(ev) = self.pending_events.pop_front() {
return Poll::Ready(libp2p_swarm::ToSwarm::GenerateEvent(ev));
}
Poll::Pending
}
}
25 changes: 25 additions & 0 deletions misc/peer-store/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! Implementation of peer store that stores address information
//! about foreign peers.
//!
//! ## Important Discrepancies
//! - **PeerStore is a local**: The peer store itself doesn't facilitate any information exchange
//! between peers. You will need other protocols like `libp2p-kad` to share addresses you know
//! across the network.
//! - **PeerStore is a standalone**: Other protocols cannot expect the existence of PeerStore, and
//! need to be manually hooked up to PeerStore in order to obtain information it provides.
//!
//! ## Usage
//! Compose [`Behaviour`] with other [`NetworkBehaviour`](libp2p_swarm::NetworkBehaviour),
//! and the PeerStore will automatically track addresses from
//! [`FromSwarm::NewExternalAddrOfPeer`](libp2p_swarm::FromSwarm)
//! and provide addresses when dialing peers(require `extend_addresses_through_behaviour` in
//! [`DialOpts`](libp2p_swarm::dial_opts::DialOpts) when dialing).
//! Other protocols need to be manually hooked up to obtain information from
//! or provide information to PeerStore.
mod behaviour;
pub mod memory_store;
mod store;

pub use behaviour::{Behaviour, Event};
pub use store::Store;
Loading

0 comments on commit 73383b8

Please sign in to comment.