1use alloc::vec::Vec;
4use core::fmt;
5use corez::io;
6
7use group::{ff::PrimeField, Curve};
8
9use bellman::{Circuit, ConstraintSystem, SynthesisError};
10use bls12_381::Bls12;
11
12use bellman::gadgets::blake2s;
13use bellman::gadgets::boolean;
14use bellman::gadgets::multipack;
15use bellman::gadgets::num;
16use bellman::gadgets::Assignment;
17
18use self::constants::{
19 NOTE_COMMITMENT_RANDOMNESS_GENERATOR, NULLIFIER_POSITION_GENERATOR,
20 PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
21 VALUE_COMMITMENT_VALUE_GENERATOR,
22};
23use crate::{value::NoteValue, PaymentAddress, ProofGenerationKey};
24
25#[cfg(test)]
26use group::ff::PrimeFieldBits;
27
28mod constants;
29mod ecc;
30mod pedersen_hash;
31
32#[derive(Clone)]
34pub struct ValueCommitmentOpening {
35 pub value: NoteValue,
36 pub randomness: jubjub::Scalar,
37}
38
39#[cfg(test)]
40impl ValueCommitmentOpening {
41 fn commitment(&self) -> jubjub::ExtendedPoint {
42 let cv = (super::constants::VALUE_COMMITMENT_VALUE_GENERATOR
43 * jubjub::Fr::from(self.value.inner()))
44 + (super::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR * self.randomness);
45 cv.into()
46 }
47}
48
49#[derive(Clone)]
51pub struct Spend {
52 pub value_commitment_opening: Option<ValueCommitmentOpening>,
54
55 pub proof_generation_key: Option<ProofGenerationKey>,
58
59 pub payment_address: Option<PaymentAddress>,
61
62 pub commitment_randomness: Option<jubjub::Fr>,
64
65 pub ar: Option<jubjub::Fr>,
67
68 pub auth_path: Vec<Option<(bls12_381::Scalar, bool)>>,
70
71 pub anchor: Option<bls12_381::Scalar>,
74}
75
76impl fmt::Debug for Spend {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 f.debug_struct("Spend")
79 .field("anchor", &self.anchor)
80 .finish_non_exhaustive()
81 }
82}
83
84#[derive(Clone)]
86pub struct Output {
87 pub value_commitment_opening: Option<ValueCommitmentOpening>,
89
90 pub payment_address: Option<PaymentAddress>,
92
93 pub commitment_randomness: Option<jubjub::Fr>,
95
96 pub esk: Option<jubjub::Fr>,
98}
99
100impl fmt::Debug for Output {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 f.debug_struct("Output").finish_non_exhaustive()
103 }
104}
105
106fn expose_value_commitment<CS>(
109 mut cs: CS,
110 value_commitment_opening: Option<ValueCommitmentOpening>,
111) -> Result<Vec<boolean::Boolean>, SynthesisError>
112where
113 CS: ConstraintSystem<bls12_381::Scalar>,
114{
115 let value_bits = boolean::u64_into_boolean_vec_le(
117 cs.namespace(|| "value"),
118 value_commitment_opening.as_ref().map(|c| c.value.inner()),
119 )?;
120
121 let value = ecc::fixed_base_multiplication(
123 cs.namespace(|| "compute the value in the exponent"),
124 &VALUE_COMMITMENT_VALUE_GENERATOR,
125 &value_bits,
126 )?;
127
128 let rcv = boolean::field_into_boolean_vec_le(
132 cs.namespace(|| "rcv"),
133 value_commitment_opening.as_ref().map(|c| c.randomness),
134 )?;
135
136 let rcv = ecc::fixed_base_multiplication(
138 cs.namespace(|| "computation of rcv"),
139 &VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
140 &rcv,
141 )?;
142
143 let cv = value.add(cs.namespace(|| "computation of cv"), &rcv)?;
145
146 cv.inputize(cs.namespace(|| "commitment point"))?;
148
149 Ok(value_bits)
150}
151
152impl Circuit<bls12_381::Scalar> for Spend {
153 fn synthesize<CS: ConstraintSystem<bls12_381::Scalar>>(
154 self,
155 cs: &mut CS,
156 ) -> Result<(), SynthesisError> {
157 let ak = ecc::EdwardsPoint::witness(
159 cs.namespace(|| "ak"),
160 self.proof_generation_key.as_ref().map(|k| k.ak().into()),
161 )?;
162
163 ak.assert_not_small_order(cs.namespace(|| "ak not small order"))?;
167
168 {
170 let ar = boolean::field_into_boolean_vec_le(cs.namespace(|| "ar"), self.ar)?;
171
172 let ar = ecc::fixed_base_multiplication(
174 cs.namespace(|| "computation of randomization for the signing key"),
175 &SPENDING_KEY_GENERATOR,
176 &ar,
177 )?;
178
179 let rk = ak.add(cs.namespace(|| "computation of rk"), &ar)?;
180
181 rk.inputize(cs.namespace(|| "rk"))?;
182 }
183
184 let nk;
186 {
187 let nsk = boolean::field_into_boolean_vec_le(
189 cs.namespace(|| "nsk"),
190 self.proof_generation_key.as_ref().map(|k| *k.nsk()),
191 )?;
192
193 nk = ecc::fixed_base_multiplication(
200 cs.namespace(|| "computation of nk"),
201 &PROOF_GENERATION_KEY_GENERATOR,
202 &nsk,
203 )?;
204 }
205
206 let mut ivk_preimage = vec![];
208
209 ivk_preimage.extend(ak.repr(cs.namespace(|| "representation of ak"))?);
211
212 let mut nf_preimage = vec![];
214
215 {
218 let repr_nk = nk.repr(cs.namespace(|| "representation of nk"))?;
219
220 ivk_preimage.extend(repr_nk.iter().cloned());
221 nf_preimage.extend(repr_nk);
222 }
223
224 assert_eq!(ivk_preimage.len(), 512);
225 assert_eq!(nf_preimage.len(), 256);
226
227 let mut ivk = blake2s::blake2s(
229 cs.namespace(|| "computation of ivk"),
230 &ivk_preimage,
231 super::constants::CRH_IVK_PERSONALIZATION,
232 )?;
233
234 ivk.truncate(jubjub::Fr::CAPACITY as usize);
236
237 let g_d = {
239 ecc::EdwardsPoint::witness(
240 cs.namespace(|| "witness g_d"),
241 self.payment_address.as_ref().map(|a| {
242 a.diversifier()
243 .g_d()
244 .expect("checked at construction")
245 .into()
246 }),
247 )?
248 };
249
250 g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"))?;
255
256 let pk_d = g_d.mul(cs.namespace(|| "compute pk_d"), &ivk)?;
258
259 let mut note_contents = vec![];
262
263 let mut value_num = num::Num::zero();
266 {
267 let value_bits = expose_value_commitment(
269 cs.namespace(|| "value commitment"),
270 self.value_commitment_opening,
271 )?;
272
273 let mut coeff = bls12_381::Scalar::one();
276 for bit in &value_bits {
277 value_num = value_num.add_bool_with_coeff(CS::one(), bit, coeff);
278 coeff = coeff.double();
279 }
280
281 note_contents.extend(value_bits);
283 }
284
285 note_contents.extend(g_d.repr(cs.namespace(|| "representation of g_d"))?);
287
288 note_contents.extend(pk_d.repr(cs.namespace(|| "representation of pk_d"))?);
290
291 assert_eq!(
292 note_contents.len(),
293 64 + 256 + 256 );
297
298 let mut cm = pedersen_hash::pedersen_hash(
300 cs.namespace(|| "note content hash"),
301 pedersen_hash::Personalization::NoteCommitment,
302 ¬e_contents,
303 )?;
304
305 {
306 let rcm = boolean::field_into_boolean_vec_le(
308 cs.namespace(|| "rcm"),
309 self.commitment_randomness,
310 )?;
311
312 let rcm = ecc::fixed_base_multiplication(
314 cs.namespace(|| "computation of commitment randomness"),
315 &NOTE_COMMITMENT_RANDOMNESS_GENERATOR,
316 &rcm,
317 )?;
318
319 cm = cm.add(cs.namespace(|| "randomization of note commitment"), &rcm)?;
322 }
323
324 let mut position_bits = vec![];
328
329 let mut cur = cm.get_u().clone();
332
333 for (i, e) in self.auth_path.into_iter().enumerate() {
335 let cs = &mut cs.namespace(|| format!("merkle tree hash {i}"));
336
337 let cur_is_right = boolean::Boolean::from(boolean::AllocatedBit::alloc(
340 cs.namespace(|| "position bit"),
341 e.map(|e| e.1),
342 )?);
343
344 position_bits.push(cur_is_right.clone());
346
347 let path_element =
350 num::AllocatedNum::alloc(cs.namespace(|| "path element"), || Ok(e.get()?.0))?;
351
352 let (ul, ur) = num::AllocatedNum::conditionally_reverse(
354 cs.namespace(|| "conditional reversal of preimage"),
355 &cur,
356 &path_element,
357 &cur_is_right,
358 )?;
359
360 let mut preimage = vec![];
365 preimage.extend(ul.to_bits_le(cs.namespace(|| "ul into bits"))?);
366 preimage.extend(ur.to_bits_le(cs.namespace(|| "ur into bits"))?);
367
368 cur = pedersen_hash::pedersen_hash(
370 cs.namespace(|| "computation of pedersen hash"),
371 pedersen_hash::Personalization::MerkleTree(i),
372 &preimage,
373 )?
374 .get_u()
375 .clone(); }
377
378 {
379 let real_anchor_value = self.anchor;
380
381 let rt = num::AllocatedNum::alloc(cs.namespace(|| "conditional anchor"), || {
383 Ok(*real_anchor_value.get()?)
384 })?;
385
386 cs.enforce(
390 || "conditionally enforce correct root",
391 |lc| lc + cur.get_variable() - rt.get_variable(),
392 |lc| lc + &value_num.lc(bls12_381::Scalar::one()),
393 |lc| lc,
394 );
395
396 rt.inputize(cs.namespace(|| "anchor"))?;
398 }
399
400 let mut rho = cm;
403 {
404 let position = ecc::fixed_base_multiplication(
406 cs.namespace(|| "g^position"),
407 &NULLIFIER_POSITION_GENERATOR,
408 &position_bits,
409 )?;
410
411 rho = rho.add(cs.namespace(|| "faerie gold prevention"), &position)?;
413 }
414
415 nf_preimage.extend(rho.repr(cs.namespace(|| "representation of rho"))?);
417
418 assert_eq!(nf_preimage.len(), 512);
419
420 let nf = blake2s::blake2s(
422 cs.namespace(|| "nf computation"),
423 &nf_preimage,
424 super::constants::PRF_NF_PERSONALIZATION,
425 )?;
426
427 multipack::pack_into_inputs(cs.namespace(|| "pack nullifier"), &nf)
428 }
429}
430
431impl Circuit<bls12_381::Scalar> for Output {
432 fn synthesize<CS: ConstraintSystem<bls12_381::Scalar>>(
433 self,
434 cs: &mut CS,
435 ) -> Result<(), SynthesisError> {
436 let mut note_contents = vec![];
439
440 note_contents.extend(expose_value_commitment(
443 cs.namespace(|| "value commitment"),
444 self.value_commitment_opening,
445 )?);
446
447 {
449 let g_d = ecc::EdwardsPoint::witness(
452 cs.namespace(|| "witness g_d"),
453 self.payment_address.as_ref().map(|a| {
454 a.diversifier()
455 .g_d()
456 .expect("checked at construction")
457 .into()
458 }),
459 )?;
460
461 g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"))?;
471
472 note_contents.extend(g_d.repr(cs.namespace(|| "representation of g_d"))?);
475
476 let esk = boolean::field_into_boolean_vec_le(cs.namespace(|| "esk"), self.esk)?;
478
479 let epk = g_d.mul(cs.namespace(|| "epk computation"), &esk)?;
481
482 epk.inputize(cs.namespace(|| "epk"))?;
484 }
485
486 {
490 let pk_d = self
492 .payment_address
493 .as_ref()
494 .map(|e| jubjub::ExtendedPoint::from(e.pk_d().inner()).to_affine());
495
496 let v_contents = boolean::field_into_boolean_vec_le(
499 cs.namespace(|| "pk_d bits of v"),
500 pk_d.map(|e| e.get_v()),
501 )?;
502
503 let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc(
505 cs.namespace(|| "pk_d bit of u"),
506 pk_d.map(|e| e.get_u().is_odd().into()),
507 )?);
508
509 note_contents.extend(v_contents);
511 note_contents.push(sign_bit);
512 }
513
514 assert_eq!(
515 note_contents.len(),
516 64 + 256 + 256 );
520
521 let mut cm = pedersen_hash::pedersen_hash(
523 cs.namespace(|| "note content hash"),
524 pedersen_hash::Personalization::NoteCommitment,
525 ¬e_contents,
526 )?;
527
528 {
529 let rcm = boolean::field_into_boolean_vec_le(
531 cs.namespace(|| "rcm"),
532 self.commitment_randomness,
533 )?;
534
535 let rcm = ecc::fixed_base_multiplication(
537 cs.namespace(|| "computation of commitment randomness"),
538 &NOTE_COMMITMENT_RANDOMNESS_GENERATOR,
539 &rcm,
540 )?;
541
542 cm = cm.add(cs.namespace(|| "randomization of note commitment"), &rcm)?;
544 }
545
546 cm.get_u().inputize(cs.namespace(|| "commitment"))?;
551
552 Ok(())
553 }
554}
555
556pub struct SpendParameters(pub(crate) groth16::Parameters<Bls12>);
558
559impl SpendParameters {
560 pub fn read<R: io::Read>(reader: R, verify_point_encodings: bool) -> io::Result<Self> {
565 Ok(Self(groth16::Parameters::<Bls12>::read(
566 reader,
567 verify_point_encodings,
568 )?))
569 }
570
571 pub fn verifying_key(&self) -> SpendVerifyingKey {
573 SpendVerifyingKey(self.0.vk.clone())
574 }
575
576 pub fn prepared_verifying_key(&self) -> PreparedSpendVerifyingKey {
579 PreparedSpendVerifyingKey(groth16::prepare_verifying_key(&self.0.vk))
580 }
581}
582
583pub struct SpendVerifyingKey(pub(crate) groth16::VerifyingKey<Bls12>);
585
586impl SpendVerifyingKey {
587 pub fn prepare(&self) -> PreparedSpendVerifyingKey {
589 PreparedSpendVerifyingKey(groth16::prepare_verifying_key(&self.0))
590 }
591}
592
593pub struct PreparedSpendVerifyingKey(pub(crate) groth16::PreparedVerifyingKey<Bls12>);
596
597pub struct OutputParameters(pub(crate) groth16::Parameters<Bls12>);
599
600impl OutputParameters {
601 pub fn read<R: io::Read>(reader: R, verify_point_encodings: bool) -> io::Result<Self> {
606 groth16::Parameters::<Bls12>::read(reader, verify_point_encodings).map(Self)
607 }
608
609 pub fn verifying_key(&self) -> OutputVerifyingKey {
611 OutputVerifyingKey(self.0.vk.clone())
612 }
613
614 pub fn prepared_verifying_key(&self) -> PreparedOutputVerifyingKey {
617 PreparedOutputVerifyingKey(groth16::prepare_verifying_key(&self.0.vk))
618 }
619}
620
621pub struct OutputVerifyingKey(pub(crate) groth16::VerifyingKey<Bls12>);
623
624impl OutputVerifyingKey {
625 pub fn prepare(&self) -> PreparedOutputVerifyingKey {
627 PreparedOutputVerifyingKey(groth16::prepare_verifying_key(&self.0))
628 }
629}
630
631pub struct PreparedOutputVerifyingKey(pub(crate) groth16::PreparedVerifyingKey<Bls12>);
634
635#[test]
636fn test_input_circuit_with_bls12_381() {
637 use crate::{
638 keys::SpendValidatingKey, pedersen_hash, Diversifier, Note, ProofGenerationKey, Rseed,
639 };
640
641 use bellman::gadgets::test::*;
642 use group::ff::Field;
643 use rand_core::{Rng, SeedableRng};
644 use rand_xorshift::XorShiftRng;
645
646 let mut rng = XorShiftRng::from_seed([
647 0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
648 0xe5,
649 ]);
650
651 let tree_depth = 32;
652
653 for _ in 0..10 {
654 let value_commitment = ValueCommitmentOpening {
655 value: NoteValue::from_raw(rng.next_u64()),
656 randomness: jubjub::Fr::random(&mut rng),
657 };
658
659 let proof_generation_key = ProofGenerationKey::from_parts(
660 SpendValidatingKey::fake_random(&mut rng),
661 jubjub::Fr::random(&mut rng),
662 )
663 .unwrap();
664
665 let viewing_key = proof_generation_key.to_viewing_key();
666
667 let payment_address;
668
669 loop {
670 let diversifier = {
671 let mut d = [0; 11];
672 rng.fill_bytes(&mut d);
673 Diversifier(d)
674 };
675
676 if let Some(p) = viewing_key.to_payment_address(diversifier) {
677 payment_address = p;
678 break;
679 }
680 }
681
682 let commitment_randomness = jubjub::Fr::random(&mut rng);
683 let auth_path =
684 vec![Some((bls12_381::Scalar::random(&mut rng), rng.next_u32() % 2 != 0)); tree_depth];
685 let ar = jubjub::Fr::random(&mut rng);
686
687 {
688 let rk = jubjub::AffinePoint::from_bytes(viewing_key.rk(ar).into()).unwrap();
689 let expected_value_commitment = value_commitment.commitment().to_affine();
690 let note = Note::from_parts(
691 payment_address,
692 value_commitment.value,
693 Rseed::BeforeZip212(commitment_randomness),
694 );
695
696 let mut position = 0u64;
697 let cmu = note.cmu();
698 let mut cur = bls12_381::Scalar::from_bytes(&cmu.to_bytes()).unwrap();
699
700 for (i, val) in auth_path.clone().into_iter().enumerate() {
701 let (uncle, b) = val.unwrap();
702
703 let mut lhs = cur;
704 let mut rhs = uncle;
705
706 if b {
707 ::core::mem::swap(&mut lhs, &mut rhs);
708 }
709
710 let lhs = lhs.to_le_bits();
711 let rhs = rhs.to_le_bits();
712
713 cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash(
714 pedersen_hash::Personalization::MerkleTree(i),
715 lhs.iter()
716 .by_vals()
717 .take(bls12_381::Scalar::NUM_BITS as usize)
718 .chain(
719 rhs.iter()
720 .by_vals()
721 .take(bls12_381::Scalar::NUM_BITS as usize),
722 ),
723 ))
724 .to_affine()
725 .get_u();
726
727 if b {
728 position |= 1 << i;
729 }
730 }
731
732 let expected_nf = note.nf(viewing_key.nk(), position);
733 let expected_nf = multipack::bytes_to_bits_le(&expected_nf.0);
734 let expected_nf = multipack::compute_multipacking(&expected_nf);
735 assert_eq!(expected_nf.len(), 2);
736
737 let mut cs = TestConstraintSystem::new();
738
739 let instance = Spend {
740 value_commitment_opening: Some(value_commitment.clone()),
741 proof_generation_key: Some(proof_generation_key.clone()),
742 payment_address: Some(payment_address),
743 commitment_randomness: Some(commitment_randomness),
744 ar: Some(ar),
745 auth_path: auth_path.clone(),
746 anchor: Some(cur),
747 };
748
749 instance.synthesize(&mut cs).unwrap();
750
751 assert!(cs.is_satisfied());
752 assert_eq!(cs.num_constraints(), 98777);
753 assert_eq!(
754 cs.hash(),
755 "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89"
756 );
757
758 assert_eq!(
759 cs.get("randomization of note commitment/u3/num").to_repr(),
760 cmu.to_bytes()
761 );
762
763 assert_eq!(cs.num_inputs(), 8);
764 assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one());
765 assert_eq!(cs.get_input(1, "rk/u/input variable"), rk.get_u());
766 assert_eq!(cs.get_input(2, "rk/v/input variable"), rk.get_v());
767 assert_eq!(
768 cs.get_input(3, "value commitment/commitment point/u/input variable"),
769 expected_value_commitment.get_u()
770 );
771 assert_eq!(
772 cs.get_input(4, "value commitment/commitment point/v/input variable"),
773 expected_value_commitment.get_v()
774 );
775 assert_eq!(
776 cs.get_input(5, "anchor/input variable").to_repr(),
777 cur.to_bytes()
778 );
779 assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]);
780 assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]);
781 }
782 }
783}
784
785#[test]
786fn test_input_circuit_with_bls12_381_external_test_vectors() {
787 use crate::{
788 keys::SpendValidatingKey, pedersen_hash, Diversifier, Note, ProofGenerationKey, Rseed,
789 };
790
791 use bellman::gadgets::test::*;
792 use group::ff::Field;
793 use rand_core::{Rng, SeedableRng};
794 use rand_xorshift::XorShiftRng;
795
796 let mut rng = XorShiftRng::from_seed([
797 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
798 0xe5,
799 ]);
800
801 let tree_depth = 32;
802
803 let expected_commitment_us = [
804 "43821661663052659750276289184181083197337192946256245809816728673021647664276",
805 "7220807656052227578299730541645543434083158611414003423211850718229633594616",
806 "13239753550660714843257636471668037031928211668773449453628093339627668081697",
807 "10900524635678389360790699587556574797582192824300145558807405770494079767974",
808 "1411013767457690636461779630023011774660680126764323588543800715293173598850",
809 "32334206652383066267661379202183359608706535021387905923603014648832344657662",
810 "20206750741605167608500278423400565295188703622528437817438897624149653579380",
811 "46716485782200334735478719487356079850582051575003452698983255860512578229998",
812 "31221372899739042781372142393132358519434268512685538373976981051223051220367",
813 "18269767207277008186871145355531741929166733260352590789136389380124992250945",
814 ];
815
816 let expected_commitment_vs = [
817 "27630722367128086497290371604583225252915685718989450292520883698391703910",
818 "23310648738313092772044712773481584369462075017189681529702825235349449805260",
819 "25709635353183537915646348052945798827495141780341329896098121888376871589480",
820 "10516315852014492141081718791576479298042117442649432716255936672048164184691",
821 "23970713991179488695004801139667700217127937225554773561645815034212389459772",
822 "3256052161046564597126736968199320852691566092694819239485673781545479548450",
823 "18887250722195819674378865377623103071236046274361890247643850134985809137409",
824 "36501156873031641173054592888886902104303750771545647842488588827138867116570",
825 "21927526310070011864833939629345235038589128172309792087590183778192091594775",
826 "32959334601512756708397683646222389414681003290313255304927423560477040775488",
827 ];
828
829 for i in 0..10 {
830 let value_commitment = ValueCommitmentOpening {
831 value: NoteValue::from_raw(i),
832 randomness: jubjub::Fr::from(1000 * (i + 1)),
833 };
834
835 let proof_generation_key = ProofGenerationKey::from_parts(
836 SpendValidatingKey::fake_random(&mut rng),
837 jubjub::Fr::random(&mut rng),
838 )
839 .unwrap();
840
841 let viewing_key = proof_generation_key.to_viewing_key();
842
843 let payment_address;
844
845 loop {
846 let diversifier = {
847 let mut d = [0; 11];
848 rng.fill_bytes(&mut d);
849 Diversifier(d)
850 };
851
852 if let Some(p) = viewing_key.to_payment_address(diversifier) {
853 payment_address = p;
854 break;
855 }
856 }
857
858 let commitment_randomness = jubjub::Fr::random(&mut rng);
859 let auth_path =
860 vec![Some((bls12_381::Scalar::random(&mut rng), rng.next_u32() % 2 != 0)); tree_depth];
861 let ar = jubjub::Fr::random(&mut rng);
862
863 {
864 let rk = jubjub::AffinePoint::from_bytes(viewing_key.rk(ar).into()).unwrap();
865 let expected_value_commitment = value_commitment.commitment().to_affine();
866 assert_eq!(
867 expected_value_commitment.get_u(),
868 bls12_381::Scalar::from_str_vartime(expected_commitment_us[i as usize]).unwrap()
869 );
870 assert_eq!(
871 expected_value_commitment.get_v(),
872 bls12_381::Scalar::from_str_vartime(expected_commitment_vs[i as usize]).unwrap()
873 );
874 let note = Note::from_parts(
875 payment_address,
876 value_commitment.value,
877 Rseed::BeforeZip212(commitment_randomness),
878 );
879
880 let mut position = 0u64;
881 let cmu = note.cmu();
882 let mut cur = bls12_381::Scalar::from_bytes(&cmu.to_bytes()).unwrap();
883
884 for (i, val) in auth_path.clone().into_iter().enumerate() {
885 let (uncle, b) = val.unwrap();
886
887 let mut lhs = cur;
888 let mut rhs = uncle;
889
890 if b {
891 ::core::mem::swap(&mut lhs, &mut rhs);
892 }
893
894 let lhs = lhs.to_le_bits();
895 let rhs = rhs.to_le_bits();
896
897 cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash(
898 pedersen_hash::Personalization::MerkleTree(i),
899 lhs.iter()
900 .by_vals()
901 .take(bls12_381::Scalar::NUM_BITS as usize)
902 .chain(
903 rhs.iter()
904 .by_vals()
905 .take(bls12_381::Scalar::NUM_BITS as usize),
906 ),
907 ))
908 .to_affine()
909 .get_u();
910
911 if b {
912 position |= 1 << i;
913 }
914 }
915
916 let expected_nf = note.nf(viewing_key.nk(), position);
917 let expected_nf = multipack::bytes_to_bits_le(&expected_nf.0);
918 let expected_nf = multipack::compute_multipacking(&expected_nf);
919 assert_eq!(expected_nf.len(), 2);
920
921 let mut cs = TestConstraintSystem::new();
922
923 let instance = Spend {
924 value_commitment_opening: Some(value_commitment.clone()),
925 proof_generation_key: Some(proof_generation_key.clone()),
926 payment_address: Some(payment_address),
927 commitment_randomness: Some(commitment_randomness),
928 ar: Some(ar),
929 auth_path: auth_path.clone(),
930 anchor: Some(cur),
931 };
932
933 instance.synthesize(&mut cs).unwrap();
934
935 assert!(cs.is_satisfied());
936 assert_eq!(cs.num_constraints(), 98777);
937 assert_eq!(
938 cs.hash(),
939 "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89"
940 );
941
942 assert_eq!(
943 cs.get("randomization of note commitment/u3/num").to_repr(),
944 cmu.to_bytes()
945 );
946
947 assert_eq!(cs.num_inputs(), 8);
948 assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one());
949 assert_eq!(cs.get_input(1, "rk/u/input variable"), rk.get_u());
950 assert_eq!(cs.get_input(2, "rk/v/input variable"), rk.get_v());
951 assert_eq!(
952 cs.get_input(3, "value commitment/commitment point/u/input variable"),
953 expected_value_commitment.get_u()
954 );
955 assert_eq!(
956 cs.get_input(4, "value commitment/commitment point/v/input variable"),
957 expected_value_commitment.get_v()
958 );
959 assert_eq!(
960 cs.get_input(5, "anchor/input variable").to_repr(),
961 cur.to_bytes()
962 );
963 assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]);
964 assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]);
965 }
966 }
967}
968
969#[test]
970fn test_output_circuit_with_bls12_381() {
971 use crate::{keys::SpendValidatingKey, Diversifier, ProofGenerationKey, Rseed};
972
973 use bellman::gadgets::test::*;
974 use group::ff::Field;
975 use rand_core::{Rng, SeedableRng};
976 use rand_xorshift::XorShiftRng;
977
978 let mut rng = XorShiftRng::from_seed([
979 0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
980 0xe5,
981 ]);
982
983 for _ in 0..100 {
984 let value_commitment = ValueCommitmentOpening {
985 value: NoteValue::from_raw(rng.next_u64()),
986 randomness: jubjub::Fr::random(&mut rng),
987 };
988
989 let nsk = jubjub::Fr::random(&mut rng);
990 let ak = SpendValidatingKey::fake_random(&mut rng);
991
992 let proof_generation_key = ProofGenerationKey::from_parts(ak, nsk).unwrap();
993
994 let viewing_key = proof_generation_key.to_viewing_key();
995
996 let payment_address;
997
998 loop {
999 let diversifier = {
1000 let mut d = [0; 11];
1001 rng.fill_bytes(&mut d);
1002 Diversifier(d)
1003 };
1004
1005 if let Some(p) = viewing_key.to_payment_address(diversifier) {
1006 payment_address = p;
1007 break;
1008 }
1009 }
1010
1011 let commitment_randomness = jubjub::Fr::random(&mut rng);
1012 let esk = jubjub::Fr::random(&mut rng);
1013
1014 {
1015 let mut cs = TestConstraintSystem::new();
1016
1017 let instance = Output {
1018 value_commitment_opening: Some(value_commitment.clone()),
1019 payment_address: Some(payment_address),
1020 commitment_randomness: Some(commitment_randomness),
1021 esk: Some(esk),
1022 };
1023
1024 instance.synthesize(&mut cs).unwrap();
1025
1026 assert!(cs.is_satisfied());
1027 assert_eq!(cs.num_constraints(), 7827);
1028 assert_eq!(
1029 cs.hash(),
1030 "c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee"
1031 );
1032
1033 let expected_cmu = payment_address
1034 .create_note(
1035 value_commitment.value,
1036 Rseed::BeforeZip212(commitment_randomness),
1037 )
1038 .cmu();
1039
1040 let expected_value_commitment = value_commitment.commitment().to_affine();
1041
1042 let expected_epk = jubjub::ExtendedPoint::from(
1043 payment_address
1044 .diversifier()
1045 .g_d()
1046 .expect("should be valid")
1047 * esk,
1048 )
1049 .to_affine();
1050
1051 assert_eq!(cs.num_inputs(), 6);
1052 assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one());
1053 assert_eq!(
1054 cs.get_input(1, "value commitment/commitment point/u/input variable"),
1055 expected_value_commitment.get_u()
1056 );
1057 assert_eq!(
1058 cs.get_input(2, "value commitment/commitment point/v/input variable"),
1059 expected_value_commitment.get_v()
1060 );
1061 assert_eq!(
1062 cs.get_input(3, "epk/u/input variable"),
1063 expected_epk.get_u()
1064 );
1065 assert_eq!(
1066 cs.get_input(4, "epk/v/input variable"),
1067 expected_epk.get_v()
1068 );
1069 assert_eq!(
1070 cs.get_input(5, "commitment/input variable").to_repr(),
1071 expected_cmu.to_bytes()
1072 );
1073 }
1074 }
1075}