refactor: use callbacks to interact with go-waku bindings and remove JsonResponse (#74)

This commit is contained in:
richΛrd 2023-11-02 13:59:41 -04:00 committed by GitHub
parent 3acddc9023
commit dc641645b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 472 additions and 197 deletions

3
Cargo.lock generated
View File

@ -1674,13 +1674,14 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "waku-bindings"
version = "0.3.1"
version = "0.4.0"
dependencies = [
"aes-gcm",
"base64 0.21.0",
"enr",
"futures",
"hex",
"libc",
"multiaddr",
"once_cell",
"rand",

View File

@ -27,6 +27,7 @@ sscanf = "0.4"
smart-default = "0.6"
url = "2.3"
waku-sys = { version = "0.4.0", path = "../waku-sys" }
libc = "0.2"
[dev-dependencies]
futures = "0.3.25"

View File

@ -4,10 +4,11 @@
use std::ffi::CString;
// crates
use aes_gcm::{Aes256Gcm, Key};
use libc::*;
use secp256k1::SecretKey;
// internal
use crate::general::{DecodedPayload, Result, WakuMessage};
use crate::utils::decode_and_free_response;
use crate::utils::{get_trampoline, handle_json_response};
/// Decrypt a message using a symmetric key
///
@ -28,14 +29,25 @@ pub fn waku_decode_symmetric(
.expect("CString should build properly from hex encoded symmetric key")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_decode_symmetric(message_ptr, symk_ptr);
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_decode_symmetric(
message_ptr,
symk_ptr,
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(message_ptr));
drop(CString::from_raw(symk_ptr));
res
out
};
decode_and_free_response(result_ptr)
handle_json_response(code, &result)
}
/// Decrypt a message using a symmetric key
@ -57,12 +69,23 @@ pub fn waku_decode_asymmetric(
.expect("CString should build properly from hex encoded symmetric key")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_decode_asymmetric(message_ptr, sk_ptr);
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_decode_asymmetric(
message_ptr,
sk_ptr,
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(message_ptr));
drop(CString::from_raw(sk_ptr));
res
out
};
decode_and_free_response(result_ptr)
handle_json_response(code, &result)
}

View File

@ -2,10 +2,11 @@
use std::ffi::CString;
// crates
use aes_gcm::{Aes256Gcm, Key};
use libc::*;
use secp256k1::{PublicKey, SecretKey};
// internal
use crate::general::{Result, WakuMessage};
use crate::utils::decode_and_free_response;
use crate::utils::{get_trampoline, handle_json_response};
/// Optionally sign and encrypt a message using asymmetric encryption
pub fn waku_encode_asymmetric(
@ -16,7 +17,7 @@ pub fn waku_encode_asymmetric(
let pk = hex::encode(public_key.serialize_uncompressed());
let sk = signing_key
.map(|signing_key| hex::encode(signing_key.secret_bytes()))
.unwrap_or_else(String::new);
.unwrap_or_default();
let message_ptr = CString::new(
serde_json::to_string(&message)
.expect("WakuMessages should always be able to success serializing"),
@ -30,15 +31,27 @@ pub fn waku_encode_asymmetric(
.expect("CString should build properly from hex encoded signing key")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_encode_asymmetric(message_ptr, pk_ptr, sk_ptr);
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_encode_asymmetric(
message_ptr,
pk_ptr,
sk_ptr,
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(message_ptr));
drop(CString::from_raw(pk_ptr));
drop(CString::from_raw(sk_ptr));
res
out
};
decode_and_free_response(result_ptr)
handle_json_response(code, &result)
}
/// Optionally sign and encrypt a message using symmetric encryption
@ -50,7 +63,7 @@ pub fn waku_encode_symmetric(
let symk = hex::encode(symmetric_key.as_slice());
let sk = signing_key
.map(|signing_key| hex::encode(signing_key.secret_bytes()))
.unwrap_or_else(String::new);
.unwrap_or_default();
let message_ptr = CString::new(
serde_json::to_string(&message)
.expect("WakuMessages should always be able to success serializing"),
@ -64,13 +77,25 @@ pub fn waku_encode_symmetric(
.expect("CString should build properly from hex encoded signing key")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_encode_symmetric(message_ptr, symk_ptr, sk_ptr);
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_encode_symmetric(
message_ptr,
symk_ptr,
sk_ptr,
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(message_ptr));
drop(CString::from_raw(symk_ptr));
drop(CString::from_raw(sk_ptr));
res
out
};
decode_and_free_response(result_ptr)
handle_json_response(code, &result)
}

View File

@ -5,7 +5,7 @@
//! When an event is emitted, this callback will be triggered receiving a [`Signal`]
// std
use std::ffi::{c_char, CStr};
use std::ffi::{c_char, c_void, CStr};
use std::ops::Deref;
use std::sync::Mutex;
// crates
@ -79,7 +79,7 @@ fn set_callback<F: FnMut(Signal) + Send + Sync + 'static>(f: F) {
/// Wrapper callback, it transformst the `*const c_char` into a [`Signal`]
/// and executes the [`CALLBACK`] funtion with it
extern "C" fn callback(data: *const c_char) {
extern "C" fn callback(data: *const c_char, _user_data: *mut c_void) {
let raw_response = unsafe { CStr::from_ptr(data) }
.to_str()
.expect("Not null ptr");
@ -95,7 +95,7 @@ extern "C" fn callback(data: *const c_char) {
/// which are used to react to asynchronous events in Waku
pub fn waku_set_event_callback<F: FnMut(Signal) + Send + Sync + 'static>(f: F) {
set_callback(f);
unsafe { waku_sys::waku_set_event_callback(callback as *mut std::ffi::c_void) };
unsafe { waku_sys::waku_set_event_callback(Some(callback)) };
}
#[cfg(test)]

View File

@ -51,29 +51,9 @@ impl Display for ProtocolId {
}
}
/// JsonResponse wrapper.
/// `go-waku` ffi returns this type as a `char *` as per the [specification](https://rfc.vac.dev/spec/36/#jsonresponse-type)
/// This is internal, as it is better to use rust plain `Result` type.
#[derive(Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
pub(crate) enum JsonResponse<T> {
Result(T),
Error(String),
}
/// Waku response, just a `Result` with an `String` error.
pub type Result<T> = std::result::Result<T, String>;
/// Convenient we can transform a [`JsonResponse`] into a [`std::result::Result`]
impl<T> From<JsonResponse<T>> for Result<T> {
fn from(response: JsonResponse<T>) -> Self {
match response {
JsonResponse::Result(t) => Ok(t),
JsonResponse::Error(e) => Err(e),
}
}
}
// TODO: Properly type and deserialize payload form base64 encoded string
/// Waku message in JSON format.
/// as per the [specification](https://rfc.vac.dev/spec/36/#jsonmessage-type)

View File

@ -3,11 +3,12 @@ use std::ffi::CString;
use std::time::Duration;
// crates
use enr::Enr;
use libc::*;
use multiaddr::Multiaddr;
use serde::Deserialize;
use url::{Host, Url};
// internal
use crate::utils::decode_and_free_response;
use crate::utils::{get_trampoline, handle_json_response, handle_no_response};
use crate::{PeerId, Result};
#[derive(Deserialize, Debug)]
@ -30,15 +31,16 @@ pub fn waku_dns_discovery(
let url = CString::new(url.to_string())
.expect("CString should build properly from a valid Url")
.into_raw();
let server = CString::new(
server
.map(|host| host.to_string())
.unwrap_or_else(|| "".to_string()),
)
.expect("CString should build properly from a String nameserver")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_dns_discovery(
let server = CString::new(server.map(|host| host.to_string()).unwrap_or_default())
.expect("CString should build properly from a String nameserver")
.into_raw();
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_dns_discovery(
url,
server,
timeout
@ -49,14 +51,17 @@ pub fn waku_dns_discovery(
.expect("Duration as milliseconds should fit in a i32")
})
.unwrap_or(0),
cb,
&mut closure as *mut _ as *mut c_void,
);
// Recover strings and drop them
drop(CString::from_raw(url));
drop(CString::from_raw(server));
res
out
};
decode_and_free_response(result_ptr)
handle_json_response(code, &result)
}
/// Update the bootnodes used by DiscoveryV5 by passing a list of ENRs
@ -68,13 +73,23 @@ pub fn waku_discv5_update_bootnodes(bootnodes: Vec<String>) -> Result<()> {
.expect("CString should build properly from the string vector")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_discv5_update_bootnodes(bootnodes_ptr);
let mut error: String = Default::default();
let error_cb = |v: &str| error = v.to_string();
let code = unsafe {
let mut closure = error_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_discv5_update_bootnodes(
bootnodes_ptr,
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(bootnodes_ptr));
res
out
};
decode_and_free_response::<bool>(result_ptr).map(|_| ())
handle_no_response(code, &error)
}
#[cfg(test)]

View File

@ -4,11 +4,11 @@
use std::ffi::CString;
use std::time::Duration;
// crates
use libc::*;
// internal
use crate::general::Result;
use crate::general::{FilterSubscription, PeerId};
use crate::utils::decode_and_free_response;
use crate::utils::{get_trampoline, handle_no_response};
/// Creates a subscription in a lightnode for messages that matches a content filter and optionally a [`WakuPubSubTopic`](`crate::general::WakuPubSubTopic`)
/// As per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_filter_subscribechar-filterjson-char-peerid-int-timeoutms)
@ -27,20 +27,29 @@ pub fn waku_filter_subscribe(
.expect("PeerId should always be able to be serialized")
.into_raw();
let result_ptr = unsafe {
let result_ptr = waku_sys::waku_legacy_filter_subscribe(
let mut error: String = Default::default();
let error_cb = |v: &str| error = v.to_string();
let code = unsafe {
let mut closure = error_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_legacy_filter_subscribe(
filter_subscription_ptr,
peer_id_ptr,
timeout
.as_millis()
.try_into()
.expect("Duration as milliseconds should fit in a i32"),
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(filter_subscription_ptr));
drop(CString::from_raw(peer_id_ptr));
result_ptr
out
};
decode_and_free_response::<bool>(result_ptr).map(|_| ())
handle_no_response(code, &error)
}
/// Removes subscriptions in a light node matching a content filter and, optionally, a [`WakuPubSubTopic`](`crate::general::WakuPubSubTopic`)
@ -55,17 +64,26 @@ pub fn waku_filter_unsubscribe(
)
.expect("CString should build properly from the serialized filter subscription")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_legacy_filter_unsubscribe(
let mut error: String = Default::default();
let error_cb = |v: &str| error = v.to_string();
let code = unsafe {
let mut closure = error_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_legacy_filter_unsubscribe(
filter_subscription_ptr,
timeout
.as_millis()
.try_into()
.expect("Duration as milliseconds should fit in a i32"),
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(filter_subscription_ptr));
res
out
};
decode_and_free_response::<bool>(result_ptr).map(|_| ())
handle_no_response(code, &error)
}

View File

@ -3,10 +3,12 @@
// std
use std::ffi::CString;
use std::time::Duration;
// crates
use libc::*;
// internal
use crate::general::{MessageId, PeerId, Result, WakuMessage, WakuPubSubTopic};
use crate::node::waku_default_pubsub_topic;
use crate::utils::decode_and_free_response;
use crate::utils::{get_trampoline, handle_response};
/// Publish a message using Waku Lightpush
/// As per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_lightpush_publishchar-messagejson-char-topic-char-peerid-int-timeoutms)
@ -31,8 +33,13 @@ pub fn waku_lightpush_publish(
let peer_id_ptr = CString::new(peer_id)
.expect("CString should build properly from peer id")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_lightpush_publish(
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_lightpush_publish(
message_ptr,
topic_ptr,
peer_id_ptr,
@ -44,12 +51,16 @@ pub fn waku_lightpush_publish(
.expect("Duration as milliseconds should fit in a i32")
})
.unwrap_or(0),
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(message_ptr));
drop(CString::from_raw(topic_ptr));
drop(CString::from_raw(peer_id_ptr));
res
out
};
decode_and_free_response(result_ptr)
handle_response(code, &result)
}

View File

@ -4,14 +4,15 @@
use multiaddr::Multiaddr;
use std::ffi::CString;
// crates
use libc::*;
// internal
use super::config::WakuNodeConfig;
use crate::general::{PeerId, Result};
use crate::utils::decode_and_free_response;
use crate::utils::{get_trampoline, handle_json_response, handle_no_response, handle_response};
/// Instantiates a Waku node
/// as per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_newchar-jsonconfig)
pub fn waku_new(config: Option<WakuNodeConfig>) -> Result<bool> {
pub fn waku_new(config: Option<WakuNodeConfig>) -> Result<()> {
let config = config.unwrap_or_default();
let config_ptr = CString::new(
@ -21,41 +22,75 @@ pub fn waku_new(config: Option<WakuNodeConfig>) -> Result<bool> {
.expect("CString should build properly from the config")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_new(config_ptr);
let mut error: String = Default::default();
let error_cb = |v: &str| error = v.to_string();
let code = unsafe {
let mut closure = error_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_new(config_ptr, cb, &mut closure as *mut _ as *mut c_void);
drop(CString::from_raw(config_ptr));
res
out
};
decode_and_free_response(result_ptr)
handle_no_response(code, &error)
}
/// Start a Waku node mounting all the protocols that were enabled during the Waku node instantiation.
/// as per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_start)
pub fn waku_start() -> Result<bool> {
let response_ptr = unsafe { waku_sys::waku_start() };
decode_and_free_response(response_ptr)
pub fn waku_start() -> Result<()> {
let mut error: String = Default::default();
let error_cb = |v: &str| error = v.to_string();
let code = unsafe {
let mut closure = error_cb;
let cb = get_trampoline(&closure);
waku_sys::waku_start(cb, &mut closure as *mut _ as *mut c_void)
};
handle_no_response(code, &error)
}
/// Stops a Waku node
/// as per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_stop)
pub fn waku_stop() -> Result<bool> {
let response_ptr = unsafe { waku_sys::waku_stop() };
decode_and_free_response(response_ptr)
pub fn waku_stop() -> Result<()> {
let mut error: String = Default::default();
let error_cb = |v: &str| error = v.to_string();
let code = unsafe {
let mut closure = error_cb;
let cb = get_trampoline(&closure);
waku_sys::waku_stop(cb, &mut closure as *mut _ as *mut c_void)
};
handle_no_response(code, &error)
}
/// If the execution is successful, the result is the peer ID as a string (base58 encoded)
/// as per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_stop)
pub fn waku_peer_id() -> Result<PeerId> {
let response_ptr = unsafe { waku_sys::waku_peerid() };
decode_and_free_response(response_ptr)
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
waku_sys::waku_peerid(cb, &mut closure as *mut _ as *mut c_void)
};
handle_response(code, &result)
}
/// Get the multiaddresses the Waku node is listening to
/// as per [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_listen_addresses)
pub fn waku_listen_addresses() -> Result<Vec<Multiaddr>> {
let response_ptr = unsafe { waku_sys::waku_listen_addresses() };
decode_and_free_response(response_ptr)
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
waku_sys::waku_listen_addresses(cb, &mut closure as *mut _ as *mut c_void)
};
handle_json_response(code, &result)
}
#[cfg(test)]

View File

@ -4,11 +4,12 @@
use std::ffi::CString;
use std::time::Duration;
// crates
use libc::*;
use multiaddr::Multiaddr;
use serde::Deserialize;
// internal
use crate::general::{PeerId, ProtocolId, Result};
use crate::utils::decode_and_free_response;
use crate::utils::{get_trampoline, handle_json_response, handle_no_response, handle_response};
/// Add a node multiaddress and protocol to the waku nodes peerstore.
/// As per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_add_peerchar-address-char-protocolid)
@ -20,14 +21,25 @@ pub fn waku_add_peers(address: &Multiaddr, protocol_id: ProtocolId) -> Result<Pe
.expect("CString should build properly from the protocol id")
.into_raw();
let response_ptr = unsafe {
let res = waku_sys::waku_add_peer(address_ptr, protocol_id_ptr);
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_add_peer(
address_ptr,
protocol_id_ptr,
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(address_ptr));
drop(CString::from_raw(protocol_id_ptr));
res
out
};
decode_and_free_response(response_ptr)
handle_response(code, &result)
}
/// Dial peer using a multiaddress
@ -42,18 +54,27 @@ pub fn waku_connect_peer_with_address(
let address_ptr = CString::new(address.to_string())
.expect("CString should build properly from multiaddress")
.into_raw();
let response_ptr = unsafe {
let res = waku_sys::waku_connect(
let mut error: String = Default::default();
let error_cb = |v: &str| error = v.to_string();
let code = unsafe {
let mut closure = error_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_connect(
address_ptr,
timeout
.map(|duration| duration.as_millis().try_into().unwrap_or(i32::MAX))
.unwrap_or(0),
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(address_ptr));
res
out
};
decode_and_free_response::<bool>(response_ptr).map(|_| ())
handle_no_response(code, &error)
}
/// Dial peer using a peer id
@ -65,18 +86,27 @@ pub fn waku_connect_peer_with_id(peer_id: &PeerId, timeout: Option<Duration>) ->
let peer_id_ptr = CString::new(peer_id.as_bytes())
.expect("CString should build properly from peer id")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_connect_peerid(
let mut error: String = Default::default();
let error_cb = |v: &str| error = v.to_string();
let code = unsafe {
let mut closure = error_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_connect_peerid(
peer_id_ptr,
timeout
.map(|duration| duration.as_millis().try_into().unwrap_or(i32::MAX))
.unwrap_or(0),
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(peer_id_ptr));
res
out
};
decode_and_free_response::<bool>(result_ptr).map(|_| ())
handle_no_response(code, &error)
}
/// Disconnect a peer using its peer id
@ -86,23 +116,33 @@ pub fn waku_disconnect_peer_with_id(peer_id: &PeerId) -> Result<()> {
.expect("CString should build properly from peer id")
.into_raw();
let response_ptr = unsafe {
let res = waku_sys::waku_disconnect(peer_id_ptr);
let mut error: String = Default::default();
let error_cb = |v: &str| error = v.to_string();
let code = unsafe {
let mut closure = error_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_disconnect(peer_id_ptr, cb, &mut closure as *mut _ as *mut c_void);
drop(CString::from_raw(peer_id_ptr));
res
out
};
decode_and_free_response::<bool>(response_ptr).map(|_| ())
handle_no_response(code, &error)
}
/// Get number of connected peers
/// As per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_peer_count)
pub fn waku_peer_count() -> Result<usize> {
let response_ptr = unsafe { waku_sys::waku_peer_cnt() };
let num_str = decode_and_free_response::<String>(response_ptr)?;
let num = num_str
.parse::<u32>()
.map_err(|_| "could not convert peer count into u32".to_string())?;
usize::try_from(num).map_err(|_| "could not convert peer count into usize".to_string())
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
waku_sys::waku_peer_cnt(cb, &mut closure as *mut _ as *mut c_void)
};
handle_response(code, &result)
}
/// Waku peer supported protocol
@ -153,8 +193,15 @@ pub type WakuPeers = Vec<WakuPeerData>;
/// Retrieve the list of peers known by the Waku node
/// As per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_peers)
pub fn waku_peers() -> Result<WakuPeers> {
let response_ptr = unsafe { waku_sys::waku_peers() };
decode_and_free_response(response_ptr)
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
waku_sys::waku_peers(cb, &mut closure as *mut _ as *mut c_void)
};
handle_json_response(code, &result)
}
#[cfg(test)]

View File

@ -1,11 +1,13 @@
//! Waku [relay](https://rfc.vac.dev/spec/36/#waku-relay) protocol related methods
// std
use std::ffi::{CStr, CString};
use std::ffi::CString;
use std::time::Duration;
// crates
use libc::*;
// internal
use crate::general::{Encoding, MessageId, Result, WakuContentTopic, WakuMessage, WakuPubSubTopic};
use crate::utils::decode_and_free_response;
use crate::utils::{get_trampoline, handle_json_response, handle_no_response, handle_response};
/// Create a content topic according to [RFC 23](https://rfc.vac.dev/spec/23/)
/// As per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_content_topicchar-applicationname-unsigned-int-applicationversion-char-contenttopicname-char-encoding)
@ -28,27 +30,29 @@ pub fn waku_create_content_topic(
.expect("Encoding should always transform to CString")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_content_topic(
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_content_topic(
application_name_ptr,
application_version,
content_topic_name_ptr,
encoding_ptr,
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(application_name_ptr));
drop(CString::from_raw(content_topic_name_ptr));
drop(CString::from_raw(encoding_ptr));
res
out
};
let result = unsafe { CStr::from_ptr(result_ptr) }
.to_str()
handle_response::<WakuContentTopic>(code, &result)
.expect("&str from result should always be extracted")
.parse()
.expect("Content topic data should be always parseable");
unsafe { waku_sys::waku_utils_free(result_ptr) };
result
}
/// Create a pubsub topic according to [RFC 23](https://rfc.vac.dev/spec/23/)
@ -61,44 +65,52 @@ pub fn waku_create_pubsub_topic(topic_name: &str, encoding: Encoding) -> WakuPub
.expect("Encoding should always transform to CString")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_pubsub_topic(topic_name_ptr, encoding_ptr);
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_pubsub_topic(
topic_name_ptr,
encoding_ptr,
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(topic_name_ptr));
drop(CString::from_raw(encoding_ptr));
res
out
};
let result = unsafe { CStr::from_ptr(result_ptr) }
.to_str()
.expect("&str from result should always be extracted")
.parse()
.expect("Pubsub topic data should be always parseable");
unsafe { waku_sys::waku_utils_free(result_ptr) };
result
handle_response(code, &result).expect("&str from result should always be extracted")
}
/// Default pubsub topic used for exchanging waku messages defined in [RFC 10](https://rfc.vac.dev/spec/10/)
pub fn waku_default_pubsub_topic() -> WakuPubSubTopic {
let result_ptr = unsafe { waku_sys::waku_default_pubsub_topic() };
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
waku_sys::waku_default_pubsub_topic(cb, &mut closure as *mut _ as *mut c_void)
};
let result = unsafe { CStr::from_ptr(result_ptr) }
.to_str()
.expect("&str from result should always be extracted")
.parse()
.expect("Default pubsub topic should always be parseable");
unsafe { waku_sys::waku_utils_free(result_ptr) };
result
handle_response(code, &result).expect("&str from result should always be extracted")
}
/// Get the list of subscribed pubsub topics in Waku Relay.
/// As per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_relay_topics)
pub fn waku_relay_topics() -> Result<Vec<String>> {
let result_ptr = unsafe { waku_sys::waku_relay_topics() };
decode_and_free_response(result_ptr)
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
waku_sys::waku_relay_topics(cb, &mut closure as *mut _ as *mut c_void)
};
handle_json_response(code, &result)
}
/// Publish a message using Waku Relay
@ -122,8 +134,12 @@ pub fn waku_relay_publish_message(
.expect("CString should build properly from pubsub topic")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_relay_publish(
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_relay_publish(
message_ptr,
pubsub_topic_ptr,
timeout
@ -134,13 +150,17 @@ pub fn waku_relay_publish_message(
.expect("Duration as milliseconds should fit in a i32")
})
.unwrap_or(0),
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(message_ptr));
drop(CString::from_raw(pubsub_topic_ptr));
res
out
};
decode_and_free_response(result_ptr)
handle_response(code, &result)
}
pub fn waku_enough_peers(pubsub_topic: Option<WakuPubSubTopic>) -> Result<bool> {
@ -152,13 +172,23 @@ pub fn waku_enough_peers(pubsub_topic: Option<WakuPubSubTopic>) -> Result<bool>
.expect("CString should build properly from pubsub topic")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_relay_enough_peers(pubsub_topic_ptr);
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_relay_enough_peers(
pubsub_topic_ptr,
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(pubsub_topic_ptr));
res
out
};
decode_and_free_response(result_ptr)
handle_response(code, &result)
}
pub fn waku_relay_subscribe(pubsub_topic: Option<WakuPubSubTopic>) -> Result<()> {
@ -170,13 +200,23 @@ pub fn waku_relay_subscribe(pubsub_topic: Option<WakuPubSubTopic>) -> Result<()>
.expect("CString should build properly from pubsub topic")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_relay_subscribe(pubsub_topic_ptr);
let mut error: String = Default::default();
let error_cb = |v: &str| error = v.to_string();
let code = unsafe {
let mut closure = error_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_relay_subscribe(
pubsub_topic_ptr,
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(pubsub_topic_ptr));
res
out
};
decode_and_free_response::<bool>(result_ptr).map(|_| ())
handle_no_response(code, &error)
}
pub fn waku_relay_unsubscribe(pubsub_topic: Option<WakuPubSubTopic>) -> Result<()> {
@ -188,11 +228,21 @@ pub fn waku_relay_unsubscribe(pubsub_topic: Option<WakuPubSubTopic>) -> Result<(
.expect("CString should build properly from pubsub topic")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_relay_unsubscribe(pubsub_topic_ptr);
let mut error: String = Default::default();
let error_cb = |v: &str| error = v.to_string();
let code = unsafe {
let mut closure = error_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_relay_subscribe(
pubsub_topic_ptr,
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(pubsub_topic_ptr));
res
out
};
decode_and_free_response::<bool>(result_ptr).map(|_| ())
handle_no_response(code, &error)
}

View File

@ -4,9 +4,10 @@
use std::ffi::CString;
use std::time::Duration;
// crates
use libc::*;
// internal
use crate::general::{PeerId, Result, StoreQuery, StoreResponse};
use crate::utils::decode_and_free_response;
use crate::utils::{get_trampoline, handle_json_response};
/// Retrieves historical messages on specific content topics. This method may be called with [`PagingOptions`](`crate::general::PagingOptions`),
/// to retrieve historical messages on a per-page basis. If the request included [`PagingOptions`](`crate::general::PagingOptions`),
@ -26,8 +27,12 @@ pub fn waku_store_query(
.expect("CString should build properly from peer id")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_store_query(
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
let out = waku_sys::waku_store_query(
query_ptr,
peer_id_ptr,
timeout
@ -38,13 +43,17 @@ pub fn waku_store_query(
.expect("Duration as milliseconds should fit in a i32")
})
.unwrap_or(0),
cb,
&mut closure as *mut _ as *mut c_void,
);
drop(CString::from_raw(query_ptr));
drop(CString::from_raw(peer_id_ptr));
res
out
};
decode_and_free_response(result_ptr)
handle_json_response(code, &result)
}
/// Retrieves locally stored historical messages on specific content topics from the local archive system. This method may be called with [`PagingOptions`](`crate::general::PagingOptions`),
@ -57,10 +66,19 @@ pub fn waku_local_store_query(query: &StoreQuery) -> Result<StoreResponse> {
)
.expect("CString should build properly from the serialized filter subscription")
.into_raw();
let result_ptr = unsafe {
let res = waku_sys::waku_store_local_query(query_ptr);
let mut result: String = Default::default();
let result_cb = |v: &str| result = v.to_string();
let code = unsafe {
let mut closure = result_cb;
let cb = get_trampoline(&closure);
let out =
waku_sys::waku_store_local_query(query_ptr, cb, &mut closure as *mut _ as *mut c_void);
drop(CString::from_raw(query_ptr));
res
out
};
decode_and_free_response(result_ptr)
handle_json_response(code, &result)
}

View File

@ -1,25 +1,66 @@
use crate::general::{JsonResponse, Result};
use crate::general::Result;
use core::str::FromStr;
use serde::de::DeserializeOwned;
use std::ffi::{c_char, CStr};
use std::ffi::CStr;
use waku_sys::WakuCallBack;
use waku_sys::{RET_ERR, RET_MISSING_CALLBACK, RET_OK};
pub fn decode<T: DeserializeOwned>(input: &str) -> Result<T> {
serde_json::from_str(input)
.map_err(|err| format!("could not deserialize waku response: {}", err))
}
/// Safety: The caller is responsible for ensuring that the pointer is valid for the duration of the call.
/// This takes a pointer to a C string coming from the waku lib, that data is consumed and then freed using [`waku_sys::waku_utils_free`].
pub fn decode_and_free_response<T: DeserializeOwned>(response_ptr: *mut c_char) -> Result<T> {
let response = unsafe { CStr::from_ptr(response_ptr) }
unsafe extern "C" fn trampoline<F>(
data: *const ::std::os::raw::c_char,
user_data: *mut ::std::os::raw::c_void,
) where
F: FnMut(&str),
{
let user_data = &mut *(user_data as *mut F);
let response = unsafe { CStr::from_ptr(data) }
.to_str()
.map_err(|err| {
format!(
"could not retrieve response from pointer returned by waku: {}",
err
)
})?;
})
.expect("could not retrieve response");
let response: JsonResponse<T> = serde_json::from_str(response)
.map_err(|err| format!("could not deserialize waku JsonResponse: {}", err))?;
unsafe {
waku_sys::waku_utils_free(response_ptr);
}
response.into()
user_data(response);
}
pub fn get_trampoline<F>(_closure: &F) -> WakuCallBack
where
F: FnMut(&str),
{
Some(trampoline::<F>)
}
pub fn handle_no_response(code: i32, error: &str) -> Result<()> {
match code {
RET_OK => Ok(()),
RET_ERR => Err(format!("waku error: {}", error)),
RET_MISSING_CALLBACK => Err("missing callback".to_string()),
_ => Err(format!("undefined return code {}", code)),
}
}
pub fn handle_json_response<F: DeserializeOwned>(code: i32, result: &str) -> Result<F> {
match code {
RET_OK => decode(result),
RET_ERR => Err(format!("waku error: {}", result)),
RET_MISSING_CALLBACK => Err("missing callback".to_string()),
_ => Err(format!("undefined return code {}", code)),
}
}
pub fn handle_response<F: FromStr>(code: i32, result: &str) -> Result<F> {
match code {
RET_OK => result
.parse()
.map_err(|_| format!("could not parse value: {}", result)),
RET_ERR => Err(format!("waku error: {}", result)),
RET_MISSING_CALLBACK => Err("missing callback".to_string()),
_ => Err(format!("undefined return code {}", code)),
}
}

View File

@ -1,5 +1,6 @@
use std::env;
use std::env::set_current_dir;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
@ -57,15 +58,24 @@ fn build_go_waku_lib(go_bin: &str, project_dir: &Path) {
set_current_dir(project_dir).expect("Going back to project dir");
}
fn patch_gowaku_lib() {
// Replacing cgo_utils.h as it is only needed when compiling go-waku bindings
let lib_dir: PathBuf = env::var_os("OUT_DIR").unwrap().into();
let file_path = lib_dir.join("libgowaku.h");
let data = fs::read_to_string(&file_path).expect("Unable to read file");
let new_data = data.replace("#include <cgo_utils.h>", "");
fs::write(&file_path, new_data).expect("Unable to write file");
}
fn generate_bindgen_code() {
let lib_dir: PathBuf = env::var_os("OUT_DIR").unwrap().into();
// let lib_dir = project_dir.join("vendor/build/lib");
println!("cargo:rustc-link-search={}", lib_dir.display());
println!("cargo:rustc-link-lib=static=gowaku");
println!("cargo:rerun-if-changed=libgowaku.h");
patch_gowaku_lib();
// Generate waku bindings with bindgen
let bindings = bindgen::Builder::default()
// The input header we would like to generate

@ -1 +1 @@
Subproject commit 8671e3a68c7c1cfefdf971f95217ea4f8baaeb32
Subproject commit b3bd45f01f1211cb18fb44ced5277758ab38eee7