#![allow(clippy::too_many_arguments)] use crate::math::Math; use crate::poseidon_transcript::{AppendToPoseidon, PoseidonTranscript}; use super::commitments::{Commitments, MultiCommitGens}; use super::errors::ProofVerifyError; use super::group::{ CompressGroupElement, CompressedGroup, DecompressGroupElement, GroupElement, UnpackGroupElement, }; use super::random::RandomTape; use super::scalar::Scalar; use ark_ec::ProjectiveCurve; use ark_ff::PrimeField; use ark_serialize::*; mod bullet; use bullet::BulletReductionProof; #[derive(CanonicalSerialize, CanonicalDeserialize, Debug)] pub struct KnowledgeProof { alpha: CompressedGroup, z1: Scalar, z2: Scalar, } impl KnowledgeProof { fn protocol_name() -> &'static [u8] { b"knowledge proof" } pub fn prove( gens_n: &MultiCommitGens, transcript: &mut PoseidonTranscript, random_tape: &mut RandomTape, x: &Scalar, r: &Scalar, ) -> (KnowledgeProof, CompressedGroup) { // transcript.append_protocol_name(KnowledgeProof::protocol_name()); // produce two random Scalars let t1 = random_tape.random_scalar(b"t1"); let t2 = random_tape.random_scalar(b"t2"); let C = x.commit(r, gens_n).compress(); C.append_to_poseidon(transcript); let alpha = t1.commit(&t2, gens_n).compress(); alpha.append_to_poseidon(transcript); let c = transcript.challenge_scalar(); let z1 = c * x + t1; let z2 = c * r + t2; (KnowledgeProof { alpha, z1, z2 }, C) } pub fn verify( &self, gens_n: &MultiCommitGens, transcript: &mut PoseidonTranscript, C: &CompressedGroup, ) -> Result<(), ProofVerifyError> { // transcript.append_protocol_name(KnowledgeProof::protocol_name()); C.append_to_poseidon(transcript); self.alpha.append_to_poseidon(transcript); let c = transcript.challenge_scalar(); let lhs = self.z1.commit(&self.z2, gens_n).compress(); let rhs = (C.unpack()?.mul(c.into_repr()) + self.alpha.unpack()?).compress(); if lhs == rhs { Ok(()) } else { Err(ProofVerifyError::InternalError) } } } #[derive(CanonicalSerialize, CanonicalDeserialize, Debug)] pub struct EqualityProof { alpha: CompressedGroup, z: Scalar, } impl EqualityProof { fn protocol_name() -> &'static [u8] { b"equality proof" } pub fn prove( gens_n: &MultiCommitGens, transcript: &mut PoseidonTranscript, random_tape: &mut RandomTape, v1: &Scalar, s1: &Scalar, v2: &Scalar, s2: &Scalar, ) -> (EqualityProof, CompressedGroup, CompressedGroup) { // transcript.append_protocol_name(EqualityProof::protocol_name()); // produce a random Scalar let r = random_tape.random_scalar(b"r"); let C1 = v1.commit(s1, gens_n).compress(); transcript.append_point(&C1); let C2 = v2.commit(s2, gens_n).compress(); transcript.append_point(&C2); let alpha = gens_n.h.mul(r.into_repr()).compress(); transcript.append_point(&alpha); let c = transcript.challenge_scalar(); let z = c * ((*s1) - s2) + r; (EqualityProof { alpha, z }, C1, C2) } pub fn verify( &self, gens_n: &MultiCommitGens, transcript: &mut PoseidonTranscript, C1: &CompressedGroup, C2: &CompressedGroup, ) -> Result<(), ProofVerifyError> { // transcript.append_protocol_name(EqualityProof::protocol_name()); transcript.append_point(&C1); transcript.append_point(&C2); transcript.append_point(&self.alpha); let c = transcript.challenge_scalar(); let rhs = { let C = C1.unpack()? - C2.unpack()?; (C.mul(c.into_repr()) + self.alpha.unpack()?).compress() }; println!("rhs {:?}", rhs); let lhs = gens_n.h.mul(self.z.into_repr()).compress(); println!("lhs {:?}", lhs); if lhs == rhs { Ok(()) } else { Err(ProofVerifyError::InternalError) } } } #[derive(CanonicalSerialize, CanonicalDeserialize, Debug)] pub struct ProductProof { alpha: CompressedGroup, beta: CompressedGroup, delta: CompressedGroup, z: Vec, } impl ProductProof { fn protocol_name() -> &'static [u8] { b"product proof" } pub fn prove( gens_n: &MultiCommitGens, transcript: &mut PoseidonTranscript, random_tape: &mut RandomTape, x: &Scalar, rX: &Scalar, y: &Scalar, rY: &Scalar, z: &Scalar, rZ: &Scalar, ) -> ( ProductProof, CompressedGroup, CompressedGroup, CompressedGroup, ) { // transcript.append_protocol_name(ProductProof::protocol_name()); // produce five random Scalar let b1 = random_tape.random_scalar(b"b1"); let b2 = random_tape.random_scalar(b"b2"); let b3 = random_tape.random_scalar(b"b3"); let b4 = random_tape.random_scalar(b"b4"); let b5 = random_tape.random_scalar(b"b5"); let X_unc = x.commit(rX, gens_n); let X = X_unc.compress(); transcript.append_point(&X); let X_new = GroupElement::decompress(&X); assert_eq!(X_unc, X_new.unwrap()); let Y = y.commit(rY, gens_n).compress(); transcript.append_point(&Y); let Z = z.commit(rZ, gens_n).compress(); transcript.append_point(&Z); let alpha = b1.commit(&b2, gens_n).compress(); transcript.append_point(&alpha); let beta = b3.commit(&b4, gens_n).compress(); transcript.append_point(&beta); let delta = { let gens_X = &MultiCommitGens { n: 1, G: vec![GroupElement::decompress(&X).unwrap()], h: gens_n.h, }; b3.commit(&b5, gens_X).compress() }; transcript.append_point(&delta); let c = transcript.challenge_scalar(); let z1 = b1 + c * x; let z2 = b2 + c * rX; let z3 = b3 + c * y; let z4 = b4 + c * rY; let z5 = b5 + c * ((*rZ) - (*rX) * y); let z = [z1, z2, z3, z4, z5].to_vec(); ( ProductProof { alpha, beta, delta, z, }, X, Y, Z, ) } fn check_equality( P: &CompressedGroup, X: &CompressedGroup, c: &Scalar, gens_n: &MultiCommitGens, z1: &Scalar, z2: &Scalar, ) -> bool { println!("{:?}", X); let lhs = (GroupElement::decompress(P).unwrap() + GroupElement::decompress(X).unwrap().mul(c.into_repr())) .compress(); let rhs = z1.commit(z2, gens_n).compress(); lhs == rhs } pub fn verify( &self, gens_n: &MultiCommitGens, transcript: &mut PoseidonTranscript, X: &CompressedGroup, Y: &CompressedGroup, Z: &CompressedGroup, ) -> Result<(), ProofVerifyError> { // transcript.append_protocol_name(ProductProof::protocol_name()); X.append_to_poseidon(transcript); Y.append_to_poseidon(transcript); Z.append_to_poseidon(transcript); self.alpha.append_to_poseidon(transcript); self.beta.append_to_poseidon(transcript); self.delta.append_to_poseidon(transcript); let z1 = self.z[0]; let z2 = self.z[1]; let z3 = self.z[2]; let z4 = self.z[3]; let z5 = self.z[4]; let c = transcript.challenge_scalar(); if ProductProof::check_equality(&self.alpha, X, &c, gens_n, &z1, &z2) && ProductProof::check_equality(&self.beta, Y, &c, gens_n, &z3, &z4) && ProductProof::check_equality( &self.delta, Z, &c, &MultiCommitGens { n: 1, G: vec![X.unpack()?], h: gens_n.h, }, &z3, &z5, ) { Ok(()) } else { Err(ProofVerifyError::InternalError) } } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct DotProductProof { delta: CompressedGroup, beta: CompressedGroup, z: Vec, z_delta: Scalar, z_beta: Scalar, } impl DotProductProof { fn protocol_name() -> &'static [u8] { b"dot product proof" } pub fn compute_dotproduct(a: &[Scalar], b: &[Scalar]) -> Scalar { assert_eq!(a.len(), b.len()); (0..a.len()).map(|i| a[i] * b[i]).sum() } pub fn prove( gens_1: &MultiCommitGens, gens_n: &MultiCommitGens, transcript: &mut PoseidonTranscript, random_tape: &mut RandomTape, x_vec: &[Scalar], blind_x: &Scalar, a_vec: &[Scalar], y: &Scalar, blind_y: &Scalar, ) -> (DotProductProof, CompressedGroup, CompressedGroup) { // transcript.append_protocol_name(DotProductProof::protocol_name()); let n = x_vec.len(); assert_eq!(x_vec.len(), a_vec.len()); assert_eq!(gens_n.n, a_vec.len()); assert_eq!(gens_1.n, 1); // produce randomness for the proofs let d_vec = random_tape.random_vector(b"d_vec", n); let r_delta = random_tape.random_scalar(b"r_delta"); let r_beta = random_tape.random_scalar(b"r_beta"); let Cx = x_vec.commit(blind_x, gens_n).compress(); Cx.append_to_poseidon(transcript); let Cy = y.commit(blind_y, gens_1).compress(); Cy.append_to_poseidon(transcript); transcript.append_scalar_vector(&a_vec.to_vec()); let delta = d_vec.commit(&r_delta, gens_n).compress(); delta.append_to_poseidon(transcript); let dotproduct_a_d = DotProductProof::compute_dotproduct(a_vec, &d_vec); let beta = dotproduct_a_d.commit(&r_beta, gens_1).compress(); beta.append_to_poseidon(transcript); let c = transcript.challenge_scalar(); let z = (0..d_vec.len()) .map(|i| c * x_vec[i] + d_vec[i]) .collect::>(); let z_delta = c * blind_x + r_delta; let z_beta = c * blind_y + r_beta; ( DotProductProof { delta, beta, z, z_delta, z_beta, }, Cx, Cy, ) } pub fn verify( &self, gens_1: &MultiCommitGens, gens_n: &MultiCommitGens, transcript: &mut PoseidonTranscript, a: &[Scalar], Cx: &CompressedGroup, Cy: &CompressedGroup, ) -> Result<(), ProofVerifyError> { assert_eq!(gens_n.n, a.len()); assert_eq!(gens_1.n, 1); // transcript.append_protocol_name(DotProductProof::protocol_name()); Cx.append_to_poseidon(transcript); Cy.append_to_poseidon(transcript); transcript.append_scalar_vector(&a.to_vec()); self.delta.append_to_poseidon(transcript); self.beta.append_to_poseidon(transcript); let c = transcript.challenge_scalar(); let mut result = Cx.unpack()?.mul(c.into_repr()) + self.delta.unpack()? == self.z.commit(&self.z_delta, gens_n); let dotproduct_z_a = DotProductProof::compute_dotproduct(&self.z, a); result &= Cy.unpack()?.mul(c.into_repr()) + self.beta.unpack()? == dotproduct_z_a.commit(&self.z_beta, gens_1); if result { Ok(()) } else { Err(ProofVerifyError::InternalError) } } } #[derive(Clone)] pub struct DotProductProofGens { n: usize, pub gens_n: MultiCommitGens, pub gens_1: MultiCommitGens, } impl DotProductProofGens { pub fn new(n: usize, label: &[u8]) -> Self { let (gens_n, gens_1) = MultiCommitGens::new(n + 1, label).split_at(n); DotProductProofGens { n, gens_n, gens_1 } } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct DotProductProofLog { bullet_reduction_proof: BulletReductionProof, delta: CompressedGroup, beta: CompressedGroup, z1: Scalar, z2: Scalar, } impl DotProductProofLog { fn protocol_name() -> &'static [u8] { b"dot product proof (log)" } pub fn compute_dotproduct(a: &[Scalar], b: &[Scalar]) -> Scalar { assert_eq!(a.len(), b.len()); (0..a.len()).map(|i| a[i] * b[i]).sum() } pub fn prove( gens: &DotProductProofGens, transcript: &mut PoseidonTranscript, random_tape: &mut RandomTape, x_vec: &[Scalar], blind_x: &Scalar, a_vec: &[Scalar], y: &Scalar, blind_y: &Scalar, ) -> (DotProductProofLog, CompressedGroup, CompressedGroup) { // transcript.append_protocol_name(DotProductProofLog::protocol_name()); let n = x_vec.len(); assert_eq!(x_vec.len(), a_vec.len()); assert_eq!(gens.n, n); // produce randomness for generating a proof let d = random_tape.random_scalar(b"d"); let r_delta = random_tape.random_scalar(b"r_delta"); let r_beta = random_tape.random_scalar(b"r_delta"); let blinds_vec = { let v1 = random_tape.random_vector(b"blinds_vec_1", 2 * n.log_2()); let v2 = random_tape.random_vector(b"blinds_vec_2", 2 * n.log_2()); (0..v1.len()) .map(|i| (v1[i], v2[i])) .collect::>() }; let Cx = x_vec.commit(blind_x, &gens.gens_n).compress(); transcript.append_point(&Cx); let Cy = y.commit(blind_y, &gens.gens_1).compress(); transcript.append_point(&Cy); transcript.append_scalar_vector(&a_vec.to_vec()); let blind_Gamma = (*blind_x) + blind_y; let (bullet_reduction_proof, _Gamma_hat, x_hat, a_hat, g_hat, rhat_Gamma) = BulletReductionProof::prove( transcript, &gens.gens_1.G[0], &gens.gens_n.G, &gens.gens_n.h, x_vec, a_vec, &blind_Gamma, &blinds_vec, ); let y_hat = x_hat * a_hat; let delta = { let gens_hat = MultiCommitGens { n: 1, G: vec![g_hat], h: gens.gens_1.h, }; d.commit(&r_delta, &gens_hat).compress() }; transcript.append_point(&delta); let beta = d.commit(&r_beta, &gens.gens_1).compress(); transcript.append_point(&beta); let c = transcript.challenge_scalar(); let z1 = d + c * y_hat; let z2 = a_hat * (c * rhat_Gamma + r_beta) + r_delta; ( DotProductProofLog { bullet_reduction_proof, delta, beta, z1, z2, }, Cx, Cy, ) } pub fn verify( &self, n: usize, gens: &DotProductProofGens, transcript: &mut PoseidonTranscript, a: &[Scalar], Cx: &CompressedGroup, Cy: &CompressedGroup, ) -> Result<(), ProofVerifyError> { assert_eq!(gens.n, n); assert_eq!(a.len(), n); // transcript.append_protocol_name(DotProductProofLog::protocol_name()); // Cx.append_to_poseidon( transcript); // Cy.append_to_poseidon( transcript); // a.append_to_poseidon( transcript); transcript.append_point(&Cx); transcript.append_point(&Cy); transcript.append_scalar_vector(&a.to_vec()); let Gamma = Cx.unpack()? + Cy.unpack()?; let (g_hat, Gamma_hat, a_hat) = self .bullet_reduction_proof .verify(n, a, transcript, &Gamma, &gens.gens_n.G)?; // self.delta.append_to_poseidon( transcript); // self.beta.append_to_poseidon( transcript); transcript.append_point(&self.delta); transcript.append_point(&self.beta); let c = transcript.challenge_scalar(); let c_s = &c; let beta_s = self.beta.unpack()?; let a_hat_s = &a_hat; let delta_s = self.delta.unpack()?; let z1_s = &self.z1; let z2_s = &self.z2; let lhs = ((Gamma_hat.mul(c_s.into_repr()) + beta_s).mul(a_hat_s.into_repr()) + delta_s).compress(); let rhs = ((g_hat + gens.gens_1.G[0].mul(a_hat_s.into_repr())).mul(z1_s.into_repr()) + gens.gens_1.h.mul(z2_s.into_repr())) .compress(); assert_eq!(lhs, rhs); if lhs == rhs { Ok(()) } else { Err(ProofVerifyError::InternalError) } } } #[cfg(test)] mod tests { use crate::parameters::poseidon_params; use super::*; use ark_std::UniformRand; #[test] fn check_knowledgeproof() { let mut rng = ark_std::rand::thread_rng(); let gens_1 = MultiCommitGens::new(1, b"test-knowledgeproof"); let x = Scalar::rand(&mut rng); let r = Scalar::rand(&mut rng); let params = poseidon_params(); let mut random_tape = RandomTape::new(b"proof"); let mut prover_transcript = PoseidonTranscript::new(¶ms); let (proof, committed_value) = KnowledgeProof::prove(&gens_1, &mut prover_transcript, &mut random_tape, &x, &r); let mut verifier_transcript = PoseidonTranscript::new(¶ms); assert!(proof .verify(&gens_1, &mut verifier_transcript, &committed_value) .is_ok()); } #[test] fn check_equalityproof() { let mut rng = ark_std::rand::thread_rng(); let params = poseidon_params(); let gens_1 = MultiCommitGens::new(1, b"test-equalityproof"); let v1 = Scalar::rand(&mut rng); let v2 = v1; let s1 = Scalar::rand(&mut rng); let s2 = Scalar::rand(&mut rng); let mut random_tape = RandomTape::new(b"proof"); let mut prover_transcript = PoseidonTranscript::new(¶ms); let (proof, C1, C2) = EqualityProof::prove( &gens_1, &mut prover_transcript, &mut random_tape, &v1, &s1, &v2, &s2, ); let mut verifier_transcript = PoseidonTranscript::new(¶ms); assert!(proof .verify(&gens_1, &mut verifier_transcript, &C1, &C2) .is_ok()); } #[test] fn check_productproof() { let mut rng = ark_std::rand::thread_rng(); let pt = GroupElement::rand(&mut rng); let pt_c = pt.compress(); let pt2 = GroupElement::decompress(&pt_c).unwrap(); assert_eq!(pt, pt2); let params = poseidon_params(); let gens_1 = MultiCommitGens::new(1, b"test-productproof"); let x = Scalar::rand(&mut rng); let rX = Scalar::rand(&mut rng); let y = Scalar::rand(&mut rng); let rY = Scalar::rand(&mut rng); let z = x * y; let rZ = Scalar::rand(&mut rng); let mut random_tape = RandomTape::new(b"proof"); let mut prover_transcript = PoseidonTranscript::new(¶ms); let (proof, X, Y, Z) = ProductProof::prove( &gens_1, &mut prover_transcript, &mut random_tape, &x, &rX, &y, &rY, &z, &rZ, ); let mut verifier_transcript = PoseidonTranscript::new(¶ms); assert!(proof .verify(&gens_1, &mut verifier_transcript, &X, &Y, &Z) .is_ok()); } #[test] fn check_dotproductproof() { let mut rng = ark_std::rand::thread_rng(); let n = 1024; let gens_1 = MultiCommitGens::new(1, b"test-two"); let gens_1024 = MultiCommitGens::new(n, b"test-1024"); let params = poseidon_params(); let mut x: Vec = Vec::new(); let mut a: Vec = Vec::new(); for _ in 0..n { x.push(Scalar::rand(&mut rng)); a.push(Scalar::rand(&mut rng)); } let y = DotProductProofLog::compute_dotproduct(&x, &a); let r_x = Scalar::rand(&mut rng); let r_y = Scalar::rand(&mut rng); let mut random_tape = RandomTape::new(b"proof"); let mut prover_transcript = PoseidonTranscript::new(¶ms); let (proof, Cx, Cy) = DotProductProof::prove( &gens_1, &gens_1024, &mut prover_transcript, &mut random_tape, &x, &r_x, &a, &y, &r_y, ); let mut verifier_transcript = PoseidonTranscript::new(¶ms); assert!(proof .verify(&gens_1, &gens_1024, &mut verifier_transcript, &a, &Cx, &Cy) .is_ok()); } #[test] fn check_dotproductproof_log() { let mut rng = ark_std::rand::thread_rng(); let n = 1024; let gens = DotProductProofGens::new(n, b"test-1024"); let x: Vec = (0..n).map(|_i| Scalar::rand(&mut rng)).collect(); let a: Vec = (0..n).map(|_i| Scalar::rand(&mut rng)).collect(); let y = DotProductProof::compute_dotproduct(&x, &a); let r_x = Scalar::rand(&mut rng); let r_y = Scalar::rand(&mut rng); let params = poseidon_params(); let mut random_tape = RandomTape::new(b"proof"); let mut prover_transcript = PoseidonTranscript::new(¶ms); let (proof, Cx, Cy) = DotProductProofLog::prove( &gens, &mut prover_transcript, &mut random_tape, &x, &r_x, &a, &y, &r_y, ); let mut verifier_transcript = PoseidonTranscript::new(¶ms); assert!(proof .verify(n, &gens, &mut verifier_transcript, &a, &Cx, &Cy) .is_ok()); } }