use bls12_381::Scalar;
use group::{ff::Field, Curve, Group};
use jubjub::ExtendedPoint;
use lazy_static::lazy_static;
use zcash_primitives::constants::{PEDERSEN_HASH_CHUNKS_PER_GENERATOR, PEDERSEN_HASH_GENERATORS};
pub(crate) const EDWARDS_D: Scalar = Scalar::from_raw([
0x0106_5fd6_d634_3eb1,
0x292d_7f6d_3757_9d26,
0xf5fd_9207_e6bd_7fd4,
0x2a93_18e7_4bfa_2b48,
]);
pub(crate) const MONTGOMERY_A: Scalar = Scalar::from_raw([
0x0000_0000_0000_a002,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
]);
pub(crate) const MONTGOMERY_SCALE: Scalar = Scalar::from_raw([
0x8f45_35f7_cf82_b8d9,
0xce40_6970_3da8_8abd,
0x31de_341e_77d7_64e5,
0x2762_de61_e862_645e,
]);
const FIXED_BASE_CHUNKS_PER_GENERATOR: usize = 84;
pub type FixedGenerator = &'static [Vec<(Scalar, Scalar)>];
pub type FixedGeneratorOwned = Vec<Vec<(Scalar, Scalar)>>;
lazy_static! {
pub static ref PROOF_GENERATION_KEY_GENERATOR: FixedGeneratorOwned =
generate_circuit_generator(zcash_primitives::constants::PROOF_GENERATION_KEY_GENERATOR);
pub static ref NOTE_COMMITMENT_RANDOMNESS_GENERATOR: FixedGeneratorOwned =
generate_circuit_generator(zcash_primitives::constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR);
pub static ref NULLIFIER_POSITION_GENERATOR: FixedGeneratorOwned =
generate_circuit_generator(zcash_primitives::constants::NULLIFIER_POSITION_GENERATOR);
pub static ref VALUE_COMMITMENT_VALUE_GENERATOR: FixedGeneratorOwned =
generate_circuit_generator(zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR);
pub static ref VALUE_COMMITMENT_RANDOMNESS_GENERATOR: FixedGeneratorOwned =
generate_circuit_generator(zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR);
pub static ref SPENDING_KEY_GENERATOR: FixedGeneratorOwned =
generate_circuit_generator(zcash_primitives::constants::SPENDING_KEY_GENERATOR);
pub(crate) static ref PEDERSEN_CIRCUIT_GENERATORS: Vec<Vec<Vec<(Scalar, Scalar)>>> =
generate_pedersen_circuit_generators();
}
pub fn generate_circuit_generator(mut gen: jubjub::SubgroupPoint) -> FixedGeneratorOwned {
let mut windows = vec![];
for _ in 0..FIXED_BASE_CHUNKS_PER_GENERATOR {
let mut coeffs = vec![(Scalar::zero(), Scalar::one())];
let mut g = gen;
for _ in 0..7 {
let g_affine = jubjub::ExtendedPoint::from(g).to_affine();
coeffs.push((g_affine.get_u(), g_affine.get_v()));
g += gen;
}
windows.push(coeffs);
gen = g;
}
windows
}
#[allow(clippy::many_single_char_names)]
pub(crate) fn to_montgomery_coords(g: ExtendedPoint) -> Option<(Scalar, Scalar)> {
let g = g.to_affine();
let (x, y) = (g.get_u(), g.get_v());
if y == Scalar::one() {
None
} else {
if x.is_zero_vartime() {
Some((Scalar::zero(), Scalar::zero()))
} else {
let u = (Scalar::one() + y) * (Scalar::one() - y).invert().unwrap();
let v = u * x.invert().unwrap();
Some((u, v * MONTGOMERY_SCALE))
}
}
}
fn generate_pedersen_circuit_generators() -> Vec<Vec<Vec<(Scalar, Scalar)>>> {
PEDERSEN_HASH_GENERATORS
.iter()
.cloned()
.map(|mut gen| {
let mut windows = vec![];
for _ in 0..PEDERSEN_HASH_CHUNKS_PER_GENERATOR {
let mut coeffs = vec![];
let mut g = gen;
for _ in 0..4 {
coeffs.push(
to_montgomery_coords(g.into())
.expect("we never encounter the point at infinity"),
);
g += gen;
}
windows.push(coeffs);
for _ in 0..4 {
gen = gen.double();
}
}
windows
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn edwards_d() {
assert_eq!(
-Scalar::from(10240) * Scalar::from(10241).invert().unwrap(),
EDWARDS_D
);
}
#[test]
fn montgomery_a() {
assert_eq!(Scalar::from(40962), MONTGOMERY_A);
}
#[test]
fn montgomery_scale() {
assert_eq!(
MONTGOMERY_SCALE.square() * (-Scalar::one() - EDWARDS_D),
Scalar::from(4),
);
}
}