diff --git a/src/circuit/gadget/sinsemilla/merkle.rs b/src/circuit/gadget/sinsemilla/merkle.rs index 20df5a54..85db6c22 100644 --- a/src/circuit/gadget/sinsemilla/merkle.rs +++ b/src/circuit/gadget/sinsemilla/merkle.rs @@ -7,10 +7,12 @@ use pasta_curves::arithmetic::CurveAffine; use super::{HashDomains, SinsemillaInstructions}; use crate::{ - circuit::gadget::utilities::{cond_swap::CondSwapInstructions, UtilitiesInstructions}, + circuit::gadget::utilities::{ + cond_swap::CondSwapInstructions, transpose_option_array, UtilitiesInstructions, + }, spec::i2lebsp, }; -use std::{convert::TryInto, iter}; +use std::iter; mod chip; @@ -28,14 +30,15 @@ pub trait MerkleInstructions< + UtilitiesInstructions + Chip { - /// Compute MerkleCRH for a given `layer`. The root is at `layer 0`, and the - /// leaves are at `layer MERKLE_DEPTH_ORCHARD` = `layer 32`. + /// Compute MerkleCRH for a given `layer`. The hash that computes the root + /// is at layer 0, and the hashes that are applied to two leaves are at + /// layer `MERKLE_DEPTH_ORCHARD - 1` = layer 31. #[allow(non_snake_case)] fn hash_layer( &self, layouter: impl Layouter, Q: C, - l_star: usize, + l: usize, left: Self::Var, right: Self::Var, ) -> Result; @@ -55,6 +58,7 @@ pub struct MerklePath< chip_2: MerkleChip, domain: MerkleChip::HashDomains, leaf_pos: Option, + // The Merkle path is ordered from leaves to root. path: Option<[C::Base; PATH_LENGTH]>, } @@ -82,41 +86,26 @@ where .chain(iter::repeat(self.chip_1.clone()).take(PATH_LENGTH / 2)) .chain(iter::repeat(self.chip_2.clone())); - let path = if let Some(path) = self.path { - path.iter() - .map(|node| Some(*node)) - .collect::>() - .try_into() - .unwrap() - } else { - [None; PATH_LENGTH] - }; + // The Merkle path is ordered from leaves to root, which is consistent with the + // little-endian representation of `pos` below. + let path = transpose_option_array(self.path); // Get position as a PATH_LENGTH-bit bitstring (little-endian bit order). let pos: [Option; PATH_LENGTH] = { let pos: Option<[bool; PATH_LENGTH]> = self.leaf_pos.map(|pos| i2lebsp(pos as u64)); - let pos: [Option; PATH_LENGTH] = if let Some(pos) = pos { - pos.iter() - .map(|pos| Some(*pos)) - .collect::>() - .try_into() - .unwrap() - } else { - [None; PATH_LENGTH] - }; - pos + transpose_option_array(pos) }; let Q = self.domain.Q(); let mut node = leaf; - for (l_star, ((sibling, pos), chip)) in path.iter().zip(pos.iter()).zip(chips).enumerate() { - // `l_star` = MERKLE_DEPTH_ORCHARD - layer - 1, which is the index obtained from + for (l, ((sibling, pos), chip)) in path.iter().zip(pos.iter()).zip(chips).enumerate() { + // `l` = MERKLE_DEPTH_ORCHARD - layer - 1, which is the index obtained from // enumerating this Merkle path (going from leaf to root). // For example, when `layer = 31` (the first sibling on the Merkle path), - // we have `l_star` = 32 - 31 - 1 = 0. + // we have `l` = 32 - 31 - 1 = 0. // On the other hand, when `layer = 0` (the final sibling on the Merkle path), - // we have `l_star` = 32 - 0 - 1 = 31. + // we have `l` = 32 - 0 - 1 = 31. let pair = { let pair = (node, *sibling); @@ -125,12 +114,12 @@ where }; // Each `hash_layer` consists of 52 Sinsemilla words: - // - l_star (10 bits) = 1 word + // - l (10 bits) = 1 word // - left (255 bits) || right (255 bits) = 51 words (510 bits) node = chip.hash_layer( - layouter.namespace(|| format!("hash l_star {}", l_star)), + layouter.namespace(|| format!("hash l {}", l)), Q, - l_star, + l, pair.0, pair.1, )?; @@ -296,14 +285,14 @@ pub mod tests { // Compute the root let mut node = leaf; - for (l_star, (sibling, pos)) in path.iter().zip(pos_bool.iter()).enumerate() { + for (l, (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 u64); + let l_star = i2lebsp::<10>(l as u64); let left: Vec<_> = left .to_le_bits() .iter() diff --git a/src/circuit/gadget/sinsemilla/merkle/chip.rs b/src/circuit/gadget/sinsemilla/merkle/chip.rs index fc8aa0cf..a0ce10e9 100644 --- a/src/circuit/gadget/sinsemilla/merkle/chip.rs +++ b/src/circuit/gadget/sinsemilla/merkle/chip.rs @@ -27,7 +27,7 @@ use std::{array, convert::TryInto}; #[derive(Clone, Debug)] pub struct MerkleConfig { advices: [Column; 5], - l_star_plus1: Column, + l_plus_1: Column, perm: Permutation, lookup_config: LookupRangeCheckConfig, pub(super) cond_swap_config: CondSwapConfig, @@ -74,7 +74,7 @@ impl MerkleChip { // - Disabling the entire decomposition gate (when set to zero) // (i.e. replacing a Selector). - let l_star_plus1 = meta.fixed_column(); + let l_plus_1 = meta.fixed_column(); // Check that pieces have been decomposed correctly for Sinsemilla hash. // @@ -88,7 +88,7 @@ impl MerkleChip { // 250 bits, 20 bits, and 250 bits respectively. // meta.create_gate("Decomposition check", |meta| { - let l_star_plus1_whole = meta.query_fixed(l_star_plus1, Rotation::cur()); + let l_plus_1_whole = meta.query_fixed(l_plus_1, Rotation::cur()); let two_pow_5 = pallas::Base::from_u64(1 << 5); let two_pow_10 = two_pow_5.square(); @@ -111,7 +111,7 @@ impl MerkleChip { // a_0 = a - (a_1 * 2^10) let a_0 = a_whole - a_1.clone() * pallas::Base::from_u64(1 << 10); let l_star_check = - a_0 - (l_star_plus1_whole.clone() - Expression::Constant(pallas::Base::one())); + a_0 - (l_plus_1_whole.clone() - Expression::Constant(pallas::Base::one())); // b = b_0||b_1||b_2 // = (bits 240..=249 of left) || (bits 250..=254 of left) || (bits 0..=4 of right) @@ -143,12 +143,12 @@ impl MerkleChip { let right_check = b_2 + c_whole * two_pow_5 - right_node; array::IntoIter::new([l_star_check, left_check, right_check, b1_b2_check]) - .map(move |poly| l_star_plus1_whole.clone() * poly) + .map(move |poly| l_plus_1_whole.clone() * poly) }); MerkleConfig { advices, - l_star_plus1, + l_plus_1, perm: sinsemilla_config.perm.clone(), cond_swap_config, lookup_config, @@ -268,13 +268,13 @@ impl MerkleInstructions( + option_array: Option<[T; LEN]>, +) -> [Option; LEN] { + if let Some(arr) = option_array { + arr.iter() + .map(|el| Some(*el)) + .collect::>() + .try_into() + .unwrap() + } else { + [None; LEN] + } +}