From 0c1001d9900fe09e6eb0311587fd4d695a0f7587 Mon Sep 17 00:00:00 2001 From: Alberto Lerda <30939098+albertolerda@users.noreply.github.com> Date: Mon, 2 Oct 2023 10:11:08 +0100 Subject: [PATCH] feat: rust spawns zencode-exec (#731) updated tests in CI --- .github/workflows/main.yml | 3 +- bindings/rust/Cargo.toml | 9 +- bindings/rust/build.rs | 32 ------- bindings/rust/src/lib.rs | 165 ++++++++++++++++++------------------- bindings/rust/wrapper.h | 1 - 5 files changed, 86 insertions(+), 124 deletions(-) delete mode 100644 bindings/rust/build.rs delete mode 100644 bindings/rust/wrapper.h diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d9797f5b3..0a3dd0faa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -205,7 +205,8 @@ jobs: - run: | pip3 install meson ninja sudo apt-get install --force-yes zsh - - run: make linux-rust + - run: make linux + - run: cp src/zencode-exec /usr/local/bin/ - name: 🧪 test bindings rust-${{ matrix.rust }} working-directory: bindings/rust run: cargo test diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index 06316e453..391aa4f62 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -6,16 +6,15 @@ edition = "2018" documentation = "https://dev.zenroom.org/#/pages/zencode-cookbook-intro" license = "AGPL-3.0-only" description = "zenroom is a small, portable and secure language interpreter of a domain specific language called zencode, able to execute cryptographic operations and smart contracts in a multiplatform environment." -include = ["src/", "zenroom.h", "clib/*.a", "build.rs", "zenroom.h", "wrapper.h"] -build = "build.rs" +include = ["src/"] [lib] name = "zenroom" path = "src/lib.rs" crate-type = ["lib"] -[build-dependencies] -bindgen = "0.59.2" - [dev-dependencies] serde_json = "1.0.74" + +[dependencies] +base64 = "0.21.3" diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs deleted file mode 100644 index fd08ef7de..000000000 --- a/bindings/rust/build.rs +++ /dev/null @@ -1,32 +0,0 @@ -extern crate bindgen; - -use std::env; -use std::path::PathBuf; - -fn main() { - println!("cargo:rustc-link-lib=static=zenroom"); - println!("cargo:rustc-link-lib=static=lua"); - println!("cargo:rustc-link-lib=static=qpz"); - println!("cargo:rustc-link-lib=static=amcl_bls_BLS381"); - println!("cargo:rustc-link-lib=static=amcl_core"); - println!("cargo:rustc-link-lib=static=amcl_curve_BLS381"); - println!("cargo:rustc-link-lib=static=amcl_curve_SECP256K1"); - println!("cargo:rustc-link-lib=static=amcl_pairing_BLS381"); - println!("cargo:rustc-link-lib=static=ed25519"); - println!("cargo:rustc-link-lib=static=mimalloc-static"); - println!("cargo:rustc-link-search=native=clib"); - - println!("cargo:rerun-if-changed=wrapper.h"); - - let bindings = bindgen::Builder::default() - .header("wrapper.h") - .clang_arg("-I../../src") - .parse_callbacks(Box::new(bindgen::CargoCallbacks)) - .generate() - .expect("Unable to generate bindings"); - - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - bindings - .write_to_file(out_path.join("bindings.rs")) - .expect("Couldn't write bindings!"); -} diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 8efc9dcb8..dad0f61f9 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -1,15 +1,7 @@ -use std::ffi::{CStr, CString}; +use base64::{engine::general_purpose, Engine as _}; use std::fmt; -use std::sync::{Mutex, MutexGuard, Once}; - -mod c { - #![allow(non_upper_case_globals)] - #![allow(non_camel_case_types)] - #![allow(non_snake_case)] - #![allow(dead_code)] - #![allow(deref_nullptr)] - include!(concat!(env!("OUT_DIR"), "/bindings.rs")); -} +use std::io::prelude::*; +use std::process::{Command, Stdio}; #[derive(Clone, Debug)] pub struct ZenResult { @@ -38,95 +30,72 @@ impl fmt::Display for ZenError { } } -impl std::error::Error for ZenError {} +pub fn zencode_exec_extra( + script: &str, + conf: &str, + keys: &str, + data: &str, + extra: &str, + context: &str, +) -> Result { + let mut zen_input: String = "".to_owned(); -type ZenGIL = MutexGuard<'static, ()>; + zen_input.push_str(conf); + zen_input.push_str("\n"); -fn aquire_zen_gil() -> ZenGIL { - static mut MUTEX: *const Mutex<()> = std::ptr::null(); - static ONCE: Once = Once::new(); - let mutex: &Mutex<()> = unsafe { - ONCE.call_once(|| { - MUTEX = std::mem::transmute(Box::new(Mutex::new(()))); - }); - MUTEX.as_ref().unwrap() - }; - mutex.lock().unwrap() -} + zen_input.push_str(&general_purpose::STANDARD.encode(script)); + zen_input.push_str("\n"); -const BUF_SIZE: usize = 2 * 1024 * 1024; + zen_input.push_str(&general_purpose::STANDARD.encode(&keys)); + zen_input.push_str("\n"); -type Fun = unsafe extern "C" fn( - *const ::std::os::raw::c_char, - *const ::std::os::raw::c_char, - *const ::std::os::raw::c_char, - *const ::std::os::raw::c_char, - *mut ::std::os::raw::c_char, - ::std::os::raw::c_ulong, - *mut ::std::os::raw::c_char, - ::std::os::raw::c_ulong, -) -> ::std::os::raw::c_int; + zen_input.push_str(&general_purpose::STANDARD.encode(&data)); + zen_input.push_str("\n"); -pub fn zencode_exec( - script: impl AsRef, - conf: impl AsRef, - keys: impl AsRef, - data: impl AsRef, -) -> Result { - exec_f(c::zencode_exec_tobuf, script, conf, keys, data) + zen_input.push_str(&general_purpose::STANDARD.encode(&extra)); + zen_input.push_str("\n"); + + zen_input.push_str(&general_purpose::STANDARD.encode(&context)); + zen_input.push_str("\n"); + + let mut child = Command::new("zencode-exec") + .stdin(Stdio::piped()) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .unwrap(); + let mut stdin = child.stdin.take().unwrap(); + + std::thread::spawn(move || { + stdin + .write_all(zen_input.as_bytes()) + .expect("Failed to write to stdin"); + }); + + let output = child.wait_with_output().expect("Failed to read stdout"); + Ok(ZenResult { + output: String::from_utf8_lossy(&output.stdout).to_string(), + logs: String::from_utf8_lossy(&output.stderr).to_string(), + }) } -pub fn zenroom_exec( - script: impl AsRef, - conf: impl AsRef, - keys: impl AsRef, - data: impl AsRef, +pub fn zencode_exec( + script: &str, + conf: &str, + keys: &str, + data: &str, ) -> Result { - exec_f(c::zenroom_exec_tobuf, script, conf, keys, data) + zencode_exec_extra(script, conf, keys, data, "", "") } -fn exec_f( - fun: Fun, +/*pub fn zenroom_exec( script: impl AsRef, conf: impl AsRef, keys: impl AsRef, data: impl AsRef, ) -> Result { - let mut stdout = Vec::::with_capacity(BUF_SIZE); - let stdout_ptr = stdout.as_mut_ptr(); - let mut stderr = Vec::::with_capacity(BUF_SIZE); - let stderr_ptr = stderr.as_mut_ptr(); - - let lock = aquire_zen_gil(); - let exit_code = unsafe { - fun( - CString::new(script.as_ref())?.into_raw(), - CString::new(conf.as_ref())?.into_raw(), - CString::new(keys.as_ref())?.into_raw(), - CString::new(data.as_ref())?.into_raw(), - stdout_ptr, - BUF_SIZE as u64, - stderr_ptr, - BUF_SIZE as u64, - ) - }; - drop(lock); - - let res = ZenResult { - output: unsafe { CStr::from_ptr(stdout_ptr) } - .to_string_lossy() - .into_owned(), - logs: unsafe { CStr::from_ptr(stderr_ptr) } - .to_string_lossy() - .into_owned(), - }; - - if exit_code == 0 { - Ok(res) - } else { - Err(ZenError::Execution(res)) - } -} + exec_f(c::zenroom_exec_tobuf, script, conf, keys, data) +}*/ #[cfg(test)] mod tests { @@ -142,11 +111,18 @@ mod tests { Then print my 'keyring' "#; + const EXTRA_USAGE: &str = r#"Scenario 'ecdh': Create the keypair +Given I have a 'string' named 'keys' +Given I have a 'string' named 'data' +Given I have a 'string' named 'extra' +Then print data +"#; #[test] fn simple_script() -> Result<(), ZenError> { let result = zencode_exec(SAMPLE_SCRIPT, "", "", "")?; let json: Value = serde_json::from_str(&result.output).unwrap(); + println!("{}", result.output); let keypair = json .as_object() .unwrap() @@ -160,6 +136,25 @@ mod tests { Ok(()) } + #[test] + fn extra_usage() -> Result<(), ZenError> { + let result = zencode_exec_extra( + EXTRA_USAGE, + "", + "{\"keys\": \"keys\"}", + "{\"data\": \"data\"}", + "{\"extra\": \"extra\"}", + "", + )?; + + let json: Value = serde_json::from_str(&result.output).unwrap(); + assert!(json.get("data").unwrap() == "data"); + assert!(json.get("extra").unwrap() == "extra"); + assert!(json.get("keys").unwrap() == "keys"); + + Ok(()) + } + #[test] fn threaded_exec() -> Result<(), ZenError> { const NUM_THREADS: usize = 5; diff --git a/bindings/rust/wrapper.h b/bindings/rust/wrapper.h deleted file mode 100644 index 39d0fec0e..000000000 --- a/bindings/rust/wrapper.h +++ /dev/null @@ -1 +0,0 @@ -#include