Skip to main content

inherence_verifier/
proof.rs

1//! SPEC §6.6 — Groth16 proof verification.
2
3use ark_bn254::{Bn254, Fr};
4use ark_groth16::{Groth16, Proof, VerifyingKey, prepare_verifying_key};
5use ark_serialize::CanonicalDeserialize;
6use ark_snark::SNARK;
7use base64::Engine;
8use serde_json::Value;
9
10use crate::binding;
11use crate::error::VerifyError;
12use crate::VerifyConfig;
13
14/// If `cryptographic_proof.proof` is non-null, deserialize the
15/// arkworks proof bytes, deserialize the vk pinned by
16/// `public_inputs.vk_hash`, reconstruct the Fr public inputs per
17/// SPEC §6.7, and run `Groth16::verify`.
18pub fn verify_if_present(p: &Value, cfg: &VerifyConfig) -> Result<(), VerifyError> {
19    let proof_evidence = p["verification"]["evidence"].as_array()
20        .and_then(|arr| arr.iter().find(|e| e["type"] == "cryptographic_proof"))
21        .ok_or_else(|| VerifyError::SchemaViolation("no cryptographic_proof evidence".into()))?;
22
23    let proof_b64 = match proof_evidence.get("proof").and_then(|v| v.as_str()) {
24        Some(s) if !s.is_empty() => s,
25        _ => return Ok(()),  // ATTESTED with null proof — accept per §6.6 last para
26    };
27
28    let proof_bytes = base64::engine::general_purpose::STANDARD.decode(proof_b64)
29        .map_err(|e| VerifyError::ProofInvalid(format!("proof not base64: {e}")))?;
30    let proof = Proof::<Bn254>::deserialize_compressed(&proof_bytes[..])
31        .or_else(|_| Proof::<Bn254>::deserialize_uncompressed(&proof_bytes[..]))
32        .map_err(|e| VerifyError::ProofInvalid(format!("proof deserialize failed: {e}")))?;
33
34    let vk_hash = p["verification"]["public_inputs"]["vk_hash"].as_str().unwrap_or("");
35    let vk_bytes = cfg.lookup_vk(vk_hash)
36        .ok_or_else(|| VerifyError::UnknownVk(vk_hash.to_string()))?;
37    let vk = VerifyingKey::<Bn254>::deserialize_compressed(vk_bytes)
38        .or_else(|_| VerifyingKey::<Bn254>::deserialize_uncompressed(vk_bytes))
39        .map_err(|e| VerifyError::ProofInvalid(format!("vk deserialize failed: {e}")))?;
40
41    let pi = &p["verification"]["public_inputs"];
42    let action_hash = pi["action_hash"].as_str().unwrap_or("");
43    let contract_hash = pi["contract_hash"].as_str().unwrap_or("");
44    let decision_bit = pi["decision_bit"].as_i64().unwrap_or(-1) as u8;
45    let inputs_fr: Vec<Fr> = binding::public_inputs_fr(
46        action_hash, contract_hash, decision_bit, vk_hash,
47    )?;
48
49    let pvk = prepare_verifying_key(&vk);
50    let ok = Groth16::<Bn254>::verify_with_processed_vk(&pvk, &inputs_fr, &proof)
51        .map_err(|e| VerifyError::ProofInvalid(format!("verify call failed: {e}")))?;
52    if !ok {
53        return Err(VerifyError::ProofInvalid("verify returned false".into()));
54    }
55    Ok(())
56}