diff --git a/src/circuit/gadget/orchard_action/merkle.rs b/src/circuit/gadget/orchard_action/merkle.rs index 84fa18beb..6f5026234 100644 --- a/src/circuit/gadget/orchard_action/merkle.rs +++ b/src/circuit/gadget/orchard_action/merkle.rs @@ -675,3 +675,153 @@ impl { + root: Option, + leaf: (Option, Option), + merkle_path: Vec>, + _marker: PhantomData, + } + + impl + Circuit for MyCircuit + { + type Config = MerkleConfig; + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let advices = [ + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + ]; + + let perm = meta.permutation( + &advices + .iter() + .map(|advice| (*advice).into()) + .collect::>(), + ); + + // Fixed columns for the Sinsemilla generator lookup table + let lookup = ( + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + ); + + MerkleChip::::configure(meta, advices, lookup, perm) + } + + fn synthesize( + &self, + cs: &mut impl Assignment, + config: Self::Config, + ) -> Result<(), Error> { + let mut layouter = SingleChipLayouter::new(cs)?; + + // Load generator table + SinsemillaChip::::load( + config.sinsemilla_config.clone(), + &mut layouter, + )?; + + let merkle_chip = MerkleChip::::construct(config); + + let domain = SinsemillaHashDomains::MerkleCrh; + + merkle_chip.merkle_path_check( + layouter.namespace(|| ""), + &domain, + self.root, + self.leaf, + self.merkle_path.clone(), + ) + } + } + + #[test] + fn merkle_chip() { + // Initialize MerkleCRH HashDomain + let merkle_crh = HashDomain::new(MERKLE_CRH_PERSONALIZATION); + + // Choose a random leaf and position + let leaf = pallas::Base::rand(); + let pos = random::(); + let pos_bool = i2lebsp::<32>(pos); + + // Choose a path of random inner nodes + let path: Vec<_> = (0..(MERKLE_DEPTH_ORCHARD)) + .map(|_| pallas::Base::rand()) + .collect(); + + // Compute the root + let mut node = leaf; + for (l_star, (sibling, pos)) in path.iter().zip(pos_bool.iter()).enumerate() { + let (left, right) = if *pos { + (*sibling, node) + } else { + (node, *sibling) + }; + + let l_star = i2lebsp::<10>(l_star as u32); + let left: Vec<_> = left + .to_le_bits() + .iter() + .by_val() + .take(L_ORCHARD_BASE) + .collect(); + let right: Vec<_> = right + .to_le_bits() + .iter() + .by_val() + .take(L_ORCHARD_BASE) + .collect(); + + let mut message = l_star.to_vec(); + message.extend_from_slice(&left); + message.extend_from_slice(&right); + + node = merkle_crh.hash(message.into_iter()).unwrap(); + } + let root = node; + + let circuit = MyCircuit:: { + root: Some(root), + leaf: (Some(leaf), Some(pos)), + merkle_path: path.into_iter().map(Some).collect(), + _marker: PhantomData, + }; + + let prover = MockProver::run(11, &circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())) + } +}