1use alloc::vec::Vec;
4use core::fmt;
5use core2::io;
6
7use group::{ff::PrimeField, Curve};
8
9use bellman::{groth16, 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::{RngCore, 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 {
660 ak: SpendValidatingKey::fake_random(&mut rng),
661 nsk: jubjub::Fr::random(&mut rng),
662 };
663
664 let viewing_key = proof_generation_key.to_viewing_key();
665
666 let payment_address;
667
668 loop {
669 let diversifier = {
670 let mut d = [0; 11];
671 rng.fill_bytes(&mut d);
672 Diversifier(d)
673 };
674
675 if let Some(p) = viewing_key.to_payment_address(diversifier) {
676 payment_address = p;
677 break;
678 }
679 }
680
681 let commitment_randomness = jubjub::Fr::random(&mut rng);
682 let auth_path =
683 vec![Some((bls12_381::Scalar::random(&mut rng), rng.next_u32() % 2 != 0)); tree_depth];
684 let ar = jubjub::Fr::random(&mut rng);
685
686 {
687 let rk = jubjub::AffinePoint::from_bytes(viewing_key.rk(ar).into()).unwrap();
688 let expected_value_commitment = value_commitment.commitment().to_affine();
689 let note = Note::from_parts(
690 payment_address,
691 value_commitment.value,
692 Rseed::BeforeZip212(commitment_randomness),
693 );
694
695 let mut position = 0u64;
696 let cmu = note.cmu();
697 let mut cur = bls12_381::Scalar::from_bytes(&cmu.to_bytes()).unwrap();
698
699 for (i, val) in auth_path.clone().into_iter().enumerate() {
700 let (uncle, b) = val.unwrap();
701
702 let mut lhs = cur;
703 let mut rhs = uncle;
704
705 if b {
706 ::core::mem::swap(&mut lhs, &mut rhs);
707 }
708
709 let lhs = lhs.to_le_bits();
710 let rhs = rhs.to_le_bits();
711
712 cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash(
713 pedersen_hash::Personalization::MerkleTree(i),
714 lhs.iter()
715 .by_vals()
716 .take(bls12_381::Scalar::NUM_BITS as usize)
717 .chain(
718 rhs.iter()
719 .by_vals()
720 .take(bls12_381::Scalar::NUM_BITS as usize),
721 ),
722 ))
723 .to_affine()
724 .get_u();
725
726 if b {
727 position |= 1 << i;
728 }
729 }
730
731 let expected_nf = note.nf(&viewing_key.nk, position);
732 let expected_nf = multipack::bytes_to_bits_le(&expected_nf.0);
733 let expected_nf = multipack::compute_multipacking(&expected_nf);
734 assert_eq!(expected_nf.len(), 2);
735
736 let mut cs = TestConstraintSystem::new();
737
738 let instance = Spend {
739 value_commitment_opening: Some(value_commitment.clone()),
740 proof_generation_key: Some(proof_generation_key.clone()),
741 payment_address: Some(payment_address),
742 commitment_randomness: Some(commitment_randomness),
743 ar: Some(ar),
744 auth_path: auth_path.clone(),
745 anchor: Some(cur),
746 };
747
748 instance.synthesize(&mut cs).unwrap();
749
750 assert!(cs.is_satisfied());
751 assert_eq!(cs.num_constraints(), 98777);
752 assert_eq!(
753 cs.hash(),
754 "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89"
755 );
756
757 assert_eq!(
758 cs.get("randomization of note commitment/u3/num").to_repr(),
759 cmu.to_bytes()
760 );
761
762 assert_eq!(cs.num_inputs(), 8);
763 assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one());
764 assert_eq!(cs.get_input(1, "rk/u/input variable"), rk.get_u());
765 assert_eq!(cs.get_input(2, "rk/v/input variable"), rk.get_v());
766 assert_eq!(
767 cs.get_input(3, "value commitment/commitment point/u/input variable"),
768 expected_value_commitment.get_u()
769 );
770 assert_eq!(
771 cs.get_input(4, "value commitment/commitment point/v/input variable"),
772 expected_value_commitment.get_v()
773 );
774 assert_eq!(
775 cs.get_input(5, "anchor/input variable").to_repr(),
776 cur.to_bytes()
777 );
778 assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]);
779 assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]);
780 }
781 }
782}
783
784#[test]
785fn test_input_circuit_with_bls12_381_external_test_vectors() {
786 use crate::{
787 keys::SpendValidatingKey, pedersen_hash, Diversifier, Note, ProofGenerationKey, Rseed,
788 };
789
790 use bellman::gadgets::test::*;
791 use group::ff::Field;
792 use rand_core::{RngCore, SeedableRng};
793 use rand_xorshift::XorShiftRng;
794
795 let mut rng = XorShiftRng::from_seed([
796 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
797 0xe5,
798 ]);
799
800 let tree_depth = 32;
801
802 let expected_commitment_us = [
803 "43821661663052659750276289184181083197337192946256245809816728673021647664276",
804 "7220807656052227578299730541645543434083158611414003423211850718229633594616",
805 "13239753550660714843257636471668037031928211668773449453628093339627668081697",
806 "10900524635678389360790699587556574797582192824300145558807405770494079767974",
807 "1411013767457690636461779630023011774660680126764323588543800715293173598850",
808 "32334206652383066267661379202183359608706535021387905923603014648832344657662",
809 "20206750741605167608500278423400565295188703622528437817438897624149653579380",
810 "46716485782200334735478719487356079850582051575003452698983255860512578229998",
811 "31221372899739042781372142393132358519434268512685538373976981051223051220367",
812 "18269767207277008186871145355531741929166733260352590789136389380124992250945",
813 ];
814
815 let expected_commitment_vs = [
816 "27630722367128086497290371604583225252915685718989450292520883698391703910",
817 "23310648738313092772044712773481584369462075017189681529702825235349449805260",
818 "25709635353183537915646348052945798827495141780341329896098121888376871589480",
819 "10516315852014492141081718791576479298042117442649432716255936672048164184691",
820 "23970713991179488695004801139667700217127937225554773561645815034212389459772",
821 "3256052161046564597126736968199320852691566092694819239485673781545479548450",
822 "18887250722195819674378865377623103071236046274361890247643850134985809137409",
823 "36501156873031641173054592888886902104303750771545647842488588827138867116570",
824 "21927526310070011864833939629345235038589128172309792087590183778192091594775",
825 "32959334601512756708397683646222389414681003290313255304927423560477040775488",
826 ];
827
828 for i in 0..10 {
829 let value_commitment = ValueCommitmentOpening {
830 value: NoteValue::from_raw(i),
831 randomness: jubjub::Fr::from(1000 * (i + 1)),
832 };
833
834 let proof_generation_key = ProofGenerationKey {
835 ak: SpendValidatingKey::fake_random(&mut rng),
836 nsk: jubjub::Fr::random(&mut rng),
837 };
838
839 let viewing_key = proof_generation_key.to_viewing_key();
840
841 let payment_address;
842
843 loop {
844 let diversifier = {
845 let mut d = [0; 11];
846 rng.fill_bytes(&mut d);
847 Diversifier(d)
848 };
849
850 if let Some(p) = viewing_key.to_payment_address(diversifier) {
851 payment_address = p;
852 break;
853 }
854 }
855
856 let commitment_randomness = jubjub::Fr::random(&mut rng);
857 let auth_path =
858 vec![Some((bls12_381::Scalar::random(&mut rng), rng.next_u32() % 2 != 0)); tree_depth];
859 let ar = jubjub::Fr::random(&mut rng);
860
861 {
862 let rk = jubjub::AffinePoint::from_bytes(viewing_key.rk(ar).into()).unwrap();
863 let expected_value_commitment = value_commitment.commitment().to_affine();
864 assert_eq!(
865 expected_value_commitment.get_u(),
866 bls12_381::Scalar::from_str_vartime(expected_commitment_us[i as usize]).unwrap()
867 );
868 assert_eq!(
869 expected_value_commitment.get_v(),
870 bls12_381::Scalar::from_str_vartime(expected_commitment_vs[i as usize]).unwrap()
871 );
872 let note = Note::from_parts(
873 payment_address,
874 value_commitment.value,
875 Rseed::BeforeZip212(commitment_randomness),
876 );
877
878 let mut position = 0u64;
879 let cmu = note.cmu();
880 let mut cur = bls12_381::Scalar::from_bytes(&cmu.to_bytes()).unwrap();
881
882 for (i, val) in auth_path.clone().into_iter().enumerate() {
883 let (uncle, b) = val.unwrap();
884
885 let mut lhs = cur;
886 let mut rhs = uncle;
887
888 if b {
889 ::core::mem::swap(&mut lhs, &mut rhs);
890 }
891
892 let lhs = lhs.to_le_bits();
893 let rhs = rhs.to_le_bits();
894
895 cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash(
896 pedersen_hash::Personalization::MerkleTree(i),
897 lhs.iter()
898 .by_vals()
899 .take(bls12_381::Scalar::NUM_BITS as usize)
900 .chain(
901 rhs.iter()
902 .by_vals()
903 .take(bls12_381::Scalar::NUM_BITS as usize),
904 ),
905 ))
906 .to_affine()
907 .get_u();
908
909 if b {
910 position |= 1 << i;
911 }
912 }
913
914 let expected_nf = note.nf(&viewing_key.nk, position);
915 let expected_nf = multipack::bytes_to_bits_le(&expected_nf.0);
916 let expected_nf = multipack::compute_multipacking(&expected_nf);
917 assert_eq!(expected_nf.len(), 2);
918
919 let mut cs = TestConstraintSystem::new();
920
921 let instance = Spend {
922 value_commitment_opening: Some(value_commitment.clone()),
923 proof_generation_key: Some(proof_generation_key.clone()),
924 payment_address: Some(payment_address),
925 commitment_randomness: Some(commitment_randomness),
926 ar: Some(ar),
927 auth_path: auth_path.clone(),
928 anchor: Some(cur),
929 };
930
931 instance.synthesize(&mut cs).unwrap();
932
933 assert!(cs.is_satisfied());
934 assert_eq!(cs.num_constraints(), 98777);
935 assert_eq!(
936 cs.hash(),
937 "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89"
938 );
939
940 assert_eq!(
941 cs.get("randomization of note commitment/u3/num").to_repr(),
942 cmu.to_bytes()
943 );
944
945 assert_eq!(cs.num_inputs(), 8);
946 assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one());
947 assert_eq!(cs.get_input(1, "rk/u/input variable"), rk.get_u());
948 assert_eq!(cs.get_input(2, "rk/v/input variable"), rk.get_v());
949 assert_eq!(
950 cs.get_input(3, "value commitment/commitment point/u/input variable"),
951 expected_value_commitment.get_u()
952 );
953 assert_eq!(
954 cs.get_input(4, "value commitment/commitment point/v/input variable"),
955 expected_value_commitment.get_v()
956 );
957 assert_eq!(
958 cs.get_input(5, "anchor/input variable").to_repr(),
959 cur.to_bytes()
960 );
961 assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]);
962 assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]);
963 }
964 }
965}
966
967#[test]
968fn test_output_circuit_with_bls12_381() {
969 use crate::{keys::SpendValidatingKey, Diversifier, ProofGenerationKey, Rseed};
970
971 use bellman::gadgets::test::*;
972 use group::ff::Field;
973 use rand_core::{RngCore, SeedableRng};
974 use rand_xorshift::XorShiftRng;
975
976 let mut rng = XorShiftRng::from_seed([
977 0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
978 0xe5,
979 ]);
980
981 for _ in 0..100 {
982 let value_commitment = ValueCommitmentOpening {
983 value: NoteValue::from_raw(rng.next_u64()),
984 randomness: jubjub::Fr::random(&mut rng),
985 };
986
987 let nsk = jubjub::Fr::random(&mut rng);
988 let ak = SpendValidatingKey::fake_random(&mut rng);
989
990 let proof_generation_key = ProofGenerationKey { ak, nsk };
991
992 let viewing_key = proof_generation_key.to_viewing_key();
993
994 let payment_address;
995
996 loop {
997 let diversifier = {
998 let mut d = [0; 11];
999 rng.fill_bytes(&mut d);
1000 Diversifier(d)
1001 };
1002
1003 if let Some(p) = viewing_key.to_payment_address(diversifier) {
1004 payment_address = p;
1005 break;
1006 }
1007 }
1008
1009 let commitment_randomness = jubjub::Fr::random(&mut rng);
1010 let esk = jubjub::Fr::random(&mut rng);
1011
1012 {
1013 let mut cs = TestConstraintSystem::new();
1014
1015 let instance = Output {
1016 value_commitment_opening: Some(value_commitment.clone()),
1017 payment_address: Some(payment_address),
1018 commitment_randomness: Some(commitment_randomness),
1019 esk: Some(esk),
1020 };
1021
1022 instance.synthesize(&mut cs).unwrap();
1023
1024 assert!(cs.is_satisfied());
1025 assert_eq!(cs.num_constraints(), 7827);
1026 assert_eq!(
1027 cs.hash(),
1028 "c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee"
1029 );
1030
1031 let expected_cmu = payment_address
1032 .create_note(
1033 value_commitment.value,
1034 Rseed::BeforeZip212(commitment_randomness),
1035 )
1036 .cmu();
1037
1038 let expected_value_commitment = value_commitment.commitment().to_affine();
1039
1040 let expected_epk = jubjub::ExtendedPoint::from(
1041 payment_address
1042 .diversifier()
1043 .g_d()
1044 .expect("should be valid")
1045 * esk,
1046 )
1047 .to_affine();
1048
1049 assert_eq!(cs.num_inputs(), 6);
1050 assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one());
1051 assert_eq!(
1052 cs.get_input(1, "value commitment/commitment point/u/input variable"),
1053 expected_value_commitment.get_u()
1054 );
1055 assert_eq!(
1056 cs.get_input(2, "value commitment/commitment point/v/input variable"),
1057 expected_value_commitment.get_v()
1058 );
1059 assert_eq!(
1060 cs.get_input(3, "epk/u/input variable"),
1061 expected_epk.get_u()
1062 );
1063 assert_eq!(
1064 cs.get_input(4, "epk/v/input variable"),
1065 expected_epk.get_v()
1066 );
1067 assert_eq!(
1068 cs.get_input(5, "commitment/input variable").to_repr(),
1069 expected_cmu.to_bytes()
1070 );
1071 }
1072 }
1073}