arkworks_r1cs_circuits/
anchor.rs

1// This file is part of Webb.
2
3// Copyright (C) 2021 Webb Technologies Inc.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! The Anchor is a cross-chain fixed sized deposit/withdraw shielded pool.
19//! It allows for users to deposit tokens on one chain and withdraw in another
20//! one without linking the deposit to the withdrawal.
21
22//! We will take inputs and do a merkle tree reconstruction for each node in the
23//! path and check if the reconstructed root is inside the current root set.
24//!
25//! This is the Groth16 setup implementation of Anchor
26use ark_ff::fields::PrimeField;
27use ark_r1cs_std::{eq::EqGadget, fields::fp::FpVar, prelude::*};
28use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError};
29use ark_std::vec::Vec;
30use arkworks_native_gadgets::merkle_tree::Path;
31use arkworks_r1cs_gadgets::{merkle_tree::PathVar, poseidon::FieldHasherGadget, set::SetGadget};
32
33/// Defines an `AnchorCircuit` struct that hold all the information thats needed
34/// to verify the following statements:
35/// * Alice knows a witness tuple `(secret, nullifier, merklePath)`
36/// and a commitment `Hash(chain_id, nullifier, secret)` stored in one of the
37/// Anchor merkle trees,
38/// * The Anchor smart contract / end-user application hasn't seen this
39///   `nullifier_hash` before.
40///
41/// Needs to implement `ConstraintSynthesizer` and a
42/// constructor to generate proper constraints
43#[derive(Clone)]
44pub struct AnchorCircuit<F: PrimeField, HG: FieldHasherGadget<F>, const N: usize, const M: usize> {
45	// Represents the hash of
46	// recepient + relayer + fee + refunds + commitment
47	arbitrary_input: F,
48	// secret
49	secret: F,
50	// nullifier to prevent double spending
51	nullifier: F,
52	// source chain_id
53	chain_id: F,
54	// Merkle root set to use on one-of-many proof
55	root_set: [F; M],
56	// Merkle path to transaction
57	path: Path<F, HG::Native, N>,
58	nullifier_hash: F,
59	// 3 input hasher
60	hasher3: HG::Native,
61	// 4 input hasher
62	hasher4: HG::Native,
63}
64
65/// A constructor for the `AnchorCircuit`
66impl<F, HG, const N: usize, const M: usize> AnchorCircuit<F, HG, N, M>
67where
68	F: PrimeField,
69	HG: FieldHasherGadget<F>,
70{
71	#[allow(clippy::too_many_arguments)]
72	pub fn new(
73		arbitrary_input: F,
74		secret: F,
75		nullifier: F,
76		chain_id: F,
77		root_set: [F; M],
78		path: Path<F, HG::Native, N>,
79		nullifier_hash: F,
80		hasher3: HG::Native,
81		hasher4: HG::Native,
82	) -> Self {
83		Self {
84			arbitrary_input,
85			secret,
86			nullifier,
87			chain_id,
88			root_set,
89			path,
90			nullifier_hash,
91			hasher3,
92			hasher4,
93		}
94	}
95}
96
97/// Implementation of the `ConstraintSynthesizer` trait for the `AnchorCircuit`
98/// https://github.com/arkworks-rs/snark/blob/master/relations/src/r1cs/constraint_system.rs
99///
100/// This is the main function that is called by the `R1CS` library to generate
101/// the constraints for the `AnchorCircuit`.
102impl<F, HG, const N: usize, const M: usize> ConstraintSynthesizer<F> for AnchorCircuit<F, HG, N, M>
103where
104	F: PrimeField,
105	HG: FieldHasherGadget<F>,
106{
107	fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
108		let arbitrary_input = self.arbitrary_input;
109		let secret = self.secret;
110		let nullifier = self.nullifier;
111		let chain_id = self.chain_id;
112		let root_set = self.root_set;
113		let path = self.path;
114		let nullifier_hash = self.nullifier_hash;
115
116		// Generating vars
117		// Public inputs
118		let nullifier_hash_var = FpVar::<F>::new_input(cs.clone(), || Ok(nullifier_hash))?;
119		let arbitrary_input_var = FpVar::<F>::new_input(cs.clone(), || Ok(arbitrary_input))?;
120		let chain_id_var = FpVar::<F>::new_input(cs.clone(), || Ok(chain_id))?;
121		let roots_var = Vec::<FpVar<F>>::new_input(cs.clone(), || Ok(root_set))?;
122
123		// Hashers
124		let hasher3_gadget: HG =
125			FieldHasherGadget::<F>::from_native(&mut cs.clone(), self.hasher3)?;
126		let hasher4_gadget: HG =
127			FieldHasherGadget::<F>::from_native(&mut cs.clone(), self.hasher4)?;
128
129		// Private inputs
130		let secret_var = FpVar::<F>::new_witness(cs.clone(), || Ok(secret))?;
131		let nullifier_var = FpVar::<F>::new_witness(cs.clone(), || Ok(nullifier))?;
132		let path_var = PathVar::<F, HG, N>::new_witness(cs.clone(), || Ok(path))?;
133
134		// Creating the leaf and checking the membership inside the tree
135		let anchor_leaf =
136			hasher4_gadget.hash(&[chain_id_var, nullifier_var.clone(), secret_var])?;
137		let anchor_nullifier = hasher3_gadget.hash_two(&nullifier_var, &nullifier_var)?;
138		let root_var = path_var.root_hash(&anchor_leaf, &hasher3_gadget)?;
139		// Check if target root is in set
140		let set_gadget = SetGadget::new(roots_var);
141		let is_set_member = set_gadget.check_membership(&root_var)?;
142		// Constraining arbitrary inputs
143		let _ = &arbitrary_input_var * &arbitrary_input_var;
144
145		// Enforcing constraints
146		is_set_member.enforce_equal(&Boolean::TRUE)?;
147		anchor_nullifier.enforce_equal(&nullifier_hash_var)?;
148
149		Ok(())
150	}
151}