arkworks_circuits/setup/
vanchor.rs

1use crate::{
2	circuit::vanchor::VAnchorCircuit as VACircuit,
3	setup::common::{
4		LeafCRHGadget, PoseidonCRH_x5_2, PoseidonCRH_x5_2Gadget, PoseidonCRH_x5_3Gadget,
5		PoseidonCRH_x5_4, TreeConfig_x5, Tree_x5,
6	},
7};
8use ark_bn254::Fr as Bn254Fr;
9use ark_crypto_primitives::Error;
10use ark_ff::PrimeField;
11use ark_std::{error::Error as ArkError, rand::RngCore, rc::Rc, string::ToString, vec::Vec};
12use arkworks_gadgets::{
13	arbitrary::vanchor_data::VAnchorArbitraryData,
14	keypair::vanchor::Keypair,
15	leaf::vanchor::{Private as LeafPrivateInput, Public as LeafPublicInput, VAnchorLeaf as Leaf},
16	merkle_tree::Path,
17};
18use arkworks_utils::{
19	poseidon::PoseidonParameters,
20	utils::common::{
21		setup_params_x5_2, setup_params_x5_3, setup_params_x5_4, setup_params_x5_5, Curve,
22	},
23};
24
25pub fn get_hash_params<F: PrimeField>(
26	curve: Curve,
27) -> (
28	PoseidonParameters<F>,
29	PoseidonParameters<F>,
30	PoseidonParameters<F>,
31	PoseidonParameters<F>,
32) {
33	(
34		setup_params_x5_2::<F>(curve),
35		setup_params_x5_3::<F>(curve),
36		setup_params_x5_4::<F>(curve),
37		setup_params_x5_5::<F>(curve),
38	)
39}
40
41#[derive(Debug)]
42pub enum UtxoError {
43	NullifierNotCalculated,
44}
45
46impl core::fmt::Display for UtxoError {
47	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
48		let msg = match self {
49			UtxoError::NullifierNotCalculated => "Nullifier not calculated".to_string(),
50		};
51		write!(f, "{}", msg)
52	}
53}
54
55impl ArkError for UtxoError {}
56
57#[derive(Clone, Copy)]
58pub struct Utxo<F: PrimeField> {
59	pub chain_id: F,
60	pub amount: F,
61	pub keypair: Keypair<F, PoseidonCRH_x5_2<F>>,
62	pub leaf_private: LeafPrivateInput<F>,
63	pub leaf_public: LeafPublicInput<F>,
64	pub index: Option<F>,
65	pub nullifier: Option<F>,
66	pub commitment: F,
67}
68
69impl<F: PrimeField> Utxo<F> {
70	pub fn new<R: RngCore>(
71		chain_id: F,
72		amount: F,
73		index: Option<F>,
74		private_key: Option<F>,
75		blinding: Option<F>,
76		params2: &PoseidonParameters<F>,
77		params4: &PoseidonParameters<F>,
78		params5: &PoseidonParameters<F>,
79		rng: &mut R,
80	) -> Result<Self, Error> {
81		let blinding = blinding.unwrap_or(F::rand(rng));
82		let private_input = LeafPrivateInput::<F>::new(amount, blinding);
83		let public_input = LeafPublicInput::<F>::new(chain_id);
84
85		let keypair = Keypair::new(private_key.unwrap_or(F::rand(rng)));
86		let pub_key = keypair.public_key(params2)?;
87
88		let leaf = Leaf::<F, PoseidonCRH_x5_4<F>>::create_leaf(
89			&private_input,
90			&public_input,
91			&pub_key,
92			&params5,
93		)?;
94
95		let nullifier = if index.is_some() {
96			let i = index.unwrap();
97
98			let signature = keypair.signature(&leaf, &i, params4)?;
99
100			let nullifier =
101				Leaf::<_, PoseidonCRH_x5_4<F>>::create_nullifier(&signature, &leaf, &params4, &i)?;
102
103			Some(nullifier)
104		} else {
105			None
106		};
107
108		Ok(Self {
109			chain_id,
110			amount,
111			keypair,
112			leaf_private: private_input,
113			leaf_public: public_input,
114			index,
115			nullifier,
116			commitment: leaf,
117		})
118	}
119
120	pub fn get_nullifier(&self) -> Result<F, Error> {
121		self.nullifier
122			.ok_or(UtxoError::NullifierNotCalculated.into())
123	}
124}
125
126#[derive(Clone)]
127pub struct VAnchorProverSetup<
128	F: PrimeField,
129	const TREE_DEPTH: usize,
130	const M: usize,
131	const INS: usize,
132	const OUTS: usize,
133> {
134	params2: PoseidonParameters<F>,
135	params3: PoseidonParameters<F>,
136	params4: PoseidonParameters<F>,
137	params5: PoseidonParameters<F>,
138}
139
140impl<
141		F: PrimeField,
142		const TREE_DEPTH: usize,
143		const M: usize,
144		const INS: usize,
145		const OUTS: usize,
146	> VAnchorProverSetup<F, TREE_DEPTH, M, INS, OUTS>
147{
148	pub fn new(
149		params2: PoseidonParameters<F>,
150		params3: PoseidonParameters<F>,
151		params4: PoseidonParameters<F>,
152		params5: PoseidonParameters<F>,
153	) -> Self {
154		Self {
155			params2,
156			params3,
157			params4,
158			params5,
159		}
160	}
161
162	pub fn new_utxo<R: RngCore>(
163		&self,
164		chain_id: F,
165		amount: F,
166		index: Option<F>,
167		secret_key: Option<F>,
168		blinding: Option<F>,
169		rng: &mut R,
170	) -> Result<Utxo<F>, Error> {
171		Utxo::new(
172			chain_id,
173			amount,
174			index,
175			secret_key,
176			blinding,
177			&self.params2,
178			&self.params4,
179			&self.params5,
180			rng,
181		)
182	}
183
184	pub fn setup_random_circuit<R: RngCore>(
185		self,
186		rng: &mut R,
187	) -> Result<
188		VACircuit<
189			F,
190			PoseidonCRH_x5_2<F>,
191			PoseidonCRH_x5_2Gadget<F>,
192			TreeConfig_x5<F>,
193			LeafCRHGadget<F>,
194			PoseidonCRH_x5_3Gadget<F>,
195			TREE_DEPTH,
196			INS,
197			OUTS,
198			M,
199		>,
200		Error,
201	> {
202		let public_amount = F::rand(rng);
203		let ext_data_hash = F::rand(rng);
204		let in_root_set = [F::rand(rng); M];
205		let in_leaves = [F::rand(rng); INS].map(|x| vec![x]);
206		let in_indices = [0; INS];
207
208		let chain_id = F::rand(rng);
209		let amount = F::rand(rng);
210		let index = F::rand(rng);
211		let secret_key = F::rand(rng);
212		let blinding = F::rand(rng);
213
214		let in_utxo = self.new_utxo(
215			chain_id,
216			amount,
217			Some(index),
218			Some(secret_key),
219			Some(blinding),
220			rng,
221		)?;
222		let in_utxos = [in_utxo; INS];
223		let out_utxo = self.new_utxo(
224			chain_id,
225			amount,
226			None,
227			Some(secret_key),
228			Some(blinding),
229			rng,
230		)?;
231		let out_utxos = [out_utxo; OUTS];
232
233		let (circuit, ..) = self.setup_circuit_with_utxos(
234			public_amount,
235			ext_data_hash,
236			in_root_set,
237			in_indices,
238			in_leaves,
239			in_utxos,
240			out_utxos,
241		)?;
242
243		Ok(circuit)
244	}
245
246	pub fn setup_circuit_with_utxos(
247		self,
248		// External data
249		public_amount: F,
250		ext_data_hash: F,
251		in_root_set: [F; M],
252		in_indices: [u64; INS],
253		in_leaves: [Vec<F>; INS],
254		// Input transactions
255		in_utxos: [Utxo<F>; INS],
256		// Output transactions
257		out_utxos: [Utxo<F>; OUTS],
258	) -> Result<
259		(
260			VACircuit<
261				F,
262				PoseidonCRH_x5_2<F>,
263				PoseidonCRH_x5_2Gadget<F>,
264				TreeConfig_x5<F>,
265				LeafCRHGadget<F>,
266				PoseidonCRH_x5_3Gadget<F>,
267				TREE_DEPTH,
268				INS,
269				OUTS,
270				M,
271			>,
272			Vec<F>,
273		),
274		Error,
275	> {
276		// Tree + set for proving input txos
277		let in_indices_f = in_indices.map(|x| F::from(x));
278		let mut in_paths = Vec::new();
279		for i in 0..INS {
280			let (in_path, _) = self.setup_tree(&in_leaves[i], in_indices[i])?;
281			in_paths.push(in_path)
282		}
283		// Arbitrary data
284		let arbitrary_data = Self::setup_arbitrary_data(ext_data_hash);
285
286		let circuit = self.setup_circuit(
287			public_amount,
288			arbitrary_data,
289			in_utxos.clone(),
290			in_indices_f,
291			in_paths,
292			in_root_set,
293			out_utxos.clone(),
294		)?;
295
296		let in_nullifiers: Result<Vec<F>, Error> =
297			in_utxos.iter().map(|x| x.get_nullifier()).collect();
298		let out_nullifiers = out_utxos.iter().map(|x| x.commitment).collect::<Vec<F>>();
299		let public_inputs = Self::construct_public_inputs(
300			in_utxos[0].leaf_public.chain_id,
301			public_amount,
302			in_root_set.to_vec(),
303			in_nullifiers?,
304			out_nullifiers,
305			ext_data_hash,
306		);
307
308		Ok((circuit, public_inputs))
309	}
310
311	pub fn setup_circuit(
312		self,
313		public_amount: F,
314		arbitrary_data: VAnchorArbitraryData<F>,
315		// Input transactions
316		in_utxos: [Utxo<F>; INS],
317		// Data related to tree
318		in_indicies: [F; INS],
319		in_paths: Vec<Path<TreeConfig_x5<F>, TREE_DEPTH>>,
320		in_root_set: [F; M],
321		// Output transactions
322		out_utxos: [Utxo<F>; OUTS],
323	) -> Result<
324		VACircuit<
325			F,
326			PoseidonCRH_x5_2<F>,
327			PoseidonCRH_x5_2Gadget<F>,
328			TreeConfig_x5<F>,
329			LeafCRHGadget<F>,
330			PoseidonCRH_x5_3Gadget<F>,
331			TREE_DEPTH,
332			INS,
333			OUTS,
334			M,
335		>,
336		Error,
337	> {
338		let in_leaf_private_inputs = in_utxos
339			.iter()
340			.map(|x| x.leaf_private.clone())
341			.collect::<Vec<LeafPrivateInput<F>>>();
342		let in_keypair_inputs = in_utxos
343			.iter()
344			.map(|x| x.keypair.clone())
345			.collect::<Vec<Keypair<F, PoseidonCRH_x5_2<F>>>>();
346		let in_nullifiers: Result<Vec<F>, Error> =
347			in_utxos.iter().map(|x| x.get_nullifier()).collect();
348
349		let out_pub_keys: Result<Vec<F>, _> = out_utxos
350			.iter()
351			.map(|x| x.keypair.public_key(&self.params2))
352			.collect();
353		let out_commitments = out_utxos.iter().map(|x| x.commitment).collect::<Vec<F>>();
354		let out_leaf_private = out_utxos
355			.iter()
356			.map(|x| x.leaf_private.clone())
357			.collect::<Vec<LeafPrivateInput<F>>>();
358		let out_leaf_public = out_utxos
359			.iter()
360			.map(|x| x.leaf_public.clone())
361			.collect::<Vec<LeafPublicInput<F>>>();
362		let circuit = VACircuit::<
363			F,
364			PoseidonCRH_x5_2<F>,
365			PoseidonCRH_x5_2Gadget<F>,
366			TreeConfig_x5<F>,
367			LeafCRHGadget<F>,
368			PoseidonCRH_x5_3Gadget<F>,
369			TREE_DEPTH,
370			INS,
371			OUTS,
372			M,
373		>::new(
374			public_amount,
375			arbitrary_data,
376			in_leaf_private_inputs,
377			in_keypair_inputs,
378			in_utxos[0].leaf_public.clone(),
379			in_root_set,
380			self.params2,
381			self.params4,
382			self.params5,
383			in_paths,
384			in_indicies.to_vec(),
385			in_nullifiers?,
386			out_commitments,
387			out_leaf_private,
388			out_leaf_public,
389			out_pub_keys?,
390		);
391
392		Ok(circuit)
393	}
394
395	pub fn setup_keypairs<R: RngCore, const N: usize>(
396		rng: &mut R,
397	) -> [Keypair<F, PoseidonCRH_x5_2<F>>; N] {
398		[(); N].map(|_| Keypair::<_, PoseidonCRH_x5_2<F>>::new(F::rand(rng)))
399	}
400
401	pub fn setup_tree(
402		&self,
403		leaves: &[F],
404		index: u64,
405	) -> Result<(Path<TreeConfig_x5<F>, TREE_DEPTH>, F), Error> {
406		let inner_params = Rc::new(self.params3.clone());
407		let tree = Tree_x5::new_sequential(inner_params, Rc::new(()), &leaves.to_vec())?;
408		let root = tree.root();
409		let path = tree.generate_membership_proof::<TREE_DEPTH>(index);
410
411		Ok((path, root.inner()))
412	}
413
414	pub fn construct_public_inputs(
415		chain_id: F,
416		public_amount: F,
417		roots: Vec<F>,
418		nullifiers: Vec<F>,
419		commitments: Vec<F>,
420		ext_data_hash: F,
421	) -> Vec<F> {
422		let mut public_inputs = vec![public_amount, ext_data_hash];
423		public_inputs.extend(nullifiers);
424		public_inputs.extend(commitments);
425		public_inputs.push(chain_id);
426		public_inputs.extend(roots);
427
428		public_inputs
429	}
430
431	// NOTE: To be used for testing
432	pub fn deconstruct_public_inputs(
433		public_inputs: Vec<F>,
434	) -> Result<
435		(
436			F,      // Chain Id
437			F,      // Public amount
438			Vec<F>, // Roots
439			Vec<F>, // Input tx Nullifiers
440			Vec<F>, // Output tx commitments
441			F,      // External data hash
442		),
443		Error,
444	> {
445		let mut pub_ins = public_inputs;
446
447		let mut root_set = Vec::new();
448		for _ in 0..INS {
449			root_set.push(pub_ins.pop().unwrap());
450		}
451
452		let chain_id = pub_ins.pop().unwrap();
453
454		let mut out_commitments = Vec::new();
455		for _ in 0..OUTS {
456			out_commitments.push(pub_ins.pop().unwrap());
457		}
458
459		let mut in_nullifiers = Vec::new();
460		for _ in 0..INS {
461			in_nullifiers.push(pub_ins.pop().unwrap());
462		}
463
464		let ext_data_hash = pub_ins.pop().unwrap();
465		let public_amount = pub_ins.pop().unwrap();
466
467		Ok((
468			chain_id,
469			public_amount,
470			root_set,
471			in_nullifiers,
472			out_commitments,
473			ext_data_hash,
474		))
475	}
476
477	pub fn setup_arbitrary_data(ext_data: F) -> VAnchorArbitraryData<F> {
478		VAnchorArbitraryData::new(ext_data)
479	}
480}
481
482// const TREE_DEPTH: usize = 30;
483// const M: usize = 2;
484// const INS: usize = 2;
485// const OUTS: usize = 2;
486pub type VAnchorProverBn2542x2 = VAnchorProverSetup<Bn254Fr, 30, 2, 2, 2>;
487
488// For backwards compatability
489// TODO: remove later
490pub fn setup_vanchor_arbitrary_data<F: PrimeField>(ext_data: F) -> VAnchorArbitraryData<F> {
491	VAnchorArbitraryData::new(ext_data)
492}