mirror of https://github.com/zcash/halo2.git
Add Orchard fixed bases and tests
This commit is contained in:
parent
4f1f32dab0
commit
2c11f3a048
239
src/constants.rs
239
src/constants.rs
|
@ -1,4 +1,18 @@
|
|||
//! Constants used in the Orchard protocol.
|
||||
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;
|
||||
|
||||
/// $\ell^\mathsf{Orchard}_\mathsf{base}$
|
||||
pub(crate) const L_ORCHARD_BASE: usize = 255;
|
||||
|
@ -26,3 +40,228 @@ 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;
|
||||
|
||||
/// Number of windows
|
||||
pub const NUM_WINDOWS: usize = pallas::Base::NUM_BITS as usize / FIXED_BASE_WINDOW_SIZE;
|
||||
|
||||
/// Number of bits used in complete addition (for variable-base scalar mul)
|
||||
pub const NUM_COMPLETE_BITS: usize = 3;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum OrchardFixedBases<C: CurveAffine> {
|
||||
CommitIvkR(OrchardFixedBase<C>),
|
||||
NoteCommitR(OrchardFixedBase<C>),
|
||||
NullifierK(OrchardFixedBase<C>),
|
||||
ValueCommitR(OrchardFixedBase<C>),
|
||||
ValueCommitV(OrchardFixedBase<C>),
|
||||
}
|
||||
|
||||
#[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.
|
||||
/// Each window will have 2^3 = 8 points.
|
||||
fn compute_window_table(&self) -> Vec<Vec<C>>;
|
||||
|
||||
/// For each window, we interpolate the x-coordinate.
|
||||
/// Here, we pre-compute and store the coefficients of the interpolation polynomial.
|
||||
fn compute_lagrange_coeffs(&self) -> Vec<Vec<C::Base>>;
|
||||
|
||||
/// 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) -> Option<Vec<u64>>;
|
||||
}
|
||||
|
||||
impl<C: CurveAffine> FixedBase<C> for OrchardFixedBase<C> {
|
||||
fn compute_window_table(&self) -> Vec<Vec<C>> {
|
||||
let h: usize = 1 << FIXED_BASE_WINDOW_SIZE;
|
||||
let mut window_table: Vec<Vec<C>> = Vec::with_capacity(NUM_WINDOWS);
|
||||
|
||||
// Generate window table entries for all windows but the last.
|
||||
// For these first 84 windows, we compute the multiple [(k+1)*(8^w)]B.
|
||||
// Here, w ranges from [0..84)
|
||||
for w in 0..(NUM_WINDOWS - 1) {
|
||||
window_table.push(
|
||||
(0..h)
|
||||
.map(|k| {
|
||||
// scalar = (k+1)*(8^w)
|
||||
let scalar = C::ScalarExt::from_u64(k as u64 + 1)
|
||||
* C::ScalarExt::from_u64(h as u64).pow(&[w as u64, 0, 0, 0]);
|
||||
(self.0 * scalar).to_affine()
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
|
||||
// Generate window table entries for the last window, w = 84.
|
||||
// For the last window, we compute [k * (8^w) - sum]B, where sum is defined
|
||||
// as sum = \sum_{j = 0}^{83} 8^j
|
||||
let sum = (0..(NUM_WINDOWS - 1)).fold(C::ScalarExt::zero(), |acc, w| {
|
||||
acc + C::ScalarExt::from_u64(h as u64).pow(&[w as u64, 0, 0, 0])
|
||||
});
|
||||
window_table.push(
|
||||
(0..h)
|
||||
.map(|k| {
|
||||
// scalar = k * (8^w) - sum, where w = 84
|
||||
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;
|
||||
(self.0 * scalar).to_affine()
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
|
||||
window_table
|
||||
}
|
||||
|
||||
fn compute_lagrange_coeffs(&self) -> Vec<Vec<C::Base>> {
|
||||
let h: usize = 1 << FIXED_BASE_WINDOW_SIZE;
|
||||
|
||||
// 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();
|
||||
|
||||
let window_table = self.compute_window_table();
|
||||
|
||||
window_table
|
||||
.iter()
|
||||
.map(|window_points| {
|
||||
let x_window_points: Vec<_> = window_points
|
||||
.iter()
|
||||
.map(|point| point.get_xy().unwrap().0)
|
||||
.collect();
|
||||
let coeffs = lagrange_interpolate(&points, &x_window_points);
|
||||
coeffs
|
||||
})
|
||||
.collect::<Vec<Vec<_>>>()
|
||||
}
|
||||
|
||||
/// 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) -> Option<Vec<u64>> {
|
||||
// Closure to find z for one window
|
||||
let find_z = |window_points: &[C]| {
|
||||
let h: usize = 1 << FIXED_BASE_WINDOW_SIZE;
|
||||
assert_eq!(h, window_points.len());
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
for z in 0..(1000 * (1 << (2 * h))) {
|
||||
if ys.iter().map(|y| z_for_single_y(*y, z)).sum::<usize>() == h {
|
||||
return Some(z);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
};
|
||||
|
||||
let window_table = self.compute_window_table();
|
||||
window_table
|
||||
.iter()
|
||||
.map(|window_points| find_z(window_points))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TestFixedBase<C: CurveAffine> {
|
||||
fn test_lagrange_coeffs(&self);
|
||||
fn test_z(&self, z: &[u64]);
|
||||
}
|
||||
|
||||
impl<C: CurveAffine> TestFixedBase<C> for OrchardFixedBase<C> {
|
||||
fn test_lagrange_coeffs(&self) {
|
||||
let h = 1 << FIXED_BASE_WINDOW_SIZE;
|
||||
let lagrange_coeffs = self.compute_lagrange_coeffs();
|
||||
let mut points = Vec::<C::CurveExt>::with_capacity(NUM_WINDOWS);
|
||||
|
||||
let scalar = C::Scalar::rand();
|
||||
let bits = util::decompose_scalar_fixed::<C>(
|
||||
scalar,
|
||||
C::Scalar::NUM_BITS as usize,
|
||||
FIXED_BASE_WINDOW_SIZE,
|
||||
);
|
||||
|
||||
// Check first 84 windows, i.e. `k_0, k_1, ..., k_83`
|
||||
for ((idx, bits), coeffs) in bits[0..(NUM_WINDOWS - 1)]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.zip(lagrange_coeffs[0..(NUM_WINDOWS - 1)].iter())
|
||||
{
|
||||
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)
|
||||
* C::Scalar::from_u64(h as u64).pow(&[idx as u64, 0, 0, 0]);
|
||||
let x = point.to_affine().get_xy().unwrap().0;
|
||||
|
||||
assert_eq!(x, interpolated_x);
|
||||
points.push(point);
|
||||
}
|
||||
|
||||
// Check last window
|
||||
{
|
||||
let last_bits = bits[NUM_WINDOWS - 1];
|
||||
let interpolated_x = util::evaluate::<C>(last_bits, &lagrange_coeffs[NUM_WINDOWS - 1]);
|
||||
|
||||
// [k * (8^w) - offset]B, where offset = \sum_{j = 0}^{83} 8^j
|
||||
let offset = (0..(NUM_WINDOWS - 1)).fold(C::Scalar::zero(), |acc, w| {
|
||||
acc + C::Scalar::from_u64(h as u64).pow(&[w as u64, 0, 0, 0])
|
||||
});
|
||||
let scalar = C::Scalar::from_u64(last_bits as u64)
|
||||
* C::Scalar::from_u64(h as u64).pow(&[(NUM_WINDOWS - 1) as u64, 0, 0, 0])
|
||||
- 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);
|
||||
}
|
||||
|
||||
fn test_z(&self, z: &[u64]) {
|
||||
let window_table = self.compute_window_table();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
use super::{OrchardFixedBase, OrchardFixedBases, COMMIT_IVK_PERSONALIZATION};
|
||||
use halo2::arithmetic::{CurveAffine, FieldExt};
|
||||
|
||||
pub const PERSONALIZATION: &str = COMMIT_IVK_PERSONALIZATION;
|
||||
|
||||
/// Generator used in SinsemillaCommit randomness for IVK commitment
|
||||
pub const GENERATOR: ([u8; 32], [u8; 32]) = (
|
||||
[
|
||||
148, 238, 176, 134, 155, 115, 130, 32, 71, 108, 43, 81, 122, 0, 18, 183, 189, 78, 168, 64,
|
||||
186, 250, 116, 181, 109, 239, 205, 50, 185, 198, 91, 14,
|
||||
],
|
||||
[
|
||||
250, 170, 248, 240, 127, 69, 3, 58, 247, 119, 172, 189, 199, 234, 17, 244, 34, 155, 121,
|
||||
180, 206, 194, 252, 134, 55, 107, 139, 116, 163, 117, 220, 15,
|
||||
],
|
||||
);
|
||||
|
||||
/// z-values for GENERATOR
|
||||
pub const Z: [u64; 85] = [
|
||||
1640, 16319, 75535, 213644, 22431, 77718, 73598, 44704, 58426, 90793, 51317, 35788, 62987,
|
||||
39128, 29961, 196204, 23144, 4960, 31792, 67688, 156889, 128199, 394678, 1391, 49801, 69085,
|
||||
177001, 27216, 17637, 12069, 8898, 134862, 137982, 35001, 261172, 3219, 171891, 6532, 93082,
|
||||
27872, 44212, 66355, 4768, 96884, 4793, 37757, 26619, 5486, 1315, 15325, 48605, 9168, 2511,
|
||||
84012, 73415, 74774, 224831, 26856, 4179, 82322, 39504, 32139, 75335, 14373, 63220, 39155,
|
||||
29901, 33099, 758, 27784, 6442, 252, 142824, 106033, 24247, 47057, 170067, 30302, 304042,
|
||||
163259, 49391, 34561, 350373, 139177, 147760,
|
||||
];
|
||||
|
||||
pub fn generator<C: CurveAffine>() -> OrchardFixedBases<C> {
|
||||
OrchardFixedBases::CommitIvkR(OrchardFixedBase::<C>::new(
|
||||
C::from_xy(
|
||||
C::Base::from_bytes(&GENERATOR.0).unwrap(),
|
||||
C::Base::from_bytes(&GENERATOR.1).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::TestFixedBase;
|
||||
use super::*;
|
||||
use crate::primitives::sinsemilla::CommitDomain;
|
||||
use group::Curve;
|
||||
use halo2::{
|
||||
arithmetic::{CurveAffine, FieldExt},
|
||||
pasta::pallas,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn generator() {
|
||||
let domain = CommitDomain::new(PERSONALIZATION);
|
||||
let point = domain.R();
|
||||
let (x, y) = point.to_affine().get_xy().unwrap();
|
||||
|
||||
assert_eq!(x, pallas::Base::from_bytes(&GENERATOR.0).unwrap());
|
||||
assert_eq!(y, pallas::Base::from_bytes(&GENERATOR.1).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lagrange_coeffs() {
|
||||
let base = super::generator::<pallas::Affine>();
|
||||
match base {
|
||||
OrchardFixedBases::CommitIvkR(inner) => inner.test_lagrange_coeffs(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn z() {
|
||||
let base = super::generator::<pallas::Affine>();
|
||||
match base {
|
||||
OrchardFixedBases::CommitIvkR(inner) => inner.test_z(&Z),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
use super::{OrchardFixedBase, OrchardFixedBases, NOTE_COMMITMENT_PERSONALIZATION};
|
||||
use halo2::arithmetic::{CurveAffine, FieldExt};
|
||||
|
||||
pub const PERSONALIZATION: &str = NOTE_COMMITMENT_PERSONALIZATION;
|
||||
|
||||
/// Generator used in SinsemillaCommit randomness for note commitment
|
||||
pub const GENERATOR: ([u8; 32], [u8; 32]) = (
|
||||
[
|
||||
27, 85, 45, 121, 90, 101, 108, 254, 2, 206, 235, 232, 42, 16, 38, 207, 116, 73, 104, 35,
|
||||
246, 164, 144, 15, 230, 159, 210, 102, 11, 44, 179, 36,
|
||||
],
|
||||
[
|
||||
184, 34, 111, 203, 218, 243, 248, 43, 50, 219, 6, 34, 114, 46, 198, 187, 54, 179, 135, 26,
|
||||
92, 178, 197, 64, 187, 63, 245, 129, 53, 180, 231, 25,
|
||||
],
|
||||
);
|
||||
|
||||
/// z-values for GENERATOR
|
||||
pub const Z: [u64; 85] = [
|
||||
10213, 84688, 5015, 29076, 5250, 12480, 1589, 21978, 40626, 116200, 36680, 56513, 80295, 1371,
|
||||
36801, 26527, 11103, 61032, 199301, 33177, 49711, 167190, 1448, 51069, 40410, 171413, 82827,
|
||||
15451, 53663, 4202, 47840, 93100, 44310, 10271, 27499, 76928, 39695, 59189, 70288, 24401,
|
||||
33207, 3472, 13911, 8835, 193349, 259, 41151, 2318, 33540, 21052, 14435, 18358, 49426, 52169,
|
||||
96418, 52931, 85348, 392973, 85905, 92539, 22878, 26933, 41387, 22788, 89854, 54883, 18584,
|
||||
19451, 4488, 283677, 74400, 56046, 20644, 5330, 27521, 99158, 9360, 10834, 78610, 7963, 19984,
|
||||
149297, 10335, 32061, 214389,
|
||||
];
|
||||
|
||||
pub fn generator<C: CurveAffine>() -> OrchardFixedBases<C> {
|
||||
OrchardFixedBases::NoteCommitR(OrchardFixedBase::<C>::new(
|
||||
C::from_xy(
|
||||
C::Base::from_bytes(&GENERATOR.0).unwrap(),
|
||||
C::Base::from_bytes(&GENERATOR.1).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::TestFixedBase;
|
||||
use super::*;
|
||||
use crate::primitives::sinsemilla::CommitDomain;
|
||||
use group::Curve;
|
||||
use halo2::{
|
||||
arithmetic::{CurveAffine, FieldExt},
|
||||
pasta::pallas,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn generator() {
|
||||
let domain = CommitDomain::new(PERSONALIZATION);
|
||||
let point = domain.R();
|
||||
let (x, y) = point.to_affine().get_xy().unwrap();
|
||||
|
||||
assert_eq!(x, pallas::Base::from_bytes(&GENERATOR.0).unwrap());
|
||||
assert_eq!(y, pallas::Base::from_bytes(&GENERATOR.1).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lagrange_coeffs() {
|
||||
let base = super::generator::<pallas::Affine>();
|
||||
match base {
|
||||
OrchardFixedBases::NoteCommitR(inner) => inner.test_lagrange_coeffs(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn z() {
|
||||
let base = super::generator::<pallas::Affine>();
|
||||
match base {
|
||||
OrchardFixedBases::NoteCommitR(inner) => inner.test_z(&Z),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
use crate::constants::{OrchardFixedBase, OrchardFixedBases, ORCHARD_PERSONALIZATION};
|
||||
use halo2::arithmetic::{CurveAffine, FieldExt};
|
||||
|
||||
pub const PERSONALIZATION: &str = ORCHARD_PERSONALIZATION;
|
||||
|
||||
pub const GENERATOR: ([u8; 32], [u8; 32]) = (
|
||||
[
|
||||
74, 166, 88, 164, 116, 16, 207, 20, 93, 0, 62, 45, 168, 59, 130, 172, 228, 79, 239, 35, 33,
|
||||
244, 20, 8, 126, 126, 100, 17, 248, 88, 183, 52,
|
||||
],
|
||||
[
|
||||
141, 155, 6, 243, 162, 111, 188, 180, 253, 188, 17, 96, 117, 217, 25, 246, 206, 193, 176,
|
||||
192, 64, 196, 91, 252, 21, 22, 204, 177, 62, 197, 187, 44,
|
||||
],
|
||||
);
|
||||
|
||||
/// z-values for GENERATOR
|
||||
pub const Z: [u64; 85] = [
|
||||
32517, 3118, 55842, 5295, 2252, 43091, 193188, 73424, 27335, 55867, 11015, 46382, 29066, 69577,
|
||||
2838, 245429, 25519, 172913, 25762, 138009, 11170, 132216, 114997, 52870, 52313, 102066, 5989,
|
||||
365, 73950, 74675, 191463, 34356, 16506, 63389, 4652, 81717, 108428, 120446, 80918, 25398,
|
||||
75806, 116193, 63775, 97332, 2183, 43473, 92592, 38016, 47712, 30288, 25445, 10737, 211404,
|
||||
26095, 72119, 25953, 3730, 19087, 28678, 11891, 69181, 214129, 2050, 72933, 124047, 16956,
|
||||
16977, 37315, 74647, 49184, 75499, 30521, 12997, 11908, 108937, 37055, 47165, 40492, 22849,
|
||||
89930, 69888, 193158, 105211, 27681, 32387,
|
||||
];
|
||||
|
||||
pub fn generator<C: CurveAffine>() -> OrchardFixedBases<C> {
|
||||
OrchardFixedBases::NullifierK(OrchardFixedBase::<C>::new(
|
||||
C::from_xy(
|
||||
C::Base::from_bytes(&GENERATOR.0).unwrap(),
|
||||
C::Base::from_bytes(&GENERATOR.1).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::TestFixedBase;
|
||||
use super::*;
|
||||
use group::Curve;
|
||||
use halo2::{
|
||||
arithmetic::{CurveAffine, CurveExt, FieldExt},
|
||||
pasta::pallas,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn generator() {
|
||||
let hasher = pallas::Point::hash_to_curve(PERSONALIZATION);
|
||||
let point = hasher(b"K");
|
||||
let (x, y) = point.to_affine().get_xy().unwrap();
|
||||
|
||||
assert_eq!(x, pallas::Base::from_bytes(&GENERATOR.0).unwrap());
|
||||
assert_eq!(y, pallas::Base::from_bytes(&GENERATOR.1).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lagrange_coeffs() {
|
||||
let base = super::generator::<pallas::Affine>();
|
||||
match base {
|
||||
OrchardFixedBases::NullifierK(inner) => inner.test_lagrange_coeffs(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn z() {
|
||||
let base = super::generator::<pallas::Affine>();
|
||||
match base {
|
||||
OrchardFixedBases::NullifierK(inner) => inner.test_z(&Z),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
use ff::PrimeField;
|
||||
use halo2::arithmetic::{CurveAffine, FieldExt};
|
||||
|
||||
/// Decompose a scalar into FIXED_BASE_WINDOW_SIZE bits (little-endian)
|
||||
/// For a window size of `w`, this returns [k_0, ..., k_n] where each `k_i`
|
||||
/// is a `w`-bit value, and `scalar = k_0 + k_1 * w + k_n * w^n`.
|
||||
pub fn decompose_scalar_fixed<C: CurveAffine>(
|
||||
scalar: C::Scalar,
|
||||
scalar_num_bits: usize,
|
||||
window_num_bits: usize,
|
||||
) -> Vec<u8> {
|
||||
let mut bits: Vec<bool> = scalar
|
||||
.to_le_bits()
|
||||
.into_iter()
|
||||
.take(scalar_num_bits)
|
||||
.collect();
|
||||
|
||||
assert_eq!(bits.len(), scalar_num_bits);
|
||||
|
||||
// Pad bits to multiple of window_num_bits
|
||||
bits.append(&mut vec![
|
||||
false;
|
||||
(window_num_bits
|
||||
- (scalar_num_bits % window_num_bits))
|
||||
% window_num_bits
|
||||
]);
|
||||
|
||||
bits.chunks_exact(window_num_bits)
|
||||
.map(|chunk| {
|
||||
let mut chunk = chunk.iter();
|
||||
*(chunk.next().unwrap()) as u8
|
||||
+ ((*(chunk.next().unwrap()) as u8) << 1)
|
||||
+ ((*(chunk.next().unwrap()) as u8) << 2)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Evaluate y = f(x) given the coefficients of f(x)
|
||||
pub fn evaluate<C: CurveAffine>(x: u8, coeffs: &[C::Base]) -> C::Base {
|
||||
(0..coeffs.len())
|
||||
.zip(coeffs.iter())
|
||||
.fold(C::Base::default(), |acc, (pow, coeff)| {
|
||||
acc + (*coeff) * C::Base::from_u64(x as u64).pow(&[pow as u64, 0, 0, 0])
|
||||
})
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
use super::{OrchardFixedBase, OrchardFixedBases, VALUE_COMMITMENT_PERSONALIZATION};
|
||||
use halo2::arithmetic::{CurveAffine, FieldExt};
|
||||
|
||||
pub const PERSONALIZATION: &str = VALUE_COMMITMENT_PERSONALIZATION;
|
||||
|
||||
/// The value commitment is used to check balance between inputs and outputs. The value is
|
||||
/// placed over this generator.
|
||||
pub const GENERATOR: ([u8; 32], [u8; 32]) = (
|
||||
[
|
||||
199, 209, 64, 100, 24, 110, 206, 85, 59, 77, 6, 42, 67, 76, 118, 116, 253, 250, 208, 71,
|
||||
184, 191, 140, 13, 93, 79, 56, 33, 94, 39, 101, 10,
|
||||
],
|
||||
[
|
||||
76, 105, 226, 202, 61, 74, 65, 200, 220, 56, 136, 63, 71, 255, 94, 246, 153, 160, 21, 162,
|
||||
106, 112, 47, 120, 165, 32, 53, 96, 19, 181, 110, 36,
|
||||
],
|
||||
);
|
||||
|
||||
/// z-values for GENERATOR
|
||||
pub const Z: [u64; 85] = [
|
||||
287008, 5261, 10541, 67788, 1084, 31201, 1662, 32921, 2652, 52006, 3486, 82692, 7295, 40007,
|
||||
37754, 44773, 3021, 171863, 33315, 8829, 67034, 50428, 40391, 6615, 40340, 238, 199437, 50234,
|
||||
899, 27825, 139735, 36053, 194684, 28229, 31719, 66166, 100600, 59796, 52804, 10221, 159298,
|
||||
32923, 158, 40332, 100062, 8923, 23819, 96460, 44805, 2951, 50005, 134465, 44269, 51778, 73741,
|
||||
11413, 19391, 84631, 96003, 71276, 61444, 49575, 154646, 229521, 4555, 313045, 30544, 15466,
|
||||
7134, 12520, 164127, 29119, 11279, 103167, 63033, 13765, 35197, 71168, 10379, 9560, 54432,
|
||||
132537, 189703, 29967, 9941,
|
||||
];
|
||||
|
||||
pub fn generator<C: CurveAffine>() -> OrchardFixedBases<C> {
|
||||
OrchardFixedBases::ValueCommitR(OrchardFixedBase::<C>::new(
|
||||
C::from_xy(
|
||||
C::Base::from_bytes(&GENERATOR.0).unwrap(),
|
||||
C::Base::from_bytes(&GENERATOR.1).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::TestFixedBase;
|
||||
use super::*;
|
||||
use group::Curve;
|
||||
use halo2::{
|
||||
arithmetic::{CurveAffine, CurveExt, FieldExt},
|
||||
pasta::pallas,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn generator() {
|
||||
let hasher = pallas::Point::hash_to_curve(PERSONALIZATION);
|
||||
let point = hasher(b"-r");
|
||||
let (x, y) = point.to_affine().get_xy().unwrap();
|
||||
|
||||
assert_eq!(x, pallas::Base::from_bytes(&GENERATOR.0).unwrap());
|
||||
assert_eq!(y, pallas::Base::from_bytes(&GENERATOR.1).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lagrange_coeffs() {
|
||||
let base = super::generator::<pallas::Affine>();
|
||||
match base {
|
||||
OrchardFixedBases::ValueCommitR(inner) => inner.test_lagrange_coeffs(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn z() {
|
||||
let base = super::generator::<pallas::Affine>();
|
||||
match base {
|
||||
OrchardFixedBases::ValueCommitR(inner) => inner.test_z(&Z),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
use super::{OrchardFixedBase, OrchardFixedBases, VALUE_COMMITMENT_PERSONALIZATION};
|
||||
use halo2::arithmetic::{CurveAffine, FieldExt};
|
||||
|
||||
pub const PERSONALIZATION: &str = VALUE_COMMITMENT_PERSONALIZATION;
|
||||
|
||||
/// The value commitment is used to check balance between inputs and outputs. The value is
|
||||
/// placed over this generator.
|
||||
pub const GENERATOR: ([u8; 32], [u8; 32]) = (
|
||||
[
|
||||
146, 134, 252, 1, 6, 122, 71, 38, 242, 210, 12, 65, 214, 129, 99, 228, 216, 165, 217, 139,
|
||||
4, 159, 130, 201, 115, 100, 204, 172, 241, 221, 192, 57,
|
||||
],
|
||||
[
|
||||
104, 21, 198, 148, 56, 181, 122, 135, 95, 147, 179, 108, 174, 10, 183, 200, 243, 25, 27,
|
||||
248, 167, 68, 37, 105, 11, 87, 167, 253, 72, 189, 248, 33,
|
||||
],
|
||||
);
|
||||
|
||||
/// z-values for GENERATOR
|
||||
pub const Z: [u64; 85] = [
|
||||
12093, 20558, 3369, 22650, 43666, 81863, 2960, 131095, 84, 117033, 7349, 122998, 47884, 43451,
|
||||
22237, 3461, 71521, 147314, 31021, 70554, 47822, 44159, 45362, 7756, 19977, 41666, 82714,
|
||||
21407, 16731, 48013, 173284, 356652, 3027, 9756, 10560, 1554, 40272, 131726, 32724, 6152,
|
||||
67912, 2642, 100128, 8950, 20487, 58314, 7440, 63032, 586, 32770, 37328, 21775, 4186, 172635,
|
||||
111256, 35867, 23903, 137179, 16694, 43650, 32899, 40670, 55501, 44805, 20211, 207309, 2718,
|
||||
63301, 145483, 5584, 55596, 349346, 30535, 112990, 44821, 48471, 107386, 16232, 16492, 88498,
|
||||
33976, 106405, 11043, 44897, 98652,
|
||||
];
|
||||
|
||||
pub fn generator<C: CurveAffine>() -> OrchardFixedBases<C> {
|
||||
OrchardFixedBases::ValueCommitV(OrchardFixedBase::<C>::new(
|
||||
C::from_xy(
|
||||
C::Base::from_bytes(&GENERATOR.0).unwrap(),
|
||||
C::Base::from_bytes(&GENERATOR.1).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::TestFixedBase;
|
||||
use super::*;
|
||||
use group::Curve;
|
||||
use halo2::{
|
||||
arithmetic::{CurveAffine, CurveExt, FieldExt},
|
||||
pasta::pallas,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn generator() {
|
||||
let hasher = pallas::Point::hash_to_curve(PERSONALIZATION);
|
||||
let point = hasher(b"-v");
|
||||
let (x, y) = point.to_affine().get_xy().unwrap();
|
||||
|
||||
println!("{:?}", x.to_bytes());
|
||||
println!("{:?}", y.to_bytes());
|
||||
|
||||
assert_eq!(x, pallas::Base::from_bytes(&GENERATOR.0).unwrap());
|
||||
assert_eq!(y, pallas::Base::from_bytes(&GENERATOR.1).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lagrange_coeffs() {
|
||||
let base = super::generator::<pallas::Affine>();
|
||||
match base {
|
||||
OrchardFixedBases::ValueCommitV(inner) => inner.test_lagrange_coeffs(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn z() {
|
||||
let base = super::generator::<pallas::Affine>();
|
||||
match base {
|
||||
OrchardFixedBases::ValueCommitV(inner) => inner.test_z(&Z),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue