mirror of https://github.com/zcash/orchard.git
Refactor src/constants and primitives::sinsemilla::constants.
This commit is contained in:
parent
d37db53e0b
commit
1a3cbeb896
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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::{
|
||||
|
|
|
@ -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::{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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},
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
431
src/constants.rs
431
src/constants.rs
|
@ -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() {
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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},
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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},
|
||||
|
|
Loading…
Reference in New Issue