Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add p2p resolver example #696

Merged
merged 8 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions storage-provider/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ tracing = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
url = { workspace = true }

[dev-dependencies]
libp2p = { workspace = true, features = ["identify", "macros", "noise", "rendezvous", "tcp", "tokio", "yamux"] }

[lints]
workspace = true
133 changes: 133 additions & 0 deletions storage-provider/client/examples/peer-resolver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//! Peer Resolver example
//!
//! This example shows how to use the rendezvous client protocol to
//! connect to rendezvous bootstrap, and send a discovery message,
//! requesting the bootstrap node to return their registrations.
//! Then it will check the registrations to see if a given Peer ID
//! is contained in them to get a Peer ID to multiaddr mapping.
//! If the bootstrap node does not have information on the given Peer
//! ID, the example will return an error.
//! NOTE: This example is to be removed and implemented into the
//! client at some point.
use std::{error::Error, time::Duration};

use clap::Parser;
use libp2p::{
futures::StreamExt,
noise,
rendezvous::client::{Behaviour, Event},
swarm::SwarmEvent,
tcp, yamux, Multiaddr, PeerId, Swarm, SwarmBuilder,
};
use tracing_subscriber::EnvFilter;

#[derive(Debug)]
struct PeerInfo {
peer_id: PeerId,
multiaddresses: Vec<Multiaddr>,
}

#[derive(Parser)]
struct Cli {
/// Peer ID to resolve
#[arg(long)]
peer_id: PeerId,

/// Rendezvous point address of the bootstrap node
#[arg(long)]
rendezvous_point_address: Multiaddr,

/// PeerID of the bootstrap node
#[arg(long)]
rendezvous_point: PeerId,
}

fn create_swarm() -> Result<Swarm<Behaviour>, Box<dyn Error>> {
Ok(SwarmBuilder::with_new_identity()
.with_tokio()
.with_tcp(
tcp::Config::default(),
noise::Config::new,
yamux::Config::default,
)?
.with_behaviour(|key| Behaviour::new(key.clone()))?
.with_swarm_config(|cfg| cfg.with_idle_connection_timeout(Duration::from_secs(10)))
.build())
}

async fn discover(
swarm: &mut Swarm<Behaviour>,
peer_id_to_find: PeerId,
rendezvous_point_address: Multiaddr,
rendezvous_point: PeerId,
) -> Result<PeerInfo, Box<dyn Error>> {
// Dial in to the rendezvous point.
swarm.dial(rendezvous_point_address)?;

loop {
match swarm.select_next_some().await {
SwarmEvent::ConnectionEstablished { peer_id, .. } => {
if peer_id == rendezvous_point {
tracing::info!("Connection established with rendezvous point {}", peer_id);

// Requesting rendezvous point for peer discovery
swarm
.behaviour_mut()
.discover(None, None, None, rendezvous_point);
}
}
// Received discovered event from the rendezvous point
SwarmEvent::Behaviour(Event::Discovered { registrations, .. }) => {
// Check registrations
for registration in &registrations {
// Get peer ID from the registration record
let peer_id = registration.record.peer_id();
// skip self
if &peer_id == swarm.local_peer_id() {
continue;
}
if peer_id == peer_id_to_find {
return Ok(PeerInfo {
peer_id,
multiaddresses: registration.record.addresses().to_vec(),
});
}
}
return Err(format!(
"No registered multi-addresses found for Peer ID {peer_id_to_find}"
)
.into());
}

other => tracing::debug!("Other event: {other:?}"),
}
}
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let _ = tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.try_init();
let args = Cli::parse();

let mut swarm = create_swarm()?;
match discover(
&mut swarm,
args.peer_id,
args.rendezvous_point_address,
args.rendezvous_point,
)
.await
{
Ok(peer_info) => {
println!("Found peer with Peer ID {}", args.peer_id);
println!("Peer Info:");
println!("Peer ID: {}", peer_info.peer_id);
println!("Multiaddresses: {:?}", peer_info.multiaddresses);
}
Err(e) => eprintln!("{e}"),
}

Ok(())
}