mirror of https://github.com/zcash/orchard.git
mul_fixed::base_field_elem: Use decompose_running_sum helper.
This commit is contained in:
parent
ee062bae3d
commit
7b497c53a3
|
@ -1,7 +1,8 @@
|
||||||
use super::EccInstructions;
|
use super::EccInstructions;
|
||||||
use crate::{
|
use crate::{
|
||||||
circuit::gadget::utilities::{
|
circuit::gadget::utilities::{
|
||||||
copy, lookup_range_check::LookupRangeCheckConfig, CellValue, Var,
|
copy, decompose_running_sum::RunningSumConfig, lookup_range_check::LookupRangeCheckConfig,
|
||||||
|
CellValue, Var,
|
||||||
},
|
},
|
||||||
constants::{self, OrchardFixedBasesFull, ValueCommitV},
|
constants::{self, OrchardFixedBasesFull, ValueCommitV},
|
||||||
primitives::sinsemilla,
|
primitives::sinsemilla,
|
||||||
|
@ -103,7 +104,7 @@ pub struct EccConfig {
|
||||||
/// Fixed-base signed short scalar multiplication
|
/// Fixed-base signed short scalar multiplication
|
||||||
pub q_mul_fixed_short: Selector,
|
pub q_mul_fixed_short: Selector,
|
||||||
/// Fixed-base multiplication using a base field element as the scalar
|
/// Fixed-base multiplication using a base field element as the scalar
|
||||||
pub base_field_fixed_mul: Selector,
|
pub q_mul_fixed_running_sum: Selector,
|
||||||
/// Canonicity checks on base field element used as scalar in fixed-base mul
|
/// Canonicity checks on base field element used as scalar in fixed-base mul
|
||||||
pub base_field_fixed_canon: Selector,
|
pub base_field_fixed_canon: Selector,
|
||||||
|
|
||||||
|
@ -121,8 +122,22 @@ pub struct EccConfig {
|
||||||
pub constants: Column<Fixed>,
|
pub constants: Column<Fixed>,
|
||||||
/// Permutation over all advice columns and the `constants` fixed column.
|
/// Permutation over all advice columns and the `constants` fixed column.
|
||||||
pub perm: Permutation,
|
pub perm: Permutation,
|
||||||
/// 10-bit lookup table
|
/// Lookup range check using 10-bit lookup table
|
||||||
pub lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
pub lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||||
|
/// Running sum decomposition for full-width base field element
|
||||||
|
pub running_sum_full_config: RunningSumConfig<
|
||||||
|
pallas::Base,
|
||||||
|
{ constants::L_ORCHARD_BASE },
|
||||||
|
{ constants::FIXED_BASE_WINDOW_SIZE },
|
||||||
|
{ constants::NUM_WINDOWS },
|
||||||
|
>,
|
||||||
|
/// Running sum decomposition for 64-bit word
|
||||||
|
pub running_sum_short_config: RunningSumConfig<
|
||||||
|
pallas::Base,
|
||||||
|
{ constants::L_VALUE },
|
||||||
|
{ constants::FIXED_BASE_WINDOW_SIZE },
|
||||||
|
{ constants::NUM_WINDOWS_SHORT },
|
||||||
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A chip implementing EccInstructions
|
/// A chip implementing EccInstructions
|
||||||
|
@ -165,6 +180,11 @@ impl EccChip {
|
||||||
lookup_table,
|
lookup_table,
|
||||||
perm.clone(),
|
perm.clone(),
|
||||||
);
|
);
|
||||||
|
let q_mul_fixed_running_sum = meta.selector();
|
||||||
|
let running_sum_full_config =
|
||||||
|
RunningSumConfig::configure(meta, q_mul_fixed_running_sum, advices[4], perm.clone());
|
||||||
|
let running_sum_short_config =
|
||||||
|
RunningSumConfig::configure(meta, q_mul_fixed_running_sum, advices[4], perm.clone());
|
||||||
|
|
||||||
let config = EccConfig {
|
let config = EccConfig {
|
||||||
advices,
|
advices,
|
||||||
|
@ -188,7 +208,7 @@ impl EccChip {
|
||||||
q_mul_lsb: meta.selector(),
|
q_mul_lsb: meta.selector(),
|
||||||
q_mul_fixed: meta.selector(),
|
q_mul_fixed: meta.selector(),
|
||||||
q_mul_fixed_short: meta.selector(),
|
q_mul_fixed_short: meta.selector(),
|
||||||
base_field_fixed_mul: meta.selector(),
|
q_mul_fixed_running_sum,
|
||||||
base_field_fixed_canon: meta.selector(),
|
base_field_fixed_canon: meta.selector(),
|
||||||
q_point: meta.selector(),
|
q_point: meta.selector(),
|
||||||
q_scalar_fixed: meta.selector(),
|
q_scalar_fixed: meta.selector(),
|
||||||
|
@ -196,6 +216,8 @@ impl EccChip {
|
||||||
constants: constants[1],
|
constants: constants[1],
|
||||||
perm,
|
perm,
|
||||||
lookup_config,
|
lookup_config,
|
||||||
|
running_sum_full_config,
|
||||||
|
running_sum_short_config,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create witness point gate
|
// Create witness point gate
|
||||||
|
|
|
@ -3,36 +3,43 @@ use super::H_BASE;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
circuit::gadget::utilities::{
|
circuit::gadget::utilities::{
|
||||||
bitrange_subset, copy, lookup_range_check::LookupRangeCheckConfig, range_check, CellValue,
|
bitrange_subset, copy, decompose_running_sum::RunningSumConfig,
|
||||||
Var,
|
lookup_range_check::LookupRangeCheckConfig, range_check, CellValue, Var,
|
||||||
},
|
},
|
||||||
constants::{self, util::decompose_word, T_P},
|
constants::{self, T_P},
|
||||||
primitives::sinsemilla,
|
primitives::sinsemilla,
|
||||||
};
|
};
|
||||||
use halo2::{
|
use halo2::{
|
||||||
circuit::{Layouter, Region},
|
circuit::Layouter,
|
||||||
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
|
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
|
||||||
poly::Rotation,
|
poly::Rotation,
|
||||||
};
|
};
|
||||||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||||
|
|
||||||
use arrayvec::ArrayVec;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
base_field_fixed_mul: Selector,
|
q_mul_fixed_running_sum: Selector,
|
||||||
base_field_fixed_canon: Selector,
|
base_field_fixed_canon: Selector,
|
||||||
canon_advices: [Column<Advice>; 3],
|
canon_advices: [Column<Advice>; 3],
|
||||||
lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||||
|
running_sum_config: RunningSumConfig<
|
||||||
|
pallas::Base,
|
||||||
|
{ constants::L_ORCHARD_BASE },
|
||||||
|
{ constants::FIXED_BASE_WINDOW_SIZE },
|
||||||
|
{ constants::NUM_WINDOWS },
|
||||||
|
>,
|
||||||
super_config: super::Config<{ constants::NUM_WINDOWS }>,
|
super_config: super::Config<{ constants::NUM_WINDOWS }>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&EccConfig> for Config {
|
impl From<&EccConfig> for Config {
|
||||||
fn from(config: &EccConfig) -> Self {
|
fn from(config: &EccConfig) -> Self {
|
||||||
let config = Self {
|
let config = Self {
|
||||||
base_field_fixed_mul: config.base_field_fixed_mul,
|
q_mul_fixed_running_sum: config.q_mul_fixed_running_sum,
|
||||||
base_field_fixed_canon: config.base_field_fixed_canon,
|
base_field_fixed_canon: config.base_field_fixed_canon,
|
||||||
canon_advices: [config.advices[6], config.advices[7], config.advices[8]],
|
canon_advices: [config.advices[6], config.advices[7], config.advices[8]],
|
||||||
lookup_config: config.lookup_config.clone(),
|
lookup_config: config.lookup_config.clone(),
|
||||||
|
running_sum_config: config.running_sum_full_config.clone(),
|
||||||
super_config: config.into(),
|
super_config: config.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,33 +51,17 @@ impl From<&EccConfig> for Config {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_eq!(config.running_sum_config.z, config.super_config.window);
|
||||||
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
|
pub fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
|
||||||
// Decompose the base field element α into three-bit windows
|
// Check that each window uses the correct y_p and interpolated x_p.
|
||||||
// using a running sum `z`, where z_{i+1} = (z_i - a_i) / (2^3)
|
meta.create_gate("Coordinates check", |meta| {
|
||||||
// for α = a_0 + 2^3 a_1 + ... + 2^{3*84} a_84.
|
let q_mul_fixed_running_sum = meta.query_selector(self.q_mul_fixed_running_sum);
|
||||||
//
|
|
||||||
// We set z_0 = α, which implies:
|
|
||||||
// z_1 = (α - a_0) / 2^3, (subtract the lowest 3 bits)
|
|
||||||
// = a_1 + 2^3 a_2 + ... + 2^{3*83} a_84,
|
|
||||||
// z_2 = (z_1 - a_1) / 2^3
|
|
||||||
// = a_2 + 2^3 a_3 + ... + 2^{3*82} a_84,
|
|
||||||
// ...,
|
|
||||||
// z_84 = a_84
|
|
||||||
// z_n = (z_84 - a_84) / 2^3
|
|
||||||
// = 0.
|
|
||||||
//
|
|
||||||
// This gate checks that each a_i = z_i - z_{i+1} * 2^3 is within
|
|
||||||
// 3 bits.
|
|
||||||
//
|
|
||||||
// This gate also checks that this window uses the correct y_p and
|
|
||||||
// interpolated x_p.
|
|
||||||
meta.create_gate("Decompose base field element", |meta| {
|
|
||||||
let base_field_fixed_mul = meta.query_selector(self.base_field_fixed_mul);
|
|
||||||
|
|
||||||
let z_cur = meta.query_advice(self.super_config.window, Rotation::cur());
|
let z_cur = meta.query_advice(self.super_config.window, Rotation::cur());
|
||||||
let z_next = meta.query_advice(self.super_config.window, Rotation::next());
|
let z_next = meta.query_advice(self.super_config.window, Rotation::next());
|
||||||
|
@ -79,16 +70,8 @@ impl Config {
|
||||||
// => a_i = z_i - z_{i+1} * 2^3
|
// => a_i = z_i - z_{i+1} * 2^3
|
||||||
let word = z_cur - z_next * pallas::Base::from_u64(constants::H as u64);
|
let word = z_cur - z_next * pallas::Base::from_u64(constants::H as u64);
|
||||||
|
|
||||||
// (word - 7) * (word - 6) * ... * (word - 1) * word = 0
|
|
||||||
let range_check = range_check(word.clone(), constants::H);
|
|
||||||
|
|
||||||
self.super_config
|
self.super_config
|
||||||
.coords_check(meta, base_field_fixed_mul.clone(), word)
|
.coords_check(meta, q_mul_fixed_running_sum, word)
|
||||||
.into_iter()
|
|
||||||
.chain(Some((
|
|
||||||
"Decomposition range check",
|
|
||||||
base_field_fixed_mul * range_check,
|
|
||||||
)))
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check that we get z_85 = 0 as the final output of the three-bit decomposition running sum.
|
// Check that we get z_85 = 0 as the final output of the three-bit decomposition running sum.
|
||||||
|
@ -206,14 +189,23 @@ impl Config {
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
|
|
||||||
// Decompose scalar
|
// Decompose scalar
|
||||||
let scalar = self.decompose_base_field_elem(scalar, offset, &mut region)?;
|
let scalar =
|
||||||
|
{
|
||||||
|
let (base_field_elem, running_sum) = self
|
||||||
|
.running_sum_config
|
||||||
|
.copy_decompose(&mut region, offset, scalar, true)?;
|
||||||
|
EccBaseFieldElemFixed {
|
||||||
|
base_field_elem,
|
||||||
|
running_sum: (*running_sum).as_slice().try_into().unwrap(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let (acc, mul_b) = self.super_config.assign_region_inner(
|
let (acc, mul_b) = self.super_config.assign_region_inner(
|
||||||
&mut region,
|
&mut region,
|
||||||
offset,
|
offset,
|
||||||
&(&scalar).into(),
|
&(&scalar).into(),
|
||||||
base.into(),
|
base.into(),
|
||||||
self.base_field_fixed_mul,
|
self.q_mul_fixed_running_sum,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok((scalar, acc, mul_b))
|
Ok((scalar, acc, mul_b))
|
||||||
|
@ -418,75 +410,6 @@ impl Config {
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decompose_base_field_elem(
|
|
||||||
&self,
|
|
||||||
base_field_elem: CellValue<pallas::Base>,
|
|
||||||
offset: usize,
|
|
||||||
region: &mut Region<'_, pallas::Base>,
|
|
||||||
) -> Result<EccBaseFieldElemFixed, Error> {
|
|
||||||
// Decompose base field element into 3-bit words.
|
|
||||||
let words: Vec<Option<u8>> = {
|
|
||||||
let words = base_field_elem.value().map(|base_field_elem| {
|
|
||||||
decompose_word::<pallas::Base>(
|
|
||||||
base_field_elem,
|
|
||||||
constants::L_ORCHARD_BASE,
|
|
||||||
constants::FIXED_BASE_WINDOW_SIZE,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(words) = words {
|
|
||||||
words.into_iter().map(Some).collect()
|
|
||||||
} else {
|
|
||||||
vec![None; constants::NUM_WINDOWS]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialize empty ArrayVec to store running sum values [z_1, ..., z_85].
|
|
||||||
let mut running_sum: ArrayVec<CellValue<pallas::Base>, { constants::NUM_WINDOWS }> =
|
|
||||||
ArrayVec::new();
|
|
||||||
|
|
||||||
// Assign running sum `z_i`, i = 0..=n, where z_{i+1} = (z_i - a_i) / (2^3)
|
|
||||||
// and `z_0` is initialized as `base_field_elem`.
|
|
||||||
let mut z = copy(
|
|
||||||
region,
|
|
||||||
|| "z_0 = base_field_elem",
|
|
||||||
self.super_config.window,
|
|
||||||
offset,
|
|
||||||
&base_field_elem,
|
|
||||||
&self.super_config.perm,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let offset = offset + 1;
|
|
||||||
|
|
||||||
let eight_inv = pallas::Base::TWO_INV.square() * pallas::Base::TWO_INV;
|
|
||||||
for (idx, word) in words.iter().enumerate() {
|
|
||||||
// z_next = (z_cur - word) / (2^3)
|
|
||||||
let z_next = {
|
|
||||||
let word = word.map(|word| pallas::Base::from_u64(word as u64));
|
|
||||||
let z_next_val = z
|
|
||||||
.value()
|
|
||||||
.zip(word)
|
|
||||||
.map(|(z_cur_val, word)| (z_cur_val - word) * eight_inv);
|
|
||||||
let cell = region.assign_advice(
|
|
||||||
|| format!("z_{:?}", idx + 1),
|
|
||||||
self.super_config.window,
|
|
||||||
offset + idx,
|
|
||||||
|| z_next_val.ok_or(Error::SynthesisError),
|
|
||||||
)?;
|
|
||||||
CellValue::new(cell, z_next_val)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update `z`.
|
|
||||||
z = z_next;
|
|
||||||
running_sum.push(z);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(EccBaseFieldElemFixed {
|
|
||||||
base_field_elem,
|
|
||||||
running_sum,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Reference in New Issue