crypto/constants: Update with latest Orchard constants.

This commit is contained in:
parazyd
2022-02-07 21:20:23 +01:00
parent 74841da137
commit 1df6c3db7f
11 changed files with 464 additions and 301 deletions

View File

@@ -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;

View File

@@ -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<OrchardFixedBasesFull> for OrchardFixedBases {
fn from(full_width_base: OrchardFixedBasesFull) -> Self {
Self::Full(full_width_base)
}
}
impl From<ValueCommitV> for OrchardFixedBases {
fn from(_value_commit_v: ValueCommitV) -> Self {
Self::ValueCommitV
}
}
impl From<NullifierK> 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<pallas::Affine> for OrchardFixedBases {
type FullScalar = OrchardFixedBasesFull;
type Base = NullifierK;
type ShortScalar = ValueCommitV;
}
impl FixedPoint<pallas::Affine> 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<u64> {
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<C: CurveAffine>(base: C, num_windows: usize) -> Vec<[C; H]> {
let mut window_table: Vec<[C; H]> = Vec::with_capacity(num_windows);
impl FixedPoint<pallas::Affine> 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::<ArrayVec<C, H>>()
.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::<ArrayVec<C, H>>()
.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<C: CurveAffine>(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::<C>(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::<C>(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<u64> {
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<C: CurveAffine>(base: C, z: &[u64], u: &[[[u8; 32]; H]], num_windows: usize) {
let window_table = compute_window_table(base, num_windows);
impl FixedPoint<pallas::Affine> 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<u64> {
value_commit_v::Z_SHORT.to_vec()
}
}

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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);
}

View File

@@ -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<OrchardFixedBasesFull> 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<NullifierK> 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<Vec<WindowLagrangeCoeffs>> for LagrangeCoeffs {
fn from(windows: Vec<WindowLagrangeCoeffs>) -> Self {
Self(windows.into_boxed_slice().try_into().unwrap())
}
}
impl From<Vec<[pallas::Base; H]>> for LagrangeCoeffs {
fn from(arrays: Vec<[pallas::Base; H]>) -> Self {
let windows: Vec<WindowLagrangeCoeffs> = 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<Vec<WindowLagrangeCoeffs>> for LagrangeCoeffsShort {
fn from(windows: Vec<WindowLagrangeCoeffs>) -> Self {
Self(windows.into_boxed_slice().try_into().unwrap())
}
}
impl From<Vec<[pallas::Base; H]>> for LagrangeCoeffsShort {
fn from(arrays: Vec<[pallas::Base; H]>) -> Self {
let windows: Vec<WindowLagrangeCoeffs> = 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::<Vec<_>>()
.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::<Vec<_>>()
.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::<Vec<_>>()
.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<Vec<WindowUs>> for U {
fn from(windows: Vec<WindowUs>) -> 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<WindowUs> = 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<Vec<WindowUs>> for UShort {
fn from(windows: Vec<WindowUs>) -> 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<WindowUs> = window_us.iter().map(|us| us.into()).collect();
windows.into()
}
}

View File

@@ -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<const NUM_BITS: usize>(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<pallas::Affine> 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<pallas::Affine, OrchardFixedBases, OrchardHashDomains> 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<pallas::Affine, OrchardFixedBases, OrchardHashDomains> 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());
}

View File

@@ -1,11 +1,3 @@
use pasta_curves::arithmetic::{CurveAffine, Field, FieldExt};
/// Evaluate y = f(x) given the coefficients of f(x)
pub fn evaluate<C: CurveAffine>(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<Output: Copy + Default, const LEN: usize>(
@@ -16,10 +8,10 @@ pub fn gen_const_array<Output: Copy + Default, const LEN: usize>(
pub(crate) fn gen_const_array_with_default<Output: Copy, const LEN: usize>(
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