1use alloc::vec::Vec;
4use ff::PrimeField;
5use group::Group;
6use jubjub::SubgroupPoint;
7use lazy_static::lazy_static;
8
9pub const GH_FIRST_BLOCK: &[u8; 64] =
14 b"096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0";
15
16pub const CRH_IVK_PERSONALIZATION: &[u8; 8] = b"Zcashivk";
19
20pub const PRF_NF_PERSONALIZATION: &[u8; 8] = b"Zcash_nf";
22
23pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &[u8; 8] = b"Zcash_PH";
26
27pub const KEY_DIVERSIFICATION_PERSONALIZATION: &[u8; 8] = b"Zcash_gd";
29
30pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_G_";
32
33pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_H_";
35
36pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_cv";
38
39pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_J_";
41
42pub(crate) const GROTH_PROOF_SIZE: usize = 48 + 96 + 48;
44
45pub const PROOF_GENERATION_KEY_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
48 bls12_381::Scalar::from_raw([
49 0x3af2_dbef_b96e_2571,
50 0xadf2_d038_f2fb_b820,
51 0x7043_03f1_e890_6081,
52 0x1457_a502_31cd_e2df,
53 ]),
54 bls12_381::Scalar::from_raw([
55 0x467a_f9f7_e05d_e8e7,
56 0x50df_51ea_f5a1_49d2,
57 0xdec9_0184_0f49_48cc,
58 0x54b6_d107_18df_2a7a,
59 ]),
60);
61
62pub const NOTE_COMMITMENT_RANDOMNESS_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
64 bls12_381::Scalar::from_raw([
65 0xa514_3b34_a8e3_6462,
66 0xf091_9d06_ffb1_ecda,
67 0xa140_9aa1_f33b_ec2c,
68 0x26eb_9f8a_9ec7_2a8c,
69 ]),
70 bls12_381::Scalar::from_raw([
71 0xd4fc_6365_796c_77ac,
72 0x96b7_8bea_fa9c_c44c,
73 0x949d_7747_6e26_2c95,
74 0x114b_7501_ad10_4c57,
75 ]),
76);
77
78pub const NULLIFIER_POSITION_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
82 bls12_381::Scalar::from_raw([
83 0x2ce3_3921_888d_30db,
84 0xe81c_ee09_a561_229e,
85 0xdb56_b6db_8d80_75ed,
86 0x2400_c2e2_e336_2644,
87 ]),
88 bls12_381::Scalar::from_raw([
89 0xa3f7_fa36_c72b_0065,
90 0xe155_b8e8_ffff_2e42,
91 0xfc9e_8a15_a096_ba8f,
92 0x6136_9d54_40bf_84a5,
93 ]),
94);
95
96pub const VALUE_COMMITMENT_VALUE_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
99 bls12_381::Scalar::from_raw([
100 0x3618_3b2c_b4d7_ef51,
101 0x9472_c89a_c043_042d,
102 0xd861_8ed1_d15f_ef4e,
103 0x273f_910d_9ecc_1615,
104 ]),
105 bls12_381::Scalar::from_raw([
106 0xa77a_81f5_0667_c8d7,
107 0xbc33_32d0_fa1c_cd18,
108 0xd322_94fd_8977_4ad6,
109 0x466a_7e3a_82f6_7ab1,
110 ]),
111);
112
113pub const VALUE_COMMITMENT_RANDOMNESS_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
115 bls12_381::Scalar::from_raw([
116 0x3bce_3b77_9366_4337,
117 0xd1d8_da41_af03_744e,
118 0x7ff6_826a_d580_04b4,
119 0x6800_f4fa_0f00_1cfc,
120 ]),
121 bls12_381::Scalar::from_raw([
122 0x3cae_fab9_380b_6a8b,
123 0xad46_f1b0_473b_803b,
124 0xe6fb_2a6e_1e22_ab50,
125 0x6d81_d3a9_cb45_dedb,
126 ]),
127);
128
129pub const SPENDING_KEY_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
131 bls12_381::Scalar::from_raw([
132 0x47bf_4692_0a95_a753,
133 0xd5b9_a7d3_ef8e_2827,
134 0xd418_a7ff_2675_3b6a,
135 0x0926_d4f3_2059_c712,
136 ]),
137 bls12_381::Scalar::from_raw([
138 0x3056_32ad_aaf2_b530,
139 0x6d65_674d_cedb_ddbc,
140 0x53bb_37d0_c21c_fd05,
141 0x57a1_019e_6de9_b675,
142 ]),
143);
144
145pub const PEDERSEN_HASH_GENERATORS: &[SubgroupPoint] = &[
147 SubgroupPoint::from_raw_unchecked(
148 bls12_381::Scalar::from_raw([
149 0x194e_4292_6f66_1b51,
150 0x2f0c_718f_6f0f_badd,
151 0xb5ea_25de_7ec0_e378,
152 0x73c0_16a4_2ded_9578,
153 ]),
154 bls12_381::Scalar::from_raw([
155 0x77bf_abd4_3224_3cca,
156 0xf947_2e8b_c04e_4632,
157 0x79c9_166b_837e_dc5e,
158 0x289e_87a2_d352_1b57,
159 ]),
160 ),
161 SubgroupPoint::from_raw_unchecked(
162 bls12_381::Scalar::from_raw([
163 0xb981_9dc8_2d90_607e,
164 0xa361_ee3f_d48f_df77,
165 0x52a3_5a8c_1908_dd87,
166 0x15a3_6d1f_0f39_0d88,
167 ]),
168 bls12_381::Scalar::from_raw([
169 0x7b0d_c53c_4ebf_1891,
170 0x1f3a_beeb_98fa_d3e8,
171 0xf789_1142_c001_d925,
172 0x015d_8c7f_5b43_fe33,
173 ]),
174 ),
175 SubgroupPoint::from_raw_unchecked(
176 bls12_381::Scalar::from_raw([
177 0x76d6_f7c2_b67f_c475,
178 0xbae8_e5c4_6641_ae5c,
179 0xeb69_ae39_f5c8_4210,
180 0x6643_21a5_8246_e2f6,
181 ]),
182 bls12_381::Scalar::from_raw([
183 0x80ed_502c_9793_d457,
184 0x8bb2_2a7f_1784_b498,
185 0xe000_a46c_8e8c_e853,
186 0x362e_1500_d24e_ee9e,
187 ]),
188 ),
189 SubgroupPoint::from_raw_unchecked(
190 bls12_381::Scalar::from_raw([
191 0x4c76_7804_c1c4_a2cc,
192 0x7d02_d50e_654b_87f2,
193 0xedc5_f4a9_cff2_9fd5,
194 0x323a_6548_ce9d_9876,
195 ]),
196 bls12_381::Scalar::from_raw([
197 0x8471_4bec_a335_70e9,
198 0x5103_afa1_a11f_6a85,
199 0x9107_0acb_d8d9_47b7,
200 0x2f7e_e40c_4b56_cad8,
201 ]),
202 ),
203 SubgroupPoint::from_raw_unchecked(
204 bls12_381::Scalar::from_raw([
205 0x4680_9430_657f_82d1,
206 0xefd5_9313_05f2_f0bf,
207 0x89b6_4b4e_0336_2796,
208 0x3bd2_6660_00b5_4796,
209 ]),
210 bls12_381::Scalar::from_raw([
211 0x9996_8299_c365_8aef,
212 0xb3b9_d809_5859_d14c,
213 0x3978_3238_1406_c9e5,
214 0x494b_c521_03ab_9d0a,
215 ]),
216 ),
217 SubgroupPoint::from_raw_unchecked(
218 bls12_381::Scalar::from_raw([
219 0xcb3c_0232_58d3_2079,
220 0x1d9e_5ca2_1135_ff6f,
221 0xda04_9746_d76d_3ee5,
222 0x6344_7b2b_a31b_b28a,
223 ]),
224 bls12_381::Scalar::from_raw([
225 0x4360_8211_9f8d_629a,
226 0xa802_00d2_c66b_13a7,
227 0x64cd_b107_0a13_6a28,
228 0x64ec_4689_e8bf_b6e5,
229 ]),
230 ),
231];
232
233pub const PEDERSEN_HASH_CHUNKS_PER_GENERATOR: usize = 63;
235
236pub const PEDERSEN_HASH_EXP_WINDOW_SIZE: u32 = 8;
238
239lazy_static! {
240 pub static ref PEDERSEN_HASH_EXP_TABLE: Vec<Vec<Vec<SubgroupPoint>>> =
242 generate_pedersen_hash_exp_table();
243}
244
245fn generate_pedersen_hash_exp_table() -> Vec<Vec<Vec<SubgroupPoint>>> {
247 let window = PEDERSEN_HASH_EXP_WINDOW_SIZE;
248
249 PEDERSEN_HASH_GENERATORS
250 .iter()
251 .cloned()
252 .map(|mut g| {
253 let mut tables = vec![];
254
255 let mut num_bits = 0;
256 while num_bits <= jubjub::Fr::NUM_BITS {
257 let mut table = Vec::with_capacity(1 << window);
258 let mut base = SubgroupPoint::identity();
259
260 for _ in 0..(1 << window) {
261 table.push(base);
262 base += g;
263 }
264
265 tables.push(table);
266 num_bits += window;
267
268 for _ in 0..window {
269 g = g.double();
270 }
271 }
272
273 tables
274 })
275 .collect()
276}
277
278#[cfg(test)]
279mod tests {
280 use jubjub::SubgroupPoint;
281
282 use super::*;
283 use crate::group_hash::group_hash;
284
285 fn find_group_hash(m: &[u8], personalization: &[u8; 8]) -> SubgroupPoint {
286 let mut tag = m.to_vec();
287 let i = tag.len();
288 tag.push(0u8);
289
290 loop {
291 let gh = group_hash(&tag, personalization);
292
293 assert!(tag[i] != u8::MAX);
295 tag[i] += 1;
296
297 if let Some(gh) = gh {
298 break gh;
299 }
300 }
301 }
302
303 #[test]
304 fn proof_generation_key_base_generator() {
305 assert_eq!(
306 find_group_hash(&[], PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION),
307 PROOF_GENERATION_KEY_GENERATOR,
308 );
309 }
310
311 #[test]
312 fn note_commitment_randomness_generator() {
313 assert_eq!(
314 find_group_hash(b"r", PEDERSEN_HASH_GENERATORS_PERSONALIZATION),
315 NOTE_COMMITMENT_RANDOMNESS_GENERATOR,
316 );
317 }
318
319 #[test]
320 fn nullifier_position_generator() {
321 assert_eq!(
322 find_group_hash(&[], NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION),
323 NULLIFIER_POSITION_GENERATOR,
324 );
325 }
326
327 #[test]
328 fn value_commitment_value_generator() {
329 assert_eq!(
330 find_group_hash(b"v", VALUE_COMMITMENT_GENERATOR_PERSONALIZATION),
331 VALUE_COMMITMENT_VALUE_GENERATOR,
332 );
333 }
334
335 #[test]
336 fn value_commitment_randomness_generator() {
337 assert_eq!(
338 find_group_hash(b"r", VALUE_COMMITMENT_GENERATOR_PERSONALIZATION),
339 VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
340 );
341 }
342
343 #[test]
344 fn spending_key_generator() {
345 assert_eq!(
346 find_group_hash(&[], SPENDING_KEY_GENERATOR_PERSONALIZATION),
347 SPENDING_KEY_GENERATOR,
348 );
349 }
350
351 #[test]
352 fn pedersen_hash_generators() {
353 for (m, actual) in PEDERSEN_HASH_GENERATORS.iter().enumerate() {
354 assert_eq!(
355 &find_group_hash(
356 &(m as u32).to_le_bytes(),
357 PEDERSEN_HASH_GENERATORS_PERSONALIZATION
358 ),
359 actual
360 );
361 }
362 }
363
364 #[test]
365 fn no_duplicate_fixed_base_generators() {
366 let fixed_base_generators = [
367 PROOF_GENERATION_KEY_GENERATOR,
368 NOTE_COMMITMENT_RANDOMNESS_GENERATOR,
369 NULLIFIER_POSITION_GENERATOR,
370 VALUE_COMMITMENT_VALUE_GENERATOR,
371 VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
372 SPENDING_KEY_GENERATOR,
373 ];
374
375 for (i, p1) in fixed_base_generators.iter().enumerate() {
377 if p1.is_identity().into() {
378 panic!("Neutral element!");
379 }
380
381 for p2 in fixed_base_generators.iter().skip(i + 1) {
382 if p1 == p2 {
383 panic!("Duplicate generator!");
384 }
385 }
386 }
387 }
388
389 fn check_consistency_of_pedersen_hash_generators(
392 pedersen_hash_generators: &[jubjub::SubgroupPoint],
393 ) {
394 for (i, p1) in pedersen_hash_generators.iter().enumerate() {
395 if p1.is_identity().into() {
396 panic!("Neutral element!");
397 }
398 for p2 in pedersen_hash_generators.iter().skip(i + 1) {
399 if p1 == p2 {
400 panic!("Duplicate generator!");
401 }
402 if *p1 == -p2 {
403 panic!("Inverse generator!");
404 }
405 }
406
407 for (j, p2) in pedersen_hash_generators.iter().enumerate() {
409 if j == i {
410 continue;
411 }
412 for (k, p3) in pedersen_hash_generators.iter().enumerate() {
413 if k == j || k == i {
414 continue;
415 }
416 let sum = p2 + p3;
417 if sum == *p1 {
418 panic!("Linear relation between generators!");
419 }
420 }
421 }
422 }
423 }
424
425 #[test]
426 fn pedersen_hash_generators_consistency() {
427 check_consistency_of_pedersen_hash_generators(PEDERSEN_HASH_GENERATORS);
428 }
429
430 #[test]
431 #[should_panic(expected = "Linear relation between generators!")]
432 fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation() {
433 let mut pedersen_hash_generators = PEDERSEN_HASH_GENERATORS.to_vec();
434
435 pedersen_hash_generators.push(PEDERSEN_HASH_GENERATORS[0] + PEDERSEN_HASH_GENERATORS[1]);
437
438 check_consistency_of_pedersen_hash_generators(&pedersen_hash_generators);
439 }
440}