2021-03-05 15:25:45 -08:00
|
|
|
//! Constants used in the Orchard protocol.
|
2021-03-18 08:38:31 -07:00
|
|
|
use ff::{Field, PrimeField};
|
|
|
|
use group::Curve;
|
|
|
|
use halo2::{
|
|
|
|
arithmetic::{lagrange_interpolate, CurveAffine, FieldExt},
|
|
|
|
pasta::pallas,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub mod commit_ivk_r;
|
|
|
|
pub mod note_commit_r;
|
|
|
|
pub mod nullifier_k;
|
|
|
|
pub mod value_commit_r;
|
|
|
|
pub mod value_commit_v;
|
|
|
|
|
|
|
|
pub mod util;
|
2021-03-05 15:25:45 -08:00
|
|
|
|
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-03-24 04:46:34 -07:00
|
|
|
/// $\ell_\mathsf{value}$
|
|
|
|
pub(crate) const L_VALUE: usize = 64;
|
|
|
|
|
2021-03-18 08:38:18 -07:00
|
|
|
// SWU hash-to-curve personalizations
|
|
|
|
/// This is used 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 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";
|
|
|
|
|
|
|
|
/// 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-03-18 08:38:31 -07:00
|
|
|
/// Number of bits used in complete addition (for variable-base scalar mul)
|
|
|
|
pub const NUM_COMPLETE_BITS: usize = 3;
|
|
|
|
|
2021-03-23 09:42:30 -07:00
|
|
|
#[derive(Copy, Clone, Debug)]
|
2021-03-18 08:38:31 -07:00
|
|
|
pub enum OrchardFixedBases<C: CurveAffine> {
|
|
|
|
CommitIvkR(OrchardFixedBase<C>),
|
|
|
|
NoteCommitR(OrchardFixedBase<C>),
|
|
|
|
NullifierK(OrchardFixedBase<C>),
|
|
|
|
ValueCommitR(OrchardFixedBase<C>),
|
|
|
|
ValueCommitV(OrchardFixedBase<C>),
|
|
|
|
}
|
|
|
|
|
2021-03-23 09:42:30 -07:00
|
|
|
impl<C: CurveAffine> std::hash::Hash for OrchardFixedBases<C> {
|
|
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
|
|
match *self {
|
2021-03-25 20:59:08 -07:00
|
|
|
OrchardFixedBases::CommitIvkR(_) => state.write(self.variant()),
|
|
|
|
OrchardFixedBases::NoteCommitR(_) => state.write(self.variant()),
|
|
|
|
OrchardFixedBases::NullifierK(_) => state.write(self.variant()),
|
|
|
|
OrchardFixedBases::ValueCommitR(_) => state.write(self.variant()),
|
|
|
|
OrchardFixedBases::ValueCommitV(_) => state.write(self.variant()),
|
2021-03-23 09:42:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<C: CurveAffine> OrchardFixedBases<C> {
|
|
|
|
pub fn inner(&self) -> OrchardFixedBase<C> {
|
|
|
|
match self {
|
|
|
|
Self::CommitIvkR(inner) => *inner,
|
|
|
|
Self::NoteCommitR(inner) => *inner,
|
|
|
|
Self::NullifierK(inner) => *inner,
|
|
|
|
Self::ValueCommitR(inner) => *inner,
|
|
|
|
Self::ValueCommitV(inner) => *inner,
|
|
|
|
}
|
|
|
|
}
|
2021-03-25 20:59:08 -07:00
|
|
|
|
|
|
|
pub fn variant(&self) -> &[u8] {
|
|
|
|
match *self {
|
|
|
|
OrchardFixedBases::CommitIvkR(_) => b"CommitIvkR",
|
|
|
|
OrchardFixedBases::NoteCommitR(_) => b"NoteCommitR",
|
|
|
|
OrchardFixedBases::NullifierK(_) => b"NullifierK",
|
|
|
|
OrchardFixedBases::ValueCommitR(_) => b"ValueCommitR",
|
|
|
|
OrchardFixedBases::ValueCommitV(_) => b"ValueCommitV",
|
|
|
|
}
|
|
|
|
}
|
2021-03-23 09:42:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<C: CurveAffine> PartialEq for OrchardFixedBases<C> {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
self.inner() == other.inner()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<C: CurveAffine> Eq for OrchardFixedBases<C> {}
|
|
|
|
|
2021-03-25 20:59:08 -07:00
|
|
|
impl<C: CurveAffine> PartialOrd for OrchardFixedBases<C> {
|
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
|
|
self.variant().partial_cmp(&other.variant())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<C: CurveAffine> Ord for OrchardFixedBases<C> {
|
|
|
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
|
|
self.variant().cmp(&other.variant())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-18 08:38:31 -07:00
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct OrchardFixedBase<C: CurveAffine>(C);
|
|
|
|
|
|
|
|
impl<C: CurveAffine> OrchardFixedBase<C> {
|
|
|
|
pub fn new(generator: C) -> Self {
|
|
|
|
OrchardFixedBase(generator)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn value(&self) -> C {
|
|
|
|
self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait FixedBase<C: CurveAffine> {
|
|
|
|
/// For each fixed base, we calculate its scalar multiples in three-bit windows.
|
2021-03-24 09:39:47 -07:00
|
|
|
/// Each window will have $2^3 = 8$ points.
|
|
|
|
fn compute_window_table(&self, num_windows: usize) -> Vec<[C; H]>;
|
2021-03-18 08:38:31 -07:00
|
|
|
|
2021-03-24 09:39:47 -07:00
|
|
|
/// For each window, we interpolate the $x$-coordinate.
|
2021-03-18 08:38:31 -07:00
|
|
|
/// Here, we pre-compute and store the coefficients of the interpolation polynomial.
|
2021-03-24 09:39:47 -07:00
|
|
|
fn compute_lagrange_coeffs(&self, num_windows: usize) -> Vec<[C::Base; H]>;
|
2021-03-18 08:38:31 -07:00
|
|
|
|
2021-03-24 09:39:47 -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.
|
|
|
|
fn find_zs(&self, num_windows: usize) -> Option<Vec<u64>>;
|
2021-03-18 08:38:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<C: CurveAffine> FixedBase<C> for OrchardFixedBase<C> {
|
2021-03-24 09:39:47 -07:00
|
|
|
fn compute_window_table(&self, 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
|
|
|
|
|
|
|
// Generate window table entries for all windows but the last.
|
2021-03-24 09:39:47 -07:00
|
|
|
// For these first `num_windows - 1` windows, we compute the multiple $[(k+1)*(8^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| {
|
|
|
|
// scalar = (k+1)*(8^w)
|
|
|
|
let scalar = C::ScalarExt::from_u64(k as u64 + 1)
|
2021-03-23 22:18:00 -07:00
|
|
|
* C::ScalarExt::from_u64(H as u64).pow(&[w as u64, 0, 0, 0]);
|
2021-03-18 08:38:31 -07:00
|
|
|
(self.0 * scalar).to_affine()
|
|
|
|
})
|
2021-03-23 22:18:00 -07:00
|
|
|
.enumerate()
|
|
|
|
.fold([C::identity(); H], |mut window, (index, entry)| {
|
|
|
|
window[index] = entry;
|
|
|
|
window
|
|
|
|
}),
|
2021-03-18 08:38:31 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-03-24 09:39:47 -07:00
|
|
|
// Generate window table entries for the last window, w = `num_windows - 1`.
|
2021-03-18 08:38:31 -07:00
|
|
|
// For the last window, we compute [k * (8^w) - sum]B, where sum is defined
|
2021-03-24 09:39:47 -07:00
|
|
|
// as sum = \sum_{j = 0}^{`num_windows - 2`} 8^j
|
|
|
|
let sum = (0..(num_windows - 1)).fold(C::ScalarExt::zero(), |acc, w| {
|
2021-03-23 22:18:00 -07:00
|
|
|
acc + C::ScalarExt::from_u64(H as u64).pow(&[w as u64, 0, 0, 0])
|
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-03-24 09:39:47 -07:00
|
|
|
// scalar = k * (8^w) - sum, where w = `num_windows - 1`
|
2021-03-18 08:38:31 -07:00
|
|
|
let scalar = C::ScalarExt::from_u64(k as u64)
|
2021-03-23 22:18:00 -07:00
|
|
|
* C::ScalarExt::from_u64(H as u64).pow(&[
|
2021-03-24 09:39:47 -07:00
|
|
|
(num_windows - 1) as u64,
|
2021-03-18 08:38:31 -07:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
])
|
|
|
|
- sum;
|
|
|
|
(self.0 * scalar).to_affine()
|
|
|
|
})
|
2021-03-23 22:18:00 -07:00
|
|
|
.enumerate()
|
|
|
|
.fold([C::identity(); H], |mut window, (index, entry)| {
|
|
|
|
window[index] = entry;
|
|
|
|
window
|
|
|
|
}),
|
2021-03-18 08:38:31 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
window_table
|
|
|
|
}
|
|
|
|
|
2021-03-24 09:39:47 -07:00
|
|
|
fn compute_lagrange_coeffs(&self, num_windows: usize) -> Vec<[C::Base; 8]> {
|
2021-03-18 08:38:31 -07:00
|
|
|
// We are interpolating over the 3-bit window, k \in [0..8)
|
2021-03-23 22:18:00 -07:00
|
|
|
let points: Vec<_> = (0..H).map(|i| C::Base::from_u64(i as u64)).collect();
|
2021-03-18 08:38:31 -07:00
|
|
|
|
2021-03-24 09:39:47 -07:00
|
|
|
let window_table = self.compute_window_table(num_windows);
|
2021-03-18 08:38:31 -07:00
|
|
|
|
|
|
|
window_table
|
|
|
|
.iter()
|
|
|
|
.map(|window_points| {
|
|
|
|
let x_window_points: Vec<_> = window_points
|
|
|
|
.iter()
|
|
|
|
.map(|point| point.get_xy().unwrap().0)
|
|
|
|
.collect();
|
2021-03-23 09:42:30 -07:00
|
|
|
lagrange_interpolate(&points, &x_window_points)
|
2021-03-23 22:18:00 -07:00
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.fold([C::Base::default(); H], |mut window, (index, entry)| {
|
|
|
|
window[index] = *entry;
|
|
|
|
window
|
|
|
|
})
|
2021-03-18 08:38:31 -07:00
|
|
|
})
|
2021-03-23 22:18:00 -07:00
|
|
|
.collect()
|
2021-03-18 08:38:31 -07:00
|
|
|
}
|
|
|
|
|
2021-03-24 09:39:47 -07:00
|
|
|
/// For each window, z is a field element such that for each point (x, y) in the window:
|
2021-03-18 08:38:31 -07:00
|
|
|
/// - z + y = u^2 (some square in the field); and
|
|
|
|
/// - z - y is not a square.
|
2021-03-24 09:39:47 -07:00
|
|
|
fn find_zs(&self, num_windows: usize) -> Option<Vec<u64>> {
|
2021-03-18 08:38:31 -07:00
|
|
|
// Closure to find z for one window
|
|
|
|
let find_z = |window_points: &[C]| {
|
2021-03-23 22:18:00 -07:00
|
|
|
assert_eq!(H, window_points.len());
|
2021-03-18 08:38:31 -07:00
|
|
|
|
|
|
|
let ys: Vec<_> = window_points
|
|
|
|
.iter()
|
|
|
|
.map(|point| point.get_xy().unwrap().1)
|
|
|
|
.collect();
|
|
|
|
let z_for_single_y = |y: C::Base, z: u64| {
|
|
|
|
let sum_y_is_square: bool = (y + C::Base::from_u64(z)).sqrt().is_some().into();
|
|
|
|
let sum_neg_y_is_square: bool = (-y + C::Base::from_u64(z)).sqrt().is_some().into();
|
|
|
|
(sum_y_is_square && !sum_neg_y_is_square) as usize
|
|
|
|
};
|
|
|
|
|
2021-03-23 22:18:00 -07:00
|
|
|
for z in 0..(1000 * (1 << (2 * H))) {
|
|
|
|
if ys.iter().map(|y| z_for_single_y(*y, z)).sum::<usize>() == H {
|
2021-03-18 08:38:31 -07:00
|
|
|
return Some(z);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2021-03-24 09:39:47 -07:00
|
|
|
let window_table = self.compute_window_table(num_windows);
|
|
|
|
window_table[21..22]
|
2021-03-18 08:38:31 -07:00
|
|
|
.iter()
|
|
|
|
.map(|window_points| find_z(window_points))
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait TestFixedBase<C: CurveAffine> {
|
2021-03-24 09:39:47 -07:00
|
|
|
fn test_lagrange_coeffs(&self, scalar: C::Scalar, scalar_num_bits: usize, num_windows: usize);
|
|
|
|
fn test_z(&self, z: &[u64], num_windows: usize);
|
2021-03-18 08:38:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<C: CurveAffine> TestFixedBase<C> for OrchardFixedBase<C> {
|
2021-03-24 09:39:47 -07:00
|
|
|
fn test_lagrange_coeffs(&self, scalar: C::Scalar, scalar_num_bits: usize, num_windows: usize) {
|
|
|
|
let lagrange_coeffs = self.compute_lagrange_coeffs(num_windows);
|
|
|
|
let mut points = Vec::<C::CurveExt>::with_capacity(num_windows);
|
|
|
|
|
|
|
|
let bits =
|
|
|
|
util::decompose_scalar_fixed::<C>(scalar, scalar_num_bits, FIXED_BASE_WINDOW_SIZE);
|
2021-03-18 08:38:31 -07:00
|
|
|
|
|
|
|
// Check first 84 windows, i.e. `k_0, k_1, ..., k_83`
|
2021-03-24 09:39:47 -07:00
|
|
|
for ((idx, bits), coeffs) in bits[0..(num_windows - 1)]
|
2021-03-18 08:38:31 -07:00
|
|
|
.iter()
|
|
|
|
.enumerate()
|
2021-03-24 09:39:47 -07:00
|
|
|
.zip(lagrange_coeffs[0..(num_windows - 1)].iter())
|
2021-03-18 08:38:31 -07:00
|
|
|
{
|
|
|
|
let interpolated_x = util::evaluate::<C>(*bits, coeffs);
|
|
|
|
|
|
|
|
// [(k+1)*(8^w)]B
|
|
|
|
let point = self.0
|
|
|
|
* C::Scalar::from_u64(*bits as u64 + 1)
|
2021-03-23 22:18:00 -07:00
|
|
|
* C::Scalar::from_u64(H as u64).pow(&[idx as u64, 0, 0, 0]);
|
2021-03-18 08:38:31 -07:00
|
|
|
let x = point.to_affine().get_xy().unwrap().0;
|
|
|
|
|
|
|
|
assert_eq!(x, interpolated_x);
|
|
|
|
points.push(point);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check last window
|
|
|
|
{
|
2021-03-24 09:39:47 -07:00
|
|
|
let last_bits = bits[num_windows - 1];
|
|
|
|
let interpolated_x = util::evaluate::<C>(last_bits, &lagrange_coeffs[num_windows - 1]);
|
2021-03-18 08:38:31 -07:00
|
|
|
|
|
|
|
// [k * (8^w) - offset]B, where offset = \sum_{j = 0}^{83} 8^j
|
2021-03-24 09:39:47 -07:00
|
|
|
let offset = (0..(num_windows - 1)).fold(C::Scalar::zero(), |acc, w| {
|
2021-03-23 22:18:00 -07:00
|
|
|
acc + C::Scalar::from_u64(H as u64).pow(&[w as u64, 0, 0, 0])
|
2021-03-18 08:38:31 -07:00
|
|
|
});
|
|
|
|
let scalar = C::Scalar::from_u64(last_bits as u64)
|
2021-03-24 09:39:47 -07:00
|
|
|
* C::Scalar::from_u64(H as u64).pow(&[(num_windows - 1) as u64, 0, 0, 0])
|
2021-03-18 08:38:31 -07:00
|
|
|
- offset;
|
|
|
|
let point = self.0 * scalar;
|
|
|
|
let x = point.to_affine().get_xy().unwrap().0;
|
|
|
|
|
|
|
|
assert_eq!(x, interpolated_x);
|
|
|
|
points.push(point);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the sum of all the window points
|
|
|
|
let window_sum = points
|
|
|
|
.iter()
|
|
|
|
.fold(C::CurveExt::default(), |acc, point| acc + point);
|
|
|
|
let multiple = self.0 * scalar;
|
|
|
|
assert_eq!(window_sum, multiple);
|
|
|
|
}
|
|
|
|
|
2021-03-24 09:39:47 -07:00
|
|
|
fn test_z(&self, z: &[u64], num_windows: usize) {
|
|
|
|
let window_table = self.compute_window_table(num_windows);
|
2021-03-18 08:38:31 -07:00
|
|
|
|
|
|
|
for (z, window_points) in z.iter().zip(window_table) {
|
|
|
|
for point in window_points.iter() {
|
|
|
|
let y = point.get_xy().unwrap().1;
|
|
|
|
assert_eq!((C::Base::from_u64(*z) + y).sqrt().is_some().unwrap_u8(), 1);
|
|
|
|
assert_eq!((C::Base::from_u64(*z) - y).sqrt().is_some().unwrap_u8(), 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|