ark_sponge/constraints/
absorb.rs

1use ark_ec::{ModelParameters, SWModelParameters, TEModelParameters};
2use ark_ff::{Field, PrimeField};
3use ark_r1cs_std::bits::boolean::Boolean;
4use ark_r1cs_std::bits::uint8::UInt8;
5use ark_r1cs_std::fields::fp::FpVar;
6use ark_r1cs_std::fields::{FieldOpsBounds, FieldVar};
7use ark_r1cs_std::groups::curves::short_weierstrass::{
8    AffineVar as SWAffineVar, ProjectiveVar as SWProjectiveVar,
9};
10use ark_r1cs_std::groups::curves::twisted_edwards::AffineVar as TEAffineVar;
11use ark_r1cs_std::{ToBytesGadget, ToConstraintFieldGadget};
12use ark_relations::r1cs::SynthesisError;
13use ark_std::vec;
14use ark_std::vec::Vec;
15/// An interface for objects that can be absorbed by a `CryptographicSpongeVar` whose constraint field
16/// is `CF`.
17pub trait AbsorbGadget<F: PrimeField> {
18    /// Converts the object into a list of bytes that can be absorbed by a `CryptographicSpongeVar`.
19    /// return the list.
20    fn to_sponge_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError>;
21
22    /// Specifies the conversion into a list of bytes for a batch.
23    fn batch_to_sponge_bytes(batch: &[Self]) -> Result<Vec<UInt8<F>>, SynthesisError>
24    where
25        Self: Sized,
26    {
27        let mut result = Vec::new();
28        for item in batch {
29            result.append(&mut (item.to_sponge_bytes()?))
30        }
31        Ok(result)
32    }
33
34    /// Converts the object into field elements that can be absorbed by a `CryptographicSpongeVar`.
35    fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<F>>, SynthesisError>;
36
37    /// Specifies the conversion into a list of field elements for a batch.
38    fn batch_to_sponge_field_elements(batch: &[Self]) -> Result<Vec<FpVar<F>>, SynthesisError>
39    where
40        Self: Sized,
41    {
42        let mut output = Vec::new();
43        for absorbable in batch {
44            output.append(&mut absorbable.to_sponge_field_elements()?);
45        }
46
47        Ok(output)
48    }
49}
50
51impl<F: PrimeField> AbsorbGadget<F> for UInt8<F> {
52    fn to_sponge_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
53        Ok(vec![self.clone()])
54    }
55
56    fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<F>>, SynthesisError> {
57        vec![self.clone()].to_constraint_field()
58    }
59
60    fn batch_to_sponge_field_elements(batch: &[Self]) -> Result<Vec<FpVar<F>>, SynthesisError> {
61        // It's okay to allocate as constant because at circuit-generation time,
62        // the length must be statically known (it cannot vary with the variable assignments).
63        let mut bytes = UInt8::constant_vec((batch.len() as u64).to_le_bytes().as_ref());
64        bytes.extend_from_slice(batch);
65        bytes.to_constraint_field()
66    }
67}
68
69impl<F: PrimeField> AbsorbGadget<F> for Boolean<F> {
70    fn to_sponge_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
71        self.to_bytes()
72    }
73
74    fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<F>>, SynthesisError> {
75        Ok(vec![FpVar::from(self.clone())])
76    }
77}
78
79impl<F: PrimeField> AbsorbGadget<F> for FpVar<F> {
80    fn to_sponge_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
81        self.to_bytes()
82    }
83
84    fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<F>>, SynthesisError> {
85        Ok(vec![self.clone()])
86    }
87
88    fn batch_to_sponge_field_elements(batch: &[Self]) -> Result<Vec<FpVar<F>>, SynthesisError> {
89        Ok(batch.to_vec())
90    }
91}
92
93macro_rules! impl_absorbable_group {
94    ($group:ident, $params:ident) => {
95        impl<P, F> AbsorbGadget<<P::BaseField as Field>::BasePrimeField> for $group<P, F>
96        where
97            P: $params,
98            F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
99            for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
100            F: ToConstraintFieldGadget<<P::BaseField as Field>::BasePrimeField>,
101        {
102            fn to_sponge_bytes(
103                &self,
104            ) -> Result<Vec<UInt8<<P::BaseField as Field>::BasePrimeField>>, SynthesisError> {
105                self.to_constraint_field()?.to_sponge_bytes()
106            }
107
108            fn to_sponge_field_elements(
109                &self,
110            ) -> Result<Vec<FpVar<<P::BaseField as Field>::BasePrimeField>>, SynthesisError> {
111                self.to_constraint_field()
112            }
113        }
114    };
115}
116
117impl_absorbable_group!(TEAffineVar, TEModelParameters);
118impl_absorbable_group!(SWAffineVar, SWModelParameters);
119
120impl<P, F> AbsorbGadget<<P::BaseField as Field>::BasePrimeField> for SWProjectiveVar<P, F>
121where
122    P: SWModelParameters,
123    F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
124    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
125    F: ToConstraintFieldGadget<<P::BaseField as Field>::BasePrimeField>,
126{
127    fn to_sponge_bytes(
128        &self,
129    ) -> Result<
130        Vec<UInt8<<<P as ModelParameters>::BaseField as Field>::BasePrimeField>>,
131        SynthesisError,
132    > {
133        self.to_bytes()
134    }
135
136    fn to_sponge_field_elements(
137        &self,
138    ) -> Result<
139        Vec<FpVar<<<P as ModelParameters>::BaseField as Field>::BasePrimeField>>,
140        SynthesisError,
141    > {
142        self.to_affine()?.to_sponge_field_elements()
143    }
144}
145
146impl<F: PrimeField, A: AbsorbGadget<F>> AbsorbGadget<F> for &[A] {
147    fn to_sponge_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
148        A::batch_to_sponge_bytes(self)
149    }
150
151    fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<F>>, SynthesisError> {
152        A::batch_to_sponge_field_elements(self)
153    }
154}
155
156impl<F: PrimeField, A: AbsorbGadget<F>> AbsorbGadget<F> for Vec<A> {
157    fn to_sponge_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
158        self.as_slice().to_sponge_bytes()
159    }
160
161    fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<F>>, SynthesisError> {
162        self.as_slice().to_sponge_field_elements()
163    }
164}
165
166impl<F: PrimeField, A: AbsorbGadget<F>> AbsorbGadget<F> for Option<A> {
167    fn to_sponge_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
168        let mut output = Vec::new();
169        output.append(&mut (Boolean::Constant(self.is_some()).to_sponge_bytes()?));
170        if let Some(item) = self {
171            output.append(&mut (item.to_sponge_bytes()?))
172        }
173        Ok(output)
174    }
175
176    fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<F>>, SynthesisError> {
177        let mut output = vec![FpVar::from(Boolean::constant(self.is_some()))];
178        if let Some(absorbable) = self.as_ref() {
179            output.append(&mut absorbable.to_sponge_field_elements()?);
180        }
181        Ok(output)
182    }
183}
184
185impl<F: PrimeField, A: AbsorbGadget<F>> AbsorbGadget<F> for &A {
186    fn to_sponge_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
187        (*self).to_sponge_bytes()
188    }
189
190    fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<F>>, SynthesisError> {
191        (*self).to_sponge_field_elements()
192    }
193}
194
195/// Individually absorbs each element in a comma-separated list of [`Absorbable`]s into a sponge.
196/// Format is `absorb!(s, a_0, a_1, ..., a_n)`, where `s` is a mutable reference to a sponge
197/// and each `a_i` implements `AbsorbableVar`.
198#[macro_export]
199macro_rules! absorb_gadget {
200    ($sponge:expr, $($absorbable:expr),+ ) => {
201        $(
202            CryptographicSpongeVar::absorb($sponge, &$absorbable)?;
203        )+
204    };
205}
206
207/// Quickly convert a list of different [`Absorbable`]s into sponge field elements.
208#[macro_export]
209macro_rules! collect_sponge_field_elements_gadget {
210    ($head:expr $(, $tail:expr)* ) => {
211        {
212            let mut output = AbsorbGadget::to_sponge_field_elements(&$head)?;
213            $(
214                output.append(&mut AbsorbGadget::to_sponge_field_elements(&$tail)?);
215            )*
216
217            Ok(output)
218        }
219    };
220}
221
222#[cfg(test)]
223mod tests {
224    use crate::constraints::AbsorbGadget;
225    use crate::Absorb;
226    use ark_r1cs_std::alloc::AllocVar;
227    use ark_r1cs_std::fields::fp::FpVar;
228    use ark_r1cs_std::uint8::UInt8;
229    use ark_r1cs_std::R1CSVar;
230    use ark_relations::r1cs::ConstraintSystem;
231    use ark_relations::*;
232    use ark_std::{test_rng, UniformRand};
233    use ark_test_curves::bls12_381::Fr;
234
235    #[test]
236    fn consistency_check() {
237        // test constraint is consistent with native
238        let cs = ConstraintSystem::<Fr>::new_ref();
239        let mut rng = test_rng();
240        // uint8
241        let data = vec![0u8, 1u8, 2u8, 3u8, 4u8, 5u8];
242        let data_var = UInt8::new_input_vec(ns!(cs, "u8data"), &data).unwrap();
243
244        let native_bytes = data.to_sponge_bytes_as_vec();
245        let constraint_bytes = data_var.to_sponge_bytes().unwrap();
246
247        assert_eq!(constraint_bytes.value().unwrap(), native_bytes);
248
249        // field
250
251        let data: Vec<_> = (0..10).map(|_| Fr::rand(&mut rng)).collect();
252        let data_var: Vec<_> = data
253            .iter()
254            .map(|item| FpVar::new_input(ns!(cs, "fpdata"), || Ok(*item)).unwrap())
255            .collect();
256
257        let native_bytes = data.to_sponge_bytes_as_vec();
258        let constraint_bytes = data_var.to_sponge_bytes().unwrap();
259        assert_eq!(constraint_bytes.value().unwrap(), native_bytes);
260
261        assert!(cs.is_satisfied().unwrap())
262    }
263}