2021-03-05 15:25:45 -08:00
|
|
|
//! Constants used in the Orchard protocol.
|
2021-05-03 08:55:37 -07:00
|
|
|
use arrayvec::ArrayVec;
|
2021-03-18 08:38:31 -07:00
|
|
|
use ff::{Field, PrimeField};
|
|
|
|
use group::Curve;
|
2021-06-11 21:36:43 -07:00
|
|
|
use halo2::arithmetic::lagrange_interpolate;
|
|
|
|
use pasta_curves::{
|
|
|
|
arithmetic::{CurveAffine, FieldExt},
|
|
|
|
pallas,
|
2021-03-18 08:38:31 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
pub mod commit_ivk_r;
|
|
|
|
pub mod note_commit_r;
|
|
|
|
pub mod nullifier_k;
|
2021-06-04 21:50:17 -07:00
|
|
|
pub mod spend_auth_g;
|
2021-03-18 08:38:31 -07:00
|
|
|
pub mod value_commit_r;
|
|
|
|
pub mod value_commit_v;
|
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
pub mod load;
|
2021-03-18 08:38:31 -07:00
|
|
|
pub mod util;
|
2021-03-05 15:25:45 -08:00
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
pub use load::{OrchardFixedBase, OrchardFixedBasesFull, ValueCommitV};
|
|
|
|
|
2021-06-04 23:17:43 -07:00
|
|
|
/// The Pallas scalar field modulus is $q = 2^{254} + \mathsf{t_q}$.
|
|
|
|
/// <https://github.com/zcash/pasta>
|
|
|
|
pub(crate) const T_Q: u128 = 45560315531506369815346746415080538113;
|
|
|
|
|
|
|
|
/// The Pallas base field modulus is $p = 2^{254} + \mathsf{t_p}$.
|
|
|
|
/// <https://github.com/zcash/pasta>
|
|
|
|
pub(crate) const T_P: u128 = 45560315531419706090280762371685220353;
|
|
|
|
|
2021-06-07 22:18:16 -07:00
|
|
|
/// $\mathsf{MerkleDepth^{Orchard}}$
|
|
|
|
pub(crate) const MERKLE_DEPTH_ORCHARD: usize = 32;
|
|
|
|
|
2021-06-09 19:31:47 -07:00
|
|
|
/// $\ell^\mathsf{Orchard}_\mathsf{Merkle}$
|
|
|
|
pub(crate) const L_ORCHARD_MERKLE: usize = 255;
|
|
|
|
|
2021-03-15 13:33:07 -07:00
|
|
|
/// $\ell^\mathsf{Orchard}_\mathsf{base}$
|
|
|
|
pub(crate) const L_ORCHARD_BASE: usize = 255;
|
2021-03-18 08:38:18 -07:00
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
/// $\ell^\mathsf{Orchard}_\mathsf{scalar}$
|
|
|
|
pub(crate) const L_ORCHARD_SCALAR: usize = 255;
|
|
|
|
|
2021-03-24 04:46:34 -07:00
|
|
|
/// $\ell_\mathsf{value}$
|
|
|
|
pub(crate) const L_VALUE: usize = 64;
|
|
|
|
|
2021-05-03 07:28:22 -07:00
|
|
|
/// SWU hash-to-curve personalization for the spending key base point and
|
|
|
|
/// the nullifier base point K^Orchard
|
2021-03-18 08:38:18 -07:00
|
|
|
pub const ORCHARD_PERSONALIZATION: &str = "z.cash:Orchard";
|
|
|
|
|
|
|
|
/// SWU hash-to-curve personalization for the group hash for key diversification
|
|
|
|
pub const KEY_DIVERSIFICATION_PERSONALIZATION: &str = "z.cash:Orchard-gd";
|
|
|
|
|
|
|
|
/// SWU hash-to-curve personalization for the value commitment generator
|
|
|
|
pub const VALUE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-cv";
|
|
|
|
|
2021-06-23 16:07:12 -07:00
|
|
|
/// SWU hash-to-curve value for the value commitment generator
|
|
|
|
pub const VALUE_COMMITMENT_V_BYTES: [u8; 1] = *b"v";
|
|
|
|
|
|
|
|
/// SWU hash-to-curve value for the value commitment generator
|
|
|
|
pub const VALUE_COMMITMENT_R_BYTES: [u8; 1] = *b"r";
|
|
|
|
|
2021-03-18 08:38:18 -07:00
|
|
|
/// SWU hash-to-curve personalization for the note commitment generator
|
|
|
|
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 Merkle CRH generator
|
|
|
|
pub const MERKLE_CRH_PERSONALIZATION: &str = "z.cash:Orchard-MerkleCRH";
|
|
|
|
|
|
|
|
/// Window size for fixed-base scalar multiplication
|
|
|
|
pub const FIXED_BASE_WINDOW_SIZE: usize = 3;
|
2021-03-18 08:38:31 -07:00
|
|
|
|
2021-03-24 09:39:47 -07:00
|
|
|
/// $2^{`FIXED_BASE_WINDOW_SIZE`}$
|
2021-03-23 22:18:00 -07:00
|
|
|
pub const H: usize = 1 << FIXED_BASE_WINDOW_SIZE;
|
|
|
|
|
2021-03-24 09:39:47 -07:00
|
|
|
/// Number of windows for a full-width scalar
|
2021-03-27 03:02:37 -07:00
|
|
|
pub const NUM_WINDOWS: usize =
|
|
|
|
(pallas::Base::NUM_BITS as usize + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE;
|
2021-03-18 08:38:31 -07:00
|
|
|
|
2021-03-24 09:39:47 -07:00
|
|
|
/// 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;
|
|
|
|
|
2021-05-03 07:28:22 -07:00
|
|
|
/// Number of bits for which complete addition needs to be used in variable-base
|
|
|
|
/// scalar multiplication
|
2021-03-18 08:38:31 -07:00
|
|
|
pub const NUM_COMPLETE_BITS: usize = 3;
|
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
/// For each fixed base, we calculate its scalar multiples in three-bit windows.
|
|
|
|
/// Each window will have $2^3 = 8$ points.
|
|
|
|
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);
|
2021-03-18 08:38:31 -07:00
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
// 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) {
|
2021-03-18 08:38:31 -07:00
|
|
|
window_table.push(
|
2021-03-23 22:18:00 -07:00
|
|
|
(0..H)
|
2021-03-18 08:38:31 -07:00
|
|
|
.map(|k| {
|
2021-06-04 22:14:29 -07:00
|
|
|
// 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()
|
2021-03-18 08:38:31 -07:00
|
|
|
})
|
2021-05-03 08:55:37 -07:00
|
|
|
.collect::<ArrayVec<C, H>>()
|
|
|
|
.into_inner()
|
|
|
|
.unwrap(),
|
2021-03-18 08:38:31 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
// 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(),
|
|
|
|
);
|
2021-03-18 08:38:31 -07:00
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
window_table
|
|
|
|
}
|
2021-03-18 08:38:31 -07:00
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
/// For each window, we interpolate the $x$-coordinate.
|
|
|
|
/// Here, we pre-compute and store the coefficients of the interpolation polynomial.
|
|
|
|
fn compute_lagrange_coeffs<C: CurveAffine>(base: C, num_windows: usize) -> Vec<[C::Base; H]> {
|
|
|
|
// We are interpolating over the 3-bit window, k \in [0..8)
|
|
|
|
let points: Vec<_> = (0..H).map(|i| C::Base::from_u64(i as u64)).collect();
|
2021-03-18 08:38:31 -07:00
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
let window_table = compute_window_table(base, num_windows);
|
2021-03-18 08:38:31 -07:00
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
window_table
|
|
|
|
.iter()
|
|
|
|
.map(|window_points| {
|
|
|
|
let x_window_points: Vec<_> = window_points
|
2021-03-18 08:38:31 -07:00
|
|
|
.iter()
|
2021-06-04 22:14:29 -07:00
|
|
|
.map(|point| *point.coordinates().unwrap().x())
|
2021-03-18 08:38:31 -07:00
|
|
|
.collect();
|
2021-06-04 22:14:29 -07:00
|
|
|
lagrange_interpolate(&points, &x_window_points)
|
|
|
|
.into_iter()
|
|
|
|
.collect::<ArrayVec<C::Base, H>>()
|
|
|
|
.into_inner()
|
|
|
|
.unwrap()
|
|
|
|
})
|
|
|
|
.collect()
|
2021-03-18 08:38:31 -07:00
|
|
|
}
|
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
/// For each window, $z$ is a field element such that for each point $(x, y)$ in the window:
|
|
|
|
/// - $z + y = u^2$ (some square in the field); and
|
|
|
|
/// - $z - y$ is not a square.
|
|
|
|
/// If successful, return a vector of `(z: u64, us: [C::Base; H])` for each window.
|
|
|
|
fn find_zs_and_us<C: CurveAffine>(base: C, num_windows: usize) -> Option<Vec<(u64, [C::Base; H])>> {
|
|
|
|
// Closure to find z and u's for one window
|
|
|
|
let find_z_and_us = |window_points: &[C]| {
|
|
|
|
assert_eq!(H, window_points.len());
|
2021-04-13 07:26:18 -07:00
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
let ys: Vec<_> = window_points
|
|
|
|
.iter()
|
|
|
|
.map(|point| *point.coordinates().unwrap().y())
|
|
|
|
.collect();
|
|
|
|
(0..(1000 * (1 << (2 * H)))).find_map(|z| {
|
|
|
|
ys.iter()
|
|
|
|
.map(|&y| {
|
|
|
|
if (-y + C::Base::from_u64(z)).sqrt().is_none().into() {
|
|
|
|
(y + C::Base::from_u64(z)).sqrt().into()
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Option<ArrayVec<C::Base, H>>>()
|
|
|
|
.map(|us| (z, us.into_inner().unwrap()))
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
let window_table = compute_window_table(base, num_windows);
|
|
|
|
window_table
|
|
|
|
.iter()
|
|
|
|
.map(|window_points| find_z_and_us(window_points))
|
|
|
|
.collect()
|
2021-03-18 08:38:31 -07:00
|
|
|
}
|
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
#[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);
|
2021-03-18 08:38:31 -07:00
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
// 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.
|
2021-04-13 07:26:18 -07:00
|
|
|
for bits in 0..(1 << FIXED_BASE_WINDOW_SIZE) {
|
2021-06-04 22:14:29 -07:00
|
|
|
{
|
|
|
|
// Interpolate the x-coordinate using this window's coefficients
|
|
|
|
let interpolated_x = 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);
|
|
|
|
}
|
2021-03-18 08:38:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
// 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 = util::evaluate::<C>(bits, &lagrange_coeffs[num_windows - 1]);
|
2021-03-18 08:38:31 -07:00
|
|
|
|
2021-06-04 22:14:29 -07:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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);
|
|
|
|
|
|
|
|
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();
|
2021-06-05 05:18:14 -07:00
|
|
|
let u = C::Base::from_bytes(u).unwrap();
|
2021-06-04 22:14:29 -07:00
|
|
|
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()));
|
2021-03-18 08:38:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-09 19:31:47 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use ff::PrimeField;
|
|
|
|
use pasta_curves::pallas;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
// Nodes in the Merkle tree are Pallas base field elements.
|
|
|
|
fn l_orchard_merkle() {
|
|
|
|
assert_eq!(super::L_ORCHARD_MERKLE, pallas::Base::NUM_BITS as usize);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
// Orchard uses the Pallas base field as its base field.
|
|
|
|
fn l_orchard_base() {
|
|
|
|
assert_eq!(super::L_ORCHARD_BASE, pallas::Base::NUM_BITS as usize);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
// Orchard uses the Pallas base field as its base field.
|
|
|
|
fn l_orchard_scalar() {
|
|
|
|
assert_eq!(super::L_ORCHARD_SCALAR, pallas::Scalar::NUM_BITS as usize);
|
|
|
|
}
|
|
|
|
}
|