diff --git a/src/circuit/gadget/orchard_action/merkle.rs b/src/circuit/gadget/orchard_action/merkle.rs index 4be60d49c..eb6871229 100644 --- a/src/circuit/gadget/orchard_action/merkle.rs +++ b/src/circuit/gadget/orchard_action/merkle.rs @@ -77,6 +77,109 @@ pub struct MerkleChip< _marker: PhantomData, } +impl + MerkleChip +{ + pub fn configure( + meta: &mut ConstraintSystem, + advices: [Column; 5], + lookup: (Column, Column, Column), + perm: Permutation, + ) -> MerkleConfig { + let cond_swap_config = + CondSwapChip::configure(meta, advices[..5].try_into().unwrap(), perm.clone()); + let sinsemilla_config = SinsemillaChip::::configure( + meta, + advices[..5].try_into().unwrap(), + lookup, + perm.clone(), + ); + + let a = advices[0]; + let b = advices[1]; + let c = advices[2]; + let left = advices[3]; + let right = advices[4]; + + let q_merkle = meta.selector(); + let l_star = meta.fixed_column(); + + // Check that pieces have been decomposed correctly for Sinsemilla hash. + // + // + // `a = a_0||a_1` = `l_star` || (bits 0..=239 of `left`) + // `b = b_0||b_1` = (bits 240..=254 of `left`) || (bits 0..=234 of `right`) + // `c = bits 235..=254 of `right` + meta.create_gate("Merkle path validity check", |meta| { + let a_whole = meta.query_advice(a, Rotation::cur()); + let b_whole = meta.query_advice(b, Rotation::cur()); + let c_whole = meta.query_advice(c, Rotation::cur()); + let left_node = meta.query_advice(left, Rotation::cur()); + let right_node = meta.query_advice(right, Rotation::cur()); + + let a_0 = meta.query_advice(a, Rotation::next()); + let a_1 = meta.query_advice(b, Rotation::next()); + let b_0 = meta.query_advice(c, Rotation::next()); + let b_1 = meta.query_advice(left, Rotation::next()); + + let l_star_plus1 = meta.query_fixed(l_star, Rotation::cur()); + + // a = a_0||a_1` = `l_star` (10 bits) || (bits 0..=239 of `left`) + // Check that a = a_0 || a_1 + let a_check = a_0.clone() + a_1.clone() * C::Base::from_u64(1 << 10) - a_whole; + + // Check that a_0 = l_star + let l_star_check = a_0 - (l_star_plus1.clone() - Expression::Constant(C::Base::one())); + + // `b = b_0||b_1` = (bits 240..=254 of `left`) || (bits 0..=234 of `right`) + // Check that b = b_0 (15 bits) || b_1 (235 bits) + let b_check = b_0.clone() + b_1.clone() * C::Base::from_u64(1 << 15) - b_whole; + + // Check that left = a_1 (240 bits) || b_0 (15 bits) + let two_pow_240 = C::Base::from_u128(1 << 120).square(); + let left_check = a_1 + b_0 * two_pow_240 - left_node; + + // Check that right = b_1 (235 bits) || c (20 bits) + let two_pow_235 = C::Base::from_u64(1 << 47).pow(&[5, 0, 0, 0]); + let right_check = b_1 + c_whole * two_pow_235 - right_node; + + [a_check, l_star_check, b_check, left_check, right_check] + .iter() + .map(|poly| l_star_plus1.clone() * poly.clone()) + .collect() + }); + + // Check that root is recovered at the end of Merkle hash. + meta.create_gate("Root check", |meta| { + let q_merkle = meta.query_selector(q_merkle, Rotation::cur()); + let root = meta.query_advice(a, Rotation::cur()); + let computed_root = meta.query_advice(b, Rotation::cur()); + + vec![q_merkle * (root - computed_root)] + }); + + MerkleConfig { + q_merkle, + a, + b, + c, + left, + right, + l_star, + perm, + cond_swap_config, + sinsemilla_config, + } + } + + pub fn construct(config: MerkleConfig) -> Self { + MerkleChip { + config, + _marker: PhantomData, + } + } +} + impl Chip for MerkleChip {