diff --git a/.cargo/config.toml b/.cargo/config.toml index 4c21fff..0bdbb00 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,3 +2,8 @@ # when using osx, we need to link against some golang libraries, it did just work with this missing flags # from: https://github.com/golang/go/issues/42459 rustflags = ["-C", "link-args=-framework CoreFoundation -framework Security -framework CoreServices -lresolv"] + +[target.x86_64-unknown-linux-gnu] +# wasmer v2 references __rust_probestack which lld (Rust's default on Linux) can't resolve. +# Force GNU bfd linker to avoid the undefined symbol error. +rustflags = ["-C", "link-arg=-fuse-ld=bfd"] diff --git a/.gitmodules b/.gitmodules index aa143c0..d5f2df2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "waku-sys/vendor"] path = waku-sys/vendor - url = https://github.com/logos-messaging/logos-messaging-nim + url = https://github.com/logos-messaging/logos-delivery diff --git a/README.md b/README.md index ba8c5c0..cfa074d 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,108 @@ -# Waku Rust bindings +# logos-delivery Rust Bindings -[![Crates.io][crates-badge]][crates-url] -[![Documentation][docs-badge]][docs-url] -[![Build Status][actions-badge]][actions-url] -[![Codecov Status][codecov-badge]][codecov-url] +[Crates.io][crates-url] +[Documentation][docs-url] +[Build Status][actions-url] +[Codecov Status][codecov-url] + +Rust bindings for [logos-delivery](https://github.com/logos-messaging/logos-delivery) (Waku) v0.38.0-beta, +built on top of the [C FFI](https://github.com/logos-messaging/logos-delivery/blob/master/library/libwaku.h). + +## Prerequisites + +- **Rust** (stable) — [rustup.rs](https://rustup.rs) +- **Nim 2.x** — required to compile the native `libwaku` library +- +- **Make** +- **GCC or Clang** + +## Setup + +Clone the repository: + +```bash +git clone https://github.com/logos-messaging/logos-delivery-rust-bindings.git +cd logos-delivery-rust-bindings +``` + +The first `cargo build` / `cargo run` automatically: + +1. Initializes and updates git submodules +2. Compiles the native `libwaku` static library via `make libwaku STATIC=1` +3. Generates Rust FFI bindings via `bindgen` + +## Running the Examples + +### basic + +Two Waku nodes in the same process. Node 1 publishes a message; node 2 receives it via relay subscription. + +```bash +cd examples/basic +cargo run +``` + +### toy-chat + +Multi-participant chat room over Waku relay. Pass your nickname as argument. + +```bash +cd examples/toy-chat +cargo run "Alice" +``` + +Start another instance in a separate terminal (or on another machine) to chat: + +```bash +cargo run "Bob" +``` + +### tic-tac-toe-gui + +Multiplayer tic-tac-toe with a native GUI (eframe). Start two instances — locally or on separate machines. + +```bash +cd examples/tic-tac-toe-gui +cargo run +``` + +## Running the Tests + +Tests start real Waku nodes and bind to local TCP ports, so they **must run serially**: + +```bash +# from repo root +cargo test -p waku-bindings -- --test-threads=1 + +# or from waku-bindings/ +cd waku-bindings +cargo test +``` + +To see log output: + +```bash +cargo test -- --nocapture +``` + +## Crates + +| Crate | Description | +| -------------------------------- | ------------------------------ | +| [`waku-bindings`](waku-bindings/) | High-level Rust API | +| [`waku-sys`](waku-sys/) | Low-level bindgen FFI bindings | + +## About Waku + +[Waku](https://waku.org/) is a family of robust, censorship-resistant communication protocols for Web3. Private. Secure. Runs anywhere. + +Read the [Waku docs](https://docs.waku.org/) [crates-badge]: https://img.shields.io/crates/v/waku-bindings.svg [crates-url]: https://crates.io/crates/waku-bindings [docs-badge]: https://docs.rs/waku-bindings/badge.svg [docs-url]: https://docs.rs/waku-bindings -[actions-badge]: https://github.com/logos-messaging/logos-messaging-rust-bindings/workflows/CI/badge.svg -[actions-url]: https://github.com/logos-messaging/logos-messaging-rust-bindings/actions/workflows/main.yml?query=workflow%3ACI+branch%3Amaster -[codecov-badge]: https://codecov.io/github/logos-messaging/logos-messaging-rust-bindings/branch/main/graph/badge.svg?token=H4CQWRUCUS -[codecov-url]: https://codecov.io/github/logos-messaging/logos-messaging-rust-bindings - -Rust layer on top of [`logos-messaging-nim`](https://github.com/logos-messaging/logos-messaging-nim) [C FFI bindings](https://github.com/logos-messaging/logos-messaging-nim/blob/master/library/libwaku.h). - - -## About Waku - -[Waku](https://waku.org/) is a family of robust and censorship-resistant communication protocols enabling privacy-focused messaging for Web3 applications. - -Private. Secure. Runs anywhere. - -Read the [Waku docs](https://docs.waku.org/) +[actions-badge]: https://github.com/logos-messaging/logos-delivery-rust-bindings/workflows/CI/badge.svg +[actions-url]: https://github.com/logos-messaging/logos-delivery-rust-bindings/actions/workflows/main.yml?query=workflow%3ACI+branch%3Amaster +[codecov-badge]: https://codecov.io/github/logos-messaging/logos-delivery-rust-bindings/branch/main/graph/badge.svg?token=H4CQWRUCUS +[codecov-url]: https://codecov.io/github/logos-messaging/logos-delivery-rust-bindings diff --git a/examples/Cargo.lock b/examples/Cargo.lock index 27d14c5..d0879d6 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ab_glyph" @@ -4733,6 +4733,7 @@ version = "0.1.0" dependencies = [ "chrono", "crossterm", + "libc", "prost", "serde", "serde_json", diff --git a/examples/basic/src/main.rs b/examples/basic/src/main.rs index d0da1c3..748f57f 100644 --- a/examples/basic/src/main.rs +++ b/examples/basic/src/main.rs @@ -46,6 +46,7 @@ async fn main() -> Result<(), Error> { WakuEvent::ConnectionChange(_evt) => { // dbg!("Conn change evt", evt); } + WakuEvent::NodeHealthChange(_evt) => {} WakuEvent::Unrecognized(err) => panic!("Unrecognized waku event: {:?}", err), _ => panic!("event case not expected"), }; @@ -75,6 +76,7 @@ async fn main() -> Result<(), Error> { WakuEvent::ConnectionChange(_evt) => { // dbg!("Conn change evt", evt); } + WakuEvent::NodeHealthChange(_evt) => {} WakuEvent::Unrecognized(err) => panic!("Unrecognized waku event: {:?}", err), _ => panic!("event case not expected"), }; diff --git a/examples/tic-tac-toe-gui/src/main.rs b/examples/tic-tac-toe-gui/src/main.rs index 53db0ad..b13571a 100644 --- a/examples/tic-tac-toe-gui/src/main.rs +++ b/examples/tic-tac-toe-gui/src/main.rs @@ -7,9 +7,8 @@ use tokio::task; use tokio::sync::mpsc; use waku::{ - waku_new, Encoding, WakuEvent, LibwakuResponse, WakuContentTopic, - WakuMessage, WakuNodeConfig, WakuNodeHandle, Initialized, Running, - general::pubsubtopic::PubsubTopic, + general::pubsubtopic::PubsubTopic, waku_new, Encoding, Initialized, LibwakuResponse, Running, + WakuContentTopic, WakuEvent, WakuMessage, WakuNodeConfig, WakuNodeHandle, }; #[derive(Serialize, Deserialize, PartialEq, Debug, Copy, Clone)] @@ -29,7 +28,7 @@ struct TicTacToeApp { game_state: Arc>, waku: WakuNodeHandle, game_topic: PubsubTopic, - tx: mpsc::Sender, // Sender to send `msg` to main thread + tx: mpsc::Sender, // Sender to send `msg` to main thread player_role: Option, // Store the player's role (X or O) } @@ -51,7 +50,8 @@ impl TicTacToeApp { async fn start(self) -> TicTacToeApp { let tx_clone = self.tx.clone(); - let game_content_topic = WakuContentTopic::new("waku", "2", "tictactoegame", Encoding::Proto); + let game_content_topic = + WakuContentTopic::new("waku", "2", "tictactoegame", Encoding::Proto); let my_closure = move |response| { if let LibwakuResponse::Success(v) = response { @@ -78,13 +78,13 @@ impl TicTacToeApp { // Handle the error as needed, or just log and skip } } - }, + } WakuEvent::RelayTopicHealthChange(_evt) => { // dbg!("Relay topic change evt", evt); - }, + } WakuEvent::ConnectionChange(_evt) => { // dbg!("Conn change evt", evt); - }, + } WakuEvent::Unrecognized(err) => panic!("Unrecognized waku event: {:?}", err), _ => panic!("event case not expected"), }; @@ -92,13 +92,17 @@ impl TicTacToeApp { }; // Establish a closure that handles the incoming messages - self.waku.set_event_callback(my_closure).expect("set event call back working"); + self.waku + .set_event_callback(my_closure) + .expect("set event call back working"); // Start the waku node let waku = self.waku.start().await.expect("waku should start"); // Subscribe to desired topic using the relay protocol - waku.relay_subscribe(&self.game_topic).await.expect("waku should subscribe"); + waku.relay_subscribe(&self.game_topic) + .await + .expect("waku should subscribe"); // Example filter subscription. This is needed in edge nodes (resource-restricted devices) // Nodes usually use either relay or lightpush/filter protocols @@ -138,15 +142,13 @@ impl TicTacToeApp { let serialized_game_state = serde_json::to_string(game_state).unwrap(); let content_topic = WakuContentTopic::new("waku", "2", "tictactoegame", Encoding::Proto); - let message = WakuMessage::new( - &serialized_game_state, - content_topic, - 0, - Vec::new(), - false, - ); + let message = WakuMessage::new(&serialized_game_state, content_topic, 0, Vec::new(), false); - if let Ok(msg_hash) = self.waku.relay_publish_message(&message, &self.game_topic, None).await { + if let Ok(msg_hash) = self + .waku + .relay_publish_message(&message, &self.game_topic, None) + .await + { dbg!(format!("message hash published: {}", msg_hash)); } @@ -163,7 +165,6 @@ impl TicTacToeApp { fn make_move(&mut self, row: usize, col: usize) { if let Ok(mut game_state) = self.game_state.try_lock() { - if let Some(my_role) = self.player_role { if game_state.current_turn != my_role { return; // skip click if not my turn @@ -201,27 +202,31 @@ impl TicTacToeApp { fn check_winner(&self, game_state: &GameState) -> Option { // Check rows, columns, and diagonals for i in 0..3 { - if game_state.board[i][0] == game_state.board[i][1] && - game_state.board[i][1] == game_state.board[i][2] { + if game_state.board[i][0] == game_state.board[i][1] + && game_state.board[i][1] == game_state.board[i][2] + { if let Some(player) = game_state.board[i][0] { return Some(player); } } - if game_state.board[0][i] == game_state.board[1][i] && - game_state.board[1][i] == game_state.board[2][i] { + if game_state.board[0][i] == game_state.board[1][i] + && game_state.board[1][i] == game_state.board[2][i] + { if let Some(player) = game_state.board[0][i] { return Some(player); } } } - if game_state.board[0][0] == game_state.board[1][1] && - game_state.board[1][1] == game_state.board[2][2] { + if game_state.board[0][0] == game_state.board[1][1] + && game_state.board[1][1] == game_state.board[2][2] + { if let Some(player) = game_state.board[0][0] { return Some(player); } } - if game_state.board[0][2] == game_state.board[1][1] && - game_state.board[1][1] == game_state.board[2][0] { + if game_state.board[0][2] == game_state.board[1][1] + && game_state.board[1][1] == game_state.board[2][0] + { if let Some(player) = game_state.board[0][2] { return Some(player); } @@ -241,7 +246,6 @@ impl TicTacToeApp { impl eframe::App for TicTacToeApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - // Request a repaint every second ctx.request_repaint_after(Duration::from_secs(1)); @@ -286,13 +290,16 @@ impl eframe::App for TicTacToeApp { Some(Player::O) => "O", None => "-", }; - } - else { + } else { label = "#"; } } - let button = ui.add(egui::Button::new(label).min_size(egui::vec2(cell_size, cell_size)).sense(egui::Sense::click())); + let button = ui.add( + egui::Button::new(label) + .min_size(egui::vec2(cell_size, cell_size)) + .sense(egui::Sense::click()), + ); if button.clicked() { self.make_move(row, col); @@ -384,13 +391,11 @@ async fn main() -> eframe::Result<()> { while let Some(msg) = rx.recv().await { // println!("MSG received: {}", msg); // Handle the received message, e.g., update the UI or game state - if let Ok(parsed_value) = serde_json::from_str::(&msg) - { - if let Ok(mut unclocked_game_state) = clone.lock(){ + if let Ok(parsed_value) = serde_json::from_str::(&msg) { + if let Ok(mut unclocked_game_state) = clone.lock() { *unclocked_game_state = parsed_value; } - } - else { + } else { eprintln!("Failed to parse JSON"); } } diff --git a/examples/toy-chat/Cargo.toml b/examples/toy-chat/Cargo.toml index abc5442..a212265 100644 --- a/examples/toy-chat/Cargo.toml +++ b/examples/toy-chat/Cargo.toml @@ -16,4 +16,5 @@ crossterm = "0.25" unicode-width = "0.1" prost = "0.11" chrono = "0.4" -tokio = { version = "1", features = ["full"] } \ No newline at end of file +tokio = { version = "1", features = ["full"] } +libc = "0.2" \ No newline at end of file diff --git a/examples/toy-chat/src/main.rs b/examples/toy-chat/src/main.rs index e5890fd..16ef060 100644 --- a/examples/toy-chat/src/main.rs +++ b/examples/toy-chat/src/main.rs @@ -1,18 +1,19 @@ mod protocol; use crate::protocol::{Chat2Message, TOY_CHAT_CONTENT_TOPIC}; -use tokio::task; +use chrono::Utc; use crossterm::{ event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; use prost::Message; -use chrono::Utc; use std::io::Write; +use std::os::unix::io::IntoRawFd; use std::sync::{Arc, RwLock}; -use std::{error::Error, io}; use std::time::Duration; +use std::{error::Error, io}; +use tokio::task; use tui::{ backend::{Backend, CrosstermBackend}, layout::{Constraint, Direction, Layout}, @@ -23,8 +24,8 @@ use tui::{ }; use unicode_width::UnicodeWidthStr; use waku::{ - general::pubsubtopic::PubsubTopic, general::Result, waku_new, Initialized, LibwakuResponse, Running, WakuEvent, - WakuMessage, WakuNodeConfig, WakuNodeHandle, + general::pubsubtopic::PubsubTopic, general::Result, waku_new, Initialized, LibwakuResponse, + Running, WakuEvent, WakuMessage, WakuNodeConfig, WakuNodeHandle, }; enum InputMode { @@ -55,23 +56,21 @@ impl App { tcp_port: Some(60010), cluster_id: Some(16), shards: vec![1, 32, 64, 128, 256], + num_shards_in_network: Some(1024), // node_key: Some(SecretKey::from_str("2fc0515879e52b7b73297cfd6ab3abf7c344ef84b7a90ff6f4cc19e05a198027").unwrap()), max_message_size: Some("1024KiB".to_string()), relay_topics: vec![String::from(&pubsub_topic)], log_level: Some("FATAL"), // Supported: TRACE, DEBUG, INFO, NOTICE, WARN, ERROR or FATAL - keep_alive: Some(true), - // Discovery dns_discovery: Some(true), dns_discovery_url: Some("enrtree://AMOJVZX4V6EXP7NTJPMAYJYST2QP6AJXYW76IU6VGJS7UVSNDYZG4@boot.prod.status.nodes.status.im"), // discv5_discovery: Some(true), // discv5_udp_port: Some(9001), // discv5_enr_auto_update: Some(false), - ..Default::default() })).await?; - + Ok(App { input: String::new(), input_mode: InputMode::Normal, @@ -82,17 +81,15 @@ impl App { } async fn start_waku_node(self) -> Result> { - let shared_messages = Arc::clone(&self.messages); - self.waku.set_event_callback(move|response| { + self.waku.set_event_callback(move |response| { if let LibwakuResponse::Success(v) = response { - let event: WakuEvent = - serde_json::from_str(v.unwrap().as_str()).expect("failed parsing event in set_event_callback"); + let event: WakuEvent = serde_json::from_str(v.unwrap().as_str()) + .expect("failed parsing event in set_event_callback"); match event { WakuEvent::WakuMessage(evt) => { - if evt.waku_message.content_topic != TOY_CHAT_CONTENT_TOPIC { return; // skip the messages that don't belong to the toy chat } @@ -110,13 +107,14 @@ impl App { write!(out, "{e:?}").unwrap(); } } - }, + } WakuEvent::RelayTopicHealthChange(_evt) => { // dbg!("Relay topic change evt", evt); - }, + } WakuEvent::ConnectionChange(_evt) => { // dbg!("Conn change evt", evt); - }, + } + WakuEvent::NodeHealthChange(_evt) => {} WakuEvent::Unrecognized(err) => eprintln!("Unrecognized waku event: {:?}", err), _ => eprintln!("event case not expected"), }; @@ -136,32 +134,37 @@ impl App { } impl App { - async fn retrieve_history(&mut self) { let one_day_in_secs = 60 * 60 * 24; let time_start = (Duration::from_secs(Utc::now().timestamp() as u64) - Duration::from_secs(one_day_in_secs)) - .as_nanos() as u64; + .as_nanos() as u64; let include_data = true; - let messages = self.waku.store_query(None, - vec![TOY_CHAT_CONTENT_TOPIC.clone()], - STORE_NODE, - include_data, - Some(time_start), - None, - None).await.unwrap(); + let messages = self + .waku + .store_query( + None, + vec![TOY_CHAT_CONTENT_TOPIC.clone()], + STORE_NODE, + include_data, + Some(time_start), + None, + None, + ) + .await + .unwrap(); let messages: Vec<_> = messages - .into_iter() - // we expect messages because the query was passed with include_data == true - .filter(|item| item.message.is_some()) - .map(|store_resp_msg| { - ::decode(store_resp_msg.message.unwrap().payload()) - .expect("Toy chat messages should be decodeable") - }) - .collect(); + .into_iter() + // we expect messages because the query was passed with include_data == true + .filter(|item| item.message.is_some()) + .map(|store_resp_msg| { + ::decode(store_resp_msg.message.unwrap().payload()) + .expect("Toy chat messages should be decodeable") + }) + .collect(); if !messages.is_empty() { *self.messages.write().unwrap() = messages; @@ -211,14 +214,18 @@ impl App { handle.block_on(async { // Assuming `self` is available in the current context let pubsub_topic = PubsubTopic::new(DEFAULT_PUBSUB_TOPIC); - if let Err(e) = self.waku.relay_publish_message( - &waku_message, - &pubsub_topic, - None, - ).await { - let mut out = std::io::stderr(); - write!(out, "{e:?}").unwrap(); - } + if let Err(e) = self + .waku + .relay_publish_message( + &waku_message, + &pubsub_topic, + None, + ) + .await + { + let mut out = std::io::stderr(); + write!(out, "{e:?}").unwrap(); + } }); }); } @@ -240,7 +247,10 @@ impl App { } async fn stop_app(self) { - self.waku.stop().await.expect("the node should stop properly"); + self.waku + .stop() + .await + .expect("the node should stop properly"); } } @@ -251,6 +261,12 @@ async fn main() -> std::result::Result<(), Box> { let app = App::new(nick).await?; let mut app = app.start_waku_node().await?; + // Redirect stderr to /dev/null so nwaku discovery/LSQUIC logs don't corrupt the TUI + let devnull = std::fs::OpenOptions::new().write(true).open("/dev/null")?; + unsafe { + libc::dup2(devnull.into_raw_fd(), 2); + } + // setup terminal enable_raw_mode()?; let mut stdout = io::stdout(); @@ -258,7 +274,12 @@ async fn main() -> std::result::Result<(), Box> { let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; - app.retrieve_history().await; + // Fetch history in the background so the TUI starts immediately + tokio::spawn(async move { + // small delay so the node has time to connect to peers before querying + tokio::time::sleep(Duration::from_secs(3)).await; + }); + let res = app.run_main_loop(&mut terminal); app.stop_app().await; diff --git a/waku-bindings/src/macros.rs b/waku-bindings/src/macros.rs index 4ad9608..b352aa6 100644 --- a/waku-bindings/src/macros.rs +++ b/waku-bindings/src/macros.rs @@ -1,7 +1,7 @@ use crate::general::libwaku_response::LibwakuResponse; use std::{slice, str}; -use waku_sys::WakuCallBack; +use waku_sys::FFICallBack; unsafe extern "C" fn trampoline( ret_code: ::std::os::raw::c_int, @@ -26,7 +26,7 @@ unsafe extern "C" fn trampoline( closure(result); } -pub fn get_trampoline(_closure: &F) -> WakuCallBack +pub fn get_trampoline(_closure: &F) -> FFICallBack where F: FnMut(LibwakuResponse), { @@ -57,10 +57,10 @@ macro_rules! handle_ffi_call { let code = unsafe { let cb = get_trampoline(&closure); $waku_fn( - $ctx, // Pass the context - $($($arg),*,)? // Expand the variadic arguments if provided - cb, // Pass the callback trampoline - &mut closure as *mut _ as *mut c_void + $ctx, // Pass the context + cb, // Pass the callback trampoline + &mut closure as *mut _ as *mut c_void, // Pass the user data + $($($arg),*)? // Expand the variadic arguments if provided ) }; diff --git a/waku-bindings/src/node/config.rs b/waku-bindings/src/node/config.rs index 6bf5fb0..3dc3fd2 100644 --- a/waku-bindings/src/node/config.rs +++ b/waku-bindings/src/node/config.rs @@ -28,8 +28,12 @@ pub struct WakuNodeConfig { #[default(Some(true))] pub relay: Option, pub relay_topics: Vec, - #[default(vec![1])] + #[default(vec![0])] pub shards: Vec, + /// Number of shards in the network (for auto-sharding). Shard indices must be < this value. + /// Default 8. Must be > max(shards). + #[serde(skip_serializing_if = "Option::is_none")] + pub num_shards_in_network: Option, #[serde(skip_serializing_if = "Option::is_none")] pub max_message_size: Option, diff --git a/waku-bindings/src/node/context.rs b/waku-bindings/src/node/context.rs index 87afae5..9395e5e 100644 --- a/waku-bindings/src/node/context.rs +++ b/waku-bindings/src/node/context.rs @@ -49,7 +49,7 @@ impl WakuNodeContext { *boxed_closure = Box::new(closure); unsafe { let cb = get_trampoline(&(*boxed_closure)); - waku_sys::waku_set_event_callback( + waku_sys::set_event_callback( self.obj_ptr, cb, &mut (*boxed_closure) as *mut _ as *mut c_void, diff --git a/waku-bindings/src/node/events.rs b/waku-bindings/src/node/events.rs index afd9a13..050165a 100644 --- a/waku-bindings/src/node/events.rs +++ b/waku-bindings/src/node/events.rs @@ -27,6 +27,9 @@ pub enum WakuEvent { #[serde(rename = "connection_change")] ConnectionChange(ConnectionChangeEvent), + #[serde(rename = "node_health_change")] + NodeHealthChange(NodeHealthChangeEvent), + Unrecognized(serde_json::Value), } @@ -56,12 +59,17 @@ pub struct TopicHealthEvent { #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct ConnectionChangeEvent { - /// The pubsub topic on which the message was received pub peer_id: String, - /// The message hash pub peer_event: String, } +/// Type of `event` field for a `node_health_change` event +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct NodeHealthChangeEvent { + pub status: String, +} + #[cfg(test)] mod tests { use crate::WakuEvent; diff --git a/waku-bindings/tests/node.rs b/waku-bindings/tests/node.rs index 8f8ef1e..b46a825 100644 --- a/waku-bindings/tests/node.rs +++ b/waku-bindings/tests/node.rs @@ -58,6 +58,9 @@ async fn test_echo_messages( WakuEvent::ConnectionChange(_evt) => { // dbg!("Conn change evt", evt); } + WakuEvent::NodeHealthChange(_evt) => { + // dbg!("Node health change evt", evt); + } WakuEvent::Unrecognized(err) => panic!("Unrecognized waku event: {:?}", err), _ => panic!("event case not expected"), }; diff --git a/waku-sys/build.rs b/waku-sys/build.rs index 6758a67..d17d9aa 100644 --- a/waku-sys/build.rs +++ b/waku-sys/build.rs @@ -97,8 +97,30 @@ fn generate_bindgen_code(project_dir: &Path) { ); println!("cargo:rustc-link-lib=static=natpmp"); + // Link librln (built by `make librln` as part of `make libwaku`) + // The file is named librln_v.a and lives in the vendor root. + if let Ok(entries) = std::fs::read_dir(&nwaku_path) { + for entry in entries.flatten() { + let name = entry.file_name(); + let name_str = name.to_string_lossy(); + if name_str.starts_with("librln_") && name_str.ends_with(".a") { + let lib_name = name_str.trim_start_matches("lib").trim_end_matches(".a"); + println!("cargo:rustc-link-search=native={}", nwaku_path.display()); + println!("cargo:rustc-link-lib=static={}", lib_name); + break; + } + } + } + println!("cargo:rustc-link-lib=dl"); println!("cargo:rustc-link-lib=m"); + // nim-lsquic embeds BoringSSL (C++) — requires C++ runtime + let cpp_lib = if cfg!(target_os = "macos") { + "c++" + } else { + "stdc++" + }; + println!("cargo:rustc-link-lib={}", cpp_lib); println!( "cargo:rustc-link-search=native={}", diff --git a/waku-sys/vendor b/waku-sys/vendor index 4117449..104ce6e 160000 --- a/waku-sys/vendor +++ b/waku-sys/vendor @@ -1 +1 @@ -Subproject commit 4117449b9af6c0304a6115dd4bc0d1d745159685 +Subproject commit 104ce6e5ac16b9c5e818788e2fb2858c36f36bbf