use ark_ec::CurveGroup; use ark_ff::Field; use ark_r1cs_std::{boolean::Boolean, prelude::CurveVar}; use ark_relations::r1cs::SynthesisError; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::Zero; use ark_std::{rand::RngCore, UniformRand}; use core::marker::PhantomData; use super::CommitmentScheme; use crate::transcript::Transcript; use crate::utils::vec::{vec_add, vec_scalar_mul}; use crate::Error; #[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)] pub struct Proof { pub R: C, pub u: Vec, pub r_u: C::ScalarField, // blind } #[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)] pub struct Params { pub h: C, pub generators: Vec, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct Pedersen { _c: PhantomData, } /// Implements the CommitmentScheme trait for Pedersen commitments impl CommitmentScheme for Pedersen { type ProverParams = Params; type VerifierParams = Params; type Proof = Proof; type ProverChallenge = (C::ScalarField, Vec, C, C::ScalarField); type Challenge = C::ScalarField; fn is_hiding() -> bool { if H { return true; } false } fn setup( mut rng: impl RngCore, len: usize, ) -> Result<(Self::ProverParams, Self::VerifierParams), Error> { let generators: Vec = std::iter::repeat_with(|| C::Affine::rand(&mut rng)) .take(len.next_power_of_two()) .collect(); let p = Params:: { h: C::rand(&mut rng), generators, }; Ok((p.clone(), p)) } fn commit( params: &Self::ProverParams, v: &[C::ScalarField], r: &C::ScalarField, // blinding factor ) -> Result { if params.generators.len() < v.len() { return Err(Error::PedersenParamsLen(params.generators.len(), v.len())); } if !H && (!r.is_zero()) { return Err(Error::BlindingNotZero); } // h⋅r + // use msm_unchecked because we already ensured at the if that lengths match if !H { return Ok(C::msm_unchecked(¶ms.generators[..v.len()], v)); } Ok(params.h.mul(r) + C::msm_unchecked(¶ms.generators[..v.len()], v)) } fn prove( params: &Self::ProverParams, transcript: &mut impl Transcript, cm: &C, v: &[C::ScalarField], r: &C::ScalarField, // blinding factor _rng: Option<&mut dyn RngCore>, ) -> Result { transcript.absorb_nonnative(cm); let r1 = transcript.get_challenge(); let d = transcript.get_challenges(v.len()); // R = h⋅r_1 + // use msm_unchecked because we already ensured at the if that lengths match let mut R: C = C::msm_unchecked(¶ms.generators[..d.len()], &d); if H { R += params.h.mul(r1); } transcript.absorb_nonnative(&R); let e = transcript.get_challenge(); let challenge = (r1, d, R, e); Self::prove_with_challenge(params, challenge, v, r, _rng) } fn prove_with_challenge( params: &Self::ProverParams, challenge: Self::ProverChallenge, v: &[C::ScalarField], // vector r: &C::ScalarField, // blinding factor _rng: Option<&mut dyn RngCore>, ) -> Result { if params.generators.len() < v.len() { return Err(Error::PedersenParamsLen(params.generators.len(), v.len())); } if !H && (!r.is_zero()) { return Err(Error::BlindingNotZero); } let (r1, d, R, e): (C::ScalarField, Vec, C, C::ScalarField) = challenge; // u = d + v⋅e let u = vec_add(&vec_scalar_mul(v, &e), &d)?; // r_u = e⋅r + r_1 let mut r_u = C::ScalarField::zero(); if H { r_u = e * r + r1; } Ok(Self::Proof { R, u, r_u }) } fn verify( params: &Self::VerifierParams, transcript: &mut impl Transcript, cm: &C, proof: &Proof, ) -> Result<(), Error> { transcript.absorb_nonnative(cm); transcript.get_challenge(); // r_1 transcript.get_challenges(proof.u.len()); // d transcript.absorb_nonnative(&proof.R); let e = transcript.get_challenge(); Self::verify_with_challenge(params, e, cm, proof) } fn verify_with_challenge( params: &Self::VerifierParams, challenge: Self::Challenge, cm: &C, proof: &Proof, ) -> Result<(), Error> { if params.generators.len() < proof.u.len() { return Err(Error::PedersenParamsLen( params.generators.len(), proof.u.len(), )); } if !H && (!proof.r_u.is_zero()) { return Err(Error::BlindingNotZero); } let e = challenge; // check that: R + cm⋅e == h⋅r_u + let lhs = proof.R + cm.mul(e); // use msm_unchecked because we already ensured at the if that lengths match let mut rhs = C::msm_unchecked(¶ms.generators[..proof.u.len()], &proof.u); if H { rhs += params.h.mul(proof.r_u); } if lhs != rhs { return Err(Error::CommitmentVerificationFail); } Ok(()) } } pub type CF = <::BaseField as Field>::BasePrimeField; pub struct PedersenGadget where C: CurveGroup, GC: CurveVar>, { _cf: PhantomData>, _c: PhantomData, _gc: PhantomData, } use ark_r1cs_std::ToBitsGadget; impl PedersenGadget where C: CurveGroup, GC: CurveVar>, { pub fn commit( h: &GC, g: &[GC], v: &[Vec>>], r: &[Boolean>], ) -> Result { let mut res = GC::zero(); if H { res += h.scalar_mul_le(r.iter())?; } for (i, v_i) in v.iter().enumerate() { res += g[i].scalar_mul_le(v_i.to_bits_le()?.iter())?; } Ok(res) } } #[cfg(test)] mod tests { use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge}; use ark_ff::{BigInteger, PrimeField}; use ark_pallas::{constraints::GVar, Fq, Fr, Projective}; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget}; use ark_relations::r1cs::ConstraintSystem; use super::*; use crate::transcript::poseidon::poseidon_canonical_config; #[test] fn test_pedersen() { test_pedersen_opt::(); test_pedersen_opt::(); } fn test_pedersen_opt() { let mut rng = ark_std::test_rng(); let n: usize = 10; // setup params let (params, _) = Pedersen::::setup(&mut rng, n).unwrap(); let poseidon_config = poseidon_canonical_config::(); // init Prover's transcript let mut transcript_p = PoseidonSponge::::new(&poseidon_config); // init Verifier's transcript let mut transcript_v = PoseidonSponge::::new(&poseidon_config); let v: Vec = std::iter::repeat_with(|| Fr::rand(&mut rng)) .take(n) .collect(); // blinding factor let r: Fr = if hiding { Fr::rand(&mut rng) } else { Fr::zero() }; let cm = Pedersen::::commit(¶ms, &v, &r).unwrap(); let proof = Pedersen::::prove(¶ms, &mut transcript_p, &cm, &v, &r, None) .unwrap(); Pedersen::::verify(¶ms, &mut transcript_v, &cm, &proof).unwrap(); } #[test] fn test_pedersen_circuit() { test_pedersen_circuit_opt::(); test_pedersen_circuit_opt::(); } fn test_pedersen_circuit_opt() { let mut rng = ark_std::test_rng(); let n: usize = 8; // setup params let (params, _) = Pedersen::::setup(&mut rng, n).unwrap(); let v: Vec = std::iter::repeat_with(|| Fr::rand(&mut rng)) .take(n) .collect(); // blinding factor let r: Fr = if hiding { Fr::rand(&mut rng) } else { Fr::zero() }; let cm = Pedersen::::commit(¶ms, &v, &r).unwrap(); let v_bits: Vec> = v.iter().map(|val| val.into_bigint().to_bits_le()).collect(); let r_bits: Vec = r.into_bigint().to_bits_le(); // circuit let cs = ConstraintSystem::::new_ref(); // prepare inputs let vVar: Vec>> = v_bits .iter() .map(|val_bits| { Vec::>::new_witness(cs.clone(), || Ok(val_bits.clone())).unwrap() }) .collect(); let rVar = Vec::>::new_witness(cs.clone(), || Ok(r_bits)).unwrap(); let gVar = Vec::::new_witness(cs.clone(), || Ok(params.generators)).unwrap(); let hVar = GVar::new_witness(cs.clone(), || Ok(params.h)).unwrap(); let expected_cmVar = GVar::new_witness(cs.clone(), || Ok(cm)).unwrap(); // use the gadget let cmVar = PedersenGadget::::commit(&hVar, &gVar, &vVar, &rVar).unwrap(); cmVar.enforce_equal(&expected_cmVar).unwrap(); } }