From 14d502a54b89d27f2290e6a0ed07bc2610f54f7b Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Fri, 31 Jul 2020 03:54:11 -0400 Subject: [PATCH] Compute pedersen hash to point in scalar field --- zebra-chain/src/commitments/sapling.rs | 79 ++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/zebra-chain/src/commitments/sapling.rs b/zebra-chain/src/commitments/sapling.rs index 597dd44bf..3119b9726 100644 --- a/zebra-chain/src/commitments/sapling.rs +++ b/zebra-chain/src/commitments/sapling.rs @@ -4,6 +4,8 @@ #[cfg(test)] mod arbitrary; +#[cfg(test)] +mod test_vectors; use std::{fmt, io}; @@ -52,21 +54,48 @@ pub fn pedersen_hash_to_point(domain: [u8; 8], M: &BitVec) -> jubjub:: find_group_hash(domain, &i.to_le_bytes()) } - // ⟨Mᵢ⟩ + /// ⟨Mᵢ⟩ + /// + /// Σ j={0,k-1}: (1 - 2x₂)⋅(1 + x₀ + 2x₁)⋅2^(4⋅j) + // XXX: Are internal functions doc'd? + // + // This is less efficient than it could be so that it can match the math + // closely. fn M_i(segment: &BitSlice) -> jubjub::Fr { - let mut m_i = [0u8; 32]; + let mut m_i = jubjub::Fr::zero(); for (j, chunk) in segment.chunks(3).enumerate() { - let mut data = [0u8; 3]; - let bits = data.bits_mut::(); - bits.copy_from_slice(chunk); + // Pad each chunk with zeros. + let mut store = 0u8; + let bits = store.bits_mut::(); + chunk + .iter() + .enumerate() + .for_each(|(i, bit)| bits.set(i, *bit)); - let enc_m_j = (1 - (2 * bits[2] as u8)) * (1 + (bits[0] as u8) + (2 * bits[1] as u8)); + let mut tmp = jubjub::Fr::one(); - m_i[0] += enc_m_j * (1 << (4 * j)) + if bits[0] { + tmp += &jubjub::Fr::one(); + } + + if bits[1] { + tmp += &jubjub::Fr::one().double(); + } + + if bits[2] { + tmp -= tmp.double(); + } + + // tmp * 2^(4*j) + if j > 0 { + tmp *= (1..(4 * j)).fold(jubjub::Fr::one(), |acc, _| acc.double()); + } + + m_i += tmp; } - jubjub::Fr::from_bytes(&m_i).unwrap() + m_i } let mut result = jubjub::ExtendedPoint::identity(); @@ -98,8 +127,8 @@ pub fn mixing_pedersen_hash(P: jubjub::ExtendedPoint, x: jubjub::Fr) -> jubjub:: P + find_group_hash(J, b"") * x } -/// Construct a 'windowed' Pedersen commitment by reusing a Perderson hash -/// constructon, and adding a randomized point on the Jubjub curve. +/// Construct a 'windowed' Pedersen commitment by reusing a Pederson hash +/// construction, and adding a randomized point on the Jubjub curve. /// /// WindowedPedersenCommit_r (s) := \ /// PedersenHashToPoint(“Zcash_PH”, s) + [r]FindGroupHash^J^(r)(“Zcash_PH”, “r”) @@ -289,3 +318,33 @@ impl ValueCommitment { Self::from(V * v + R * rcv) } } + +#[cfg(test)] +mod tests { + + use super::*; + // use crate::commitments::sapling::test_vectors::TEST_VECTORS; + + #[test] + fn pedersen_hash_to_point_test_vectors() { + const D: [u8; 8] = *b"Zcash_PH"; + + let result = + pedersen_hash_to_point(D, &BitVec::::from_vec(vec![1, 1, 1, 1, 1, 1])); + + let point = jubjub::AffinePoint::from(result); + + println!("{:?}", point); + + //println!("u: "); + + // for test_vector in TEST_VECTORS.iter() { + // let result = pedersen_hash_to_point( + // D, + // &BitVec::::from_vec(test_vector.input_bits.clone()), + // ); + + // assert_eq!(jubjub::AffinePoint::from(result), test_vector.hash_point); + // } + } +}