use super::EccInstructions; use crate::circuit::gadget::utilities::{copy, CellValue, Var}; use crate::constants::{self, OrchardFixedBasesFull, ValueCommitV}; use arrayvec::ArrayVec; use group::prime::PrimeCurveAffine; use halo2::{ circuit::{Chip, Layouter}, plonk::{Advice, Column, ConstraintSystem, Error, Fixed, Permutation, Selector}, }; use pasta_curves::{arithmetic::CurveAffine, pallas}; pub(super) mod add; pub(super) mod add_incomplete; pub(super) mod mul; pub(super) mod mul_fixed; pub(super) mod witness_point; pub(super) mod witness_scalar_fixed; /// A curve point represented in affine (x, y) coordinates. Each coordinate is /// assigned to a cell. #[derive(Clone, Debug)] pub struct EccPoint { /// x-coordinate x: CellValue, /// y-coordinate y: CellValue, } impl EccPoint { /// Constructs a point from its coordinates, without checking they are on the curve. /// /// This is an internal API that we only use where we know we have a valid curve point /// (specifically inside Sinsemilla). pub(in crate::circuit::gadget) fn from_coordinates_unchecked( x: CellValue, y: CellValue, ) -> Self { EccPoint { x, y } } /// Returns the value of this curve point, if known. pub fn point(&self) -> Option { match (self.x.value(), self.y.value()) { (Some(x), Some(y)) => { if x == pallas::Base::zero() && y == pallas::Base::zero() { Some(pallas::Affine::identity()) } else { Some(pallas::Affine::from_xy(x, y).unwrap()) } } _ => None, } } /// The cell containing the affine short-Weierstrass x-coordinate, /// or 0 for the zero point. pub fn x(&self) -> CellValue { self.x } /// The cell containing the affine short-Weierstrass y-coordinate, /// or 0 for the zero point. pub fn y(&self) -> CellValue { self.y } } /// Configuration for the ECC chip #[derive(Clone, Debug, Eq, PartialEq)] #[allow(non_snake_case)] pub struct EccConfig { /// Advice columns needed by instructions in the ECC chip. pub advices: [Column; 10], /// Coefficients of interpolation polynomials for x-coordinates (used in fixed-base scalar multiplication) pub lagrange_coeffs: [Column; constants::H], /// Fixed z such that y + z = u^2 some square, and -y + z is a non-square. (Used in fixed-base scalar multiplication) pub fixed_z: Column, /// Incomplete addition pub q_add_incomplete: Selector, /// Complete addition pub q_add: Selector, /// Variable-base scalar multiplication (hi half) pub q_mul_hi: Selector, /// Variable-base scalar multiplication (lo half) pub q_mul_lo: Selector, /// Selector used in scalar decomposition for variable-base scalar mul pub q_mul_decompose_var: Selector, /// Selector used in scalar decomposition for variable-base scalar mul pub q_init_z: Selector, /// Variable-base scalar multiplication (final scalar) pub q_mul_complete: Selector, /// Fixed-base full-width scalar multiplication pub q_mul_fixed: Selector, /// Fixed-base signed short scalar multiplication pub q_mul_fixed_short: Selector, /// Witness point pub q_point: Selector, /// Witness full-width scalar for fixed-base scalar mul pub q_scalar_fixed: Selector, /// Witness signed short scalar for full-width fixed-base scalar mul pub q_scalar_fixed_short: Selector, /// Permutation pub perm: Permutation, } /// A chip implementing EccInstructions #[derive(Clone, Debug, Eq, PartialEq)] pub struct EccChip { config: EccConfig, } impl Chip for EccChip { type Config = EccConfig; type Loaded = (); fn config(&self) -> &Self::Config { &self.config } fn loaded(&self) -> &Self::Loaded { &() } } impl EccChip { pub fn construct(config: >::Config) -> Self { Self { config } } #[allow(non_snake_case)] pub fn configure( meta: &mut ConstraintSystem, advices: [Column; 10], perm: Permutation, ) -> >::Config { let config = EccConfig { advices, lagrange_coeffs: [ meta.fixed_column(), meta.fixed_column(), meta.fixed_column(), meta.fixed_column(), meta.fixed_column(), meta.fixed_column(), meta.fixed_column(), meta.fixed_column(), ], fixed_z: meta.fixed_column(), q_add_incomplete: meta.selector(), q_add: meta.selector(), q_mul_hi: meta.selector(), q_mul_lo: meta.selector(), q_mul_decompose_var: meta.selector(), q_init_z: meta.selector(), q_mul_complete: meta.selector(), q_mul_fixed: meta.selector(), q_mul_fixed_short: meta.selector(), q_point: meta.selector(), q_scalar_fixed: meta.selector(), q_scalar_fixed_short: meta.selector(), perm, }; // Create witness point gate { let config: witness_point::Config = (&config).into(); config.create_gate(meta); } // Create incomplete point addition gate { let config: add_incomplete::Config = (&config).into(); config.create_gate(meta); } // Create complete point addition gate { let add_config: add::Config = (&config).into(); add_config.create_gate(meta); } // Create variable-base scalar mul gates { let mul_config: mul::Config = (&config).into(); mul_config.create_gate(meta); } // Create witness scalar_fixed gate that applies to both full-width and // short scalars { let config: witness_scalar_fixed::Config = (&config).into(); config.create_gate(meta); } // Create witness scalar_fixed gate that only apploes to short scalars { let config: witness_scalar_fixed::short::Config = (&config).into(); config.create_gate(meta); } // Create fixed-base scalar mul gate that os used in both full-width // and short multiplication. { let mul_fixed_config: mul_fixed::Config<{ constants::NUM_WINDOWS }> = (&config).into(); mul_fixed_config.create_gate(meta); } // Create gate that is only used in short fixed-base scalar mul. { let short_config: mul_fixed::short::Config<{ constants::NUM_WINDOWS_SHORT }> = (&config).into(); short_config.create_gate(meta); } config } } /// A full-width scalar used for fixed-base scalar multiplication. /// This is decomposed in chunks of `window_width` bits in little-endian order. /// For example, if `window_width` = 3, we will have [k_0, k_1, ..., k_n] /// where `scalar = k_0 + k_1 * (2^3) + ... + k_n * (2^3)^n` and each `k_i` is /// in the range [0..2^3). #[derive(Clone, Debug)] pub struct EccScalarFixed { value: Option, windows: ArrayVec, { constants::NUM_WINDOWS }>, } /// A signed short scalar used for fixed-base scalar multiplication. /// This is decomposed in chunks of `window_width` bits in little-endian order. /// For example, if `window_width` = 3, we will have [k_0, k_1, ..., k_n] /// where `scalar = k_0 + k_1 * (2^3) + ... + k_n * (2^3)^n` and each `k_i` is /// in the range [0..2^3). #[derive(Clone, Debug)] pub struct EccScalarFixedShort { magnitude: Option, sign: CellValue, windows: ArrayVec, { constants::NUM_WINDOWS_SHORT }>, } impl EccInstructions for EccChip { type ScalarFixed = EccScalarFixed; type ScalarFixedShort = EccScalarFixedShort; type ScalarVar = CellValue; type Point = EccPoint; type X = CellValue; type FixedPoints = OrchardFixedBasesFull; type FixedPointsShort = ValueCommitV; fn constrain_equal( &self, layouter: &mut impl Layouter, a: &Self::Point, b: &Self::Point, ) -> Result<(), Error> { let config = self.config().clone(); layouter.assign_region( || "constrain equal", |mut region| { // Constrain x-coordinates region.constrain_equal(&config.perm, a.x().cell(), b.x().cell())?; // Constrain x-coordinates region.constrain_equal(&config.perm, a.y().cell(), b.y().cell()) }, ) } fn witness_scalar_var( &self, layouter: &mut impl Layouter, value: Option, ) -> Result { let config = self.config().clone(); layouter.assign_region( || "Witness scalar for variable-base mul", |mut region| { let cell = region.assign_advice( || "Witness scalar var", config.advices[0], 0, || value.ok_or(Error::SynthesisError), )?; Ok(CellValue::new(cell, value)) }, ) } fn witness_scalar_fixed( &self, layouter: &mut impl Layouter, value: Option, ) -> Result { let config: witness_scalar_fixed::full_width::Config = self.config().into(); layouter.assign_region( || "witness scalar for fixed-base mul", |mut region| config.assign_region(value, 0, &mut region), ) } fn witness_scalar_fixed_short( &self, layouter: &mut impl Layouter, value: Option, ) -> Result { let config: witness_scalar_fixed::short::Config = self.config().into(); layouter.assign_region( || "witness short scalar for fixed-base mul", |mut region| config.assign_region(value, 0, &mut region), ) } fn witness_point( &self, layouter: &mut impl Layouter, value: Option, ) -> Result { let config: witness_point::Config = self.config().into(); layouter.assign_region( || "witness point", |mut region| config.assign_region(value, 0, &mut region), ) } fn extract_p(point: &Self::Point) -> &Self::X { &point.x } fn add_incomplete( &self, layouter: &mut impl Layouter, a: &Self::Point, b: &Self::Point, ) -> Result { let config: add_incomplete::Config = self.config().into(); layouter.assign_region( || "incomplete point addition", |mut region| config.assign_region(a, b, 0, &mut region), ) } fn add( &self, layouter: &mut impl Layouter, a: &Self::Point, b: &Self::Point, ) -> Result { let config: add::Config = self.config().into(); layouter.assign_region( || "complete point addition", |mut region| config.assign_region(a, b, 0, &mut region), ) } fn mul( &self, layouter: &mut impl Layouter, scalar: &Self::ScalarVar, base: &Self::Point, ) -> Result { let config: mul::Config = self.config().into(); layouter.assign_region( || "variable-base scalar mul", |mut region| config.assign_region(scalar, base, 0, &mut region), ) } fn mul_fixed( &self, layouter: &mut impl Layouter, scalar: &Self::ScalarFixed, base: &Self::FixedPoints, ) -> Result { let config: mul_fixed::full_width::Config<{ constants::NUM_WINDOWS }> = self.config().into(); layouter.assign_region( || format!("fixed-base mul of {:?}", base), |mut region| config.assign_region(scalar, *base, 0, &mut region), ) } fn mul_fixed_short( &self, layouter: &mut impl Layouter, scalar: &Self::ScalarFixedShort, base: &Self::FixedPointsShort, ) -> Result { let config: mul_fixed::short::Config<{ constants::NUM_WINDOWS_SHORT }> = self.config().into(); layouter.assign_region( || format!("short fixed-base mul of {:?}", base), |mut region| config.assign_region(scalar, base, 0, &mut region), ) } }