Verifying
Verifying a linked proof looks just like proving one. You'll call the same
methods on the LinkedProofVerificationBuilder that you did on the
LinkedProofBuilder, in the same order, but instead of supplying the private
values, you'll supply the public ones. Then you'll specify the proof, ZKP
program, and any public or constant inputs, as we did for the unlinked ZKP
programs.
use sunscreen::{ bulletproofs::BulletproofsBackend, fhe_program, linked::{LinkedProof, LinkedProofBuilder}, types::{ bfv::Signed, zkp::{ AsFieldElement, BfvSigned, BulletproofsField, ConstrainCmp, ConstrainFresh, Field, FieldSpec, }, Cipher, }, zkp_program, zkp_var, Ciphertext, CompiledFheProgram, CompiledZkpProgram, Compiler, Error, FheProgramInput, FheRuntime, FheZkpApplication, FheZkpRuntime, Params, PrivateKey, PublicKey, Result, ZkpProgramInput, }; #[fhe_program(scheme = "bfv")] fn increase_by_factor(x: Signed, scale: Cipher<Signed>) -> Cipher<Signed> { x * scale } #[zkp_program] fn is_greater_than_one<F: FieldSpec>(#[linked] scale: BfvSigned<F>) { scale .into_field_elem() .constrain_gt_bounded(zkp_var!(1), 64); } fn main() -> Result<(), Error> { let app = Compiler::new() .fhe_program(increase_by_factor) .zkp_backend::<BulletproofsBackend>() .zkp_program(is_greater_than_one) .compile()?; let runtime = FheZkpRuntime::new(app.params(), &BulletproofsBackend::new())?; let (public_key, private_key) = runtime.generate_keys()?; let mut proof_builder = runtime.linkedproof_builder(); let (ct, link) = proof_builder.encrypt_returning_link(&Signed::from(2), &public_key)?; let proof = proof_builder .zkp_program(app.get_zkp_program(is_greater_than_one).unwrap())? .linked_input(link) .build()?; let mut verify_builder = runtime.linkedproof_verification_builder(); verify_builder.encrypt_returning_link::<Signed>(&ct, &public_key)?; verify_builder .proof(proof) .zkp_program(app.get_zkp_program(is_greater_than_one).unwrap())? .verify()?; Ok(()) }