mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-05-06 08:02:18 +00:00
Currently, our `NetworkBehaviour` derive macro depends on the `libp2p` crate to be in scope. This prevents standalone usage which forces us to depend on `libp2p` in all our tests where we want to derive a `NetworkBehaviour`. This PR introduces a `prelude` option that - by default - points to `libp2p::swarm::derive_prelude`, a new module added to `libp2p_swarm`. With this config option, users of `libp2p_swarm` can now refer to the macro without depending on `libp2p`, breaking the circular dependency in our workspace. For consistency with the ecosystem, the macro is now also re-exported by `libp2p_swarm` instead of `libp2p` at the same position as the trait that it implements. Lastly, we introduce an off-by-default `macros` feature flag that shrinks the dependency tree for users that don't need the derive macro.
172 lines
6.3 KiB
Rust
172 lines
6.3 KiB
Rust
// Copyright 2018 Parity Technologies (UK) Ltd.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
//! A basic chat application demonstrating libp2p with the mDNS and floodsub protocols
|
|
//! using tokio for all asynchronous tasks and I/O. In order for all used libp2p
|
|
//! crates to use tokio, it enables tokio-specific features for some crates.
|
|
//!
|
|
//! The example is run per node as follows:
|
|
//!
|
|
//! ```sh
|
|
//! cargo run --example chat-tokio --features=full
|
|
//! ```
|
|
|
|
use futures::StreamExt;
|
|
use libp2p::{
|
|
core::upgrade,
|
|
floodsub::{self, Floodsub, FloodsubEvent},
|
|
identity,
|
|
mdns::{
|
|
MdnsEvent,
|
|
// `TokioMdns` is available through the `mdns-tokio` feature.
|
|
TokioMdns,
|
|
},
|
|
mplex, noise,
|
|
swarm::{NetworkBehaviour, SwarmBuilder, SwarmEvent},
|
|
tcp, Multiaddr, PeerId, Transport,
|
|
};
|
|
use std::error::Error;
|
|
use tokio::io::{self, AsyncBufReadExt};
|
|
|
|
/// The `tokio::main` attribute sets up a tokio runtime.
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn Error>> {
|
|
env_logger::init();
|
|
|
|
// Create a random PeerId
|
|
let id_keys = identity::Keypair::generate_ed25519();
|
|
let peer_id = PeerId::from(id_keys.public());
|
|
println!("Local peer id: {peer_id:?}");
|
|
|
|
// Create a tokio-based TCP transport use noise for authenticated
|
|
// encryption and Mplex for multiplexing of substreams on a TCP stream.
|
|
let transport = tcp::tokio::Transport::new(tcp::Config::default().nodelay(true))
|
|
.upgrade(upgrade::Version::V1)
|
|
.authenticate(
|
|
noise::NoiseAuthenticated::xx(&id_keys)
|
|
.expect("Signing libp2p-noise static DH keypair failed."),
|
|
)
|
|
.multiplex(mplex::MplexConfig::new())
|
|
.boxed();
|
|
|
|
// Create a Floodsub topic
|
|
let floodsub_topic = floodsub::Topic::new("chat");
|
|
|
|
// We create a custom behaviour that combines floodsub and mDNS.
|
|
// The derive generates a delegating `NetworkBehaviour` impl.
|
|
#[derive(NetworkBehaviour)]
|
|
#[behaviour(out_event = "MyBehaviourEvent")]
|
|
struct MyBehaviour {
|
|
floodsub: Floodsub,
|
|
mdns: TokioMdns,
|
|
}
|
|
|
|
#[allow(clippy::large_enum_variant)]
|
|
enum MyBehaviourEvent {
|
|
Floodsub(FloodsubEvent),
|
|
Mdns(MdnsEvent),
|
|
}
|
|
|
|
impl From<FloodsubEvent> for MyBehaviourEvent {
|
|
fn from(event: FloodsubEvent) -> Self {
|
|
MyBehaviourEvent::Floodsub(event)
|
|
}
|
|
}
|
|
|
|
impl From<MdnsEvent> for MyBehaviourEvent {
|
|
fn from(event: MdnsEvent) -> Self {
|
|
MyBehaviourEvent::Mdns(event)
|
|
}
|
|
}
|
|
|
|
// Create a Swarm to manage peers and events.
|
|
let mut swarm = {
|
|
let mdns = TokioMdns::new(Default::default())?;
|
|
let mut behaviour = MyBehaviour {
|
|
floodsub: Floodsub::new(peer_id),
|
|
mdns,
|
|
};
|
|
|
|
behaviour.floodsub.subscribe(floodsub_topic.clone());
|
|
|
|
SwarmBuilder::new(transport, behaviour, peer_id)
|
|
// We want the connection background tasks to be spawned
|
|
// onto the tokio runtime.
|
|
.executor(Box::new(|fut| {
|
|
tokio::spawn(fut);
|
|
}))
|
|
.build()
|
|
};
|
|
|
|
// Reach out to another node if specified
|
|
if let Some(to_dial) = std::env::args().nth(1) {
|
|
let addr: Multiaddr = to_dial.parse()?;
|
|
swarm.dial(addr)?;
|
|
println!("Dialed {to_dial:?}");
|
|
}
|
|
|
|
// Read full lines from stdin
|
|
let mut stdin = io::BufReader::new(io::stdin()).lines();
|
|
|
|
// Listen on all interfaces and whatever port the OS assigns
|
|
swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
|
|
|
|
// Kick it off
|
|
loop {
|
|
tokio::select! {
|
|
line = stdin.next_line() => {
|
|
let line = line?.expect("stdin closed");
|
|
swarm.behaviour_mut().floodsub.publish(floodsub_topic.clone(), line.as_bytes());
|
|
}
|
|
event = swarm.select_next_some() => {
|
|
match event {
|
|
SwarmEvent::NewListenAddr { address, .. } => {
|
|
println!("Listening on {address:?}");
|
|
}
|
|
SwarmEvent::Behaviour(MyBehaviourEvent::Floodsub(FloodsubEvent::Message(message))) => {
|
|
println!(
|
|
"Received: '{:?}' from {:?}",
|
|
String::from_utf8_lossy(&message.data),
|
|
message.source
|
|
);
|
|
}
|
|
SwarmEvent::Behaviour(MyBehaviourEvent::Mdns(event)) => {
|
|
match event {
|
|
MdnsEvent::Discovered(list) => {
|
|
for (peer, _) in list {
|
|
swarm.behaviour_mut().floodsub.add_node_to_partial_view(peer);
|
|
}
|
|
}
|
|
MdnsEvent::Expired(list) => {
|
|
for (peer, _) in list {
|
|
if !swarm.behaviour().mdns.has_node(&peer) {
|
|
swarm.behaviour_mut().floodsub.remove_node_from_partial_view(&peer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|