fil_sapling_crypto/
pedersen_hash.rs1use fff::{Field, PrimeField, PrimeFieldRepr};
2use lazy_static::lazy_static;
3#[cfg(target_arch = "x86_64")]
4use paired::bls12_381::Bls12;
5
6use crate::jubjub::*;
7
8lazy_static! {
9 pub static ref CPU_SUPPORTS_ADX_INSTRUCTION: bool = {
10 #[cfg(target_arch = "x86_64")]
11 {
12 is_x86_feature_detected!("adx")
13 }
14 #[cfg(not(target_arch = "x86_64"))]
15 {
16 false
17 }
18 };
19}
20
21#[derive(Copy, Clone)]
22pub enum Personalization {
23 NoteCommitment,
24 MerkleTree(usize),
25 None,
26}
27
28impl Personalization {
29 pub fn get_bits(&self) -> Vec<bool> {
30 match *self {
31 Personalization::None => Vec::new(),
32 Personalization::NoteCommitment => vec![true, true, true, true, true, true],
33 Personalization::MerkleTree(num) => {
34 assert!(num < 63);
35
36 (0..6).map(|i| (num >> i) & 1 == 1).collect()
37 }
38 }
39 }
40}
41
42pub fn pedersen_hash<E, I>(
43 personalization: Personalization,
44 bits: I,
45 params: &E::Params,
46) -> edwards::Point<E, PrimeOrder>
47where
48 I: IntoIterator<Item = bool>,
49 E: JubjubEngine,
50{
51 let mut bits = personalization
52 .get_bits()
53 .into_iter()
54 .chain(bits.into_iter());
55
56 let mut result = edwards::Point::zero();
57 let mut generators = params.pedersen_hash_exp_table().iter();
58
59 loop {
60 let mut acc = E::Fs::zero();
61 let mut cur = E::Fs::one();
62 let mut chunks_remaining = params.pedersen_hash_chunks_per_generator();
63 let mut encountered_bits = false;
64
65 while let Some(a) = bits.next() {
67 encountered_bits = true;
68
69 let b = bits.next().unwrap_or(false);
70 let c = bits.next().unwrap_or(false);
71
72 let mut tmp = cur;
74 if a {
75 tmp.add_assign(&cur);
76 }
77 cur.double(); if b {
79 tmp.add_assign(&cur);
80 }
81
82 if c {
84 tmp.negate();
85 }
86
87 acc.add_assign(&tmp);
88
89 chunks_remaining -= 1;
90
91 if chunks_remaining == 0 {
92 break;
93 } else {
94 cur.double(); cur.double(); cur.double(); }
98 }
99
100 if !encountered_bits {
101 break;
102 }
103
104 let mut table: &[Vec<edwards::Point<E, _>>] =
105 &generators.next().expect("we don't have enough generators");
106 let window = params.pedersen_hash_exp_window_size();
107 let window_mask = (1 << window) - 1;
108
109 let mut acc = acc.into_repr();
110
111 let mut tmp = edwards::Point::zero();
112
113 while !acc.is_zero() {
114 let i = (acc.as_ref()[0] & window_mask) as usize;
115
116 tmp = tmp.add(&table[0][i], params);
117
118 acc.shr(window);
119 table = &table[1..];
120 }
121
122 result = result.add(&tmp, params);
123 }
124
125 result
126}
127
128#[cfg(target_arch = "x86_64")]
131pub fn pedersen_hash_bls12_381_with_precomp<I>(
132 personalization: Personalization,
133 bits: I,
134 params: &<Bls12 as JubjubEngine>::Params,
135) -> edwards::Point<Bls12, PrimeOrder>
136where
137 I: IntoIterator<Item = bool>,
138{
139 use std::convert::TryInto;
140 use std::slice;
141
142 if !*CPU_SUPPORTS_ADX_INSTRUCTION {
145 return pedersen_hash::<Bls12, _>(personalization, bits, params);
146 }
147
148 let mut bits = personalization
149 .get_bits()
150 .into_iter()
151 .chain(bits.into_iter());
152
153 let mut result = edwards::Point::zero();
154 let mut generators = params.pedersen_hash_exp_table_precomp().iter();
155
156 loop {
157 let mut acc = <Bls12 as JubjubEngine>::Fs::zero();
158 let mut cur = <Bls12 as JubjubEngine>::Fs::one();
159 let mut chunks_remaining = params.pedersen_hash_chunks_per_generator();
160 let mut encountered_bits = false;
161
162 while let Some(a) = bits.next() {
164 encountered_bits = true;
165
166 let b = bits.next().unwrap_or(false);
167 let c = bits.next().unwrap_or(false);
168
169 let mut tmp = cur;
171 if a {
172 tmp.add_assign(&cur);
173 }
174 cur.double(); if b {
176 tmp.add_assign(&cur);
177 }
178
179 if c {
181 tmp.negate();
182 }
183
184 acc.add_assign(&tmp);
185
186 chunks_remaining -= 1;
187
188 if chunks_remaining == 0 {
189 break;
190 } else {
191 cur.double(); cur.double(); cur.double(); }
195 }
196
197 if !encountered_bits {
198 break;
199 }
200
201 let mut table: &[Vec<edwards::Point<Bls12, PrimeOrder>>] =
202 &generators.next().expect("we don't have enough generators");
203 let window = params.pedersen_hash_exp_window_size();
204 let window_mask = (1 << window) - 1;
205
206 let mut acc = acc.into_repr();
207
208 let mut tmp = edwards::Point::zero();
209 let tmp_bytes_mut =
210 unsafe { slice::from_raw_parts_mut((&mut tmp as *mut _) as *mut u64, 16) };
211 let tmp_bytes = unsafe { slice::from_raw_parts((&tmp as *const _) as *const u64, 16) };
212
213 while !acc.is_zero() {
214 let i = (acc.as_ref()[0] & window_mask) as usize;
215 let p2 = unsafe { slice::from_raw_parts((&table[0][i] as *const _) as *const u64, 16) };
216
217 twisted_edwards_add::ext_twisted_ed_add_256(
218 tmp_bytes.try_into().expect("slice needs len of 16"),
219 p2.try_into().expect("slice needs len of 16"),
220 tmp_bytes_mut.try_into().expect("slice needs len of 16"),
221 );
222
223 acc.shr(window);
224 table = &table[1..];
225 }
226
227 result = result.add(&tmp, params);
228 }
229
230 result
231}
232
233#[cfg(test)]
234mod test {
235 use super::*;
236 use paired::bls12_381::Bls12;
237 use rand::{Rng, SeedableRng};
238 use rand_xorshift::XorShiftRng;
239
240 #[cfg(target_arch = "x86_64")]
241 #[test]
242 fn test_pedersen_hash_vs_precomp() {
243 let params = JubjubBls12::new_with_window_size(16);
244 let rng = &mut XorShiftRng::from_seed([
245 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
246 0xbc, 0xe5,
247 ]);
248 let personalization = Personalization::MerkleTree(31);
249
250 let max_preimage_bits = 945 - 6;
253
254 for _ in 0..500000 {
255 let preimage_len_bits = rng.gen_range(0, max_preimage_bits + 1);
256 let bits: Vec<bool> = (0..preimage_len_bits).map(|_| rng.gen()).collect();
257 let hash_orig =
258 pedersen_hash::<Bls12, _>(personalization, bits.clone(), ¶ms).into_xy();
259 let hash_new =
260 pedersen_hash_bls12_381_with_precomp::<_>(personalization, bits.clone(), ¶ms)
261 .into_xy();
262 assert_eq!(hash_orig, hash_new);
263 }
264 }
265}