arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/crypto-primitives/src/merkle_tree/constraints.rs
crypto-primitives/src/merkle_tree/constraints.rsRaw
  1use algebra::Field;
  2use r1cs_core::{ConstraintSystem, SynthesisError};
  3use r1cs_std::prelude::*;
  4use r1cs_std::boolean::AllocatedBit;
  5
  6use crate::merkle_tree::*;
  7use crate::crh::{FixedLengthCRH, FixedLengthCRHGadget};
  8
  9use std::borrow::Borrow;
 10
 11pub struct MerkleTreePathGadget<P, HGadget, ConstraintF>
 12where
 13    P: MerkleTreeConfig,
 14    HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
 15    ConstraintF: Field,
 16{
 17    path:    Vec<(HGadget::OutputGadget, HGadget::OutputGadget)>,
 18}
 19
 20impl<P, CRHGadget, ConstraintF> MerkleTreePathGadget<P, CRHGadget, ConstraintF>
 21where
 22    P: MerkleTreeConfig,
 23    ConstraintF: Field,
 24    CRHGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
 25{
 26    pub fn check_membership<CS: ConstraintSystem<ConstraintF>>(
 27        &self,
 28        cs: CS,
 29        parameters: &CRHGadget::ParametersGadget,
 30        root: &CRHGadget::OutputGadget,
 31        leaf: impl ToBytesGadget<ConstraintF>,
 32    ) -> Result<(), SynthesisError> {
 33    self.conditionally_check_membership(
 34            cs,
 35            parameters,
 36            root,
 37            leaf,
 38            &Boolean::Constant(true),
 39        )
 40    }
 41
 42    pub fn conditionally_check_membership<CS: ConstraintSystem<ConstraintF>>(
 43        &self,
 44        mut cs: CS,
 45        parameters: &CRHGadget::ParametersGadget,
 46        root: &CRHGadget::OutputGadget,
 47        leaf: impl ToBytesGadget<ConstraintF>,
 48        should_enforce: &Boolean,
 49    ) -> Result<(), SynthesisError> {
 50        assert_eq!(self.path.len(), P::HEIGHT - 1);
 51        // Check that the hash of the given leaf matches the leaf hash in the membership
 52        // proof.
 53        let leaf_bits = leaf.to_bytes(&mut cs.ns(|| "leaf_to_bytes"))?;
 54        let leaf_hash = CRHGadget::check_evaluation_gadget(
 55            cs.ns(|| "check_evaluation_gadget"),
 56            parameters,
 57            &leaf_bits,
 58        )?;
 59
 60        // Check if leaf is one of the bottom-most siblings.
 61        let leaf_is_left = AllocatedBit::alloc(&mut cs.ns(|| "leaf_is_left"), || {
 62            Ok(leaf_hash == self.path[0].0)
 63        })?
 64        .into();
 65        CRHGadget::OutputGadget::conditional_enforce_equal_or(
 66            &mut cs.ns(|| "check_leaf_is_left"),
 67            &leaf_is_left,
 68            &leaf_hash,
 69            &self.path[0].0,
 70            &self.path[0].1,
 71            should_enforce,
 72        )?;
 73
 74        // Check levels between leaf level and root.
 75        let mut previous_hash = leaf_hash;
 76        for (i, &(ref left_hash, ref right_hash)) in self.path.iter().enumerate() {
 77            // Check if the previous_hash matches the correct current hash.
 78            let previous_is_left =
 79                AllocatedBit::alloc(&mut cs.ns(|| format!("previous_is_left_{}", i)), || {
 80                    Ok(&previous_hash == left_hash)
 81                })?
 82                .into();
 83
 84            CRHGadget::OutputGadget::conditional_enforce_equal_or(
 85                &mut cs.ns(|| format!("check_equals_which_{}", i)),
 86                &previous_is_left,
 87                &previous_hash,
 88                left_hash,
 89                right_hash,
 90                should_enforce,
 91            )?;
 92
 93            previous_hash = hash_inner_node_gadget::<P::H, CRHGadget, ConstraintF, _>(
 94                &mut cs.ns(|| format!("hash_inner_node_{}", i)),
 95                parameters,
 96                left_hash,
 97                right_hash,
 98            )?;
 99        }
100
101        root.conditional_enforce_equal(
102            &mut cs.ns(|| "root_is_last"),
103            &previous_hash,
104            should_enforce,
105        )
106    }
107}
108
109pub(crate) fn hash_inner_node_gadget<H, HG, ConstraintF, CS>(
110    mut cs: CS,
111    parameters: &HG::ParametersGadget,
112    left_child: &HG::OutputGadget,
113    right_child: &HG::OutputGadget,
114) -> Result<HG::OutputGadget, SynthesisError>
115where
116    ConstraintF: Field,
117    CS: ConstraintSystem<ConstraintF>,
118    H: FixedLengthCRH,
119    HG: FixedLengthCRHGadget<H, ConstraintF>,
120{
121    let left_bytes = left_child.to_bytes(&mut cs.ns(|| "left_to_bytes"))?;
122    let right_bytes = right_child.to_bytes(&mut cs.ns(|| "right_to_bytes"))?;
123    let mut bytes = left_bytes;
124    bytes.extend_from_slice(&right_bytes);
125
126    HG::check_evaluation_gadget(cs, parameters, &bytes)
127}
128
129impl<P, HGadget, ConstraintF> AllocGadget<MerkleTreePath<P>, ConstraintF>
130    for MerkleTreePathGadget<P, HGadget, ConstraintF>
131where
132    P: MerkleTreeConfig,
133    HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
134    ConstraintF: Field,
135{
136    fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
137        mut cs: CS,
138        value_gen: F,
139    ) -> Result<Self, SynthesisError>
140    where
141        F: FnOnce() -> Result<T, SynthesisError>,
142        T: Borrow<MerkleTreePath<P>>,
143    {
144        let mut path = Vec::new();
145        for (i, &(ref l, ref r)) in value_gen()?.borrow().path.iter().enumerate() {
146            let l_hash =
147                HGadget::OutputGadget::alloc(&mut cs.ns(|| format!("l_child_{}", i)), || {
148                    Ok(l.clone())
149                })?;
150            let r_hash =
151                HGadget::OutputGadget::alloc(&mut cs.ns(|| format!("r_child_{}", i)), || {
152                    Ok(r.clone())
153                })?;
154            path.push((l_hash, r_hash));
155        }
156        Ok(MerkleTreePathGadget {
157            path,
158        })
159    }
160
161    fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
162        mut cs: CS,
163        value_gen: F,
164    ) -> Result<Self, SynthesisError>
165    where
166        F: FnOnce() -> Result<T, SynthesisError>,
167        T: Borrow<MerkleTreePath<P>>,
168    {
169        let mut path = Vec::new();
170        for (i, &(ref l, ref r)) in value_gen()?.borrow().path.iter().enumerate() {
171            let l_hash = HGadget::OutputGadget::alloc_input(
172                &mut cs.ns(|| format!("l_child_{}", i)),
173                || Ok(l.clone()),
174            )?;
175            let r_hash = HGadget::OutputGadget::alloc_input(
176                &mut cs.ns(|| format!("r_child_{}", i)),
177                || Ok(r.clone()),
178            )?;
179            path.push((l_hash, r_hash));
180        }
181
182        Ok(MerkleTreePathGadget {
183            path,
184        })
185    }
186}
187
188#[cfg(test)]
189mod test {
190    use std::rc::Rc;
191
192    use crate::{
193        crh::{
194            pedersen::{PedersenCRH, PedersenWindow},
195            pedersen::constraints::PedersenCRHGadget, 
196            FixedLengthCRH,
197            FixedLengthCRHGadget,
198        },
199        merkle_tree::*,
200    };
201    use algebra::{
202        curves::jubjub::JubJubAffine as JubJub,
203        fields::jubjub::fq::Fq,
204    };
205    use rand::SeedableRng;
206    use rand_xorshift::XorShiftRng;
207    use r1cs_core::ConstraintSystem;
208
209    use super::*;
210    use r1cs_std::{
211        groups::curves::twisted_edwards::jubjub::JubJubGadget,
212        test_constraint_system::TestConstraintSystem,
213    };
214
215    #[derive(Clone)]
216    pub(super) struct Window4x256;
217    impl PedersenWindow for Window4x256 {
218        const WINDOW_SIZE: usize = 4;
219        const NUM_WINDOWS: usize = 256;
220    }
221
222    type H = PedersenCRH<JubJub, Window4x256>;
223    type HG = PedersenCRHGadget<JubJub, Fq, JubJubGadget>;
224
225    struct JubJubMerkleTreeParams;
226    
227    impl MerkleTreeConfig for JubJubMerkleTreeParams {
228        const HEIGHT: usize = 32;
229        type H = H;
230    }
231
232    type JubJubMerkleTree = MerkleHashTree<JubJubMerkleTreeParams>;
233
234    fn generate_merkle_tree(leaves: &[[u8; 30]], use_bad_root: bool) -> () {
235        let mut rng = XorShiftRng::seed_from_u64(9174123u64);
236
237        let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
238        let tree = JubJubMerkleTree::new(crh_parameters.clone(), leaves).unwrap();
239        let root = tree.root();
240        let mut satisfied = true;
241        for (i, leaf) in leaves.iter().enumerate() {
242            let mut cs = TestConstraintSystem::<Fq>::new();
243            let proof = tree.generate_proof(i, &leaf).unwrap();
244            assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
245
246            // Allocate Merkle Tree Root
247            let root = <HG as FixedLengthCRHGadget<H, _>>::OutputGadget::alloc(&mut cs.ns(|| format!("new_digest_{}", i)), || {
248                if use_bad_root {
249                    Ok(<H as FixedLengthCRH>::Output::default())
250                } else {
251                    Ok(root)
252                }
253            })
254            .unwrap();
255
256            let constraints_from_digest = cs.num_constraints();
257            println!("constraints from digest: {}", constraints_from_digest);
258
259            // Allocate Parameters for CRH
260            let crh_parameters =
261                <HG as FixedLengthCRHGadget<H, Fq>>::ParametersGadget::alloc(
262                    &mut cs.ns(|| format!("new_parameters_{}", i)),
263                    || Ok(crh_parameters.clone()),
264                )
265                .unwrap();
266
267            let constraints_from_parameters = cs.num_constraints() - constraints_from_digest;
268            println!(
269                "constraints from parameters: {}",
270                constraints_from_parameters
271            );
272
273            // Allocate Leaf
274            let leaf_g = UInt8::constant_vec(leaf);
275                
276            let constraints_from_leaf =
277                cs.num_constraints() - constraints_from_parameters - constraints_from_digest;
278            println!("constraints from leaf: {}", constraints_from_leaf);
279
280            // Allocate Merkle Tree Path
281            let cw = MerkleTreePathGadget::<_, HG, _>::alloc(&mut cs.ns(|| format!("new_witness_{}", i)), || {
282                Ok(proof)
283            })
284            .unwrap();
285
286            let constraints_from_path = cs.num_constraints()
287                - constraints_from_parameters
288                - constraints_from_digest
289                - constraints_from_leaf;
290            println!("constraints from path: {}", constraints_from_path);
291            let leaf_g: &[UInt8] = leaf_g.as_slice();
292            cw.check_membership(
293                &mut cs.ns(|| format!("new_witness_check_{}", i)),
294                &crh_parameters,
295                &root,
296                &leaf_g,
297            )
298            .unwrap();
299            if !cs.is_satisfied() {
300                satisfied = false;
301                println!(
302                    "Unsatisfied constraint: {}",
303                    cs.which_is_unsatisfied().unwrap()
304                );
305            }
306            let setup_constraints = constraints_from_leaf
307                + constraints_from_digest
308                + constraints_from_parameters
309                + constraints_from_path;
310            println!(
311                "number of constraints: {}",
312                cs.num_constraints() - setup_constraints
313            );
314        }
315
316        assert!(satisfied);
317    }
318
319    #[test]
320    fn good_root_test() {
321        let mut leaves = Vec::new();
322        for i in 0..4u8 {
323            let input = [i ; 30];
324            leaves.push(input);
325        }
326        generate_merkle_tree(&leaves, false);
327    }
328
329    #[should_panic]
330    #[test]
331    fn bad_root_test() {
332        let mut leaves = Vec::new();
333        for i in 0..4u8 {
334            let input = [i ; 30];
335            leaves.push(input);
336        }
337        generate_merkle_tree(&leaves, true);
338    }
339}