Various fixes and improvements.

This commit is contained in:
Alejandro Cabeza Romero 2026-04-27 18:17:40 +02:00
parent 67b35faf4f
commit f91bd073d1
No known key found for this signature in database
GPG Key ID: DA3D14AE478030FD
11 changed files with 116 additions and 115 deletions

56
rust/Cargo.lock generated
View File

@ -166,7 +166,6 @@ name = "logos-blockchain-circuits-types"
version = "0.4.2"
dependencies = [
"libc",
"thiserror",
]
[[package]]
@ -197,24 +196,6 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.7.4"
@ -304,17 +285,6 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tar"
version = "0.4.45"
@ -326,32 +296,6 @@ dependencies = [
"xattr",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "untrusted"
version = "0.9.0"

View File

@ -24,5 +24,4 @@ lbc-types = { default-features = false, package = "logos-blockchain-circuits-typ
flate2 = "1"
libc = "0.2.185"
tar = "0.4"
thiserror = "2.0.18"
ureq = "3.3.0"

View File

@ -10,7 +10,7 @@ repository.workspace = true
version.workspace = true
[dependencies]
lbc-types = { workspace = true}
lbc-types = { workspace = true }
[build-dependencies]
flate2 = { workspace = true }

View File

@ -20,6 +20,7 @@ fn get_artifact_url(version: &str, os: &str, arch: &str) -> String {
fn fetch_library(version: &str, os: &str, arch: &str) -> Response<Body> {
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}. \
@ -35,7 +36,11 @@ fn unpack_library(response: Response<Body>, version: &str, os: &str, arch: &str,
let unpacked_artifact_path = output_dir.join(get_artifact_name(version, os, arch));
let unpacked_library_directory = unpacked_artifact_path.join(CIRCUIT_NAME);
assert!(unpacked_library_directory.exists(), "Expected the unpacked library at {}.", unpacked_library_directory.display());
if !unpacked_library_directory.exists() {
panic!("Failed to find the unpacked library at {}.", unpacked_library_directory.display());
}
unpacked_library_directory
}
@ -57,12 +62,18 @@ fn provision_library() -> PathBuf {
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);
assert!(lib_dir_path.exists(), "The library directory at {} does not exist.", lib_dir_path.display());
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(|_| {

View File

@ -1,18 +1,28 @@
use std::ffi::c_char;
use std::path::Path;
use lbc_types::{ffi, native::{Bytes, Error, WitnessInput}};
use crate::ffi::{poq_generate_witness, poq_generate_witness_from_files};
fn into_null_terminated_string(
string: &str,
) -> Result<std::ffi::CString, Error> {
std::ffi::CString::new(string)
.map_err(|error| Error::InvalidInput(Some(format!("Could not convert string to CString: {error}"))))
}
fn path_as_null_terminated_string(
path: &Path,
) -> Result<std::ffi::CString, Error> {
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 fn generate_witness(
input: WitnessInput,
) -> Result<Bytes, Error> {
let ffi_input_guard = input.as_ffi();
let ffi_input = ffi_input_guard.as_ref();
let mut ffi_output_bytes = ffi::Bytes {
data: std::ptr::null_mut(),
size: 0
};
let mut ffi_output_bytes = ffi::Bytes::null();
let status = unsafe {
poq_generate_witness(
@ -29,15 +39,15 @@ pub fn generate_witness_from_files(
inputs: &Path,
output: &Path,
) -> Result<(), Error> {
let dat = dat.to_str().ok_or_else(|| Error::InvalidInput)?; // TODO: Message
let inputs = inputs.to_str().ok_or_else(|| Error::InvalidInput)?;
let output = output.to_str().ok_or_else(|| Error::InvalidInput)?;
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 (
dat.as_ptr() as *const c_char,
inputs.as_ptr() as *const c_char,
output.as_ptr() as *const c_char
c_dat.as_ptr(),
c_inputs.as_ptr(),
c_output.as_ptr(),
)
}.try_into()
}

View File

@ -11,4 +11,3 @@ version.workspace = true
[dependencies]
libc = { workspace = true }
thiserror = { workspace = true }

View File

@ -8,6 +8,24 @@ mod inner {
}
}
impl<T> inner::Buffer<*const T> {
pub fn null() -> Self {
Self {
data: std::ptr::null(),
size: 0,
}
}
}
impl<T> inner::Buffer<*mut T> {
pub fn null() -> Self {
Self {
data: std::ptr::null_mut(),
size: 0,
}
}
}
/// Owned byte buffer returned by the C witness generator functions.
///
/// The inner `data` pointer must be null-initialized. It's heap-allocated by the C side and must be
@ -29,7 +47,7 @@ pub type ConstBytes = inner::Buffer<*const u8>;
///
/// # Safety
///
/// Dereferences raw pointers. The caller must ensure that the pointer is valid.
/// Dereferences raw pointers.
pub unsafe fn free_bytes(bytes: *mut Bytes) {
if bytes.is_null() {
return;

View File

@ -1,17 +1,20 @@
use std::ffi::c_char;
/// Status codes for C API functions.
#[repr(C)]
pub enum Code {
Ok = 0,
DynError = 1,
InvalidInput = 2,
OutOfMemory = 3,
#[derive(PartialEq, Eq)] // Enables comparisons with named constants.
#[repr(transparent)]
pub struct Code(pub i32);
impl Code {
pub const OK: Self = Self(0);
pub const DYN_ERROR: Self = Self(1);
pub const INVALID_INPUT: Self = Self(2);
pub const OUT_OF_MEMORY: Self = Self(3);
}
impl Code {
pub fn is_ok(&self) -> bool {
matches!(self, Code::Ok)
self == &Self::OK
}
pub fn is_error(&self) -> bool {
@ -29,7 +32,7 @@ pub struct Status {
impl Status {
pub fn ok() -> Self {
Status {
code: Code::Ok,
code: Code::OK,
message: [0; 256],
}
}

View File

@ -26,10 +26,14 @@ impl From<Vec<u8>> for Bytes {
impl From<ffi::Bytes> for Bytes {
fn from(mut ffi_value: ffi::Bytes) -> Self {
let raw = unsafe {
std::slice::from_raw_parts(ffi_value.data, ffi_value.size).to_vec()
let vec = if ffi_value.size == 0 || ffi_value.data.is_null() {
Vec::new()
} else {
unsafe {
std::slice::from_raw_parts(ffi_value.data, ffi_value.size).to_vec()
}
};
unsafe { ffi::free_bytes(&mut ffi_value); }
Self(raw)
unsafe { ffi::free_bytes(&mut ffi_value) };
Self(vec)
}
}

View File

@ -1,44 +1,52 @@
use std::ffi::CStr;
use thiserror::Error;
use std::fmt::Display;
use crate::ffi::status::Code as FfiStatusCode;
pub type DynError = Box<dyn std::error::Error + Send + Sync>;
pub type Result<T> = std::result::Result<T, Error>;
/// Error returned when a witness generator call does not succeed.
#[derive(Debug, Error)]
#[derive(Debug)]
pub enum Error {
#[error("Invalid input")]
InvalidInput,
#[error("Out of memory")]
OutOfMemory,
#[error(transparent)]
Other(#[from] DynError),
InvalidInput(Option<String>),
OutOfMemory(Option<String>),
Other(Option<String>),
}
impl std::error::Error for Error {}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (kind, message) = match self {
Error::InvalidInput(msg) => ("Invalid input", msg),
Error::OutOfMemory(msg) => ("Out of memory", msg),
Error::Other(msg) => ("Other error", msg),
};
match message {
Some(message) => write!(f, "{kind}: {message}"),
None => write!(f, "{kind}"),
}
}
}
impl TryFrom<crate::ffi::Status> for () {
type Error = Error;
fn try_from(status: crate::ffi::Status) -> Result<()> {
let message: Option<String> = if status.has_message() {
let status_message = unsafe {
CStr::from_ptr(status.message.as_ptr())
};
Some(status_message.to_string_lossy().into_owned())
} else {
None
};
match status.code {
FfiStatusCode::Ok => Ok(()),
FfiStatusCode::DynError => {
let message: Option<&CStr> =
if status.has_message() {
let status_message = unsafe {
CStr::from_ptr(status.message.as_ptr())
};
Some(status_message)
} else {
None
};
let error_message = message
.map(|inner| DynError::from(inner.to_string_lossy().into_owned()))
.unwrap_or_else(|| DynError::from("Unknown error"));
Err(error_message.into())
},
FfiStatusCode::InvalidInput => Err(Error::InvalidInput),
FfiStatusCode::OutOfMemory => Err(Error::OutOfMemory),
FfiStatusCode::OK => Ok(()),
FfiStatusCode::DYN_ERROR => Err(Error::Other(message)),
FfiStatusCode::INVALID_INPUT => Err(Error::InvalidInput(message)),
FfiStatusCode::OUT_OF_MEMORY => Err(Error::OutOfMemory(message)),
other => Err(Error::Other(Some(format!("Unknown status code: {}", other.0))))
}
}
}

View File

@ -1,5 +1,6 @@
use std::ffi::{CString, NulError};
use std::ffi::CString;
use crate::ffi;
use crate::native::Error;
/// Input for witness generators
pub struct WitnessInput {
@ -10,8 +11,12 @@ pub struct WitnessInput {
}
impl WitnessInput {
pub fn new(dat: Vec<u8>, inputs_json: String) -> Result<Self, NulError> {
let inputs_json = CString::new(inputs_json)?;
pub fn new(dat: Vec<u8>, inputs_json: String) -> Result<Self, Error> {
let inputs_json = CString::new(inputs_json).map_err(
|error| Error::InvalidInput(Some(
format!("The parameter inputs_json could not be converted to CString: {}", error)
))
)?;
Ok(Self { dat, inputs_json })
}