feat(rln): extend error handling for rln module (#358)

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.
This commit is contained in:
Vinh Trịnh
2025-12-17 19:27:07 +07:00
committed by GitHub
parent c890bc83ad
commit 0ebeea50fd
56 changed files with 1667 additions and 1320 deletions

View File

@@ -1,14 +1,14 @@
[tasks.build]
clear = true
dependencies = ["pack_build", "pack_rename"]
dependencies = ["pack_build", "pack_rename", "pack_add_keywords"]
[tasks.build_parallel]
clear = true
dependencies = ["pack_build_parallel", "pack_rename"]
dependencies = ["pack_build_parallel", "pack_rename", "pack_add_keywords"]
[tasks.build_utils]
clear = true
dependencies = ["pack_build_utils", "pack_rename_utils"]
dependencies = ["pack_build_utils", "pack_rename_utils", "pack_add_keywords"]
[tasks.pack_build]
command = "wasm-pack"
@@ -54,6 +54,11 @@ args = [
[tasks.pack_rename_utils]
script = "sed -i.bak 's/rln-wasm/zerokit-rln-wasm-utils/g' pkg/package.json && rm pkg/package.json.bak"
[tasks.pack_add_keywords]
script = """
jq '. + {keywords: ["zerokit", "rln", "wasm"]}' pkg/package.json > pkg/package.json.tmp && \
mv pkg/package.json.tmp pkg/package.json
"""
[tasks.test]
command = "wasm-pack"

View File

@@ -13,7 +13,7 @@ function debugUint8Array(uint8Array) {
async function calculateWitness(circomPath, inputs, witnessCalculatorFile) {
const wasmFile = readFileSync(circomPath);
const wasmFileBuffer = wasmFile.slice(
const wasmFileBuffer = wasmFile.buffer.slice(
wasmFile.byteOffset,
wasmFile.byteOffset + wasmFile.byteLength
);
@@ -49,11 +49,23 @@ async function main() {
console.log("Creating RLN instance");
const zkeyData = readFileSync(zkeyPath);
const rlnInstance = new rlnWasm.WasmRLN(new Uint8Array(zkeyData));
let rlnInstance;
try {
rlnInstance = new rlnWasm.WasmRLN(new Uint8Array(zkeyData));
} catch (error) {
console.error("Initial RLN instance creation error:", error);
return;
}
console.log("RLN instance created successfully");
console.log("\nGenerating identity keys");
const identity = rlnWasm.Identity.generate();
let identity;
try {
identity = rlnWasm.Identity.generate();
} catch (error) {
console.error("Key generation error:", error);
return;
}
const identitySecret = identity.getSecretHash();
const idCommitment = identity.getCommitment();
console.log("Identity generated");
@@ -65,10 +77,16 @@ async function main() {
console.log(" - user_message_limit = " + userMessageLimit.debug());
console.log("\nComputing rate commitment");
const rateCommitment = rlnWasm.Hasher.poseidonHashPair(
idCommitment,
userMessageLimit
);
let rateCommitment;
try {
rateCommitment = rlnWasm.Hasher.poseidonHashPair(
idCommitment,
userMessageLimit
);
} catch (error) {
console.error("Rate commitment hash error:", error);
return;
}
console.log(" - rate_commitment = " + rateCommitment.debug());
console.log("\nWasmFr serialization: WasmFr <-> bytes");
@@ -79,22 +97,59 @@ async function main() {
"]"
);
const deserRateCommitment = rlnWasm.WasmFr.fromBytesLE(serRateCommitment);
let deserRateCommitment;
try {
deserRateCommitment = rlnWasm.WasmFr.fromBytesLE(serRateCommitment);
} catch (error) {
console.error("Rate commitment deserialization error:", error);
return;
}
console.log(
" - deserialized rate_commitment = " + deserRateCommitment.debug()
);
console.log("\nIdentity serialization: Identity <-> bytes");
const serIdentity = identity.toBytesLE();
console.log(
" - serialized identity = [" + debugUint8Array(serIdentity) + "]"
);
let deserIdentity;
try {
deserIdentity = rlnWasm.Identity.fromBytesLE(serIdentity);
} catch (error) {
console.error("Identity deserialization error:", error);
return;
}
const deserIdentitySecret = deserIdentity.getSecretHash();
const deserIdCommitment = deserIdentity.getCommitment();
console.log(
" - deserialized identity = [" +
deserIdentitySecret.debug() +
", " +
deserIdCommitment.debug() +
"]"
);
console.log("\nBuilding Merkle path for stateless mode");
const treeDepth = 20;
const defaultLeaf = rlnWasm.WasmFr.zero();
const defaultHashes = [];
defaultHashes[0] = rlnWasm.Hasher.poseidonHashPair(defaultLeaf, defaultLeaf);
for (let i = 1; i < treeDepth - 1; i++) {
defaultHashes[i] = rlnWasm.Hasher.poseidonHashPair(
defaultHashes[i - 1],
defaultHashes[i - 1]
try {
defaultHashes[0] = rlnWasm.Hasher.poseidonHashPair(
defaultLeaf,
defaultLeaf
);
for (let i = 1; i < treeDepth - 1; i++) {
defaultHashes[i] = rlnWasm.Hasher.poseidonHashPair(
defaultHashes[i - 1],
defaultHashes[i - 1]
);
}
} catch (error) {
console.error("Poseidon hash error:", error);
return;
}
const pathElements = new rlnWasm.VecWasmFr();
@@ -110,7 +165,13 @@ async function main() {
" - serialized path_elements = [" + debugUint8Array(serPathElements) + "]"
);
const deserPathElements = rlnWasm.VecWasmFr.fromBytesLE(serPathElements);
let deserPathElements;
try {
deserPathElements = rlnWasm.VecWasmFr.fromBytesLE(serPathElements);
} catch (error) {
console.error("Path elements deserialization error:", error);
return;
}
console.log(" - deserialized path_elements = ", deserPathElements.debug());
console.log("\nUint8Array serialization: Uint8Array <-> bytes");
@@ -119,21 +180,30 @@ async function main() {
" - serialized path_index = [" + debugUint8Array(serPathIndex) + "]"
);
const deserPathIndex = rlnWasm.Uint8ArrayUtils.fromBytesLE(serPathIndex);
let deserPathIndex;
try {
deserPathIndex = rlnWasm.Uint8ArrayUtils.fromBytesLE(serPathIndex);
} catch (error) {
console.error("Path index deserialization error:", error);
return;
}
console.log(" - deserialized path_index =", deserPathIndex);
console.log("\nComputing Merkle root for stateless mode");
console.log(" - computing root for index 0 with rate_commitment");
let computedRoot = rlnWasm.Hasher.poseidonHashPair(
rateCommitment,
defaultLeaf
);
for (let i = 1; i < treeDepth; i++) {
computedRoot = rlnWasm.Hasher.poseidonHashPair(
computedRoot,
defaultHashes[i - 1]
);
let computedRoot;
try {
computedRoot = rlnWasm.Hasher.poseidonHashPair(rateCommitment, defaultLeaf);
for (let i = 1; i < treeDepth; i++) {
computedRoot = rlnWasm.Hasher.poseidonHashPair(
computedRoot,
defaultHashes[i - 1]
);
}
} catch (error) {
console.error("Poseidon hash error:", error);
return;
}
console.log(" - computed_root = " + computedRoot.debug());
@@ -142,28 +212,47 @@ async function main() {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
]);
const x = rlnWasm.Hasher.hashToFieldLE(signal);
let x;
try {
x = rlnWasm.Hasher.hashToFieldLE(signal);
} catch (error) {
console.error("Hash signal error:", error);
return;
}
console.log(" - x = " + x.debug());
console.log("\nHashing epoch");
const epochStr = "test-epoch";
const epoch = rlnWasm.Hasher.hashToFieldLE(
new TextEncoder().encode(epochStr)
);
let epoch;
try {
epoch = rlnWasm.Hasher.hashToFieldLE(new TextEncoder().encode(epochStr));
} catch (error) {
console.error("Hash epoch error:", error);
return;
}
console.log(" - epoch = " + epoch.debug());
console.log("\nHashing RLN identifier");
const rlnIdStr = "test-rln-identifier";
const rlnIdentifier = rlnWasm.Hasher.hashToFieldLE(
new TextEncoder().encode(rlnIdStr)
);
let rlnIdentifier;
try {
rlnIdentifier = rlnWasm.Hasher.hashToFieldLE(
new TextEncoder().encode(rlnIdStr)
);
} catch (error) {
console.error("Hash RLN identifier error:", error);
return;
}
console.log(" - rln_identifier = " + rlnIdentifier.debug());
console.log("\nComputing Poseidon hash for external nullifier");
const externalNullifier = rlnWasm.Hasher.poseidonHashPair(
epoch,
rlnIdentifier
);
let externalNullifier;
try {
externalNullifier = rlnWasm.Hasher.poseidonHashPair(epoch, rlnIdentifier);
} catch (error) {
console.error("External nullifier hash error:", error);
return;
}
console.log(" - external_nullifier = " + externalNullifier.debug());
console.log("\nCreating message_id");
@@ -182,8 +271,37 @@ async function main() {
);
console.log("RLN Witness created successfully");
console.log(
"\nWasmRLNWitnessInput serialization: WasmRLNWitnessInput <-> bytes"
);
let serWitness;
try {
serWitness = witness.toBytesLE();
} catch (error) {
console.error("Witness serialization error:", error);
return;
}
console.log(
" - serialized witness = [" + debugUint8Array(serWitness) + " ]"
);
let deserWitness;
try {
deserWitness = rlnWasm.WasmRLNWitnessInput.fromBytesLE(serWitness);
} catch (error) {
console.error("Witness deserialization error:", error);
return;
}
console.log(" - witness deserialized successfully");
console.log("\nCalculating witness");
const witnessJson = witness.toBigIntJson();
let witnessJson;
try {
witnessJson = witness.toBigIntJson();
} catch (error) {
console.error("Witness to BigInt JSON error:", error);
return;
}
const calculatedWitness = await calculateWitness(
circomPath,
witnessJson,
@@ -192,10 +310,16 @@ async function main() {
console.log("Witness calculated successfully");
console.log("\nGenerating RLN Proof");
const rln_proof = rlnInstance.generateRLNProofWithWitness(
calculatedWitness,
witness
);
let rln_proof;
try {
rln_proof = rlnInstance.generateRLNProofWithWitness(
calculatedWitness,
witness
);
} catch (error) {
console.error("Proof generation error:", error);
return;
}
console.log("Proof generated successfully");
console.log("\nGetting proof values");
@@ -209,10 +333,22 @@ async function main() {
);
console.log("\nRLNProof serialization: RLNProof <-> bytes");
const serProof = rln_proof.toBytesLE();
let serProof;
try {
serProof = rln_proof.toBytesLE();
} catch (error) {
console.error("Proof serialization error:", error);
return;
}
console.log(" - serialized proof = [" + debugUint8Array(serProof) + " ]");
const deserProof = rlnWasm.WasmRLNProof.fromBytesLE(serProof);
let deserProof;
try {
deserProof = rlnWasm.WasmRLNProof.fromBytesLE(serProof);
} catch (error) {
console.error("Proof deserialization error:", error);
return;
}
console.log(" - proof deserialized successfully");
console.log("\nRLNProofValues serialization: RLNProofValues <-> bytes");
@@ -221,8 +357,13 @@ async function main() {
" - serialized proof_values = [" + debugUint8Array(serProofValues) + " ]"
);
const deserProofValues2 =
rlnWasm.WasmRLNProofValues.fromBytesLE(serProofValues);
let deserProofValues2;
try {
deserProofValues2 = rlnWasm.WasmRLNProofValues.fromBytesLE(serProofValues);
} catch (error) {
console.error("Proof values deserialization error:", error);
return;
}
console.log(" - proof_values deserialized successfully");
console.log(
" - deserialized external_nullifier = " +
@@ -232,7 +373,13 @@ async function main() {
console.log("\nVerifying Proof");
const roots = new rlnWasm.VecWasmFr();
roots.push(computedRoot);
const isValid = rlnInstance.verifyWithRoots(rln_proof, roots, x);
let isValid;
try {
isValid = rlnInstance.verifyWithRoots(rln_proof, roots, x);
} catch (error) {
console.error("Proof verification error:", error);
return;
}
if (isValid) {
console.log("Proof verified successfully");
} else {
@@ -249,7 +396,13 @@ async function main() {
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]);
const x2 = rlnWasm.Hasher.hashToFieldLE(signal2);
let x2;
try {
x2 = rlnWasm.Hasher.hashToFieldLE(signal2);
} catch (error) {
console.error("Hash second signal error:", error);
return;
}
console.log(" - x2 = " + x2.debug());
console.log("\nCreating second message with the same id");
@@ -269,7 +422,13 @@ async function main() {
console.log("Second RLN Witness created successfully");
console.log("\nCalculating second witness");
const witnessJson2 = witness2.toBigIntJson();
let witnessJson2;
try {
witnessJson2 = witness2.toBigIntJson();
} catch (error) {
console.error("Second witness to BigInt JSON error:", error);
return;
}
const calculatedWitness2 = await calculateWitness(
circomPath,
witnessJson2,
@@ -278,24 +437,42 @@ async function main() {
console.log("Second witness calculated successfully");
console.log("\nGenerating second RLN Proof");
const rln_proof2 = rlnInstance.generateRLNProofWithWitness(
calculatedWitness2,
witness2
);
let rln_proof2;
try {
rln_proof2 = rlnInstance.generateRLNProofWithWitness(
calculatedWitness2,
witness2
);
} catch (error) {
console.error("Second proof generation error:", error);
return;
}
console.log("Second proof generated successfully");
console.log("\nVerifying second proof");
const isValid2 = rlnInstance.verifyWithRoots(rln_proof2, roots, x2);
let isValid2;
try {
isValid2 = rlnInstance.verifyWithRoots(rln_proof2, roots, x2);
} catch (error) {
console.error("Proof verification error:", error);
return;
}
if (isValid2) {
console.log("Second proof verified successfully");
console.log("\nRecovering identity secret");
const proofValues1 = rln_proof.getValues();
const proofValues2 = rln_proof2.getValues();
const recoveredSecret = rlnWasm.WasmRLNProofValues.recoverIdSecret(
proofValues1,
proofValues2
);
let recoveredSecret;
try {
recoveredSecret = rlnWasm.WasmRLNProofValues.recoverIdSecret(
proofValues1,
proofValues2
);
} catch (error) {
console.error("Identity recovery error:", error);
return;
}
console.log(" - recovered_secret = " + recoveredSecret.debug());
console.log(" - original_secret = " + identitySecret.debug());
console.log("Slashing successful: Identity is recovered!");

View File

@@ -1,7 +1,7 @@
#![cfg(target_arch = "wasm32")]
mod wasm_rln;
mod wasm_utils;
pub mod wasm_rln;
pub mod wasm_utils;
#[cfg(all(feature = "parallel", not(feature = "utils")))]
pub use wasm_bindgen_rayon::init_thread_pool;

View File

@@ -29,10 +29,18 @@ impl WasmRLN {
let calculated_witness_bigint: Vec<BigInt> = calculated_witness
.iter()
.map(|js_bigint| {
let str_val = js_bigint.to_string(10).unwrap().as_string().unwrap();
str_val.parse::<BigInt>().unwrap()
js_bigint
.to_string(10)
.ok()
.and_then(|js_str| js_str.as_string())
.ok_or_else(|| "Failed to convert JsBigInt to string".to_string())
.and_then(|str_val| {
str_val
.parse::<BigInt>()
.map_err(|err| format!("Failed to parse BigInt: {}", err))
})
})
.collect();
.collect::<Result<Vec<_>, _>>()?;
let (proof, proof_values) = self
.0
@@ -76,26 +84,28 @@ impl WasmRLNProof {
}
#[wasm_bindgen(js_name = toBytesLE)]
pub fn to_bytes_le(&self) -> Uint8Array {
Uint8Array::from(&rln_proof_to_bytes_le(&self.0)[..])
pub fn to_bytes_le(&self) -> Result<Uint8Array, String> {
let bytes = rln_proof_to_bytes_le(&self.0).map_err(|err| err.to_string())?;
Ok(Uint8Array::from(&bytes[..]))
}
#[wasm_bindgen(js_name = toBytesBE)]
pub fn to_bytes_be(&self) -> Uint8Array {
Uint8Array::from(&rln_proof_to_bytes_be(&self.0)[..])
pub fn to_bytes_be(&self) -> Result<Uint8Array, String> {
let bytes = rln_proof_to_bytes_be(&self.0).map_err(|err| err.to_string())?;
Ok(Uint8Array::from(&bytes[..]))
}
#[wasm_bindgen(js_name = fromBytesLE)]
pub fn from_bytes_le(bytes: &Uint8Array) -> Result<WasmRLNProof, String> {
let bytes_vec = bytes.to_vec();
let (proof, _) = bytes_le_to_rln_proof(&bytes_vec).map_err(|e| e.to_string())?;
let (proof, _) = bytes_le_to_rln_proof(&bytes_vec).map_err(|err| err.to_string())?;
Ok(WasmRLNProof(proof))
}
#[wasm_bindgen(js_name = fromBytesBE)]
pub fn from_bytes_be(bytes: &Uint8Array) -> Result<WasmRLNProof, String> {
let bytes_vec = bytes.to_vec();
let (proof, _) = bytes_be_to_rln_proof(&bytes_vec).map_err(|e| e.to_string())?;
let (proof, _) = bytes_be_to_rln_proof(&bytes_vec).map_err(|err| err.to_string())?;
Ok(WasmRLNProof(proof))
}
}
@@ -144,7 +154,7 @@ impl WasmRLNProofValues {
pub fn from_bytes_le(bytes: &Uint8Array) -> Result<WasmRLNProofValues, String> {
let bytes_vec = bytes.to_vec();
let (proof_values, _) =
bytes_le_to_rln_proof_values(&bytes_vec).map_err(|e| e.to_string())?;
bytes_le_to_rln_proof_values(&bytes_vec).map_err(|err| err.to_string())?;
Ok(WasmRLNProofValues(proof_values))
}
@@ -152,7 +162,7 @@ impl WasmRLNProofValues {
pub fn from_bytes_be(bytes: &Uint8Array) -> Result<WasmRLNProofValues, String> {
let bytes_vec = bytes.to_vec();
let (proof_values, _) =
bytes_be_to_rln_proof_values(&bytes_vec).map_err(|e| e.to_string())?;
bytes_be_to_rln_proof_values(&bytes_vec).map_err(|err| err.to_string())?;
Ok(WasmRLNProofValues(proof_values))
}

View File

@@ -45,14 +45,14 @@ impl WasmFr {
#[wasm_bindgen(js_name = fromBytesLE)]
pub fn from_bytes_le(bytes: &Uint8Array) -> Result<Self, String> {
let bytes_vec = bytes.to_vec();
let (fr, _) = bytes_le_to_fr(&bytes_vec).map_err(|e| e.to_string())?;
let (fr, _) = bytes_le_to_fr(&bytes_vec).map_err(|err| err.to_string())?;
Ok(Self(fr))
}
#[wasm_bindgen(js_name = fromBytesBE)]
pub fn from_bytes_be(bytes: &Uint8Array) -> Result<Self, String> {
let bytes_vec = bytes.to_vec();
let (fr, _) = bytes_be_to_fr(&bytes_vec).map_err(|e| e.to_string())?;
let (fr, _) = bytes_be_to_fr(&bytes_vec).map_err(|err| err.to_string())?;
Ok(Self(fr))
}
@@ -194,18 +194,24 @@ pub struct Hasher;
#[wasm_bindgen]
impl Hasher {
#[wasm_bindgen(js_name = hashToFieldLE)]
pub fn hash_to_field_le(input: &Uint8Array) -> WasmFr {
WasmFr(hash_to_field_le(&input.to_vec()))
pub fn hash_to_field_le(input: &Uint8Array) -> Result<WasmFr, String> {
hash_to_field_le(&input.to_vec())
.map(WasmFr)
.map_err(|err| err.to_string())
}
#[wasm_bindgen(js_name = hashToFieldBE)]
pub fn hash_to_field_be(input: &Uint8Array) -> WasmFr {
WasmFr(hash_to_field_be(&input.to_vec()))
pub fn hash_to_field_be(input: &Uint8Array) -> Result<WasmFr, String> {
hash_to_field_be(&input.to_vec())
.map(WasmFr)
.map_err(|err| err.to_string())
}
#[wasm_bindgen(js_name = poseidonHashPair)]
pub fn poseidon_hash_pair(a: &WasmFr, b: &WasmFr) -> WasmFr {
WasmFr(poseidon_hash(&[a.0, b.0]))
pub fn poseidon_hash_pair(a: &WasmFr, b: &WasmFr) -> Result<WasmFr, String> {
poseidon_hash(&[a.0, b.0])
.map(WasmFr)
.map_err(|err| err.to_string())
}
}
@@ -218,22 +224,23 @@ pub struct Identity {
#[wasm_bindgen]
impl Identity {
#[wasm_bindgen(js_name = generate)]
pub fn generate() -> Identity {
let (identity_secret, id_commitment) = keygen();
Identity {
pub fn generate() -> Result<Identity, String> {
let (identity_secret, id_commitment) = keygen().map_err(|err| err.to_string())?;
Ok(Identity {
identity_secret: *identity_secret,
id_commitment,
}
})
}
#[wasm_bindgen(js_name = generateSeeded)]
pub fn generate_seeded(seed: &Uint8Array) -> Identity {
pub fn generate_seeded(seed: &Uint8Array) -> Result<Identity, String> {
let seed_vec = seed.to_vec();
let (identity_secret, id_commitment) = seeded_keygen(&seed_vec);
Identity {
let (identity_secret, id_commitment) =
seeded_keygen(&seed_vec).map_err(|err| err.to_string())?;
Ok(Identity {
identity_secret,
id_commitment,
}
})
}
#[wasm_bindgen(js_name = getSecretHash)]
@@ -250,6 +257,46 @@ impl Identity {
pub fn to_array(&self) -> VecWasmFr {
VecWasmFr(vec![self.identity_secret, self.id_commitment])
}
#[wasm_bindgen(js_name = toBytesLE)]
pub fn to_bytes_le(&self) -> Uint8Array {
let vec_fr = vec![self.identity_secret, self.id_commitment];
let bytes = vec_fr_to_bytes_le(&vec_fr);
Uint8Array::from(&bytes[..])
}
#[wasm_bindgen(js_name = toBytesBE)]
pub fn to_bytes_be(&self) -> Uint8Array {
let vec_fr = vec![self.identity_secret, self.id_commitment];
let bytes = vec_fr_to_bytes_be(&vec_fr);
Uint8Array::from(&bytes[..])
}
#[wasm_bindgen(js_name = fromBytesLE)]
pub fn from_bytes_le(bytes: &Uint8Array) -> Result<Identity, String> {
let bytes_vec = bytes.to_vec();
let (vec_fr, _) = bytes_le_to_vec_fr(&bytes_vec).map_err(|err| err.to_string())?;
if vec_fr.len() != 2 {
return Err(format!("Expected 2 elements, got {}", vec_fr.len()));
}
Ok(Identity {
identity_secret: vec_fr[0],
id_commitment: vec_fr[1],
})
}
#[wasm_bindgen(js_name = fromBytesBE)]
pub fn from_bytes_be(bytes: &Uint8Array) -> Result<Identity, String> {
let bytes_vec = bytes.to_vec();
let (vec_fr, _) = bytes_be_to_vec_fr(&bytes_vec).map_err(|err| err.to_string())?;
if vec_fr.len() != 2 {
return Err(format!("Expected 2 elements, got {}", vec_fr.len()));
}
Ok(Identity {
identity_secret: vec_fr[0],
id_commitment: vec_fr[1],
})
}
}
#[wasm_bindgen]
@@ -263,28 +310,28 @@ pub struct ExtendedIdentity {
#[wasm_bindgen]
impl ExtendedIdentity {
#[wasm_bindgen(js_name = generate)]
pub fn generate() -> ExtendedIdentity {
pub fn generate() -> Result<ExtendedIdentity, String> {
let (identity_trapdoor, identity_nullifier, identity_secret, id_commitment) =
extended_keygen();
ExtendedIdentity {
extended_keygen().map_err(|err| err.to_string())?;
Ok(ExtendedIdentity {
identity_trapdoor,
identity_nullifier,
identity_secret,
id_commitment,
}
})
}
#[wasm_bindgen(js_name = generateSeeded)]
pub fn generate_seeded(seed: &Uint8Array) -> ExtendedIdentity {
pub fn generate_seeded(seed: &Uint8Array) -> Result<ExtendedIdentity, String> {
let seed_vec = seed.to_vec();
let (identity_trapdoor, identity_nullifier, identity_secret, id_commitment) =
extended_seeded_keygen(&seed_vec);
ExtendedIdentity {
extended_seeded_keygen(&seed_vec).map_err(|err| err.to_string())?;
Ok(ExtendedIdentity {
identity_trapdoor,
identity_nullifier,
identity_secret,
id_commitment,
}
})
}
#[wasm_bindgen(js_name = getTrapdoor)]
@@ -316,4 +363,58 @@ impl ExtendedIdentity {
self.id_commitment,
])
}
#[wasm_bindgen(js_name = toBytesLE)]
pub fn to_bytes_le(&self) -> Uint8Array {
let vec_fr = vec![
self.identity_trapdoor,
self.identity_nullifier,
self.identity_secret,
self.id_commitment,
];
let bytes = vec_fr_to_bytes_le(&vec_fr);
Uint8Array::from(&bytes[..])
}
#[wasm_bindgen(js_name = toBytesBE)]
pub fn to_bytes_be(&self) -> Uint8Array {
let vec_fr = vec![
self.identity_trapdoor,
self.identity_nullifier,
self.identity_secret,
self.id_commitment,
];
let bytes = vec_fr_to_bytes_be(&vec_fr);
Uint8Array::from(&bytes[..])
}
#[wasm_bindgen(js_name = fromBytesLE)]
pub fn from_bytes_le(bytes: &Uint8Array) -> Result<ExtendedIdentity, String> {
let bytes_vec = bytes.to_vec();
let (vec_fr, _) = bytes_le_to_vec_fr(&bytes_vec).map_err(|err| err.to_string())?;
if vec_fr.len() != 4 {
return Err(format!("Expected 4 elements, got {}", vec_fr.len()));
}
Ok(ExtendedIdentity {
identity_trapdoor: vec_fr[0],
identity_nullifier: vec_fr[1],
identity_secret: vec_fr[2],
id_commitment: vec_fr[3],
})
}
#[wasm_bindgen(js_name = fromBytesBE)]
pub fn from_bytes_be(bytes: &Uint8Array) -> Result<ExtendedIdentity, String> {
let bytes_vec = bytes.to_vec();
let (vec_fr, _) = bytes_be_to_vec_fr(&bytes_vec).map_err(|err| err.to_string())?;
if vec_fr.len() != 4 {
return Err(format!("Expected 4 elements, got {}", vec_fr.len()));
}
Ok(ExtendedIdentity {
identity_trapdoor: vec_fr[0],
identity_nullifier: vec_fr[1],
identity_secret: vec_fr[2],
id_commitment: vec_fr[3],
})
}
}

View File

@@ -10,7 +10,7 @@ mod test {
};
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
use wasm_bindgen_test::{console_log, wasm_bindgen_test, wasm_bindgen_test_configure};
use zerokit_utils::{
use zerokit_utils::merkle_tree::{
OptimalMerkleProof, OptimalMerkleTree, ZerokitMerkleProof, ZerokitMerkleTree,
};
#[cfg(feature = "parallel")]
@@ -80,72 +80,64 @@ mod test {
pub async fn rln_wasm_benchmark() {
// Check if thread pool is supported
#[cfg(feature = "parallel")]
if !isThreadpoolSupported().expect("Failed to check thread pool support") {
if !isThreadpoolSupported().unwrap() {
panic!("Thread pool is NOT supported");
} else {
// Initialize thread pool
let cpu_count = window()
.expect("Failed to get window")
.navigator()
.hardware_concurrency() as usize;
JsFuture::from(init_thread_pool(cpu_count))
.await
.expect("Failed to initialize thread pool");
let cpu_count = window().unwrap().navigator().hardware_concurrency() as usize;
JsFuture::from(init_thread_pool(cpu_count)).await.unwrap();
}
// Initialize witness calculator
initWitnessCalculator(WITNESS_CALCULATOR_JS)
.expect("Failed to initialize witness calculator");
initWitnessCalculator(WITNESS_CALCULATOR_JS).unwrap();
let mut results = String::from("\nbenchmarks:\n");
let iterations = 10;
let zkey = readFile(ARKZKEY_BYTES).expect("Failed to read zkey file");
let zkey = readFile(ARKZKEY_BYTES).unwrap();
// Benchmark RLN instance creation
let start_rln_new = Date::now();
for _ in 0..iterations {
let _ = WasmRLN::new(&zkey).expect("Failed to create RLN instance");
let _ = WasmRLN::new(&zkey).unwrap();
}
let rln_new_result = Date::now() - start_rln_new;
// Create RLN instance for other benchmarks
let rln_instance = WasmRLN::new(&zkey).expect("Failed to create RLN instance");
let rln_instance = WasmRLN::new(&zkey).unwrap();
let mut tree: OptimalMerkleTree<PoseidonHash> =
OptimalMerkleTree::default(DEFAULT_TREE_DEPTH).expect("Failed to create tree");
OptimalMerkleTree::default(DEFAULT_TREE_DEPTH).unwrap();
// Benchmark generate identity
let start_identity_gen = Date::now();
for _ in 0..iterations {
let _ = Identity::generate();
let _ = Identity::generate().unwrap();
}
let identity_gen_result = Date::now() - start_identity_gen;
// Generate identity for other benchmarks
let identity_pair = Identity::generate();
let identity_pair = Identity::generate().unwrap();
let identity_secret = identity_pair.get_secret_hash();
let id_commitment = identity_pair.get_commitment();
let epoch = Hasher::hash_to_field_le(&Uint8Array::from(b"test-epoch" as &[u8]));
let epoch = Hasher::hash_to_field_le(&Uint8Array::from(b"test-epoch" as &[u8])).unwrap();
let rln_identifier =
Hasher::hash_to_field_le(&Uint8Array::from(b"test-rln-identifier" as &[u8]));
let external_nullifier = Hasher::poseidon_hash_pair(&epoch, &rln_identifier);
Hasher::hash_to_field_le(&Uint8Array::from(b"test-rln-identifier" as &[u8])).unwrap();
let external_nullifier = Hasher::poseidon_hash_pair(&epoch, &rln_identifier).unwrap();
let identity_index = tree.leaves_set();
let user_message_limit = WasmFr::from_uint(100);
let rate_commitment = Hasher::poseidon_hash_pair(&id_commitment, &user_message_limit);
tree.update_next(*rate_commitment)
.expect("Failed to update tree");
let rate_commitment =
Hasher::poseidon_hash_pair(&id_commitment, &user_message_limit).unwrap();
tree.update_next(*rate_commitment).unwrap();
let message_id = WasmFr::from_uint(0);
let signal: [u8; 32] = [0; 32];
let x = Hasher::hash_to_field_le(&Uint8Array::from(&signal[..]));
let x = Hasher::hash_to_field_le(&Uint8Array::from(&signal[..])).unwrap();
let merkle_proof: OptimalMerkleProof<PoseidonHash> = tree
.proof(identity_index)
.expect("Failed to generate merkle proof");
let merkle_proof: OptimalMerkleProof<PoseidonHash> = tree.proof(identity_index).unwrap();
let mut path_elements = VecWasmFr::new();
for path_element in merkle_proof.get_path_elements() {
@@ -162,32 +154,30 @@ mod test {
&x,
&external_nullifier,
)
.expect("Failed to create WasmRLNWitnessInput");
.unwrap();
let bigint_json = witness
.to_bigint_json()
.expect("Failed to convert witness to BigInt JSON");
let bigint_json = witness.to_bigint_json().unwrap();
// Benchmark witness calculation
let start_calculate_witness = Date::now();
for _ in 0..iterations {
let _ = calculateWitness(CIRCOM_BYTES, bigint_json.clone())
.await
.expect("Failed to calculate witness");
.unwrap();
}
let calculate_witness_result = Date::now() - start_calculate_witness;
// Calculate witness for other benchmarks
let calculated_witness_str = calculateWitness(CIRCOM_BYTES, bigint_json.clone())
.await
.expect("Failed to calculate witness")
.unwrap()
.as_string()
.expect("Failed to convert calculated witness to string");
.unwrap();
let calculated_witness_vec_str: Vec<String> =
serde_json::from_str(&calculated_witness_str).expect("Failed to parse JSON");
serde_json::from_str(&calculated_witness_str).unwrap();
let calculated_witness: Vec<JsBigInt> = calculated_witness_vec_str
.iter()
.map(|x| JsBigInt::new(&x.into()).expect("Failed to create JsBigInt"))
.map(|x| JsBigInt::new(&x.into()).unwrap())
.collect();
// Benchmark proof generation with witness
@@ -195,7 +185,7 @@ mod test {
for _ in 0..iterations {
let _ = rln_instance
.generate_rln_proof_with_witness(calculated_witness.clone(), &witness)
.expect("Failed to generate proof");
.unwrap();
}
let generate_rln_proof_with_witness_result =
Date::now() - start_generate_rln_proof_with_witness;
@@ -203,7 +193,7 @@ mod test {
// Generate proof with witness for other benchmarks
let proof: WasmRLNProof = rln_instance
.generate_rln_proof_with_witness(calculated_witness, &witness)
.expect("Failed to generate proof");
.unwrap();
let root = WasmFr::from(tree.root());
let mut roots = VecWasmFr::new();
@@ -212,16 +202,12 @@ mod test {
// Benchmark proof verification with the root
let start_verify_with_roots = Date::now();
for _ in 0..iterations {
let _ = rln_instance
.verify_with_roots(&proof, &roots, &x)
.expect("Failed to verify proof");
let _ = rln_instance.verify_with_roots(&proof, &roots, &x).unwrap();
}
let verify_with_roots_result = Date::now() - start_verify_with_roots;
// Verify proof with the root for other benchmarks
let is_proof_valid = rln_instance
.verify_with_roots(&proof, &roots, &x)
.expect("Failed to verify proof");
let is_proof_valid = rln_instance.verify_with_roots(&proof, &roots, &x).unwrap();
assert!(is_proof_valid, "verification failed");
// Format and display the benchmark results

View File

@@ -10,7 +10,7 @@ mod test {
};
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
use wasm_bindgen_test::{console_log, wasm_bindgen_test};
use zerokit_utils::{
use zerokit_utils::merkle_tree::{
OptimalMerkleProof, OptimalMerkleTree, ZerokitMerkleProof, ZerokitMerkleTree,
};
@@ -40,7 +40,7 @@ mod test {
calculateWitness: async function (circom_path, inputs) {
const wasmFile = fs.readFileSync(circom_path);
const wasmFileBuffer = wasmFile.slice(
const wasmFileBuffer = wasmFile.buffer.slice(
wasmFile.byteOffset,
wasmFile.byteOffset + wasmFile.byteLength
);
@@ -75,58 +75,55 @@ mod test {
#[wasm_bindgen_test]
pub async fn rln_wasm_benchmark() {
// Initialize witness calculator
initWitnessCalculator(WITNESS_CALCULATOR_JS)
.expect("Failed to initialize witness calculator");
initWitnessCalculator(WITNESS_CALCULATOR_JS).unwrap();
let mut results = String::from("\nbenchmarks:\n");
let iterations = 10;
let zkey = readFile(ARKZKEY_PATH).expect("Failed to read zkey file");
let zkey = readFile(ARKZKEY_PATH).unwrap();
// Benchmark RLN instance creation
let start_rln_new = Date::now();
for _ in 0..iterations {
let _ = WasmRLN::new(&zkey).expect("Failed to create RLN instance");
let _ = WasmRLN::new(&zkey).unwrap();
}
let rln_new_result = Date::now() - start_rln_new;
// Create RLN instance for other benchmarks
let rln_instance = WasmRLN::new(&zkey).expect("Failed to create RLN instance");
let rln_instance = WasmRLN::new(&zkey).unwrap();
let mut tree: OptimalMerkleTree<PoseidonHash> =
OptimalMerkleTree::default(DEFAULT_TREE_DEPTH).expect("Failed to create tree");
OptimalMerkleTree::default(DEFAULT_TREE_DEPTH).unwrap();
// Benchmark generate identity
let start_identity_gen = Date::now();
for _ in 0..iterations {
let _ = Identity::generate();
let _ = Identity::generate().unwrap();
}
let identity_gen_result = Date::now() - start_identity_gen;
// Generate identity for other benchmarks
let identity_pair = Identity::generate();
let identity_pair = Identity::generate().unwrap();
let identity_secret = identity_pair.get_secret_hash();
let id_commitment = identity_pair.get_commitment();
let epoch = Hasher::hash_to_field_le(&Uint8Array::from(b"test-epoch" as &[u8]));
let epoch = Hasher::hash_to_field_le(&Uint8Array::from(b"test-epoch" as &[u8])).unwrap();
let rln_identifier =
Hasher::hash_to_field_le(&Uint8Array::from(b"test-rln-identifier" as &[u8]));
let external_nullifier = Hasher::poseidon_hash_pair(&epoch, &rln_identifier);
Hasher::hash_to_field_le(&Uint8Array::from(b"test-rln-identifier" as &[u8])).unwrap();
let external_nullifier = Hasher::poseidon_hash_pair(&epoch, &rln_identifier).unwrap();
let identity_index = tree.leaves_set();
let user_message_limit = WasmFr::from_uint(100);
let rate_commitment = Hasher::poseidon_hash_pair(&id_commitment, &user_message_limit);
tree.update_next(*rate_commitment)
.expect("Failed to update tree");
let rate_commitment =
Hasher::poseidon_hash_pair(&id_commitment, &user_message_limit).unwrap();
tree.update_next(*rate_commitment).unwrap();
let message_id = WasmFr::from_uint(0);
let signal: [u8; 32] = [0; 32];
let x = Hasher::hash_to_field_le(&Uint8Array::from(&signal[..]));
let x = Hasher::hash_to_field_le(&Uint8Array::from(&signal[..])).unwrap();
let merkle_proof: OptimalMerkleProof<PoseidonHash> = tree
.proof(identity_index)
.expect("Failed to generate merkle proof");
let merkle_proof: OptimalMerkleProof<PoseidonHash> = tree.proof(identity_index).unwrap();
let mut path_elements = VecWasmFr::new();
for path_element in merkle_proof.get_path_elements() {
@@ -143,32 +140,30 @@ mod test {
&x,
&external_nullifier,
)
.expect("Failed to create WasmRLNWitnessInput");
.unwrap();
let bigint_json = witness
.to_bigint_json()
.expect("Failed to convert witness to BigInt JSON");
let bigint_json = witness.to_bigint_json().unwrap();
// Benchmark witness calculation
let start_calculate_witness = Date::now();
for _ in 0..iterations {
let _ = calculateWitness(CIRCOM_PATH, bigint_json.clone())
.await
.expect("Failed to calculate witness");
.unwrap();
}
let calculate_witness_result = Date::now() - start_calculate_witness;
// Calculate witness for other benchmarks
let calculated_witness_str = calculateWitness(CIRCOM_PATH, bigint_json.clone())
.await
.expect("Failed to calculate witness")
.unwrap()
.as_string()
.expect("Failed to convert calculated witness to string");
.unwrap();
let calculated_witness_vec_str: Vec<String> =
serde_json::from_str(&calculated_witness_str).expect("Failed to parse JSON");
serde_json::from_str(&calculated_witness_str).unwrap();
let calculated_witness: Vec<JsBigInt> = calculated_witness_vec_str
.iter()
.map(|x| JsBigInt::new(&x.into()).expect("Failed to create JsBigInt"))
.map(|x| JsBigInt::new(&x.into()).unwrap())
.collect();
// Benchmark proof generation with witness
@@ -176,7 +171,7 @@ mod test {
for _ in 0..iterations {
let _ = rln_instance
.generate_rln_proof_with_witness(calculated_witness.clone(), &witness)
.expect("Failed to generate proof");
.unwrap();
}
let generate_rln_proof_with_witness_result =
Date::now() - start_generate_rln_proof_with_witness;
@@ -184,7 +179,7 @@ mod test {
// Generate proof with witness for other benchmarks
let proof: WasmRLNProof = rln_instance
.generate_rln_proof_with_witness(calculated_witness, &witness)
.expect("Failed to generate proof");
.unwrap();
let root = WasmFr::from(tree.root());
let mut roots = VecWasmFr::new();
@@ -193,16 +188,12 @@ mod test {
// Benchmark proof verification with the root
let start_verify_with_roots = Date::now();
for _ in 0..iterations {
let _ = rln_instance
.verify_with_roots(&proof, &roots, &x)
.expect("Failed to verify proof");
let _ = rln_instance.verify_with_roots(&proof, &roots, &x).unwrap();
}
let verify_with_roots_result = Date::now() - start_verify_with_roots;
// Verify proof with the root for other benchmarks
let is_proof_valid = rln_instance
.verify_with_roots(&proof, &roots, &x)
.expect("Failed to verify proof");
let is_proof_valid = rln_instance.verify_with_roots(&proof, &roots, &x).unwrap();
assert!(is_proof_valid, "verification failed");
// Format and display the benchmark results

View File

@@ -13,7 +13,7 @@ mod test {
#[wasm_bindgen_test]
fn test_keygen_wasm() {
let identity = Identity::generate();
let identity = Identity::generate().unwrap();
let identity_secret = *identity.get_secret_hash();
let id_commitment = *identity.get_commitment();
@@ -28,7 +28,7 @@ mod test {
#[wasm_bindgen_test]
fn test_extended_keygen_wasm() {
let identity = ExtendedIdentity::generate();
let identity = ExtendedIdentity::generate().unwrap();
let identity_trapdoor = *identity.get_trapdoor();
let identity_nullifier = *identity.get_nullifier();
@@ -53,7 +53,7 @@ mod test {
let seed_bytes: Vec<u8> = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let seed = Uint8Array::from(&seed_bytes[..]);
let identity = Identity::generate_seeded(&seed);
let identity = Identity::generate_seeded(&seed).unwrap();
let identity_secret = *identity.get_secret_hash();
let id_commitment = *identity.get_commitment();
@@ -77,7 +77,7 @@ mod test {
let seed_bytes: Vec<u8> = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let seed = Uint8Array::from(&seed_bytes[..]);
let identity = ExtendedIdentity::generate_seeded(&seed);
let identity = ExtendedIdentity::generate_seeded(&seed).unwrap();
let identity_trapdoor = *identity.get_trapdoor();
let identity_nullifier = *identity.get_nullifier();
@@ -128,7 +128,7 @@ mod test {
let wasmfr_debug_str = wasmfr_int.debug();
assert_eq!(wasmfr_debug_str.to_string(), "42");
let identity = Identity::generate();
let identity = Identity::generate().unwrap();
let mut id_secret_fr = *identity.get_secret_hash();
let id_secret_hash = IdSecret::from(&mut id_secret_fr);
let id_commitment = *identity.get_commitment();
@@ -184,12 +184,12 @@ mod test {
let signal_gen: [u8; 32] = rng.gen();
let signal = Uint8Array::from(&signal_gen[..]);
let wasmfr_le_1 = Hasher::hash_to_field_le(&signal);
let fr_le_2 = rln::hashers::hash_to_field_le(&signal_gen);
let wasmfr_le_1 = Hasher::hash_to_field_le(&signal).unwrap();
let fr_le_2 = hash_to_field_le(&signal_gen).unwrap();
assert_eq!(*wasmfr_le_1, fr_le_2);
let wasmfr_be_1 = Hasher::hash_to_field_be(&signal);
let fr_be_2 = rln::hashers::hash_to_field_be(&signal_gen);
let wasmfr_be_1 = Hasher::hash_to_field_be(&signal).unwrap();
let fr_be_2 = hash_to_field_be(&signal_gen).unwrap();
assert_eq!(*wasmfr_be_1, fr_be_2);
assert_eq!(*wasmfr_le_1, *wasmfr_be_1);
@@ -212,10 +212,10 @@ mod test {
let input_1 = Fr::from(42u8);
let input_2 = Fr::from(99u8);
let expected_hash = poseidon_hash(&[input_1, input_2]);
let expected_hash = poseidon_hash(&[input_1, input_2]).unwrap();
let wasmfr_1 = WasmFr::from_uint(42);
let wasmfr_2 = WasmFr::from_uint(99);
let received_hash = Hasher::poseidon_hash_pair(&wasmfr_1, &wasmfr_2);
let received_hash = Hasher::poseidon_hash_pair(&wasmfr_1, &wasmfr_2).unwrap();
assert_eq!(*received_hash, expected_hash);
}