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