arkworks_circuits/circuit/
vanchor.rs

1use crate::Vec;
2
3use ark_crypto_primitives::{crh::CRHGadget, CRH};
4use ark_ff::fields::PrimeField;
5use ark_r1cs_std::{eq::EqGadget, fields::fp::FpVar, prelude::*};
6use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError};
7use ark_std::{cmp::Ordering::Less, marker::PhantomData};
8use arkworks_gadgets::{
9	arbitrary::vanchor_data::{
10		constraints::VAnchorArbitraryDataVar as ArbitraryInputVar,
11		VAnchorArbitraryData as ArbitraryInput,
12	},
13	keypair::vanchor::{constraints::KeypairVar, Keypair},
14	leaf::vanchor::{
15		constraints::{
16			PrivateVar as LeafPrivateInputsVar, PublicVar as LeafPublicInputsVar, VAnchorLeafGadget,
17		},
18		Private as LeafPrivateInputs, Public as LeafPublicInputs,
19	},
20	merkle_tree::{constraints::PathVar, Config as MerkleConfig, Path},
21	set::constraints::SetGadget,
22};
23
24pub struct VAnchorCircuit<
25	F: PrimeField,
26	// Hasher for the leaf creation,  Nullifier, Public key generation
27	H: CRH,
28	HG: CRHGadget<H, F>,
29	// Merkle config and hasher gadget for the tree
30	C: MerkleConfig,
31	LHGT: CRHGadget<C::LeafH, F>,
32	HGT: CRHGadget<C::H, F>,
33	const K: usize,
34	const N_INS: usize,
35	const N_OUTS: usize,
36	const M: usize,
37> {
38	public_amount: F,
39	ext_data_hash: ArbitraryInput<F>,
40
41	leaf_private_inputs: Vec<LeafPrivateInputs<F>>, // amount, blinding
42	keypair_inputs: Vec<Keypair<F, H>>,
43	leaf_public_input: LeafPublicInputs<F>, // chain_id
44	root_set: [F; M],
45	hasher_params_w2: H::Parameters,
46	hasher_params_w4: H::Parameters,
47	hasher_params_w5: H::Parameters,
48	paths: Vec<Path<C, K>>,
49	indices: Vec<F>,
50	nullifier_hash: Vec<H::Output>,
51
52	output_commitment: Vec<H::Output>,
53	out_leaf_private: Vec<LeafPrivateInputs<F>>,
54	out_leaf_public: Vec<LeafPublicInputs<F>>,
55	out_pubkey: Vec<F>,
56
57	_hasher: PhantomData<H>,
58	_hasher_gadget: PhantomData<HG>,
59	_leaf_hasher_gadget: PhantomData<LHGT>,
60	_tree_hasher_gadget: PhantomData<HGT>,
61	_merkle_config: PhantomData<C>,
62}
63
64impl<
65		F,
66		H,
67		HG,
68		C,
69		LHGT,
70		HGT,
71		const K: usize,
72		const N_INS: usize,
73		const N_OUTS: usize,
74		const M: usize,
75	> VAnchorCircuit<F, H, HG, C, LHGT, HGT, K, N_INS, N_OUTS, M>
76where
77	F: PrimeField,
78	H: CRH,
79	HG: CRHGadget<H, F>,
80	C: MerkleConfig,
81	LHGT: CRHGadget<C::LeafH, F>,
82	HGT: CRHGadget<C::H, F>,
83{
84	#[allow(clippy::too_many_arguments)]
85	pub fn new(
86		public_amount: F,
87		ext_data_hash: ArbitraryInput<F>,
88		leaf_private_inputs: Vec<LeafPrivateInputs<F>>,
89		keypair_inputs: Vec<Keypair<F, H>>,
90		leaf_public_input: LeafPublicInputs<F>,
91		root_set: [F; M],
92		hasher_params_w2: H::Parameters,
93		hasher_params_w4: H::Parameters,
94		hasher_params_w5: H::Parameters,
95		paths: Vec<Path<C, K>>,
96		indices: Vec<F>,
97		nullifier_hash: Vec<H::Output>,
98		output_commitment: Vec<H::Output>,
99		out_leaf_private: Vec<LeafPrivateInputs<F>>,
100		out_leaf_public: Vec<LeafPublicInputs<F>>,
101		out_pubkey: Vec<F>,
102	) -> Self {
103		Self {
104			public_amount,
105			ext_data_hash,
106			leaf_private_inputs,
107			keypair_inputs,
108			leaf_public_input,
109			root_set,
110			hasher_params_w2,
111			hasher_params_w4,
112			hasher_params_w5,
113			paths,
114			indices,
115			nullifier_hash,
116			output_commitment,
117			out_leaf_private,
118			out_leaf_public,
119			out_pubkey,
120			_hasher: PhantomData,
121			_hasher_gadget: PhantomData,
122			_leaf_hasher_gadget: PhantomData,
123			_tree_hasher_gadget: PhantomData,
124			_merkle_config: PhantomData,
125		}
126	}
127
128	#[allow(clippy::too_many_arguments)]
129	pub fn verify_input_var(
130		hasher_params_w2_var: &HG::ParametersVar,
131		hasher_params_w4_var: &HG::ParametersVar,
132		hasher_params_w5_var: &HG::ParametersVar,
133		leaf_private_var: &[LeafPrivateInputsVar<F>],
134		inkeypair_var: &[KeypairVar<F, H, HG>],
135		leaf_public_input_var: &LeafPublicInputsVar<F>,
136		in_path_indices_var: &[FpVar<F>],
137		in_path_elements_var: &[PathVar<F, C, HGT, LHGT, K>],
138		in_nullifier_var: &[HG::OutputVar],
139		set_gadget: &SetGadget<F>,
140	) -> Result<FpVar<F>, SynthesisError> {
141		let mut sums_ins_var = FpVar::<F>::zero();
142
143		for tx in 0..N_INS {
144			// Computing the public key
145			let pub_key = inkeypair_var[tx].public_key(hasher_params_w2_var)?;
146			// Computing the hash
147			let in_utxo_hasher_var = VAnchorLeafGadget::<F, H, HG>::create_leaf(
148				&leaf_private_var[tx],
149				leaf_public_input_var,
150				&pub_key,
151				hasher_params_w5_var,
152			)?;
153			// End of computing the hash
154
155			let signature = inkeypair_var[tx].signature(
156				&in_utxo_hasher_var,
157				&in_path_indices_var[tx],
158				hasher_params_w4_var,
159			)?;
160			// Nullifier
161			let nullifier_hash = VAnchorLeafGadget::<F, H, HG>::create_nullifier(
162				&signature,
163				&in_utxo_hasher_var,
164				hasher_params_w4_var,
165				&in_path_indices_var[tx],
166			)?;
167
168			nullifier_hash.enforce_equal(&in_nullifier_var[tx])?;
169
170			// Add the roots and diffs signals to the vanchor circuit
171			let roothash = &in_path_elements_var[tx].root_hash(&in_utxo_hasher_var)?;
172			let in_amount_tx = &leaf_private_var[tx].amount;
173
174			// Check membership if in_amount is non zero
175			let check = set_gadget.check_membership_enabled(&roothash, in_amount_tx)?;
176			check.enforce_equal(&Boolean::TRUE)?;
177
178			sums_ins_var += in_amount_tx;
179		}
180		Ok(sums_ins_var)
181	}
182
183	// Verify correctness of transaction outputs
184	pub fn verify_output_var(
185		hasher_params_w5_var: &HG::ParametersVar,
186		output_commitment_var: &[HG::OutputVar],
187		leaf_private_var: &[LeafPrivateInputsVar<F>],
188		leaf_public_var: &[LeafPublicInputsVar<F>],
189		out_pubkey_var: &[FpVar<F>],
190		limit_var: &FpVar<F>,
191	) -> Result<FpVar<F>, SynthesisError> {
192		let mut sums_outs_var = FpVar::<F>::zero();
193
194		for tx in 0..N_OUTS {
195			// Computing the hash
196			let out_utxo_hasher_var = VAnchorLeafGadget::<F, H, HG>::create_leaf(
197				&leaf_private_var[tx],
198				&leaf_public_var[tx],
199				&out_pubkey_var[tx],
200				hasher_params_w5_var,
201			)?;
202			// End of computing the hash
203			let out_amount_var = &leaf_private_var[tx].amount;
204			out_utxo_hasher_var.enforce_equal(&output_commitment_var[tx])?;
205
206			// Check that amount is less than 2^248 in the field (to prevent overflow)
207			out_amount_var.enforce_cmp_unchecked(limit_var, Less, false)?;
208
209			sums_outs_var += out_amount_var;
210		}
211		Ok(sums_outs_var)
212	}
213
214	// Check that there are no same nullifiers among all inputs
215	pub fn verify_no_same_nul(in_nullifier_var: &[HG::OutputVar]) -> Result<(), SynthesisError> {
216		for i in 0..N_INS - 1 {
217			for j in (i + 1)..N_INS {
218				in_nullifier_var[i].enforce_not_equal(&in_nullifier_var[j])?;
219			}
220		}
221
222		Ok(())
223	}
224
225	// Verify amount invariant
226	pub fn verify_input_invariant(
227		public_amount_var: &FpVar<F>,
228		sum_ins_var: &FpVar<F>,
229		sum_outs_var: &FpVar<F>,
230	) -> Result<(), SynthesisError> {
231		let res = sum_ins_var + public_amount_var;
232		res.enforce_equal(sum_outs_var)?;
233		Ok(())
234	}
235}
236
237impl<
238		F,
239		H,
240		HG,
241		C,
242		LHGT,
243		HGT,
244		const K: usize,
245		const N_INS: usize,
246		const N_OUTS: usize,
247		const M: usize,
248	> Clone for VAnchorCircuit<F, H, HG, C, LHGT, HGT, K, N_INS, N_OUTS, M>
249where
250	F: PrimeField,
251	H: CRH,
252	HG: CRHGadget<H, F>,
253	C: MerkleConfig,
254	LHGT: CRHGadget<C::LeafH, F>,
255	HGT: CRHGadget<C::H, F>,
256{
257	fn clone(&self) -> Self {
258		let public_amount = self.public_amount;
259		let ext_data_hash = self.ext_data_hash.clone();
260		let leaf_private_inputs = self.leaf_private_inputs.clone();
261		let leaf_public_input = self.leaf_public_input.clone();
262		let root_set = self.root_set;
263		let hasher_params_w2 = self.hasher_params_w2.clone();
264		let hasher_params_w4 = self.hasher_params_w4.clone();
265		let hasher_params_w5 = self.hasher_params_w5.clone();
266		let paths = self.paths.clone();
267		let indices = self.indices.clone();
268		let nullifier_hash = self.nullifier_hash.clone();
269		let keypair_inputs = self.keypair_inputs.clone();
270		let output_commitment = self.output_commitment.clone();
271		let out_leaf_private = self.out_leaf_private.clone();
272		let out_leaf_public = self.out_leaf_public.clone();
273		let out_pubkey = self.out_pubkey.clone();
274
275		Self::new(
276			public_amount,
277			ext_data_hash,
278			leaf_private_inputs,
279			keypair_inputs,
280			leaf_public_input,
281			root_set,
282			hasher_params_w2,
283			hasher_params_w4,
284			hasher_params_w5,
285			paths,
286			indices,
287			nullifier_hash,
288			output_commitment,
289			out_leaf_private,
290			out_leaf_public,
291			out_pubkey,
292		)
293	}
294}
295
296impl<
297		F,
298		H,
299		HG,
300		C,
301		LHGT,
302		HGT,
303		const K: usize,
304		const N_INS: usize,
305		const N_OUTS: usize,
306		const M: usize,
307	> ConstraintSynthesizer<F> for VAnchorCircuit<F, H, HG, C, LHGT, HGT, K, N_INS, N_OUTS, M>
308where
309	F: PrimeField,
310	H: CRH,
311	HG: CRHGadget<H, F>,
312	C: MerkleConfig,
313	LHGT: CRHGadget<C::LeafH, F>,
314	HGT: CRHGadget<C::H, F>,
315{
316	fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
317		let public_amount = self.public_amount;
318		let ext_data_hash = self.ext_data_hash;
319		let leaf_private = self.leaf_private_inputs; // amount, blinding
320		let keypair_inputs = self.keypair_inputs;
321		let leaf_public_input = self.leaf_public_input; // chain id
322		let root_set = self.root_set;
323		let hasher_params_w2 = self.hasher_params_w2;
324		let hasher_params_w4 = self.hasher_params_w4;
325		let hasher_params_w5 = self.hasher_params_w5;
326		let paths = self.paths;
327		let indices = self.indices;
328		let nullifier_hash = self.nullifier_hash;
329
330		let output_commitment = self.output_commitment;
331		let out_leaf_private = self.out_leaf_private;
332		let out_leaf_public = self.out_leaf_public;
333		let out_pubkey = self.out_pubkey;
334
335		// TODO: move outside the circuit
336		// 2^248
337		let limit: F = F::from_str(
338			"452312848583266388373324160190187140051835877600158453279131187530910662656",
339		)
340		.unwrap_or_default();
341		// check the previous conversion is done correctly
342		assert_ne!(limit, F::default());
343
344		// Generating vars
345		// Public inputs
346		let public_amount_var = FpVar::<F>::new_input(cs.clone(), || Ok(public_amount))?;
347		let arbitrary_input_var = ArbitraryInputVar::new_input(cs.clone(), || Ok(ext_data_hash))?;
348		let in_nullifier_var = Vec::<HG::OutputVar>::new_input(cs.clone(), || Ok(nullifier_hash))?;
349		let output_commitment_var =
350			Vec::<HG::OutputVar>::new_input(cs.clone(), || Ok(output_commitment))?;
351		let leaf_public_input_var =
352			LeafPublicInputsVar::new_input(cs.clone(), || Ok(leaf_public_input))?;
353		let root_set_var = Vec::<FpVar<F>>::new_input(cs.clone(), || Ok(root_set))?;
354
355		// Constants
356		let limit_var: FpVar<F> = FpVar::<F>::new_constant(cs.clone(), limit)?;
357		let hasher_params_w2_var = HG::ParametersVar::new_constant(cs.clone(), hasher_params_w2)?;
358		let hasher_params_w4_var = HG::ParametersVar::new_constant(cs.clone(), hasher_params_w4)?;
359		let hasher_params_w5_var = HG::ParametersVar::new_constant(cs.clone(), hasher_params_w5)?;
360
361		// Private inputs
362		let leaf_private_var =
363			Vec::<LeafPrivateInputsVar<F>>::new_witness(cs.clone(), || Ok(leaf_private))?;
364		let inkeypair_var =
365			Vec::<KeypairVar<F, H, HG>>::new_witness(cs.clone(), || Ok(keypair_inputs))?;
366		let in_path_elements_var =
367			Vec::<PathVar<F, C, HGT, LHGT, K>>::new_witness(cs.clone(), || Ok(paths))?;
368		let in_path_indices_var = Vec::<FpVar<F>>::new_witness(cs.clone(), || Ok(indices))?;
369
370		// Outputs
371		let out_leaf_private_var =
372			Vec::<LeafPrivateInputsVar<F>>::new_witness(cs.clone(), || Ok(out_leaf_private))?;
373		let out_leaf_public_var =
374			Vec::<LeafPublicInputsVar<F>>::new_witness(cs.clone(), || Ok(out_leaf_public))?;
375		let out_pubkey_var = Vec::<FpVar<F>>::new_witness(cs, || Ok(out_pubkey))?;
376
377		let set_gadget = SetGadget::new(root_set_var);
378		// verify correctness of transaction inputs
379		let sum_ins_var = Self::verify_input_var(
380			&hasher_params_w2_var,
381			&hasher_params_w4_var,
382			&hasher_params_w5_var,
383			&leaf_private_var,
384			&inkeypair_var,
385			&leaf_public_input_var,
386			&in_path_indices_var,
387			&in_path_elements_var,
388			&in_nullifier_var,
389			&set_gadget,
390		)?;
391
392		// verify correctness of transaction outputs
393		let sum_outs_var = Self::verify_output_var(
394			&hasher_params_w5_var,
395			&output_commitment_var,
396			&out_leaf_private_var,
397			&out_leaf_public_var,
398			&out_pubkey_var,
399			&limit_var,
400		)?;
401
402		// check that there are no same nullifiers among all inputs
403		Self::verify_no_same_nul(&in_nullifier_var)?;
404
405		// verify amount invariant
406		Self::verify_input_invariant(&public_amount_var, &sum_ins_var, &sum_outs_var)?;
407
408		// optional safety constraint to make sure extDataHash cannot be changed
409		arbitrary_input_var.constrain()?;
410
411		Ok(())
412	}
413}
414
415#[cfg(test)]
416mod test {
417	use ark_std::vec;
418
419	use crate::{
420		ark_std::{One, Zero},
421		setup::{common::*, vanchor::VAnchorProverBn2542x2},
422	};
423	use ark_serialize::CanonicalDeserialize;
424	use arkworks_utils::{
425		poseidon::PoseidonParameters,
426		utils::common::{
427			setup_params_x5_2, setup_params_x5_3, setup_params_x5_4, setup_params_x5_5, Curve,
428		},
429	};
430
431	use ark_bn254::{Bn254, Fr as BnFr};
432	use ark_ff::UniformRand;
433	use ark_groth16::{Groth16, Proof, VerifyingKey};
434
435	use crate::prelude::ark_std::str::FromStr;
436	use ark_snark::SNARK;
437	use ark_std::test_rng;
438
439	#[test]
440	fn should_create_proof_for_random_circuit() {
441		let rng = &mut test_rng();
442		let curve = Curve::Bn254;
443		let params2 = setup_params_x5_2::<BnFr>(curve);
444		let params3 = setup_params_x5_3::<BnFr>(curve);
445		let params4 = setup_params_x5_4::<BnFr>(curve);
446		let params5 = setup_params_x5_5::<BnFr>(curve);
447
448		// Set up a random circuit and make pk/vk pair
449		let prover = VAnchorProverBn2542x2::new(params2, params3, params4, params5);
450		let random_circuit = prover.clone().setup_random_circuit(rng).unwrap();
451		let (proving_key, verifying_key) = setup_keys::<Bn254, _, _>(random_circuit, rng).unwrap();
452
453		// Make a proof now
454		let public_amount = BnFr::from(10u32);
455		let ext_data_hash = BnFr::rand(rng);
456
457		// Input Utxos
458		let in_chain_id = BnFr::from(0u32);
459		let in_amount = BnFr::from(5u32);
460		let index = BnFr::from(0u32);
461		let in_utxo1 = prover
462			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
463			.unwrap();
464		let in_utxo2 = prover
465			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
466			.unwrap();
467		let in_utxos = [in_utxo1, in_utxo2];
468
469		// Output Utxos
470		let out_chain_id = BnFr::from(0u32);
471		let out_amount = BnFr::from(10u32);
472		let out_utxo1 = prover
473			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
474			.unwrap();
475		let out_utxo2 = prover
476			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
477			.unwrap();
478		let out_utxos = [out_utxo1, out_utxo2];
479
480		let leaf0 = in_utxo1.commitment;
481		let (in_path0, _) = prover.setup_tree(&vec![leaf0], 0).unwrap();
482		let root0 = in_path0.root_hash(&leaf0).unwrap().inner();
483		let leaf1 = in_utxo2.commitment;
484		let (in_path1, _) = prover.setup_tree(&vec![leaf1], 0).unwrap();
485		let root1 = in_path1.root_hash(&leaf1).unwrap().inner();
486
487		let in_leaves = [vec![leaf0], vec![leaf1]];
488		let in_indices = [0; 2];
489		let in_root_set = [root0, root1];
490
491		let (circuit, .., pub_ins) = prover
492			.setup_circuit_with_utxos(
493				public_amount,
494				ext_data_hash,
495				in_root_set,
496				in_indices,
497				in_leaves,
498				in_utxos,
499				out_utxos,
500			)
501			.unwrap();
502
503		let proof = prove::<Bn254, _, _>(circuit, &proving_key, rng).unwrap();
504		let res = verify::<Bn254>(&pub_ins, &verifying_key, &proof).unwrap();
505
506		assert!(res);
507	}
508
509	#[test]
510	fn should_create_circuit_and_prove_groth16_2_input_2_output() {
511		let rng = &mut test_rng();
512		let curve = Curve::Bn254;
513		let params2: PoseidonParameters<BnFr> = setup_params_x5_2(curve);
514		let params3: PoseidonParameters<BnFr> = setup_params_x5_3(curve);
515		let params4: PoseidonParameters<BnFr> = setup_params_x5_4(curve);
516		let params5: PoseidonParameters<BnFr> = setup_params_x5_5(curve);
517		let prover = VAnchorProverBn2542x2::new(params2, params3, params4, params5);
518
519		let public_amount = BnFr::from(10u32);
520		let ext_data_hash = BnFr::rand(rng);
521
522		// Input Utxos
523		let in_chain_id = BnFr::from(0u32);
524		let in_amount = BnFr::from(5u32);
525		let index = BnFr::from(0u32);
526		let in_utxo1 = prover
527			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
528			.unwrap();
529		let in_utxo2 = prover
530			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
531			.unwrap();
532		let in_utxos = [in_utxo1, in_utxo2];
533
534		// Output Utxos
535		let out_chain_id = BnFr::from(0u32);
536		let out_amount = BnFr::from(10u32);
537		let out_utxo1 = prover
538			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
539			.unwrap();
540		let out_utxo2 = prover
541			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
542			.unwrap();
543		let out_utxos = [out_utxo1, out_utxo2];
544
545		let leaf0 = in_utxo1.commitment;
546		let (in_path0, _) = prover.setup_tree(&vec![leaf0], 0).unwrap();
547		let root0 = in_path0.root_hash(&leaf0).unwrap().inner();
548		let leaf1 = in_utxo2.commitment;
549		let (in_path1, _) = prover.setup_tree(&vec![leaf1], 0).unwrap();
550		let root1 = in_path1.root_hash(&leaf1).unwrap().inner();
551
552		let in_leaves = [vec![leaf0], vec![leaf1]];
553		let in_indices = [0; 2];
554		let in_root_set = [root0, root1];
555
556		let (circuit, .., pub_ins) = prover
557			.setup_circuit_with_utxos(
558				public_amount,
559				ext_data_hash,
560				in_root_set,
561				in_indices,
562				in_leaves,
563				in_utxos,
564				out_utxos,
565			)
566			.unwrap();
567
568		let (proving_key, verifying_key) = setup_keys::<Bn254, _, _>(circuit.clone(), rng).unwrap();
569		let proof = prove::<Bn254, _, _>(circuit, &proving_key, rng).unwrap();
570		let res = verify::<Bn254>(&pub_ins, &verifying_key, &proof).unwrap();
571
572		assert!(res);
573	}
574
575	#[test]
576	fn should_fail_with_invalid_root() {
577		let rng = &mut test_rng();
578		let curve = Curve::Bn254;
579		let params2: PoseidonParameters<BnFr> = setup_params_x5_2(curve);
580		let params3: PoseidonParameters<BnFr> = setup_params_x5_3(curve);
581		let params4: PoseidonParameters<BnFr> = setup_params_x5_4(curve);
582		let params5: PoseidonParameters<BnFr> = setup_params_x5_5(curve);
583		let prover = VAnchorProverBn2542x2::new(params2, params3, params4, params5);
584
585		let public_amount = BnFr::from(10u32);
586		let ext_data_hash = BnFr::rand(rng);
587
588		// Input Utxos
589		let in_chain_id = BnFr::from(0u32);
590		let in_amount = BnFr::from(5u32);
591		let index = BnFr::from(0u32);
592		let in_utxo1 = prover
593			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
594			.unwrap();
595		let in_utxo2 = prover
596			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
597			.unwrap();
598		let in_utxos = [in_utxo1, in_utxo2];
599
600		// Output Utxos
601		let out_chain_id = BnFr::from(0u32);
602		let out_amount = BnFr::from(10u32);
603		let out_utxo1 = prover
604			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
605			.unwrap();
606		let out_utxo2 = prover
607			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
608			.unwrap();
609		let out_utxos = [out_utxo1, out_utxo2];
610
611		let leaf0 = in_utxos[0].commitment;
612		let leaf1 = in_utxos[1].commitment;
613
614		let in_leaves = [vec![leaf0], vec![leaf1]];
615		let in_indices = [0; 2];
616
617		// Invalid root set
618		let in_root_set = [BnFr::rand(rng); 2];
619
620		let (circuit, .., pub_ins) = prover
621			.setup_circuit_with_utxos(
622				public_amount,
623				ext_data_hash,
624				in_root_set,
625				in_indices,
626				in_leaves,
627				in_utxos,
628				out_utxos,
629			)
630			.unwrap();
631
632		let (proving_key, verifying_key) = setup_keys::<Bn254, _, _>(circuit.clone(), rng).unwrap();
633		let proof = prove::<Bn254, _, _>(circuit, &proving_key, rng).unwrap();
634		let res = verify::<Bn254>(&pub_ins, &verifying_key, &proof).unwrap();
635
636		assert!(!res);
637	}
638
639	#[test]
640	fn should_fail_with_invalid_nullifier() {
641		let rng = &mut test_rng();
642		let curve = Curve::Bn254;
643		let params2: PoseidonParameters<BnFr> = setup_params_x5_2(curve);
644		let params3: PoseidonParameters<BnFr> = setup_params_x5_3(curve);
645		let params4: PoseidonParameters<BnFr> = setup_params_x5_4(curve);
646		let params5: PoseidonParameters<BnFr> = setup_params_x5_5(curve);
647		let prover = VAnchorProverBn2542x2::new(params2, params3, params4, params5);
648
649		let public_amount = BnFr::from(10u32);
650		let ext_data_hash = BnFr::rand(rng);
651
652		// Input Utxos
653		let in_chain_id = BnFr::from(0u32);
654		let in_amount = BnFr::from(5u32);
655		let index = BnFr::from(0u32);
656		let mut in_utxo1 = prover
657			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
658			.unwrap();
659		let in_utxo2 = prover
660			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
661			.unwrap();
662
663		// Adding invalid nullifier
664		in_utxo1.nullifier = Some(BnFr::rand(rng));
665
666		let in_utxos = [in_utxo1, in_utxo2];
667
668		// Output Utxos
669		let out_chain_id = BnFr::from(0u32);
670		let out_amount = BnFr::from(10u32);
671		let out_utxo1 = prover
672			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
673			.unwrap();
674		let out_utxo2 = prover
675			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
676			.unwrap();
677		let out_utxos = [out_utxo1, out_utxo2];
678
679		let leaf0 = in_utxos[0].commitment;
680		let (in_path0, _) = prover.setup_tree(&vec![leaf0], 0).unwrap();
681		let root0 = in_path0.root_hash(&leaf0).unwrap().inner();
682		let leaf1 = in_utxos[1].commitment;
683		let (in_path1, _) = prover.setup_tree(&vec![leaf1], 0).unwrap();
684		let root1 = in_path1.root_hash(&leaf1).unwrap().inner();
685
686		let in_leaves = [vec![leaf0], vec![leaf1]];
687		let in_indices = [0; 2];
688		let in_root_set = [root0, root1];
689
690		let (circuit, .., pub_ins) = prover
691			.setup_circuit_with_utxos(
692				public_amount,
693				ext_data_hash,
694				in_root_set,
695				in_indices,
696				in_leaves,
697				in_utxos,
698				out_utxos,
699			)
700			.unwrap();
701
702		let (proving_key, verifying_key) = setup_keys::<Bn254, _, _>(circuit.clone(), rng).unwrap();
703		let proof = prove::<Bn254, _, _>(circuit, &proving_key, rng).unwrap();
704		let res = verify::<Bn254>(&pub_ins, &verifying_key, &proof).unwrap();
705
706		assert!(!res);
707	}
708
709	#[test]
710	#[ignore]
711	fn should_fail_with_same_nullifier() {
712		let rng = &mut test_rng();
713		let curve = Curve::Bn254;
714		let params2: PoseidonParameters<BnFr> = setup_params_x5_2(curve);
715		let params3: PoseidonParameters<BnFr> = setup_params_x5_3(curve);
716		let params4: PoseidonParameters<BnFr> = setup_params_x5_4(curve);
717		let params5: PoseidonParameters<BnFr> = setup_params_x5_5(curve);
718		let prover = VAnchorProverBn2542x2::new(params2, params3, params4, params5);
719
720		let public_amount = BnFr::from(0u32);
721		let ext_data_hash = BnFr::rand(rng);
722
723		// Input Utxos
724		let in_chain_id = BnFr::from(0u32);
725		let in_amount = BnFr::from(5u32);
726		let index = BnFr::from(0u32);
727		let in_utxo1 = prover
728			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
729			.unwrap();
730
731		// Both inputs are the same -- attempt of double spending
732		let in_utxos = [in_utxo1, in_utxo1];
733
734		// Output Utxos
735		let out_chain_id = BnFr::from(0u32);
736		let out_amount = BnFr::from(10u32);
737		let out_utxo1 = prover
738			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
739			.unwrap();
740		let out_utxo2 = prover
741			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
742			.unwrap();
743		let out_utxos = [out_utxo1, out_utxo2];
744
745		let leaf0 = in_utxos[0].commitment;
746		let (in_path0, _) = prover.setup_tree(&vec![leaf0], 0).unwrap();
747		let root0 = in_path0.root_hash(&leaf0).unwrap().inner();
748		let leaf1 = in_utxos[1].commitment;
749		let (in_path1, _) = prover.setup_tree(&vec![leaf1], 0).unwrap();
750		let root1 = in_path1.root_hash(&leaf1).unwrap().inner();
751
752		let in_leaves = [vec![leaf0], vec![leaf1]];
753		let in_indices = [0; 2];
754		let in_root_set = [root0, root1];
755
756		let (circuit, .., pub_ins) = prover
757			.setup_circuit_with_utxos(
758				public_amount,
759				ext_data_hash,
760				in_root_set,
761				in_indices,
762				in_leaves,
763				in_utxos,
764				out_utxos,
765			)
766			.unwrap();
767
768		let (proving_key, verifying_key) = setup_keys::<Bn254, _, _>(circuit.clone(), rng).unwrap();
769		let proof = prove::<Bn254, _, _>(circuit, &proving_key, rng).unwrap();
770		let res = verify::<Bn254>(&pub_ins, &verifying_key, &proof).unwrap();
771
772		assert!(!res);
773	}
774
775	#[test]
776	fn should_fail_with_inconsistent_input_output_values() {
777		let rng = &mut test_rng();
778		let curve = Curve::Bn254;
779		let params2: PoseidonParameters<BnFr> = setup_params_x5_2(curve);
780		let params3: PoseidonParameters<BnFr> = setup_params_x5_3(curve);
781		let params4: PoseidonParameters<BnFr> = setup_params_x5_4(curve);
782		let params5: PoseidonParameters<BnFr> = setup_params_x5_5(curve);
783		let prover = VAnchorProverBn2542x2::new(params2, params3, params4, params5);
784
785		let public_amount = BnFr::from(10u32);
786		let ext_data_hash = BnFr::rand(rng);
787
788		// Input Utxos
789		let in_chain_id = BnFr::from(0u32);
790		// Input amount too high
791		let in_amount = BnFr::from(10u32);
792		let index = BnFr::from(0u32);
793		let in_utxo1 = prover
794			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
795			.unwrap();
796		let in_utxo2 = prover
797			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
798			.unwrap();
799		let in_utxos = [in_utxo1, in_utxo2];
800
801		// Output Utxos
802		let out_chain_id = BnFr::from(0u32);
803		let out_amount = BnFr::from(10u32);
804		let out_utxo1 = prover
805			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
806			.unwrap();
807		let out_utxo2 = prover
808			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
809			.unwrap();
810		let out_utxos = [out_utxo1, out_utxo2];
811
812		let leaf0 = in_utxos[0].commitment;
813		let (in_path0, _) = prover.setup_tree(&vec![leaf0], 0).unwrap();
814		let root0 = in_path0.root_hash(&leaf0).unwrap().inner();
815		let leaf1 = in_utxos[1].commitment;
816		let (in_path1, _) = prover.setup_tree(&vec![leaf1], 0).unwrap();
817		let root1 = in_path1.root_hash(&leaf1).unwrap().inner();
818
819		let in_leaves = [vec![leaf0], vec![leaf1]];
820		let in_indices = [0; 2];
821		let in_root_set = [root0, root1];
822
823		let (circuit, .., pub_ins) = prover
824			.setup_circuit_with_utxos(
825				public_amount,
826				ext_data_hash,
827				in_root_set,
828				in_indices,
829				in_leaves,
830				in_utxos,
831				out_utxos,
832			)
833			.unwrap();
834
835		let (proving_key, verifying_key) = setup_keys::<Bn254, _, _>(circuit.clone(), rng).unwrap();
836		let proof = prove::<Bn254, _, _>(circuit, &proving_key, rng).unwrap();
837		let res = verify::<Bn254>(&pub_ins, &verifying_key, &proof).unwrap();
838
839		assert!(!res);
840	}
841
842	#[test]
843	fn should_fail_with_big_amount() {
844		let rng = &mut test_rng();
845		let curve = Curve::Bn254;
846		let params2: PoseidonParameters<BnFr> = setup_params_x5_2(curve);
847		let params3: PoseidonParameters<BnFr> = setup_params_x5_3(curve);
848		let params4: PoseidonParameters<BnFr> = setup_params_x5_4(curve);
849		let params5: PoseidonParameters<BnFr> = setup_params_x5_5(curve);
850		let prover = VAnchorProverBn2542x2::new(params2, params3, params4, params5);
851
852		// 2^248
853		let limit = BnFr::from_str(
854			"452312848583266388373324160190187140051835877600158453279131187530910662656",
855		)
856		.unwrap();
857
858		let public_amount = BnFr::zero();
859		let ext_data_hash = BnFr::rand(rng);
860
861		// Input Utxos
862		let in_chain_id = BnFr::from(0u32);
863		let in_amount = BnFr::from(limit + BnFr::one());
864		let index = BnFr::from(0u32);
865		let in_utxo1 = prover
866			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
867			.unwrap();
868		let in_utxo2 = prover
869			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
870			.unwrap();
871		let in_utxos = [in_utxo1, in_utxo2];
872
873		// Output Utxos
874		let out_chain_id = BnFr::from(0u32);
875		let out_amount = BnFr::from(10u32);
876		let out_utxo1 = prover
877			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
878			.unwrap();
879		let out_utxo2 = prover
880			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
881			.unwrap();
882		let out_utxos = [out_utxo1, out_utxo2];
883
884		let leaf0 = in_utxos[0].commitment;
885		let (in_path0, _) = prover.setup_tree(&vec![leaf0], 0).unwrap();
886		let root0 = in_path0.root_hash(&leaf0).unwrap().inner();
887		let leaf1 = in_utxos[1].commitment;
888		let (in_path1, _) = prover.setup_tree(&vec![leaf1], 0).unwrap();
889		let root1 = in_path1.root_hash(&leaf1).unwrap().inner();
890
891		let in_leaves = [vec![leaf0], vec![leaf1]];
892		let in_indices = [0; 2];
893		let in_root_set = [root0, root1];
894
895		let (circuit, .., pub_ins) = prover
896			.setup_circuit_with_utxos(
897				public_amount,
898				ext_data_hash,
899				in_root_set,
900				in_indices,
901				in_leaves,
902				in_utxos,
903				out_utxos,
904			)
905			.unwrap();
906
907		let (proving_key, verifying_key) = setup_keys::<Bn254, _, _>(circuit.clone(), rng).unwrap();
908		let proof = prove::<Bn254, _, _>(circuit, &proving_key, rng).unwrap();
909		let res = verify::<Bn254>(&pub_ins, &verifying_key, &proof).unwrap();
910
911		assert!(!res);
912	}
913
914	#[test]
915	fn should_fail_with_invalid_public_input() {
916		let rng = &mut test_rng();
917		let curve = Curve::Bn254;
918		let params2: PoseidonParameters<BnFr> = setup_params_x5_2(curve);
919		let params3: PoseidonParameters<BnFr> = setup_params_x5_3(curve);
920		let params4: PoseidonParameters<BnFr> = setup_params_x5_4(curve);
921		let params5: PoseidonParameters<BnFr> = setup_params_x5_5(curve);
922		let prover = VAnchorProverBn2542x2::new(params2, params3, params4, params5);
923
924		let public_amount = BnFr::from(0u32);
925		let ext_data_hash = BnFr::rand(rng);
926
927		// Input Utxos
928		let in_chain_id = BnFr::from(0u32);
929		let in_amount = BnFr::from(5u32);
930		let index = BnFr::from(0u32);
931		let in_utxo1 = prover
932			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
933			.unwrap();
934		let in_utxo2 = prover
935			.new_utxo(in_chain_id, in_amount, Some(index), None, None, rng)
936			.unwrap();
937		let in_utxos = [in_utxo1, in_utxo2];
938
939		// Output Utxos
940		let out_chain_id = BnFr::from(0u32);
941		let out_amount = BnFr::from(10u32);
942		let out_utxo1 = prover
943			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
944			.unwrap();
945		let out_utxo2 = prover
946			.new_utxo(out_chain_id, out_amount, None, None, None, rng)
947			.unwrap();
948		let out_utxos = [out_utxo1, out_utxo2];
949
950		let leaf0 = in_utxos[0].commitment;
951		let (in_path0, _) = prover.setup_tree(&vec![leaf0], 0).unwrap();
952		let root0 = in_path0.root_hash(&leaf0).unwrap().inner();
953		let leaf1 = in_utxos[1].commitment;
954		let (in_path1, _) = prover.setup_tree(&vec![leaf1], 0).unwrap();
955		let root1 = in_path1.root_hash(&leaf1).unwrap().inner();
956
957		let in_leaves = [vec![leaf0], vec![leaf1]];
958		let in_indices = [0; 2];
959		let in_root_set = [root0, root1];
960
961		let (circuit, .., pub_ins) = prover
962			.setup_circuit_with_utxos(
963				public_amount,
964				ext_data_hash,
965				in_root_set,
966				in_indices,
967				in_leaves,
968				in_utxos,
969				out_utxos,
970			)
971			.unwrap();
972
973		let truncated_public_inputs = &pub_ins[2..];
974		let (proving_key, verifying_key) = setup_keys::<Bn254, _, _>(circuit.clone(), rng).unwrap();
975		let proof = prove::<Bn254, _, _>(circuit, &proving_key, rng).unwrap();
976
977		let vk = VerifyingKey::<Bn254>::deserialize(&verifying_key[..]).unwrap();
978		let proof = Proof::<Bn254>::deserialize(&proof[..]).unwrap();
979		let res = Groth16::<Bn254>::verify(&vk, truncated_public_inputs, &proof);
980
981		assert!(res.is_err());
982	}
983}