Skip to main content

sapling_crypto_ce/alt_babyjubjub/
mod.rs

1//! Alternative Baby Jubjub is a twisted Edwards curve defined over the BN256 scalar
2//! field, Fr. 
3//! `Fr modulus = 21888242871839275222246405745257275088548364400416034343698204186575808495617`
4//! 
5//! It takes the form `-x^2 + y^2 = 1 + dx^2y^2` with
6//! `d = -(168696/168700)` using the isomorphism from usual Baby Jubjub 
7//! with a requirement that `a' = -1, a = 168696`, that results in 
8//! ```text
9//! scaling = 1911982854305225074381251344103329931637610209014896889891168275855466657090 
10//! a' = 21888242871839275222246405745257275088548364400416034343698204186575808495616 == -1 = a*scale^2 mod P
11//! d' = 12181644023421730124874158521699555681764249180949974110617291017600649128846 == -(168696/168700) = d*scale^2
12//! ```
13//! 
14//! It is birationally equivalent to a Montgomery
15//! curve of the form `y^2 = x^3 + Ax^2 + x` with `A = 168698`. This
16//! value `A` is the smallest integer choice such that:
17//!
18//! * `(A - 2) / 4` is a small integer (`10240`).
19//! * `A^2 - 4` is quadratic nonresidue.
20//! * The group order of the curve and its quadratic twist has a large
21//!   prime factor.
22//!
23//! Jubjub has `s = 2736030358979909402780800718157159386076813972158567259200215660948447373041`
24//! as the prime subgroup order, with cofactor 8. (The twist has
25//! cofactor 4.)
26//!
27//! It is a complete twisted Edwards curve, so the equivalence with
28//! the Montgomery curve forms a group isomorphism, allowing points
29//! to be freely converted between the two forms.
30
31// use ::jubjub::{
32//     Unknown,
33//     PrimeOrder,
34//     FixedGenerators,
35//     ToUniform,
36//     JubjubEngine,
37//     JubjubParams,
38// };
39
40use bellman::pairing::ff::{
41    Field,
42    PrimeField,
43};
44
45use group_hash::{baby_group_hash, generic_group_hash};
46
47use constants;
48
49use bellman::pairing::bn256::{
50    Bn256,
51    Fr
52};
53
54pub use super::jubjub::{
55    Unknown,
56    PrimeOrder,
57    FixedGenerators,
58    ToUniform,
59    JubjubEngine,
60    JubjubParams,
61    edwards,
62    montgomery
63};
64
65// /// This is an implementation of the twisted Edwards Jubjub curve.
66// pub mod edwards;
67
68// /// This is an implementation of the birationally equivalent
69// /// Montgomery curve.
70// pub mod montgomery;
71
72/// This is an implementation of the scalar field for Jubjub.
73pub mod fs;
74
75#[cfg(test)]
76pub mod tests;
77
78use super::group_hash::GroupHasher;
79
80impl JubjubEngine for Bn256 {
81    type Fs = self::fs::Fs;
82    type Params = AltJubjubBn256;
83}
84#[derive(Clone)]
85pub struct AltJubjubBn256 {
86    edwards_d: Fr,
87    montgomery_a: Fr,
88    montgomery_2a: Fr,
89    scale: Fr,
90
91    pedersen_hash_generators: Vec<edwards::Point<Bn256, PrimeOrder>>,
92    pedersen_hash_exp: Vec<Vec<Vec<edwards::Point<Bn256, PrimeOrder>>>>,
93    pedersen_circuit_generators: Vec<Vec<Vec<(Fr, Fr)>>>,
94
95    fixed_base_generators: Vec<edwards::Point<Bn256, PrimeOrder>>,
96    fixed_base_circuit_generators: Vec<Vec<Vec<(Fr, Fr)>>>,
97}
98
99impl JubjubParams<Bn256> for AltJubjubBn256 {
100    fn edwards_d(&self) -> &Fr { &self.edwards_d }
101    fn montgomery_a(&self) -> &Fr { &self.montgomery_a }
102    fn montgomery_2a(&self) -> &Fr { &self.montgomery_2a }
103    fn scale(&self) -> &Fr { &self.scale }
104    fn pedersen_hash_generators(&self) -> &[edwards::Point<Bn256, PrimeOrder>] {
105        &self.pedersen_hash_generators
106    }
107    fn pedersen_hash_exp_table(&self) -> &[Vec<Vec<edwards::Point<Bn256, PrimeOrder>>>] {
108        &self.pedersen_hash_exp
109    }
110    fn pedersen_hash_chunks_per_generator(&self) -> usize {
111        62
112    }
113    fn fixed_base_chunks_per_generator(&self) -> usize {
114        84
115    }
116    fn pedersen_circuit_generators(&self) -> &[Vec<Vec<(Fr, Fr)>>] {
117        &self.pedersen_circuit_generators
118    }
119    fn generator(&self, base: FixedGenerators) -> &edwards::Point<Bn256, PrimeOrder>
120    {
121        &self.fixed_base_generators[base as usize]
122    }
123    fn circuit_generators(&self, base: FixedGenerators) -> &[Vec<(Fr, Fr)>]
124    {
125        &self.fixed_base_circuit_generators[base as usize][..]
126    }
127    fn pedersen_hash_exp_window_size(&self) -> u32 {
128        8
129    }
130}
131
132impl AltJubjubBn256 {
133    pub fn new() -> Self {
134        let montgomery_a = Fr::from_str("168698").unwrap();
135        let mut montgomery_2a = montgomery_a;
136        montgomery_2a.double();
137
138        let mut tmp_params = AltJubjubBn256 {
139            // d = -(168696/168700)
140            edwards_d: Fr::from_str("12181644023421730124874158521699555681764249180949974110617291017600649128846").unwrap(),
141            // A = 168698
142            montgomery_a: montgomery_a,
143            // 2A = 2.A
144            montgomery_2a: montgomery_2a,
145            // scaling factor = sqrt(4 / (a - d))
146            scale: Fr::from_str("6360561867910373094066688120553762416144456282423235903351243436111059670888").unwrap(),
147
148            // We'll initialize these below
149            pedersen_hash_generators: vec![],
150            pedersen_hash_exp: vec![],
151            pedersen_circuit_generators: vec![],
152            fixed_base_generators: vec![],
153            fixed_base_circuit_generators: vec![],
154        };
155
156        fn find_group_hash<E: JubjubEngine>(
157            m: &[u8],
158            personalization: &[u8; 8],
159            params: &E::Params
160        ) -> edwards::Point<E, PrimeOrder>
161        {
162            let mut tag = m.to_vec();
163            let i = tag.len();
164            tag.push(0u8);
165
166            loop {
167                let gh = baby_group_hash(
168                    &tag,
169                    personalization,
170                    params
171                );
172
173                // We don't want to overflow and start reusing generators
174                assert!(tag[i] != u8::max_value());
175                tag[i] += 1;
176
177                if let Some(gh) = gh {
178                    break gh;
179                }
180            }
181        }
182
183        // Create the bases for the Pedersen hashes
184        {
185            let mut pedersen_hash_generators = vec![];
186
187            for m in 0..5 {
188                use byteorder::{WriteBytesExt, LittleEndian};
189
190                let mut segment_number = [0u8; 4];
191                (&mut segment_number[0..4]).write_u32::<LittleEndian>(m).unwrap();
192
193                pedersen_hash_generators.push(
194                    find_group_hash(
195                        &segment_number,
196                        constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
197                        &tmp_params
198                    )
199                );
200            }
201
202            // Check for duplicates, far worse than spec inconsistencies!
203            for (i, p1) in pedersen_hash_generators.iter().enumerate() {
204                if p1 == &edwards::Point::zero() {
205                    panic!("Neutral element!");
206                }
207
208                for p2 in pedersen_hash_generators.iter().skip(i+1) {
209                    if p1 == p2 {
210                        panic!("Duplicate generator!");
211                    }
212                }
213            }
214
215            tmp_params.pedersen_hash_generators = pedersen_hash_generators;
216        }
217
218        // Create the exp table for the Pedersen hash generators
219        {
220            let mut pedersen_hash_exp = vec![];
221
222            for g in &tmp_params.pedersen_hash_generators {
223                let mut g = g.clone();
224
225                let window = tmp_params.pedersen_hash_exp_window_size();
226
227                let mut tables = vec![];
228
229                let mut num_bits = 0;
230                while num_bits <= fs::Fs::NUM_BITS {
231                    let mut table = Vec::with_capacity(1 << window);
232
233                    let mut base = edwards::Point::zero();
234
235                    for _ in 0..(1 << window) {
236                        table.push(base.clone());
237                        base = base.add(&g, &tmp_params);
238                    }
239
240                    tables.push(table);
241                    num_bits += window;
242
243                    for _ in 0..window {
244                        g = g.double(&tmp_params);
245                    }
246                }
247
248                pedersen_hash_exp.push(tables);
249            }
250
251            tmp_params.pedersen_hash_exp = pedersen_hash_exp;
252        }
253
254        // Create the bases for other parts of the protocol
255        {
256            let mut fixed_base_generators = vec![edwards::Point::zero(); FixedGenerators::Max as usize];
257
258            fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] =
259                find_group_hash(&[], constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, &tmp_params);
260
261            fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] =
262                find_group_hash(b"r", constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp_params);
263
264            fixed_base_generators[FixedGenerators::NullifierPosition as usize] =
265                find_group_hash(&[], constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, &tmp_params);
266
267            fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] =
268                find_group_hash(b"v", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params);
269
270            fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] =
271                find_group_hash(b"r", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params);
272
273            fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] =
274                find_group_hash(&[], constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, &tmp_params);
275
276            // Check for duplicates, far worse than spec inconsistencies!
277            for (i, p1) in fixed_base_generators.iter().enumerate() {
278                if p1 == &edwards::Point::zero() {
279                    panic!("Neutral element!");
280                }
281
282                for p2 in fixed_base_generators.iter().skip(i+1) {
283                    if p1 == p2 {
284                        panic!("Duplicate generator!");
285                    }
286                }
287            }
288
289            tmp_params.fixed_base_generators = fixed_base_generators;
290        }
291
292        // Create the 2-bit window table lookups for each 4-bit
293        // "chunk" in each segment of the Pedersen hash
294        {
295            let mut pedersen_circuit_generators = vec![];
296
297            // Process each segment
298            for gen in tmp_params.pedersen_hash_generators.iter().cloned() {
299                let mut gen = montgomery::Point::from_edwards(&gen, &tmp_params);
300                let mut windows = vec![];
301                for _ in 0..tmp_params.pedersen_hash_chunks_per_generator() {
302                    // Create (x, y) coeffs for this chunk
303                    let mut coeffs = vec![];
304                    let mut g = gen.clone();
305
306                    // coeffs = g, g*2, g*3, g*4
307                    for _ in 0..4 {
308                        coeffs.push(g.into_xy().expect("cannot produce O"));
309                        g = g.add(&gen, &tmp_params);
310                    }
311                    windows.push(coeffs);
312
313                    // Our chunks are separated by 2 bits to prevent overlap.
314                    for _ in 0..4 {
315                        gen = gen.double(&tmp_params);
316                    }
317                }
318                pedersen_circuit_generators.push(windows);
319            }
320
321            tmp_params.pedersen_circuit_generators = pedersen_circuit_generators;
322        }
323
324        // Create the 3-bit window table lookups for fixed-base
325        // exp of each base in the protocol.
326        {
327            let mut fixed_base_circuit_generators = vec![];
328
329            for mut gen in tmp_params.fixed_base_generators.iter().cloned() {
330                let mut windows = vec![];
331                for _ in 0..tmp_params.fixed_base_chunks_per_generator() {
332                    let mut coeffs = vec![(Fr::zero(), Fr::one())];
333                    let mut g = gen.clone();
334                    for _ in 0..7 {
335                        coeffs.push(g.into_xy());
336                        g = g.add(&gen, &tmp_params);
337                    }
338                    windows.push(coeffs);
339
340                    // gen = gen * 8
341                    gen = g;
342                }
343                fixed_base_circuit_generators.push(windows);
344            }
345
346            tmp_params.fixed_base_circuit_generators = fixed_base_circuit_generators;
347        }
348
349        tmp_params
350    }
351
352    pub fn new_with_hasher<H: GroupHasher>() -> Self {
353        let montgomery_a = Fr::from_str("168698").unwrap();
354        let mut montgomery_2a = montgomery_a;
355        montgomery_2a.double();
356
357        let mut tmp_params = AltJubjubBn256 {
358            // d = -(168696/168700)
359            edwards_d: Fr::from_str("12181644023421730124874158521699555681764249180949974110617291017600649128846").unwrap(),
360            // A = 168698
361            montgomery_a: montgomery_a,
362            // 2A = 2.A
363            montgomery_2a: montgomery_2a,
364            // scaling factor = sqrt(4 / (a - d))
365            scale: Fr::from_str("6360561867910373094066688120553762416144456282423235903351243436111059670888").unwrap(),
366
367            // We'll initialize these below
368            pedersen_hash_generators: vec![],
369            pedersen_hash_exp: vec![],
370            pedersen_circuit_generators: vec![],
371            fixed_base_generators: vec![],
372            fixed_base_circuit_generators: vec![],
373        };
374
375        fn find_group_hash<E: JubjubEngine, HH: GroupHasher>(
376            m: &[u8],
377            personalization: &[u8; 8],
378            params: &E::Params
379        ) -> edwards::Point<E, PrimeOrder>
380        {
381            let mut tag = m.to_vec();
382            let i = tag.len();
383            tag.push(0u8);
384
385            loop {
386                let gh = generic_group_hash::<_, HH>(
387                    &tag,
388                    personalization,
389                    params
390                );
391
392                // We don't want to overflow and start reusing generators
393                assert!(tag[i] != u8::max_value());
394                tag[i] += 1;
395
396                if let Some(gh) = gh {
397                    break gh;
398                }
399            }
400        }
401
402        // Create the bases for the Pedersen hashes
403        {
404            let mut pedersen_hash_generators = vec![];
405
406            for m in 0..5 {
407                use byteorder::{WriteBytesExt, LittleEndian};
408
409                let mut segment_number = [0u8; 4];
410                (&mut segment_number[0..4]).write_u32::<LittleEndian>(m).unwrap();
411
412                pedersen_hash_generators.push(
413                    find_group_hash::<_, H>(
414                        &segment_number,
415                        constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
416                        &tmp_params
417                    )
418                );
419            }
420
421            // Check for duplicates, far worse than spec inconsistencies!
422            for (i, p1) in pedersen_hash_generators.iter().enumerate() {
423                if p1 == &edwards::Point::zero() {
424                    panic!("Neutral element!");
425                }
426
427                for p2 in pedersen_hash_generators.iter().skip(i+1) {
428                    if p1 == p2 {
429                        panic!("Duplicate generator!");
430                    }
431                }
432            }
433
434            tmp_params.pedersen_hash_generators = pedersen_hash_generators;
435        }
436
437        // Create the exp table for the Pedersen hash generators
438        {
439            let mut pedersen_hash_exp = vec![];
440
441            for g in &tmp_params.pedersen_hash_generators {
442                let mut g = g.clone();
443
444                let window = tmp_params.pedersen_hash_exp_window_size();
445
446                let mut tables = vec![];
447
448                let mut num_bits = 0;
449                while num_bits <= fs::Fs::NUM_BITS {
450                    let mut table = Vec::with_capacity(1 << window);
451
452                    let mut base = edwards::Point::zero();
453
454                    for _ in 0..(1 << window) {
455                        table.push(base.clone());
456                        base = base.add(&g, &tmp_params);
457                    }
458
459                    tables.push(table);
460                    num_bits += window;
461
462                    for _ in 0..window {
463                        g = g.double(&tmp_params);
464                    }
465                }
466
467                pedersen_hash_exp.push(tables);
468            }
469
470            tmp_params.pedersen_hash_exp = pedersen_hash_exp;
471        }
472
473        // Create the bases for other parts of the protocol
474        {
475            let mut fixed_base_generators = vec![edwards::Point::zero(); FixedGenerators::Max as usize];
476
477            fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] =
478                find_group_hash::<_, H>(&[], constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, &tmp_params);
479
480            fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] =
481                find_group_hash::<_, H>(b"r", constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp_params);
482
483            fixed_base_generators[FixedGenerators::NullifierPosition as usize] =
484                find_group_hash::<_, H>(&[], constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, &tmp_params);
485
486            fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] =
487                find_group_hash::<_, H>(b"v", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params);
488
489            fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] =
490                find_group_hash::<_, H>(b"r", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params);
491
492            fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] =
493                find_group_hash::<_, H>(&[], constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, &tmp_params);
494
495            // Check for duplicates, far worse than spec inconsistencies!
496            for (i, p1) in fixed_base_generators.iter().enumerate() {
497                if p1 == &edwards::Point::zero() {
498                    panic!("Neutral element!");
499                }
500
501                for p2 in fixed_base_generators.iter().skip(i+1) {
502                    if p1 == p2 {
503                        panic!("Duplicate generator!");
504                    }
505                }
506            }
507
508            tmp_params.fixed_base_generators = fixed_base_generators;
509        }
510
511        // Create the 2-bit window table lookups for each 4-bit
512        // "chunk" in each segment of the Pedersen hash
513        {
514            let mut pedersen_circuit_generators = vec![];
515
516            // Process each segment
517            for gen in tmp_params.pedersen_hash_generators.iter().cloned() {
518                let mut gen = montgomery::Point::from_edwards(&gen, &tmp_params);
519                let mut windows = vec![];
520                for _ in 0..tmp_params.pedersen_hash_chunks_per_generator() {
521                    // Create (x, y) coeffs for this chunk
522                    let mut coeffs = vec![];
523                    let mut g = gen.clone();
524
525                    // coeffs = g, g*2, g*3, g*4
526                    for _ in 0..4 {
527                        coeffs.push(g.into_xy().expect("cannot produce O"));
528                        g = g.add(&gen, &tmp_params);
529                    }
530                    windows.push(coeffs);
531
532                    // Our chunks are separated by 2 bits to prevent overlap.
533                    for _ in 0..4 {
534                        gen = gen.double(&tmp_params);
535                    }
536                }
537                pedersen_circuit_generators.push(windows);
538            }
539
540            tmp_params.pedersen_circuit_generators = pedersen_circuit_generators;
541        }
542
543        // Create the 3-bit window table lookups for fixed-base
544        // exp of each base in the protocol.
545        {
546            let mut fixed_base_circuit_generators = vec![];
547
548            for mut gen in tmp_params.fixed_base_generators.iter().cloned() {
549                let mut windows = vec![];
550                for _ in 0..tmp_params.fixed_base_chunks_per_generator() {
551                    let mut coeffs = vec![(Fr::zero(), Fr::one())];
552                    let mut g = gen.clone();
553                    for _ in 0..7 {
554                        coeffs.push(g.into_xy());
555                        g = g.add(&gen, &tmp_params);
556                    }
557                    windows.push(coeffs);
558
559                    // gen = gen * 8
560                    gen = g;
561                }
562                fixed_base_circuit_generators.push(windows);
563            }
564
565            tmp_params.fixed_base_circuit_generators = fixed_base_circuit_generators;
566        }
567
568        tmp_params
569    }
570}
571
572#[test]
573fn test_jubjub_altbn256() {
574    let params = AltJubjubBn256::new();
575
576    tests::test_suite::<Bn256>(&params);
577
578    // let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31");
579    // let p = edwards::Point::<Bn256, _>::read(&test_repr[..], &params).unwrap();
580    // let q = edwards::Point::<Bn256, _>::get_for_y(
581    //     Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(),
582    //     false,
583    //     &params
584    // ).unwrap();
585
586    // assert!(p == q);
587
588    // // Same thing, but sign bit set
589    // let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139db1");
590    // let p = edwards::Point::<Bn256, _>::read(&test_repr[..], &params).unwrap();
591    // let q = edwards::Point::<Bn256, _>::get_for_y(
592    //     Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(),
593    //     true,
594    //     &params
595    // ).unwrap();
596
597    // assert!(p == q);
598}
599
600#[test]
601fn test_generic_params() {
602    use super::group_hash::BlakeHasher;
603
604    let params = AltJubjubBn256::new();
605    let generic_params = AltJubjubBn256::new_with_hasher::<BlakeHasher>();
606
607    assert!(params.pedersen_hash_generators == generic_params.pedersen_hash_generators);
608    assert!(params.fixed_base_generators == generic_params.fixed_base_generators);
609
610    assert_eq!(params.pedersen_circuit_generators, generic_params.pedersen_circuit_generators);
611    assert_eq!(params.fixed_base_circuit_generators, generic_params.fixed_base_circuit_generators);
612}
613
614#[test]
615fn pretty_print_params_for_blake() {
616    use super::group_hash::BlakeHasher;
617
618    let generic_params = AltJubjubBn256::new_with_hasher::<BlakeHasher>();
619
620    println!("Creating generators using Blake2s");
621
622    println!("Using personalization `{}`", std::str::from_utf8(constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION).unwrap());
623
624    println!("Pedersen hash generators:");
625
626    for (i, e) in generic_params.pedersen_hash_generators.iter().enumerate() {
627        let (x, y) = e.into_xy();
628        println!("Generator {}", i);
629        println!("X = {}", x);
630        println!("Y = {}", y);
631    }
632}
633
634#[test]
635fn pretty_print_params_for_keccak() {
636    use super::group_hash::Keccak256Hasher;
637
638    let generic_params = AltJubjubBn256::new_with_hasher::<Keccak256Hasher>();
639
640    println!("Creating generators using Keccak256 (Ethereum style)");
641
642    println!("Using personalization `{}`", std::str::from_utf8(constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION).unwrap());
643
644    println!("Pedersen hash generators:");
645
646    for (i, e) in generic_params.pedersen_hash_generators.iter().enumerate() {
647        let (x, y) = e.into_xy();
648        println!("Generator {}", i);
649        println!("X = {}", x);
650        println!("Y = {}", y);
651    }
652}