mirror of
https://github.com/vacp2p/zerokit.git
synced 2026-01-09 21:58:06 -05:00
Changes: - Unified error types (`PoseidonError`, `HashError`, etc.) across hashing, keygen, witness calculation, and serialization for consistent and descriptive error handling. - Refactored tests and examples to use `unwrap()` where safe, and limited `expect()` in library code to non-panicking cases with clear messaging. - Improved witness and proof generation by removing panicking code paths and enforcing proper error propagation. - Cleaned up outdated imports, removed unused operations in `graph.rs`, and updated public API documentation. - Updated C, Nim, and WASM FFI bindings with more robust serialization and clearer error log messages. - Added keywords to package.json and update dependencies in Makefile.toml and Nightly CI.
567 lines
15 KiB
Rust
567 lines
15 KiB
Rust
#![allow(non_camel_case_types)]
|
|
|
|
use num_bigint::BigInt;
|
|
use safer_ffi::{boxed::Box_, derive_ReprC, ffi_export, prelude::repr_c};
|
|
#[cfg(not(feature = "stateless"))]
|
|
use {safer_ffi::prelude::char_p, std::fs::File, std::io::Read};
|
|
|
|
use super::ffi_utils::{CBoolResult, CFr, CResult};
|
|
use crate::prelude::*;
|
|
|
|
#[cfg(not(feature = "stateless"))]
|
|
const MAX_CONFIG_SIZE: u64 = 1024 * 1024; // 1MB
|
|
|
|
// FFI_RLN
|
|
|
|
#[derive_ReprC]
|
|
#[repr(opaque)]
|
|
pub struct FFI_RLN(pub(crate) RLN);
|
|
|
|
// RLN initialization APIs
|
|
|
|
#[cfg(not(feature = "stateless"))]
|
|
#[ffi_export]
|
|
pub fn ffi_rln_new(
|
|
tree_depth: usize,
|
|
config_path: char_p::Ref<'_>,
|
|
) -> CResult<repr_c::Box<FFI_RLN>, repr_c::String> {
|
|
let config_str = File::open(config_path.to_str())
|
|
.and_then(|mut file| {
|
|
let metadata = file.metadata()?;
|
|
if metadata.len() > MAX_CONFIG_SIZE {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::InvalidData,
|
|
format!(
|
|
"Config file too large: {} bytes (max {} bytes)",
|
|
metadata.len(),
|
|
MAX_CONFIG_SIZE
|
|
),
|
|
));
|
|
}
|
|
let mut s = String::new();
|
|
file.read_to_string(&mut s)?;
|
|
Ok(s)
|
|
})
|
|
.unwrap_or_default();
|
|
|
|
match RLN::new(tree_depth, config_str.as_str()) {
|
|
Ok(rln) => CResult {
|
|
ok: Some(Box_::new(FFI_RLN(rln))),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "stateless")]
|
|
#[ffi_export]
|
|
pub fn ffi_rln_new() -> CResult<repr_c::Box<FFI_RLN>, repr_c::String> {
|
|
match RLN::new() {
|
|
Ok(rln) => CResult {
|
|
ok: Some(Box_::new(FFI_RLN(rln))),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "stateless"))]
|
|
#[ffi_export]
|
|
pub fn ffi_rln_new_with_params(
|
|
tree_depth: usize,
|
|
zkey_data: &repr_c::Vec<u8>,
|
|
graph_data: &repr_c::Vec<u8>,
|
|
config_path: char_p::Ref<'_>,
|
|
) -> CResult<repr_c::Box<FFI_RLN>, repr_c::String> {
|
|
let config_str = File::open(config_path.to_str())
|
|
.and_then(|mut file| {
|
|
let metadata = file.metadata()?;
|
|
if metadata.len() > MAX_CONFIG_SIZE {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::InvalidData,
|
|
format!(
|
|
"Config file too large: {} bytes (max {} bytes)",
|
|
metadata.len(),
|
|
MAX_CONFIG_SIZE
|
|
),
|
|
));
|
|
}
|
|
let mut s = String::new();
|
|
file.read_to_string(&mut s)?;
|
|
Ok(s)
|
|
})
|
|
.unwrap_or_default();
|
|
|
|
match RLN::new_with_params(
|
|
tree_depth,
|
|
zkey_data.to_vec(),
|
|
graph_data.to_vec(),
|
|
config_str.as_str(),
|
|
) {
|
|
Ok(rln) => CResult {
|
|
ok: Some(Box_::new(FFI_RLN(rln))),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "stateless")]
|
|
#[ffi_export]
|
|
pub fn ffi_rln_new_with_params(
|
|
zkey_data: &repr_c::Vec<u8>,
|
|
graph_data: &repr_c::Vec<u8>,
|
|
) -> CResult<repr_c::Box<FFI_RLN>, repr_c::String> {
|
|
match RLN::new_with_params(zkey_data.to_vec(), graph_data.to_vec()) {
|
|
Ok(rln) => CResult {
|
|
ok: Some(Box_::new(FFI_RLN(rln))),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_free(rln: repr_c::Box<FFI_RLN>) {
|
|
drop(rln);
|
|
}
|
|
|
|
// RLNProof
|
|
|
|
#[derive_ReprC]
|
|
#[repr(opaque)]
|
|
pub struct FFI_RLNProof(pub(crate) RLNProof);
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_proof_get_values(
|
|
rln_proof: &repr_c::Box<FFI_RLNProof>,
|
|
) -> repr_c::Box<FFI_RLNProofValues> {
|
|
Box_::new(FFI_RLNProofValues(rln_proof.0.proof_values))
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_proof_to_bytes_le(
|
|
rln_proof: &repr_c::Box<FFI_RLNProof>,
|
|
) -> CResult<repr_c::Vec<u8>, repr_c::String> {
|
|
match rln_proof_to_bytes_le(&rln_proof.0) {
|
|
Ok(bytes) => CResult {
|
|
ok: Some(bytes.into()),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_proof_to_bytes_be(
|
|
rln_proof: &repr_c::Box<FFI_RLNProof>,
|
|
) -> CResult<repr_c::Vec<u8>, repr_c::String> {
|
|
match rln_proof_to_bytes_be(&rln_proof.0) {
|
|
Ok(bytes) => CResult {
|
|
ok: Some(bytes.into()),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_bytes_le_to_rln_proof(
|
|
bytes: &repr_c::Vec<u8>,
|
|
) -> CResult<repr_c::Box<FFI_RLNProof>, repr_c::String> {
|
|
match bytes_le_to_rln_proof(bytes) {
|
|
Ok((rln_proof, _)) => CResult {
|
|
ok: Some(Box_::new(FFI_RLNProof(rln_proof))),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_bytes_be_to_rln_proof(
|
|
bytes: &repr_c::Vec<u8>,
|
|
) -> CResult<repr_c::Box<FFI_RLNProof>, repr_c::String> {
|
|
match bytes_be_to_rln_proof(bytes) {
|
|
Ok((rln_proof, _)) => CResult {
|
|
ok: Some(Box_::new(FFI_RLNProof(rln_proof))),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_proof_free(rln_proof: repr_c::Box<FFI_RLNProof>) {
|
|
drop(rln_proof);
|
|
}
|
|
|
|
// RLNWitnessInput
|
|
|
|
#[derive_ReprC]
|
|
#[repr(opaque)]
|
|
pub struct FFI_RLNWitnessInput(pub(crate) RLNWitnessInput);
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_witness_input_new(
|
|
identity_secret: &CFr,
|
|
user_message_limit: &CFr,
|
|
message_id: &CFr,
|
|
path_elements: &repr_c::Vec<CFr>,
|
|
identity_path_index: &repr_c::Vec<u8>,
|
|
x: &CFr,
|
|
external_nullifier: &CFr,
|
|
) -> CResult<repr_c::Box<FFI_RLNWitnessInput>, repr_c::String> {
|
|
let mut identity_secret_fr = identity_secret.0;
|
|
let path_elements: Vec<Fr> = path_elements.iter().map(|cfr| cfr.0).collect();
|
|
let identity_path_index: Vec<u8> = identity_path_index.iter().copied().collect();
|
|
match RLNWitnessInput::new(
|
|
IdSecret::from(&mut identity_secret_fr),
|
|
user_message_limit.0,
|
|
message_id.0,
|
|
path_elements,
|
|
identity_path_index,
|
|
x.0,
|
|
external_nullifier.0,
|
|
) {
|
|
Ok(witness) => CResult {
|
|
ok: Some(Box_::new(FFI_RLNWitnessInput(witness))),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_witness_to_bytes_le(
|
|
witness: &repr_c::Box<FFI_RLNWitnessInput>,
|
|
) -> CResult<repr_c::Vec<u8>, repr_c::String> {
|
|
match rln_witness_to_bytes_le(&witness.0) {
|
|
Ok(bytes) => CResult {
|
|
ok: Some(bytes.into()),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_witness_to_bytes_be(
|
|
witness: &repr_c::Box<FFI_RLNWitnessInput>,
|
|
) -> CResult<repr_c::Vec<u8>, repr_c::String> {
|
|
match rln_witness_to_bytes_be(&witness.0) {
|
|
Ok(bytes) => CResult {
|
|
ok: Some(bytes.into()),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_bytes_le_to_rln_witness(
|
|
bytes: &repr_c::Vec<u8>,
|
|
) -> CResult<repr_c::Box<FFI_RLNWitnessInput>, repr_c::String> {
|
|
match bytes_le_to_rln_witness(bytes) {
|
|
Ok((witness, _)) => CResult {
|
|
ok: Some(Box_::new(FFI_RLNWitnessInput(witness))),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_bytes_be_to_rln_witness(
|
|
bytes: &repr_c::Vec<u8>,
|
|
) -> CResult<repr_c::Box<FFI_RLNWitnessInput>, repr_c::String> {
|
|
match bytes_be_to_rln_witness(bytes) {
|
|
Ok((witness, _)) => CResult {
|
|
ok: Some(Box_::new(FFI_RLNWitnessInput(witness))),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_witness_to_bigint_json(
|
|
witness: &repr_c::Box<FFI_RLNWitnessInput>,
|
|
) -> CResult<repr_c::String, repr_c::String> {
|
|
match rln_witness_to_bigint_json(&witness.0) {
|
|
Ok(json) => CResult {
|
|
ok: Some(json.to_string().into()),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_witness_input_free(witness: repr_c::Box<FFI_RLNWitnessInput>) {
|
|
drop(witness);
|
|
}
|
|
|
|
// RLNProofValues
|
|
|
|
#[derive_ReprC]
|
|
#[repr(opaque)]
|
|
pub struct FFI_RLNProofValues(pub(crate) RLNProofValues);
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_proof_values_get_y(pv: &repr_c::Box<FFI_RLNProofValues>) -> repr_c::Box<CFr> {
|
|
CFr::from(pv.0.y).into()
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_proof_values_get_nullifier(
|
|
pv: &repr_c::Box<FFI_RLNProofValues>,
|
|
) -> repr_c::Box<CFr> {
|
|
CFr::from(pv.0.nullifier).into()
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_proof_values_get_root(pv: &repr_c::Box<FFI_RLNProofValues>) -> repr_c::Box<CFr> {
|
|
CFr::from(pv.0.root).into()
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_proof_values_get_x(pv: &repr_c::Box<FFI_RLNProofValues>) -> repr_c::Box<CFr> {
|
|
CFr::from(pv.0.x).into()
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_proof_values_get_external_nullifier(
|
|
pv: &repr_c::Box<FFI_RLNProofValues>,
|
|
) -> repr_c::Box<CFr> {
|
|
CFr::from(pv.0.external_nullifier).into()
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_proof_values_to_bytes_le(pv: &repr_c::Box<FFI_RLNProofValues>) -> repr_c::Vec<u8> {
|
|
rln_proof_values_to_bytes_le(&pv.0).into()
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_proof_values_to_bytes_be(pv: &repr_c::Box<FFI_RLNProofValues>) -> repr_c::Vec<u8> {
|
|
rln_proof_values_to_bytes_be(&pv.0).into()
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_bytes_le_to_rln_proof_values(
|
|
bytes: &repr_c::Vec<u8>,
|
|
) -> CResult<repr_c::Box<FFI_RLNProofValues>, repr_c::String> {
|
|
match bytes_le_to_rln_proof_values(bytes) {
|
|
Ok((pv, _)) => CResult {
|
|
ok: Some(Box_::new(FFI_RLNProofValues(pv))),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(format!("{:?}", err).into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_bytes_be_to_rln_proof_values(
|
|
bytes: &repr_c::Vec<u8>,
|
|
) -> CResult<repr_c::Box<FFI_RLNProofValues>, repr_c::String> {
|
|
match bytes_be_to_rln_proof_values(bytes) {
|
|
Ok((pv, _)) => CResult {
|
|
ok: Some(Box_::new(FFI_RLNProofValues(pv))),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(format!("{:?}", err).into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_rln_proof_values_free(proof_values: repr_c::Box<FFI_RLNProofValues>) {
|
|
drop(proof_values);
|
|
}
|
|
|
|
// Proof generation APIs
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_generate_rln_proof(
|
|
rln: &repr_c::Box<FFI_RLN>,
|
|
witness: &repr_c::Box<FFI_RLNWitnessInput>,
|
|
) -> CResult<repr_c::Box<FFI_RLNProof>, repr_c::String> {
|
|
match rln.0.generate_rln_proof(&witness.0) {
|
|
Ok((proof, proof_values)) => {
|
|
let rln_proof = RLNProof {
|
|
proof_values,
|
|
proof,
|
|
};
|
|
CResult {
|
|
ok: Some(Box_::new(FFI_RLNProof(rln_proof))),
|
|
err: None,
|
|
}
|
|
}
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_generate_rln_proof_with_witness(
|
|
rln: &repr_c::Box<FFI_RLN>,
|
|
calculated_witness: &repr_c::Vec<repr_c::String>,
|
|
witness: &repr_c::Box<FFI_RLNWitnessInput>,
|
|
) -> CResult<repr_c::Box<FFI_RLNProof>, repr_c::String> {
|
|
let calculated_witness_bigint: Result<Vec<BigInt>, _> = calculated_witness
|
|
.iter()
|
|
.map(|s| {
|
|
let s_str = unsafe { std::str::from_utf8_unchecked(s.as_bytes()) };
|
|
s_str.parse::<BigInt>()
|
|
})
|
|
.collect();
|
|
|
|
let calculated_witness_bigint = match calculated_witness_bigint {
|
|
Ok(w) => w,
|
|
Err(err) => {
|
|
return CResult {
|
|
ok: None,
|
|
err: Some(format!("Failed to parse witness: {}", err).into()),
|
|
}
|
|
}
|
|
};
|
|
|
|
match rln
|
|
.0
|
|
.generate_rln_proof_with_witness(calculated_witness_bigint, &witness.0)
|
|
{
|
|
Ok((proof, proof_values)) => {
|
|
let rln_proof = RLNProof {
|
|
proof_values,
|
|
proof,
|
|
};
|
|
CResult {
|
|
ok: Some(Box_::new(FFI_RLNProof(rln_proof))),
|
|
err: None,
|
|
}
|
|
}
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
// Proof verification APIs
|
|
|
|
#[cfg(not(feature = "stateless"))]
|
|
#[ffi_export]
|
|
pub fn ffi_verify_rln_proof(
|
|
rln: &repr_c::Box<FFI_RLN>,
|
|
rln_proof: &repr_c::Box<FFI_RLNProof>,
|
|
x: &CFr,
|
|
) -> CBoolResult {
|
|
match rln
|
|
.0
|
|
.verify_rln_proof(&rln_proof.0.proof, &rln_proof.0.proof_values, &x.0)
|
|
{
|
|
Ok(verified) => CBoolResult {
|
|
ok: verified,
|
|
err: None,
|
|
},
|
|
Err(err) => CBoolResult {
|
|
ok: false,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_verify_with_roots(
|
|
rln: &repr_c::Box<FFI_RLN>,
|
|
rln_proof: &repr_c::Box<FFI_RLNProof>,
|
|
roots: &repr_c::Vec<CFr>,
|
|
x: &CFr,
|
|
) -> CBoolResult {
|
|
let roots_fr: Vec<Fr> = roots.iter().map(|cfr| cfr.0).collect();
|
|
|
|
match rln.0.verify_with_roots(
|
|
&rln_proof.0.proof,
|
|
&rln_proof.0.proof_values,
|
|
&x.0,
|
|
&roots_fr,
|
|
) {
|
|
Ok(verified) => CBoolResult {
|
|
ok: verified,
|
|
err: None,
|
|
},
|
|
Err(err) => CBoolResult {
|
|
ok: false,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
// Identity secret recovery API
|
|
|
|
#[ffi_export]
|
|
pub fn ffi_recover_id_secret(
|
|
proof_values_1: &repr_c::Box<FFI_RLNProofValues>,
|
|
proof_values_2: &repr_c::Box<FFI_RLNProofValues>,
|
|
) -> CResult<repr_c::Box<CFr>, repr_c::String> {
|
|
match recover_id_secret(&proof_values_1.0, &proof_values_2.0) {
|
|
Ok(secret) => CResult {
|
|
ok: Some(Box_::new(CFr::from(*secret))),
|
|
err: None,
|
|
},
|
|
Err(err) => CResult {
|
|
ok: None,
|
|
err: Some(err.to_string().into()),
|
|
},
|
|
}
|
|
}
|