Created
February 3, 2026 17:05
-
-
Save prasad-kumkar/600c60e2bccd3b2788ef7dcaaa80650f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use ark_serialize::CanonicalDeserialize; | |
| use serde_json::Value; | |
| use std::env; | |
| use std::fs; | |
| use std::path::{Path, PathBuf}; | |
| use ark_bls12_381::Bls12_381; | |
| use ark_ed_on_bls12_381_bandersnatch::{EdwardsAffine, Fq}; | |
| use w3f_pcs::pcs::kzg::KZG; | |
| use w3f_plonk_common::domain::Domain; | |
| use w3f_ring_proof::{ArkTranscript, PiopParams, RingProof, VerifierKey}; | |
| use w3f_ring_proof::ring_verifier::RingVerifier; | |
| fn from_hex(hex: &str) -> Result<Vec<u8>, String> { | |
| if hex.len() % 2 != 0 { | |
| return Err("Hex string has odd length".to_string()); | |
| } | |
| (0..hex.len()) | |
| .step_by(2) | |
| .map(|i| u8::from_str_radix(&hex[i..i + 2], 16)) | |
| .collect::<Result<Vec<_>, _>>() | |
| .map_err(|e| format!("Invalid hex: {}", e)) | |
| } | |
| fn get_str<'a>(value: &'a Value, path: &[&str]) -> Result<&'a str, String> { | |
| let mut current = value; | |
| for key in path { | |
| current = current | |
| .get(key) | |
| .ok_or_else(|| format!("Missing key: {}", key))?; | |
| } | |
| current | |
| .as_str() | |
| .ok_or_else(|| format!("Expected string at {:?}", path)) | |
| } | |
| fn get_u64(value: &Value, path: &[&str]) -> Result<u64, String> { | |
| let mut current = value; | |
| for key in path { | |
| current = current | |
| .get(key) | |
| .ok_or_else(|| format!("Missing key: {}", key))?; | |
| } | |
| current | |
| .as_u64() | |
| .ok_or_else(|| format!("Expected u64 at {:?}", path)) | |
| } | |
| fn verify_file(path: &Path) -> Result<(), String> { | |
| let json_content = fs::read_to_string(path) | |
| .map_err(|e| format!("Failed to read {}: {}", path.display(), e))?; | |
| let data: Value = serde_json::from_str(&json_content) | |
| .map_err(|e| format!("Failed to parse JSON: {}", e))?; | |
| // Parse metadata | |
| let domain_size = get_u64(&data, &["metadata", "parameters", "domain_size"])? as usize; | |
| // Parse result point (instance) | |
| let result_x_hex = get_str(&data, &["metadata", "parameters", "result", "x"])?; | |
| let result_y_hex = get_str(&data, &["metadata", "parameters", "result", "y"])?; | |
| let result_x_bytes = from_hex(result_x_hex)?; | |
| let result_y_bytes = from_hex(result_y_hex)?; | |
| // Deserialize as little-endian field elements (Bandersnatch uses Fq) | |
| let result_x = Fq::deserialize_uncompressed(&result_x_bytes[..]) | |
| .map_err(|e| format!("Failed to deserialize result.x: {:?}", e))?; | |
| let result_y = Fq::deserialize_uncompressed(&result_y_bytes[..]) | |
| .map_err(|e| format!("Failed to deserialize result.y: {:?}", e))?; | |
| let result = EdwardsAffine::new_unchecked(result_x, result_y); | |
| // Parse seed and h points | |
| let seed_x_bytes = from_hex(get_str(&data, &["metadata", "parameters", "seed", "x"])? )?; | |
| let seed_y_bytes = from_hex(get_str(&data, &["metadata", "parameters", "seed", "y"])? )?; | |
| let seed_x = Fq::deserialize_uncompressed(&seed_x_bytes[..]) | |
| .map_err(|e| format!("Failed to deserialize seed.x: {:?}", e))?; | |
| let seed_y = Fq::deserialize_uncompressed(&seed_y_bytes[..]) | |
| .map_err(|e| format!("Failed to deserialize seed.y: {:?}", e))?; | |
| let seed = EdwardsAffine::new_unchecked(seed_x, seed_y); | |
| let h_x_bytes = from_hex(get_str(&data, &["metadata", "parameters", "h", "x"])? )?; | |
| let h_y_bytes = from_hex(get_str(&data, &["metadata", "parameters", "h", "y"])? )?; | |
| let h_x = Fq::deserialize_uncompressed(&h_x_bytes[..]) | |
| .map_err(|e| format!("Failed to deserialize h.x: {:?}", e))?; | |
| let h_y = Fq::deserialize_uncompressed(&h_y_bytes[..]) | |
| .map_err(|e| format!("Failed to deserialize h.y: {:?}", e))?; | |
| let h = EdwardsAffine::new_unchecked(h_x, h_y); | |
| // Padding point may not be in JSON, use identity point as default | |
| let padding = EdwardsAffine::default(); | |
| // Parse verifier key | |
| let vk_hex = get_str(&data, &["verifier_key", "verification_key"])?; | |
| let vk_bytes = from_hex(vk_hex)?; | |
| let verifier_key = VerifierKey::<Fq, KZG<Bls12_381>>::deserialize_compressed(&vk_bytes[..]) | |
| .map_err(|e| format!("Failed to deserialize verifier key: {:?}", e))?; | |
| // Parse proof components | |
| let column_commitments_hex = get_str(&data, &["proof", "column_commitments"])?; | |
| let columns_at_zeta_hex = get_str(&data, &["proof", "columns_at_zeta"])?; | |
| let quotient_commitment_hex = get_str(&data, &["proof", "quotient_commitment"])?; | |
| let lin_at_zeta_omega_hex = get_str(&data, &["proof", "lin_at_zeta_omega"])?; | |
| let agg_at_zeta_proof_hex = get_str(&data, &["proof", "agg_at_zeta_proof"])?; | |
| let lin_at_zeta_omega_proof_hex = get_str(&data, &["proof", "lin_at_zeta_omega_proof"])?; | |
| // Concatenate all proof components in the exact order of Proof's fields: | |
| // 1. column_commitments (RingCommitments) | |
| // 2. columns_at_zeta (RingEvaluations) | |
| // 3. quotient_commitment (G1) | |
| // 4. lin_at_zeta_omega (Fq) | |
| // 5. agg_at_zeta_proof (G1) | |
| // 6. lin_at_zeta_omega_proof (G1) | |
| let mut proof_bytes = Vec::new(); | |
| proof_bytes.extend(from_hex(column_commitments_hex)?); | |
| proof_bytes.extend(from_hex(columns_at_zeta_hex)?); | |
| proof_bytes.extend(from_hex(quotient_commitment_hex)?); | |
| proof_bytes.extend(from_hex(lin_at_zeta_omega_hex)?); | |
| proof_bytes.extend(from_hex(agg_at_zeta_proof_hex)?); | |
| proof_bytes.extend(from_hex(lin_at_zeta_omega_proof_hex)?); | |
| // Deserialize the proof | |
| let proof = RingProof::<Fq, KZG<Bls12_381>>::deserialize_compressed(&proof_bytes[..]) | |
| .map_err(|e| format!("Failed to deserialize proof: {:?}", e))?; | |
| // Create PIOP params | |
| let domain = Domain::new(domain_size, true); | |
| let piop_params = PiopParams::setup(domain, h, seed, padding); | |
| // Verify the proof | |
| let ring_verifier = RingVerifier::init( | |
| verifier_key, | |
| piop_params, | |
| ArkTranscript::new(b"w3f-ring-proof-test"), | |
| ); | |
| let is_valid = ring_verifier.verify(proof, result); | |
| if !is_valid { | |
| return Err("Verification failed".to_string()); | |
| } | |
| Ok(()) | |
| } | |
| fn collect_json_paths(target: &Path) -> Result<Vec<PathBuf>, String> { | |
| if target.is_file() { | |
| return Ok(vec![target.to_path_buf()]); | |
| } | |
| if !target.is_dir() { | |
| return Err(format!("Path is not a file or directory: {}", target.display())); | |
| } | |
| let mut paths = Vec::new(); | |
| for entry in fs::read_dir(target) | |
| .map_err(|e| format!("Failed to read directory {}: {}", target.display(), e))? | |
| { | |
| let entry = entry.map_err(|e| format!("Failed to read entry: {}", e))?; | |
| let path = entry.path(); | |
| if path.extension().and_then(|s| s.to_str()) == Some("json") { | |
| paths.push(path); | |
| } | |
| } | |
| paths.sort(); | |
| Ok(paths) | |
| } | |
| fn main() { | |
| let args: Vec<String> = env::args().collect(); | |
| let target = if args.len() > 1 { | |
| PathBuf::from(&args[1]) | |
| } else { | |
| PathBuf::from("../../dot-ring/tests/vectors/others") | |
| }; | |
| println!("Target: {}", target.display()); | |
| let paths = match collect_json_paths(&target) { | |
| Ok(paths) => paths, | |
| Err(err) => { | |
| eprintln!("ERROR: {}", err); | |
| std::process::exit(1); | |
| } | |
| }; | |
| if paths.is_empty() { | |
| eprintln!("ERROR: No JSON files found at {}", target.display()); | |
| std::process::exit(1); | |
| } | |
| let mut ok = 0usize; | |
| let mut failed = 0usize; | |
| for path in paths { | |
| match verify_file(&path) { | |
| Ok(()) => { | |
| println!("[OK] {}", path.display()); | |
| ok += 1; | |
| } | |
| Err(err) => { | |
| eprintln!("[FAIL] {}", path.display()); | |
| eprintln!(" {}", err); | |
| failed += 1; | |
| } | |
| } | |
| } | |
| println!("\nSummary: {} ok, {} failed", ok, failed); | |
| if failed > 0 { | |
| std::process::exit(1); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment