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
557#[cfg(feature = "pinned-vk-only")]
558mod bundled;
559
560#[cfg(feature = "pinned-vk-only")]
561pub use bundled::pinned_verifying_keys;
562
563const VERIFYING_KEY_FIXED_BYTES: u64 = 3 * 96 + 3 * 192 + 4;
565const VERIFYING_KEY_INPUT_BYTES: u64 = 96;
566
567pub struct SpendParameters(pub(crate) groth16::Parameters<Bls12>);
569
570impl SpendParameters {
571 pub fn read<R: io::Read>(reader: R, verify_point_encodings: bool) -> io::Result<Self> {
576 Ok(Self(groth16::Parameters::<Bls12>::read(
577 reader,
578 verify_point_encodings,
579 )?))
580 }
581
582 pub fn verifying_key(&self) -> SpendVerifyingKey {
584 SpendVerifyingKey::new(self.0.vk.clone())
585 }
586
587 pub fn prepared_verifying_key(&self) -> PreparedSpendVerifyingKey {
590 PreparedSpendVerifyingKey(groth16::prepare_verifying_key(&self.0.vk))
591 }
592}
593
594pub struct SpendVerifyingKey(
596 pub(crate) groth16::VerifyingKey<Bls12>,
597 OnceLock<groth16::batch::PreparedBatchG2<Bls12>>,
598);
599
600impl SpendVerifyingKey {
601 pub fn read<R: io::Read>(reader: R) -> io::Result<Self> {
607 let limit = VERIFYING_KEY_FIXED_BYTES + 8 * VERIFYING_KEY_INPUT_BYTES;
608 groth16::VerifyingKey::<Bls12>::read(io::Read::take(reader, limit)).map(Self::new)
609 }
610
611 pub(crate) fn new(vk: groth16::VerifyingKey<Bls12>) -> Self {
612 Self(vk, OnceLock::new())
613 }
614
615 pub(crate) fn prepared_batch(
616 &self,
617 ) -> Result<groth16::batch::PreparedBatchVerifyingKey<'_, Bls12>, bellman::VerificationError>
618 {
619 let g2 = self
620 .1
621 .get_or_init(|| groth16::batch::PreparedBatchG2::from(&self.0));
622 groth16::batch::PreparedBatchVerifyingKey::from_cached(&self.0, g2)
623 }
624
625 pub fn prepare(&self) -> PreparedSpendVerifyingKey {
627 PreparedSpendVerifyingKey(groth16::prepare_verifying_key(&self.0))
628 }
629}
630
631pub struct PreparedSpendVerifyingKey(pub(crate) groth16::PreparedVerifyingKey<Bls12>);
634
635pub struct OutputParameters(pub(crate) groth16::Parameters<Bls12>);
637
638impl OutputParameters {
639 pub fn read<R: io::Read>(reader: R, verify_point_encodings: bool) -> io::Result<Self> {
644 groth16::Parameters::<Bls12>::read(reader, verify_point_encodings).map(Self)
645 }
646
647 pub fn verifying_key(&self) -> OutputVerifyingKey {
649 OutputVerifyingKey::new(self.0.vk.clone())
650 }
651
652 pub fn prepared_verifying_key(&self) -> PreparedOutputVerifyingKey {
655 PreparedOutputVerifyingKey(groth16::prepare_verifying_key(&self.0.vk))
656 }
657}
658
659pub struct OutputVerifyingKey(
661 pub(crate) groth16::VerifyingKey<Bls12>,
662 OnceLock<groth16::batch::PreparedBatchG2<Bls12>>,
663);
664
665impl OutputVerifyingKey {
666 pub fn read<R: io::Read>(reader: R) -> io::Result<Self> {
672 let limit = VERIFYING_KEY_FIXED_BYTES + 6 * VERIFYING_KEY_INPUT_BYTES;
673 groth16::VerifyingKey::<Bls12>::read(io::Read::take(reader, limit)).map(Self::new)
674 }
675
676 pub(crate) fn new(vk: groth16::VerifyingKey<Bls12>) -> Self {
677 Self(vk, OnceLock::new())
678 }
679
680 pub(crate) fn prepared_batch(
681 &self,
682 ) -> Result<groth16::batch::PreparedBatchVerifyingKey<'_, Bls12>, bellman::VerificationError>
683 {
684 let g2 = self
685 .1
686 .get_or_init(|| groth16::batch::PreparedBatchG2::from(&self.0));
687 groth16::batch::PreparedBatchVerifyingKey::from_cached(&self.0, g2)
688 }
689
690 pub fn prepare(&self) -> PreparedOutputVerifyingKey {
692 PreparedOutputVerifyingKey(groth16::prepare_verifying_key(&self.0))
693 }
694}
695
696pub struct PreparedOutputVerifyingKey(pub(crate) groth16::PreparedVerifyingKey<Bls12>);
699
700#[test]
701fn test_input_circuit_with_bls12_381() {
702 use crate::{
703 Diversifier, Note, ProofGenerationKey, Rseed, keys::SpendValidatingKey, pedersen_hash,
704 };
705
706 use bellman::gadgets::test::*;
707 use group::ff::Field;
708 use rand_core::{Rng, SeedableRng};
709 use rand_xorshift::XorShiftRng;
710
711 let mut rng = XorShiftRng::from_seed([
712 0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
713 0xe5,
714 ]);
715
716 let tree_depth = 32;
717
718 for _ in 0..10 {
719 let value_commitment = ValueCommitmentOpening {
720 value: NoteValue::from_raw(rng.next_u64()),
721 randomness: jubjub::Fr::random(&mut rng),
722 };
723
724 let proof_generation_key = ProofGenerationKey {
725 ak: SpendValidatingKey::fake_random(&mut rng),
726 nsk: jubjub::Fr::random(&mut rng),
727 };
728
729 let viewing_key = proof_generation_key.to_viewing_key();
730
731 let payment_address;
732
733 loop {
734 let diversifier = {
735 let mut d = [0; 11];
736 rng.fill_bytes(&mut d);
737 Diversifier(d)
738 };
739
740 if let Some(p) = viewing_key.to_payment_address(diversifier) {
741 payment_address = p;
742 break;
743 }
744 }
745
746 let commitment_randomness = jubjub::Fr::random(&mut rng);
747 let auth_path = vec![
748 Some((
749 bls12_381::Scalar::random(&mut rng),
750 !rng.next_u32().is_multiple_of(2),
751 ));
752 tree_depth
753 ];
754 let ar = jubjub::Fr::random(&mut rng);
755
756 {
757 let rk = jubjub::AffinePoint::from_bytes(viewing_key.rk(ar).into()).unwrap();
758 let expected_value_commitment = value_commitment.commitment().to_affine();
759 let note = Note::from_parts(
760 payment_address,
761 value_commitment.value,
762 Rseed::BeforeZip212(commitment_randomness),
763 );
764
765 let mut position = 0u64;
766 let cmu = note.cmu();
767 let mut cur = bls12_381::Scalar::from_bytes(&cmu.to_bytes()).unwrap();
768
769 for (i, val) in auth_path.clone().into_iter().enumerate() {
770 let (uncle, b) = val.unwrap();
771
772 let mut lhs = cur;
773 let mut rhs = uncle;
774
775 if b {
776 ::core::mem::swap(&mut lhs, &mut rhs);
777 }
778
779 let lhs = lhs.to_le_bits();
780 let rhs = rhs.to_le_bits();
781
782 cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash(
783 pedersen_hash::Personalization::MerkleTree(i),
784 lhs.iter()
785 .by_vals()
786 .take(bls12_381::Scalar::NUM_BITS as usize)
787 .chain(
788 rhs.iter()
789 .by_vals()
790 .take(bls12_381::Scalar::NUM_BITS as usize),
791 ),
792 ))
793 .to_affine()
794 .get_u();
795
796 if b {
797 position |= 1 << i;
798 }
799 }
800
801 let expected_nf = note.nf(&viewing_key.nk, position);
802 let expected_nf = multipack::bytes_to_bits_le(&expected_nf.0);
803 let expected_nf = multipack::compute_multipacking(&expected_nf);
804 assert_eq!(expected_nf.len(), 2);
805
806 let mut cs = TestConstraintSystem::new();
807
808 let instance = Spend {
809 value_commitment_opening: Some(value_commitment.clone()),
810 proof_generation_key: Some(proof_generation_key.clone()),
811 payment_address: Some(payment_address),
812 commitment_randomness: Some(commitment_randomness),
813 ar: Some(ar),
814 auth_path: auth_path.clone(),
815 anchor: Some(cur),
816 };
817
818 instance.synthesize(&mut cs).unwrap();
819
820 assert!(cs.is_satisfied());
821 assert_eq!(cs.num_constraints(), 98777);
822 assert_eq!(
823 cs.hash(),
824 "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89"
825 );
826
827 assert_eq!(
828 cs.get("randomization of note commitment/u3/num").to_repr(),
829 cmu.to_bytes()
830 );
831
832 assert_eq!(cs.num_inputs(), 8);
833 assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one());
834 assert_eq!(cs.get_input(1, "rk/u/input variable"), rk.get_u());
835 assert_eq!(cs.get_input(2, "rk/v/input variable"), rk.get_v());
836 assert_eq!(
837 cs.get_input(3, "value commitment/commitment point/u/input variable"),
838 expected_value_commitment.get_u()
839 );
840 assert_eq!(
841 cs.get_input(4, "value commitment/commitment point/v/input variable"),
842 expected_value_commitment.get_v()
843 );
844 assert_eq!(
845 cs.get_input(5, "anchor/input variable").to_repr(),
846 cur.to_bytes()
847 );
848 assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]);
849 assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]);
850 }
851 }
852}
853
854#[test]
855fn test_input_circuit_with_bls12_381_external_test_vectors() {
856 use crate::{
857 Diversifier, Note, ProofGenerationKey, Rseed, keys::SpendValidatingKey, pedersen_hash,
858 };
859
860 use bellman::gadgets::test::*;
861 use group::ff::Field;
862 use rand_core::{Rng, SeedableRng};
863 use rand_xorshift::XorShiftRng;
864
865 let mut rng = XorShiftRng::from_seed([
866 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
867 0xe5,
868 ]);
869
870 let tree_depth = 32;
871
872 let expected_commitment_us = [
873 "43821661663052659750276289184181083197337192946256245809816728673021647664276",
874 "7220807656052227578299730541645543434083158611414003423211850718229633594616",
875 "13239753550660714843257636471668037031928211668773449453628093339627668081697",
876 "10900524635678389360790699587556574797582192824300145558807405770494079767974",
877 "1411013767457690636461779630023011774660680126764323588543800715293173598850",
878 "32334206652383066267661379202183359608706535021387905923603014648832344657662",
879 "20206750741605167608500278423400565295188703622528437817438897624149653579380",
880 "46716485782200334735478719487356079850582051575003452698983255860512578229998",
881 "31221372899739042781372142393132358519434268512685538373976981051223051220367",
882 "18269767207277008186871145355531741929166733260352590789136389380124992250945",
883 ];
884
885 let expected_commitment_vs = [
886 "27630722367128086497290371604583225252915685718989450292520883698391703910",
887 "23310648738313092772044712773481584369462075017189681529702825235349449805260",
888 "25709635353183537915646348052945798827495141780341329896098121888376871589480",
889 "10516315852014492141081718791576479298042117442649432716255936672048164184691",
890 "23970713991179488695004801139667700217127937225554773561645815034212389459772",
891 "3256052161046564597126736968199320852691566092694819239485673781545479548450",
892 "18887250722195819674378865377623103071236046274361890247643850134985809137409",
893 "36501156873031641173054592888886902104303750771545647842488588827138867116570",
894 "21927526310070011864833939629345235038589128172309792087590183778192091594775",
895 "32959334601512756708397683646222389414681003290313255304927423560477040775488",
896 ];
897
898 for i in 0..10 {
899 let value_commitment = ValueCommitmentOpening {
900 value: NoteValue::from_raw(i),
901 randomness: jubjub::Fr::from(1000 * (i + 1)),
902 };
903
904 let proof_generation_key = ProofGenerationKey {
905 ak: SpendValidatingKey::fake_random(&mut rng),
906 nsk: jubjub::Fr::random(&mut rng),
907 };
908
909 let viewing_key = proof_generation_key.to_viewing_key();
910
911 let payment_address;
912
913 loop {
914 let diversifier = {
915 let mut d = [0; 11];
916 rng.fill_bytes(&mut d);
917 Diversifier(d)
918 };
919
920 if let Some(p) = viewing_key.to_payment_address(diversifier) {
921 payment_address = p;
922 break;
923 }
924 }
925
926 let commitment_randomness = jubjub::Fr::random(&mut rng);
927 let auth_path = vec![
928 Some((
929 bls12_381::Scalar::random(&mut rng),
930 !rng.next_u32().is_multiple_of(2),
931 ));
932 tree_depth
933 ];
934 let ar = jubjub::Fr::random(&mut rng);
935
936 {
937 let rk = jubjub::AffinePoint::from_bytes(viewing_key.rk(ar).into()).unwrap();
938 let expected_value_commitment = value_commitment.commitment().to_affine();
939 assert_eq!(
940 expected_value_commitment.get_u(),
941 bls12_381::Scalar::from_str_vartime(expected_commitment_us[i as usize]).unwrap()
942 );
943 assert_eq!(
944 expected_value_commitment.get_v(),
945 bls12_381::Scalar::from_str_vartime(expected_commitment_vs[i as usize]).unwrap()
946 );
947 let note = Note::from_parts(
948 payment_address,
949 value_commitment.value,
950 Rseed::BeforeZip212(commitment_randomness),
951 );
952
953 let mut position = 0u64;
954 let cmu = note.cmu();
955 let mut cur = bls12_381::Scalar::from_bytes(&cmu.to_bytes()).unwrap();
956
957 for (i, val) in auth_path.clone().into_iter().enumerate() {
958 let (uncle, b) = val.unwrap();
959
960 let mut lhs = cur;
961 let mut rhs = uncle;
962
963 if b {
964 ::core::mem::swap(&mut lhs, &mut rhs);
965 }
966
967 let lhs = lhs.to_le_bits();
968 let rhs = rhs.to_le_bits();
969
970 cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash(
971 pedersen_hash::Personalization::MerkleTree(i),
972 lhs.iter()
973 .by_vals()
974 .take(bls12_381::Scalar::NUM_BITS as usize)
975 .chain(
976 rhs.iter()
977 .by_vals()
978 .take(bls12_381::Scalar::NUM_BITS as usize),
979 ),
980 ))
981 .to_affine()
982 .get_u();
983
984 if b {
985 position |= 1 << i;
986 }
987 }
988
989 let expected_nf = note.nf(&viewing_key.nk, position);
990 let expected_nf = multipack::bytes_to_bits_le(&expected_nf.0);
991 let expected_nf = multipack::compute_multipacking(&expected_nf);
992 assert_eq!(expected_nf.len(), 2);
993
994 let mut cs = TestConstraintSystem::new();
995
996 let instance = Spend {
997 value_commitment_opening: Some(value_commitment.clone()),
998 proof_generation_key: Some(proof_generation_key.clone()),
999 payment_address: Some(payment_address),
1000 commitment_randomness: Some(commitment_randomness),
1001 ar: Some(ar),
1002 auth_path: auth_path.clone(),
1003 anchor: Some(cur),
1004 };
1005
1006 instance.synthesize(&mut cs).unwrap();
1007
1008 assert!(cs.is_satisfied());
1009 assert_eq!(cs.num_constraints(), 98777);
1010 assert_eq!(
1011 cs.hash(),
1012 "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89"
1013 );
1014
1015 assert_eq!(
1016 cs.get("randomization of note commitment/u3/num").to_repr(),
1017 cmu.to_bytes()
1018 );
1019
1020 assert_eq!(cs.num_inputs(), 8);
1021 assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one());
1022 assert_eq!(cs.get_input(1, "rk/u/input variable"), rk.get_u());
1023 assert_eq!(cs.get_input(2, "rk/v/input variable"), rk.get_v());
1024 assert_eq!(
1025 cs.get_input(3, "value commitment/commitment point/u/input variable"),
1026 expected_value_commitment.get_u()
1027 );
1028 assert_eq!(
1029 cs.get_input(4, "value commitment/commitment point/v/input variable"),
1030 expected_value_commitment.get_v()
1031 );
1032 assert_eq!(
1033 cs.get_input(5, "anchor/input variable").to_repr(),
1034 cur.to_bytes()
1035 );
1036 assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]);
1037 assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]);
1038 }
1039 }
1040}
1041
1042#[test]
1043fn test_output_circuit_with_bls12_381() {
1044 use crate::{Diversifier, ProofGenerationKey, Rseed, keys::SpendValidatingKey};
1045
1046 use bellman::gadgets::test::*;
1047 use group::ff::Field;
1048 use rand_core::{Rng, SeedableRng};
1049 use rand_xorshift::XorShiftRng;
1050
1051 let mut rng = XorShiftRng::from_seed([
1052 0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
1053 0xe5,
1054 ]);
1055
1056 for _ in 0..100 {
1057 let value_commitment = ValueCommitmentOpening {
1058 value: NoteValue::from_raw(rng.next_u64()),
1059 randomness: jubjub::Fr::random(&mut rng),
1060 };
1061
1062 let nsk = jubjub::Fr::random(&mut rng);
1063 let ak = SpendValidatingKey::fake_random(&mut rng);
1064
1065 let proof_generation_key = ProofGenerationKey { ak, nsk };
1066
1067 let viewing_key = proof_generation_key.to_viewing_key();
1068
1069 let payment_address;
1070
1071 loop {
1072 let diversifier = {
1073 let mut d = [0; 11];
1074 rng.fill_bytes(&mut d);
1075 Diversifier(d)
1076 };
1077
1078 if let Some(p) = viewing_key.to_payment_address(diversifier) {
1079 payment_address = p;
1080 break;
1081 }
1082 }
1083
1084 let commitment_randomness = jubjub::Fr::random(&mut rng);
1085 let esk = jubjub::Fr::random(&mut rng);
1086
1087 {
1088 let mut cs = TestConstraintSystem::new();
1089
1090 let instance = Output {
1091 value_commitment_opening: Some(value_commitment.clone()),
1092 payment_address: Some(payment_address),
1093 commitment_randomness: Some(commitment_randomness),
1094 esk: Some(esk),
1095 };
1096
1097 instance.synthesize(&mut cs).unwrap();
1098
1099 assert!(cs.is_satisfied());
1100 assert_eq!(cs.num_constraints(), 7827);
1101 assert_eq!(
1102 cs.hash(),
1103 "c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee"
1104 );
1105
1106 let expected_cmu = payment_address
1107 .create_note(
1108 value_commitment.value,
1109 Rseed::BeforeZip212(commitment_randomness),
1110 )
1111 .cmu();
1112
1113 let expected_value_commitment = value_commitment.commitment().to_affine();
1114
1115 let expected_epk = jubjub::ExtendedPoint::from(
1116 payment_address
1117 .diversifier()
1118 .g_d()
1119 .expect("should be valid")
1120 * esk,
1121 )
1122 .to_affine();
1123
1124 assert_eq!(cs.num_inputs(), 6);
1125 assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one());
1126 assert_eq!(
1127 cs.get_input(1, "value commitment/commitment point/u/input variable"),
1128 expected_value_commitment.get_u()
1129 );
1130 assert_eq!(
1131 cs.get_input(2, "value commitment/commitment point/v/input variable"),
1132 expected_value_commitment.get_v()
1133 );
1134 assert_eq!(
1135 cs.get_input(3, "epk/u/input variable"),
1136 expected_epk.get_u()
1137 );
1138 assert_eq!(
1139 cs.get_input(4, "epk/v/input variable"),
1140 expected_epk.get_v()
1141 );
1142 assert_eq!(
1143 cs.get_input(5, "commitment/input variable").to_repr(),
1144 expected_cmu.to_bytes()
1145 );
1146 }
1147 }
1148}