diff --git a/benches/pedersen_hash.rs b/benches/pedersen_hash.rs new file mode 100644 index 000000000..c5968dec4 --- /dev/null +++ b/benches/pedersen_hash.rs @@ -0,0 +1,23 @@ +#![feature(test)] + +extern crate rand; +extern crate test; +extern crate pairing; +extern crate sapling_crypto; + +use rand::{Rand, thread_rng}; +use pairing::bls12_381::Bls12; +use sapling_crypto::jubjub::JubjubBls12; +use sapling_crypto::pedersen_hash::{pedersen_hash, Personalization}; + +#[bench] +fn bench_pedersen_hash(b: &mut test::Bencher) { + let params = JubjubBls12::new(); + let rng = &mut thread_rng(); + let bits = (0..510).map(|_| bool::rand(rng)).collect::>(); + let personalization = Personalization::MerkleTree(31); + + b.iter(|| { + pedersen_hash::(personalization, bits.clone(), ¶ms) + }); +} diff --git a/src/jubjub/edwards.rs b/src/jubjub/edwards.rs index 2137d99eb..73f0dfc98 100644 --- a/src/jubjub/edwards.rs +++ b/src/jubjub/edwards.rs @@ -355,12 +355,72 @@ impl Point { p } - pub fn double(&self, params: &E::Params) -> Self { - // Point addition is unified and complete. - // There are dedicated formulae, but we do - // not implement these now. - - self.add(self, params) + pub fn double(&self, _: &E::Params) -> Self { + // See "Twisted Edwards Curves Revisited" + // Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson + // Section 3.3 + // http://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd + + // A = X1^2 + let mut a = self.x; + a.square(); + + // B = Y1^2 + let mut b = self.y; + b.square(); + + // C = 2*Z1^2 + let mut c = self.z; + c.square(); + c.double(); + + // D = a*A + // = -A + let mut d = a; + d.negate(); + + // E = (X1+Y1)^2 - A - B + let mut e = self.x; + e.add_assign(&self.y); + e.square(); + e.add_assign(&d); // -A = D + e.sub_assign(&b); + + // G = D+B + let mut g = d; + g.add_assign(&b); + + // F = G-C + let mut f = g; + f.sub_assign(&c); + + // H = D-B + let mut h = d; + h.sub_assign(&b); + + // X3 = E*F + let mut x3 = e; + x3.mul_assign(&f); + + // Y3 = G*H + let mut y3 = g; + y3.mul_assign(&h); + + // T3 = E*H + let mut t3 = e; + t3.mul_assign(&h); + + // Z3 = F*G + let mut z3 = f; + z3.mul_assign(&g); + + Point { + x: x3, + y: y3, + t: t3, + z: z3, + _marker: PhantomData + } } pub fn add(&self, other: &Self, params: &E::Params) -> Self diff --git a/src/jubjub/mod.rs b/src/jubjub/mod.rs index 80a79866c..51a000a29 100644 --- a/src/jubjub/mod.rs +++ b/src/jubjub/mod.rs @@ -112,6 +112,8 @@ pub trait JubjubParams: Sized { fn scale(&self) -> &E::Fr; /// Returns the generators (for each segment) used in all Pedersen commitments. fn pedersen_hash_generators(&self) -> &[edwards::Point]; + /// Returns the exp table for Pedersen hashes. + fn pedersen_hash_exp_table(&self) -> &[Vec>>]; /// Returns the maximum number of chunks per segment of the Pedersen hash. fn pedersen_hash_chunks_per_generator(&self) -> usize; /// Returns the pre-computed window tables [-4, 3, 2, 1, 1, 2, 3, 4] of different @@ -126,6 +128,9 @@ pub trait JubjubParams: Sized { /// Returns a window table [0, 1, ..., 8] for different magnitudes of some /// fixed generator. fn circuit_generators(&self, FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>]; + /// Returns the window size for exponentiation of Pedersen hash generators + /// outside the circuit + fn pedersen_hash_exp_window_size() -> u32; } impl JubjubEngine for Bls12 { @@ -140,6 +145,7 @@ pub struct JubjubBls12 { scale: Fr, pedersen_hash_generators: Vec>, + pedersen_hash_exp: Vec>>>, pedersen_circuit_generators: Vec>>, fixed_base_generators: Vec>, @@ -154,6 +160,9 @@ impl JubjubParams for JubjubBls12 { fn pedersen_hash_generators(&self) -> &[edwards::Point] { &self.pedersen_hash_generators } + fn pedersen_hash_exp_table(&self) -> &[Vec>>] { + &self.pedersen_hash_exp + } fn pedersen_hash_chunks_per_generator(&self) -> usize { 63 } @@ -171,6 +180,9 @@ impl JubjubParams for JubjubBls12 { { &self.fixed_base_circuit_generators[base as usize][..] } + fn pedersen_hash_exp_window_size() -> u32 { + 8 + } } impl JubjubBls12 { @@ -191,6 +203,7 @@ impl JubjubBls12 { // We'll initialize these below pedersen_hash_generators: vec![], + pedersen_hash_exp: vec![], pedersen_circuit_generators: vec![], fixed_base_generators: vec![], fixed_base_circuit_generators: vec![], @@ -258,6 +271,42 @@ impl JubjubBls12 { tmp_params.pedersen_hash_generators = pedersen_hash_generators; } + // Create the exp table for the Pedersen hash generators + { + let mut pedersen_hash_exp = vec![]; + + for g in &tmp_params.pedersen_hash_generators { + let mut g = g.clone(); + + let window = JubjubBls12::pedersen_hash_exp_window_size(); + + let mut tables = vec![]; + + let mut num_bits = 0; + while num_bits <= fs::Fs::NUM_BITS { + let mut table = Vec::with_capacity(1 << window); + + let mut base = edwards::Point::zero(); + + for _ in 0..(1 << window) { + table.push(base.clone()); + base = base.add(&g, &tmp_params); + } + + tables.push(table); + num_bits += window; + + for _ in 0..window { + g = g.double(&tmp_params); + } + } + + pedersen_hash_exp.push(tables); + } + + tmp_params.pedersen_hash_exp = pedersen_hash_exp; + } + // Create the bases for other parts of the protocol { let mut fixed_base_generators = vec![edwards::Point::zero(); FixedGenerators::Max as usize]; diff --git a/src/pedersen_hash.rs b/src/pedersen_hash.rs index 5c3bb9028..0590a5c88 100644 --- a/src/pedersen_hash.rs +++ b/src/pedersen_hash.rs @@ -1,6 +1,7 @@ use jubjub::*; use pairing::*; +#[derive(Copy, Clone)] pub enum Personalization { NoteCommitment, MerkleTree(usize) @@ -31,7 +32,7 @@ pub fn pedersen_hash( let mut bits = personalization.get_bits().into_iter().chain(bits.into_iter()); let mut result = edwards::Point::zero(); - let mut generators = params.pedersen_hash_generators().iter(); + let mut generators = params.pedersen_hash_exp_table().iter(); loop { let mut acc = E::Fs::zero(); @@ -78,8 +79,23 @@ pub fn pedersen_hash( break; } - let mut tmp = generators.next().expect("we don't have enough generators").clone(); - tmp = tmp.mul(acc, params); + 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); }