Any ideas on how to use libp2p in a Tauri app? #5078
-
I'm writing a p2p chat app with Tauri for UI. The code compiles and runs with no runtime errors. But when I call the I thought it is because swarm was locked in Related code: pub struct SwarmState {
inner: Mutex<Swarm<MyBehaviour>>,
}
#[derive(NetworkBehaviour)]
pub struct MyBehaviour {
gossipsub: gossipsub::Behaviour,
mdns: mdns::tokio::Behaviour,
request_response: libp2p::request_response::cbor::Behaviour<Message, Message>,
}
fn main() -> anyhow::Result<()> {
let mut swarm = libp2p::SwarmBuilder::with_new_identity()
.with_tokio()
.with_tcp(
tcp::Config::default(),
noise::Config::new,
yamux::Config::default,
)
.unwrap()
.with_quic()
.with_behaviour(|key| {
// To content-address message, we can take the hash of message and use it as an ID.
let message_id_fn = |message: &gossipsub::Message| {
let mut s = DefaultHasher::new();
message.data.hash(&mut s);
gossipsub::MessageId::from(s.finish().to_string())
};
// Set a custom gossipsub configuration
let gossipsub_config = gossipsub::ConfigBuilder::default()
.heartbeat_interval(Duration::from_secs(10)) // This is set to aid debugging by not cluttering the log space
.validation_mode(gossipsub::ValidationMode::Strict) // This sets the kind of message validation. The default is Strict (enforce message signing)
.message_id_fn(message_id_fn) // content-address messages. No two messages of the same content will be propagated.
.build()?;
// build a gossipsub network behaviour
let gossipsub = gossipsub::Behaviour::new(
gossipsub::MessageAuthenticity::Signed(key.clone()),
gossipsub_config,
)?;
let mdns =
mdns::tokio::Behaviour::new(mdns::Config::default(), key.public().to_peer_id())?;
let request_response = libp2p::request_response::Behaviour::new(
[(
StreamProtocol::new("/lime/private"),
request_response::ProtocolSupport::Full,
)],
request_response::Config::default(),
);
Ok(MyBehaviour {
gossipsub,
mdns,
request_response,
})
})
.unwrap()
.with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60)))
.build();
// Create a Gossipsub topic
let topic = gossipsub::IdentTopic::new("test-net");
// subscribes to our topic
swarm.behaviour_mut().gossipsub.subscribe(&topic).unwrap();
tauri::Builder::default()
.plugin(tauri_plugin_window::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_dialog::init())
.manage(SwarmState {
inner: Mutex::new(swarm),
})
.invoke_handler(tauri::generate_handler![
send_text_message,
send_updated_profile
])
.setup(move |app| {
let _ = build_system_tray(app.app_handle());
let handle = app.app_handle().clone();
tauri::async_runtime::spawn(async move {
let swarm = handle.state::<SwarmState>();
let mut swarm = swarm.inner.lock().await;
// // Listen on all interfaces and whatever port the OS assigns
swarm
.listen_on("/ip4/0.0.0.0/udp/0/quic-v1".parse().unwrap())
.unwrap();
swarm
.listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap())
.unwrap();
loop {
let event = swarm.select_next_some().await;
match event {
SwarmEvent::Behaviour(MyBehaviourEvent::Mdns(mdns::Event::Discovered(
list,
))) => {
for (peer_id, multiaddr) in list {
println!("mDNS discovered a new peer: {peer_id}");
swarm.behaviour_mut().gossipsub.add_explicit_peer(&peer_id);
swarm
.behaviour_mut()
.request_response
.add_address(&peer_id, multiaddr);
handle.emit("peer-find", peer_id.to_string()).unwrap();
}
}
SwarmEvent::Behaviour(MyBehaviourEvent::Mdns(mdns::Event::Expired(
list,
))) => {
for (peer_id, _multiaddr) in list {
println!("mDNS discover peer has expired: {peer_id}");
swarm
.behaviour_mut()
.gossipsub
.remove_explicit_peer(&peer_id);
}
}
SwarmEvent::Behaviour(MyBehaviourEvent::Gossipsub(
gossipsub::Event::Message {
propagation_source: peer_id,
message_id: id,
message,
},
)) => println!(
"Got message: '{}' with id: {id} from peer: {peer_id}",
String::from_utf8_lossy(&message.data),
),
SwarmEvent::NewListenAddr { address, .. } => {
println!("Local node is listening on {address}");
}
SwarmEvent::Behaviour(MyBehaviourEvent::RequestResponse(event)) => {
dbg!(event);
}
_ => {}
}
}
});
// rt.block_on(async move {
// });
// });
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
Ok(())
}
#[tauri::command]
pub async fn send_text_message(
msg: String,
peer_id: String,
swarm: State<'_, SwarmState>,
) -> Result<(), String> {
let message = Message {
kind: MessageKind::Text,
user_info: None,
text: msg,
};
let mut swarm = swarm.inner.lock().await;
let peer_id = PeerId::from_str(&peer_id).map_err(|e| e.to_string())?;
swarm
.behaviour_mut()
.request_response
.send_request(&peer_id, message);
Ok(())
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
|
Beta Was this translation helpful? Give feedback.
Swarm
is like aFuture
orStream
, it needs to be polled continuously to make progress. Most likely, a better approach would be to follow the file-sharing example (https://github.com/libp2p/rust-libp2p/tree/master/examples/file-sharing), spawn a separate event loop and share aControl
across your app that allows you to send message to the event loop.