From 1df6c3db7f81245b02dbd7fc4cf39a405351bc46 Mon Sep 17 00:00:00 2001 From: parazyd Date: Mon, 7 Feb 2022 21:20:23 +0100 Subject: [PATCH] crypto/constants: Update with latest Orchard constants. --- src/crypto/constants.rs | 11 +- src/crypto/constants/fixed_bases.rs | 257 +++++++----------- .../constants/fixed_bases/commit_ivk_r.rs | 24 +- .../constants/fixed_bases/note_commit_r.rs | 29 +- .../constants/fixed_bases/nullifier_k.rs | 22 +- .../constants/fixed_bases/spend_auth_g.rs | 19 +- .../constants/fixed_bases/value_commit_r.rs | 21 +- .../constants/fixed_bases/value_commit_v.rs | 26 +- src/crypto/constants/load.rs | 257 ++++++++++++++++++ src/crypto/constants/sinsemilla.rs | 87 +++--- src/crypto/constants/util.rs | 12 +- 11 files changed, 464 insertions(+), 301 deletions(-) create mode 100644 src/crypto/constants/load.rs diff --git a/src/crypto/constants.rs b/src/crypto/constants.rs index 1c496a301..dd65c4d8f 100644 --- a/src/crypto/constants.rs +++ b/src/crypto/constants.rs @@ -2,10 +2,19 @@ pub mod fixed_bases; pub mod sinsemilla; pub mod util; -pub use fixed_bases::OrchardFixedBases; +pub use fixed_bases::{NullifierK, OrchardFixedBases, OrchardFixedBasesFull, ValueCommitV, H}; pub const DRK_SCHNORR_DOMAIN: &[u8] = b"DarkFi_Schnorr"; pub const MERKLE_DEPTH_ORCHARD: usize = 32; pub const L_ORCHARD_MERKLE: usize = 255; + +/// $\ell^\mathsf{Orchard}_\mathsf{base}$ +pub(crate) const L_ORCHARD_BASE: usize = 255; + +/// $\ell^\mathsf{Orchard}_\mathsf{scalar}$ +pub(crate) const L_ORCHARD_SCALAR: usize = 255; + +/// $\ell_\mathsf{value}$ +pub(crate) const L_VALUE: usize = 64; diff --git a/src/crypto/constants/fixed_bases.rs b/src/crypto/constants/fixed_bases.rs index 20a1933f5..d6c7c7706 100644 --- a/src/crypto/constants/fixed_bases.rs +++ b/src/crypto/constants/fixed_bases.rs @@ -1,14 +1,12 @@ -use arrayvec::ArrayVec; +//! Orchard fixed bases. +use super::{L_ORCHARD_SCALAR, L_VALUE}; use halo2_gadgets::ecc::{ - chip::{compute_lagrange_coeffs, NUM_WINDOWS, NUM_WINDOWS_SHORT}, - FixedPoints, H, -}; -use pasta_curves::{ - arithmetic::{CurveAffine, Field, FieldExt}, - group::Curve, - pallas, + chip::{BaseFieldElem, FixedPoint, FullScalar, ShortScalar}, + FixedPoints, }; +use pasta_curves::pallas; + pub mod commit_ivk_r; pub mod note_commit_r; pub mod nullifier_k; @@ -16,14 +14,18 @@ pub mod spend_auth_g; pub mod value_commit_r; pub mod value_commit_v; +/// SWU hash-to-curve personalization for the spending key base point and +/// the nullifier base point K^Orchard +pub const ORCHARD_PERSONALIZATION: &str = "z.cash:Orchard"; + /// SWU hash-to-curve personalization for the value commitment generator pub const VALUE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-cv"; /// SWU hash-to-curve value for the value commitment generator -pub const VALUE_COMMITMENT_R_BYTES: [u8; 1] = *b"r"; +pub const VALUE_COMMITMENT_V_BYTES: [u8; 1] = *b"v"; /// SWU hash-to-curve value for the value commitment generator -pub const VALUE_COMMITMENT_V_BYTES: [u8; 1] = *b"v"; +pub const VALUE_COMMITMENT_R_BYTES: [u8; 1] = *b"r"; /// SWU hash-to-curve personalization for the note commitment generator pub const NOTE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-NoteCommit"; @@ -31,184 +33,129 @@ pub const NOTE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-NoteCommit"; /// SWU hash-to-curve personalization for the IVK commitment generator pub const COMMIT_IVK_PERSONALIZATION: &str = "z.cash:Orchard-CommitIvk"; -/// SWU hash-to-curve personalization for the spending key base point and -/// the nullifier base point K^Orchard -pub const ORCHARD_PERSONALIZATION: &str = "z.cash:Orchard"; - /// Window size for fixed-base scalar multiplication pub const FIXED_BASE_WINDOW_SIZE: usize = 3; +/// $2^{`FIXED_BASE_WINDOW_SIZE`}$ +pub const H: usize = 1 << FIXED_BASE_WINDOW_SIZE; + +/// Number of windows for a full-width scalar +pub const NUM_WINDOWS: usize = + (L_ORCHARD_SCALAR + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE; + +/// Number of windows for a short signed scalar +pub const NUM_WINDOWS_SHORT: usize = + (L_VALUE + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE; + #[derive(Copy, Clone, Debug, Eq, PartialEq)] +// 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. pub enum OrchardFixedBases { - CommitIvkR, - NoteCommitR, - ValueCommitR, - SpendAuthG, + Full(OrchardFixedBasesFull), NullifierK, ValueCommitV, } +impl From for OrchardFixedBases { + fn from(full_width_base: OrchardFixedBasesFull) -> Self { + Self::Full(full_width_base) + } +} + +impl From for OrchardFixedBases { + fn from(_value_commit_v: ValueCommitV) -> Self { + Self::ValueCommitV + } +} + +impl From for OrchardFixedBases { + fn from(_nullifier_k: NullifierK) -> Self { + Self::NullifierK + } +} + +/// The Orchard fixed bases used in scalar mul with full-width scalars. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum OrchardFixedBasesFull { + CommitIvkR, + NoteCommitR, + ValueCommitR, + SpendAuthG, +} + +/// NullifierK is used in scalar mul with a base field element. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct NullifierK; + +/// ValueCommitV is used in scalar mul with a short signed scalar. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct ValueCommitV; + impl FixedPoints for OrchardFixedBases { + type FullScalar = OrchardFixedBasesFull; + type Base = NullifierK; + type ShortScalar = ValueCommitV; +} + +impl FixedPoint for OrchardFixedBasesFull { + type ScalarKind = FullScalar; + fn generator(&self) -> pallas::Affine { match self { - OrchardFixedBases::CommitIvkR => commit_ivk_r::generator(), - OrchardFixedBases::NoteCommitR => note_commit_r::generator(), - OrchardFixedBases::ValueCommitR => value_commit_r::generator(), - OrchardFixedBases::SpendAuthG => spend_auth_g::generator(), - OrchardFixedBases::NullifierK => nullifier_k::generator(), - OrchardFixedBases::ValueCommitV => value_commit_v::generator(), + Self::CommitIvkR => commit_ivk_r::generator(), + Self::NoteCommitR => note_commit_r::generator(), + Self::ValueCommitR => value_commit_r::generator(), + Self::SpendAuthG => spend_auth_g::generator(), } } + fn u(&self) -> Vec<[[u8; 32]; H]> { match self { - OrchardFixedBases::CommitIvkR => commit_ivk_r::U.to_vec(), - OrchardFixedBases::NoteCommitR => note_commit_r::U.to_vec(), - OrchardFixedBases::ValueCommitR => value_commit_r::U.to_vec(), - OrchardFixedBases::SpendAuthG => spend_auth_g::U.to_vec(), - OrchardFixedBases::NullifierK => nullifier_k::U.to_vec(), - OrchardFixedBases::ValueCommitV => value_commit_v::U_SHORT.to_vec(), + Self::CommitIvkR => commit_ivk_r::U.to_vec(), + Self::NoteCommitR => note_commit_r::U.to_vec(), + Self::ValueCommitR => value_commit_r::U.to_vec(), + Self::SpendAuthG => spend_auth_g::U.to_vec(), } } fn z(&self) -> Vec { match self { - OrchardFixedBases::CommitIvkR => commit_ivk_r::Z.to_vec(), - OrchardFixedBases::NoteCommitR => note_commit_r::Z.to_vec(), - OrchardFixedBases::ValueCommitR => value_commit_r::Z.to_vec(), - OrchardFixedBases::SpendAuthG => spend_auth_g::Z.to_vec(), - OrchardFixedBases::NullifierK => nullifier_k::Z.to_vec(), - OrchardFixedBases::ValueCommitV => value_commit_v::Z_SHORT.to_vec(), - } - } - - fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> { - match self { - OrchardFixedBases::ValueCommitV => { - compute_lagrange_coeffs(self.generator(), NUM_WINDOWS_SHORT) - } - _ => compute_lagrange_coeffs(self.generator(), NUM_WINDOWS), + Self::CommitIvkR => commit_ivk_r::Z.to_vec(), + Self::NoteCommitR => note_commit_r::Z.to_vec(), + Self::ValueCommitR => value_commit_r::Z.to_vec(), + Self::SpendAuthG => spend_auth_g::Z.to_vec(), } } } -/// For each fixed base, we calculate its scalar multiples in three-bit windows. -/// Each window will have $2^3 = 8$ points. -#[allow(dead_code)] -fn compute_window_table(base: C, num_windows: usize) -> Vec<[C; H]> { - let mut window_table: Vec<[C; H]> = Vec::with_capacity(num_windows); +impl FixedPoint for NullifierK { + type ScalarKind = BaseFieldElem; - // Generate window table entries for all windows but the last. - // For these first `num_windows - 1` windows, we compute the multiple [(k+2)*(2^3)^w]B. - // Here, w ranges from [0..`num_windows - 1`) - for w in 0..(num_windows - 1) { - window_table.push( - (0..H) - .map(|k| { - // scalar = (k+2)*(8^w) - let scalar = C::ScalarExt::from_u64(k as u64 + 2) * - C::ScalarExt::from_u64(H as u64).pow(&[w as u64, 0, 0, 0]); - (base * scalar).to_affine() - }) - .collect::>() - .into_inner() - .unwrap(), - ); + fn generator(&self) -> pallas::Affine { + nullifier_k::generator() } - // Generate window table entries for the last window, w = `num_windows - 1`. - // For the last window, we compute [k * (2^3)^w - sum]B, where sum is defined - // as sum = \sum_{j = 0}^{`num_windows - 2`} 2^{3j+1} - let sum = (0..(num_windows - 1)).fold(C::ScalarExt::zero(), |acc, j| { - acc + C::ScalarExt::from_u64(2).pow(&[ - FIXED_BASE_WINDOW_SIZE as u64 * j as u64 + 1, - 0, - 0, - 0, - ]) - }); - window_table.push( - (0..H) - .map(|k| { - // scalar = k * (2^3)^w - sum, where w = `num_windows - 1` - let scalar = C::ScalarExt::from_u64(k as u64) * - C::ScalarExt::from_u64(H as u64).pow(&[(num_windows - 1) as u64, 0, 0, 0]) - - sum; - (base * scalar).to_affine() - }) - .collect::>() - .into_inner() - .unwrap(), - ); - - window_table -} - -#[cfg(test)] -// Test that Lagrange interpolation coefficients reproduce the correct x-coordinate -// for each fixed-base multiple in each window. -fn test_lagrange_coeffs(base: C, num_windows: usize) { - let lagrange_coeffs = compute_lagrange_coeffs(base, num_windows); - - // Check first 84 windows, i.e. `k_0, k_1, ..., k_83` - for (idx, coeffs) in lagrange_coeffs[0..(num_windows - 1)].iter().enumerate() { - // Test each three-bit chunk in this window. - for bits in 0..(1 << FIXED_BASE_WINDOW_SIZE) { - { - // Interpolate the x-coordinate using this window's coefficients - let interpolated_x = super::util::evaluate::(bits, coeffs); - - // Compute the actual x-coordinate of the multiple [(k+2)*(8^w)]B. - let point = base * - C::Scalar::from_u64(bits as u64 + 2) * - C::Scalar::from_u64(H as u64).pow(&[idx as u64, 0, 0, 0]); - let x = *point.to_affine().coordinates().unwrap().x(); - - // Check that the interpolated x-coordinate matches the actual one. - assert_eq!(x, interpolated_x); - } - } + fn u(&self) -> Vec<[[u8; 32]; H]> { + nullifier_k::U.to_vec() } - // Check last window. - for bits in 0..(1 << FIXED_BASE_WINDOW_SIZE) { - // Interpolate the x-coordinate using the last window's coefficients - let interpolated_x = super::util::evaluate::(bits, &lagrange_coeffs[num_windows - 1]); - - // Compute the actual x-coordinate of the multiple [k * (8^84) - offset]B, - // where offset = \sum_{j = 0}^{83} 2^{3j+1} - let offset = (0..(num_windows - 1)).fold(C::Scalar::zero(), |acc, w| { - acc + C::Scalar::from_u64(2).pow(&[ - FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1, - 0, - 0, - 0, - ]) - }); - let scalar = C::Scalar::from_u64(bits as u64) * - C::Scalar::from_u64(H as u64).pow(&[(num_windows - 1) as u64, 0, 0, 0]) - - offset; - let point = base * scalar; - let x = *point.to_affine().coordinates().unwrap().x(); - - // Check that the interpolated x-coordinate matches the actual one. - assert_eq!(x, interpolated_x); + fn z(&self) -> Vec { + nullifier_k::Z.to_vec() } } -#[cfg(test)] -// Test that the z-values and u-values satisfy the conditions: -// 1. z + y = u^2, -// 2. z - y is not a square -// for the y-coordinate of each fixed-base multiple in each window. -fn test_zs_and_us(base: C, z: &[u64], u: &[[[u8; 32]; H]], num_windows: usize) { - let window_table = compute_window_table(base, num_windows); +impl FixedPoint for ValueCommitV { + type ScalarKind = ShortScalar; - for ((u, z), window_points) in u.iter().zip(z.iter()).zip(window_table) { - for (u, point) in u.iter().zip(window_points.iter()) { - let y = *point.coordinates().unwrap().y(); - let u = C::Base::from_bytes(u).unwrap(); - assert_eq!(C::Base::from_u64(*z) + y, u * u); // allow either square root - assert!(bool::from((C::Base::from_u64(*z) - y).sqrt().is_none())); - } + fn generator(&self) -> pallas::Affine { + value_commit_v::generator() + } + + fn u(&self) -> Vec<[[u8; 32]; H]> { + value_commit_v::U_SHORT.to_vec() + } + + fn z(&self) -> Vec { + value_commit_v::Z_SHORT.to_vec() } } diff --git a/src/crypto/constants/fixed_bases/commit_ivk_r.rs b/src/crypto/constants/fixed_bases/commit_ivk_r.rs index 62bb5b517..e2a0734ac 100644 --- a/src/crypto/constants/fixed_bases/commit_ivk_r.rs +++ b/src/crypto/constants/fixed_bases/commit_ivk_r.rs @@ -1,7 +1,5 @@ -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, -}; +use group::ff::PrimeField; +use pasta_curves::{arithmetic::CurveAffine, pallas}; /// Generator used in SinsemillaCommit randomness for IVK commitment pub const GENERATOR: ([u8; 32], [u8; 32]) = ( @@ -2922,8 +2920,8 @@ pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ pub fn generator() -> pallas::Affine { pallas::Affine::from_xy( - pallas::Base::from_bytes(&GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&GENERATOR.1).unwrap(), + pallas::Base::from_repr(GENERATOR.0).unwrap(), + pallas::Base::from_repr(GENERATOR.1).unwrap(), ) .unwrap() } @@ -2931,15 +2929,15 @@ pub fn generator() -> pallas::Affine { #[cfg(test)] mod tests { use super::{ - super::{test_lagrange_coeffs, test_zs_and_us, COMMIT_IVK_PERSONALIZATION, NUM_WINDOWS}, + super::{COMMIT_IVK_PERSONALIZATION, NUM_WINDOWS}, *, }; use group::Curve; - use halo2_gadgets::primitives::sinsemilla::CommitDomain; - use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, + use halo2_gadgets::{ + ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us}, + primitives::sinsemilla::CommitDomain, }; + use pasta_curves::{arithmetic::CurveAffine, pallas}; #[test] fn generator() { @@ -2947,8 +2945,8 @@ mod tests { let point = domain.R(); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&GENERATOR.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&GENERATOR.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(GENERATOR.1).unwrap()); } #[test] diff --git a/src/crypto/constants/fixed_bases/note_commit_r.rs b/src/crypto/constants/fixed_bases/note_commit_r.rs index 864d42a4a..71a9fea64 100644 --- a/src/crypto/constants/fixed_bases/note_commit_r.rs +++ b/src/crypto/constants/fixed_bases/note_commit_r.rs @@ -1,7 +1,5 @@ -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, -}; +use group::ff::PrimeField; +use pasta_curves::{arithmetic::CurveAffine, pallas}; /// Generator used in SinsemillaCommit randomness for note commitment pub const GENERATOR: ([u8; 32], [u8; 32]) = ( @@ -2922,8 +2920,8 @@ pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ pub fn generator() -> pallas::Affine { pallas::Affine::from_xy( - pallas::Base::from_bytes(&GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&GENERATOR.1).unwrap(), + pallas::Base::from_repr(GENERATOR.0).unwrap(), + pallas::Base::from_repr(GENERATOR.1).unwrap(), ) .unwrap() } @@ -2931,26 +2929,25 @@ pub fn generator() -> pallas::Affine { #[cfg(test)] mod tests { use super::{ - super::{ - test_lagrange_coeffs, test_zs_and_us, NOTE_COMMITMENT_PERSONALIZATION, NUM_WINDOWS, - }, + super::{NOTE_COMMITMENT_PERSONALIZATION, NUM_WINDOWS}, *, }; - use group::Curve; - use halo2_gadgets::primitives::sinsemilla::CommitDomain; - use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, + use halo2_gadgets::{ + ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us}, + primitives::sinsemilla::CommitDomain, }; + use group::Curve; + use pasta_curves::{arithmetic::CurveAffine, pallas}; + #[test] fn generator() { let domain = CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION); let point = domain.R(); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&GENERATOR.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&GENERATOR.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(GENERATOR.1).unwrap()); } #[test] diff --git a/src/crypto/constants/fixed_bases/nullifier_k.rs b/src/crypto/constants/fixed_bases/nullifier_k.rs index 71b26c6e7..10fbfeb87 100644 --- a/src/crypto/constants/fixed_bases/nullifier_k.rs +++ b/src/crypto/constants/fixed_bases/nullifier_k.rs @@ -1,7 +1,5 @@ -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, -}; +use group::ff::PrimeField; +use pasta_curves::{arithmetic::CurveAffine, pallas}; pub const GENERATOR: ([u8; 32], [u8; 32]) = ( [ @@ -2921,8 +2919,8 @@ pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ pub fn generator() -> pallas::Affine { pallas::Affine::from_xy( - pallas::Base::from_bytes(&GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&GENERATOR.1).unwrap(), + pallas::Base::from_repr(GENERATOR.0).unwrap(), + pallas::Base::from_repr(GENERATOR.1).unwrap(), ) .unwrap() } @@ -2930,14 +2928,12 @@ pub fn generator() -> pallas::Affine { #[cfg(test)] mod tests { use super::{ - super::{test_lagrange_coeffs, test_zs_and_us, NUM_WINDOWS, ORCHARD_PERSONALIZATION}, + super::{NUM_WINDOWS, ORCHARD_PERSONALIZATION}, *, }; use group::Curve; - use pasta_curves::{ - arithmetic::{CurveExt, FieldExt}, - pallas, - }; + use halo2_gadgets::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us}; + use pasta_curves::{arithmetic::CurveExt, pallas}; #[test] fn generator() { @@ -2945,8 +2941,8 @@ mod tests { let point = hasher(b"K"); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&GENERATOR.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&GENERATOR.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(GENERATOR.1).unwrap()); } #[test] diff --git a/src/crypto/constants/fixed_bases/spend_auth_g.rs b/src/crypto/constants/fixed_bases/spend_auth_g.rs index 726d754ce..82b59a988 100644 --- a/src/crypto/constants/fixed_bases/spend_auth_g.rs +++ b/src/crypto/constants/fixed_bases/spend_auth_g.rs @@ -1,7 +1,5 @@ -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, -}; +use group::ff::PrimeField; +use pasta_curves::{arithmetic::CurveAffine, pallas}; /// The value commitment is used to check balance between inputs and outputs. The value is /// placed over this generator. @@ -2923,8 +2921,8 @@ pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ pub fn generator() -> pallas::Affine { pallas::Affine::from_xy( - pallas::Base::from_bytes(&GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&GENERATOR.1).unwrap(), + pallas::Base::from_repr(GENERATOR.0).unwrap(), + pallas::Base::from_repr(GENERATOR.1).unwrap(), ) .unwrap() } @@ -2932,12 +2930,13 @@ pub fn generator() -> pallas::Affine { #[cfg(test)] mod tests { use super::{ - super::{test_lagrange_coeffs, test_zs_and_us, NUM_WINDOWS, ORCHARD_PERSONALIZATION}, + super::{NUM_WINDOWS, ORCHARD_PERSONALIZATION}, *, }; use group::Curve; + use halo2_gadgets::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us}; use pasta_curves::{ - arithmetic::{CurveAffine, CurveExt, FieldExt}, + arithmetic::{CurveAffine, CurveExt}, pallas, }; @@ -2947,8 +2946,8 @@ mod tests { let point = hasher(b"G"); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&GENERATOR.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&GENERATOR.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(GENERATOR.1).unwrap()); } #[test] diff --git a/src/crypto/constants/fixed_bases/value_commit_r.rs b/src/crypto/constants/fixed_bases/value_commit_r.rs index ae9d8c567..b5a73f2c2 100644 --- a/src/crypto/constants/fixed_bases/value_commit_r.rs +++ b/src/crypto/constants/fixed_bases/value_commit_r.rs @@ -1,7 +1,5 @@ -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, -}; +use group::ff::PrimeField; +use pasta_curves::{arithmetic::CurveAffine, pallas}; /// The value commitment is used to check balance between inputs and outputs. The value is /// placed over this generator. @@ -2923,8 +2921,8 @@ pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ pub fn generator() -> pallas::Affine { pallas::Affine::from_xy( - pallas::Base::from_bytes(&GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&GENERATOR.1).unwrap(), + pallas::Base::from_repr(GENERATOR.0).unwrap(), + pallas::Base::from_repr(GENERATOR.1).unwrap(), ) .unwrap() } @@ -2932,14 +2930,13 @@ pub fn generator() -> pallas::Affine { #[cfg(test)] mod tests { use super::{ - super::{ - test_lagrange_coeffs, test_zs_and_us, NUM_WINDOWS, VALUE_COMMITMENT_PERSONALIZATION, - }, + super::{NUM_WINDOWS, VALUE_COMMITMENT_PERSONALIZATION}, *, }; use group::Curve; + use halo2_gadgets::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us}; use pasta_curves::{ - arithmetic::{CurveAffine, CurveExt, FieldExt}, + arithmetic::{CurveAffine, CurveExt}, pallas, }; @@ -2949,8 +2946,8 @@ mod tests { let point = hasher(b"r"); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&GENERATOR.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&GENERATOR.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(GENERATOR.1).unwrap()); } #[test] diff --git a/src/crypto/constants/fixed_bases/value_commit_v.rs b/src/crypto/constants/fixed_bases/value_commit_v.rs index 372dd6200..a3f747199 100644 --- a/src/crypto/constants/fixed_bases/value_commit_v.rs +++ b/src/crypto/constants/fixed_bases/value_commit_v.rs @@ -1,7 +1,5 @@ -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, -}; +use group::ff::PrimeField; +use pasta_curves::{arithmetic::CurveAffine, pallas}; /// The value commitment is used to check balance between inputs and outputs. The value is /// placed over this generator. @@ -776,8 +774,8 @@ pub const U_SHORT: [[[u8; 32]; super::H]; super::NUM_WINDOWS_SHORT] = [ pub fn generator() -> pallas::Affine { pallas::Affine::from_xy( - pallas::Base::from_bytes(&GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&GENERATOR.1).unwrap(), + pallas::Base::from_repr(GENERATOR.0).unwrap(), + pallas::Base::from_repr(GENERATOR.1).unwrap(), ) .unwrap() } @@ -785,15 +783,13 @@ pub fn generator() -> pallas::Affine { #[cfg(test)] mod tests { use super::{ - super::{ - test_lagrange_coeffs, test_zs_and_us, NUM_WINDOWS_SHORT, - VALUE_COMMITMENT_PERSONALIZATION, - }, + super::{NUM_WINDOWS_SHORT, VALUE_COMMITMENT_PERSONALIZATION}, *, }; use group::Curve; + use halo2_gadgets::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us}; use pasta_curves::{ - arithmetic::{CurveAffine, CurveExt, FieldExt}, + arithmetic::{CurveAffine, CurveExt}, pallas, }; @@ -803,18 +799,18 @@ mod tests { let point = hasher(b"v"); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&GENERATOR.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&GENERATOR.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(GENERATOR.1).unwrap()); } #[test] - fn lagrange_coeffs_short() { + fn lagrange_coeffs() { let base = super::generator(); test_lagrange_coeffs(base, NUM_WINDOWS_SHORT); } #[test] - fn z_short() { + fn z() { let base = super::generator(); test_zs_and_us(base, &Z_SHORT, &U_SHORT, NUM_WINDOWS_SHORT); } diff --git a/src/crypto/constants/load.rs b/src/crypto/constants/load.rs new file mode 100644 index 000000000..5368a9629 --- /dev/null +++ b/src/crypto/constants/load.rs @@ -0,0 +1,257 @@ +use std::convert::TryInto; + +use crate::constants::{self, compute_lagrange_coeffs, H, NUM_WINDOWS, NUM_WINDOWS_SHORT}; +use group::ff::PrimeField; +use pasta_curves::pallas; + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum OrchardFixedBasesFull { + CommitIvkR, + NoteCommitR, + ValueCommitR, + SpendAuthG, +} + +impl OrchardFixedBasesFull { + pub fn generator(&self) -> pallas::Affine { + match self { + OrchardFixedBasesFull::CommitIvkR => super::commit_ivk_r::generator(), + OrchardFixedBasesFull::NoteCommitR => super::note_commit_r::generator(), + OrchardFixedBasesFull::ValueCommitR => super::value_commit_r::generator(), + OrchardFixedBasesFull::SpendAuthG => super::spend_auth_g::generator(), + } + } + + pub fn u(&self) -> U { + match self { + OrchardFixedBasesFull::CommitIvkR => super::commit_ivk_r::U.into(), + OrchardFixedBasesFull::NoteCommitR => super::note_commit_r::U.into(), + OrchardFixedBasesFull::ValueCommitR => super::value_commit_r::U.into(), + OrchardFixedBasesFull::SpendAuthG => super::spend_auth_g::U.into(), + } + } +} + +/// A fixed base to be used in scalar multiplication with a full-width scalar. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct OrchardFixedBase { + pub generator: pallas::Affine, + pub lagrange_coeffs: LagrangeCoeffs, + pub z: Z, + pub u: U, +} + +impl From for OrchardFixedBase { + fn from(base: OrchardFixedBasesFull) -> Self { + let (generator, z, u) = match base { + OrchardFixedBasesFull::CommitIvkR => ( + super::commit_ivk_r::generator(), + super::commit_ivk_r::Z.into(), + super::commit_ivk_r::U.into(), + ), + OrchardFixedBasesFull::NoteCommitR => ( + super::note_commit_r::generator(), + super::note_commit_r::Z.into(), + super::note_commit_r::U.into(), + ), + OrchardFixedBasesFull::ValueCommitR => ( + super::value_commit_r::generator(), + super::value_commit_r::Z.into(), + super::value_commit_r::U.into(), + ), + OrchardFixedBasesFull::SpendAuthG => ( + super::spend_auth_g::generator(), + super::spend_auth_g::Z.into(), + super::spend_auth_g::U.into(), + ), + }; + + Self { + generator, + lagrange_coeffs: compute_lagrange_coeffs(generator, NUM_WINDOWS).into(), + z, + u, + } + } +} + +/// A fixed base to be used in scalar multiplication with a base field element. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ValueCommitV { + pub generator: pallas::Affine, + pub lagrange_coeffs_short: LagrangeCoeffsShort, + pub z_short: ZShort, + pub u_short: UShort, +} + +impl ValueCommitV { + pub fn get() -> Self { + let generator = super::value_commit_v::generator(); + Self { + generator, + lagrange_coeffs_short: compute_lagrange_coeffs(generator, NUM_WINDOWS_SHORT).into(), + z_short: super::value_commit_v::Z_SHORT.into(), + u_short: super::value_commit_v::U_SHORT.into(), + } + } +} + +/// A fixed base to be used in scalar multiplication with a short signed exponent. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct NullifierK; + +impl From for OrchardFixedBase { + fn from(_nullifier_k: NullifierK) -> Self { + let (generator, z, u) = ( + super::nullifier_k::generator(), + super::nullifier_k::Z.into(), + super::nullifier_k::U.into(), + ); + Self { + generator, + lagrange_coeffs: compute_lagrange_coeffs(generator, NUM_WINDOWS).into(), + z, + u, + } + } +} + +impl NullifierK { + pub fn generator(&self) -> pallas::Affine { + super::nullifier_k::generator() + } + + pub fn u(&self) -> U { + super::nullifier_k::U.into() + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +// 8 coefficients per window +pub struct WindowLagrangeCoeffs(pub Box<[pallas::Base; H]>); + +impl From<&[pallas::Base; H]> for WindowLagrangeCoeffs { + fn from(array: &[pallas::Base; H]) -> Self { + Self(Box::new(*array)) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +// 85 windows per base (with the exception of ValueCommitV) +pub struct LagrangeCoeffs(pub Box<[WindowLagrangeCoeffs; constants::NUM_WINDOWS]>); + +impl From> for LagrangeCoeffs { + fn from(windows: Vec) -> Self { + Self(windows.into_boxed_slice().try_into().unwrap()) + } +} + +impl From> for LagrangeCoeffs { + fn from(arrays: Vec<[pallas::Base; H]>) -> Self { + let windows: Vec = arrays.iter().map(|array| array.into()).collect(); + windows.into() + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +// 22 windows for ValueCommitV +pub struct LagrangeCoeffsShort(pub Box<[WindowLagrangeCoeffs; NUM_WINDOWS_SHORT]>); + +impl From> for LagrangeCoeffsShort { + fn from(windows: Vec) -> Self { + Self(windows.into_boxed_slice().try_into().unwrap()) + } +} + +impl From> for LagrangeCoeffsShort { + fn from(arrays: Vec<[pallas::Base; H]>) -> Self { + let windows: Vec = arrays.iter().map(|array| array.into()).collect(); + windows.into() + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +// 85 Z's per base (with the exception of ValueCommitV) +pub struct Z(pub Box<[pallas::Base; NUM_WINDOWS]>); + +impl From<[u64; NUM_WINDOWS]> for Z { + fn from(zs: [u64; NUM_WINDOWS]) -> Self { + Self( + zs.iter() + .map(|z| pallas::Base::from(*z)) + .collect::>() + .into_boxed_slice() + .try_into() + .unwrap(), + ) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +// 22 Z's for ValueCommitV +pub struct ZShort(pub Box<[pallas::Base; NUM_WINDOWS_SHORT]>); + +impl From<[u64; NUM_WINDOWS_SHORT]> for ZShort { + fn from(zs: [u64; NUM_WINDOWS_SHORT]) -> Self { + Self( + zs.iter() + .map(|z| pallas::Base::from(*z)) + .collect::>() + .into_boxed_slice() + .try_into() + .unwrap(), + ) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +// 8 u's per window +pub struct WindowUs(pub Box<[pallas::Base; H]>); + +impl From<&[[u8; 32]; H]> for WindowUs { + fn from(window_us: &[[u8; 32]; H]) -> Self { + Self( + window_us + .iter() + .map(|u| pallas::Base::from_repr(*u).unwrap()) + .collect::>() + .into_boxed_slice() + .try_into() + .unwrap(), + ) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +// 85 windows per base (with the exception of ValueCommitV) +pub struct U(pub Box<[WindowUs; NUM_WINDOWS]>); + +impl From> for U { + fn from(windows: Vec) -> Self { + Self(windows.into_boxed_slice().try_into().unwrap()) + } +} + +impl From<[[[u8; 32]; H]; NUM_WINDOWS]> for U { + fn from(window_us: [[[u8; 32]; H]; NUM_WINDOWS]) -> Self { + let windows: Vec = window_us.iter().map(|us| us.into()).collect(); + windows.into() + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +// 22 windows for ValueCommitV +pub struct UShort(pub Box<[WindowUs; NUM_WINDOWS_SHORT]>); + +impl From> for UShort { + fn from(windows: Vec) -> Self { + Self(windows.into_boxed_slice().try_into().unwrap()) + } +} + +impl From<[[[u8; 32]; H]; NUM_WINDOWS_SHORT]> for UShort { + fn from(window_us: [[[u8; 32]; H]; NUM_WINDOWS_SHORT]) -> Self { + let windows: Vec = window_us.iter().map(|us| us.into()).collect(); + windows.into() + } +} diff --git a/src/crypto/constants/sinsemilla.rs b/src/crypto/constants/sinsemilla.rs index e167688a9..5420ed106 100644 --- a/src/crypto/constants/sinsemilla.rs +++ b/src/crypto/constants/sinsemilla.rs @@ -1,12 +1,10 @@ //! Sinsemilla generators -use super::OrchardFixedBases; -//use crate::spec::i2lebsp; - +use super::{OrchardFixedBases, OrchardFixedBasesFull}; +use crate::crypto::util::i2lebsp; use halo2_gadgets::sinsemilla::{CommitDomains, HashDomains}; -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, -}; + +use group::ff::PrimeField; +use pasta_curves::{arithmetic::CurveAffine, pallas}; /// Number of bits of each message piece in $\mathsf{SinsemillaHashToPoint}$ pub const K: usize = 10; @@ -21,19 +19,12 @@ pub const INV_TWO_POW_K: [u8; 32] = [ /// of Pallas. pub const C: usize = 253; +/// $\ell^\mathsf{Orchard}_\mathsf{Merkle}$ +pub(crate) const L_ORCHARD_MERKLE: usize = 255; + /// SWU hash-to-curve personalization for the Merkle CRH generator pub const MERKLE_CRH_PERSONALIZATION: &str = "z.cash:Orchard-MerkleCRH"; -// Sinsemilla Q generators - -/// SWU hash-to-curve personalization for Sinsemilla $Q$ generators. -pub const Q_PERSONALIZATION: &str = "z.cash:SinsemillaQ"; - -// Sinsemilla S generators - -/// SWU hash-to-curve personalization for Sinsemilla $S$ generators. -pub const S_PERSONALIZATION: &str = "z.cash:SinsemillaS"; - /// Generator used in SinsemillaHashToPoint for note commitment pub const Q_NOTE_COMMITMENT_M_GENERATOR: ([u8; 32], [u8; 32]) = ( [ @@ -70,20 +61,14 @@ pub const Q_MERKLE_CRH: ([u8; 32], [u8; 32]) = ( ], ); -pub fn i2lebsp(int: u64) -> [bool; NUM_BITS] { - assert!(NUM_BITS <= 64); - super::util::gen_const_array(|mask: usize| (int & (1 << mask)) != 0) -} - -#[allow(dead_code)] -fn lebs2ip_k(bits: &[bool]) -> u32 { +pub(crate) fn lebs2ip_k(bits: &[bool]) -> u32 { assert!(bits.len() == K); bits.iter().enumerate().fold(0u32, |acc, (i, b)| acc + if *b { 1 << i } else { 0 }) } /// The sequence of K bits in little-endian order representing an integer /// up to `2^K` - 1. -pub fn i2lebsp_k(int: usize) -> [bool; K] { +pub(crate) fn i2lebsp_k(int: usize) -> [bool; K] { assert!(int < (1 << K)); i2lebsp(int as u64) } @@ -100,18 +85,18 @@ impl HashDomains for OrchardHashDomains { fn Q(&self) -> pallas::Affine { match self { OrchardHashDomains::CommitIvk => pallas::Affine::from_xy( - pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.1).unwrap(), + pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.0).unwrap(), + pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.1).unwrap(), ) .unwrap(), OrchardHashDomains::NoteCommit => pallas::Affine::from_xy( - pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap(), + pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap(), + pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap(), ) .unwrap(), OrchardHashDomains::MerkleCrh => pallas::Affine::from_xy( - pallas::Base::from_bytes(&Q_MERKLE_CRH.0).unwrap(), - pallas::Base::from_bytes(&Q_MERKLE_CRH.1).unwrap(), + pallas::Base::from_repr(Q_MERKLE_CRH.0).unwrap(), + pallas::Base::from_repr(Q_MERKLE_CRH.1).unwrap(), ) .unwrap(), } @@ -125,10 +110,10 @@ pub enum OrchardCommitDomains { } impl CommitDomains for OrchardCommitDomains { - fn r(&self) -> OrchardFixedBases { + fn r(&self) -> OrchardFixedBasesFull { match self { - Self::NoteCommit => OrchardFixedBases::NoteCommitR, - Self::CommitIvk => OrchardFixedBases::CommitIvkR, + Self::NoteCommit => OrchardFixedBasesFull::NoteCommitR, + Self::CommitIvk => OrchardFixedBasesFull::CommitIvkR, } } @@ -143,23 +128,19 @@ impl CommitDomains for Or #[cfg(test)] mod tests { use super::*; - use crate::crypto::constants::{ + use crate::constants::{ fixed_bases::{COMMIT_IVK_PERSONALIZATION, NOTE_COMMITMENT_PERSONALIZATION}, sinsemilla::MERKLE_CRH_PERSONALIZATION, }; + use group::{ff::PrimeField, Curve}; use halo2_gadgets::primitives::sinsemilla::{CommitDomain, HashDomain}; - - use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - group::{ff::PrimeField, Curve}, - pallas, - }; + use halo2_proofs::{arithmetic::CurveAffine, pasta::pallas}; use rand::{self, rngs::OsRng, Rng}; #[test] // Nodes in the Merkle tree are Pallas base field elements. fn l_orchard_merkle() { - assert_eq!(255, pallas::Base::NUM_BITS as usize); + assert_eq!(super::L_ORCHARD_MERKLE, pallas::Base::NUM_BITS as usize); } #[test] @@ -198,14 +179,8 @@ mod tests { let point = domain.Q(); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!( - *coords.x(), - pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap() - ); - assert_eq!( - *coords.y(), - pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap() - ); + assert_eq!(*coords.x(), pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap()); } #[test] @@ -214,8 +189,8 @@ mod tests { let point = domain.Q(); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.1).unwrap()); } #[test] @@ -224,14 +199,14 @@ mod tests { let point = domain.Q(); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&Q_MERKLE_CRH.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&Q_MERKLE_CRH.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(Q_MERKLE_CRH.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(Q_MERKLE_CRH.1).unwrap()); } #[test] fn inv_two_pow_k() { - let two_pow_k = pallas::Base::from_u64(1u64 << K); - let inv_two_pow_k = pallas::Base::from_bytes(&INV_TWO_POW_K).unwrap(); + let two_pow_k = pallas::Base::from(1u64 << K); + let inv_two_pow_k = pallas::Base::from_repr(INV_TWO_POW_K).unwrap(); assert_eq!(two_pow_k * inv_two_pow_k, pallas::Base::one()); } diff --git a/src/crypto/constants/util.rs b/src/crypto/constants/util.rs index 4bc3e9a4d..756f68521 100644 --- a/src/crypto/constants/util.rs +++ b/src/crypto/constants/util.rs @@ -1,11 +1,3 @@ -use pasta_curves::arithmetic::{CurveAffine, Field, FieldExt}; - -/// Evaluate y = f(x) given the coefficients of f(x) -pub fn evaluate(x: u8, coeffs: &[C::Base]) -> C::Base { - let x = C::Base::from_u64(x as u64); - coeffs.iter().rev().cloned().reduce(|acc, coeff| acc * x + coeff).unwrap_or_else(C::Base::zero) -} - /// Takes in an FnMut closure and returns a constant-length array with elements of /// type `Output`. pub fn gen_const_array( @@ -16,10 +8,10 @@ pub fn gen_const_array( pub(crate) fn gen_const_array_with_default( default_value: Output, - closure: impl FnMut(usize) -> Output, + mut closure: impl FnMut(usize) -> Output, ) -> [Output; LEN] { let mut ret: [Output; LEN] = [default_value; LEN]; - for (bit, val) in ret.iter_mut().zip((0..LEN).map(closure)) { + for (bit, val) in ret.iter_mut().zip((0..LEN).map(|idx| closure(idx))) { *bit = val; } ret