use soroban_sdk::Env;
use super::crypto::{bn254_g1_add, bn254_g1_mul, bn254_pairing_check};
use super::error::ZKError;
use super::types::{Groth16Proof, Scalar, VerificationKey};
pub fn validate_groth16_contract(
vk: &VerificationKey,
public_inputs: &[Scalar],
) -> Result<(), ZKError> {
if vk.ic.is_empty() || vk.ic.len() as usize != public_inputs.len() + 1 {
return Err(ZKError::InvalidVerificationKey);
}
Ok(())
}
pub fn verify_groth16(
env: &Env,
vk: &VerificationKey,
proof: &Groth16Proof,
public_inputs: &[Scalar],
) -> Result<bool, ZKError> {
validate_groth16_contract(vk, public_inputs)?;
let mut vk_x = vk.ic.get(0).ok_or(ZKError::InvalidVerificationKey)?;
for (i, input) in public_inputs.iter().enumerate() {
let ic_point = vk
.ic
.get((i + 1) as u32)
.ok_or(ZKError::InvalidVerificationKey)?;
let term = bn254_g1_mul(env, &ic_point, input)?;
vk_x = bn254_g1_add(env, &vk_x, &term)?;
}
let g1_points = [proof.a.clone(), vk.alpha.clone(), vk_x, proof.c.clone()];
let g2_points = [
proof.b.clone(),
vk.beta.clone(),
vk.gamma.clone(),
vk.delta.clone(),
];
bn254_pairing_check(env, &g1_points, &g2_points)
}
#[cfg(test)]
mod tests {
use super::super::types::G1Point;
use super::*;
use soroban_sdk::{BytesN, Env, Vec};
fn make_g1(env: &Env) -> G1Point {
G1Point {
bytes: BytesN::from_array(env, &[0u8; 64]),
}
}
fn make_g2(env: &Env) -> super::super::types::G2Point {
super::super::types::G2Point {
bytes: BytesN::from_array(env, &[0u8; 128]),
}
}
#[test]
fn test_verify_groth16_wrong_ic_length() {
let env = Env::default();
let g1 = make_g1(&env);
let g2 = make_g2(&env);
let mut ic = Vec::new(&env);
ic.push_back(g1.clone());
let vk = VerificationKey {
alpha: g1.clone(),
beta: g2.clone(),
gamma: g2.clone(),
delta: g2,
ic,
};
let proof = Groth16Proof {
a: g1.clone(),
b: make_g2(&env),
c: g1,
};
let scalar = Scalar {
bytes: BytesN::from_array(&env, &[0u8; 32]),
};
let result = verify_groth16(&env, &vk, &proof, &[scalar]);
assert_eq!(result, Err(ZKError::InvalidVerificationKey));
}
#[test]
fn test_verify_groth16_empty_ic() {
let env = Env::default();
let g1 = make_g1(&env);
let g2 = make_g2(&env);
let ic = Vec::new(&env);
let vk = VerificationKey {
alpha: g1.clone(),
beta: g2.clone(),
gamma: g2.clone(),
delta: g2,
ic,
};
let proof = Groth16Proof {
a: g1.clone(),
b: make_g2(&env),
c: g1,
};
let result = verify_groth16(&env, &vk, &proof, &[]);
assert_eq!(result, Err(ZKError::InvalidVerificationKey));
}
#[test]
fn test_validate_groth16_contract_accepts_matching_lengths() {
let env = Env::default();
let g1 = make_g1(&env);
let g2 = make_g2(&env);
let mut ic = Vec::new(&env);
ic.push_back(g1.clone());
ic.push_back(g1);
let vk = VerificationKey {
alpha: make_g1(&env),
beta: g2.clone(),
gamma: g2.clone(),
delta: g2,
ic,
};
let scalar = Scalar {
bytes: BytesN::from_array(&env, &[0u8; 32]),
};
assert_eq!(validate_groth16_contract(&vk, &[scalar]), Ok(()));
}
}