Skip to content

Commit be0d246

Browse files
authored
test: RLN-WASM bindings error cases (#381)
## Description A batch of tests to cover negative/invalid input cases for RLN-WASM bindings. ## Tests added rln-wasm/tests/node.rs: - test_wasm_invalid_inputs rln-wasm/tests/utils.rs: - test_wasmfr_from_bytes_invalid - test_vec_wasmfr_from_bytes_invalid - test_uint8array_utils_from_bytes_invalid - test_identity_from_bytes_invalid_len - test_extended_identity_from_bytes_invalid_len ## Other changes - Add `cargo make fmt` and `cargo make fmt_check` to cover excluded modules. ## Checklist - [x] My PR title follows [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format - [x] I have linked the related issue(s) - [x] `cargo fmt --all -- --check` produces no changes - [x] Clippy passes for all affected crate/feature combinations (see [Linting](#linting-mirrors-ci) above) - [x] `make test` passes locally - [x] No new `unwrap()` / `expect()` / `panic!()` in library code - [x] New code includes appropriate tests (unit / integration / WASM where applicable) - [x] I have run the CI coverage report — add the `run-coverage` label to enable it - [x] All CI checks pass and the PR is marked **Ready for review**
1 parent cb6a1ba commit be0d246

File tree

3 files changed

+263
-2
lines changed

3 files changed

+263
-2
lines changed

Makefile.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,18 @@
11
[env]
22
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
3+
4+
[tasks.fmt]
5+
description = "Format workspace members and excluded crates"
6+
command = "sh"
7+
args = [
8+
"-c",
9+
"cd .. && cargo +nightly fmt --all && cargo +nightly fmt --manifest-path rln-cli/Cargo.toml --all && cargo +nightly fmt --manifest-path rln-wasm/Cargo.toml --all",
10+
]
11+
12+
[tasks.fmt_check]
13+
description = "Check formatting for workspace members and excluded crates"
14+
command = "sh"
15+
args = [
16+
"-c",
17+
"cd .. && cargo +nightly fmt --all -- --check && cargo +nightly fmt --manifest-path rln-cli/Cargo.toml --all -- --check && cargo +nightly fmt --manifest-path rln-wasm/Cargo.toml --all -- --check",
18+
]

rln-wasm/tests/node.rs

Lines changed: 154 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ mod test {
66
use js_sys::{BigInt as JsBigInt, Date, Object, Uint8Array};
77
use rln::prelude::*;
88
use rln_wasm::{
9-
Hasher, Identity, VecWasmFr, WasmFr, WasmRLN, WasmRLNProof, WasmRLNWitnessInput,
9+
Hasher, Identity, VecWasmFr, WasmFr, WasmRLN, WasmRLNProof, WasmRLNProofValues,
10+
WasmRLNWitnessInput,
1011
};
1112
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
1213
use wasm_bindgen_test::{console_log, wasm_bindgen_test};
@@ -72,6 +73,55 @@ mod test {
7273

7374
const CIRCOM_PATH: &str = "../rln/resources/tree_depth_20/rln.wasm";
7475

76+
fn build_witness_parts() -> (
77+
WasmFr,
78+
WasmFr,
79+
WasmFr,
80+
VecWasmFr,
81+
Uint8Array,
82+
WasmFr,
83+
WasmFr,
84+
) {
85+
let mut tree: OptimalMerkleTree<PoseidonHash> =
86+
OptimalMerkleTree::default(DEFAULT_TREE_DEPTH).unwrap();
87+
88+
let identity_pair = Identity::generate().unwrap();
89+
let identity_secret = identity_pair.get_secret_hash();
90+
let id_commitment = identity_pair.get_commitment();
91+
92+
let epoch = Hasher::hash_to_field_le(&Uint8Array::from(b"test-epoch" as &[u8])).unwrap();
93+
let rln_identifier =
94+
Hasher::hash_to_field_le(&Uint8Array::from(b"test-rln-identifier" as &[u8])).unwrap();
95+
let external_nullifier = Hasher::poseidon_hash_pair(&epoch, &rln_identifier).unwrap();
96+
97+
let identity_index = tree.leaves_set();
98+
let user_message_limit = WasmFr::from_uint(10);
99+
let rate_commitment =
100+
Hasher::poseidon_hash_pair(&id_commitment, &user_message_limit).unwrap();
101+
tree.update_next(*rate_commitment).unwrap();
102+
103+
let message_id = WasmFr::from_uint(0);
104+
let signal: [u8; 32] = [0; 32];
105+
let x = Hasher::hash_to_field_le(&Uint8Array::from(&signal[..])).unwrap();
106+
107+
let merkle_proof: OptimalMerkleProof<PoseidonHash> = tree.proof(identity_index).unwrap();
108+
let mut path_elements = VecWasmFr::new();
109+
for path_element in merkle_proof.get_path_elements() {
110+
path_elements.push(&WasmFr::from(path_element));
111+
}
112+
let path_index = Uint8Array::from(&merkle_proof.get_path_index()[..]);
113+
114+
(
115+
identity_secret,
116+
user_message_limit,
117+
message_id,
118+
path_elements,
119+
path_index,
120+
x,
121+
external_nullifier,
122+
)
123+
}
124+
75125
#[wasm_bindgen_test]
76126
pub async fn rln_wasm_benchmark() {
77127
// Initialize witness calculator
@@ -230,4 +280,107 @@ mod test {
230280
// Log the results
231281
console_log!("{results}");
232282
}
283+
284+
#[wasm_bindgen_test]
285+
pub fn test_wasm_invalid_inputs() {
286+
// Invalid zkey data
287+
let invalid_zkey = Uint8Array::from(&[0u8; 16][..]);
288+
assert!(WasmRLN::new(&invalid_zkey).is_err());
289+
290+
let (
291+
identity_secret,
292+
user_message_limit,
293+
message_id,
294+
path_elements,
295+
path_index,
296+
x,
297+
external_nullifier,
298+
) = build_witness_parts();
299+
300+
// Invalid user message limit (zero)
301+
let zero_limit = WasmFr::zero();
302+
let result = WasmRLNWitnessInput::new(
303+
&identity_secret,
304+
&zero_limit,
305+
&message_id,
306+
&path_elements,
307+
&path_index,
308+
&x,
309+
&external_nullifier,
310+
);
311+
assert!(result.is_err());
312+
313+
// Invalid message id (>= limit)
314+
let invalid_message_id = user_message_limit;
315+
let result = WasmRLNWitnessInput::new(
316+
&identity_secret,
317+
&user_message_limit,
318+
&invalid_message_id,
319+
&path_elements,
320+
&path_index,
321+
&x,
322+
&external_nullifier,
323+
);
324+
assert!(result.is_err());
325+
326+
// Invalid merkle proof length (path elements vs path index)
327+
let mut shorter_path_elements = VecWasmFr::new();
328+
for i in 0..path_elements.length().saturating_sub(1) {
329+
shorter_path_elements.push(&path_elements.get(i).unwrap());
330+
}
331+
let result = WasmRLNWitnessInput::new(
332+
&identity_secret,
333+
&user_message_limit,
334+
&message_id,
335+
&shorter_path_elements,
336+
&path_index,
337+
&x,
338+
&external_nullifier,
339+
);
340+
assert!(result.is_err());
341+
342+
// Witness bytes: truncated and extra data
343+
let valid_witness = WasmRLNWitnessInput::new(
344+
&identity_secret,
345+
&user_message_limit,
346+
&message_id,
347+
&path_elements,
348+
&path_index,
349+
&x,
350+
&external_nullifier,
351+
)
352+
.unwrap();
353+
354+
let witness_le = valid_witness.to_bytes_le().unwrap();
355+
let witness_le_vec = witness_le.to_vec();
356+
let truncated_le = Uint8Array::from(&witness_le_vec[..witness_le_vec.len() - 1]);
357+
assert!(WasmRLNWitnessInput::from_bytes_le(&truncated_le).is_err());
358+
359+
let mut extra_le_vec = witness_le_vec.clone();
360+
extra_le_vec.push(0);
361+
let extra_le = Uint8Array::from(&extra_le_vec[..]);
362+
assert!(WasmRLNWitnessInput::from_bytes_le(&extra_le).is_err());
363+
364+
let witness_be = valid_witness.to_bytes_be().unwrap();
365+
let witness_be_vec = witness_be.to_vec();
366+
let truncated_be = Uint8Array::from(&witness_be_vec[..witness_be_vec.len() - 1]);
367+
assert!(WasmRLNWitnessInput::from_bytes_be(&truncated_be).is_err());
368+
369+
let mut extra_be_vec = witness_be_vec.clone();
370+
extra_be_vec.push(0);
371+
let extra_be = Uint8Array::from(&extra_be_vec[..]);
372+
assert!(WasmRLNWitnessInput::from_bytes_be(&extra_be).is_err());
373+
374+
// Proof values bytes: insufficient length
375+
let short_pv = [0u8; FR_BYTE_SIZE * 5 - 1];
376+
let short_pv = Uint8Array::from(&short_pv[..]);
377+
assert!(WasmRLNProofValues::from_bytes_le(&short_pv).is_err());
378+
assert!(WasmRLNProofValues::from_bytes_be(&short_pv).is_err());
379+
380+
// Proof bytes: insufficient length (no proof values)
381+
let short_proof = [0u8; COMPRESS_PROOF_SIZE];
382+
let short_proof = Uint8Array::from(&short_proof[..]);
383+
assert!(WasmRLNProof::from_bytes_le(&short_proof).is_err());
384+
assert!(WasmRLNProof::from_bytes_be(&short_proof).is_err());
385+
}
233386
}

rln-wasm/tests/utils.rs

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ mod test {
88
use js_sys::Uint8Array;
99
use rand::Rng;
1010
use rln::prelude::*;
11-
use rln_wasm::{ExtendedIdentity, Hasher, Identity, VecWasmFr, WasmFr};
11+
use rln_wasm::{
12+
wasm_utils::Uint8ArrayUtils, ExtendedIdentity, Hasher, Identity, VecWasmFr, WasmFr,
13+
};
1214
use wasm_bindgen_test::wasm_bindgen_test;
1315

1416
#[wasm_bindgen_test]
@@ -219,4 +221,94 @@ mod test {
219221

220222
assert_eq!(*received_hash, expected_hash);
221223
}
224+
225+
#[wasm_bindgen_test]
226+
fn test_wasmfr_from_bytes_invalid() {
227+
let short_bytes = [0u8; FR_BYTE_SIZE - 1];
228+
let short = Uint8Array::from(&short_bytes[..]);
229+
assert!(WasmFr::from_bytes_le(&short).is_err());
230+
assert!(WasmFr::from_bytes_be(&short).is_err());
231+
232+
let empty = Uint8Array::from(&[][..]);
233+
assert!(WasmFr::from_bytes_le(&empty).is_err());
234+
assert!(WasmFr::from_bytes_be(&empty).is_err());
235+
}
236+
237+
#[wasm_bindgen_test]
238+
fn test_vec_wasmfr_from_bytes_invalid() {
239+
let vec_fr = vec![Fr::from(1u8)];
240+
let bytes_le = vec_fr_to_bytes_le(&vec_fr);
241+
let truncated = Uint8Array::from(&bytes_le[..bytes_le.len() - 1]);
242+
assert!(VecWasmFr::from_bytes_le(&truncated).is_err());
243+
244+
let mut wrong_len = bytes_le.clone();
245+
wrong_len[..8].copy_from_slice(&normalize_usize_le(2));
246+
let wrong_len = Uint8Array::from(&wrong_len[..]);
247+
assert!(VecWasmFr::from_bytes_le(&wrong_len).is_err());
248+
249+
let max_safe_len = (usize::MAX - 8) / FR_BYTE_SIZE;
250+
let mut overflow_len = [0u8; 8];
251+
overflow_len[..8].copy_from_slice(&normalize_usize_le(max_safe_len));
252+
let overflow_len = Uint8Array::from(&overflow_len[..]);
253+
assert!(VecWasmFr::from_bytes_le(&overflow_len).is_err());
254+
255+
let bytes_be = vec_fr_to_bytes_be(&vec_fr);
256+
let truncated_be = Uint8Array::from(&bytes_be[..bytes_be.len() - 1]);
257+
assert!(VecWasmFr::from_bytes_be(&truncated_be).is_err());
258+
259+
let mut wrong_len_be = bytes_be.clone();
260+
wrong_len_be[..8].copy_from_slice(&normalize_usize_be(2));
261+
let wrong_len_be = Uint8Array::from(&wrong_len_be[..]);
262+
assert!(VecWasmFr::from_bytes_be(&wrong_len_be).is_err());
263+
264+
let mut overflow_len_be = [0u8; 8];
265+
overflow_len_be[..8].copy_from_slice(&normalize_usize_be(max_safe_len));
266+
let overflow_len_be = Uint8Array::from(&overflow_len_be[..]);
267+
assert!(VecWasmFr::from_bytes_be(&overflow_len_be).is_err());
268+
}
269+
270+
#[wasm_bindgen_test]
271+
fn test_uint8array_utils_from_bytes_invalid() {
272+
let short = Uint8Array::from(&[0u8; 7][..]);
273+
assert!(Uint8ArrayUtils::from_bytes_le(&short).is_err());
274+
assert!(Uint8ArrayUtils::from_bytes_be(&short).is_err());
275+
276+
let invalid_len_le = Vec::from(normalize_usize_le(5));
277+
let invalid_len_le = Uint8Array::from(&invalid_len_le[..]);
278+
assert!(Uint8ArrayUtils::from_bytes_le(&invalid_len_le).is_err());
279+
280+
let invalid_len_be = Vec::from(normalize_usize_be(5));
281+
let invalid_len_be = Uint8Array::from(&invalid_len_be[..]);
282+
assert!(Uint8ArrayUtils::from_bytes_be(&invalid_len_be).is_err());
283+
284+
let overflow_len_le = Vec::from(normalize_usize_le(usize::MAX - 8));
285+
let overflow_len_le = Uint8Array::from(&overflow_len_le[..]);
286+
assert!(Uint8ArrayUtils::from_bytes_le(&overflow_len_le).is_err());
287+
288+
let overflow_len_be = Vec::from(normalize_usize_be(usize::MAX - 8));
289+
let overflow_len_be = Uint8Array::from(&overflow_len_be[..]);
290+
assert!(Uint8ArrayUtils::from_bytes_be(&overflow_len_be).is_err());
291+
}
292+
293+
#[wasm_bindgen_test]
294+
fn test_identity_from_bytes_invalid_len() {
295+
let bytes_le = vec_fr_to_bytes_le(&[Fr::from(1u8)]);
296+
let bytes_le = Uint8Array::from(&bytes_le[..]);
297+
assert!(Identity::from_bytes_le(&bytes_le).is_err());
298+
299+
let bytes_be = vec_fr_to_bytes_be(&[Fr::from(1u8)]);
300+
let bytes_be = Uint8Array::from(&bytes_be[..]);
301+
assert!(Identity::from_bytes_be(&bytes_be).is_err());
302+
}
303+
304+
#[wasm_bindgen_test]
305+
fn test_extended_identity_from_bytes_invalid_len() {
306+
let bytes_le = vec_fr_to_bytes_le(&[Fr::from(1u8), Fr::from(2u8), Fr::from(3u8)]);
307+
let bytes_le = Uint8Array::from(&bytes_le[..]);
308+
assert!(ExtendedIdentity::from_bytes_le(&bytes_le).is_err());
309+
310+
let bytes_be = vec_fr_to_bytes_be(&[Fr::from(1u8), Fr::from(2u8), Fr::from(3u8)]);
311+
let bytes_be = Uint8Array::from(&bytes_be[..]);
312+
assert!(ExtendedIdentity::from_bytes_be(&bytes_be).is_err());
313+
}
222314
}

0 commit comments

Comments
 (0)