]>
Commit | Line | Data |
---|---|---|
074eb3a2 | 1 | #include "zcash/circuit/utils.tcc" |
ca8d6c93 | 2 | #include "zcash/circuit/prfs.tcc" |
5e61a78f | 3 | #include "zcash/circuit/commitment.tcc" |
59c3d926 | 4 | #include "zcash/circuit/merkle.tcc" |
ca8d6c93 | 5 | #include "zcash/circuit/note.tcc" |
074eb3a2 | 6 | |
369df065 SB |
7 | template<typename FieldT, size_t NumInputs, size_t NumOutputs> |
8 | class joinsplit_gadget : gadget<FieldT> { | |
074eb3a2 SB |
9 | private: |
10 | // Verifier inputs | |
11 | pb_variable_array<FieldT> zk_packed_inputs; | |
12 | pb_variable_array<FieldT> zk_unpacked_inputs; | |
13 | std::shared_ptr<multipacking_gadget<FieldT>> unpacker; | |
14 | ||
15 | std::shared_ptr<digest_variable<FieldT>> zk_merkle_root; | |
16 | std::shared_ptr<digest_variable<FieldT>> zk_h_sig; | |
17 | boost::array<std::shared_ptr<digest_variable<FieldT>>, NumInputs> zk_input_nullifiers; | |
032164d5 | 18 | boost::array<std::shared_ptr<digest_variable<FieldT>>, NumInputs> zk_input_macs; |
074eb3a2 SB |
19 | boost::array<std::shared_ptr<digest_variable<FieldT>>, NumOutputs> zk_output_commitments; |
20 | pb_variable_array<FieldT> zk_vpub_old; | |
21 | pb_variable_array<FieldT> zk_vpub_new; | |
22 | ||
53d2ade7 SB |
23 | // Aux inputs |
24 | pb_variable<FieldT> ZERO; | |
6b010d9b | 25 | std::shared_ptr<digest_variable<FieldT>> zk_phi; |
dbab2437 | 26 | pb_variable_array<FieldT> zk_total_uint64; |
53d2ade7 | 27 | |
ca8d6c93 SB |
28 | // Input note gadgets |
29 | boost::array<std::shared_ptr<input_note_gadget<FieldT>>, NumInputs> zk_input_notes; | |
032164d5 | 30 | boost::array<std::shared_ptr<PRF_pk_gadget<FieldT>>, NumInputs> zk_mac_authentication; |
ca8d6c93 | 31 | |
6b010d9b SB |
32 | // Output note gadgets |
33 | boost::array<std::shared_ptr<output_note_gadget<FieldT>>, NumOutputs> zk_output_notes; | |
34 | ||
369df065 | 35 | public: |
e52f40e8 | 36 | // PRF_pk only has a 1-bit domain separation "nonce" |
6b010d9b | 37 | // for different macs. |
e52f40e8 SB |
38 | BOOST_STATIC_ASSERT(NumInputs <= 2); |
39 | ||
6b010d9b SB |
40 | // PRF_rho only has a 1-bit domain separation "nonce" |
41 | // for different output `rho`. | |
42 | BOOST_STATIC_ASSERT(NumOutputs <= 2); | |
43 | ||
369df065 | 44 | joinsplit_gadget(protoboard<FieldT> &pb) : gadget<FieldT>(pb) { |
074eb3a2 SB |
45 | // Verification |
46 | { | |
47 | // The verification inputs are all bit-strings of various | |
48 | // lengths (256-bit digests and 64-bit integers) and so we | |
49 | // pack them into as few field elements as possible. (The | |
50 | // more verification inputs you have, the more expensive | |
51 | // verification is.) | |
52 | zk_packed_inputs.allocate(pb, verifying_field_element_size()); | |
53 | pb.set_input_sizes(verifying_field_element_size()); | |
54 | ||
55 | alloc_uint256(zk_unpacked_inputs, zk_merkle_root); | |
56 | alloc_uint256(zk_unpacked_inputs, zk_h_sig); | |
57 | ||
58 | for (size_t i = 0; i < NumInputs; i++) { | |
59 | alloc_uint256(zk_unpacked_inputs, zk_input_nullifiers[i]); | |
032164d5 | 60 | alloc_uint256(zk_unpacked_inputs, zk_input_macs[i]); |
074eb3a2 SB |
61 | } |
62 | ||
63 | for (size_t i = 0; i < NumOutputs; i++) { | |
64 | alloc_uint256(zk_unpacked_inputs, zk_output_commitments[i]); | |
65 | } | |
369df065 | 66 | |
074eb3a2 SB |
67 | alloc_uint64(zk_unpacked_inputs, zk_vpub_old); |
68 | alloc_uint64(zk_unpacked_inputs, zk_vpub_new); | |
69 | ||
70 | assert(zk_unpacked_inputs.size() == verifying_input_bit_size()); | |
71 | ||
72 | // This gadget will ensure that all of the inputs we provide are | |
73 | // boolean constrained. | |
74 | unpacker.reset(new multipacking_gadget<FieldT>( | |
75 | pb, | |
76 | zk_unpacked_inputs, | |
77 | zk_packed_inputs, | |
78 | FieldT::capacity(), | |
79 | "unpacker" | |
80 | )); | |
81 | } | |
53d2ade7 SB |
82 | |
83 | // We need a constant "zero" variable in some contexts. In theory | |
84 | // it should never be necessary, but libsnark does not synthesize | |
85 | // optimal circuits. | |
86 | // | |
87 | // The first variable of our constraint system is constrained | |
88 | // to be one automatically for us, and is known as `ONE`. | |
89 | ZERO.allocate(pb); | |
90 | ||
6b010d9b SB |
91 | zk_phi.reset(new digest_variable<FieldT>(pb, 252, "")); |
92 | ||
dbab2437 DH |
93 | zk_total_uint64.allocate(pb, 64); |
94 | ||
ca8d6c93 | 95 | for (size_t i = 0; i < NumInputs; i++) { |
032164d5 | 96 | // Input note gadget for commitments, macs, nullifiers, |
ca8d6c93 SB |
97 | // and spend authority. |
98 | zk_input_notes[i].reset(new input_note_gadget<FieldT>( | |
99 | pb, | |
2a2f3fb8 | 100 | ZERO, |
59c3d926 SB |
101 | zk_input_nullifiers[i], |
102 | *zk_merkle_root | |
ca8d6c93 | 103 | )); |
e52f40e8 SB |
104 | |
105 | // The input keys authenticate h_sig to prevent | |
106 | // malleability. | |
032164d5 | 107 | zk_mac_authentication[i].reset(new PRF_pk_gadget<FieldT>( |
e52f40e8 SB |
108 | pb, |
109 | ZERO, | |
110 | zk_input_notes[i]->a_sk->bits, | |
111 | zk_h_sig->bits, | |
112 | i ? true : false, | |
032164d5 | 113 | zk_input_macs[i] |
e52f40e8 | 114 | )); |
ca8d6c93 | 115 | } |
6b010d9b SB |
116 | |
117 | for (size_t i = 0; i < NumOutputs; i++) { | |
118 | zk_output_notes[i].reset(new output_note_gadget<FieldT>( | |
119 | pb, | |
120 | ZERO, | |
121 | zk_phi->bits, | |
122 | zk_h_sig->bits, | |
5e61a78f SB |
123 | i ? true : false, |
124 | zk_output_commitments[i] | |
6b010d9b SB |
125 | )); |
126 | } | |
369df065 SB |
127 | } |
128 | ||
129 | void generate_r1cs_constraints() { | |
074eb3a2 SB |
130 | // The true passed here ensures all the inputs |
131 | // are boolean constrained. | |
132 | unpacker->generate_r1cs_constraints(true); | |
53d2ade7 SB |
133 | |
134 | // Constrain `ZERO` | |
135 | generate_r1cs_equals_const_constraint<FieldT>(this->pb, ZERO, FieldT::zero(), "ZERO"); | |
ca8d6c93 | 136 | |
6b010d9b SB |
137 | // Constrain bitness of phi |
138 | zk_phi->generate_r1cs_constraints(); | |
139 | ||
ca8d6c93 SB |
140 | for (size_t i = 0; i < NumInputs; i++) { |
141 | // Constrain the JoinSplit input constraints. | |
142 | zk_input_notes[i]->generate_r1cs_constraints(); | |
e52f40e8 SB |
143 | |
144 | // Authenticate h_sig with a_sk | |
032164d5 | 145 | zk_mac_authentication[i]->generate_r1cs_constraints(); |
ca8d6c93 | 146 | } |
6b010d9b SB |
147 | |
148 | for (size_t i = 0; i < NumOutputs; i++) { | |
149 | // Constrain the JoinSplit output constraints. | |
150 | zk_output_notes[i]->generate_r1cs_constraints(); | |
151 | } | |
e5f7c49d SB |
152 | |
153 | // Value balance | |
154 | { | |
155 | linear_combination<FieldT> left_side = packed_addition(zk_vpub_old); | |
156 | for (size_t i = 0; i < NumInputs; i++) { | |
157 | left_side = left_side + packed_addition(zk_input_notes[i]->value); | |
158 | } | |
159 | ||
160 | linear_combination<FieldT> right_side = packed_addition(zk_vpub_new); | |
161 | for (size_t i = 0; i < NumOutputs; i++) { | |
162 | right_side = right_side + packed_addition(zk_output_notes[i]->value); | |
163 | } | |
164 | ||
165 | // Ensure that both sides are equal | |
166 | this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>( | |
167 | 1, | |
168 | left_side, | |
169 | right_side | |
170 | )); | |
171 | ||
dbab2437 DH |
172 | // #854: Ensure that left_side is a 64-bit integer. |
173 | for (size_t i = 0; i < 64; i++) { | |
174 | generate_boolean_r1cs_constraint<FieldT>( | |
175 | this->pb, | |
176 | zk_total_uint64[i], | |
177 | "" | |
178 | ); | |
179 | } | |
180 | ||
181 | this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>( | |
182 | 1, | |
183 | left_side, | |
184 | packed_addition(zk_total_uint64) | |
185 | )); | |
e5f7c49d | 186 | } |
369df065 SB |
187 | } |
188 | ||
189 | void generate_r1cs_witness( | |
defe37a6 | 190 | const uint252& phi, |
369df065 SB |
191 | const uint256& rt, |
192 | const uint256& h_sig, | |
193 | const boost::array<JSInput, NumInputs>& inputs, | |
194 | const boost::array<Note, NumOutputs>& outputs, | |
195 | uint64_t vpub_old, | |
196 | uint64_t vpub_new | |
197 | ) { | |
53d2ade7 SB |
198 | // Witness `zero` |
199 | this->pb.val(ZERO) = FieldT::zero(); | |
200 | ||
59c3d926 SB |
201 | // Witness rt. This is not a sanity check. |
202 | // | |
203 | // This ensures the read gadget constrains | |
204 | // the intended root in the event that | |
205 | // both inputs are zero-valued. | |
206 | zk_merkle_root->bits.fill_with_bits( | |
207 | this->pb, | |
208 | uint256_to_bool_vector(rt) | |
209 | ); | |
210 | ||
e5f7c49d SB |
211 | // Witness public balance values |
212 | zk_vpub_old.fill_with_bits( | |
213 | this->pb, | |
214 | uint64_to_bool_vector(vpub_old) | |
215 | ); | |
216 | zk_vpub_new.fill_with_bits( | |
217 | this->pb, | |
218 | uint64_to_bool_vector(vpub_new) | |
219 | ); | |
220 | ||
dbab2437 DH |
221 | { |
222 | // Witness total_uint64 bits | |
223 | uint64_t left_side_acc = vpub_old; | |
224 | for (size_t i = 0; i < NumInputs; i++) { | |
225 | left_side_acc += inputs[i].note.value; | |
226 | } | |
227 | ||
228 | zk_total_uint64.fill_with_bits( | |
229 | this->pb, | |
230 | uint64_to_bool_vector(left_side_acc) | |
231 | ); | |
232 | } | |
233 | ||
6b010d9b SB |
234 | // Witness phi |
235 | zk_phi->bits.fill_with_bits( | |
236 | this->pb, | |
defe37a6 | 237 | uint252_to_bool_vector(phi) |
6b010d9b SB |
238 | ); |
239 | ||
e52f40e8 SB |
240 | // Witness h_sig |
241 | zk_h_sig->bits.fill_with_bits( | |
242 | this->pb, | |
243 | uint256_to_bool_vector(h_sig) | |
244 | ); | |
245 | ||
ca8d6c93 SB |
246 | for (size_t i = 0; i < NumInputs; i++) { |
247 | // Witness the input information. | |
59c3d926 SB |
248 | auto merkle_path = inputs[i].witness.path(); |
249 | zk_input_notes[i]->generate_r1cs_witness( | |
250 | merkle_path, | |
251 | inputs[i].key, | |
252 | inputs[i].note | |
253 | ); | |
e52f40e8 | 254 | |
032164d5 SB |
255 | // Witness macs |
256 | zk_mac_authentication[i]->generate_r1cs_witness(); | |
ca8d6c93 SB |
257 | } |
258 | ||
6b010d9b SB |
259 | for (size_t i = 0; i < NumOutputs; i++) { |
260 | // Witness the output information. | |
261 | zk_output_notes[i]->generate_r1cs_witness(outputs[i]); | |
262 | } | |
263 | ||
59c3d926 SB |
264 | // [SANITY CHECK] Ensure that the intended root |
265 | // was witnessed by the inputs, even if the read | |
266 | // gadget overwrote it. This allows the prover to | |
267 | // fail instead of the verifier, in the event that | |
268 | // the roots of the inputs do not match the | |
269 | // treestate provided to the proving API. | |
270 | zk_merkle_root->bits.fill_with_bits( | |
271 | this->pb, | |
272 | uint256_to_bool_vector(rt) | |
273 | ); | |
274 | ||
074eb3a2 SB |
275 | // This happens last, because only by now are all the |
276 | // verifier inputs resolved. | |
277 | unpacker->generate_r1cs_witness_from_bits(); | |
369df065 SB |
278 | } |
279 | ||
280 | static r1cs_primary_input<FieldT> witness_map( | |
281 | const uint256& rt, | |
282 | const uint256& h_sig, | |
032164d5 | 283 | const boost::array<uint256, NumInputs>& macs, |
369df065 SB |
284 | const boost::array<uint256, NumInputs>& nullifiers, |
285 | const boost::array<uint256, NumOutputs>& commitments, | |
286 | uint64_t vpub_old, | |
287 | uint64_t vpub_new | |
288 | ) { | |
074eb3a2 SB |
289 | std::vector<bool> verify_inputs; |
290 | ||
59c3d926 | 291 | insert_uint256(verify_inputs, rt); |
e52f40e8 | 292 | insert_uint256(verify_inputs, h_sig); |
074eb3a2 SB |
293 | |
294 | for (size_t i = 0; i < NumInputs; i++) { | |
2a2f3fb8 | 295 | insert_uint256(verify_inputs, nullifiers[i]); |
032164d5 | 296 | insert_uint256(verify_inputs, macs[i]); |
074eb3a2 SB |
297 | } |
298 | ||
299 | for (size_t i = 0; i < NumOutputs; i++) { | |
5e61a78f | 300 | insert_uint256(verify_inputs, commitments[i]); |
074eb3a2 SB |
301 | } |
302 | ||
e5f7c49d SB |
303 | insert_uint64(verify_inputs, vpub_old); |
304 | insert_uint64(verify_inputs, vpub_new); | |
369df065 | 305 | |
074eb3a2 SB |
306 | assert(verify_inputs.size() == verifying_input_bit_size()); |
307 | auto verify_field_elements = pack_bit_vector_into_field_element_vector<FieldT>(verify_inputs); | |
308 | assert(verify_field_elements.size() == verifying_field_element_size()); | |
309 | return verify_field_elements; | |
310 | } | |
311 | ||
312 | static size_t verifying_input_bit_size() { | |
313 | size_t acc = 0; | |
314 | ||
315 | acc += 256; // the merkle root (anchor) | |
316 | acc += 256; // h_sig | |
317 | for (size_t i = 0; i < NumInputs; i++) { | |
318 | acc += 256; // nullifier | |
032164d5 | 319 | acc += 256; // mac |
074eb3a2 SB |
320 | } |
321 | for (size_t i = 0; i < NumOutputs; i++) { | |
322 | acc += 256; // new commitment | |
323 | } | |
324 | acc += 64; // vpub_old | |
325 | acc += 64; // vpub_new | |
369df065 | 326 | |
074eb3a2 SB |
327 | return acc; |
328 | } | |
329 | ||
330 | static size_t verifying_field_element_size() { | |
331 | return div_ceil(verifying_input_bit_size(), FieldT::capacity()); | |
332 | } | |
333 | ||
334 | void alloc_uint256( | |
335 | pb_variable_array<FieldT>& packed_into, | |
336 | std::shared_ptr<digest_variable<FieldT>>& var | |
337 | ) { | |
338 | var.reset(new digest_variable<FieldT>(this->pb, 256, "")); | |
339 | packed_into.insert(packed_into.end(), var->bits.begin(), var->bits.end()); | |
340 | } | |
341 | ||
342 | void alloc_uint64( | |
343 | pb_variable_array<FieldT>& packed_into, | |
344 | pb_variable_array<FieldT>& integer | |
345 | ) { | |
346 | integer.allocate(this->pb, 64, ""); | |
347 | packed_into.insert(packed_into.end(), integer.begin(), integer.end()); | |
369df065 | 348 | } |
dbab2437 | 349 | }; |