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)
+}