Skip to content
Open
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
5 changes: 5 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -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
116 changes: 99 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -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]

[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 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).

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).
## Prerequisites

- **Rust** (stable) — [rustup.rs](https://rustup.rs)
- **Nim 2.x** — required to compile the native `libwaku` library
-
- **Make**
- **GCC or Clang**

## About Waku
## 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"
```

[Waku](https://waku.org/) is a family of robust and censorship-resistant communication protocols enabling privacy-focused messaging for Web3 applications.
### tic-tac-toe-gui

Private. Secure. Runs anywhere.
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-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
3 changes: 2 additions & 1 deletion examples/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions examples/basic/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
};
Expand Down Expand Up @@ -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"),
};
Expand Down
77 changes: 41 additions & 36 deletions examples/tic-tac-toe-gui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -29,7 +28,7 @@ struct TicTacToeApp<State> {
game_state: Arc<Mutex<GameState>>,
waku: WakuNodeHandle<State>,
game_topic: PubsubTopic,
tx: mpsc::Sender<String>, // Sender to send `msg` to main thread
tx: mpsc::Sender<String>, // Sender to send `msg` to main thread
player_role: Option<Player>, // Store the player's role (X or O)
}

Expand All @@ -51,7 +50,8 @@ impl TicTacToeApp<Initialized> {

async fn start(self) -> TicTacToeApp<Running> {
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 {
Expand All @@ -78,27 +78,31 @@ impl TicTacToeApp<Initialized> {
// 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"),
};
}
};

// 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
Expand Down Expand Up @@ -138,15 +142,13 @@ impl TicTacToeApp<Running> {
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));
}

Expand All @@ -163,7 +165,6 @@ impl TicTacToeApp<Running> {

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
Expand Down Expand Up @@ -201,27 +202,31 @@ impl TicTacToeApp<Running> {
fn check_winner(&self, game_state: &GameState) -> Option<Player> {
// 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);
}
Expand All @@ -241,7 +246,6 @@ impl TicTacToeApp<Running> {

impl eframe::App for TicTacToeApp<Running> {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {

// Request a repaint every second
ctx.request_repaint_after(Duration::from_secs(1));

Expand Down Expand Up @@ -286,13 +290,16 @@ impl eframe::App for TicTacToeApp<Running> {
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);
Expand Down Expand Up @@ -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::<GameState>(&msg)
{
if let Ok(mut unclocked_game_state) = clone.lock(){
if let Ok(parsed_value) = serde_json::from_str::<GameState>(&msg) {
if let Ok(mut unclocked_game_state) = clone.lock() {
*unclocked_game_state = parsed_value;
}
}
else {
} else {
eprintln!("Failed to parse JSON");
}
}
Expand Down
3 changes: 2 additions & 1 deletion examples/toy-chat/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ crossterm = "0.25"
unicode-width = "0.1"
prost = "0.11"
chrono = "0.4"
tokio = { version = "1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
libc = "0.2"
Loading
Loading