Files
rust-libp2p/examples/distributed-key-value-store/src/main.rs
Max Inden d605255fec feat(libp2p): add SwarmBuilder
Introduce the new `libp2p::SwarmBuilder`. Users should use the new `libp2p::SwarmBuilder` instead of the now deprecated `libp2p::swarm::SwarmBuilder`. See `libp2p::SwarmBuilder` docs on how to use the new builder.

Fixes #3657.
Fixes #3563.
Fixes #3179.

Pull-Request: #4120.
2023-10-10 06:55:14 +00:00

221 lines
8.0 KiB
Rust

// Copyright 20l9 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.
#![doc = include_str!("../README.md")]
use async_std::io;
use futures::{prelude::*, select};
use libp2p::kad;
use libp2p::kad::record::store::MemoryStore;
use libp2p::kad::Mode;
use libp2p::{
mdns, noise,
swarm::{NetworkBehaviour, SwarmEvent},
tcp, yamux,
};
use std::error::Error;
#[async_std::main]
async fn main() -> Result<(), Box<dyn Error>> {
env_logger::init();
// We create a custom network behaviour that combines Kademlia and mDNS.
#[derive(NetworkBehaviour)]
struct Behaviour {
kademlia: kad::Behaviour<MemoryStore>,
mdns: mdns::async_io::Behaviour,
}
let mut swarm = libp2p::SwarmBuilder::with_new_identity()
.with_async_std()
.with_tcp(
tcp::Config::default(),
noise::Config::new,
yamux::Config::default,
)?
.with_behaviour(|key| {
Ok(Behaviour {
kademlia: kad::Behaviour::new(
key.public().to_peer_id(),
MemoryStore::new(key.public().to_peer_id()),
),
mdns: mdns::async_io::Behaviour::new(
mdns::Config::default(),
key.public().to_peer_id(),
)?,
})
})?
.build();
swarm.behaviour_mut().kademlia.set_mode(Some(Mode::Server));
// Read full lines from stdin
let mut stdin = io::BufReader::new(io::stdin()).lines().fuse();
// 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 {
select! {
line = stdin.select_next_some() => handle_input_line(&mut swarm.behaviour_mut().kademlia, line.expect("Stdin not to close")),
event = swarm.select_next_some() => match event {
SwarmEvent::NewListenAddr { address, .. } => {
println!("Listening in {address:?}");
},
SwarmEvent::Behaviour(BehaviourEvent::Mdns(mdns::Event::Discovered(list))) => {
for (peer_id, multiaddr) in list {
swarm.behaviour_mut().kademlia.add_address(&peer_id, multiaddr);
}
}
SwarmEvent::Behaviour(BehaviourEvent::Kademlia(kad::Event::OutboundQueryProgressed { result, ..})) => {
match result {
kad::QueryResult::GetProviders(Ok(kad::GetProvidersOk::FoundProviders { key, providers, .. })) => {
for peer in providers {
println!(
"Peer {peer:?} provides key {:?}",
std::str::from_utf8(key.as_ref()).unwrap()
);
}
}
kad::QueryResult::GetProviders(Err(err)) => {
eprintln!("Failed to get providers: {err:?}");
}
kad::QueryResult::GetRecord(Ok(
kad::GetRecordOk::FoundRecord(kad::PeerRecord {
record: kad::Record { key, value, .. },
..
})
)) => {
println!(
"Got record {:?} {:?}",
std::str::from_utf8(key.as_ref()).unwrap(),
std::str::from_utf8(&value).unwrap(),
);
}
kad::QueryResult::GetRecord(Ok(_)) => {}
kad::QueryResult::GetRecord(Err(err)) => {
eprintln!("Failed to get record: {err:?}");
}
kad::QueryResult::PutRecord(Ok(kad::PutRecordOk { key })) => {
println!(
"Successfully put record {:?}",
std::str::from_utf8(key.as_ref()).unwrap()
);
}
kad::QueryResult::PutRecord(Err(err)) => {
eprintln!("Failed to put record: {err:?}");
}
kad::QueryResult::StartProviding(Ok(kad::AddProviderOk { key })) => {
println!(
"Successfully put provider record {:?}",
std::str::from_utf8(key.as_ref()).unwrap()
);
}
kad::QueryResult::StartProviding(Err(err)) => {
eprintln!("Failed to put provider record: {err:?}");
}
_ => {}
}
}
_ => {}
}
}
}
}
fn handle_input_line(kademlia: &mut kad::Behaviour<MemoryStore>, line: String) {
let mut args = line.split(' ');
match args.next() {
Some("GET") => {
let key = {
match args.next() {
Some(key) => kad::record::Key::new(&key),
None => {
eprintln!("Expected key");
return;
}
}
};
kademlia.get_record(key);
}
Some("GET_PROVIDERS") => {
let key = {
match args.next() {
Some(key) => kad::record::Key::new(&key),
None => {
eprintln!("Expected key");
return;
}
}
};
kademlia.get_providers(key);
}
Some("PUT") => {
let key = {
match args.next() {
Some(key) => kad::record::Key::new(&key),
None => {
eprintln!("Expected key");
return;
}
}
};
let value = {
match args.next() {
Some(value) => value.as_bytes().to_vec(),
None => {
eprintln!("Expected value");
return;
}
}
};
let record = kad::Record {
key,
value,
publisher: None,
expires: None,
};
kademlia
.put_record(record, kad::Quorum::One)
.expect("Failed to store record locally.");
}
Some("PUT_PROVIDER") => {
let key = {
match args.next() {
Some(key) => kad::record::Key::new(&key),
None => {
eprintln!("Expected key");
return;
}
}
};
kademlia
.start_providing(key)
.expect("Failed to start providing key");
}
_ => {
eprintln!("expected GET, GET_PROVIDERS, PUT or PUT_PROVIDER");
}
}
}