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