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