From 4d1cd2651a0112c7559fea63f04a939eb3ef408c Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sat, 24 Jul 2021 23:54:54 +0800 Subject: [PATCH] Return full running sum [z_0, ..., z_W] from lookup_range_check and decompose_running_sum. Previously, these two helpers were returning different outputs. They have now been standardised to return only the full running sum. Note the z_0 is the original element being decomposed by the helper. --- src/circuit/gadget/ecc/chip.rs | 6 ++-- src/circuit/gadget/ecc/chip/mul_fixed.rs | 12 ++------ .../ecc/chip/mul_fixed/base_field_elem.rs | 13 ++++---- .../gadget/ecc/chip/mul_fixed/short.rs | 4 +-- src/circuit/gadget/sinsemilla/commit_ivk.rs | 6 ++-- src/circuit/gadget/sinsemilla/note_commit.rs | 12 +++++--- .../gadget/utilities/decompose_running_sum.rs | 19 +++++++----- .../gadget/utilities/lookup_range_check.rs | 30 +++++++++++-------- 8 files changed, 54 insertions(+), 48 deletions(-) diff --git a/src/circuit/gadget/ecc/chip.rs b/src/circuit/gadget/ecc/chip.rs index 5e0ca2eb..d4ddcc81 100644 --- a/src/circuit/gadget/ecc/chip.rs +++ b/src/circuit/gadget/ecc/chip.rs @@ -291,7 +291,7 @@ pub struct EccScalarFixed { pub struct EccScalarFixedShort { magnitude: CellValue, sign: CellValue, - running_sum: ArrayVec, { constants::NUM_WINDOWS_SHORT }>, + running_sum: ArrayVec, { constants::NUM_WINDOWS_SHORT + 1 }>, } /// A base field element used for fixed-base scalar multiplication. @@ -300,13 +300,13 @@ pub struct EccScalarFixedShort { /// for element α = a_0 + (2^3) a_1 + ... + (2^{3(n-1)}) a_{n-1}. /// Each `a_i` is in the range [0..2^3). /// -/// `windows` = [z_1, ..., z_85], where we expect z_85 = 0. +/// `running_sum` = [z_0, ..., z_85], where we expect z_85 = 0. /// Since z_0 is initialized as the scalar α, we store it as /// `base_field_elem`. #[derive(Clone, Debug)] struct EccBaseFieldElemFixed { base_field_elem: CellValue, - running_sum: ArrayVec, { constants::NUM_WINDOWS }>, + running_sum: ArrayVec, { constants::NUM_WINDOWS + 1 }>, } impl EccBaseFieldElemFixed { diff --git a/src/circuit/gadget/ecc/chip/mul_fixed.rs b/src/circuit/gadget/ecc/chip/mul_fixed.rs index 2c6c958d..90c31aae 100644 --- a/src/circuit/gadget/ecc/chip/mul_fixed.rs +++ b/src/circuit/gadget/ecc/chip/mul_fixed.rs @@ -511,16 +511,8 @@ impl ScalarFixed { .collect::>() }; match self { - Self::BaseFieldElem(scalar) => { - let mut zs = vec![scalar.base_field_elem]; - zs.extend_from_slice(&scalar.running_sum); - running_sum_to_windows(zs) - } - Self::Short(scalar) => { - let mut zs = vec![scalar.magnitude]; - zs.extend_from_slice(&scalar.running_sum); - running_sum_to_windows(zs) - } + Self::BaseFieldElem(scalar) => running_sum_to_windows(scalar.running_sum.to_vec()), + Self::Short(scalar) => running_sum_to_windows(scalar.running_sum.to_vec()), Self::FullWidth(scalar) => scalar .windows .iter() diff --git a/src/circuit/gadget/ecc/chip/mul_fixed/base_field_elem.rs b/src/circuit/gadget/ecc/chip/mul_fixed/base_field_elem.rs index b4e79501..201624da 100644 --- a/src/circuit/gadget/ecc/chip/mul_fixed/base_field_elem.rs +++ b/src/circuit/gadget/ecc/chip/mul_fixed/base_field_elem.rs @@ -166,7 +166,7 @@ impl Config { // Decompose scalar let scalar = { - let (base_field_elem, running_sum) = self.running_sum_config.copy_decompose( + let running_sum = self.running_sum_config.copy_decompose( &mut region, offset, scalar, @@ -175,7 +175,7 @@ impl Config { constants::NUM_WINDOWS, )?; EccBaseFieldElemFixed { - base_field_elem, + base_field_elem: running_sum[0], running_sum: (*running_sum).as_slice().try_into().unwrap(), } }; @@ -240,9 +240,9 @@ impl Config { // => z_13_alpha_0_prime = 0 // let (alpha, running_sum) = (scalar.base_field_elem, &scalar.running_sum); - let z_43_alpha = running_sum[42]; - let z_44_alpha = running_sum[43]; - let z_84_alpha = running_sum[83]; + let z_43_alpha = running_sum[43]; + let z_44_alpha = running_sum[44]; + let z_84_alpha = running_sum[84]; // α_0 = α - z_84_alpha * 2^252 let alpha_0 = alpha @@ -260,12 +260,13 @@ impl Config { let t_p = pallas::Base::from_u128(T_P); alpha_0 + two_pow_130 - t_p }); - let (alpha_0_prime, zs) = self.lookup_config.witness_check( + let zs = self.lookup_config.witness_check( layouter.namespace(|| "Lookup range check alpha_0 + 2^130 - t_p"), alpha_0_prime, 13, false, )?; + let alpha_0_prime = zs[0]; (alpha_0_prime, zs[13]) }; diff --git a/src/circuit/gadget/ecc/chip/mul_fixed/short.rs b/src/circuit/gadget/ecc/chip/mul_fixed/short.rs index f64fe1c9..020bcf7b 100644 --- a/src/circuit/gadget/ecc/chip/mul_fixed/short.rs +++ b/src/circuit/gadget/ecc/chip/mul_fixed/short.rs @@ -80,7 +80,7 @@ impl Config { let (magnitude, sign) = magnitude_sign; // Decompose magnitude - let (magnitude, running_sum) = self.running_sum_config.copy_decompose( + let running_sum = self.running_sum_config.copy_decompose( region, offset, magnitude, @@ -150,7 +150,7 @@ impl Config { // Copy last window to `u` column. // (Although the last window is not a `u` value; we are copying it into the `u` // column because there is an available cell there.) - let z_21 = scalar.running_sum[20]; + let z_21 = scalar.running_sum[21]; copy( &mut region, || "last_window", diff --git a/src/circuit/gadget/sinsemilla/commit_ivk.rs b/src/circuit/gadget/sinsemilla/commit_ivk.rs index 71e55219..48fe4786 100644 --- a/src/circuit/gadget/sinsemilla/commit_ivk.rs +++ b/src/circuit/gadget/sinsemilla/commit_ivk.rs @@ -392,12 +392,13 @@ impl CommitIvkConfig { let t_p = pallas::Base::from_u128(T_P); a + two_pow_130 - t_p }); - let (a_prime, zs) = self.sinsemilla_config.lookup_config.witness_check( + let zs = self.sinsemilla_config.lookup_config.witness_check( layouter.namespace(|| "Decompose low 130 bits of (a + 2^130 - t_P)"), a_prime, 13, false, )?; + let a_prime = zs[0]; assert_eq!(zs.len(), 14); // [z_0, z_1, ..., z13_a] Ok((a_prime, zs[13])) @@ -428,12 +429,13 @@ impl CommitIvkConfig { let t_p = pallas::Base::from_u128(T_P); b_2 + c * two_pow_5 + two_pow_140 - t_p }); - let (b2_c_prime, zs) = self.sinsemilla_config.lookup_config.witness_check( + let zs = self.sinsemilla_config.lookup_config.witness_check( layouter.namespace(|| "Decompose low 140 bits of (b_2 + c * 2^5 + 2^140 - t_P)"), b2_c_prime, 14, false, )?; + let b2_c_prime = zs[0]; assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z14] Ok((b2_c_prime, zs[14])) diff --git a/src/circuit/gadget/sinsemilla/note_commit.rs b/src/circuit/gadget/sinsemilla/note_commit.rs index e544f0ec..412ea482 100644 --- a/src/circuit/gadget/sinsemilla/note_commit.rs +++ b/src/circuit/gadget/sinsemilla/note_commit.rs @@ -710,12 +710,13 @@ impl NoteCommitConfig { let t_p = pallas::Base::from_u128(T_P); a + two_pow_130 - t_p }); - let (a_prime, zs) = self.sinsemilla_config.lookup_config.witness_check( + let zs = self.sinsemilla_config.lookup_config.witness_check( layouter.namespace(|| "Decompose low 130 bits of (a + 2^130 - t_P)"), a_prime, 13, false, )?; + let a_prime = zs[0]; assert_eq!(zs.len(), 14); // [z_0, z_1, ..., z_13] Ok((a_prime, zs[13])) @@ -748,12 +749,13 @@ impl NoteCommitConfig { b_3 + (two_pow_4 * c) + two_pow_140 - t_p }); - let (b3_c_prime, zs) = self.sinsemilla_config.lookup_config.witness_check( + let zs = self.sinsemilla_config.lookup_config.witness_check( layouter.namespace(|| "Decompose low 140 bits of (b_3 + 2^4 c + 2^140 - t_P)"), b3_c_prime, 14, false, )?; + let b3_c_prime = zs[0]; assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z_13, z_14] Ok((b3_c_prime, zs[14])) @@ -787,12 +789,13 @@ impl NoteCommitConfig { // Decompose the low 140 bits of e1_f_prime = e_1 + 2^4 f + 2^140 - t_P, // and output the running sum at the end of it. // If e1_f_prime < 2^140, the running sum will be 0. - let (e1_f_prime, zs) = self.sinsemilla_config.lookup_config.witness_check( + let zs = self.sinsemilla_config.lookup_config.witness_check( layouter.namespace(|| "Decompose low 140 bits of (e_1 + 2^4 f + 2^140 - t_P)"), e1_f_prime, 14, false, )?; + let e1_f_prime = zs[0]; assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z_13, z_14] Ok((e1_f_prime, zs[14])) @@ -823,12 +826,13 @@ impl NoteCommitConfig { g_1 + (two_pow_9 * g_2) + two_pow_140 - t_p }); - let (g1_g2_prime, zs) = self.sinsemilla_config.lookup_config.witness_check( + let zs = self.sinsemilla_config.lookup_config.witness_check( layouter.namespace(|| "Decompose low 140 bits of (g_1 + (2^9)g_2 + 2^140 - t_P)"), g1_g2_prime, 14, false, )?; + let g1_g2_prime = zs[0]; assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z_13, z_14] Ok((g1_g2_prime, zs[14])) diff --git a/src/circuit/gadget/utilities/decompose_running_sum.rs b/src/circuit/gadget/utilities/decompose_running_sum.rs index 8892bf0d..433f63b3 100644 --- a/src/circuit/gadget/utilities/decompose_running_sum.rs +++ b/src/circuit/gadget/utilities/decompose_running_sum.rs @@ -34,7 +34,7 @@ use crate::constants::util::decompose_word; use pasta_curves::arithmetic::FieldExt; use std::marker::PhantomData; -/// The running sum $[z_1, ..., z_W]$. If created in strict mode, $z_W = 0$. +/// The running sum $[z_0, ..., z_W]$. If created in strict mode, $z_W = 0$. pub struct RunningSum(Vec>); impl std::ops::Deref for RunningSum { type Target = Vec>; @@ -43,6 +43,7 @@ impl std::ops::Deref for RunningSum { &self.0 } } + #[derive(Debug, Clone, Eq, PartialEq)] pub struct RunningSumConfig { q_range_check: Selector, @@ -103,7 +104,7 @@ impl strict: bool, word_num_bits: usize, num_windows: usize, - ) -> Result<(CellValue, RunningSum), Error> { + ) -> Result, Error> { let z_0 = { let cell = region.assign_advice( || "z_0 = alpha", @@ -128,7 +129,7 @@ impl strict: bool, word_num_bits: usize, num_windows: usize, - ) -> Result<(CellValue, RunningSum), Error> { + ) -> Result, Error> { let z_0 = copy(region, || "copy z_0 = alpha", self.z, offset, &alpha)?; self.decompose(region, offset, z_0, strict, word_num_bits, num_windows) } @@ -146,7 +147,7 @@ impl strict: bool, word_num_bits: usize, num_windows: usize, - ) -> Result<(CellValue, RunningSum), Error> { + ) -> Result, Error> { // Make sure that we do not have more windows than required for the number // of bits in the word. In other words, every window must contain at least // one bit of the word (no empty windows). @@ -177,8 +178,8 @@ impl } }; - // Initialize empty vector to store running sum values [z_1, ..., z_W]. - let mut zs: Vec> = Vec::with_capacity(num_windows); + // Initialize empty vector to store running sum values [z_0, ..., z_W]. + let mut zs: Vec> = vec![z_0]; let mut z = z_0; // Assign running sum `z_{i+1}` = (z_i - k_i) / (2^K) for i = 0..=n-1. @@ -206,13 +207,14 @@ impl z = z_next; zs.push(z); } + assert_eq!(zs.len(), num_windows + 1); if strict { // Constrain the final running sum output to be zero. region.constrain_constant(zs.last().unwrap().cell(), F::zero())?; } - Ok((z_0, RunningSum(zs))) + Ok(RunningSum(zs)) } } @@ -274,7 +276,7 @@ mod tests { || "decompose", |mut region| { let offset = 0; - let (alpha, _zs) = config.witness_decompose( + let zs = config.witness_decompose( &mut region, offset, self.alpha, @@ -282,6 +284,7 @@ mod tests { WORD_NUM_BITS, NUM_WINDOWS, )?; + let alpha = zs[0]; let offset = offset + NUM_WINDOWS + 1; diff --git a/src/circuit/gadget/utilities/lookup_range_check.rs b/src/circuit/gadget/utilities/lookup_range_check.rs index 092306ea..dba41e2d 100644 --- a/src/circuit/gadget/utilities/lookup_range_check.rs +++ b/src/circuit/gadget/utilities/lookup_range_check.rs @@ -13,6 +13,16 @@ use ff::PrimeFieldBits; use super::*; +/// The running sum $[z_0, ..., z_W]$. If created in strict mode, $z_W = 0$. +pub struct RunningSum(Vec>); +impl std::ops::Deref for RunningSum { + type Target = Vec>; + + fn deref(&self) -> &Vec> { + &self.0 + } +} + #[derive(Eq, PartialEq, Debug, Clone)] pub struct LookupRangeCheckConfig { pub q_lookup: Selector, @@ -125,16 +135,13 @@ impl LookupRangeCheckConfig element: CellValue, num_words: usize, strict: bool, - ) -> Result>, Error> { + ) -> Result, Error> { layouter.assign_region( || format!("{:?} words range check", num_words), |mut region| { // Copy `element` and initialize running sum `z_0 = element` to decompose it. let z_0 = copy(&mut region, || "z_0", self.running_sum, 0, &element)?; - - let zs = self.range_check(&mut region, z_0, num_words, strict)?; - - Ok(zs) + self.range_check(&mut region, z_0, num_words, strict) }, ) } @@ -146,7 +153,7 @@ impl LookupRangeCheckConfig value: Option, num_words: usize, strict: bool, - ) -> Result<(CellValue, Vec>), Error> { + ) -> Result, Error> { layouter.assign_region( || "Witness element", |mut region| { @@ -159,10 +166,7 @@ impl LookupRangeCheckConfig )?; CellValue::new(cell, value) }; - - let zs = self.range_check(&mut region, z_0, num_words, strict)?; - - Ok((z_0, zs)) + self.range_check(&mut region, z_0, num_words, strict) }, ) } @@ -180,7 +184,7 @@ impl LookupRangeCheckConfig element: CellValue, num_words: usize, strict: bool, - ) -> Result>, Error> { + ) -> Result, Error> { // `num_words` must fit into a single field element. assert!(num_words * K <= F::CAPACITY as usize); let num_bits = num_words * K; @@ -247,7 +251,7 @@ impl LookupRangeCheckConfig region.constrain_constant(zs.last().unwrap().cell(), F::zero())?; } - Ok(zs) + Ok(RunningSum(zs)) } /// Short range check on an existing cell that is copied into this helper. @@ -440,7 +444,7 @@ mod tests { for (element, expected_final_z, strict) in elements_and_expected_final_zs.iter() { let expected_zs = expected_zs::(*element, self.num_words); - let (_, zs) = config.witness_check( + let zs = config.witness_check( layouter.namespace(|| format!("Lookup {:?}", self.num_words)), Some(*element), self.num_words,