1 #ifndef __ZETH_CIRCUITS_NOTE_TCC__
2 #define __ZETH_CIRCUITS_NOTE_TCC__
5 // Content Taken and adapted from Zcash
6 // https://github.com/zcash/zcash/blob/master/src/zcash/circuit/note.tcc
8 #include "libzeth/circuits/notes/note.hpp"
13 template<typename FieldT>
14 note_gadget<FieldT>::note_gadget(
15 libsnark::protoboard<FieldT> &pb, const std::string &annotation_prefix)
16 : libsnark::gadget<FieldT>(pb, annotation_prefix)
21 FMT(this->annotation_prefix, " value")); // ZETH_V_SIZE = 64
25 FMT(this->annotation_prefix, " r")); // ZETH_R_SIZE = 256
28 template<typename FieldT> void note_gadget<FieldT>::generate_r1cs_constraints()
30 for (size_t i = 0; i < ZETH_V_SIZE; i++) {
31 libsnark::generate_boolean_r1cs_constraint<FieldT>(
32 this->pb, value[i], FMT(this->annotation_prefix, " value[%zu]", i));
35 for (size_t i = 0; i < ZETH_R_SIZE; i++) {
36 libsnark::generate_boolean_r1cs_constraint<FieldT>(
37 this->pb, r[i], FMT(this->annotation_prefix, " r[%zu]", i));
41 template<typename FieldT>
42 void note_gadget<FieldT>::generate_r1cs_witness(const zeth_note ¬e)
44 note.r.fill_pb_variable_array(this->pb, r);
45 note.value.fill_pb_variable_array(this->pb, value);
48 // Gadget that makes sure that all conditions are met in order to spend a note:
49 // - The nullifier is correctly computed from a_sk and rho
50 // - The commitment cm is correctly computed from the coin's data
51 // - commitment cm is in the tree of merkle root rt
52 template<typename FieldT, typename HashT, typename HashTreeT, size_t TreeDepth>
53 input_note_gadget<FieldT, HashT, HashTreeT, TreeDepth>::input_note_gadget(
54 libsnark::protoboard<FieldT> &pb,
55 const libsnark::pb_variable<FieldT> &ZERO,
56 std::shared_ptr<libsnark::digest_variable<FieldT>> a_sk,
57 std::shared_ptr<libsnark::digest_variable<FieldT>> nullifier,
58 const libsnark::pb_variable<FieldT> &rt,
59 const std::string &annotation_prefix)
60 : note_gadget<FieldT>(pb, annotation_prefix)
62 // ZETH_RHO_SIZE = 256
63 rho.allocate(pb, ZETH_RHO_SIZE, " rho");
64 address_bits_va.allocate(
65 pb, TreeDepth, FMT(this->annotation_prefix, " merkle_tree_depth"));
66 a_pk.reset(new libsnark::digest_variable<FieldT>(
67 pb, HashT::get_digest_len(), FMT(this->annotation_prefix, " a_pk")));
68 commitment.allocate(pb, FMT(this->annotation_prefix, " commitment"));
70 libsnark::pb_variable_array<FieldT> *pb_auth_path =
71 new libsnark::pb_variable_array<FieldT>();
72 pb_auth_path->allocate(
73 pb, TreeDepth, FMT(this->annotation_prefix, " authentication_path"));
74 auth_path.reset(pb_auth_path);
76 // Call to the "PRF_addr_a_pk_gadget" to make sure a_pk is correctly
78 spend_authority.reset(
79 new PRF_addr_a_pk_gadget<FieldT, HashT>(pb, ZERO, a_sk->bits, a_pk));
81 // Call to the "PRF_nf_gadget" to make sure the nullifier is correctly
82 // computed from a_sk and rho
83 expose_nullifiers.reset(
84 new PRF_nf_gadget<FieldT, HashT>(pb, ZERO, a_sk->bits, rho, nullifier));
86 // Below this point, we need to do several calls
87 // to the commitment gagdets.
89 // Call to the "note_commitment_gadget" to make sure that the
90 // commitment cm is computed correctly from the coin data
91 // ie: a_pk, value, rho, and trap_r#include "circuits/notes/note.tcc"
93 // This gadget compute the commitment cm (coin commitment)
95 // Note: In our case it can be useful to retrieve the commitment k if we
96 // want to implement the mint function the same way as it is done in
97 // Zerocash. That way we only need to provide k along with the value when we
98 // deposit onto the mixer. Doing so removes the need to generate a proof
99 // when we deposit However, this comes with a drawback of introducing
100 // different types of function calls on the smart contract and also requires
101 // additional steps/function calls to "pour"/split the newly created
102 // commitment corresponding to a coin of value V, into a set of commitments
103 // corresponding to coins of value v_i such that Sum_i coins.value = V (ie:
104 // this step provides an additional layer of obfuscation and minimizes the
105 // interactions with the mixer (that we know affect the public state and
107 commit_to_inputs_cm.reset(new COMM_cm_gadget<FieldT, HashT>(
108 pb, a_pk->bits, rho, this->r, this->value, commitment));
110 // We do not forget to allocate the `value_enforce` variable
111 // since it is submitted to boolean constraints
112 value_enforce.allocate(pb, FMT(this->annotation_prefix, " value_enforce"));
114 // This gadget makes sure that the computed
115 // commitment is in the merkle tree of root rt
116 // We finally compute a root from the (field) commitment and the
117 // authentication path We furthermore check, depending on value_enforce, if
118 // the computed root is equal to the current one
119 check_membership.reset(new merkle_path_authenticator<FieldT, HashTreeT>(
126 value_enforce, // boolean that is set to ONE if the cm needs to be in
127 // the tree of root rt (and if the given path needs to be
128 // correct), ZERO otherwise
129 FMT(this->annotation_prefix, " auth_path")));
132 template<typename FieldT, typename HashT, typename HashTreeT, size_t TreeDepth>
133 void input_note_gadget<FieldT, HashT, HashTreeT, TreeDepth>::
134 generate_r1cs_constraints()
136 // Generate constraints of parent gadget
137 note_gadget<FieldT>::generate_r1cs_constraints();
139 // Generate the constraints for the rho 256-bit string
140 for (size_t i = 0; i < ZETH_RHO_SIZE; i++) { // ZETH_RHO_SIZE = 256
141 libsnark::generate_boolean_r1cs_constraint<FieldT>(
142 this->pb, rho[i], FMT(this->annotation_prefix, " rho"));
144 spend_authority->generate_r1cs_constraints();
145 expose_nullifiers->generate_r1cs_constraints();
146 commit_to_inputs_cm->generate_r1cs_constraints();
147 // value * (1 - enforce) = 0
148 // Given `enforce` is boolean constrained:
149 // If `value` is zero, `enforce` _can_ be zero.
150 // If `value` is nonzero, `enforce` _must_ be one.
151 libsnark::generate_boolean_r1cs_constraint<FieldT>(
154 FMT(this->annotation_prefix, " value_enforce"));
156 this->pb.add_r1cs_constraint(
157 libsnark::r1cs_constraint<FieldT>(
158 packed_addition(this->value), (1 - value_enforce), 0),
159 FMT(this->annotation_prefix, " wrap_constraint_mkpath_dummy_inputs"));
161 check_membership->generate_r1cs_constraints();
164 template<typename FieldT, typename HashT, typename HashTreeT, size_t TreeDepth>
165 void input_note_gadget<FieldT, HashT, HashTreeT, TreeDepth>::
166 generate_r1cs_witness(
167 const std::vector<FieldT> &merkle_path,
168 const bits_addr<TreeDepth> &address_bits,
169 const zeth_note ¬e)
171 // Generate witness of parent gadget
172 note_gadget<FieldT>::generate_r1cs_witness(note);
174 // Witness a_pk for a_sk with PRF_addr
175 spend_authority->generate_r1cs_witness();
177 // Witness rho for the input note
178 note.rho.fill_pb_variable_array(this->pb, rho);
179 // Witness the nullifier for the input note
180 expose_nullifiers->generate_r1cs_witness();
182 // Witness the commitment of the input note
183 commit_to_inputs_cm->generate_r1cs_witness();
185 // Set enforce flag for nonzero input value
186 // Set the enforce flag according to the value of the note
187 // Remember that if the note has a value of 0, we do not enforce the
188 // corresponding commitment to be in the tree. If the value is > 0 though,
189 // we enforce the corresponding commitment to be in the merkle tree of
192 // Note: We need to set the value of `value_enforce`, because this bit is
193 // used in the merkle_tree_check_read_gadget which uses a
194 // `field_vector_copy_gadget` that does a check with the computed root and
195 // the root given to the `merkle_tree_check_read_gadget`
197 // This check is in the form of constraints like:
199 // template<typename FieldT>
200 // void field_vector_copy_gadget<FieldT>::generate_r1cs_constraints(){
201 // for (size_t i = 0; i < source.size(); ++i)
203 // this->pb.add_r1cs_constraint(
204 // r1cs_constraint<FieldT>(
206 // source[i] - target[i],
210 // this->annotation_prefix, "
211 // copying_check_%zu", i));
215 // If `do_copy` is set to 0, we basically do NOT compare the computed root
216 // and the given root. This is useful in our case as we can use any random
217 // merkle path with our dummy/0-valued coin and make sure that the computed
218 // root is never compared with the actual root (because they will never be
219 // equal as we used a random merkle path to witness the 0-valued coin)
221 // Basically `value_enforce` makes sure that we give a VALID merkle auth
222 // path to our commitment or, in other words, that the commitment is in the
225 // The `value_enforce` is NOT set by the gagdet, it is set by us to tell
226 // whether we want to verify the commitment is in the tree or whether we
227 // want to render this check useless by having a tautology (ie: 0 = 0 for
228 // each constraints of the field_vector_copy_gadget)
231 // The way the variable `value_enforce` works here is that: we give a root
232 // as input to the `merkle_tree_check_read_gadget`. This gadget computes the
233 // root obtained by the verification of the merkle authentication path and
234 // stores the result in `computed_root` which is a digest variable. Then,
235 // the value of the `value_enforce` or `read_successful` variable is checked
236 // to copy the result of the `computed_root` IN the variable `root` which is
237 // the given root If the value of `value_enforce` is FieldT::one() => the
238 // content of `root` is replaced by the content of `computed_root`. Else (if
239 // `value_enforce == FieldT::zero()`), then the value of `root` remains the
242 // Note that if the given path is not an auth path to the given commitment,
243 // and if the value of `value_enforce` is set to FieldT::one(), then the
244 // value of `root` is changed to the value of the computed root. But because
245 // the merkle root is a public input, it is sent to the verifier. Thus, if
246 // the auth path is not valid, the verifier gets the root value `root`, but
247 // this value is replaced by `computed_root` in the circuit, which should
248 // lead the verification of the proof to fail.
250 // WARNING: Because we decide to use a single root for ALL the inputs of the
251 // JoinSplit here, we need to be extra careful. Note that if one of the
252 // input oes not have a valid auth path (is not correctly authenticated),
253 // the root (shared by all inputs) will be changed and the proof should be
255 this->pb.val(value_enforce) =
256 (note.is_zero_valued()) ? FieldT::zero() : FieldT::one();
257 std::cout << "[DEBUG] Value of `value_enforce`: "
258 << (this->pb.val(value_enforce)).as_ulong() << std::endl;
260 // Witness merkle tree authentication path
261 address_bits.fill_pb_variable_array(this->pb, address_bits_va);
263 // Set auth_path values
264 auth_path->fill_with_field_elements(this->pb, merkle_path);
266 check_membership->generate_r1cs_witness();
269 // Commit to the output notes of the JS
270 template<typename FieldT, typename HashT>
271 output_note_gadget<FieldT, HashT>::output_note_gadget(
272 libsnark::protoboard<FieldT> &pb,
273 std::shared_ptr<libsnark::digest_variable<FieldT>> rho,
274 const libsnark::pb_variable<FieldT> &commitment,
275 const std::string &annotation_prefix)
276 : note_gadget<FieldT>(pb, annotation_prefix)
278 a_pk.reset(new libsnark::digest_variable<FieldT>(
279 pb, HashT::get_digest_len(), FMT(this->annotation_prefix, " a_pk")));
281 // Commit to the output notes publicly without disclosing them.
282 commit_to_outputs_cm.reset(new COMM_cm_gadget<FieldT, HashT>(
283 pb, a_pk->bits, rho->bits, this->r, this->value, commitment));
286 template<typename FieldT, typename HashT>
287 void output_note_gadget<FieldT, HashT>::generate_r1cs_constraints()
289 // Generate constraints of the parent gadget
290 note_gadget<FieldT>::generate_r1cs_constraints();
292 a_pk->generate_r1cs_constraints();
293 commit_to_outputs_cm->generate_r1cs_constraints();
296 template<typename FieldT, typename HashT>
297 void output_note_gadget<FieldT, HashT>::generate_r1cs_witness(
298 const zeth_note ¬e)
300 // Generate witness of the parent gadget
301 note_gadget<FieldT>::generate_r1cs_witness(note);
303 // Witness a_pk with note information
304 note.a_pk.fill_pb_variable_array(this->pb, a_pk->bits);
306 commit_to_outputs_cm->generate_r1cs_witness();
309 } // namespace libzeth
311 #endif // __ZETH_CIRCUITS_NOTE_TCC__