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.
This commit is contained in:
therealyingtong 2021-07-24 23:54:54 +08:00
parent 092cc389bb
commit 4d1cd2651a
8 changed files with 54 additions and 48 deletions

View File

@ -291,7 +291,7 @@ pub struct EccScalarFixed {
pub struct EccScalarFixedShort {
magnitude: CellValue<pallas::Base>,
sign: CellValue<pallas::Base>,
running_sum: ArrayVec<CellValue<pallas::Base>, { constants::NUM_WINDOWS_SHORT }>,
running_sum: ArrayVec<CellValue<pallas::Base>, { 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<pallas::Base>,
running_sum: ArrayVec<CellValue<pallas::Base>, { constants::NUM_WINDOWS }>,
running_sum: ArrayVec<CellValue<pallas::Base>, { constants::NUM_WINDOWS + 1 }>,
}
impl EccBaseFieldElemFixed {

View File

@ -511,16 +511,8 @@ impl ScalarFixed {
.collect::<Vec<_>>()
};
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()

View File

@ -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])
};

View File

@ -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",

View File

@ -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]))

View File

@ -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]))

View File

@ -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<F: FieldExt + PrimeFieldBits>(Vec<CellValue<F>>);
impl<F: FieldExt + PrimeFieldBits> std::ops::Deref for RunningSum<F> {
type Target = Vec<CellValue<F>>;
@ -43,6 +43,7 @@ impl<F: FieldExt + PrimeFieldBits> std::ops::Deref for RunningSum<F> {
&self.0
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RunningSumConfig<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize> {
q_range_check: Selector,
@ -103,7 +104,7 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
strict: bool,
word_num_bits: usize,
num_windows: usize,
) -> Result<(CellValue<F>, RunningSum<F>), Error> {
) -> Result<RunningSum<F>, Error> {
let z_0 = {
let cell = region.assign_advice(
|| "z_0 = alpha",
@ -128,7 +129,7 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
strict: bool,
word_num_bits: usize,
num_windows: usize,
) -> Result<(CellValue<F>, RunningSum<F>), Error> {
) -> Result<RunningSum<F>, 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<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
strict: bool,
word_num_bits: usize,
num_windows: usize,
) -> Result<(CellValue<F>, RunningSum<F>), Error> {
) -> Result<RunningSum<F>, 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<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
}
};
// Initialize empty vector to store running sum values [z_1, ..., z_W].
let mut zs: Vec<CellValue<F>> = Vec::with_capacity(num_windows);
// Initialize empty vector to store running sum values [z_0, ..., z_W].
let mut zs: Vec<CellValue<F>> = 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<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
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;

View File

@ -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<F: FieldExt + PrimeFieldBits>(Vec<CellValue<F>>);
impl<F: FieldExt + PrimeFieldBits> std::ops::Deref for RunningSum<F> {
type Target = Vec<CellValue<F>>;
fn deref(&self) -> &Vec<CellValue<F>> {
&self.0
}
}
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct LookupRangeCheckConfig<F: FieldExt + PrimeFieldBits, const K: usize> {
pub q_lookup: Selector,
@ -125,16 +135,13 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
element: CellValue<F>,
num_words: usize,
strict: bool,
) -> Result<Vec<CellValue<F>>, Error> {
) -> Result<RunningSum<F>, 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<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
value: Option<F>,
num_words: usize,
strict: bool,
) -> Result<(CellValue<F>, Vec<CellValue<F>>), Error> {
) -> Result<RunningSum<F>, Error> {
layouter.assign_region(
|| "Witness element",
|mut region| {
@ -159,10 +166,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
)?;
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<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
element: CellValue<F>,
num_words: usize,
strict: bool,
) -> Result<Vec<CellValue<F>>, Error> {
) -> Result<RunningSum<F>, 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<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
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::<F, K>(*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,