diff --git a/Cargo.toml b/Cargo.toml index 1fb2e67c..828299e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ subtle = "2.3" [dependencies.halo2] git = "https://github.com/zcash/halo2.git" -rev = "6acacf1aca12f34fc311aa59056e40adc0e6d8bd" +rev = "cae6f6af725cf1f5bc94e126a0b41e9ac602a302" [dependencies.pasta_curves] git = "https://github.com/zcash/pasta_curves.git" diff --git a/src/circuit/gadget/ecc.rs b/src/circuit/gadget/ecc.rs index 049c6b9e..31d252e3 100644 --- a/src/circuit/gadget/ecc.rs +++ b/src/circuit/gadget/ecc.rs @@ -1,140 +1,378 @@ //! Gadgets for elliptic curve operations. -use std::fmt; +use ff::Field; +use std::fmt::Debug; use halo2::{ - arithmetic::CurveAffine, + arithmetic::{CurveAffine, FieldExt}, circuit::{Chip, Layouter}, plonk::Error, }; -/// Trait allowing circuit's fixed points to be enumerated. -pub trait FixedPoints: Clone + fmt::Debug {} - /// The set of circuit instructions required to use the ECC gadgets. -pub trait EccInstructions: Chip { - /// Variable representing an element of the elliptic curve's scalar field. - type Scalar: Clone + fmt::Debug; +pub trait EccInstructions: Chip { + /// Variable representing an element of the elliptic curve's base field, that + /// is used as a scalar in variable-base scalar mul. + /// + /// It is not true in general that a scalar field element fits in a curve's + /// base field, and in particular it is untrue for the Pallas curve, whose + /// scalar field `Fq` is larger than its base field `Fp`. + /// + /// However, the only use of variable-base scalar mul in the Orchard protocol + /// is in deriving diversified addresses `[ivk] g_d`, and `ivk` is guaranteed + /// to be in the base field of the curve. (See non-normative notes in + /// https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents.) + type ScalarVar: Clone + Debug; + /// Variable representing a full-width element of the elliptic curve's + /// scalar field, to be used for fixed-base scalar mul. + type ScalarFixed: Clone + Debug; + /// Variable representing a signed short element of the elliptic curve's + /// scalar field, to be used for fixed-base scalar mul. + /// + /// A `ScalarFixedShort` must be in the range [-(2^64 - 1), 2^64 - 1]. + type ScalarFixedShort: Clone + Debug; /// Variable representing an elliptic curve point. - type Point: Clone + fmt::Debug; + type Point: Clone + Debug; + /// Variable representing the affine short Weierstrass x-coordinate of an + /// elliptic curve point. + type X: Clone + Debug; /// Variable representing the set of fixed bases in the circuit. - type FixedPoints: FixedPoints; + type FixedPoints: Clone + Debug; + /// Variable representing the set of fixed bases to be used in scalar + /// multiplication with a short signed exponent. + type FixedPointsShort: Clone + Debug; /// Variable representing a fixed elliptic curve point (constant in the circuit). - type FixedPoint: Clone + fmt::Debug; + type FixedPoint: Clone + Debug; + /// Variable representing a fixed elliptic curve point (constant in the circuit) + /// to be used in scalar multiplication with a short signed exponent. + type FixedPointShort: Clone + Debug; - /// Witnesses the given scalar as a private input to the circuit. - fn witness_scalar( - layouter: &mut impl Layouter, + /// Witnesses the given base field element as a private input to the circuit + /// for variable-base scalar mul. + fn witness_scalar_var( + &self, + layouter: &mut impl Layouter, + value: Option, + ) -> Result; + + /// Witnesses the given full-width scalar as a private input to the circuit + /// for fixed-base scalar mul. + fn witness_scalar_fixed( + &self, + layouter: &mut impl Layouter, value: Option, - ) -> Result; + ) -> Result; + + /// Witnesses the given signed short scalar as a private input to the circuit + /// for fixed-base scalar mul. + fn witness_scalar_fixed_short( + &self, + layouter: &mut impl Layouter, + value: Option, + ) -> Result; /// Witnesses the given point as a private input to the circuit. fn witness_point( - layouter: &mut impl Layouter, + &self, + layouter: &mut impl Layouter, value: Option, ) -> Result; - /// Gets a fixed point into the circuit. - fn get_fixed( - layouter: &mut impl Layouter, - fixed_points: Self::FixedPoints, - ) -> Result; + /// Extracts the x-coordinate of a point. + fn extract_p(point: &Self::Point) -> &Self::X; - /// Performs point addition, returning `a + b`. + /// Returns a fixed point that had been previously loaded into the circuit. + /// The pre-loaded cells are used to set up equality constraints in other + /// parts of the circuit where the fixed base is used. + fn get_fixed(&self, fixed_points: Self::FixedPoints) -> Result; + + /// Returns a fixed point to be used in scalar multiplication with a signed + /// short exponent. + fn get_fixed_short( + &self, + fixed_points: Self::FixedPointsShort, + ) -> Result; + + /// Performs incomplete point addition, returning `a + b`. + /// + /// This returns an error in exceptional cases. + fn add_incomplete( + &self, + layouter: &mut impl Layouter, + a: &Self::Point, + b: &Self::Point, + ) -> Result; + + /// Performs complete point addition, returning `a + b`. fn add( - layouter: &mut impl Layouter, + &self, + layouter: &mut impl Layouter, a: &Self::Point, b: &Self::Point, ) -> Result; /// Performs point doubling, returning `[2] a`. - fn double(layouter: &mut impl Layouter, a: &Self::Point) -> Result; + fn double( + &self, + layouter: &mut impl Layouter, + a: &Self::Point, + ) -> Result; /// Performs variable-base scalar multiplication, returning `[scalar] base`. fn mul( - layouter: &mut impl Layouter, - scalar: &Self::Scalar, + &self, + layouter: &mut impl Layouter, + scalar: &Self::ScalarVar, base: &Self::Point, ) -> Result; - /// Performs fixed-base scalar multiplication, returning `[scalar] base`. + /// Performs fixed-base scalar multiplication using a full-width scalar, returning `[scalar] base`. fn mul_fixed( - layouter: &mut impl Layouter, - scalar: &Self::Scalar, + &self, + layouter: &mut impl Layouter, + scalar: &Self::ScalarFixed, base: &Self::FixedPoint, ) -> Result; + + /// Performs fixed-base scalar multiplication using a short signed scalar, returning `[scalar] base`. + fn mul_fixed_short( + &self, + layouter: &mut impl Layouter, + scalar: &Self::ScalarFixedShort, + base: &Self::FixedPointShort, + ) -> Result; } -/// An element of the given elliptic curve's scalar field. +/// An element of the given elliptic curve's base field, that is used as a scalar +/// in variable-base scalar mul. +/// +/// It is not true in general that a scalar field element fits in a curve's +/// base field, and in particular it is untrue for the Pallas curve, whose +/// scalar field `Fq` is larger than its base field `Fp`. +/// +/// However, the only use of variable-base scalar mul in the Orchard protocol +/// is in deriving diversified addresses `[ivk] g_d`, and `ivk` is guaranteed +/// to be in the base field of the curve. (See non-normative notes in +/// https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents.) #[derive(Debug)] -pub struct Scalar> { - inner: EccChip::Scalar, +pub struct ScalarVar + Clone + Debug + Eq> { + chip: EccChip, + inner: EccChip::ScalarVar, } -impl> Scalar { - /// Constructs a new point with the given value. +impl + Clone + Debug + Eq> ScalarVar { + /// Constructs a new ScalarVar with the given value. pub fn new( - mut layouter: impl Layouter, + chip: EccChip, + mut layouter: impl Layouter, + value: Option, + ) -> Result { + chip.witness_scalar_var(&mut layouter, value) + .map(|inner| ScalarVar { chip, inner }) + } +} + +/// A full-width element of the given elliptic curve's scalar field, to be used for fixed-base scalar mul. +#[derive(Debug)] +pub struct ScalarFixed + Clone + Debug + Eq> { + chip: EccChip, + inner: EccChip::ScalarFixed, +} + +impl + Clone + Debug + Eq> ScalarFixed { + /// Constructs a new ScalarFixed with the given value. + pub fn new( + chip: EccChip, + mut layouter: impl Layouter, value: Option, ) -> Result { - EccChip::witness_scalar(&mut layouter, value).map(|inner| Scalar { inner }) + chip.witness_scalar_fixed(&mut layouter, value) + .map(|inner| ScalarFixed { chip, inner }) + } +} + +/// A signed short element of the given elliptic curve's scalar field, to be used for fixed-base scalar mul. +#[derive(Debug)] +pub struct ScalarFixedShort + Clone + Debug + Eq> { + chip: EccChip, + inner: EccChip::ScalarFixedShort, +} + +impl + Clone + Debug + Eq> + ScalarFixedShort +{ + /// Constructs a new ScalarFixedShort with the given value. + /// + /// # Panics + /// + /// The short scalar must be in the range [-(2^64 - 1), (2^64 - 1)]. + pub fn new( + chip: EccChip, + mut layouter: impl Layouter, + value: Option, + ) -> Result { + // Check that the scalar is in the range [-(2^64 - 1), (2^64 - 1)] + if let Some(value) = value { + let mut sign = C::Scalar::one(); + + // T = (p-1) / 2 + let t = (C::Scalar::zero() - C::Scalar::one()) * C::Scalar::TWO_INV; + + if value > t { + sign = -sign; + } + let magnitude = value * sign; + assert!(magnitude < C::Scalar::from_u128(1 << 64)); + } + + chip.witness_scalar_fixed_short(&mut layouter, value) + .map(|inner| ScalarFixedShort { chip, inner }) } } /// An elliptic curve point over the given curve. #[derive(Debug)] -pub struct Point> { +pub struct Point + Clone + Debug + Eq> { + chip: EccChip, inner: EccChip::Point, } -impl> Point { +impl + Clone + Debug + Eq> Point { /// Constructs a new point with the given value. - pub fn new(mut layouter: impl Layouter, value: Option) -> Result { - EccChip::witness_point(&mut layouter, value).map(|inner| Point { inner }) + pub fn new( + chip: EccChip, + mut layouter: impl Layouter, + value: Option, + ) -> Result { + let point = chip.witness_point(&mut layouter, value); + point.map(|inner| Point { chip, inner }) } - /// Returns `self + other`. - pub fn add(&self, mut layouter: impl Layouter, other: &Self) -> Result { - EccChip::add(&mut layouter, &self.inner, &other.inner).map(|inner| Point { inner }) + /// Extracts the x-coordinate of a point. + pub fn extract_p(&self) -> X { + X::from_inner(self.chip.clone(), EccChip::extract_p(&self.inner).clone()) } - /// Returns `[2] self`. - pub fn double(&self, mut layouter: impl Layouter) -> Result { - EccChip::double(&mut layouter, &self.inner).map(|inner| Point { inner }) + /// Wraps the given point (obtained directly from an instruction) in a gadget. + pub fn from_inner(chip: EccChip, inner: EccChip::Point) -> Self { + Point { chip, inner } + } + + /// Returns `self + other` using complete addition. + pub fn add(&self, mut layouter: impl Layouter, other: &Self) -> Result { + assert_eq!(self.chip, other.chip); + self.chip + .add(&mut layouter, &self.inner, &other.inner) + .map(|inner| Point { + chip: self.chip.clone(), + inner, + }) + } + + /// Returns `self + other` using incomplete addition. + pub fn add_incomplete( + &self, + mut layouter: impl Layouter, + other: &Self, + ) -> Result { + assert_eq!(self.chip, other.chip); + self.chip + .add_incomplete(&mut layouter, &self.inner, &other.inner) + .map(|inner| Point { + chip: self.chip.clone(), + inner, + }) } /// Returns `[by] self`. pub fn mul( &self, - mut layouter: impl Layouter, - by: &Scalar, + mut layouter: impl Layouter, + by: &ScalarVar, ) -> Result { - EccChip::mul(&mut layouter, &by.inner, &self.inner).map(|inner| Point { inner }) + assert_eq!(self.chip, by.chip); + self.chip + .mul(&mut layouter, &by.inner, &self.inner) + .map(|inner| Point { + chip: self.chip.clone(), + inner, + }) } } -/// A constant elliptic curve point over the given curve, for which scalar multiplication -/// is more efficient. +/// The affine short Weierstrass x-coordinate of an elliptic curve point over the +/// given curve. #[derive(Debug)] -pub struct FixedPoint> { +pub struct X + Clone + Debug + Eq> { + chip: EccChip, + inner: EccChip::X, +} + +impl + Clone + Debug + Eq> X { + /// Wraps the given x-coordinate (obtained directly from an instruction) in a gadget. + pub fn from_inner(chip: EccChip, inner: EccChip::X) -> Self { + X { chip, inner } + } +} + +/// A constant elliptic curve point over the given curve, for which window tables have +/// been provided to make scalar multiplication more efficient. +#[derive(Clone, Debug)] +pub struct FixedPoint + Clone + Debug + Eq> { + chip: EccChip, inner: EccChip::FixedPoint, } -impl> FixedPoint { +impl + Clone + Debug + Eq> FixedPoint { /// Gets a reference to the specified fixed point in the circuit. - pub fn get( - mut layouter: impl Layouter, - point: EccChip::FixedPoints, - ) -> Result { - EccChip::get_fixed(&mut layouter, point).map(|inner| FixedPoint { inner }) + pub fn get(chip: EccChip, point: EccChip::FixedPoints) -> Result { + chip.get_fixed(point) + .map(|inner| FixedPoint { chip, inner }) } /// Returns `[by] self`. pub fn mul( &self, - mut layouter: impl Layouter, - by: &Scalar, + mut layouter: impl Layouter, + by: &ScalarFixed, ) -> Result, Error> { - EccChip::mul_fixed(&mut layouter, &by.inner, &self.inner).map(|inner| Point { inner }) + assert_eq!(self.chip, by.chip); + self.chip + .mul_fixed(&mut layouter, &by.inner, &self.inner) + .map(|inner| Point { + chip: self.chip.clone(), + inner, + }) + } +} + +/// A constant elliptic curve point over the given curve, used in scalar multiplication +/// with a short signed exponent +#[derive(Clone, Debug)] +pub struct FixedPointShort + Clone + Debug + Eq> { + chip: EccChip, + inner: EccChip::FixedPointShort, +} + +impl + Clone + Debug + Eq> FixedPointShort { + /// Gets a reference to the specified fixed point in the circuit. + pub fn get(chip: EccChip, point: EccChip::FixedPointsShort) -> Result { + chip.get_fixed_short(point) + .map(|inner| FixedPointShort { chip, inner }) + } + + /// Returns `[by] self`. + pub fn mul( + &self, + mut layouter: impl Layouter, + by: &ScalarFixedShort, + ) -> Result, Error> { + assert_eq!(self.chip, by.chip); + self.chip + .mul_fixed_short(&mut layouter, &by.inner, &self.inner) + .map(|inner| Point { + chip: self.chip.clone(), + inner, + }) } }