//! Implementation of the Pedersen hash function used in Sapling. use crate::jubjub::*; use ff::{Field, PrimeField, PrimeFieldRepr}; use std::ops::AddAssign; #[derive(Copy, Clone)] pub enum Personalization { NoteCommitment, MerkleTree(usize), } impl Personalization { pub fn get_bits(&self) -> Vec { match *self { Personalization::NoteCommitment => vec![true, true, true, true, true, true], Personalization::MerkleTree(num) => { assert!(num < 63); (0..6).map(|i| (num >> i) & 1 == 1).collect() } } } } pub fn pedersen_hash( personalization: Personalization, bits: I, params: &E::Params, ) -> edwards::Point where I: IntoIterator, E: JubjubEngine, { let mut bits = personalization .get_bits() .into_iter() .chain(bits.into_iter()); let mut result = edwards::Point::zero(); let mut generators = params.pedersen_hash_exp_table().iter(); loop { let mut acc = E::Fs::zero(); let mut cur = E::Fs::one(); let mut chunks_remaining = params.pedersen_hash_chunks_per_generator(); let mut encountered_bits = false; // Grab three bits from the input while let Some(a) = bits.next() { encountered_bits = true; let b = bits.next().unwrap_or(false); let c = bits.next().unwrap_or(false); // Start computing this portion of the scalar let mut tmp = cur; if a { tmp.add_assign(&cur); } cur.double(); // 2^1 * cur if b { tmp.add_assign(&cur); } // conditionally negate if c { tmp.negate(); } acc.add_assign(&tmp); chunks_remaining -= 1; if chunks_remaining == 0 { break; } else { cur.double(); // 2^2 * cur cur.double(); // 2^3 * cur cur.double(); // 2^4 * cur } } if !encountered_bits { break; } let mut table: &[Vec>] = &generators.next().expect("we don't have enough generators"); let window = JubjubBls12::pedersen_hash_exp_window_size(); let window_mask = (1 << window) - 1; let mut acc = acc.into_repr(); let mut tmp = edwards::Point::zero(); while !acc.is_zero() { let i = (acc.as_ref()[0] & window_mask) as usize; tmp = tmp.add(&table[0][i], params); acc.shr(window); table = &table[1..]; } result = result.add(&tmp, params); } result } #[cfg(test)] pub mod test { use super::*; use crate::test_vectors::pedersen_hash_vectors; use pairing::bls12_381::Bls12; pub struct TestVector<'a> { pub personalization: Personalization, pub input_bits: Vec, pub hash_x: &'a str, pub hash_y: &'a str, } #[test] fn test_pedersen_hash_points() { let test_vectors = pedersen_hash_vectors::get_vectors(); assert!(test_vectors.len() > 0); for v in test_vectors.iter() { let params = &JubjubBls12::new(); let input_bools: Vec = v.input_bits.iter().map(|&i| i == 1).collect(); // The 6 bits prefix is handled separately assert_eq!(v.personalization.get_bits(), &input_bools[..6]); let (x, y) = pedersen_hash::( v.personalization, input_bools.into_iter().skip(6), params, ) .to_xy(); assert_eq!(x.to_string(), v.hash_x); assert_eq!(y.to_string(), v.hash_y); } } }