Refactor src/constants and primitives::sinsemilla::constants.

This commit is contained in:
therealyingtong 2021-08-20 12:21:46 +08:00
parent d37db53e0b
commit 1a3cbeb896
28 changed files with 811 additions and 726 deletions

View File

@ -642,8 +642,8 @@ mod tests {
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
},
constants::{
OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains,
COMMIT_IVK_PERSONALIZATION, L_ORCHARD_BASE, T_Q,
fixed_bases::COMMIT_IVK_PERSONALIZATION, OrchardCommitDomains, OrchardFixedBases,
OrchardHashDomains, L_ORCHARD_BASE, T_Q,
},
primitives::sinsemilla::CommitDomain,
};

View File

@ -3,7 +3,6 @@ use crate::{
circuit::gadget::utilities::{
lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions,
},
constants,
primitives::sinsemilla,
};
use arrayvec::ArrayVec;
@ -24,6 +23,40 @@ pub(super) mod mul;
pub(super) mod mul_fixed;
pub(super) mod witness_point;
/// 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;
/// $\ell_\mathsf{value}$
/// Number of bits in an unsigned short scalar.
pub(crate) const L_VALUE: usize = 64;
/// $\ell^\mathsf{Orchard}_\mathsf{base}$
/// Number of bits in a Pallas base field element.
pub(crate) const L_ORCHARD_BASE: usize = 255;
/// $\ell^\mathsf{Orchard}_\mathsf{scalar}$
/// Number of bits in a Pallas scalar field element.
pub(crate) const L_ORCHARD_SCALAR: usize = 255;
/// 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;
/// A curve point represented in affine (x, y) coordinates, or the
/// identity represented as (0, 0).
/// Each coordinate is assigned to a cell.
@ -165,9 +198,9 @@ pub struct EccConfig<FixedPoints: super::FixedPoints<pallas::Affine>> {
/// arrays instead of `Vec`s.
pub trait FixedPoint<C: CurveAffine>: std::fmt::Debug + Eq + Clone {
fn generator(&self) -> C;
fn u(&self) -> Vec<[[u8; 32]; constants::H]>;
fn u(&self) -> Vec<[[u8; 32]; H]>;
fn z(&self) -> Vec<u64>;
fn lagrange_coeffs(&self) -> Vec<[C::Base; constants::H]>;
fn lagrange_coeffs(&self) -> Vec<[C::Base; H]>;
}
/// A chip implementing EccInstructions
@ -276,7 +309,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> EccChip<FixedPoints> {
#[derive(Clone, Debug)]
pub struct EccScalarFixed {
value: Option<pallas::Scalar>,
windows: ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { constants::NUM_WINDOWS }>,
windows: ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { NUM_WINDOWS }>,
}
// TODO: Make V a `u64`
@ -301,8 +334,7 @@ type MagnitudeSign = (MagnitudeCell, SignCell);
pub struct EccScalarFixedShort {
magnitude: MagnitudeCell,
sign: SignCell,
running_sum:
ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { constants::NUM_WINDOWS_SHORT + 1 }>,
running_sum: ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { NUM_WINDOWS_SHORT + 1 }>,
}
/// A base field element used for fixed-base scalar multiplication.
@ -317,7 +349,7 @@ pub struct EccScalarFixedShort {
#[derive(Clone, Debug)]
struct EccBaseFieldElemFixed {
base_field_elem: AssignedCell<pallas::Base, pallas::Base>,
running_sum: ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { constants::NUM_WINDOWS + 1 }>,
running_sum: ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { NUM_WINDOWS + 1 }>,
}
impl EccBaseFieldElemFixed {

View File

@ -1,7 +1,6 @@
use super::{add, EccPoint, NonIdentityEccPoint};
use super::{add, EccPoint, NonIdentityEccPoint, T_Q};
use crate::{
circuit::gadget::utilities::{bool_check, lookup_range_check::LookupRangeCheckConfig, ternary},
constants::T_Q,
primitives::sinsemilla,
};
use std::{

View File

@ -1,7 +1,6 @@
use super::Z;
use super::{T_Q, Z};
use crate::{
circuit::gadget::utilities::lookup_range_check::LookupRangeCheckConfig, constants::T_Q,
primitives::sinsemilla,
circuit::gadget::utilities::lookup_range_check::LookupRangeCheckConfig, primitives::sinsemilla,
};
use halo2::circuit::AssignedCell;
use halo2::{

View File

@ -1,9 +1,8 @@
use super::{
add, add_incomplete, EccBaseFieldElemFixed, EccScalarFixed, EccScalarFixedShort, FixedPoint,
NonIdentityEccPoint,
NonIdentityEccPoint, FIXED_BASE_WINDOW_SIZE, H,
};
use crate::circuit::gadget::utilities::decompose_running_sum::RunningSumConfig;
use crate::constants;
use std::marker::PhantomData;
@ -26,15 +25,15 @@ pub mod short;
lazy_static! {
static ref TWO_SCALAR: pallas::Scalar = pallas::Scalar::from(2);
// H = 2^3 (3-bit window)
static ref H_SCALAR: pallas::Scalar = pallas::Scalar::from(constants::H as u64);
static ref H_BASE: pallas::Base = pallas::Base::from(constants::H as u64);
static ref H_SCALAR: pallas::Scalar = pallas::Scalar::from_u64(H as u64);
static ref H_BASE: pallas::Base = pallas::Base::from_u64(H as u64);
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Config<FixedPoints: super::FixedPoints<pallas::Affine>> {
running_sum_config: RunningSumConfig<pallas::Base, { constants::FIXED_BASE_WINDOW_SIZE }>,
running_sum_config: RunningSumConfig<pallas::Base, FIXED_BASE_WINDOW_SIZE>,
// The fixed Lagrange interpolation coefficients for `x_p`.
lagrange_coeffs: [Column<Fixed>; constants::H],
lagrange_coeffs: [Column<Fixed>; H],
// The fixed `z` for each window such that `y + z = u^2`.
fixed_z: Column<Fixed>,
// Decomposition of an `n-1`-bit scalar into `k`-bit windows:
@ -57,7 +56,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
#[allow(clippy::too_many_arguments)]
pub(super) fn configure(
meta: &mut ConstraintSystem<pallas::Base>,
lagrange_coeffs: [Column<Fixed>; constants::H],
lagrange_coeffs: [Column<Fixed>; H],
window: Column<Advice>,
x_p: Column<Advice>,
y_p: Column<Advice>,
@ -138,7 +137,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
// z_{i+1} = (z_i - a_i) / 2^3
// => a_i = z_i - z_{i+1} * 2^3
let word = z_cur - z_next * pallas::Base::from(constants::H as u64);
let word = z_cur - z_next * pallas::Base::from(H as u64);
self.coords_check(meta, q_mul_fixed_running_sum, word)
});
@ -156,7 +155,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
let z = meta.query_fixed(self.fixed_z, Rotation::cur());
let u = meta.query_advice(self.u, Rotation::cur());
let window_pow: Vec<Expression<pallas::Base>> = (0..constants::H)
let window_pow: Vec<Expression<pallas::Base>> = (0..H)
.map(|pow| {
(0..pow).fold(Expression::Constant(pallas::Base::one()), |acc, _| {
acc * window.clone()
@ -228,7 +227,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
coords_check_toggle.enable(region, window + offset)?;
// Assign x-coordinate Lagrange interpolation coefficients
for k in 0..(constants::H) {
for k in 0..(H) {
region.assign_fixed(
|| {
format!(
@ -374,12 +373,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
// offset_acc = \sum_{j = 0}^{NUM_WINDOWS - 2} 2^{FIXED_BASE_WINDOW_SIZE*j + 1}
let offset_acc = (0..(NUM_WINDOWS - 1)).fold(pallas::Scalar::zero(), |acc, w| {
acc + (*TWO_SCALAR).pow(&[
constants::FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1,
0,
0,
0,
])
acc + (*TWO_SCALAR).pow(&[FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1, 0, 0, 0])
});
// `scalar = [k * 8^84 - offset_acc]`, where `offset_acc = \sum_{j = 0}^{83} 2^{FIXED_BASE_WINDOW_SIZE*j + 1}`.
@ -484,7 +478,7 @@ impl ScalarFixed {
.map(|window| {
if let Some(window) = window {
let window = window.get_lower_32() as usize;
assert!(window < constants::H);
assert!(window < H);
Some(window)
} else {
None

View File

@ -1,11 +1,12 @@
use super::super::{EccBaseFieldElemFixed, EccPoint, FixedPoints};
use super::super::{
EccBaseFieldElemFixed, EccPoint, FixedPoints, L_ORCHARD_BASE, NUM_WINDOWS, T_P,
};
use super::H_BASE;
use crate::{
circuit::gadget::utilities::{
bitrange_subset, lookup_range_check::LookupRangeCheckConfig, range_check,
},
constants::{self, T_P},
primitives::sinsemilla,
};
use halo2::circuit::AssignedCell;
@ -18,7 +19,7 @@ use pasta_curves::{arithmetic::FieldExt, pallas};
use std::convert::TryInto;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Config<Fixed: FixedPoints<pallas::Affine>> {
q_mul_fixed_base_field: Selector,
canon_advices: [Column<Advice>; 3],
@ -178,8 +179,8 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
offset,
scalar.clone(),
true,
constants::L_ORCHARD_BASE,
constants::NUM_WINDOWS,
L_ORCHARD_BASE,
NUM_WINDOWS,
)?;
EccBaseFieldElemFixed {
base_field_elem: running_sum[0].clone(),
@ -187,15 +188,13 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
}
};
let (acc, mul_b) = self
.super_config
.assign_region_inner::<_, { constants::NUM_WINDOWS }>(
&mut region,
offset,
&(&scalar).into(),
base,
self.super_config.running_sum_config.q_range_check,
)?;
let (acc, mul_b) = self.super_config.assign_region_inner::<_, NUM_WINDOWS>(
&mut region,
offset,
&(&scalar).into(),
base,
self.super_config.running_sum_config.q_range_check,
)?;
Ok((scalar, acc, mul_b))
},
@ -390,12 +389,12 @@ pub mod tests {
use crate::circuit::gadget::{
ecc::{
chip::{EccChip, FixedPoint},
chip::{EccChip, FixedPoint, H},
FixedPointBaseField, NonIdentityPoint, Point,
},
utilities::UtilitiesInstructions,
};
use crate::constants::{self, NullifierK, OrchardFixedBases};
use crate::constants::{NullifierK, OrchardFixedBases};
pub fn test_mul_fixed_base_field(
chip: EccChip<OrchardFixedBases>,
@ -463,7 +462,7 @@ pub mod tests {
// (There is another *non-canonical* sequence
// 5333333333333333333333333333333333333333332711161673731021062440252244051273333333333 in octal.)
{
let h = pallas::Base::from(constants::H as u64);
let h = pallas::Base::from(H as u64);
let scalar_fixed = "1333333333333333333333333333333333333333333333333333333333333333333333333333333333334"
.chars()
.fold(pallas::Base::zero(), |acc, c| {

View File

@ -1,9 +1,8 @@
use super::super::{EccPoint, EccScalarFixed, FixedPoints};
use crate::{
circuit::gadget::utilities::{decompose_word, range_check},
constants::{self, L_ORCHARD_SCALAR, NUM_WINDOWS},
use super::super::{
EccPoint, EccScalarFixed, FixedPoints, FIXED_BASE_WINDOW_SIZE, H, L_ORCHARD_SCALAR, NUM_WINDOWS,
};
use crate::circuit::gadget::utilities::{decompose_word, range_check};
use arrayvec::ArrayVec;
use halo2::{
circuit::{AssignedCell, Layouter, Region},
@ -12,7 +11,7 @@ use halo2::{
};
use pasta_curves::pallas;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Config<Fixed: FixedPoints<pallas::Affine>> {
q_mul_fixed_full: Selector,
super_config: super::Config<Fixed>,
@ -46,7 +45,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
// 1 * (window - 0) * (window - 1) * ... * (window - 7)
.chain(Some((
"window range check",
q_mul_fixed_full * range_check(window, constants::H),
q_mul_fixed_full * range_check(window, H),
)))
});
}
@ -84,11 +83,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
// Decompose scalar into `k-bit` windows
let scalar_windows: Option<Vec<u8>> = scalar.map(|scalar| {
decompose_word::<pallas::Scalar>(
&scalar,
SCALAR_NUM_BITS,
constants::FIXED_BASE_WINDOW_SIZE,
)
decompose_word::<pallas::Scalar>(&scalar, SCALAR_NUM_BITS, FIXED_BASE_WINDOW_SIZE)
});
// Store the scalar decomposition
@ -135,15 +130,13 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
let scalar = self.witness(&mut region, offset, scalar)?;
let (acc, mul_b) = self
.super_config
.assign_region_inner::<_, { constants::NUM_WINDOWS }>(
&mut region,
offset,
&(&scalar).into(),
base,
self.q_mul_fixed_full,
)?;
let (acc, mul_b) = self.super_config.assign_region_inner::<_, NUM_WINDOWS>(
&mut region,
offset,
&(&scalar).into(),
base,
self.q_mul_fixed_full,
)?;
Ok((scalar, acc, mul_b))
},
@ -188,10 +181,10 @@ pub mod tests {
use rand::rngs::OsRng;
use crate::circuit::gadget::ecc::{
chip::{EccChip, FixedPoint as FixedPointTrait},
chip::{EccChip, FixedPoint as FixedPointTrait, H},
FixedPoint, NonIdentityPoint, Point,
};
use crate::constants::{self, OrchardFixedBases, OrchardFixedBasesFull};
use crate::constants::{OrchardFixedBases, OrchardFixedBasesFull};
pub fn test_mul_fixed(
chip: EccChip<OrchardFixedBases>,
@ -277,7 +270,7 @@ pub mod tests {
// (There is another *non-canonical* sequence
// 5333333333333333333333333333333333333333332711161673731021062440252244051273333333333 in octal.)
{
let h = pallas::Scalar::from(constants::H as u64);
let h = pallas::Scalar::from(H as u64);
let scalar_fixed = "1333333333333333333333333333333333333333333333333333333333333333333333333333333333334"
.chars()
.fold(pallas::Scalar::zero(), |acc, c| {

View File

@ -1,10 +1,7 @@
use std::{array, convert::TryInto};
use super::super::{EccPoint, EccScalarFixedShort, FixedPoints};
use crate::{
circuit::gadget::{ecc::chip::MagnitudeSign, utilities::bool_check},
constants::{L_VALUE, NUM_WINDOWS_SHORT},
};
use super::super::{EccPoint, EccScalarFixedShort, FixedPoints, L_VALUE, NUM_WINDOWS_SHORT};
use crate::circuit::gadget::{ecc::chip::MagnitudeSign, utilities::bool_check};
use halo2::{
circuit::{Layouter, Region},
@ -13,7 +10,7 @@ use halo2::{
};
use pasta_curves::pallas;
#[derive(Clone, Debug, Copy, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Config<Fixed: FixedPoints<pallas::Affine>> {
// Selector used for fixed-base scalar mul with short signed exponent.
q_mul_fixed_short: Selector,

View File

@ -428,8 +428,8 @@ mod tests {
utilities::lookup_range_check::LookupRangeCheckConfig,
},
constants::{
fixed_bases::COMMIT_IVK_PERSONALIZATION, sinsemilla::MERKLE_CRH_PERSONALIZATION,
OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains,
COMMIT_IVK_PERSONALIZATION, MERKLE_CRH_PERSONALIZATION,
},
primitives::sinsemilla::{self, K},
};

View File

@ -13,6 +13,16 @@ use std::iter;
pub(in crate::circuit) mod chip;
/// SWU hash-to-curve personalization for the Merkle CRH generator
pub const MERKLE_CRH_PERSONALIZATION: &str = "z.cash:Orchard-MerkleCRH";
/// $\mathsf{MerkleDepth^{Orchard}}$
pub(crate) const MERKLE_DEPTH_ORCHARD: usize = 32;
/// $\ell^\mathsf{Orchard}_\mathsf{base}$
/// Number of bits in a Pallas base field element.
pub(crate) const L_ORCHARD_BASE: usize = 255;
/// Instructions to check the validity of a Merkle path of a given `PATH_LENGTH`.
/// The hash function used is a Sinsemilla instance with `K`-bit words.
/// The hash function can process `MAX_WORDS` words.
@ -130,7 +140,7 @@ where
pub mod tests {
use super::{
chip::{MerkleChip, MerkleConfig},
MerklePath,
MerklePath, MERKLE_DEPTH_ORCHARD,
};
use crate::{
@ -138,9 +148,7 @@ pub mod tests {
sinsemilla::chip::SinsemillaChip,
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
},
constants::{
OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains, MERKLE_DEPTH_ORCHARD,
},
constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains},
note::commitment::ExtractedNoteCommitment,
tree,
};

View File

@ -5,7 +5,7 @@ use halo2::{
};
use pasta_curves::{arithmetic::FieldExt, pallas};
use super::MerkleInstructions;
use super::{MerkleInstructions, L_ORCHARD_BASE, MERKLE_DEPTH_ORCHARD};
use crate::{
circuit::gadget::{
@ -20,7 +20,6 @@ use crate::{
UtilitiesInstructions,
},
},
constants::{L_ORCHARD_BASE, MERKLE_DEPTH_ORCHARD},
primitives::sinsemilla,
};
use std::array;
@ -345,10 +344,8 @@ where
// Check layer hash output against Sinsemilla primitives hash
#[cfg(test)]
{
use crate::{
constants::MERKLE_CRH_PERSONALIZATION, primitives::sinsemilla::HashDomain,
spec::i2lebsp,
};
use super::MERKLE_CRH_PERSONALIZATION;
use crate::{primitives::sinsemilla::HashDomain, spec::i2lebsp};
use group::ff::{PrimeField, PrimeFieldBits};
if let (Some(left), Some(right)) = (left.value(), right.value()) {

View File

@ -216,7 +216,6 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
#[cfg(test)]
mod tests {
use super::*;
use crate::constants::{self, FIXED_BASE_WINDOW_SIZE, L_ORCHARD_BASE, L_VALUE};
use group::ff::Field;
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
@ -226,6 +225,12 @@ mod tests {
use pasta_curves::{arithmetic::FieldExt, pallas};
use rand::rngs::OsRng;
const FIXED_BASE_WINDOW_SIZE: usize = 3;
const NUM_WINDOWS: usize = 85;
const NUM_WINDOWS_SHORT: usize = 22;
const L_BASE: usize = 255;
const L_SHORT: usize = 64;
#[test]
fn test_running_sum() {
struct MyCircuit<
@ -305,15 +310,11 @@ mod tests {
let alpha = pallas::Base::random(OsRng);
// Strict full decomposition should pass.
let circuit: MyCircuit<
pallas::Base,
L_ORCHARD_BASE,
FIXED_BASE_WINDOW_SIZE,
{ constants::NUM_WINDOWS },
> = MyCircuit {
alpha: Some(alpha),
strict: true,
};
let circuit: MyCircuit<pallas::Base, L_BASE, FIXED_BASE_WINDOW_SIZE, { NUM_WINDOWS }> =
MyCircuit {
alpha: Some(alpha),
strict: true,
};
let prover = MockProver::<pallas::Base>::run(8, &circuit, vec![]).unwrap();
assert_eq!(prover.verify(), Ok(()));
}
@ -325,9 +326,9 @@ mod tests {
// Strict full decomposition should pass.
let circuit: MyCircuit<
pallas::Base,
L_VALUE,
L_SHORT,
FIXED_BASE_WINDOW_SIZE,
{ constants::NUM_WINDOWS_SHORT },
{ NUM_WINDOWS_SHORT },
> = MyCircuit {
alpha: Some(alpha),
strict: true,
@ -343,9 +344,9 @@ mod tests {
// Strict partial decomposition should fail.
let circuit: MyCircuit<
pallas::Base,
L_VALUE,
L_SHORT,
FIXED_BASE_WINDOW_SIZE,
{ constants::NUM_WINDOWS_SHORT },
{ NUM_WINDOWS_SHORT },
> = MyCircuit {
alpha: Some(alpha),
strict: true,
@ -376,9 +377,9 @@ mod tests {
// Non-strict partial decomposition should pass.
let circuit: MyCircuit<
pallas::Base,
{ constants::L_VALUE },
{ L_SHORT },
FIXED_BASE_WINDOW_SIZE,
{ constants::NUM_WINDOWS_SHORT },
{ NUM_WINDOWS_SHORT },
> = MyCircuit {
alpha: Some(alpha),
strict: false,

View File

@ -1465,8 +1465,8 @@ mod tests {
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
},
constants::{
OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains, L_ORCHARD_BASE, L_VALUE,
NOTE_COMMITMENT_PERSONALIZATION, T_Q,
fixed_bases::NOTE_COMMITMENT_PERSONALIZATION, OrchardCommitDomains, OrchardFixedBases,
OrchardHashDomains, L_ORCHARD_BASE, L_VALUE, T_Q,
},
primitives::sinsemilla::CommitDomain,
};

View File

@ -1,30 +1,15 @@
//! Constants used in the Orchard protocol.
use crate::circuit::gadget::{
ecc::{chip::FixedPoint, FixedPoints},
sinsemilla::{CommitDomains, HashDomains},
};
use crate::primitives::sinsemilla::{
Q_COMMIT_IVK_M_GENERATOR, Q_MERKLE_CRH, Q_NOTE_COMMITMENT_M_GENERATOR,
};
use arrayvec::ArrayVec;
use ff::{Field, PrimeField};
use group::Curve;
use halo2::arithmetic::lagrange_interpolate;
use pasta_curves::{
arithmetic::{CurveAffine, FieldExt},
pallas,
};
pub mod commit_ivk_r;
pub mod note_commit_r;
pub mod nullifier_k;
pub mod spend_auth_g;
pub mod value_commit_r;
pub mod value_commit_v;
pub mod fixed_bases;
pub mod sinsemilla;
pub mod util;
pub use fixed_bases::{NullifierK, OrchardFixedBases, OrchardFixedBasesFull, ValueCommitV, H};
pub use sinsemilla::{OrchardCommitDomains, OrchardHashDomains};
pub use util::{evaluate, gen_const_array};
/// $\mathsf{MerkleDepth^{Orchard}}$
pub(crate) const MERKLE_DEPTH_ORCHARD: usize = 32;
/// The Pallas scalar field modulus is $q = 2^{254} + \mathsf{t_q}$.
/// <https://github.com/zcash/pasta>
pub(crate) const T_Q: u128 = 45560315531506369815346746415080538113;
@ -33,12 +18,6 @@ pub(crate) const T_Q: u128 = 45560315531506369815346746415080538113;
/// <https://github.com/zcash/pasta>
pub(crate) const T_P: u128 = 45560315531419706090280762371685220353;
/// $\mathsf{MerkleDepth^{Orchard}}$
pub(crate) const MERKLE_DEPTH_ORCHARD: usize = 32;
/// $\ell^\mathsf{Orchard}_\mathsf{Merkle}$
pub(crate) const L_ORCHARD_MERKLE: usize = 255;
/// $\ell^\mathsf{Orchard}_\mathsf{base}$
pub(crate) const L_ORCHARD_BASE: usize = 255;
@ -48,406 +27,14 @@ pub(crate) const L_ORCHARD_SCALAR: usize = 255;
/// $\ell_\mathsf{value}$
pub(crate) const L_VALUE: usize = 64;
/// T_PRIME_BITS is the smallest multiple of 10 such that 2^T_PRIME_BITS
/// is larger than t_P. t_P is defined in q_P = 2^254 + t_P for the
/// Pallas base field.
pub(crate) const PALLAS_T_PRIME_BITS: usize = 130;
/// 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 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 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";
/// 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;
/// $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 =
(pallas::Base::NUM_BITS as usize + 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;
/// 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);
// 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::Scalar::from(k as u64 + 2)
* C::Scalar::from(H as u64).pow(&[w as u64, 0, 0, 0]);
(base * scalar).to_affine()
})
.collect::<ArrayVec<C, H>>()
.into_inner()
.unwrap(),
);
}
// 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::Scalar::from(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::Scalar::from(k as u64)
* C::Scalar::from(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
}
/// 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(i as u64)).collect();
let window_table = compute_window_table(base, num_windows);
window_table
.iter()
.map(|window_points| {
let x_window_points: Vec<_> = window_points
.iter()
.map(|point| *point.coordinates().unwrap().x())
.collect();
lagrange_interpolate(&points, &x_window_points)
.into_iter()
.collect::<ArrayVec<C::Base, H>>()
.into_inner()
.unwrap()
})
.collect()
}
/// 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.
///
/// This function was used to generate the `z`s and `u`s for the Orchard fixed
/// bases. The outputs of this function have been stored as constants, and it
/// is not called anywhere in this codebase. However, we keep this function here
/// as a utility for those who wish to use it with different parameters.
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());
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(z)).sqrt().is_none().into() {
(y + C::Base::from(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()
}
#[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 {
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 {
fn generator(&self) -> pallas::Affine {
match self {
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 {
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 {
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(),
}
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS)
}
}
impl FixedPoint<pallas::Affine> for NullifierK {
fn generator(&self) -> pallas::Affine {
nullifier_k::generator()
}
fn u(&self) -> Vec<[[u8; 32]; H]> {
nullifier_k::U.to_vec()
}
fn z(&self) -> Vec<u64> {
nullifier_k::Z.to_vec()
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS)
}
}
impl FixedPoint<pallas::Affine> for ValueCommitV {
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()
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS_SHORT)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OrchardHashDomains {
NoteCommit,
CommitIvk,
MerkleCrh,
}
#[allow(non_snake_case)]
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(),
)
.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(),
)
.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(),
)
.unwrap(),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OrchardCommitDomains {
NoteCommit,
CommitIvk,
}
impl CommitDomains<pallas::Affine, OrchardFixedBases, OrchardHashDomains> for OrchardCommitDomains {
fn r(&self) -> OrchardFixedBasesFull {
match self {
Self::NoteCommit => OrchardFixedBasesFull::NoteCommitR,
Self::CommitIvk => OrchardFixedBasesFull::CommitIvkR,
}
}
fn hash_domain(&self) -> OrchardHashDomains {
match self {
Self::NoteCommit => OrchardHashDomains::NoteCommit,
Self::CommitIvk => OrchardHashDomains::CommitIvk,
}
}
}
#[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 = util::evaluate::<C>(bits, coeffs);
// Compute the actual x-coordinate of the multiple [(k+2)*(8^w)]B.
let point = base
* C::Scalar::from(bits as u64 + 2)
* C::Scalar::from(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);
}
}
}
// 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]);
// 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(2).pow(&[FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1, 0, 0, 0])
});
let scalar = C::Scalar::from(bits as u64)
* C::Scalar::from(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(base: pallas::Affine, 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();
let u = pallas::Base::from_repr(*u).unwrap();
assert_eq!(pallas::Base::from(*z) + y, u * u); // allow either square root
assert!(bool::from((pallas::Base::from(*z) - y).sqrt().is_none()));
}
}
}
#[cfg(test)]
mod tests {
use ff::PrimeField;
use pasta_curves::{arithmetic::FieldExt, 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() {

View File

@ -0,0 +1,355 @@
//! Orchard fixed bases.
use super::{L_ORCHARD_SCALAR, L_VALUE};
use crate::circuit::gadget::ecc::{chip::FixedPoint, FixedPoints};
use arrayvec::ArrayVec;
use ff::Field;
use group::Curve;
use halo2::arithmetic::lagrange_interpolate;
use pasta_curves::{
arithmetic::{CurveAffine, FieldExt},
pallas,
};
pub mod commit_ivk_r;
pub mod note_commit_r;
pub mod nullifier_k;
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_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";
/// 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";
/// 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;
/// 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);
// 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(),
);
}
// 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
}
/// 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();
let window_table = compute_window_table(base, num_windows);
window_table
.iter()
.map(|window_points| {
let x_window_points: Vec<_> = window_points
.iter()
.map(|point| *point.coordinates().unwrap().x())
.collect();
lagrange_interpolate(&points, &x_window_points)
.into_iter()
.collect::<ArrayVec<C::Base, H>>()
.into_inner()
.unwrap()
})
.collect()
}
/// 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.
///
/// This function was used to generate the `z`s and `u`s for the Orchard fixed
/// bases. The outputs of this function have been stored as constants, and it
/// is not called anywhere in this codebase. However, we keep this function here
/// as a utility for those who wish to use it with different parameters.
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());
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()
}
#[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 {
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 {
fn generator(&self) -> pallas::Affine {
match self {
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 {
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 {
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(),
}
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS)
}
}
impl FixedPoint<pallas::Affine> for NullifierK {
fn generator(&self) -> pallas::Affine {
nullifier_k::generator()
}
fn u(&self) -> Vec<[[u8; 32]; H]> {
nullifier_k::U.to_vec()
}
fn z(&self) -> Vec<u64> {
nullifier_k::Z.to_vec()
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS)
}
}
impl FixedPoint<pallas::Affine> for ValueCommitV {
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()
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS_SHORT)
}
}
#[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::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);
}
}
}
// 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::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);
}
}
#[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();
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()));
}
}
}

255
src/constants/sinsemilla.rs Normal file
View File

@ -0,0 +1,255 @@
//! Sinsemilla generators
use super::{OrchardFixedBases, OrchardFixedBasesFull};
use crate::circuit::gadget::sinsemilla::{CommitDomains, HashDomains};
use crate::spec::i2lebsp;
use pasta_curves::{
arithmetic::{CurveAffine, FieldExt},
pallas,
};
/// Number of bits of each message piece in $\mathsf{SinsemillaHashToPoint}$
pub const K: usize = 10;
/// $\frac{1}{2^K}$
pub const INV_TWO_POW_K: [u8; 32] = [
1, 0, 192, 196, 160, 229, 70, 82, 221, 165, 74, 202, 85, 7, 62, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 240, 63,
];
/// The largest integer such that $2^c \leq (r_P - 1) / 2$, where $r_P$ is the order
/// 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]) = (
[
93, 116, 168, 64, 9, 186, 14, 50, 42, 221, 70, 253, 90, 15, 150, 197, 93, 237, 176, 121,
180, 242, 159, 247, 13, 205, 251, 86, 160, 7, 128, 23,
],
[
99, 172, 73, 115, 90, 10, 39, 135, 158, 94, 219, 129, 136, 18, 34, 136, 44, 201, 244, 110,
217, 194, 190, 78, 131, 112, 198, 138, 147, 88, 160, 50,
],
);
/// Generator used in SinsemillaHashToPoint for IVK commitment
pub const Q_COMMIT_IVK_M_GENERATOR: ([u8; 32], [u8; 32]) = (
[
242, 130, 15, 121, 146, 47, 203, 107, 50, 162, 40, 81, 36, 204, 27, 66, 250, 65, 162, 90,
184, 129, 204, 125, 17, 200, 169, 74, 241, 12, 188, 5,
],
[
190, 222, 173, 207, 206, 229, 90, 190, 241, 165, 109, 201, 29, 53, 196, 70, 75, 5, 222, 32,
70, 7, 89, 239, 230, 190, 26, 212, 246, 76, 1, 27,
],
);
/// Generator used in SinsemillaHashToPoint for Merkle collision-resistant hash
pub const Q_MERKLE_CRH: ([u8; 32], [u8; 32]) = (
[
160, 198, 41, 127, 249, 199, 185, 248, 112, 16, 141, 192, 85, 185, 190, 201, 153, 14, 137,
239, 90, 54, 15, 160, 185, 24, 168, 99, 150, 210, 22, 22,
],
[
98, 234, 242, 37, 206, 174, 233, 134, 150, 21, 116, 5, 234, 150, 28, 226, 121, 89, 163, 79,
62, 242, 196, 45, 153, 32, 175, 227, 163, 66, 134, 53,
],
);
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(crate) fn i2lebsp_k(int: usize) -> [bool; K] {
assert!(int < (1 << K));
i2lebsp(int as u64)
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OrchardHashDomains {
NoteCommit,
CommitIvk,
MerkleCrh,
}
#[allow(non_snake_case)]
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(),
)
.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(),
)
.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(),
)
.unwrap(),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OrchardCommitDomains {
NoteCommit,
CommitIvk,
}
impl CommitDomains<pallas::Affine, OrchardFixedBases, OrchardHashDomains> for OrchardCommitDomains {
fn r(&self) -> OrchardFixedBasesFull {
match self {
Self::NoteCommit => OrchardFixedBasesFull::NoteCommitR,
Self::CommitIvk => OrchardFixedBasesFull::CommitIvkR,
}
}
fn hash_domain(&self) -> OrchardHashDomains {
match self {
Self::NoteCommit => OrchardHashDomains::NoteCommit,
Self::CommitIvk => OrchardHashDomains::CommitIvk,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::constants::{
fixed_bases::{COMMIT_IVK_PERSONALIZATION, NOTE_COMMITMENT_PERSONALIZATION},
sinsemilla::MERKLE_CRH_PERSONALIZATION,
};
use crate::primitives::sinsemilla::{CommitDomain, HashDomain};
use group::{ff::PrimeField, Curve};
use halo2::arithmetic::CurveAffine;
use halo2::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!(super::L_ORCHARD_MERKLE, pallas::Base::NUM_BITS as usize);
}
#[test]
fn lebs2ip_k_round_trip() {
let mut rng = OsRng;
{
let int = rng.gen_range(0..(1 << K));
assert_eq!(lebs2ip_k(&i2lebsp_k(int)) as usize, int);
}
assert_eq!(lebs2ip_k(&i2lebsp_k(0)) as usize, 0);
assert_eq!(lebs2ip_k(&i2lebsp_k((1 << K) - 1)) as usize, (1 << K) - 1);
}
#[test]
fn i2lebsp_k_round_trip() {
{
let bitstring = (0..K).map(|_| rand::random()).collect::<Vec<_>>();
assert_eq!(
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
bitstring
);
}
{
let bitstring = [false; K];
assert_eq!(
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
bitstring
);
}
{
let bitstring = [true; K];
assert_eq!(
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
bitstring
);
}
}
#[test]
fn q_note_commitment_m() {
let domain = CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION);
let point = domain.Q();
let coords = point.to_affine().coordinates().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]
fn q_commit_ivk_m() {
let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
let point = domain.Q();
let coords = point.to_affine().coordinates().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]
fn q_merkle_crh() {
let domain = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
let point = domain.Q();
let coords = point.to_affine().coordinates().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(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

@ -6,7 +6,7 @@ use pasta_curves::pallas;
use subtle::{ConstantTimeEq, CtOption};
use crate::{
constants::{L_ORCHARD_BASE, NOTE_COMMITMENT_PERSONALIZATION},
constants::{fixed_bases::NOTE_COMMITMENT_PERSONALIZATION, L_ORCHARD_BASE},
primitives::sinsemilla,
spec::extract_p,
value::NoteValue,

View File

@ -1,19 +1,37 @@
//! The Sinsemilla hash function and commitment scheme.
use group::Wnaf;
use group::{Curve, Wnaf};
use halo2::arithmetic::{CurveAffine, CurveExt};
use pasta_curves::pallas;
use subtle::CtOption;
use crate::spec::{extract_p_bottom, i2lebsp};
mod addition;
use self::addition::IncompletePoint;
mod constants;
mod sinsemilla_s;
pub use constants::*;
pub(crate) use sinsemilla_s::*;
pub use sinsemilla_s::SINSEMILLA_S;
/// Number of bits of each message piece in $\mathsf{SinsemillaHashToPoint}$
pub const K: usize = 10;
/// $\frac{1}{2^K}$
pub const INV_TWO_POW_K: [u8; 32] = [
1, 0, 192, 196, 160, 229, 70, 82, 221, 165, 74, 202, 85, 7, 62, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 240, 63,
];
/// The largest integer such that $2^c \leq (r_P - 1) / 2$, where $r_P$ is the order
/// of Pallas.
pub const C: usize = 253;
// 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";
pub(crate) fn lebs2ip_k(bits: &[bool]) -> u32 {
assert!(bits.len() == K);
@ -22,11 +40,18 @@ pub(crate) fn lebs2ip_k(bits: &[bool]) -> u32 {
.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(crate) fn i2lebsp_k(int: usize) -> [bool; K] {
assert!(int < (1 << K));
i2lebsp(int as u64)
/// Coordinate extractor for Pallas.
///
/// Defined in [Zcash Protocol Spec § 5.4.9.7: Coordinate Extractor for Pallas][concreteextractorpallas].
///
/// [concreteextractorpallas]: https://zips.z.cash/protocol/nu5.pdf#concreteextractorpallas
fn extract_p_bottom(point: CtOption<pallas::Point>) -> CtOption<pallas::Base> {
point.map(|p| {
p.to_affine()
.coordinates()
.map(|c| *c.x())
.unwrap_or_else(pallas::Base::zero)
})
}
/// Pads the given iterator (which MUST have length $\leq K * C$) with zero-bits to a
@ -196,12 +221,19 @@ impl CommitDomain {
pub(crate) fn R(&self) -> pallas::Point {
self.R
}
/// Returns the Sinsemilla $Q$ constant for this domain.
#[cfg(test)]
#[allow(non_snake_case)]
pub(crate) fn Q(&self) -> pallas::Point {
self.M.Q
}
}
#[cfg(test)]
mod tests {
use super::{i2lebsp_k, lebs2ip_k, Pad, K};
use rand::{self, rngs::OsRng, Rng};
use super::{Pad, K};
use pasta_curves::{arithmetic::CurveExt, pallas};
#[test]
fn pad() {
@ -242,41 +274,20 @@ mod tests {
}
#[test]
fn lebs2ip_k_round_trip() {
let mut rng = OsRng;
{
let int = rng.gen_range(0..(1 << K));
assert_eq!(lebs2ip_k(&i2lebsp_k(int)) as usize, int);
}
fn sinsemilla_s() {
use super::sinsemilla_s::SINSEMILLA_S;
use group::Curve;
use pasta_curves::arithmetic::CurveAffine;
assert_eq!(lebs2ip_k(&i2lebsp_k(0)) as usize, 0);
assert_eq!(lebs2ip_k(&i2lebsp_k((1 << K) - 1)) as usize, (1 << K) - 1);
}
let hasher = pallas::Point::hash_to_curve(super::S_PERSONALIZATION);
#[test]
fn i2lebsp_k_round_trip() {
{
let bitstring = (0..K).map(|_| rand::random()).collect::<Vec<_>>();
assert_eq!(
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
bitstring
);
}
{
let bitstring = [false; K];
assert_eq!(
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
bitstring
);
}
{
let bitstring = [true; K];
assert_eq!(
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
bitstring
);
for j in 0..(1u32 << K) {
let computed = {
let point = hasher(&j.to_le_bytes()).to_affine().coordinates().unwrap();
(*point.x(), *point.y())
};
let actual = SINSEMILLA_S[j as usize];
assert_eq!(computed, actual);
}
}
}

View File

@ -1,143 +0,0 @@
//! Sinsemilla generators
/// Number of bits of each message piece in $\mathsf{SinsemillaHashToPoint}$
pub const K: usize = 10;
/// $\frac{1}{2^K}$
pub const INV_TWO_POW_K: [u8; 32] = [
1, 0, 192, 196, 160, 229, 70, 82, 221, 165, 74, 202, 85, 7, 62, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 240, 63,
];
/// The largest integer such that $2^c \leq (r_P - 1) / 2$, where $r_P$ is the order
/// of Pallas.
pub const C: usize = 253;
// Sinsemilla Q generators
/// SWU hash-to-curve personalization for Sinsemilla $Q$ generators.
pub const Q_PERSONALIZATION: &str = "z.cash:SinsemillaQ";
/// Generator used in SinsemillaHashToPoint for note commitment
pub const Q_NOTE_COMMITMENT_M_GENERATOR: ([u8; 32], [u8; 32]) = (
[
93, 116, 168, 64, 9, 186, 14, 50, 42, 221, 70, 253, 90, 15, 150, 197, 93, 237, 176, 121,
180, 242, 159, 247, 13, 205, 251, 86, 160, 7, 128, 23,
],
[
99, 172, 73, 115, 90, 10, 39, 135, 158, 94, 219, 129, 136, 18, 34, 136, 44, 201, 244, 110,
217, 194, 190, 78, 131, 112, 198, 138, 147, 88, 160, 50,
],
);
/// Generator used in SinsemillaHashToPoint for IVK commitment
pub const Q_COMMIT_IVK_M_GENERATOR: ([u8; 32], [u8; 32]) = (
[
242, 130, 15, 121, 146, 47, 203, 107, 50, 162, 40, 81, 36, 204, 27, 66, 250, 65, 162, 90,
184, 129, 204, 125, 17, 200, 169, 74, 241, 12, 188, 5,
],
[
190, 222, 173, 207, 206, 229, 90, 190, 241, 165, 109, 201, 29, 53, 196, 70, 75, 5, 222, 32,
70, 7, 89, 239, 230, 190, 26, 212, 246, 76, 1, 27,
],
);
/// Generator used in SinsemillaHashToPoint for Merkle collision-resistant hash
pub const Q_MERKLE_CRH: ([u8; 32], [u8; 32]) = (
[
160, 198, 41, 127, 249, 199, 185, 248, 112, 16, 141, 192, 85, 185, 190, 201, 153, 14, 137,
239, 90, 54, 15, 160, 185, 24, 168, 99, 150, 210, 22, 22,
],
[
98, 234, 242, 37, 206, 174, 233, 134, 150, 21, 116, 5, 234, 150, 28, 226, 121, 89, 163, 79,
62, 242, 196, 45, 153, 32, 175, 227, 163, 66, 134, 53,
],
);
// Sinsemilla S generators
/// SWU hash-to-curve personalization for Sinsemilla $S$ generators.
pub const S_PERSONALIZATION: &str = "z.cash:SinsemillaS";
#[cfg(test)]
mod tests {
use super::super::{CommitDomain, HashDomain};
use super::*;
use crate::constants::{
COMMIT_IVK_PERSONALIZATION, MERKLE_CRH_PERSONALIZATION, NOTE_COMMITMENT_PERSONALIZATION,
};
use group::{ff::PrimeField, Curve};
use halo2::arithmetic::{CurveAffine, CurveExt};
use halo2::pasta::pallas;
#[test]
fn sinsemilla_s() {
use super::super::sinsemilla_s::SINSEMILLA_S;
let hasher = pallas::Point::hash_to_curve(S_PERSONALIZATION);
for j in 0..(1u32 << K) {
let computed = {
let point = hasher(&j.to_le_bytes()).to_affine().coordinates().unwrap();
(*point.x(), *point.y())
};
let actual = SINSEMILLA_S[j as usize];
assert_eq!(computed, actual);
}
}
#[test]
fn q_note_commitment_m() {
let domain = CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION);
let point = domain.M.Q;
let coords = point.to_affine().coordinates().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]
fn q_commit_ivk_m() {
let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
let point = domain.M.Q;
let coords = point.to_affine().coordinates().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]
fn q_merkle_crh() {
let domain = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
let point = domain.Q;
let coords = point.to_affine().coordinates().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(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

@ -12,8 +12,8 @@ use subtle::{ConditionallySelectable, CtOption};
use crate::{
constants::{
util::gen_const_array, COMMIT_IVK_PERSONALIZATION, KEY_DIVERSIFICATION_PERSONALIZATION,
L_ORCHARD_BASE,
fixed_bases::COMMIT_IVK_PERSONALIZATION, util::gen_const_array,
KEY_DIVERSIFICATION_PERSONALIZATION, L_ORCHARD_BASE,
},
primitives::{poseidon, sinsemilla},
};

View File

@ -2,11 +2,12 @@
use crate::{
constants::{
util::gen_const_array_with_default, L_ORCHARD_MERKLE, MERKLE_CRH_PERSONALIZATION,
sinsemilla::{i2lebsp_k, L_ORCHARD_MERKLE, MERKLE_CRH_PERSONALIZATION},
util::gen_const_array_with_default,
MERKLE_DEPTH_ORCHARD,
},
note::commitment::ExtractedNoteCommitment,
primitives::sinsemilla::{i2lebsp_k, HashDomain},
primitives::sinsemilla::HashDomain,
};
use incrementalmerkletree::{Altitude, Hashable};
use pasta_curves::pallas;

View File

@ -51,7 +51,7 @@ use rand::RngCore;
use subtle::CtOption;
use crate::{
constants::{
constants::fixed_bases::{
VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES, VALUE_COMMITMENT_V_BYTES,
},
primitives::redpallas::{self, Binding},