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

This commit is contained in:
therealyingtong 2021-08-20 12:21:46 +08:00
parent f34b4ba51c
commit 5168c0c2bb
28 changed files with 558 additions and 487 deletions

View File

@ -650,8 +650,8 @@ mod tests {
},
},
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

@ -9,10 +9,15 @@ use halo2::{
};
use crate::circuit::gadget::utilities::UtilitiesInstructions;
use crate::constants;
pub mod chip;
/// 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;
/// The set of circuit instructions required to use the ECC gadgets.
pub trait EccInstructions<C: CurveAffine>:
Chip<C::Base> + UtilitiesInstructions<C::Base> + Clone + Debug + Eq
@ -132,9 +137,9 @@ pub trait EccInstructions<C: CurveAffine>:
/// Returns information about a fixed point.
pub trait FixedPoints<C: CurveAffine>: 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]>;
}
/// An element of the given elliptic curve's base field, that is used as a scalar

View File

@ -1,10 +1,9 @@
use super::{EccInstructions, FixedPoints};
use super::{EccInstructions, FixedPoints, FIXED_BASE_WINDOW_SIZE, H};
use crate::{
circuit::gadget::utilities::{
copy, decompose_running_sum::RunningSumConfig, lookup_range_check::LookupRangeCheckConfig,
CellValue, UtilitiesInstructions, Var,
},
constants,
primitives::sinsemilla,
};
use arrayvec::ArrayVec;
@ -23,6 +22,34 @@ pub(super) mod mul;
pub(super) mod mul_fixed;
pub(super) mod witness_point;
/// 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.
@ -135,7 +162,7 @@ pub struct EccConfig {
pub advices: [Column<Advice>; 10],
/// Coefficients of interpolation polynomials for x-coordinates (used in fixed-base scalar multiplication)
pub lagrange_coeffs: [Column<Fixed>; constants::H],
pub lagrange_coeffs: [Column<Fixed>; H],
/// Fixed z such that y + z = u^2 some square, and -y + z is a non-square. (Used in fixed-base scalar multiplication)
pub fixed_z: Column<Fixed>,
@ -174,7 +201,7 @@ pub struct EccConfig {
/// Lookup range check using 10-bit lookup table
pub lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
/// Running sum decomposition.
pub running_sum_config: RunningSumConfig<pallas::Base, { constants::FIXED_BASE_WINDOW_SIZE }>,
pub running_sum_config: RunningSumConfig<pallas::Base, { FIXED_BASE_WINDOW_SIZE }>,
}
/// A chip implementing EccInstructions
@ -305,7 +332,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> EccChip<FixedPoints> {
// and fixed-base mul using a base field element.
{
// The const generic does not matter when creating gates.
let mul_fixed_config: mul_fixed::Config<FixedPoints, { constants::NUM_WINDOWS }> =
let mul_fixed_config: mul_fixed::Config<FixedPoints, { NUM_WINDOWS }> =
(&config).into();
mul_fixed_config.running_sum_coords_gate(meta);
}
@ -342,7 +369,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> EccChip<FixedPoints> {
#[derive(Clone, Debug)]
pub struct EccScalarFixed {
value: Option<pallas::Scalar>,
windows: ArrayVec<CellValue<pallas::Base>, { constants::NUM_WINDOWS }>,
windows: ArrayVec<CellValue<pallas::Base>, { NUM_WINDOWS }>,
}
/// A signed short scalar used for fixed-base scalar multiplication.
@ -361,7 +388,7 @@ pub struct EccScalarFixed {
pub struct EccScalarFixedShort {
magnitude: CellValue<pallas::Base>,
sign: CellValue<pallas::Base>,
running_sum: ArrayVec<CellValue<pallas::Base>, { constants::NUM_WINDOWS_SHORT + 1 }>,
running_sum: ArrayVec<CellValue<pallas::Base>, { NUM_WINDOWS_SHORT + 1 }>,
}
/// A base field element used for fixed-base scalar multiplication.
@ -376,7 +403,7 @@ pub struct EccScalarFixedShort {
#[derive(Clone, Debug)]
struct EccBaseFieldElemFixed {
base_field_elem: CellValue<pallas::Base>,
running_sum: ArrayVec<CellValue<pallas::Base>, { constants::NUM_WINDOWS + 1 }>,
running_sum: ArrayVec<CellValue<pallas::Base>, { NUM_WINDOWS + 1 }>,
}
impl EccBaseFieldElemFixed {

View File

@ -1,5 +1,5 @@
use super::{add, CellValue, EccConfig, EccPoint, NonIdentityEccPoint, Var};
use crate::{circuit::gadget::utilities::copy, constants::T_Q};
use super::{add, CellValue, EccConfig, EccPoint, NonIdentityEccPoint, Var, T_Q};
use crate::circuit::gadget::utilities::copy;
use std::ops::{Deref, Range};
use bigint::U256;

View File

@ -1,8 +1,7 @@
use super::super::{copy, CellValue, EccConfig, Var};
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::Layouter,

View File

@ -1,8 +1,7 @@
use super::{
add, add_incomplete, CellValue, EccBaseFieldElemFixed, EccConfig, EccScalarFixed,
EccScalarFixedShort, FixedPoints, NonIdentityEccPoint, Var,
EccScalarFixedShort, FixedPoints, NonIdentityEccPoint, Var, FIXED_BASE_WINDOW_SIZE, H,
};
use crate::constants;
use std::marker::PhantomData;
use group::Curve;
@ -24,15 +23,15 @@ pub mod short;
lazy_static! {
static ref TWO_SCALAR: pallas::Scalar = pallas::Scalar::from_u64(2);
// H = 2^3 (3-bit window)
static ref H_SCALAR: pallas::Scalar = pallas::Scalar::from_u64(constants::H as u64);
static ref H_BASE: pallas::Base = pallas::Base::from_u64(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, Debug)]
pub struct Config<F: FixedPoints<pallas::Affine>, const NUM_WINDOWS: usize> {
q_mul_fixed_running_sum: Selector,
// 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:
@ -121,7 +120,7 @@ impl<Fixed: FixedPoints<pallas::Affine>, const NUM_WINDOWS: usize> Config<Fixed,
// 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_u64(constants::H as u64);
let word = z_cur - z_next * pallas::Base::from_u64(H as u64);
self.coords_check(meta, q_mul_fixed_running_sum, word)
});
@ -139,7 +138,7 @@ impl<Fixed: FixedPoints<pallas::Affine>, const NUM_WINDOWS: usize> Config<Fixed,
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()
@ -208,7 +207,7 @@ impl<Fixed: FixedPoints<pallas::Affine>, const NUM_WINDOWS: usize> Config<Fixed,
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!(
@ -359,12 +358,7 @@ impl<Fixed: FixedPoints<pallas::Affine>, const NUM_WINDOWS: usize> Config<Fixed,
// 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}`.
@ -472,7 +466,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,4 +1,7 @@
use super::super::{EccBaseFieldElemFixed, EccConfig, EccPoint, FixedPoints};
use super::super::{
EccBaseFieldElemFixed, EccConfig, EccPoint, FixedPoints, FIXED_BASE_WINDOW_SIZE,
L_ORCHARD_BASE, NUM_WINDOWS, T_P,
};
use super::H_BASE;
use crate::{
@ -6,7 +9,6 @@ use crate::{
bitrange_subset, copy, decompose_running_sum::RunningSumConfig,
lookup_range_check::LookupRangeCheckConfig, range_check, CellValue, Var,
},
constants::{self, T_P},
primitives::sinsemilla,
};
use halo2::{
@ -23,8 +25,8 @@ pub struct Config<Fixed: FixedPoints<pallas::Affine>> {
q_mul_fixed_base_field: Selector,
canon_advices: [Column<Advice>; 3],
lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
running_sum_config: RunningSumConfig<pallas::Base, { constants::FIXED_BASE_WINDOW_SIZE }>,
super_config: super::Config<Fixed, { constants::NUM_WINDOWS }>,
running_sum_config: RunningSumConfig<pallas::Base, { FIXED_BASE_WINDOW_SIZE }>,
super_config: super::Config<Fixed, { NUM_WINDOWS }>,
}
impl<Fixed: FixedPoints<pallas::Affine>> From<&EccConfig> for Config<Fixed> {
@ -171,8 +173,8 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
offset,
scalar,
true,
constants::L_ORCHARD_BASE,
constants::NUM_WINDOWS,
L_ORCHARD_BASE,
NUM_WINDOWS,
)?;
EccBaseFieldElemFixed {
base_field_elem: running_sum[0],
@ -386,10 +388,10 @@ pub mod tests {
use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::circuit::gadget::{
ecc::{chip::EccChip, FixedPoint, FixedPoints, NonIdentityPoint, Point},
ecc::{chip::EccChip, FixedPoint, FixedPoints, NonIdentityPoint, Point, H},
utilities::UtilitiesInstructions,
};
use crate::constants::{self, OrchardFixedBases};
use crate::constants::OrchardFixedBases;
pub fn test_mul_fixed_base_field(
chip: EccChip<OrchardFixedBases>,
@ -456,7 +458,7 @@ pub mod tests {
// (There is another *non-canonical* sequence
// 5333333333333333333333333333333333333333332711161673731021062440252244051273333333333 in octal.)
{
let h = pallas::Base::from_u64(constants::H as u64);
let h = pallas::Base::from_u64(H as u64);
let scalar_fixed = "1333333333333333333333333333333333333333333333333333333333333333333333333333333333334"
.chars()
.fold(pallas::Base::zero(), |acc, c| {

View File

@ -1,9 +1,9 @@
use super::super::{EccConfig, EccPoint, EccScalarFixed, FixedPoints};
use crate::{
circuit::gadget::utilities::{decompose_word, range_check, CellValue, Var},
constants::{self, L_ORCHARD_SCALAR, NUM_WINDOWS},
use super::super::{
EccConfig, EccPoint, EccScalarFixed, FixedPoints, FIXED_BASE_WINDOW_SIZE, H, L_ORCHARD_SCALAR,
NUM_WINDOWS,
};
use crate::circuit::gadget::utilities::{decompose_word, range_check, CellValue, Var};
use arrayvec::ArrayVec;
use halo2::{
circuit::{Layouter, Region},
@ -40,7 +40,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),
)))
});
}
@ -78,11 +78,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
@ -173,9 +169,9 @@ pub mod tests {
use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::circuit::gadget::ecc::{
chip::EccChip, FixedPoint, FixedPoints, NonIdentityPoint, Point,
chip::EccChip, FixedPoint, FixedPoints, NonIdentityPoint, Point, H,
};
use crate::constants::{self, OrchardFixedBases};
use crate::constants::OrchardFixedBases;
pub fn test_mul_fixed(
chip: EccChip<OrchardFixedBases>,
@ -261,7 +257,7 @@ pub mod tests {
// (There is another *non-canonical* sequence
// 5333333333333333333333333333333333333333332711161673731021062440252244051273333333333 in octal.)
{
let h = pallas::Scalar::from_u64(constants::H as u64);
let h = pallas::Scalar::from_u64(H as u64);
let scalar_fixed = "1333333333333333333333333333333333333333333333333333333333333333333333333333333333334"
.chars()
.fold(pallas::Scalar::zero(), |acc, c| {

View File

@ -1,9 +1,11 @@
use std::{array, convert::TryInto};
use super::super::{EccConfig, EccPoint, EccScalarFixedShort, FixedPoints};
use crate::{
circuit::gadget::utilities::{copy, decompose_running_sum::RunningSumConfig, CellValue, Var},
constants::{FIXED_BASE_WINDOW_SIZE, L_VALUE, NUM_WINDOWS_SHORT},
use super::super::{
EccConfig, EccPoint, EccScalarFixedShort, FixedPoints, FIXED_BASE_WINDOW_SIZE, L_VALUE,
NUM_WINDOWS_SHORT,
};
use crate::circuit::gadget::utilities::{
copy, decompose_running_sum::RunningSumConfig, CellValue, Var,
};
use halo2::{

View File

@ -430,8 +430,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

@ -17,6 +17,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.
@ -134,7 +144,7 @@ where
pub mod tests {
use super::{
chip::{MerkleChip, MerkleConfig},
MerklePath,
MerklePath, MERKLE_DEPTH_ORCHARD,
};
use crate::{
@ -142,9 +152,7 @@ pub mod tests {
sinsemilla::chip::SinsemillaChip,
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions, Var},
},
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::{
copy, CellValue, UtilitiesInstructions, Var,
},
},
constants::{L_ORCHARD_BASE, MERKLE_DEPTH_ORCHARD},
primitives::sinsemilla,
};
use std::array;
@ -361,10 +360,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 ff::PrimeFieldBits;
if let (Some(left), Some(right)) = (left.value(), right.value()) {

View File

@ -220,7 +220,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 halo2::{
circuit::{Layouter, SimpleFloorPlanner},
dev::{MockProver, VerifyFailure},
@ -228,6 +227,12 @@ mod tests {
};
use pasta_curves::{arithmetic::FieldExt, pallas};
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<
@ -307,15 +312,11 @@ mod tests {
let alpha = pallas::Base::rand();
// 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(()));
}
@ -327,9 +328,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,
@ -345,9 +346,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,
@ -378,9 +379,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

@ -1444,8 +1444,8 @@ mod tests {
},
},
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::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::OrchardFixedBases;
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,351 +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::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)]
pub enum OrchardFixedBases {
CommitIvkR,
NoteCommitR,
ValueCommitR,
SpendAuthG,
NullifierK,
ValueCommitV,
}
impl FixedPoints<pallas::Affine> for OrchardFixedBases {
fn generator(&self) -> pallas::Affine {
match self {
OrchardFixedBases::CommitIvkR => commit_ivk_r::generator(),
OrchardFixedBases::NoteCommitR => note_commit_r::generator(),
OrchardFixedBases::ValueCommitR => value_commit_r::generator(),
OrchardFixedBases::SpendAuthG => spend_auth_g::generator(),
OrchardFixedBases::NullifierK => nullifier_k::generator(),
OrchardFixedBases::ValueCommitV => value_commit_v::generator(),
}
}
fn u(&self) -> Vec<[[u8; 32]; H]> {
match self {
OrchardFixedBases::CommitIvkR => commit_ivk_r::U.to_vec(),
OrchardFixedBases::NoteCommitR => note_commit_r::U.to_vec(),
OrchardFixedBases::ValueCommitR => value_commit_r::U.to_vec(),
OrchardFixedBases::SpendAuthG => spend_auth_g::U.to_vec(),
OrchardFixedBases::NullifierK => nullifier_k::U.to_vec(),
OrchardFixedBases::ValueCommitV => value_commit_v::U_SHORT.to_vec(),
}
}
fn z(&self) -> Vec<u64> {
match self {
OrchardFixedBases::CommitIvkR => commit_ivk_r::Z.to_vec(),
OrchardFixedBases::NoteCommitR => note_commit_r::Z.to_vec(),
OrchardFixedBases::ValueCommitR => value_commit_r::Z.to_vec(),
OrchardFixedBases::SpendAuthG => spend_auth_g::Z.to_vec(),
OrchardFixedBases::NullifierK => nullifier_k::Z.to_vec(),
OrchardFixedBases::ValueCommitV => value_commit_v::Z_SHORT.to_vec(),
}
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
match self {
OrchardFixedBases::ValueCommitV => {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS_SHORT)
}
_ => compute_lagrange_coeffs(self.generator(), NUM_WINDOWS),
}
}
}
#[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) -> OrchardFixedBases {
match self {
Self::NoteCommit => OrchardFixedBases::NoteCommitR,
Self::CommitIvk => OrchardFixedBases::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_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 = 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_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()));
}
}
}
#[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,290 @@
//! Orchard fixed bases.
use super::{L_ORCHARD_SCALAR, L_VALUE};
use crate::circuit::gadget::ecc::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)]
pub enum OrchardFixedBases {
CommitIvkR,
NoteCommitR,
ValueCommitR,
SpendAuthG,
NullifierK,
ValueCommitV,
}
impl FixedPoints<pallas::Affine> for OrchardFixedBases {
fn generator(&self) -> pallas::Affine {
match self {
OrchardFixedBases::CommitIvkR => commit_ivk_r::generator(),
OrchardFixedBases::NoteCommitR => note_commit_r::generator(),
OrchardFixedBases::ValueCommitR => value_commit_r::generator(),
OrchardFixedBases::SpendAuthG => spend_auth_g::generator(),
OrchardFixedBases::NullifierK => nullifier_k::generator(),
OrchardFixedBases::ValueCommitV => value_commit_v::generator(),
}
}
fn u(&self) -> Vec<[[u8; 32]; H]> {
match self {
OrchardFixedBases::CommitIvkR => commit_ivk_r::U.to_vec(),
OrchardFixedBases::NoteCommitR => note_commit_r::U.to_vec(),
OrchardFixedBases::ValueCommitR => value_commit_r::U.to_vec(),
OrchardFixedBases::SpendAuthG => spend_auth_g::U.to_vec(),
OrchardFixedBases::NullifierK => nullifier_k::U.to_vec(),
OrchardFixedBases::ValueCommitV => value_commit_v::U_SHORT.to_vec(),
}
}
fn z(&self) -> Vec<u64> {
match self {
OrchardFixedBases::CommitIvkR => commit_ivk_r::Z.to_vec(),
OrchardFixedBases::NoteCommitR => note_commit_r::Z.to_vec(),
OrchardFixedBases::ValueCommitR => value_commit_r::Z.to_vec(),
OrchardFixedBases::SpendAuthG => spend_auth_g::Z.to_vec(),
OrchardFixedBases::NullifierK => nullifier_k::Z.to_vec(),
OrchardFixedBases::ValueCommitV => value_commit_v::Z_SHORT.to_vec(),
}
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
match self {
OrchardFixedBases::ValueCommitV => {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS_SHORT)
}
_ => compute_lagrange_coeffs(self.generator(), NUM_WINDOWS),
}
}
}
#[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()));
}
}
}

View File

@ -1,4 +1,11 @@
//! Sinsemilla generators
use super::OrchardFixedBases;
use crate::circuit::gadget::sinsemilla::{CommitDomains, HashDomains};
use pasta_curves::{
arithmetic::{CurveAffine, FieldExt},
pallas,
};
/// Number of bits of each message piece in $\mathsf{SinsemillaHashToPoint}$
pub const K: usize = 10;
@ -13,11 +20,22 @@ pub const INV_TWO_POW_K: [u8; 32] = [
/// 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]) = (
[
@ -54,41 +72,84 @@ pub const Q_MERKLE_CRH: ([u8; 32], [u8; 32]) = (
],
);
// Sinsemilla S generators
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OrchardHashDomains {
NoteCommit,
CommitIvk,
MerkleCrh,
}
/// SWU hash-to-curve personalization for Sinsemilla $S$ generators.
pub const S_PERSONALIZATION: &str = "z.cash:SinsemillaS";
#[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) -> OrchardFixedBases {
match self {
Self::NoteCommit => OrchardFixedBases::NoteCommitR,
Self::CommitIvk => OrchardFixedBases::CommitIvkR,
}
}
fn hash_domain(&self) -> OrchardHashDomains {
match self {
Self::NoteCommit => OrchardHashDomains::NoteCommit,
Self::CommitIvk => OrchardHashDomains::CommitIvk,
}
}
}
#[cfg(test)]
mod tests {
use super::super::{CommitDomain, HashDomain};
use super::*;
use crate::constants::{
COMMIT_IVK_PERSONALIZATION, MERKLE_CRH_PERSONALIZATION, NOTE_COMMITMENT_PERSONALIZATION,
fixed_bases::{COMMIT_IVK_PERSONALIZATION, NOTE_COMMITMENT_PERSONALIZATION},
sinsemilla::MERKLE_CRH_PERSONALIZATION,
};
use crate::primitives::sinsemilla::{CommitDomain, HashDomain};
use ff::PrimeField;
use group::Curve;
use halo2::arithmetic::{CurveAffine, CurveExt, FieldExt};
use halo2::pasta::pallas;
use pasta_curves::{
arithmetic::{CurveAffine, FieldExt},
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);
}
// 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 q_note_commitment_m() {
let domain = CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION);
let point = domain.M.Q;
let point = domain.Q();
let coords = point.to_affine().coordinates().unwrap();
assert_eq!(
@ -104,7 +165,7 @@ mod tests {
#[test]
fn q_commit_ivk_m() {
let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
let point = domain.M.Q;
let point = domain.Q();
let coords = point.to_affine().coordinates().unwrap();
assert_eq!(
@ -120,7 +181,7 @@ mod tests {
#[test]
fn q_merkle_crh() {
let domain = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
let point = domain.Q;
let point = domain.Q();
let coords = point.to_affine().coordinates().unwrap();
assert_eq!(

View File

@ -6,7 +6,7 @@ use pasta_curves::{arithmetic::FieldExt, 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

@ -9,11 +9,31 @@ 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);
@ -196,11 +216,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 pasta_curves::{arithmetic::CurveExt, pallas};
use rand::{self, rngs::OsRng, Rng};
#[test]
@ -279,4 +307,22 @@ mod tests {
);
}
}
#[test]
fn sinsemilla_s() {
use super::sinsemilla_s::SINSEMILLA_S;
use group::Curve;
use pasta_curves::arithmetic::CurveAffine;
let hasher = pallas::Point::hash_to_curve(super::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);
}
}
}

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,7 +2,8 @@
use crate::{
constants::{
util::gen_const_array_with_default, L_ORCHARD_MERKLE, MERKLE_CRH_PERSONALIZATION,
sinsemilla::{L_ORCHARD_MERKLE, MERKLE_CRH_PERSONALIZATION},
util::gen_const_array_with_default,
MERKLE_DEPTH_ORCHARD,
},
note::commitment::ExtractedNoteCommitment,

View File

@ -30,7 +30,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},