mirror of https://github.com/zcash/orchard.git
Minor refactors, cleanups, clippy fixes, docfixes.
Co-authored-by: Daira Hopwood <daira@jacaranda.org> Co-authored-by: Jack Grigg <jack@electriccoin.co>
This commit is contained in:
parent
96863c9f73
commit
22ec16f129
|
@ -284,8 +284,12 @@ pub struct EccScalarFixed {
|
||||||
/// A signed short scalar used for fixed-base scalar multiplication.
|
/// A signed short scalar used for fixed-base scalar multiplication.
|
||||||
/// A short scalar must have magnitude in the range [0..2^64), with
|
/// A short scalar must have magnitude in the range [0..2^64), with
|
||||||
/// a sign of either 1 or -1.
|
/// a sign of either 1 or -1.
|
||||||
/// This is decomposed into 22 3-bit windows in little-endian order,
|
/// This is decomposed into 3-bit windows in little-endian order
|
||||||
/// i.e. `windows` = [k_0, k_1, ..., k_21] (for a 64-bit magnitude)
|
/// using a running sum `z`, where z_{i+1} = (z_i - a_i) / (2^3)
|
||||||
|
/// 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` = [k_0, k_1, ..., k_21] (for a 64-bit magnitude)
|
||||||
/// where `scalar = k_0 + k_1 * (2^3) + ... + k_84 * (2^3)^84` and
|
/// where `scalar = k_0 + k_1 * (2^3) + ... + k_84 * (2^3)^84` and
|
||||||
/// each `k_i` is in the range [0..2^3).
|
/// each `k_i` is in the range [0..2^3).
|
||||||
/// k_21 must be a single bit, i.e. 0 or 1.
|
/// k_21 must be a single bit, i.e. 0 or 1.
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
use super::{add, CellValue, EccConfig, EccPoint, EccScalarVar, Var};
|
use super::{add, CellValue, EccConfig, EccPoint, EccScalarVar, Var};
|
||||||
use crate::{
|
use crate::{circuit::gadget::utilities::copy, constants::T_Q};
|
||||||
circuit::gadget::utilities::copy,
|
|
||||||
constants::{NUM_COMPLETE_BITS, T_Q},
|
|
||||||
};
|
|
||||||
use std::ops::{Deref, Range};
|
use std::ops::{Deref, Range};
|
||||||
|
|
||||||
use bigint::U256;
|
use bigint::U256;
|
||||||
|
@ -20,12 +17,18 @@ mod complete;
|
||||||
mod incomplete;
|
mod incomplete;
|
||||||
mod overflow;
|
mod overflow;
|
||||||
|
|
||||||
|
/// Number of bits for which complete addition needs to be used in variable-base
|
||||||
|
/// scalar multiplication
|
||||||
|
const NUM_COMPLETE_BITS: usize = 3;
|
||||||
|
|
||||||
// Bits used in incomplete addition. k_{254} to k_{4} inclusive
|
// Bits used in incomplete addition. k_{254} to k_{4} inclusive
|
||||||
const INCOMPLETE_LEN: usize = pallas::Scalar::NUM_BITS as usize - 1 - NUM_COMPLETE_BITS;
|
const INCOMPLETE_LEN: usize = pallas::Scalar::NUM_BITS as usize - 1 - NUM_COMPLETE_BITS;
|
||||||
const INCOMPLETE_RANGE: Range<usize> = 0..INCOMPLETE_LEN;
|
const INCOMPLETE_RANGE: Range<usize> = 0..INCOMPLETE_LEN;
|
||||||
|
|
||||||
// Bits k_{254} to k_{4} inclusive are used in incomplete addition.
|
// Bits k_{254} to k_{4} inclusive are used in incomplete addition.
|
||||||
// The `hi` half is k_{254} to k_{130} inclusive (length 125 bits).
|
// The `hi` half is k_{254} to k_{130} inclusive (length 125 bits).
|
||||||
|
// (It is a coincidence that k_{130} matches the boundary of the
|
||||||
|
// overflow check described in [the book](https://zcash.github.io/halo2/design/gadgets/ecc/var-base-scalar-mul.html#overflow-check).)
|
||||||
const INCOMPLETE_HI_RANGE: Range<usize> = 0..(INCOMPLETE_LEN / 2);
|
const INCOMPLETE_HI_RANGE: Range<usize> = 0..(INCOMPLETE_LEN / 2);
|
||||||
|
|
||||||
// Bits k_{254} to k_{4} inclusive are used in incomplete addition.
|
// Bits k_{254} to k_{4} inclusive are used in incomplete addition.
|
||||||
|
|
|
@ -16,6 +16,7 @@ use halo2::{
|
||||||
},
|
},
|
||||||
poly::Rotation,
|
poly::Rotation,
|
||||||
};
|
};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use pasta_curves::{
|
use pasta_curves::{
|
||||||
arithmetic::{CurveAffine, FieldExt},
|
arithmetic::{CurveAffine, FieldExt},
|
||||||
pallas,
|
pallas,
|
||||||
|
@ -25,6 +26,13 @@ pub mod base_field_elem;
|
||||||
pub mod full_width;
|
pub mod full_width;
|
||||||
pub mod short;
|
pub mod short;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref TWO_SCALAR: pallas::Scalar = pallas::Scalar::from_u64(2);
|
||||||
|
// H = 2^3 (3-bit window)
|
||||||
|
static ref H_SCALAR: pallas::Scalar = pallas::Scalar::from_u64(constants::H as u64);
|
||||||
|
static ref H_BASE: pallas::Base = pallas::Base::from_u64(constants::H as u64);
|
||||||
|
}
|
||||||
|
|
||||||
// A sum type for both full-width and short bases. This enables us to use the
|
// A sum type for both full-width and short bases. This enables us to use the
|
||||||
// shared functionality of full-width and short fixed-base scalar multiplication.
|
// shared functionality of full-width and short fixed-base scalar multiplication.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
@ -301,100 +309,22 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_accumulator(
|
fn process_window(
|
||||||
&self,
|
&self,
|
||||||
region: &mut Region<'_, pallas::Base>,
|
region: &mut Region<'_, pallas::Base>,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
|
w: usize,
|
||||||
|
k: Option<pallas::Scalar>,
|
||||||
|
k_usize: Option<usize>,
|
||||||
base: OrchardFixedBases,
|
base: OrchardFixedBases,
|
||||||
scalar: &ScalarFixed,
|
|
||||||
) -> Result<EccPoint, Error> {
|
) -> Result<EccPoint, Error> {
|
||||||
// Recall that the message at each window `w` is represented as
|
|
||||||
// `m_w = [(k_w + 2) ⋅ 8^w]B`.
|
|
||||||
// When `w = 0`, we have `m_0 = [(k_0 + 2)]B`.
|
|
||||||
let m0 = {
|
|
||||||
let k0 = scalar.windows_field()[0];
|
|
||||||
let m0 = k0.map(|k0| base.generator() * (k0 + pallas::Scalar::from_u64(2)));
|
|
||||||
let m0 = m0.map(|m0| m0.to_affine().coordinates().unwrap());
|
|
||||||
|
|
||||||
let x = m0.map(|m0| *m0.x());
|
|
||||||
let x_cell = region.assign_advice(
|
|
||||||
|| "m0_x",
|
|
||||||
self.x_p,
|
|
||||||
offset,
|
|
||||||
|| x.ok_or(Error::SynthesisError),
|
|
||||||
)?;
|
|
||||||
let x = CellValue::new(x_cell, x);
|
|
||||||
|
|
||||||
let y = m0.map(|m0| *m0.y());
|
|
||||||
let y_cell = region.assign_advice(
|
|
||||||
|| "m0_y",
|
|
||||||
self.y_p,
|
|
||||||
offset,
|
|
||||||
|| y.ok_or(Error::SynthesisError),
|
|
||||||
)?;
|
|
||||||
let y = CellValue::new(y_cell, y);
|
|
||||||
|
|
||||||
EccPoint { x, y }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Assign u = (y_p + z_w).sqrt() for `m0`
|
|
||||||
{
|
|
||||||
let k0 = scalar.windows_usize()[0];
|
|
||||||
let u0 = &base.u()[0];
|
|
||||||
let u0 = k0.map(|k0| u0.0[k0]);
|
|
||||||
|
|
||||||
region.assign_advice(|| "u", self.u, offset, || u0.ok_or(Error::SynthesisError))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy `m0` into `x_qr`, `y_qr` cells on row 1 of the incomplete addition.
|
|
||||||
let x = copy(
|
|
||||||
region,
|
|
||||||
|| "initialize acc x",
|
|
||||||
self.add_incomplete_config.x_qr,
|
|
||||||
offset + 1,
|
|
||||||
&m0.x,
|
|
||||||
&self.perm,
|
|
||||||
)?;
|
|
||||||
let y = copy(
|
|
||||||
region,
|
|
||||||
|| "initialize acc y",
|
|
||||||
self.add_incomplete_config.y_qr,
|
|
||||||
offset + 1,
|
|
||||||
&m0.y,
|
|
||||||
&self.perm,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(EccPoint { x, y })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_incomplete(
|
|
||||||
&self,
|
|
||||||
region: &mut Region<'_, pallas::Base>,
|
|
||||||
offset: usize,
|
|
||||||
mut acc: EccPoint,
|
|
||||||
base: OrchardFixedBases,
|
|
||||||
scalar: &ScalarFixed,
|
|
||||||
) -> Result<EccPoint, Error> {
|
|
||||||
// This is 2^w, where w is the window width
|
|
||||||
let h = pallas::Scalar::from_u64(constants::H as u64);
|
|
||||||
|
|
||||||
let base_value = base.generator();
|
let base_value = base.generator();
|
||||||
let base_u = base.u();
|
let base_u = base.u();
|
||||||
let scalar_windows_field = scalar.windows_field();
|
|
||||||
let scalar_windows_usize = scalar.windows_usize();
|
|
||||||
|
|
||||||
for (w, k) in scalar_windows_field[1..(scalar_windows_field.len() - 1)]
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
// Offset window index by 1 since we are starting on k_1
|
|
||||||
let w = w + 1;
|
|
||||||
|
|
||||||
// Compute [(k_w + 2) ⋅ 8^w]B
|
// Compute [(k_w + 2) ⋅ 8^w]B
|
||||||
let mul_b = {
|
let mul_b = {
|
||||||
let mul_b = k.map(|k| {
|
let mul_b =
|
||||||
base_value * (k + pallas::Scalar::from_u64(2)) * h.pow(&[w as u64, 0, 0, 0])
|
k.map(|k| base_value * (k + *TWO_SCALAR) * H_SCALAR.pow(&[w as u64, 0, 0, 0]));
|
||||||
});
|
|
||||||
let mul_b = mul_b.map(|mul_b| mul_b.to_affine().coordinates().unwrap());
|
let mul_b = mul_b.map(|mul_b| mul_b.to_affine().coordinates().unwrap());
|
||||||
|
|
||||||
let x = mul_b.map(|mul_b| *mul_b.x());
|
let x = mul_b.map(|mul_b| *mul_b.x());
|
||||||
|
@ -419,7 +349,7 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Assign u = (y_p + z_w).sqrt()
|
// Assign u = (y_p + z_w).sqrt()
|
||||||
let u_val = scalar_windows_usize[w].map(|k| base_u[w].0[k]);
|
let u_val = k_usize.map(|k| base_u[w].0[k]);
|
||||||
region.assign_advice(
|
region.assign_advice(
|
||||||
|| "u",
|
|| "u",
|
||||||
self.u,
|
self.u,
|
||||||
|
@ -427,6 +357,46 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
|
||||||
|| u_val.ok_or(Error::SynthesisError),
|
|| u_val.ok_or(Error::SynthesisError),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
Ok(mul_b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialize_accumulator(
|
||||||
|
&self,
|
||||||
|
region: &mut Region<'_, pallas::Base>,
|
||||||
|
offset: usize,
|
||||||
|
base: OrchardFixedBases,
|
||||||
|
scalar: &ScalarFixed,
|
||||||
|
) -> Result<EccPoint, Error> {
|
||||||
|
// Recall that the message at each window `w` is represented as
|
||||||
|
// `m_w = [(k_w + 2) ⋅ 8^w]B`.
|
||||||
|
// When `w = 0`, we have `m_0 = [(k_0 + 2)]B`.
|
||||||
|
let w = 0;
|
||||||
|
let k0 = scalar.windows_field()[0];
|
||||||
|
let k0_usize = scalar.windows_usize()[0];
|
||||||
|
self.process_window(region, offset, w, k0, k0_usize, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_incomplete(
|
||||||
|
&self,
|
||||||
|
region: &mut Region<'_, pallas::Base>,
|
||||||
|
offset: usize,
|
||||||
|
mut acc: EccPoint,
|
||||||
|
base: OrchardFixedBases,
|
||||||
|
scalar: &ScalarFixed,
|
||||||
|
) -> Result<EccPoint, Error> {
|
||||||
|
let scalar_windows_field = scalar.windows_field();
|
||||||
|
let scalar_windows_usize = scalar.windows_usize();
|
||||||
|
|
||||||
|
for (w, (k, k_usize)) in scalar_windows_field[..(scalar_windows_field.len() - 1)]
|
||||||
|
.iter()
|
||||||
|
.zip(scalar_windows_usize[..(scalar_windows_field.len() - 1)].iter())
|
||||||
|
.enumerate()
|
||||||
|
// Skip k_0 (already processed).
|
||||||
|
.skip(1)
|
||||||
|
{
|
||||||
|
// Compute [(k_w + 2) ⋅ 8^w]B
|
||||||
|
let mul_b = self.process_window(region, offset, w, *k, *k_usize, base)?;
|
||||||
|
|
||||||
// Add to the accumulator
|
// Add to the accumulator
|
||||||
acc = self
|
acc = self
|
||||||
.add_incomplete_config
|
.add_incomplete_config
|
||||||
|
@ -442,9 +412,6 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
|
||||||
base: OrchardFixedBases,
|
base: OrchardFixedBases,
|
||||||
scalar: &ScalarFixed,
|
scalar: &ScalarFixed,
|
||||||
) -> Result<EccPoint, Error> {
|
) -> Result<EccPoint, Error> {
|
||||||
// This is 2^w, where w is the window width
|
|
||||||
let h = pallas::Scalar::from_u64(constants::H as u64);
|
|
||||||
|
|
||||||
// Assign u = (y_p + z_w).sqrt() for the most significant window
|
// Assign u = (y_p + z_w).sqrt() for the most significant window
|
||||||
{
|
{
|
||||||
let u_val =
|
let u_val =
|
||||||
|
@ -459,7 +426,7 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
|
||||||
|
|
||||||
// offset_acc = \sum_{j = 0}^{NUM_WINDOWS - 2} 2^{FIXED_BASE_WINDOW_SIZE * j+1}
|
// offset_acc = \sum_{j = 0}^{NUM_WINDOWS - 2} 2^{FIXED_BASE_WINDOW_SIZE * j+1}
|
||||||
let offset_acc = (0..(NUM_WINDOWS - 1)).fold(pallas::Scalar::zero(), |acc, w| {
|
let offset_acc = (0..(NUM_WINDOWS - 1)).fold(pallas::Scalar::zero(), |acc, w| {
|
||||||
acc + pallas::Scalar::from_u64(2).pow(&[
|
acc + (*TWO_SCALAR).pow(&[
|
||||||
constants::FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1,
|
constants::FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -469,7 +436,7 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
|
||||||
|
|
||||||
// `scalar = [k * 8^84 - offset_acc]`, where `offset_acc = \sum_{j = 0}^{83} 2^{FIXED_BASE_WINDOW_SIZE * j + 1}`.
|
// `scalar = [k * 8^84 - offset_acc]`, where `offset_acc = \sum_{j = 0}^{83} 2^{FIXED_BASE_WINDOW_SIZE * j + 1}`.
|
||||||
let scalar = scalar.windows_field()[scalar.windows_field().len() - 1]
|
let scalar = scalar.windows_field()[scalar.windows_field().len() - 1]
|
||||||
.map(|k| k * h.pow(&[(NUM_WINDOWS - 1) as u64, 0, 0, 0]) - offset_acc);
|
.map(|k| k * (*H_SCALAR).pow(&[(NUM_WINDOWS - 1) as u64, 0, 0, 0]) - offset_acc);
|
||||||
|
|
||||||
let mul_b = {
|
let mul_b = {
|
||||||
let mul_b = scalar.map(|scalar| base.generator() * scalar);
|
let mul_b = scalar.map(|scalar| base.generator() * scalar);
|
||||||
|
@ -545,9 +512,9 @@ impl ScalarFixed {
|
||||||
.map(|idx| {
|
.map(|idx| {
|
||||||
let z_cur = zs[idx].value();
|
let z_cur = zs[idx].value();
|
||||||
let z_next = zs[idx + 1].value();
|
let z_next = zs[idx + 1].value();
|
||||||
let word = z_cur.zip(z_next).map(|(z_cur, z_next)| {
|
let word = z_cur
|
||||||
z_cur - z_next * pallas::Base::from_u64(constants::H as u64)
|
.zip(z_next)
|
||||||
});
|
.map(|(z_cur, z_next)| z_cur - z_next * *H_BASE);
|
||||||
word.map(|word| pallas::Scalar::from_bytes(&word.to_bytes()).unwrap())
|
word.map(|word| pallas::Scalar::from_bytes(&word.to_bytes()).unwrap())
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
|
|
@ -2,7 +2,8 @@ use super::super::{EccBaseFieldElemFixed, EccConfig, EccPoint, OrchardFixedBases
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
circuit::gadget::utilities::{
|
circuit::gadget::utilities::{
|
||||||
bitrange_subset, copy, lookup_range_check::LookupRangeCheckConfig, CellValue, Var,
|
bitrange_subset, copy, lookup_range_check::LookupRangeCheckConfig, range_check, CellValue,
|
||||||
|
Var,
|
||||||
},
|
},
|
||||||
constants::{self, util::decompose_scalar_fixed, T_P},
|
constants::{self, util::decompose_scalar_fixed, T_P},
|
||||||
primitives::sinsemilla,
|
primitives::sinsemilla,
|
||||||
|
@ -37,7 +38,7 @@ impl From<&EccConfig> for Config {
|
||||||
let add_incomplete_advices = config.super_config.add_incomplete_config.advice_columns();
|
let add_incomplete_advices = config.super_config.add_incomplete_config.advice_columns();
|
||||||
for canon_advice in config.canon_advices.iter() {
|
for canon_advice in config.canon_advices.iter() {
|
||||||
assert!(
|
assert!(
|
||||||
!add_incomplete_advices.contains(&canon_advice),
|
!add_incomplete_advices.contains(canon_advice),
|
||||||
"Deconflict canon_advice columns with incomplete addition columns."
|
"Deconflict canon_advice columns with incomplete addition columns."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -48,29 +49,21 @@ impl From<&EccConfig> for 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>) {
|
||||||
// Check that an expression is in the range [0..range),
|
|
||||||
// i.e. 0 ≤ word < range.
|
|
||||||
let range_check = |word: Expression<pallas::Base>, range: usize| {
|
|
||||||
(0..range).fold(Expression::Constant(pallas::Base::one()), |acc, i| {
|
|
||||||
acc * (word.clone() - Expression::Constant(pallas::Base::from_u64(i as u64)))
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
// Decompose the base field element α into three-bit windows
|
// Decompose the base field element α into three-bit windows
|
||||||
// using a running sum `z`, where z_{i+1} = (z_i - a_i) / (2^3)
|
// using a running sum `z`, where z_{i+1} = (z_i - a_i) / (2^3)
|
||||||
// for α = a_0 + (2^3) a_1 + ... + (2^{3(84)}) a_{84}.
|
// for α = a_0 + 2^3 a_1 + ... + 2^{3*84} a_84.
|
||||||
//
|
//
|
||||||
// We set z_0 = α, which implies:
|
// We set z_0 = α, which implies:
|
||||||
// z_1 = (α - a_0) / 2^3, (subtract the lowest 3 bits)
|
// z_1 = (α - a_0) / 2^3, (subtract the lowest 3 bits)
|
||||||
// = a_1 + (2^3) a_2 + ... + 2^{3(83)} a_{84},
|
// = a_1 + 2^3 a_2 + ... + 2^{3*83} a_84,
|
||||||
// z_2 = (z_1 - a_1) / 2^3
|
// z_2 = (z_1 - a_1) / 2^3
|
||||||
// = a_2 + (2^3) a_3 + ... + 2^{3(82)} a_{84},
|
// = a_2 + 2^3 a_3 + ... + 2^{3*82} a_84,
|
||||||
// ...,
|
// ...,
|
||||||
// z_{84} = a_{84}
|
// z_84 = a_84
|
||||||
// z_n = (z_{84} - a_{84}) / 2^3
|
// z_n = (z_84 - a_84) / 2^3
|
||||||
// = 0.
|
// = 0.
|
||||||
//
|
//
|
||||||
// This gate checks that each a_i = z_i - z_{i+1} * (2^3) is within
|
// This gate checks that each a_i = z_i - z_{i+1} * 2^3 is within
|
||||||
// 3 bits.
|
// 3 bits.
|
||||||
//
|
//
|
||||||
// This gate also checks that this window uses the correct y_p and
|
// This gate also checks that this window uses the correct y_p and
|
||||||
|
@ -81,8 +74,8 @@ impl Config {
|
||||||
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());
|
||||||
|
|
||||||
// z_{i+1} = (z_i - a_i) / (2^3)
|
// z_{i+1} = (z_i - a_i) / 2^3
|
||||||
// => 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
|
// (word - 7) * (word - 6) * ... * (word - 1) * word = 0
|
||||||
|
@ -108,7 +101,7 @@ impl Config {
|
||||||
// The last three bits of α.
|
// The last three bits of α.
|
||||||
let z_84_alpha = meta.query_advice(self.canon_advices[2], Rotation::prev());
|
let z_84_alpha = meta.query_advice(self.canon_advices[2], Rotation::prev());
|
||||||
|
|
||||||
// Decompose α into three pieces,
|
// Decompose α into three pieces, in little-endian order:
|
||||||
// α = α_0 (252 bits) || α_1 (2 bits) || α_2 (1 bit).
|
// α = α_0 (252 bits) || α_1 (2 bits) || α_2 (1 bit).
|
||||||
//
|
//
|
||||||
// α_0 is derived, not witnessed.
|
// α_0 is derived, not witnessed.
|
||||||
|
@ -152,8 +145,9 @@ impl Config {
|
||||||
// = 2^254 + 45560315531419706090280762371685220353.
|
// = 2^254 + 45560315531419706090280762371685220353.
|
||||||
// Note that t_p < 2^130.
|
// Note that t_p < 2^130.
|
||||||
//
|
//
|
||||||
// α has been decomposed into three pieces,
|
// α has been decomposed into three pieces in little-endian order:
|
||||||
// α = α_0 (252 bits) || α_1 (2 bits) || α_2 (1 bit).
|
// α = α_0 (252 bits) || α_1 (2 bits) || α_2 (1 bit).
|
||||||
|
// = α_0 + 2^252 α_1 + 2^254 α_2.
|
||||||
//
|
//
|
||||||
// If the MSB α_2 = 1, then:
|
// If the MSB α_2 = 1, then:
|
||||||
// - α_2 = 1 => α_1 = 0, and
|
// - α_2 = 1 => α_1 = 0, and
|
||||||
|
@ -259,8 +253,9 @@ impl Config {
|
||||||
// = 2^254 + 45560315531419706090280762371685220353.
|
// = 2^254 + 45560315531419706090280762371685220353.
|
||||||
// Note that t_p < 2^130.
|
// Note that t_p < 2^130.
|
||||||
//
|
//
|
||||||
// α has been decomposed into three pieces,
|
// α has been decomposed into three pieces in little-endian order:
|
||||||
// α = α_0 (252 bits) || α_1 (2 bits) || α_2 (1 bit).
|
// α = α_0 (252 bits) || α_1 (2 bits) || α_2 (1 bit).
|
||||||
|
// = α_0 + 2^252 α_1 + 2^254 α_2.
|
||||||
//
|
//
|
||||||
// If the MSB α_2 = 1, then:
|
// If the MSB α_2 = 1, then:
|
||||||
// - α_2 = 1 => α_1 = 0, and
|
// - α_2 = 1 => α_1 = 0, and
|
||||||
|
@ -274,14 +269,6 @@ impl Config {
|
||||||
let (alpha, running_sum) = (scalar.base_field_elem, &scalar.running_sum);
|
let (alpha, running_sum) = (scalar.base_field_elem, &scalar.running_sum);
|
||||||
let z_43_alpha = running_sum[42];
|
let z_43_alpha = running_sum[42];
|
||||||
let z_44_alpha = running_sum[43];
|
let z_44_alpha = running_sum[43];
|
||||||
{
|
|
||||||
let a_43 = z_44_alpha
|
|
||||||
.value()
|
|
||||||
.zip(z_43_alpha.value())
|
|
||||||
.map(|(z_44, z_43)| z_43 - z_44 * pallas::Base::from_u64(8));
|
|
||||||
println!("a_43: {:?}", a_43);
|
|
||||||
}
|
|
||||||
|
|
||||||
let z_84_alpha = running_sum[83];
|
let z_84_alpha = running_sum[83];
|
||||||
let z_85_alpha = running_sum[84];
|
let z_85_alpha = running_sum[84];
|
||||||
|
|
||||||
|
@ -485,7 +472,7 @@ impl Config {
|
||||||
.zip(word)
|
.zip(word)
|
||||||
.map(|(z_cur_val, word)| (z_cur_val - word) * eight_inv);
|
.map(|(z_cur_val, word)| (z_cur_val - word) * eight_inv);
|
||||||
let cell = region.assign_advice(
|
let cell = region.assign_advice(
|
||||||
|| format!("word {:?}", idx),
|
|| format!("z_{:?}", idx + 1),
|
||||||
self.super_config.window,
|
self.super_config.window,
|
||||||
offset + idx,
|
offset + idx,
|
||||||
|| z_next_val.ok_or(Error::SynthesisError),
|
|| z_next_val.ok_or(Error::SynthesisError),
|
||||||
|
@ -538,7 +525,7 @@ pub mod tests {
|
||||||
chip.clone(),
|
chip.clone(),
|
||||||
layouter.namespace(|| "commit_ivk_r"),
|
layouter.namespace(|| "commit_ivk_r"),
|
||||||
commit_ivk_r,
|
commit_ivk_r,
|
||||||
&zero,
|
zero,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// note_commit_r
|
// note_commit_r
|
||||||
|
@ -548,7 +535,7 @@ pub mod tests {
|
||||||
chip.clone(),
|
chip.clone(),
|
||||||
layouter.namespace(|| "note_commit_r"),
|
layouter.namespace(|| "note_commit_r"),
|
||||||
note_commit_r,
|
note_commit_r,
|
||||||
&zero,
|
zero,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// nullifier_k
|
// nullifier_k
|
||||||
|
@ -558,7 +545,7 @@ pub mod tests {
|
||||||
chip.clone(),
|
chip.clone(),
|
||||||
layouter.namespace(|| "nullifier_k"),
|
layouter.namespace(|| "nullifier_k"),
|
||||||
nullifier_k,
|
nullifier_k,
|
||||||
&zero,
|
zero,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// value_commit_r
|
// value_commit_r
|
||||||
|
@ -568,7 +555,7 @@ pub mod tests {
|
||||||
chip.clone(),
|
chip.clone(),
|
||||||
layouter.namespace(|| "value_commit_r"),
|
layouter.namespace(|| "value_commit_r"),
|
||||||
value_commit_r,
|
value_commit_r,
|
||||||
&zero,
|
zero,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// spend_auth_g
|
// spend_auth_g
|
||||||
|
@ -578,7 +565,7 @@ pub mod tests {
|
||||||
chip,
|
chip,
|
||||||
layouter.namespace(|| "spend_auth_g"),
|
layouter.namespace(|| "spend_auth_g"),
|
||||||
spend_auth_g,
|
spend_auth_g,
|
||||||
&zero,
|
zero,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -637,7 +624,7 @@ pub mod tests {
|
||||||
)?;
|
)?;
|
||||||
let result =
|
let result =
|
||||||
base.mul_base_field_elem(layouter.namespace(|| "mul by zero"), scalar_fixed)?;
|
base.mul_base_field_elem(layouter.namespace(|| "mul by zero"), scalar_fixed)?;
|
||||||
result.constrain_equal(layouter.namespace(|| "[0]B = 𝒪"), &zero)?;
|
result.constrain_equal(layouter.namespace(|| "[0]B = 𝒪"), zero)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// [-1]B is the largest base field element
|
// [-1]B is the largest base field element
|
||||||
|
|
|
@ -88,7 +88,7 @@ pub mod tests {
|
||||||
chip.clone(),
|
chip.clone(),
|
||||||
layouter.namespace(|| "commit_ivk_r"),
|
layouter.namespace(|| "commit_ivk_r"),
|
||||||
commit_ivk_r,
|
commit_ivk_r,
|
||||||
&zero,
|
zero,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// note_commit_r
|
// note_commit_r
|
||||||
|
@ -98,7 +98,7 @@ pub mod tests {
|
||||||
chip.clone(),
|
chip.clone(),
|
||||||
layouter.namespace(|| "note_commit_r"),
|
layouter.namespace(|| "note_commit_r"),
|
||||||
note_commit_r,
|
note_commit_r,
|
||||||
&zero,
|
zero,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// nullifier_k
|
// nullifier_k
|
||||||
|
@ -108,7 +108,7 @@ pub mod tests {
|
||||||
chip.clone(),
|
chip.clone(),
|
||||||
layouter.namespace(|| "nullifier_k"),
|
layouter.namespace(|| "nullifier_k"),
|
||||||
nullifier_k,
|
nullifier_k,
|
||||||
&zero,
|
zero,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// value_commit_r
|
// value_commit_r
|
||||||
|
@ -118,7 +118,7 @@ pub mod tests {
|
||||||
chip.clone(),
|
chip.clone(),
|
||||||
layouter.namespace(|| "value_commit_r"),
|
layouter.namespace(|| "value_commit_r"),
|
||||||
value_commit_r,
|
value_commit_r,
|
||||||
&zero,
|
zero,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// spend_auth_g
|
// spend_auth_g
|
||||||
|
@ -128,7 +128,7 @@ pub mod tests {
|
||||||
chip,
|
chip,
|
||||||
layouter.namespace(|| "spend_auth_g"),
|
layouter.namespace(|| "spend_auth_g"),
|
||||||
spend_auth_g,
|
spend_auth_g,
|
||||||
&zero,
|
zero,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -188,7 +188,7 @@ pub mod tests {
|
||||||
Some(scalar_fixed),
|
Some(scalar_fixed),
|
||||||
)?;
|
)?;
|
||||||
let result = base.mul(layouter.namespace(|| "mul by zero"), &scalar_fixed)?;
|
let result = base.mul(layouter.namespace(|| "mul by zero"), &scalar_fixed)?;
|
||||||
result.constrain_equal(layouter.namespace(|| "[0]B = 𝒪"), &zero)?;
|
result.constrain_equal(layouter.namespace(|| "[0]B = 𝒪"), zero)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// [-1]B is the largest scalar field element.
|
// [-1]B is the largest scalar field element.
|
||||||
|
|
|
@ -194,7 +194,7 @@ pub mod tests {
|
||||||
Some(scalar_fixed),
|
Some(scalar_fixed),
|
||||||
)?;
|
)?;
|
||||||
let result = value_commit_v.mul(layouter.namespace(|| "mul by zero"), &scalar_fixed)?;
|
let result = value_commit_v.mul(layouter.namespace(|| "mul by zero"), &scalar_fixed)?;
|
||||||
result.constrain_equal(layouter.namespace(|| "[0]B = 𝒪"), &zero)?;
|
result.constrain_equal(layouter.namespace(|| "[0]B = 𝒪"), zero)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Random [a]B
|
// Random [a]B
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
use super::{CellValue, EccConfig, Var};
|
use super::{CellValue, EccConfig, Var};
|
||||||
use crate::constants::{self, util};
|
use crate::{
|
||||||
|
circuit::gadget::utilities::range_check,
|
||||||
|
constants::{self, util},
|
||||||
|
};
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use halo2::{
|
use halo2::{
|
||||||
circuit::Region,
|
circuit::Region,
|
||||||
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Permutation, Selector},
|
plonk::{Advice, Column, ConstraintSystem, Error, Permutation, Selector},
|
||||||
poly::Rotation,
|
poly::Rotation,
|
||||||
};
|
};
|
||||||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||||
|
@ -38,11 +41,7 @@ impl Config {
|
||||||
|
|
||||||
// Constrain each window to a 3-bit value:
|
// Constrain each window to a 3-bit value:
|
||||||
// 1 * (window - 0) * (window - 1) * ... * (window - 7)
|
// 1 * (window - 0) * (window - 1) * ... * (window - 7)
|
||||||
let range_check =
|
vec![q_scalar_fixed * range_check(window, constants::H)]
|
||||||
(0..constants::H).fold(Expression::Constant(pallas::Base::one()), |acc, i| {
|
|
||||||
acc * (window.clone() - Expression::Constant(pallas::Base::from_u64(i as u64)))
|
|
||||||
});
|
|
||||||
vec![q_scalar_fixed * range_check]
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use ff::PrimeFieldBits;
|
use ff::PrimeFieldBits;
|
||||||
use halo2::{
|
use halo2::{
|
||||||
circuit::{Cell, Layouter, Region},
|
circuit::{Cell, Layouter, Region},
|
||||||
plonk::{Advice, Column, Error, Permutation},
|
plonk::{Advice, Column, Error, Expression, Permutation},
|
||||||
};
|
};
|
||||||
use pasta_curves::arithmetic::FieldExt;
|
use pasta_curves::arithmetic::FieldExt;
|
||||||
use std::array;
|
use std::array;
|
||||||
|
@ -123,3 +123,12 @@ pub fn bitrange_subset<F: FieldExt + PrimeFieldBits>(
|
||||||
|
|
||||||
F::from_bytes(&bytearray.try_into().unwrap()).unwrap()
|
F::from_bytes(&bytearray.try_into().unwrap()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check that an expression is in the small range [0..range),
|
||||||
|
/// i.e. 0 ≤ word < range.
|
||||||
|
pub fn range_check<F: FieldExt>(word: Expression<F>, range: usize) -> Expression<F> {
|
||||||
|
(0..range)
|
||||||
|
.map(|i| Expression::Constant(F::from_u64(i as u64)))
|
||||||
|
.reduce(|acc, i| acc * (word.clone() - i))
|
||||||
|
.expect("range > 0")
|
||||||
|
}
|
||||||
|
|
|
@ -82,10 +82,6 @@ pub const NUM_WINDOWS: usize =
|
||||||
pub const NUM_WINDOWS_SHORT: usize =
|
pub const NUM_WINDOWS_SHORT: usize =
|
||||||
(L_VALUE + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE;
|
(L_VALUE + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE;
|
||||||
|
|
||||||
/// Number of bits for which complete addition needs to be used in variable-base
|
|
||||||
/// scalar multiplication
|
|
||||||
pub const NUM_COMPLETE_BITS: usize = 3;
|
|
||||||
|
|
||||||
/// For each fixed base, we calculate its scalar multiples in three-bit windows.
|
/// For each fixed base, we calculate its scalar multiples in three-bit windows.
|
||||||
/// Each window will have $2^3 = 8$ points.
|
/// Each window will have $2^3 = 8$ points.
|
||||||
fn compute_window_table<C: CurveAffine>(base: C, num_windows: usize) -> Vec<[C; H]> {
|
fn compute_window_table<C: CurveAffine>(base: C, num_windows: usize) -> Vec<[C; H]> {
|
||||||
|
|
Loading…
Reference in New Issue