2021-07-14 22:05:22 -07:00
|
|
|
|
use super::super::{EccBaseFieldElemFixed, EccConfig, EccPoint, NullifierK};
|
2021-07-08 19:23:40 -07:00
|
|
|
|
use super::H_BASE;
|
2021-06-18 02:41:13 -07:00
|
|
|
|
|
|
|
|
|
use crate::{
|
2021-07-07 00:53:43 -07:00
|
|
|
|
circuit::gadget::utilities::{
|
2021-07-09 17:50:07 -07:00
|
|
|
|
bitrange_subset, copy, decompose_running_sum::RunningSumConfig,
|
|
|
|
|
lookup_range_check::LookupRangeCheckConfig, range_check, CellValue, Var,
|
2021-07-07 00:53:43 -07:00
|
|
|
|
},
|
2021-07-09 17:50:07 -07:00
|
|
|
|
constants::{self, T_P},
|
2021-07-07 00:53:43 -07:00
|
|
|
|
primitives::sinsemilla,
|
2021-06-18 02:41:13 -07:00
|
|
|
|
};
|
|
|
|
|
use halo2::{
|
2021-07-09 17:50:07 -07:00
|
|
|
|
circuit::Layouter,
|
2021-07-07 00:53:43 -07:00
|
|
|
|
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
|
2021-06-18 02:41:13 -07:00
|
|
|
|
poly::Rotation,
|
|
|
|
|
};
|
|
|
|
|
use pasta_curves::{arithmetic::FieldExt, pallas};
|
|
|
|
|
|
2021-07-09 17:50:07 -07:00
|
|
|
|
use std::convert::TryInto;
|
2021-06-18 02:41:13 -07:00
|
|
|
|
|
|
|
|
|
pub struct Config {
|
2021-07-09 17:50:07 -07:00
|
|
|
|
q_mul_fixed_running_sum: Selector,
|
2021-07-17 08:59:25 -07:00
|
|
|
|
q_mul_fixed_base_field: Selector,
|
2021-07-07 00:53:43 -07:00
|
|
|
|
canon_advices: [Column<Advice>; 3],
|
|
|
|
|
lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
2021-07-16 21:10:13 -07:00
|
|
|
|
running_sum_config: RunningSumConfig<pallas::Base, { constants::FIXED_BASE_WINDOW_SIZE }>,
|
2021-06-18 02:41:13 -07:00
|
|
|
|
super_config: super::Config<{ constants::NUM_WINDOWS }>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<&EccConfig> for Config {
|
|
|
|
|
fn from(config: &EccConfig) -> Self {
|
2021-07-07 00:53:43 -07:00
|
|
|
|
let config = Self {
|
2021-07-09 17:50:07 -07:00
|
|
|
|
q_mul_fixed_running_sum: config.q_mul_fixed_running_sum,
|
2021-07-17 08:59:25 -07:00
|
|
|
|
q_mul_fixed_base_field: config.q_mul_fixed_base_field,
|
2021-07-07 21:00:52 -07:00
|
|
|
|
canon_advices: [config.advices[6], config.advices[7], config.advices[8]],
|
2021-07-07 00:53:43 -07:00
|
|
|
|
lookup_config: config.lookup_config.clone(),
|
2021-07-16 21:10:13 -07:00
|
|
|
|
running_sum_config: config.running_sum_config.clone(),
|
2021-06-18 02:41:13 -07:00
|
|
|
|
super_config: config.into(),
|
2021-07-07 00:53:43 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let add_incomplete_advices = config.super_config.add_incomplete_config.advice_columns();
|
|
|
|
|
for canon_advice in config.canon_advices.iter() {
|
|
|
|
|
assert!(
|
2021-07-07 22:14:53 -07:00
|
|
|
|
!add_incomplete_advices.contains(canon_advice),
|
2021-07-07 00:53:43 -07:00
|
|
|
|
"Deconflict canon_advice columns with incomplete addition columns."
|
|
|
|
|
);
|
2021-06-18 02:41:13 -07:00
|
|
|
|
}
|
2021-07-07 00:53:43 -07:00
|
|
|
|
|
2021-07-09 17:50:07 -07:00
|
|
|
|
assert_eq!(config.running_sum_config.z, config.super_config.window);
|
|
|
|
|
|
2021-07-07 00:53:43 -07:00
|
|
|
|
config
|
2021-06-18 02:41:13 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Config {
|
|
|
|
|
pub fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
|
2021-07-17 04:17:49 -07:00
|
|
|
|
// Check that the base field element is canonical.
|
2021-07-07 00:53:43 -07:00
|
|
|
|
meta.create_gate("Canonicity checks", |meta| {
|
2021-07-17 08:59:25 -07:00
|
|
|
|
let q_mul_fixed_base_field = meta.query_selector(self.q_mul_fixed_base_field);
|
2021-07-07 00:53:43 -07:00
|
|
|
|
|
|
|
|
|
let alpha = meta.query_advice(self.canon_advices[0], Rotation::prev());
|
|
|
|
|
// The last three bits of α.
|
|
|
|
|
let z_84_alpha = meta.query_advice(self.canon_advices[2], Rotation::prev());
|
|
|
|
|
|
2021-07-07 22:14:53 -07:00
|
|
|
|
// Decompose α into three pieces, in little-endian order:
|
2021-07-07 00:53:43 -07:00
|
|
|
|
// α = α_0 (252 bits) || α_1 (2 bits) || α_2 (1 bit).
|
|
|
|
|
//
|
2021-07-07 20:24:18 -07:00
|
|
|
|
// α_0 is derived, not witnessed.
|
|
|
|
|
let alpha_0 = {
|
|
|
|
|
let two_pow_252 = pallas::Base::from_u128(1 << 126).square();
|
|
|
|
|
alpha - (z_84_alpha.clone() * two_pow_252)
|
|
|
|
|
};
|
2021-07-07 00:53:43 -07:00
|
|
|
|
let alpha_1 = meta.query_advice(self.canon_advices[1], Rotation::cur());
|
|
|
|
|
let alpha_2 = meta.query_advice(self.canon_advices[2], Rotation::cur());
|
|
|
|
|
|
2021-07-07 20:24:18 -07:00
|
|
|
|
let alpha_0_prime = meta.query_advice(self.canon_advices[0], Rotation::cur());
|
|
|
|
|
let z_13_alpha_0_prime = meta.query_advice(self.canon_advices[0], Rotation::next());
|
|
|
|
|
let z_44_alpha = meta.query_advice(self.canon_advices[1], Rotation::next());
|
|
|
|
|
let z_43_alpha = meta.query_advice(self.canon_advices[2], Rotation::next());
|
2021-07-07 00:53:43 -07:00
|
|
|
|
|
|
|
|
|
let decomposition_checks = {
|
|
|
|
|
// Range-constrain α_1 to be 2 bits
|
|
|
|
|
let alpha_1_range_check = range_check(alpha_1.clone(), 1 << 2);
|
|
|
|
|
// Boolean-constrain α_2
|
|
|
|
|
let alpha_2_range_check = range_check(alpha_2.clone(), 1 << 1);
|
|
|
|
|
// Check that α_1 + 2^2 α_2 = z_84_alpha
|
|
|
|
|
let z_84_alpha_check = z_84_alpha.clone()
|
2021-07-07 03:38:43 -07:00
|
|
|
|
- (alpha_1.clone() + alpha_2.clone() * pallas::Base::from_u64(1 << 2));
|
2021-07-07 00:53:43 -07:00
|
|
|
|
|
|
|
|
|
std::iter::empty()
|
|
|
|
|
.chain(Some(("alpha_1_range_check", alpha_1_range_check)))
|
|
|
|
|
.chain(Some(("alpha_2_range_check", alpha_2_range_check)))
|
|
|
|
|
.chain(Some(("z_84_alpha_check", z_84_alpha_check)))
|
2021-06-18 02:41:13 -07:00
|
|
|
|
};
|
|
|
|
|
|
2021-07-07 00:53:43 -07:00
|
|
|
|
// Check α_0_prime = α_0 + 2^130 - t_p
|
|
|
|
|
let alpha_0_prime_check = {
|
2021-07-07 17:49:46 -07:00
|
|
|
|
let two_pow_130 = Expression::Constant(pallas::Base::from_u128(1 << 65).square());
|
2021-07-07 00:53:43 -07:00
|
|
|
|
let t_p = Expression::Constant(pallas::Base::from_u128(T_P));
|
|
|
|
|
alpha_0_prime - (alpha_0 + two_pow_130 - t_p)
|
2021-06-18 02:41:13 -07:00
|
|
|
|
};
|
|
|
|
|
|
2021-07-07 00:53:43 -07:00
|
|
|
|
// We want to enforce canonicity of a 255-bit base field element, α.
|
|
|
|
|
// That is, we want to check that 0 ≤ α < p, where p is Pallas base
|
|
|
|
|
// field modulus p = 2^254 + t_p
|
|
|
|
|
// = 2^254 + 45560315531419706090280762371685220353.
|
|
|
|
|
// Note that t_p < 2^130.
|
|
|
|
|
//
|
2021-07-07 22:14:53 -07:00
|
|
|
|
// α has been decomposed into three pieces in little-endian order:
|
2021-07-07 00:53:43 -07:00
|
|
|
|
// α = α_0 (252 bits) || α_1 (2 bits) || α_2 (1 bit).
|
2021-07-07 22:14:53 -07:00
|
|
|
|
// = α_0 + 2^252 α_1 + 2^254 α_2.
|
2021-07-07 00:53:43 -07:00
|
|
|
|
//
|
|
|
|
|
// If the MSB α_2 = 1, then:
|
|
|
|
|
// - α_2 = 1 => α_1 = 0, and
|
|
|
|
|
// - α_2 = 1 => α_0 < t_p. To enforce this:
|
|
|
|
|
// - α_2 = 1 => 0 ≤ α_0 < 2^130
|
2021-07-07 20:24:18 -07:00
|
|
|
|
// - alpha_0_hi_120 = 0 (constrain α_0 to be 132 bits)
|
|
|
|
|
// - a_43 = 0 or 1 (constrain α_0[130..=131] to be 0)
|
2021-07-07 00:53:43 -07:00
|
|
|
|
// - α_2 = 1 => 0 ≤ α_0 + 2^130 - t_p < 2^130
|
|
|
|
|
// => 13 ten-bit lookups of α_0 + 2^130 - t_p
|
|
|
|
|
// => z_13_alpha_0_prime = 0
|
|
|
|
|
//
|
|
|
|
|
let canon_checks = {
|
2021-07-07 20:24:18 -07:00
|
|
|
|
// alpha_0_hi_120 = z_44 - 2^120 z_84
|
|
|
|
|
let alpha_0_hi_120 = {
|
|
|
|
|
let two_pow_120 =
|
|
|
|
|
Expression::Constant(pallas::Base::from_u128(1 << 60).square());
|
|
|
|
|
z_44_alpha.clone() - z_84_alpha * two_pow_120
|
|
|
|
|
};
|
|
|
|
|
// a_43 = z_43 - (2^3)z_44
|
2021-07-08 19:23:40 -07:00
|
|
|
|
let a_43 = z_43_alpha - z_44_alpha * *H_BASE;
|
2021-07-07 20:24:18 -07:00
|
|
|
|
|
2021-07-07 00:53:43 -07:00
|
|
|
|
std::iter::empty()
|
2021-07-07 03:38:43 -07:00
|
|
|
|
.chain(Some(("MSB = 1 => alpha_1 = 0", alpha_2.clone() * alpha_1)))
|
2021-07-07 00:53:43 -07:00
|
|
|
|
.chain(Some((
|
2021-07-07 20:24:18 -07:00
|
|
|
|
"MSB = 1 => alpha_0_hi_120 = 0",
|
|
|
|
|
alpha_2.clone() * alpha_0_hi_120,
|
|
|
|
|
)))
|
|
|
|
|
.chain(Some((
|
|
|
|
|
"MSB = 1 => a_43 = 0 or 1",
|
|
|
|
|
alpha_2.clone() * range_check(a_43, 2),
|
2021-07-07 00:53:43 -07:00
|
|
|
|
)))
|
|
|
|
|
.chain(Some((
|
|
|
|
|
"MSB = 1 => z_13_alpha_0_prime = 0",
|
|
|
|
|
alpha_2 * z_13_alpha_0_prime,
|
|
|
|
|
)))
|
|
|
|
|
};
|
2021-06-18 02:41:13 -07:00
|
|
|
|
|
2021-07-07 00:53:43 -07:00
|
|
|
|
canon_checks
|
|
|
|
|
.chain(decomposition_checks)
|
|
|
|
|
.chain(Some(("alpha_0_prime check", alpha_0_prime_check)))
|
2021-07-17 08:59:25 -07:00
|
|
|
|
.map(move |(name, poly)| (name, q_mul_fixed_base_field.clone() * poly))
|
2021-06-18 02:41:13 -07:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-06 20:26:32 -07:00
|
|
|
|
pub fn assign(
|
2021-06-18 02:41:13 -07:00
|
|
|
|
&self,
|
2021-07-06 20:26:32 -07:00
|
|
|
|
mut layouter: impl Layouter<pallas::Base>,
|
2021-06-18 02:41:13 -07:00
|
|
|
|
scalar: CellValue<pallas::Base>,
|
2021-07-14 22:05:22 -07:00
|
|
|
|
base: NullifierK,
|
2021-06-18 02:41:13 -07:00
|
|
|
|
) -> Result<EccPoint, Error> {
|
2021-07-07 21:00:52 -07:00
|
|
|
|
let (scalar, acc, mul_b) = layouter.assign_region(
|
|
|
|
|
|| "Base-field elem fixed-base mul (incomplete addition)",
|
2021-07-06 20:26:32 -07:00
|
|
|
|
|mut region| {
|
|
|
|
|
let offset = 0;
|
|
|
|
|
|
|
|
|
|
// Decompose scalar
|
2021-07-16 21:10:13 -07:00
|
|
|
|
let scalar = {
|
2021-07-24 08:54:54 -07:00
|
|
|
|
let running_sum = self.running_sum_config.copy_decompose(
|
2021-07-16 21:10:13 -07:00
|
|
|
|
&mut region,
|
|
|
|
|
offset,
|
|
|
|
|
scalar,
|
|
|
|
|
true,
|
|
|
|
|
constants::L_ORCHARD_BASE,
|
|
|
|
|
constants::NUM_WINDOWS,
|
|
|
|
|
)?;
|
|
|
|
|
EccBaseFieldElemFixed {
|
2021-07-24 08:54:54 -07:00
|
|
|
|
base_field_elem: running_sum[0],
|
2021-07-16 21:10:13 -07:00
|
|
|
|
running_sum: (*running_sum).as_slice().try_into().unwrap(),
|
|
|
|
|
}
|
|
|
|
|
};
|
2021-07-06 20:26:32 -07:00
|
|
|
|
|
|
|
|
|
let (acc, mul_b) = self.super_config.assign_region_inner(
|
|
|
|
|
&mut region,
|
|
|
|
|
offset,
|
|
|
|
|
&(&scalar).into(),
|
|
|
|
|
base.into(),
|
2021-07-09 17:50:07 -07:00
|
|
|
|
self.q_mul_fixed_running_sum,
|
2021-07-06 20:26:32 -07:00
|
|
|
|
)?;
|
|
|
|
|
|
2021-07-07 21:00:52 -07:00
|
|
|
|
Ok((scalar, acc, mul_b))
|
|
|
|
|
},
|
|
|
|
|
)?;
|
2021-07-06 20:26:32 -07:00
|
|
|
|
|
2021-07-07 21:00:52 -07:00
|
|
|
|
// Add to the accumulator and return the final result as `[scalar]B`.
|
|
|
|
|
let result = layouter.assign_region(
|
|
|
|
|
|| "Base-field elem fixed-base mul (complete addition)",
|
|
|
|
|
|mut region| {
|
2021-09-27 01:49:08 -07:00
|
|
|
|
self.super_config.add_config.assign_region(
|
|
|
|
|
&mul_b.into(),
|
|
|
|
|
&acc.into(),
|
|
|
|
|
0,
|
|
|
|
|
&mut region,
|
|
|
|
|
)
|
2021-07-06 20:26:32 -07:00
|
|
|
|
},
|
2021-07-07 00:53:43 -07:00
|
|
|
|
)?;
|
|
|
|
|
|
2021-07-07 21:00:52 -07:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
// Check that the correct multiple is obtained.
|
|
|
|
|
{
|
|
|
|
|
use group::Curve;
|
|
|
|
|
|
|
|
|
|
let base: super::OrchardFixedBases = base.into();
|
|
|
|
|
let scalar = &scalar
|
|
|
|
|
.base_field_elem()
|
|
|
|
|
.value()
|
|
|
|
|
.map(|scalar| pallas::Scalar::from_bytes(&scalar.to_bytes()).unwrap());
|
|
|
|
|
let real_mul = scalar.map(|scalar| base.generator() * scalar);
|
|
|
|
|
let result = result.point();
|
|
|
|
|
|
|
|
|
|
if let (Some(real_mul), Some(result)) = (real_mul, result) {
|
|
|
|
|
assert_eq!(real_mul.to_affine(), result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-07 00:53:43 -07:00
|
|
|
|
// We want to enforce canonicity of a 255-bit base field element, α.
|
|
|
|
|
// That is, we want to check that 0 ≤ α < p, where p is Pallas base
|
|
|
|
|
// field modulus p = 2^254 + t_p
|
|
|
|
|
// = 2^254 + 45560315531419706090280762371685220353.
|
|
|
|
|
// Note that t_p < 2^130.
|
|
|
|
|
//
|
2021-07-07 22:14:53 -07:00
|
|
|
|
// α has been decomposed into three pieces in little-endian order:
|
2021-07-07 00:53:43 -07:00
|
|
|
|
// α = α_0 (252 bits) || α_1 (2 bits) || α_2 (1 bit).
|
2021-07-07 22:14:53 -07:00
|
|
|
|
// = α_0 + 2^252 α_1 + 2^254 α_2.
|
2021-07-07 00:53:43 -07:00
|
|
|
|
//
|
|
|
|
|
// If the MSB α_2 = 1, then:
|
|
|
|
|
// - α_2 = 1 => α_1 = 0, and
|
|
|
|
|
// - α_2 = 1 => α_0 < t_p. To enforce this:
|
|
|
|
|
// - α_2 = 1 => 0 ≤ α_0 < 2^130
|
|
|
|
|
// => 13 ten-bit lookups of α_0
|
|
|
|
|
// - α_2 = 1 => 0 ≤ α_0 + 2^130 - t_p < 2^130
|
|
|
|
|
// => 13 ten-bit lookups of α_0 + 2^130 - t_p
|
|
|
|
|
// => z_13_alpha_0_prime = 0
|
|
|
|
|
//
|
|
|
|
|
let (alpha, running_sum) = (scalar.base_field_elem, &scalar.running_sum);
|
2021-07-24 08:54:54 -07:00
|
|
|
|
let z_43_alpha = running_sum[43];
|
|
|
|
|
let z_44_alpha = running_sum[44];
|
|
|
|
|
let z_84_alpha = running_sum[84];
|
2021-07-07 00:53:43 -07:00
|
|
|
|
|
2021-07-07 20:24:18 -07:00
|
|
|
|
// α_0 = α - z_84_alpha * 2^252
|
|
|
|
|
let alpha_0 = alpha
|
|
|
|
|
.value()
|
|
|
|
|
.zip(z_84_alpha.value())
|
|
|
|
|
.map(|(alpha, z_84_alpha)| {
|
|
|
|
|
let two_pow_252 = pallas::Base::from_u128(1 << 126).square();
|
|
|
|
|
alpha - z_84_alpha * two_pow_252
|
|
|
|
|
});
|
2021-07-07 00:53:43 -07:00
|
|
|
|
|
|
|
|
|
let (alpha_0_prime, z_13_alpha_0_prime) = {
|
|
|
|
|
// alpha_0_prime = alpha + 2^130 - t_p.
|
2021-07-07 20:24:18 -07:00
|
|
|
|
let alpha_0_prime = alpha_0.map(|alpha_0| {
|
2021-07-07 17:49:46 -07:00
|
|
|
|
let two_pow_130 = pallas::Base::from_u128(1 << 65).square();
|
2021-07-07 00:53:43 -07:00
|
|
|
|
let t_p = pallas::Base::from_u128(T_P);
|
|
|
|
|
alpha_0 + two_pow_130 - t_p
|
|
|
|
|
});
|
2021-07-24 08:54:54 -07:00
|
|
|
|
let zs = self.lookup_config.witness_check(
|
2021-07-07 00:53:43 -07:00
|
|
|
|
layouter.namespace(|| "Lookup range check alpha_0 + 2^130 - t_p"),
|
|
|
|
|
alpha_0_prime,
|
|
|
|
|
13,
|
|
|
|
|
false,
|
|
|
|
|
)?;
|
2021-07-24 08:54:54 -07:00
|
|
|
|
let alpha_0_prime = zs[0];
|
2021-07-07 00:53:43 -07:00
|
|
|
|
|
|
|
|
|
(alpha_0_prime, zs[13])
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
layouter.assign_region(
|
|
|
|
|
|| "Canonicity checks",
|
|
|
|
|
|mut region| {
|
|
|
|
|
// Activate canonicity check gate
|
2021-07-17 08:59:25 -07:00
|
|
|
|
self.q_mul_fixed_base_field.enable(&mut region, 1)?;
|
2021-07-07 00:53:43 -07:00
|
|
|
|
|
|
|
|
|
// Offset 0
|
|
|
|
|
{
|
|
|
|
|
let offset = 0;
|
|
|
|
|
|
|
|
|
|
// Copy α
|
|
|
|
|
copy(
|
|
|
|
|
&mut region,
|
|
|
|
|
|| "Copy α",
|
|
|
|
|
self.canon_advices[0],
|
|
|
|
|
offset,
|
|
|
|
|
&alpha,
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
// z_84_alpha = the top three bits of alpha.
|
|
|
|
|
copy(
|
|
|
|
|
&mut region,
|
|
|
|
|
|| "Copy z_84_alpha",
|
|
|
|
|
self.canon_advices[2],
|
|
|
|
|
offset,
|
|
|
|
|
&z_84_alpha,
|
|
|
|
|
)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Offset 1
|
|
|
|
|
{
|
|
|
|
|
let offset = 1;
|
2021-07-07 20:24:18 -07:00
|
|
|
|
// Copy alpha_0_prime = alpha_0 + 2^130 - t_p.
|
|
|
|
|
// We constrain this in the custom gate to be derived correctly.
|
2021-07-07 00:53:43 -07:00
|
|
|
|
copy(
|
|
|
|
|
&mut region,
|
2021-07-07 20:24:18 -07:00
|
|
|
|
|| "Copy α_0 + 2^130 - t_p",
|
2021-07-07 00:53:43 -07:00
|
|
|
|
self.canon_advices[0],
|
|
|
|
|
offset,
|
2021-07-07 20:24:18 -07:00
|
|
|
|
&alpha_0_prime,
|
2021-07-07 00:53:43 -07:00
|
|
|
|
)?;
|
|
|
|
|
|
2021-07-07 20:24:18 -07:00
|
|
|
|
// Decompose α into three pieces,
|
|
|
|
|
// α = α_0 (252 bits) || α_1 (2 bits) || α_2 (1 bit).
|
|
|
|
|
// We only need to witness α_1 and α_2. α_0 is derived in the gate.
|
2021-07-07 00:53:43 -07:00
|
|
|
|
// Witness α_1 = α[252..=253]
|
|
|
|
|
let alpha_1 = alpha.value().map(|alpha| bitrange_subset(alpha, 252..254));
|
|
|
|
|
region.assign_advice(
|
|
|
|
|
|| "α_1 = α[252..=253]",
|
|
|
|
|
self.canon_advices[1],
|
|
|
|
|
offset,
|
|
|
|
|
|| alpha_1.ok_or(Error::SynthesisError),
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
// Witness the MSB α_2 = α[254]
|
|
|
|
|
let alpha_2 = alpha.value().map(|alpha| bitrange_subset(alpha, 254..255));
|
|
|
|
|
region.assign_advice(
|
|
|
|
|
|| "α_2 = α[254]",
|
|
|
|
|
self.canon_advices[2],
|
|
|
|
|
offset,
|
|
|
|
|
|| alpha_2.ok_or(Error::SynthesisError),
|
|
|
|
|
)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Offset 2
|
|
|
|
|
{
|
|
|
|
|
let offset = 2;
|
2021-07-07 20:24:18 -07:00
|
|
|
|
// Copy z_13_alpha_0_prime
|
2021-07-07 00:53:43 -07:00
|
|
|
|
copy(
|
|
|
|
|
&mut region,
|
2021-07-07 20:24:18 -07:00
|
|
|
|
|| "Copy z_13_alpha_0_prime",
|
2021-07-07 00:53:43 -07:00
|
|
|
|
self.canon_advices[0],
|
|
|
|
|
offset,
|
2021-07-07 20:24:18 -07:00
|
|
|
|
&z_13_alpha_0_prime,
|
2021-07-07 00:53:43 -07:00
|
|
|
|
)?;
|
|
|
|
|
|
2021-07-07 20:24:18 -07:00
|
|
|
|
// Copy z_44_alpha
|
2021-07-07 00:53:43 -07:00
|
|
|
|
copy(
|
|
|
|
|
&mut region,
|
2021-07-07 20:24:18 -07:00
|
|
|
|
|| "Copy z_44_alpha",
|
2021-07-07 00:53:43 -07:00
|
|
|
|
self.canon_advices[1],
|
|
|
|
|
offset,
|
2021-07-07 20:24:18 -07:00
|
|
|
|
&z_44_alpha,
|
2021-07-07 00:53:43 -07:00
|
|
|
|
)?;
|
|
|
|
|
|
2021-07-07 20:24:18 -07:00
|
|
|
|
// Copy z_43_alpha
|
2021-07-07 00:53:43 -07:00
|
|
|
|
copy(
|
|
|
|
|
&mut region,
|
2021-07-07 20:24:18 -07:00
|
|
|
|
|| "Copy z_43_alpha",
|
2021-07-07 00:53:43 -07:00
|
|
|
|
self.canon_advices[2],
|
|
|
|
|
offset,
|
2021-07-07 20:24:18 -07:00
|
|
|
|
&z_43_alpha,
|
2021-07-07 00:53:43 -07:00
|
|
|
|
)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
},
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
Ok(result)
|
2021-06-18 02:41:13 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub mod tests {
|
2021-07-08 00:06:47 -07:00
|
|
|
|
use group::Curve;
|
2021-06-18 02:41:13 -07:00
|
|
|
|
use halo2::{
|
|
|
|
|
circuit::{Chip, Layouter},
|
|
|
|
|
plonk::Error,
|
|
|
|
|
};
|
|
|
|
|
use pasta_curves::{arithmetic::FieldExt, pallas};
|
|
|
|
|
|
|
|
|
|
use crate::circuit::gadget::{
|
|
|
|
|
ecc::{
|
2021-07-14 22:05:22 -07:00
|
|
|
|
chip::{EccChip, NullifierK},
|
2021-09-27 03:14:01 -07:00
|
|
|
|
FixedPointBaseField, NonIdentityPoint, Point,
|
2021-06-18 02:41:13 -07:00
|
|
|
|
},
|
2021-07-09 18:09:26 -07:00
|
|
|
|
utilities::UtilitiesInstructions,
|
2021-06-18 02:41:13 -07:00
|
|
|
|
};
|
|
|
|
|
use crate::constants;
|
|
|
|
|
|
|
|
|
|
pub fn test_mul_fixed_base_field(
|
|
|
|
|
chip: EccChip,
|
|
|
|
|
mut layouter: impl Layouter<pallas::Base>,
|
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
// nullifier_k
|
2021-07-14 22:05:22 -07:00
|
|
|
|
let nullifier_k = NullifierK;
|
2021-06-18 02:41:13 -07:00
|
|
|
|
test_single_base(
|
|
|
|
|
chip.clone(),
|
|
|
|
|
layouter.namespace(|| "nullifier_k"),
|
2021-07-14 22:05:22 -07:00
|
|
|
|
FixedPointBaseField::from_inner(chip, nullifier_k),
|
2021-07-08 00:06:47 -07:00
|
|
|
|
nullifier_k.generator(),
|
2021-07-14 22:05:22 -07:00
|
|
|
|
)
|
2021-06-18 02:41:13 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(clippy::op_ref)]
|
|
|
|
|
fn test_single_base(
|
|
|
|
|
chip: EccChip,
|
|
|
|
|
mut layouter: impl Layouter<pallas::Base>,
|
2021-07-14 22:05:22 -07:00
|
|
|
|
base: FixedPointBaseField<pallas::Affine, EccChip>,
|
2021-07-08 00:06:47 -07:00
|
|
|
|
base_val: pallas::Affine,
|
2021-06-18 02:41:13 -07:00
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
let column = chip.config().advices[0];
|
2021-07-09 22:19:42 -07:00
|
|
|
|
|
2021-09-27 03:14:01 -07:00
|
|
|
|
fn constrain_equal_non_id(
|
2021-07-08 00:06:47 -07:00
|
|
|
|
chip: EccChip,
|
|
|
|
|
mut layouter: impl Layouter<pallas::Base>,
|
|
|
|
|
base_val: pallas::Affine,
|
|
|
|
|
scalar_val: pallas::Base,
|
|
|
|
|
result: Point<pallas::Affine, EccChip>,
|
|
|
|
|
) -> Result<(), Error> {
|
2021-07-08 19:23:40 -07:00
|
|
|
|
// Move scalar from base field into scalar field (which always fits for Pallas).
|
2021-07-08 00:06:47 -07:00
|
|
|
|
let scalar = pallas::Scalar::from_bytes(&scalar_val.to_bytes()).unwrap();
|
2021-09-27 03:14:01 -07:00
|
|
|
|
let expected = NonIdentityPoint::new(
|
2021-07-08 00:06:47 -07:00
|
|
|
|
chip,
|
|
|
|
|
layouter.namespace(|| "expected point"),
|
|
|
|
|
Some((base_val * scalar).to_affine()),
|
|
|
|
|
)?;
|
|
|
|
|
result.constrain_equal(layouter.namespace(|| "constrain result"), &expected)
|
|
|
|
|
}
|
2021-06-18 02:41:13 -07:00
|
|
|
|
|
|
|
|
|
// [a]B
|
|
|
|
|
{
|
|
|
|
|
let scalar_fixed = pallas::Base::rand();
|
2021-07-08 00:06:47 -07:00
|
|
|
|
let result = {
|
|
|
|
|
let scalar_fixed = chip.load_private(
|
|
|
|
|
layouter.namespace(|| "random base field element"),
|
|
|
|
|
column,
|
|
|
|
|
Some(scalar_fixed),
|
|
|
|
|
)?;
|
2021-07-14 22:05:22 -07:00
|
|
|
|
base.mul(layouter.namespace(|| "random [a]B"), scalar_fixed)?
|
2021-07-08 00:06:47 -07:00
|
|
|
|
};
|
2021-09-27 03:14:01 -07:00
|
|
|
|
constrain_equal_non_id(
|
2021-07-08 00:06:47 -07:00
|
|
|
|
chip.clone(),
|
|
|
|
|
layouter.namespace(|| "random [a]B"),
|
|
|
|
|
base_val,
|
|
|
|
|
scalar_fixed,
|
|
|
|
|
result,
|
2021-06-18 02:41:13 -07:00
|
|
|
|
)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// There is a single canonical sequence of window values for which a doubling occurs on the last step:
|
|
|
|
|
// 1333333333333333333333333333333333333333333333333333333333333333333333333333333333334 in octal.
|
|
|
|
|
// (There is another *non-canonical* sequence
|
|
|
|
|
// 5333333333333333333333333333333333333333332711161673731021062440252244051273333333333 in octal.)
|
|
|
|
|
{
|
|
|
|
|
let h = pallas::Base::from_u64(constants::H as u64);
|
|
|
|
|
let scalar_fixed = "1333333333333333333333333333333333333333333333333333333333333333333333333333333333334"
|
|
|
|
|
.chars()
|
|
|
|
|
.fold(pallas::Base::zero(), |acc, c| {
|
|
|
|
|
acc * &h + &pallas::Base::from_u64(c.to_digit(8).unwrap().into())
|
|
|
|
|
});
|
2021-07-08 00:06:47 -07:00
|
|
|
|
let result = {
|
|
|
|
|
let scalar_fixed = chip.load_private(
|
|
|
|
|
layouter.namespace(|| "mul with double"),
|
|
|
|
|
column,
|
|
|
|
|
Some(scalar_fixed),
|
|
|
|
|
)?;
|
2021-07-14 22:05:22 -07:00
|
|
|
|
base.mul(layouter.namespace(|| "mul with double"), scalar_fixed)?
|
2021-07-08 00:06:47 -07:00
|
|
|
|
};
|
2021-09-27 03:14:01 -07:00
|
|
|
|
constrain_equal_non_id(
|
2021-07-08 00:06:47 -07:00
|
|
|
|
chip.clone(),
|
2021-06-18 02:41:13 -07:00
|
|
|
|
layouter.namespace(|| "mul with double"),
|
2021-07-08 00:06:47 -07:00
|
|
|
|
base_val,
|
|
|
|
|
scalar_fixed,
|
|
|
|
|
result,
|
2021-06-18 02:41:13 -07:00
|
|
|
|
)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [0]B should return (0,0) since it uses complete addition
|
|
|
|
|
// on the last step.
|
|
|
|
|
{
|
|
|
|
|
let scalar_fixed = pallas::Base::zero();
|
2021-07-08 00:06:47 -07:00
|
|
|
|
let result = {
|
|
|
|
|
let scalar_fixed =
|
|
|
|
|
chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_fixed))?;
|
2021-07-14 22:05:22 -07:00
|
|
|
|
base.mul(layouter.namespace(|| "mul by zero"), scalar_fixed)?
|
2021-07-08 00:06:47 -07:00
|
|
|
|
};
|
2021-09-27 03:14:01 -07:00
|
|
|
|
assert!(result.is_identity().unwrap());
|
2021-06-18 02:41:13 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [-1]B is the largest base field element
|
|
|
|
|
{
|
|
|
|
|
let scalar_fixed = -pallas::Base::one();
|
2021-07-08 00:06:47 -07:00
|
|
|
|
let result = {
|
|
|
|
|
let scalar_fixed =
|
|
|
|
|
chip.load_private(layouter.namespace(|| "-1"), column, Some(scalar_fixed))?;
|
2021-07-14 22:05:22 -07:00
|
|
|
|
base.mul(layouter.namespace(|| "mul by -1"), scalar_fixed)?
|
2021-07-08 00:06:47 -07:00
|
|
|
|
};
|
2021-09-27 03:14:01 -07:00
|
|
|
|
constrain_equal_non_id(
|
2021-07-08 00:06:47 -07:00
|
|
|
|
chip,
|
2021-06-18 02:41:13 -07:00
|
|
|
|
layouter.namespace(|| "mul by -1"),
|
2021-07-08 00:06:47 -07:00
|
|
|
|
base_val,
|
|
|
|
|
scalar_fixed,
|
|
|
|
|
result,
|
2021-06-18 02:41:13 -07:00
|
|
|
|
)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|