//! Gadgets for elliptic curve operations. use ff::Field; use std::fmt::Debug; use halo2::{ arithmetic::{CurveAffine, FieldExt}, circuit::{Chip, Layouter}, plonk::Error, }; /// The set of circuit instructions required to use the ECC gadgets. 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 + 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: 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 + 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 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; /// 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( &self, layouter: &mut impl Layouter, value: Option, ) -> Result; /// Extracts the x-coordinate of a point. fn extract_p(point: &Self::Point) -> &Self::X; /// 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( &self, layouter: &mut impl Layouter, a: &Self::Point, b: &Self::Point, ) -> Result; /// Performs point doubling, returning `[2] a`. fn double( &self, layouter: &mut impl Layouter, a: &Self::Point, ) -> Result; /// Performs variable-base scalar multiplication, returning `[scalar] base`. fn mul( &self, layouter: &mut impl Layouter, scalar: &Self::ScalarVar, base: &Self::Point, ) -> Result; /// Performs fixed-base scalar multiplication using a full-width scalar, returning `[scalar] base`. fn mul_fixed( &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 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 ScalarVar + Clone + Debug + Eq> { chip: EccChip, inner: EccChip::ScalarVar, } impl + Clone + Debug + Eq> ScalarVar { /// Constructs a new ScalarVar with the given value. pub fn new( 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 { 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 + Clone + Debug + Eq> { chip: EccChip, inner: EccChip::Point, } impl + Clone + Debug + Eq> Point { /// Constructs a new point with the given value. 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 }) } /// 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()) } /// 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: &ScalarVar, ) -> Result { assert_eq!(self.chip, by.chip); self.chip .mul(&mut layouter, &by.inner, &self.inner) .map(|inner| Point { chip: self.chip.clone(), inner, }) } } /// The affine short Weierstrass x-coordinate of an elliptic curve point over the /// given curve. #[derive(Debug)] 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 + Clone + Debug + Eq> FixedPoint { /// Gets a reference to the specified fixed point in the circuit. 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: &ScalarFixed, ) -> Result, Error> { 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, }) } }