1use anyhow::Result;
6use ligerito::{FinalizedLigeritoProof, verify};
7use ligerito_binary_fields::{BinaryElem32, BinaryElem128};
8use crate::verifier_config_for_log_size;
9
10#[cfg(not(target_arch = "wasm32"))]
11use std::thread;
12
13fn deserialize_proof(bytes: &[u8]) -> Result<(FinalizedLigeritoProof<BinaryElem32, BinaryElem128>, u8)> {
16 if bytes.is_empty() {
17 anyhow::bail!("empty proof bytes");
18 }
19 let log_size = bytes[0];
20 let proof = bincode::deserialize(&bytes[1..])
21 .map_err(|e| anyhow::anyhow!("failed to deserialize proof: {}", e))?;
22 Ok((proof, log_size))
23}
24
25fn verify_single(proof_bytes: &[u8]) -> Result<bool> {
27 let (proof, log_size) = deserialize_proof(proof_bytes)?;
28 let config = verifier_config_for_log_size(log_size as u32);
29 verify(&config, &proof).map_err(|e| anyhow::anyhow!("verification error: {}", e))
30}
31
32#[cfg(not(target_arch = "wasm32"))]
36pub fn verify_proofs(combined_proof: &[u8]) -> Result<(bool, bool)> {
37 if combined_proof.len() < 4 {
38 anyhow::bail!("proof too small");
39 }
40
41 let gigaproof_size = u32::from_le_bytes([
42 combined_proof[0],
43 combined_proof[1],
44 combined_proof[2],
45 combined_proof[3],
46 ]) as usize;
47
48 if combined_proof.len() < 4 + gigaproof_size {
49 anyhow::bail!("invalid proof format");
50 }
51
52 let gigaproof_bytes = combined_proof[4..4 + gigaproof_size].to_vec();
53 let tip_bytes = combined_proof[4 + gigaproof_size..].to_vec();
54
55 let giga_handle = thread::spawn(move || verify_single(&gigaproof_bytes));
57 let tip_handle = thread::spawn(move || verify_single(&tip_bytes));
58
59 let gigaproof_valid = giga_handle.join()
60 .map_err(|_| anyhow::anyhow!("gigaproof thread panicked"))??;
61 let tip_valid = tip_handle.join()
62 .map_err(|_| anyhow::anyhow!("tip thread panicked"))??;
63
64 Ok((gigaproof_valid, tip_valid))
65}
66
67#[cfg(target_arch = "wasm32")]
69pub fn verify_proofs(combined_proof: &[u8]) -> Result<(bool, bool)> {
70 if combined_proof.len() < 4 {
71 anyhow::bail!("proof too small");
72 }
73
74 let gigaproof_size = u32::from_le_bytes([
75 combined_proof[0],
76 combined_proof[1],
77 combined_proof[2],
78 combined_proof[3],
79 ]) as usize;
80
81 if combined_proof.len() < 4 + gigaproof_size {
82 anyhow::bail!("invalid proof format");
83 }
84
85 let gigaproof_bytes = &combined_proof[4..4 + gigaproof_size];
86 let tip_bytes = &combined_proof[4 + gigaproof_size..];
87
88 let gigaproof_valid = verify_single(gigaproof_bytes)?;
90 let tip_valid = verify_single(tip_bytes)?;
91
92 Ok((gigaproof_valid, tip_valid))
93}
94
95pub fn verify_tip(tip_proof: &[u8]) -> Result<bool> {
97 verify_single(tip_proof)
98}
99
100#[cfg(not(target_arch = "wasm32"))]
102pub fn verify_batch(proofs: Vec<Vec<u8>>) -> Result<Vec<bool>> {
103 use std::sync::mpsc;
104
105 let (tx, rx) = mpsc::channel();
106 let num_proofs = proofs.len();
107
108 for (i, proof) in proofs.into_iter().enumerate() {
109 let tx = tx.clone();
110 thread::spawn(move || {
111 let result = verify_single(&proof);
112 let _ = tx.send((i, result));
113 });
114 }
115 drop(tx);
116
117 let mut results = vec![false; num_proofs];
118 for _ in 0..num_proofs {
119 let (i, result) = rx.recv()
120 .map_err(|_| anyhow::anyhow!("channel error"))?;
121 results[i] = result?;
122 }
123
124 Ok(results)
125}
126
127#[cfg(target_arch = "wasm32")]
129pub fn verify_batch(proofs: Vec<Vec<u8>>) -> Result<Vec<bool>> {
130 proofs.iter().map(|p| verify_single(p)).collect()
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn test_empty_proof_fails() {
139 let result = verify_proofs(&[]);
140 assert!(result.is_err());
141 }
142
143 #[test]
144 fn test_too_small_proof_fails() {
145 let result = verify_proofs(&[1, 2, 3]);
146 assert!(result.is_err());
147 }
148
149 #[test]
150 fn test_invalid_format_fails() {
151 let mut data = vec![0xe8, 0x03, 0x00, 0x00]; data.extend_from_slice(&[0u8; 10]);
154 let result = verify_proofs(&data);
155 assert!(result.is_err());
156 }
157}