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