From 9af51ddf7eb880d58410d115abab9364f7aa7f10 Mon Sep 17 00:00:00 2001 From: Alejandro Cabeza Romero Date: Tue, 5 May 2026 16:24:21 +0200 Subject: [PATCH] Implement poc, pol and signature circuits. Abstract utils into their own crate. Abstract CircuitWitnessInput. --- rust/Cargo.lock | 41 +++++++++ rust/Cargo.toml | 14 ++- .../Cargo.toml | 19 ++++ .../build.rs | 89 +++++++++++++++++++ .../src/ffi.rs | 12 +++ .../src/lib.rs | 4 + .../src/native.rs | 87 ++++++++++++++++++ .../Cargo.toml | 19 ++++ .../build.rs | 89 +++++++++++++++++++ .../src/ffi.rs | 12 +++ .../src/lib.rs | 4 + .../src/native.rs | 87 ++++++++++++++++++ .../Cargo.toml | 1 + .../src/lib.rs | 2 +- .../src/native.rs | 64 +++---------- .../Cargo.toml | 19 ++++ .../build.rs | 89 +++++++++++++++++++ .../src/ffi.rs | 12 +++ .../src/lib.rs | 4 + .../src/native.rs | 87 ++++++++++++++++++ .../src/lib.rs | 35 ++++++++ .../Cargo.toml | 13 +++ .../src/lib.rs | 1 + .../src/string.rs | 16 ++++ 24 files changed, 765 insertions(+), 55 deletions(-) create mode 100644 rust/logos-blockchain-circuits-poc-sys/Cargo.toml create mode 100644 rust/logos-blockchain-circuits-poc-sys/build.rs create mode 100644 rust/logos-blockchain-circuits-poc-sys/src/ffi.rs create mode 100644 rust/logos-blockchain-circuits-poc-sys/src/lib.rs create mode 100644 rust/logos-blockchain-circuits-poc-sys/src/native.rs create mode 100644 rust/logos-blockchain-circuits-pol-sys/Cargo.toml create mode 100644 rust/logos-blockchain-circuits-pol-sys/build.rs create mode 100644 rust/logos-blockchain-circuits-pol-sys/src/ffi.rs create mode 100644 rust/logos-blockchain-circuits-pol-sys/src/lib.rs create mode 100644 rust/logos-blockchain-circuits-pol-sys/src/native.rs create mode 100644 rust/logos-blockchain-circuits-signature-sys/Cargo.toml create mode 100644 rust/logos-blockchain-circuits-signature-sys/build.rs create mode 100644 rust/logos-blockchain-circuits-signature-sys/src/ffi.rs create mode 100644 rust/logos-blockchain-circuits-signature-sys/src/lib.rs create mode 100644 rust/logos-blockchain-circuits-signature-sys/src/native.rs create mode 100644 rust/logos-blockchain-circuits-utils/Cargo.toml create mode 100644 rust/logos-blockchain-circuits-utils/src/lib.rs create mode 100644 rust/logos-blockchain-circuits-utils/src/string.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 823efcf..3185d5c 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -151,12 +151,46 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "logos-blockchain-circuits-poc-sys" +version = "0.4.2" +dependencies = [ + "flate2", + "logos-blockchain-circuits-types", + "logos-blockchain-circuits-utils", + "tar", + "ureq", +] + +[[package]] +name = "logos-blockchain-circuits-pol-sys" +version = "0.4.2" +dependencies = [ + "flate2", + "logos-blockchain-circuits-types", + "logos-blockchain-circuits-utils", + "tar", + "ureq", +] + [[package]] name = "logos-blockchain-circuits-poq-sys" version = "0.4.2" dependencies = [ "flate2", "logos-blockchain-circuits-types", + "logos-blockchain-circuits-utils", + "tar", + "ureq", +] + +[[package]] +name = "logos-blockchain-circuits-signature-sys" +version = "0.4.2" +dependencies = [ + "flate2", + "logos-blockchain-circuits-types", + "logos-blockchain-circuits-utils", "tar", "ureq", ] @@ -168,6 +202,13 @@ dependencies = [ "libc", ] +[[package]] +name = "logos-blockchain-circuits-utils" +version = "0.4.2" +dependencies = [ + "logos-blockchain-circuits-types", +] + [[package]] name = "miniz_oxide" version = "0.8.9" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index a83eff5..123bd58 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -10,15 +10,23 @@ version = "0.4.2" [workspace] members = [ + "logos-blockchain-circuits-poc-sys", + "logos-blockchain-circuits-pol-sys", "logos-blockchain-circuits-poq-sys", - "logos-blockchain-circuits-types" + "logos-blockchain-circuits-signature-sys", + "logos-blockchain-circuits-types", + "logos-blockchain-circuits-utils" ] resolver = "3" [workspace.dependencies] # Internal -lbc-poq-sys = { default-features = false, package = "logos-blockchain-circuits-poq-sys", path = "./logos-blockchain-circuits-poq-sys" } -lbc-types = { default-features = false, package = "logos-blockchain-circuits-types", path = "./logos-blockchain-circuits-types" } +lbc-poc-sys = { default-features = false, package = "logos-blockchain-circuits-poc-sys", path = "./logos-blockchain-circuits-poc-sys" } +lbc-pol-sys = { default-features = false, package = "logos-blockchain-circuits-pol-sys", path = "./logos-blockchain-circuits-pol-sys" } +lbc-poq-sys = { default-features = false, package = "logos-blockchain-circuits-poq-sys", path = "./logos-blockchain-circuits-poq-sys" } +lbc-signature-sys = { default-features = false, package = "logos-blockchain-circuits-signature-sys", path = "./logos-blockchain-circuits-signature-sys" } +lbc-types = { default-features = false, package = "logos-blockchain-circuits-types", path = "./logos-blockchain-circuits-types" } +lbc-utils = { default-features = false, package = "logos-blockchain-circuits-utils", path = "./logos-blockchain-circuits-utils" } # External flate2 = "1" diff --git a/rust/logos-blockchain-circuits-poc-sys/Cargo.toml b/rust/logos-blockchain-circuits-poc-sys/Cargo.toml new file mode 100644 index 0000000..a3e823e --- /dev/null +++ b/rust/logos-blockchain-circuits-poc-sys/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "logos-blockchain-circuits-poc-sys" +categories.workspace = true +description.workspace = true +edition.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +lbc-types = { workspace = true } +lbc-utils = { workspace = true } + +[build-dependencies] +flate2 = { workspace = true } +tar = { workspace = true } +ureq = { workspace = true } diff --git a/rust/logos-blockchain-circuits-poc-sys/build.rs b/rust/logos-blockchain-circuits-poc-sys/build.rs new file mode 100644 index 0000000..8b83fca --- /dev/null +++ b/rust/logos-blockchain-circuits-poc-sys/build.rs @@ -0,0 +1,89 @@ +use std::path::{Path, PathBuf}; +use ureq::Body; +use ureq::http::Response; + +static CIRCUIT_NAME: &str = "poc"; +static LIB_VAR_NAME: &str = "LBC_POC_LIB_DIR"; + + +fn get_artifact_name(version: &str, os: &str, arch: &str) -> String { + format!("logos-blockchain-circuits-v{version}-{os}-{arch}") +} + +fn get_artifact_url(version: &str, os: &str, arch: &str) -> String { + let artifact = get_artifact_name(version, os, arch); + let artifact_tar_gz = format!("{artifact}.tar.gz"); + format!( + "https://github.com/logos-blockchain/logos-blockchain-circuits/releases/download/v{version}/{artifact_tar_gz}" + ) +} + +fn fetch_library(version: &str, os: &str, arch: &str) -> Response { + let url = get_artifact_url(version, os, arch); + // TODO: Verify checksum. + ureq::get(&url).call().unwrap_or_else(|error| { + panic!( + "Failed to download a prebuilt library for {os}-{arch} v{version}: {error}. \ + Set {LIB_VAR_NAME} to point to a local build instead." + ) + }) +} + +fn unpack_library(response: Response, version: &str, os: &str, arch: &str, output_dir: &Path) -> PathBuf { + let gz_decoder = flate2::read::GzDecoder::new(response.into_body().into_reader()); + let mut archive = tar::Archive::new(gz_decoder); + archive.unpack(output_dir).expect("Failed to unpack the downloaded archive."); + + let unpacked_artifact_path = output_dir.join(get_artifact_name(version, os, arch)); + let unpacked_library_directory = unpacked_artifact_path.join(CIRCUIT_NAME); + + if !unpacked_library_directory.exists() { + panic!("Failed to find the unpacked library at {}.", unpacked_library_directory.display()); + } + + unpacked_library_directory +} + +fn provision_library() -> PathBuf { + let version = env!("CARGO_PKG_VERSION"); + let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); + let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + + let expected_library_directory = out_dir.join(get_artifact_name(version, &os, &arch)).join(CIRCUIT_NAME); + if expected_library_directory.exists() { + println!("Found an existing library at {}. Reusing it.", expected_library_directory.display()); + return expected_library_directory; + } + + let response = fetch_library(version, &os, &arch); + unpack_library(response, version, &os, &arch, &out_dir) +} + +fn main() { + println!("cargo:rerun-if-env-changed={LIB_VAR_NAME}"); + println!("cargo:rerun-if-env-changed=CARGO_PKG_VERSION"); + println!("cargo:rerun-if-changed=Cargo.toml"); + println!("cargo:rerun-if-changed=build.rs"); + + let lib_dir = std::env::var(LIB_VAR_NAME).map( + |lib_dir| { + println!("Using a library directory from {LIB_VAR_NAME}: {lib_dir}"); + let lib_dir_path = PathBuf::from(lib_dir); + if !lib_dir_path.exists() { + panic!("The library directory specified in {LIB_VAR_NAME} at {} does not exist.", lib_dir_path.display()); + + } + lib_dir_path + } + ).unwrap_or_else(|_| { + provision_library() + }); + + let lib_dir = lib_dir.to_str().expect("Failed to convert the library directory path to a string"); + println!("cargo:rustc-env={LIB_VAR_NAME}={lib_dir}"); + println!("cargo:rustc-link-search=native={lib_dir}"); + println!("cargo:rustc-link-lib=static={CIRCUIT_NAME}"); + println!("cargo:rustc-link-lib=stdc++"); + println!("cargo:rustc-link-lib=gmp"); +} diff --git a/rust/logos-blockchain-circuits-poc-sys/src/ffi.rs b/rust/logos-blockchain-circuits-poc-sys/src/ffi.rs new file mode 100644 index 0000000..aeddef5 --- /dev/null +++ b/rust/logos-blockchain-circuits-poc-sys/src/ffi.rs @@ -0,0 +1,12 @@ +use std::ffi::c_char; +use lbc_types::ffi::{Bytes, Status, WitnessInput}; + +unsafe extern "C" { + pub fn poc_generate_witness(input: *const WitnessInput, output: *mut Bytes) -> Status; + + pub fn poc_generate_witness_from_files( + dat: *const c_char, + inputs: *const c_char, + output: *const c_char, + ) -> Status; +} diff --git a/rust/logos-blockchain-circuits-poc-sys/src/lib.rs b/rust/logos-blockchain-circuits-poc-sys/src/lib.rs new file mode 100644 index 0000000..aea275f --- /dev/null +++ b/rust/logos-blockchain-circuits-poc-sys/src/lib.rs @@ -0,0 +1,4 @@ +mod ffi; +pub mod native; + +pub use native::{generate_witness, generate_witness_from_files, PocWitnessInput}; diff --git a/rust/logos-blockchain-circuits-poc-sys/src/native.rs b/rust/logos-blockchain-circuits-poc-sys/src/native.rs new file mode 100644 index 0000000..692c3f4 --- /dev/null +++ b/rust/logos-blockchain-circuits-poc-sys/src/native.rs @@ -0,0 +1,87 @@ +use std::path::Path; +use lbc_types::{ffi, native::{Bytes, Error}}; +use lbc_types::inputs::CircuitDat; +use lbc_utils::string::path_as_null_terminated_string; +use crate::ffi::{poc_generate_witness, poc_generate_witness_from_files}; + +pub(crate) const RAW_CIRCUIT_DAT: &[u8] = include_bytes!(concat!(env!("LBC_POC_LIB_DIR"), "/witness_generator.dat")); + +pub struct PocDat; +impl CircuitDat for PocDat { + const DAT: &'static [u8] = RAW_CIRCUIT_DAT; +} + +pub type PocWitnessInput<'a> = lbc_types::inputs::CircuitWitnessInput<'a, PocDat>; + +pub fn generate_witness( + input: PocWitnessInput, +) -> Result { + let input: lbc_types::WitnessInput = input.into(); + let ffi_input_guard = input.as_ffi(); + let ffi_input = ffi_input_guard.as_ref(); + + let mut ffi_output_bytes = ffi::Bytes::null(); + + let status = unsafe { + poc_generate_witness( + ffi_input as *const ffi::WitnessInput, + &mut ffi_output_bytes as *mut ffi::Bytes + ) + }; + + status.try_into().map(|()| { Bytes::from(ffi_output_bytes) }) +} + +pub fn generate_witness_from_files( + dat: &Path, + inputs: &Path, + output: &Path, +) -> Result<(), Error> { + let c_dat = path_as_null_terminated_string(dat)?; + let c_inputs = path_as_null_terminated_string(inputs)?; + let c_output = path_as_null_terminated_string(output)?; + + unsafe { + poc_generate_witness_from_files( + c_dat.as_ptr(), + c_inputs.as_ptr(), + c_output.as_ptr(), + ) + }.try_into() +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + use std::sync::LazyLock; + use super::{generate_witness, generate_witness_from_files, PocWitnessInput}; + + static LIB_DIR: LazyLock = LazyLock::new(|| { + const ENV_VAR: &str = "LBC_POC_LIB_DIR"; + PathBuf::from( + std::env::var(ENV_VAR) + .expect(format!("Environment variable '{ENV_VAR}' must be available, as provided by the build script.").as_str()), + ) + }); + static INPUTS: LazyLock = LazyLock::new(|| { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json") + }); + + #[test] + fn test_generate_witness() { + let dat = LIB_DIR.join("witness_generator"); + let witness_output_path = std::env::temp_dir().join("poc_test_witness.wtns"); + + generate_witness_from_files(&dat, &*INPUTS, &witness_output_path) + .expect("generate_witness_from_files failed."); + + let inputs_json = std::fs::read_to_string(&*INPUTS) + .expect(format!("Failed to read {}.", INPUTS.display()).as_str()); + + let input = PocWitnessInput::new(inputs_json).expect("Failed to construct the input for the witness generator."); + let output = generate_witness(input).expect("generate_witness failed."); + + let expected = std::fs::read(&witness_output_path).expect(format!("Failed to read the generated witness from {}.", witness_output_path.display()).as_str()); + assert_eq!(output.as_slice(), expected.as_slice()); + } +} diff --git a/rust/logos-blockchain-circuits-pol-sys/Cargo.toml b/rust/logos-blockchain-circuits-pol-sys/Cargo.toml new file mode 100644 index 0000000..dbc9505 --- /dev/null +++ b/rust/logos-blockchain-circuits-pol-sys/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "logos-blockchain-circuits-pol-sys" +categories.workspace = true +description.workspace = true +edition.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +lbc-types = { workspace = true } +lbc-utils = { workspace = true } + +[build-dependencies] +flate2 = { workspace = true } +tar = { workspace = true } +ureq = { workspace = true } diff --git a/rust/logos-blockchain-circuits-pol-sys/build.rs b/rust/logos-blockchain-circuits-pol-sys/build.rs new file mode 100644 index 0000000..700d69e --- /dev/null +++ b/rust/logos-blockchain-circuits-pol-sys/build.rs @@ -0,0 +1,89 @@ +use std::path::{Path, PathBuf}; +use ureq::Body; +use ureq::http::Response; + +static CIRCUIT_NAME: &str = "pol"; +static LIB_VAR_NAME: &str = "LBC_POL_LIB_DIR"; + + +fn get_artifact_name(version: &str, os: &str, arch: &str) -> String { + format!("logos-blockchain-circuits-v{version}-{os}-{arch}") +} + +fn get_artifact_url(version: &str, os: &str, arch: &str) -> String { + let artifact = get_artifact_name(version, os, arch); + let artifact_tar_gz = format!("{artifact}.tar.gz"); + format!( + "https://github.com/logos-blockchain/logos-blockchain-circuits/releases/download/v{version}/{artifact_tar_gz}" + ) +} + +fn fetch_library(version: &str, os: &str, arch: &str) -> Response { + let url = get_artifact_url(version, os, arch); + // TODO: Verify checksum. + ureq::get(&url).call().unwrap_or_else(|error| { + panic!( + "Failed to download a prebuilt library for {os}-{arch} v{version}: {error}. \ + Set {LIB_VAR_NAME} to point to a local build instead." + ) + }) +} + +fn unpack_library(response: Response, version: &str, os: &str, arch: &str, output_dir: &Path) -> PathBuf { + let gz_decoder = flate2::read::GzDecoder::new(response.into_body().into_reader()); + let mut archive = tar::Archive::new(gz_decoder); + archive.unpack(output_dir).expect("Failed to unpack the downloaded archive."); + + let unpacked_artifact_path = output_dir.join(get_artifact_name(version, os, arch)); + let unpacked_library_directory = unpacked_artifact_path.join(CIRCUIT_NAME); + + if !unpacked_library_directory.exists() { + panic!("Failed to find the unpacked library at {}.", unpacked_library_directory.display()); + } + + unpacked_library_directory +} + +fn provision_library() -> PathBuf { + let version = env!("CARGO_PKG_VERSION"); + let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); + let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + + let expected_library_directory = out_dir.join(get_artifact_name(version, &os, &arch)).join(CIRCUIT_NAME); + if expected_library_directory.exists() { + println!("Found an existing library at {}. Reusing it.", expected_library_directory.display()); + return expected_library_directory; + } + + let response = fetch_library(version, &os, &arch); + unpack_library(response, version, &os, &arch, &out_dir) +} + +fn main() { + println!("cargo:rerun-if-env-changed={LIB_VAR_NAME}"); + println!("cargo:rerun-if-env-changed=CARGO_PKG_VERSION"); + println!("cargo:rerun-if-changed=Cargo.toml"); + println!("cargo:rerun-if-changed=build.rs"); + + let lib_dir = std::env::var(LIB_VAR_NAME).map( + |lib_dir| { + println!("Using a library directory from {LIB_VAR_NAME}: {lib_dir}"); + let lib_dir_path = PathBuf::from(lib_dir); + if !lib_dir_path.exists() { + panic!("The library directory specified in {LIB_VAR_NAME} at {} does not exist.", lib_dir_path.display()); + + } + lib_dir_path + } + ).unwrap_or_else(|_| { + provision_library() + }); + + let lib_dir = lib_dir.to_str().expect("Failed to convert the library directory path to a string"); + println!("cargo:rustc-env={LIB_VAR_NAME}={lib_dir}"); + println!("cargo:rustc-link-search=native={lib_dir}"); + println!("cargo:rustc-link-lib=static={CIRCUIT_NAME}"); + println!("cargo:rustc-link-lib=stdc++"); + println!("cargo:rustc-link-lib=gmp"); +} diff --git a/rust/logos-blockchain-circuits-pol-sys/src/ffi.rs b/rust/logos-blockchain-circuits-pol-sys/src/ffi.rs new file mode 100644 index 0000000..e944990 --- /dev/null +++ b/rust/logos-blockchain-circuits-pol-sys/src/ffi.rs @@ -0,0 +1,12 @@ +use std::ffi::c_char; +use lbc_types::ffi::{Bytes, Status, WitnessInput}; + +unsafe extern "C" { + pub fn pol_generate_witness(input: *const WitnessInput, output: *mut Bytes) -> Status; + + pub fn pol_generate_witness_from_files( + dat: *const c_char, + inputs: *const c_char, + output: *const c_char, + ) -> Status; +} diff --git a/rust/logos-blockchain-circuits-pol-sys/src/lib.rs b/rust/logos-blockchain-circuits-pol-sys/src/lib.rs new file mode 100644 index 0000000..f425030 --- /dev/null +++ b/rust/logos-blockchain-circuits-pol-sys/src/lib.rs @@ -0,0 +1,4 @@ +mod ffi; +pub mod native; + +pub use native::{generate_witness, generate_witness_from_files, PolWitnessInput}; diff --git a/rust/logos-blockchain-circuits-pol-sys/src/native.rs b/rust/logos-blockchain-circuits-pol-sys/src/native.rs new file mode 100644 index 0000000..2011767 --- /dev/null +++ b/rust/logos-blockchain-circuits-pol-sys/src/native.rs @@ -0,0 +1,87 @@ +use std::path::Path; +use lbc_types::{ffi, native::{Bytes, Error}}; +use lbc_types::inputs::CircuitDat; +use lbc_utils::string::path_as_null_terminated_string; +use crate::ffi::{pol_generate_witness, pol_generate_witness_from_files}; + +pub(crate) const RAW_CIRCUIT_DAT: &[u8] = include_bytes!(concat!(env!("LBC_POL_LIB_DIR"), "/witness_generator.dat")); + +pub struct PolDat; +impl CircuitDat for PolDat { + const DAT: &'static [u8] = RAW_CIRCUIT_DAT; +} + +pub type PolWitnessInput<'a> = lbc_types::inputs::CircuitWitnessInput<'a, PolDat>; + +pub fn generate_witness( + input: PolWitnessInput, +) -> Result { + let input: lbc_types::WitnessInput = input.into(); + let ffi_input_guard = input.as_ffi(); + let ffi_input = ffi_input_guard.as_ref(); + + let mut ffi_output_bytes = ffi::Bytes::null(); + + let status = unsafe { + pol_generate_witness( + ffi_input as *const ffi::WitnessInput, + &mut ffi_output_bytes as *mut ffi::Bytes + ) + }; + + status.try_into().map(|()| { Bytes::from(ffi_output_bytes) }) +} + +pub fn generate_witness_from_files( + dat: &Path, + inputs: &Path, + output: &Path, +) -> Result<(), Error> { + let c_dat = path_as_null_terminated_string(dat)?; + let c_inputs = path_as_null_terminated_string(inputs)?; + let c_output = path_as_null_terminated_string(output)?; + + unsafe { + pol_generate_witness_from_files( + c_dat.as_ptr(), + c_inputs.as_ptr(), + c_output.as_ptr(), + ) + }.try_into() +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + use std::sync::LazyLock; + use super::{generate_witness, generate_witness_from_files, PolWitnessInput}; + + static LIB_DIR: LazyLock = LazyLock::new(|| { + const ENV_VAR: &str = "LBC_POL_LIB_DIR"; + PathBuf::from( + std::env::var(ENV_VAR) + .expect(format!("Environment variable '{ENV_VAR}' must be available, as provided by the build script.").as_str()), + ) + }); + static INPUTS: LazyLock = LazyLock::new(|| { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json") + }); + + #[test] + fn test_generate_witness() { + let dat = LIB_DIR.join("witness_generator"); + let witness_output_path = std::env::temp_dir().join("pol_test_witness.wtns"); + + generate_witness_from_files(&dat, &*INPUTS, &witness_output_path) + .expect("generate_witness_from_files failed."); + + let inputs_json = std::fs::read_to_string(&*INPUTS) + .expect(format!("Failed to read {}.", INPUTS.display()).as_str()); + + let input = PolWitnessInput::new(inputs_json).expect("Failed to construct the input for the witness generator."); + let output = generate_witness(input).expect("generate_witness failed."); + + let expected = std::fs::read(&witness_output_path).expect(format!("Failed to read the generated witness from {}.", witness_output_path.display()).as_str()); + assert_eq!(output.as_slice(), expected.as_slice()); + } +} diff --git a/rust/logos-blockchain-circuits-poq-sys/Cargo.toml b/rust/logos-blockchain-circuits-poq-sys/Cargo.toml index cbac3bd..b3eff0f 100644 --- a/rust/logos-blockchain-circuits-poq-sys/Cargo.toml +++ b/rust/logos-blockchain-circuits-poq-sys/Cargo.toml @@ -11,6 +11,7 @@ version.workspace = true [dependencies] lbc-types = { workspace = true } +lbc-utils = { workspace = true } [build-dependencies] flate2 = { workspace = true } diff --git a/rust/logos-blockchain-circuits-poq-sys/src/lib.rs b/rust/logos-blockchain-circuits-poq-sys/src/lib.rs index 1f22cc7..efb86b6 100644 --- a/rust/logos-blockchain-circuits-poq-sys/src/lib.rs +++ b/rust/logos-blockchain-circuits-poq-sys/src/lib.rs @@ -1,4 +1,4 @@ mod ffi; pub mod native; -pub use native::{generate_witness, generate_witness_from_files, inputs::PoqWitnessInput}; +pub use native::{generate_witness, generate_witness_from_files, PoqWitnessInput}; diff --git a/rust/logos-blockchain-circuits-poq-sys/src/native.rs b/rust/logos-blockchain-circuits-poq-sys/src/native.rs index 37ce330..3ee935e 100644 --- a/rust/logos-blockchain-circuits-poq-sys/src/native.rs +++ b/rust/logos-blockchain-circuits-poq-sys/src/native.rs @@ -1,58 +1,20 @@ use std::path::Path; use lbc_types::{ffi, native::{Bytes, Error}}; +use lbc_types::inputs::CircuitDat; +use lbc_utils::string::path_as_null_terminated_string; use crate::ffi::{poq_generate_witness, poq_generate_witness_from_files}; -mod helpers { - use std::path::Path; - use lbc_types::native::Error; +pub(crate) const RAW_CIRCUIT_DAT: &[u8] = include_bytes!(concat!(env!("LBC_POQ_LIB_DIR"), "/witness_generator.dat")); - pub fn into_null_terminated_string( - string: &str, - ) -> Result { - std::ffi::CString::new(string) - .map_err(|error| Error::InvalidInput(Some(format!("Could not convert string to CString: {error}")))) - } - - pub fn path_as_null_terminated_string( - path: &Path, - ) -> Result { - let path = path.to_str().ok_or(Error::InvalidInput(Some(format!("Could not convert the path to a string: {}", path.display()))))?; - into_null_terminated_string(path) - } +pub struct PoqDat; +impl CircuitDat for PoqDat { + const DAT: &'static [u8] = RAW_CIRCUIT_DAT; } -pub mod inputs { - use lbc_types::native::Error; - use lbc_types::WitnessInput; - - pub(crate) static DAT: &[u8] = include_bytes!(concat!(env!("LBC_POQ_LIB_DIR"), "/witness_generator.dat")); - - pub struct PoqWitnessInput<'a> { - inner: WitnessInput<'a> - } - - impl<'a> PoqWitnessInput<'a> { - pub fn new(inputs_json: String) -> Result { - let inner = WitnessInput::new(DAT, inputs_json)?; - Ok(Self { inner }) - } - } - - impl<'a> From> for WitnessInput<'a> { - fn from(value: PoqWitnessInput<'a>) -> Self { - value.inner - } - } - - impl<'a> From> for PoqWitnessInput<'a> { - fn from(value: WitnessInput<'a>) -> Self { - Self { inner: value } - } - } -} +pub type PoqWitnessInput<'a> = lbc_types::inputs::CircuitWitnessInput<'a, PoqDat>; pub fn generate_witness( - input: inputs::PoqWitnessInput, + input: PoqWitnessInput, ) -> Result { let input: lbc_types::WitnessInput = input.into(); let ffi_input_guard = input.as_ffi(); @@ -75,9 +37,9 @@ pub fn generate_witness_from_files( inputs: &Path, output: &Path, ) -> Result<(), Error> { - let c_dat = helpers::path_as_null_terminated_string(dat)?; - let c_inputs = helpers::path_as_null_terminated_string(inputs)?; - let c_output = helpers::path_as_null_terminated_string(output)?; + let c_dat = path_as_null_terminated_string(dat)?; + let c_inputs = path_as_null_terminated_string(inputs)?; + let c_output = path_as_null_terminated_string(output)?; unsafe { poq_generate_witness_from_files ( @@ -92,7 +54,7 @@ pub fn generate_witness_from_files( mod tests { use std::path::PathBuf; use std::sync::LazyLock; - use super::{generate_witness, generate_witness_from_files, inputs}; + use super::{generate_witness, generate_witness_from_files, PoqWitnessInput}; static LIB_DIR: LazyLock = LazyLock::new(|| { const ENV_VAR: &str = "LBC_POQ_LIB_DIR"; @@ -116,7 +78,7 @@ mod tests { let inputs_json = std::fs::read_to_string(&*INPUTS) .expect(format!("Failed to read {}.", INPUTS.display()).as_str()); - let input = inputs::PoqWitnessInput::new(inputs_json).expect("Failed to construct the input for the witness generator."); + let input = PoqWitnessInput::new(inputs_json).expect("Failed to construct the input for the witness generator."); let output = generate_witness(input).expect("generate_witness failed."); let expected = std::fs::read(&witness_output_path).expect(format!("Failed to read the generated witness from {}.", witness_output_path.display()).as_str()); diff --git a/rust/logos-blockchain-circuits-signature-sys/Cargo.toml b/rust/logos-blockchain-circuits-signature-sys/Cargo.toml new file mode 100644 index 0000000..fabc71e --- /dev/null +++ b/rust/logos-blockchain-circuits-signature-sys/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "logos-blockchain-circuits-signature-sys" +categories.workspace = true +description.workspace = true +edition.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +lbc-types = { workspace = true } +lbc-utils = { workspace = true } + +[build-dependencies] +flate2 = { workspace = true } +tar = { workspace = true } +ureq = { workspace = true } diff --git a/rust/logos-blockchain-circuits-signature-sys/build.rs b/rust/logos-blockchain-circuits-signature-sys/build.rs new file mode 100644 index 0000000..415d0ba --- /dev/null +++ b/rust/logos-blockchain-circuits-signature-sys/build.rs @@ -0,0 +1,89 @@ +use std::path::{Path, PathBuf}; +use ureq::Body; +use ureq::http::Response; + +static CIRCUIT_NAME: &str = "signature"; +static LIB_VAR_NAME: &str = "LBC_SIGNATURE_LIB_DIR"; + + +fn get_artifact_name(version: &str, os: &str, arch: &str) -> String { + format!("logos-blockchain-circuits-v{version}-{os}-{arch}") +} + +fn get_artifact_url(version: &str, os: &str, arch: &str) -> String { + let artifact = get_artifact_name(version, os, arch); + let artifact_tar_gz = format!("{artifact}.tar.gz"); + format!( + "https://github.com/logos-blockchain/logos-blockchain-circuits/releases/download/v{version}/{artifact_tar_gz}" + ) +} + +fn fetch_library(version: &str, os: &str, arch: &str) -> Response { + let url = get_artifact_url(version, os, arch); + // TODO: Verify checksum. + ureq::get(&url).call().unwrap_or_else(|error| { + panic!( + "Failed to download a prebuilt library for {os}-{arch} v{version}: {error}. \ + Set {LIB_VAR_NAME} to point to a local build instead." + ) + }) +} + +fn unpack_library(response: Response, version: &str, os: &str, arch: &str, output_dir: &Path) -> PathBuf { + let gz_decoder = flate2::read::GzDecoder::new(response.into_body().into_reader()); + let mut archive = tar::Archive::new(gz_decoder); + archive.unpack(output_dir).expect("Failed to unpack the downloaded archive."); + + let unpacked_artifact_path = output_dir.join(get_artifact_name(version, os, arch)); + let unpacked_library_directory = unpacked_artifact_path.join(CIRCUIT_NAME); + + if !unpacked_library_directory.exists() { + panic!("Failed to find the unpacked library at {}.", unpacked_library_directory.display()); + } + + unpacked_library_directory +} + +fn provision_library() -> PathBuf { + let version = env!("CARGO_PKG_VERSION"); + let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); + let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + + let expected_library_directory = out_dir.join(get_artifact_name(version, &os, &arch)).join(CIRCUIT_NAME); + if expected_library_directory.exists() { + println!("Found an existing library at {}. Reusing it.", expected_library_directory.display()); + return expected_library_directory; + } + + let response = fetch_library(version, &os, &arch); + unpack_library(response, version, &os, &arch, &out_dir) +} + +fn main() { + println!("cargo:rerun-if-env-changed={LIB_VAR_NAME}"); + println!("cargo:rerun-if-env-changed=CARGO_PKG_VERSION"); + println!("cargo:rerun-if-changed=Cargo.toml"); + println!("cargo:rerun-if-changed=build.rs"); + + let lib_dir = std::env::var(LIB_VAR_NAME).map( + |lib_dir| { + println!("Using a library directory from {LIB_VAR_NAME}: {lib_dir}"); + let lib_dir_path = PathBuf::from(lib_dir); + if !lib_dir_path.exists() { + panic!("The library directory specified in {LIB_VAR_NAME} at {} does not exist.", lib_dir_path.display()); + + } + lib_dir_path + } + ).unwrap_or_else(|_| { + provision_library() + }); + + let lib_dir = lib_dir.to_str().expect("Failed to convert the library directory path to a string"); + println!("cargo:rustc-env={LIB_VAR_NAME}={lib_dir}"); + println!("cargo:rustc-link-search=native={lib_dir}"); + println!("cargo:rustc-link-lib=static={CIRCUIT_NAME}"); + println!("cargo:rustc-link-lib=stdc++"); + println!("cargo:rustc-link-lib=gmp"); +} diff --git a/rust/logos-blockchain-circuits-signature-sys/src/ffi.rs b/rust/logos-blockchain-circuits-signature-sys/src/ffi.rs new file mode 100644 index 0000000..7469dae --- /dev/null +++ b/rust/logos-blockchain-circuits-signature-sys/src/ffi.rs @@ -0,0 +1,12 @@ +use std::ffi::c_char; +use lbc_types::ffi::{Bytes, Status, WitnessInput}; + +unsafe extern "C" { + pub fn signature_generate_witness(input: *const WitnessInput, output: *mut Bytes) -> Status; + + pub fn signature_generate_witness_from_files( + dat: *const c_char, + inputs: *const c_char, + output: *const c_char, + ) -> Status; +} diff --git a/rust/logos-blockchain-circuits-signature-sys/src/lib.rs b/rust/logos-blockchain-circuits-signature-sys/src/lib.rs new file mode 100644 index 0000000..6a74894 --- /dev/null +++ b/rust/logos-blockchain-circuits-signature-sys/src/lib.rs @@ -0,0 +1,4 @@ +mod ffi; +pub mod native; + +pub use native::{generate_witness, generate_witness_from_files, SignatureWitnessInput}; diff --git a/rust/logos-blockchain-circuits-signature-sys/src/native.rs b/rust/logos-blockchain-circuits-signature-sys/src/native.rs new file mode 100644 index 0000000..91e59e3 --- /dev/null +++ b/rust/logos-blockchain-circuits-signature-sys/src/native.rs @@ -0,0 +1,87 @@ +use std::path::Path; +use lbc_types::{ffi, native::{Bytes, Error}}; +use lbc_types::inputs::CircuitDat; +use lbc_utils::string::path_as_null_terminated_string; +use crate::ffi::{signature_generate_witness, signature_generate_witness_from_files}; + +pub(crate) const RAW_CIRCUIT_DAT: &[u8] = include_bytes!(concat!(env!("LBC_SIGNATURE_LIB_DIR"), "/witness_generator.dat")); + +pub struct SignatureDat; +impl CircuitDat for SignatureDat { + const DAT: &'static [u8] = RAW_CIRCUIT_DAT; +} + +pub type SignatureWitnessInput<'a> = lbc_types::inputs::CircuitWitnessInput<'a, SignatureDat>; + +pub fn generate_witness( + input: SignatureWitnessInput, +) -> Result { + let input: lbc_types::WitnessInput = input.into(); + let ffi_input_guard = input.as_ffi(); + let ffi_input = ffi_input_guard.as_ref(); + + let mut ffi_output_bytes = ffi::Bytes::null(); + + let status = unsafe { + signature_generate_witness( + ffi_input as *const ffi::WitnessInput, + &mut ffi_output_bytes as *mut ffi::Bytes + ) + }; + + status.try_into().map(|()| { Bytes::from(ffi_output_bytes) }) +} + +pub fn generate_witness_from_files( + dat: &Path, + inputs: &Path, + output: &Path, +) -> Result<(), Error> { + let c_dat = path_as_null_terminated_string(dat)?; + let c_inputs = path_as_null_terminated_string(inputs)?; + let c_output = path_as_null_terminated_string(output)?; + + unsafe { + signature_generate_witness_from_files( + c_dat.as_ptr(), + c_inputs.as_ptr(), + c_output.as_ptr(), + ) + }.try_into() +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + use std::sync::LazyLock; + use super::{generate_witness, generate_witness_from_files, SignatureWitnessInput}; + + static LIB_DIR: LazyLock = LazyLock::new(|| { + const ENV_VAR: &str = "LBC_SIGNATURE_LIB_DIR"; + PathBuf::from( + std::env::var(ENV_VAR) + .expect(format!("Environment variable '{ENV_VAR}' must be available, as provided by the build script.").as_str()), + ) + }); + static INPUTS: LazyLock = LazyLock::new(|| { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json") + }); + + #[test] + fn test_generate_witness() { + let dat = LIB_DIR.join("witness_generator"); + let witness_output_path = std::env::temp_dir().join("signature_test_witness.wtns"); + + generate_witness_from_files(&dat, &*INPUTS, &witness_output_path) + .expect("generate_witness_from_files failed."); + + let inputs_json = std::fs::read_to_string(&*INPUTS) + .expect(format!("Failed to read {}.", INPUTS.display()).as_str()); + + let input = SignatureWitnessInput::new(inputs_json).expect("Failed to construct the input for the witness generator."); + let output = generate_witness(input).expect("generate_witness failed."); + + let expected = std::fs::read(&witness_output_path).expect(format!("Failed to read the generated witness from {}.", witness_output_path.display()).as_str()); + assert_eq!(output.as_slice(), expected.as_slice()); + } +} diff --git a/rust/logos-blockchain-circuits-types/src/lib.rs b/rust/logos-blockchain-circuits-types/src/lib.rs index eb67373..7c3c4ac 100644 --- a/rust/logos-blockchain-circuits-types/src/lib.rs +++ b/rust/logos-blockchain-circuits-types/src/lib.rs @@ -8,3 +8,38 @@ pub mod native; pub mod ffi; pub use native::witness_input::WitnessInput; + + +pub mod inputs { + use crate::native::Error; + use crate::WitnessInput; + + pub trait CircuitDat { + const DAT: &'static [u8]; + } + + // TODO: Remove in favour on native::WitnessInput. + pub struct CircuitWitnessInput<'input, Dat> { + inner: WitnessInput<'input>, + _phantom: std::marker::PhantomData + } + + impl<'input, Dat: CircuitDat> CircuitWitnessInput<'input, Dat> { + pub fn new(inputs_json: String) -> Result { + let inner = WitnessInput::new(Dat::DAT, inputs_json)?; + Ok(Self { inner, _phantom: Default::default() }) + } + } + + impl<'input, Dat> From> for WitnessInput<'input> { + fn from(value: CircuitWitnessInput<'input, Dat>) -> Self { + value.inner + } + } + + impl<'input, Dat> From> for CircuitWitnessInput<'input, Dat> { + fn from(value: WitnessInput<'input>) -> Self { + Self { inner: value, _phantom: Default::default() } + } + } +} diff --git a/rust/logos-blockchain-circuits-utils/Cargo.toml b/rust/logos-blockchain-circuits-utils/Cargo.toml new file mode 100644 index 0000000..4a6d36c --- /dev/null +++ b/rust/logos-blockchain-circuits-utils/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "logos-blockchain-circuits-utils" +categories.workspace = true +description.workspace = true +edition.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +lbc-types = { workspace = true} diff --git a/rust/logos-blockchain-circuits-utils/src/lib.rs b/rust/logos-blockchain-circuits-utils/src/lib.rs new file mode 100644 index 0000000..d245b85 --- /dev/null +++ b/rust/logos-blockchain-circuits-utils/src/lib.rs @@ -0,0 +1 @@ +pub mod string; diff --git a/rust/logos-blockchain-circuits-utils/src/string.rs b/rust/logos-blockchain-circuits-utils/src/string.rs new file mode 100644 index 0000000..86e7348 --- /dev/null +++ b/rust/logos-blockchain-circuits-utils/src/string.rs @@ -0,0 +1,16 @@ +use std::path::Path; +use lbc_types::native::Error; + +pub fn into_null_terminated_string( + string: &str, +) -> Result { + std::ffi::CString::new(string) + .map_err(|error| Error::InvalidInput(Some(format!("Could not convert string to CString: {error}")))) +} + +pub fn path_as_null_terminated_string( + path: &Path, +) -> Result { + let path = path.to_str().ok_or(Error::InvalidInput(Some(format!("Could not convert the path to a string: {}", path.display()))))?; + into_null_terminated_string(path) +}