sonobe/folding-schemes/src/folding/protogalaxy/traits.rs
folding-schemes/src/folding/protogalaxy/traits.rsRaw
1use ark_crypto_primitives::sponge::{constraints::AbsorbGadget, Absorb};
2use ark_ec::CurveGroup;
3use ark_ff::PrimeField;
4use ark_r1cs_std::{fields::fp::FpVar, uint8::UInt8, ToConstraintFieldGadget};
5use ark_relations::r1cs::SynthesisError;
6use ark_std::{cfg_iter, log2, rand::RngCore, One, Zero};
7use rayon::prelude::*;
8
9use super::{utils::pow_i, CommittedInstance, CommittedInstanceVar, Witness};
10use crate::{
11 arith::r1cs::{RelaxedR1CS, R1CS},
12 transcript::AbsorbNonNative,
13 Error,
14};
15
16// Implements the trait for absorbing ProtoGalaxy's CommittedInstance.
17impl<C: CurveGroup> Absorb for CommittedInstance<C>
18where
19 C::ScalarField: Absorb,
20{
21 fn to_sponge_bytes(&self, dest: &mut Vec<u8>) {
22 C::ScalarField::batch_to_sponge_bytes(&self.to_sponge_field_elements_as_vec(), dest);
23 }
24
25 fn to_sponge_field_elements<F: PrimeField>(&self, dest: &mut Vec<F>) {
26 self.phi
27 .to_native_sponge_field_elements_as_vec()
28 .to_sponge_field_elements(dest);
29 self.betas.to_sponge_field_elements(dest);
30 self.e.to_sponge_field_elements(dest);
31 self.x.to_sponge_field_elements(dest);
32 }
33}
34
35// Implements the trait for absorbing ProtoGalaxy's CommittedInstanceVar in-circuit.
36impl<C: CurveGroup> AbsorbGadget<C::ScalarField> for CommittedInstanceVar<C> {
37 fn to_sponge_bytes(&self) -> Result<Vec<UInt8<C::ScalarField>>, SynthesisError> {
38 FpVar::batch_to_sponge_bytes(&self.to_sponge_field_elements()?)
39 }
40
41 fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
42 Ok([
43 self.phi.to_constraint_field()?,
44 self.betas.to_sponge_field_elements()?,
45 self.e.to_sponge_field_elements()?,
46 self.x.to_sponge_field_elements()?,
47 ]
48 .concat())
49 }
50}
51
52impl<C: CurveGroup> RelaxedR1CS<C, Witness<C::ScalarField>, CommittedInstance<C>>
53 for R1CS<C::ScalarField>
54{
55 fn dummy_running_instance(&self) -> (Witness<C::ScalarField>, CommittedInstance<C>) {
56 let w_len = self.A.n_cols - 1 - self.l;
57 let w_dummy = Witness::new(vec![C::ScalarField::zero(); w_len]);
58 let u_dummy = CommittedInstance::<C>::dummy_running(self.l, log2(self.A.n_rows) as usize);
59 (w_dummy, u_dummy)
60 }
61
62 fn dummy_incoming_instance(&self) -> (Witness<C::ScalarField>, CommittedInstance<C>) {
63 let w_len = self.A.n_cols - 1 - self.l;
64 let w_dummy = Witness::new(vec![C::ScalarField::zero(); w_len]);
65 let u_dummy = CommittedInstance::<C>::dummy_incoming(self.l);
66 (w_dummy, u_dummy)
67 }
68
69 fn is_relaxed(_w: &Witness<C::ScalarField>, u: &CommittedInstance<C>) -> bool {
70 u.e != C::ScalarField::zero() || !u.betas.is_empty()
71 }
72
73 fn extract_z(w: &Witness<C::ScalarField>, u: &CommittedInstance<C>) -> Vec<C::ScalarField> {
74 [&[C::ScalarField::one()][..], &u.x, &w.w].concat()
75 }
76
77 fn check_error_terms(
78 _w: &Witness<C::ScalarField>,
79 u: &CommittedInstance<C>,
80 e: Vec<C::ScalarField>,
81 ) -> Result<(), Error> {
82 if u.betas.len() != log2(e.len()) as usize {
83 return Err(Error::NotSameLength(
84 "instance.betas.len()".to_string(),
85 u.betas.len(),
86 "log2(e.len())".to_string(),
87 log2(e.len()) as usize,
88 ));
89 }
90
91 let r = cfg_iter!(e)
92 .enumerate()
93 .map(|(i, e_i)| pow_i(i, &u.betas) * e_i)
94 .sum();
95 if u.e == r {
96 Ok(())
97 } else {
98 Err(Error::NotSatisfied)
99 }
100 }
101
102 fn sample<CS>(
103 &self,
104 _params: &CS::ProverParams,
105 _rng: impl RngCore,
106 ) -> Result<(Witness<C::ScalarField>, CommittedInstance<C>), Error>
107 where
108 CS: crate::commitment::CommitmentScheme<C, true>,
109 {
110 // Sampling a random pair of witness and committed instance is required
111 // for the zero-knowledge layer for ProtoGalaxy, which is not supported
112 // yet.
113 // Tracking issue: https://github.com/privacy-scaling-explorations/sonobe/issues/82
114 unimplemented!()
115 }
116}
117
118#[cfg(test)]
119pub mod tests {
120 use super::*;
121 use ark_bn254::{Fr, G1Projective as Projective};
122 use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
123 use ark_relations::r1cs::ConstraintSystem;
124 use ark_std::UniformRand;
125 use rand::Rng;
126
127 /// test that checks the native CommittedInstance.to_sponge_{bytes,field_elements}
128 /// vs the R1CS constraints version
129 #[test]
130 pub fn test_committed_instance_to_sponge_preimage() {
131 let mut rng = ark_std::test_rng();
132
133 let t = rng.gen::<u8>() as usize;
134 let io_len = rng.gen::<u8>() as usize;
135
136 let ci = CommittedInstance::<Projective> {
137 phi: Projective::rand(&mut rng),
138 betas: (0..t).map(|_| Fr::rand(&mut rng)).collect(),
139 e: Fr::rand(&mut rng),
140 x: (0..io_len).map(|_| Fr::rand(&mut rng)).collect(),
141 };
142
143 let bytes = ci.to_sponge_bytes_as_vec();
144 let field_elements = ci.to_sponge_field_elements_as_vec();
145
146 let cs = ConstraintSystem::<Fr>::new_ref();
147
148 let ciVar =
149 CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(ci.clone())).unwrap();
150 let bytes_var = ciVar.to_sponge_bytes().unwrap();
151 let field_elements_var = ciVar.to_sponge_field_elements().unwrap();
152
153 assert!(cs.is_satisfied().unwrap());
154
155 // check that the natively computed and in-circuit computed hashes match
156 assert_eq!(bytes_var.value().unwrap(), bytes);
157 assert_eq!(field_elements_var.value().unwrap(), field_elements);
158 }
159}