circom_groth16_verifier/
lib.rs1#![no_std]
2
3extern crate alloc;
8
9pub use contract_types::{Groth16Error, Groth16Proof, VerificationKeyBytes};
10use soroban_sdk::{
11 Env, Vec, contract, contractimpl, contracttype,
12 crypto::bn254::{Bn254G1Affine as G1Affine, Bn254G2Affine as G2Affine, Fr},
13 vec,
14};
15
16#[derive(Clone)]
18pub struct VerificationKey {
19 pub alpha: G1Affine,
20 pub beta: G2Affine,
21 pub gamma: G2Affine,
22 pub delta: G2Affine,
23 pub ic: Vec<G1Affine>,
24}
25
26fn verification_key_from_bytes(env: &Env, vk_bytes: &VerificationKeyBytes) -> VerificationKey {
27 let mut ic_vec: Vec<G1Affine> = Vec::new(env);
28 for bytes in vk_bytes.ic.iter() {
29 ic_vec.push_back(G1Affine::from_bytes(bytes));
30 }
31
32 VerificationKey {
33 alpha: G1Affine::from_bytes(vk_bytes.alpha.clone()),
34 beta: G2Affine::from_bytes(vk_bytes.beta.clone()),
35 gamma: G2Affine::from_bytes(vk_bytes.gamma.clone()),
36 delta: G2Affine::from_bytes(vk_bytes.delta.clone()),
37 ic: ic_vec,
38 }
39}
40
41#[contracttype]
42#[derive(Clone)]
43enum DataKey {
44 VerificationKey,
45}
46
47#[contract]
49pub struct CircomGroth16Verifier;
50
51#[contractimpl]
52impl CircomGroth16Verifier {
53 pub fn __constructor(env: Env, vk: VerificationKeyBytes) -> Result<(), Groth16Error> {
55 let storage = env.storage().persistent();
56 storage.set(&DataKey::VerificationKey, &vk);
57 Ok(())
58 }
59
60 pub fn verify(
62 env: Env,
63 proof: Groth16Proof,
64 public_inputs: Vec<Fr>,
65 ) -> Result<bool, Groth16Error> {
66 let vk_bytes: VerificationKeyBytes = env
67 .storage()
68 .persistent()
69 .get(&DataKey::VerificationKey)
70 .ok_or(Groth16Error::NotInitialized)?;
71 let vk = verification_key_from_bytes(&env, &vk_bytes);
72 Self::verify_with_vk(&env, &vk, proof, public_inputs)
73 }
74
75 fn verify_with_vk(
76 env: &Env,
77 vk: &VerificationKey,
78 proof: Groth16Proof,
79 pub_inputs: Vec<Fr>,
80 ) -> Result<bool, Groth16Error> {
81 let bn = env.crypto().bn254();
82
83 if pub_inputs.len() + 1 != vk.ic.len() {
84 return Err(Groth16Error::MalformedPublicInputs);
85 }
86
87 let mut vk_x = vk.ic.get(0).ok_or(Groth16Error::MalformedPublicInputs)?;
88
89 for i in 0..pub_inputs.len() {
90 let s = pub_inputs.get(i).unwrap();
91 let v = vk.ic.get(i + 1).unwrap();
92 let prod = bn.g1_mul(&v, &s);
93 vk_x = bn.g1_add(&vk_x, &prod);
94 }
95
96 let neg_a = -proof.a;
99
100 let g1_points = vec![env, neg_a, vk.alpha.clone(), vk_x, proof.c];
101 let g2_points = vec![
102 env,
103 proof.b,
104 vk.beta.clone(),
105 vk.gamma.clone(),
106 vk.delta.clone(),
107 ];
108 if bn.pairing_check(g1_points, g2_points) {
109 Ok(true)
110 } else {
111 Err(Groth16Error::InvalidProof)
112 }
113 }
114}
115
116#[cfg(test)]
117mod test;