1 // Copyright (c) 2015-2022 Clearmatics Technologies Ltd
3 // SPDX-License-Identifier: LGPL-3.0+
5 #ifndef __ZETH_CIRCUITS_JOINSPLIT_TCC__
6 #define __ZETH_CIRCUITS_JOINSPLIT_TCC__
8 #include "libzeth/circuits/notes/note.hpp"
9 #include "libzeth/circuits/safe_arithmetic.hpp"
10 #include "libzeth/core/joinsplit_input.hpp"
11 #include "libzeth/core/merkle_tree_field.hpp"
12 #include "libzeth/zeth_constants.hpp"
14 #include <boost/static_assert.hpp>
26 class joinsplit_gadget : public libsnark::gadget<FieldT>
29 const size_t digest_len_minus_field_cap =
30 subtract_with_clamp(HashT::get_digest_len(), FieldT::capacity());
32 // Number of residual bits from packing of hash digests into smaller
33 // field elements to which are added the public value of size 64 bits
34 const size_t length_bit_residual =
35 2 * ZETH_V_SIZE + digest_len_minus_field_cap * (1 + 2 * NumInputs);
36 // Number of field elements needed to pack this number of bits
37 const size_t nb_field_residual =
38 libff::div_ceil(length_bit_residual, FieldT::capacity());
40 // Multipacking gadgets for the inputs (nullifierS, hsig, message
41 // authentication tags (h_is) and the residual bits (comprising the
42 // previous variables' bits not containable in a single field element as
43 // well as the public values) (the root and cms are field elements)
44 // because we pack the nullifiers (Inputs of JS = NumInputs),
45 // AND the signature hash h_sig (+1) AND the message authentication tags
46 // h_iS (+ NumInputs) AND the residual field elements
47 // which aggregate the extra bits and public values (+1)
49 libsnark::pb_variable_array<FieldT>,
50 NumInputs + 1 + NumInputs + 1>
53 libsnark::pb_variable_array<FieldT>,
54 NumInputs + 1 + NumInputs + 1>
57 // We use an array of multipackers here instead of a single packer that
60 std::shared_ptr<libsnark::multipacking_gadget<FieldT>>,
61 NumInputs + 1 + NumInputs + 1>
64 libsnark::pb_variable<FieldT> ZERO;
66 // PUBLIC DATA: to be made available to the mixer
69 std::shared_ptr<libsnark::pb_variable<FieldT>> merkle_root;
70 // List of nullifiers of the notes to spend
71 std::array<std::shared_ptr<libsnark::digest_variable<FieldT>>, NumInputs>
73 // List of commitments generated for the new notes
74 libsnark::pb_variable_array<FieldT> output_commitments;
75 // Public value that is put into the mix
76 libsnark::pb_variable_array<FieldT> zk_vpub_in;
77 // Value that is taken out of the mix
78 libsnark::pb_variable_array<FieldT> zk_vpub_out;
79 // Sighash h_sig := hSigCRH(randomSeed, {nf_old},
80 // joinSplitPubKey) (p.53 ZCash proto. spec.)
81 std::shared_ptr<libsnark::digest_variable<FieldT>> h_sig;
82 // List of message authentication tags
83 std::array<std::shared_ptr<libsnark::digest_variable<FieldT>>, NumInputs>
86 // PRIVATE DATA: must be auxiliary (private) inputs to the statement.
87 // Protoboard owner is responsible for ensuring this. (Note that the PUBLIC
88 // inputs above are allocated first, so only the first
89 // get_num_public_elements allocated by this gadget are "public").
91 // Total amount transferred in the transaction
92 libsnark::pb_variable_array<FieldT> zk_total_uint64;
93 // List of all spending keys
94 std::array<std::shared_ptr<libsnark::digest_variable<FieldT>>, NumInputs>
96 // List of all output rhos
97 std::array<std::shared_ptr<libsnark::digest_variable<FieldT>>, NumOutputs>
99 // random seed for uniqueness of the new rho
100 std::shared_ptr<libsnark::digest_variable<FieldT>> phi;
102 // Input note gadgets
104 std::shared_ptr<input_note_gadget<FieldT, HashT, HashTreeT, TreeDepth>>,
107 // Message authentication tag gadgets
108 std::array<std::shared_ptr<PRF_pk_gadget<FieldT, HashT>>, NumInputs>
112 std::array<std::shared_ptr<PRF_rho_gadget<FieldT, HashT>>, NumOutputs>
114 // Output note gadgets
115 std::array<std::shared_ptr<output_note_gadget<FieldT, HashT>>, NumOutputs>
119 // Make sure that we do not exceed the number of inputs/outputs
120 // specified in zeth's configuration file (see: zeth.h file)
121 BOOST_STATIC_ASSERT(NumInputs <= ZETH_NUM_JS_INPUTS);
122 BOOST_STATIC_ASSERT(NumOutputs <= ZETH_NUM_JS_OUTPUTS);
124 // Primary inputs are packed to be added to the extended proof and given to
125 // the verifier on-chain
126 explicit joinsplit_gadget(
127 libsnark::protoboard<FieldT> &pb,
128 const std::string &annotation_prefix = "joinsplit_gadget")
129 : libsnark::gadget<FieldT>(pb, annotation_prefix)
131 // Block dedicated to generate the verifier inputs
133 // PUBLIC DATA: allocated first so that the protoboard has access.
135 // Allocation is currently performed here in the following order
136 // (with the protoboard owner determining whether these are primary
137 // or auxiliary inputs to the circuit):
143 // - Residual field element(S)
145 // This yields the following index mappings:
147 // 1, ... : Nullifiers (NumInputs)
148 // 1 + NumInputs, ... : Commitments (Num Outputs)
149 // 1 + NumInputs + NumOutputs : h_sig
150 // 2 + NumInputs + NumOutputs, ... : h_iS (NumInputs)
151 // 2 + 2xNumInputs + NumOutputs, ... : v_in, v_out, residual
152 // (nb_field_residual)
154 // We first allocate the root
155 merkle_root.reset(new libsnark::pb_variable<FieldT>);
156 merkle_root->allocate(
157 pb, FMT(this->annotation_prefix, " merkle_root"));
159 output_commitments.allocate(pb, NumOutputs, " output_commitments");
161 // We allocate a field element for each of the input nullifiers
162 // to pack their first FieldT::capacity() bits
163 for (size_t i = 0; i < NumInputs; i++) {
164 packed_inputs[i].allocate(
167 FMT(this->annotation_prefix, " in_nullifier[%zu]", i));
170 // We allocate a field element for h_sig to pack its first
171 // FieldT::capacity() bits
172 packed_inputs[NumInputs].allocate(
173 pb, 1, FMT(this->annotation_prefix, " h_sig"));
175 // We allocate a field element for each message authentication tags
176 // h_iS to pack their first FieldT::capacity() bits
177 for (size_t i = NumInputs + 1; i < NumInputs + 1 + NumInputs; i++) {
178 packed_inputs[i].allocate(
179 pb, 1, FMT(this->annotation_prefix, " h_i[%zu]", i));
182 // We allocate as many field elements as needed to pack the public
183 // values and the hash digests' residual bits
184 packed_inputs[NumInputs + 1 + NumInputs].allocate(
187 FMT(this->annotation_prefix, " residual_bits"));
189 // Compute the number of packed public elements, and the total
190 // number of public elements (see table above). The "packed" inputs
191 // (those represented as a field element and some residual bits)
193 // H_sig, nullifier, commitments and h_iS
194 const size_t num_packed_public_elements =
195 2 * NumInputs + 1 + nb_field_residual;
196 const size_t num_public_elements =
197 1 + NumOutputs + num_packed_public_elements;
201 // Allocate a ZERO variable
202 // TODO: check whether/why this is actually needed
203 ZERO.allocate(pb, FMT(this->annotation_prefix, " ZERO"));
205 // Initialize the digest_variables
206 phi.reset(new libsnark::digest_variable<FieldT>(
207 pb, ZETH_PHI_SIZE, FMT(this->annotation_prefix, " phi")));
208 h_sig.reset(new libsnark::digest_variable<FieldT>(
209 pb, ZETH_HSIG_SIZE, FMT(this->annotation_prefix, " h_sig")));
210 for (size_t i = 0; i < NumInputs; i++) {
211 input_nullifiers[i].reset(new libsnark::digest_variable<FieldT>(
213 HashT::get_digest_len(),
214 FMT(this->annotation_prefix, " input_nullifiers[%zu]", i)));
215 a_sks[i].reset(new libsnark::digest_variable<FieldT>(
218 FMT(this->annotation_prefix, " a_sks[%zu]", i)));
219 h_is[i].reset(new libsnark::digest_variable<FieldT>(
221 HashT::get_digest_len(),
222 FMT(this->annotation_prefix, " h_is[%zu]", i)));
224 for (size_t i = 0; i < NumOutputs; i++) {
225 rho_is[i].reset(new libsnark::digest_variable<FieldT>(
227 HashT::get_digest_len(),
228 FMT(this->annotation_prefix, " rho_is[%zu]", i)));
231 // Allocate the zk_vpub_in and zk_vpub_out
233 pb, ZETH_V_SIZE, FMT(this->annotation_prefix, " zk_vpub_in"));
234 zk_vpub_out.allocate(
235 pb, ZETH_V_SIZE, FMT(this->annotation_prefix, " zk_vpub_out"));
237 // Assign digests to unpacked field elements and residual bits.
238 // Note that the order here dictates the layout of residual bits
239 // (from lowest order to highest order):
243 // h_0, ..., h_{num_inputs},
244 // nf_0, ..., nf_{num_inputs},
247 // where vpub_out and vpub_in are each 64 bits.
248 libsnark::pb_variable_array<FieldT> &residual_bits =
249 unpacked_inputs[NumInputs + 1 + NumInputs];
251 // Assign the public output and input values to the first residual
252 // bits (in this way, they will always appear in the same place in
253 // the field element).
254 assign_public_value_to_residual_bits(zk_vpub_out, residual_bits);
255 assign_public_value_to_residual_bits(zk_vpub_in, residual_bits);
257 // Initialize the unpacked input corresponding to the h_is
258 for (size_t i = NumInputs + 1, j = 0;
259 i < NumInputs + 1 + NumInputs && j < NumInputs;
261 digest_variable_assign_to_field_element_and_residual(
262 *h_is[j], unpacked_inputs[i], residual_bits);
265 // Initialize the unpacked input corresponding to the input
267 for (size_t i = 0; i < NumInputs; i++) {
268 digest_variable_assign_to_field_element_and_residual(
269 *input_nullifiers[i], unpacked_inputs[i], residual_bits);
272 // Initialize the unpacked input corresponding to the h_sig
273 digest_variable_assign_to_field_element_and_residual(
274 *h_sig, unpacked_inputs[NumInputs], residual_bits);
277 // The root is a FieldT, hence is not packed, likewise for the cms.
278 // The size of the packed inputs should be 2*NumInputs + 1 + 1
279 // since we are packing all the inputs nullifiers + the h_is +
280 // + the h_sig + the residual bits
281 assert(packed_inputs.size() == NumInputs + 1 + NumInputs + 1);
282 assert(num_packed_public_elements == [this]() {
284 for (const auto &i : packed_inputs) {
285 sum = sum + i.size();
289 assert(num_public_elements == get_num_public_elements());
290 (void)num_public_elements;
292 // [SANITY CHECK] Total size of unpacked inputs
293 size_t total_size_unpacked_inputs = 0;
294 for (size_t i = 0; i < NumInputs + 1 + NumInputs + 1; i++) {
295 total_size_unpacked_inputs += unpacked_inputs[i].size();
298 total_size_unpacked_inputs == get_unpacked_inputs_bit_size());
300 // These gadgets will ensure that all of the inputs we provide are
301 // boolean constrained, and and correctly packed into field elements
302 // We basically build the public inputs here
304 // 1. Pack the nullifiers
305 for (size_t i = 0; i < NumInputs; i++) {
306 packers[i].reset(new libsnark::multipacking_gadget<FieldT>(
311 FMT(this->annotation_prefix,
312 " packer_nullifiers[%zu]",
317 packers[NumInputs].reset(new libsnark::multipacking_gadget<FieldT>(
319 unpacked_inputs[NumInputs],
320 packed_inputs[NumInputs],
322 FMT(this->annotation_prefix, " packer_h_sig")));
325 for (size_t i = NumInputs + 1; i < NumInputs + 1 + NumInputs; i++) {
326 packers[i].reset(new libsnark::multipacking_gadget<FieldT>(
331 FMT(this->annotation_prefix, " packer_h_i[%zu]", i)));
334 // 4. Pack the other values and residual bits
335 packers[NumInputs + 1 + NumInputs].reset(
336 new libsnark::multipacking_gadget<FieldT>(
339 packed_inputs[NumInputs + 1 + NumInputs],
341 FMT(this->annotation_prefix, " packer_residual_bits")));
343 } // End of the block dedicated to generate the verifier inputs
345 zk_total_uint64.allocate(
346 pb, ZETH_V_SIZE, FMT(this->annotation_prefix, " zk_total"));
348 // Input note gadgets for commitments, nullifiers, and spend authority
349 // as well as PRF gadgets for the h_iS
350 for (size_t i = 0; i < NumInputs; i++) {
351 input_notes[i].reset(
352 new input_note_gadget<FieldT, HashT, HashTreeT, TreeDepth>(
353 pb, ZERO, a_sks[i], input_nullifiers[i], *merkle_root));
355 h_i_gadgets[i].reset(new PRF_pk_gadget<FieldT, HashT>(
356 pb, ZERO, a_sks[i]->bits, h_sig->bits, i, h_is[i]));
359 // Output note gadgets for commitments as well as PRF gadgets for the
361 for (size_t i = 0; i < NumOutputs; i++) {
362 rho_i_gadgets[i].reset(new PRF_rho_gadget<FieldT, HashT>(
363 pb, ZERO, phi->bits, h_sig->bits, i, rho_is[i]));
365 output_notes[i].reset(new output_note_gadget<FieldT, HashT>(
366 pb, rho_is[i], output_commitments[i]));
370 // Check the booleaness of packing variables
371 // Check the booleaness of phi and the a_sks
372 // Check value of ZERO (i.e. that ZERO = FieldT::zero())
373 // Check input notes, output notes, h_iS and rhoS are correctly computed
374 // Check the joinsplit is balanced
375 // N.B. note_gadget checks the booleaness of v and r_trap
376 // N.B. input_note_gadget checks the booleaness of rho^old
377 // N.B. output_note_gadget checks the booleaness of of a_pk^new
378 void generate_r1cs_constraints()
380 // The `true` passed to `generate_r1cs_constraints` ensures that all
381 // inputs are boolean strings
382 for (size_t i = 0; i < packers.size(); i++) {
383 packers[i]->generate_r1cs_constraints(true);
386 // Constrain the not-packed digest variables, ensure there are 256 bit
387 // long boolean arrays
388 phi->generate_r1cs_constraints();
389 for (size_t i = 0; i < NumInputs; i++) {
390 a_sks[i]->generate_r1cs_constraints();
393 // Constrain `ZERO`: Make sure that the ZERO variable is the zero of the
395 libsnark::generate_r1cs_equals_const_constraint<FieldT>(
399 FMT(this->annotation_prefix, " ZERO"));
401 // Constrain the JoinSplit inputs and the h_iS
402 for (size_t i = 0; i < NumInputs; i++) {
403 input_notes[i]->generate_r1cs_constraints();
404 h_i_gadgets[i]->generate_r1cs_constraints();
407 // Constrain the JoinSplit outputs and the output rho_iS
408 for (size_t i = 0; i < NumOutputs; i++) {
409 rho_i_gadgets[i]->generate_r1cs_constraints();
410 output_notes[i]->generate_r1cs_constraints();
413 // Generate the constraints to ensure that the condition of the
414 // joinsplit holds (ie: LHS = RHS)
417 libsnark::linear_combination<FieldT> left_side =
418 packed_addition(zk_vpub_in);
419 for (size_t i = 0; i < NumInputs; i++) {
420 left_side = left_side + packed_addition(input_notes[i]->value);
424 libsnark::linear_combination<FieldT> right_side =
425 packed_addition(zk_vpub_out);
426 for (size_t i = 0; i < NumOutputs; i++) {
428 right_side + packed_addition(output_notes[i]->value);
431 // Ensure that both sides are equal (ie: 1 * left_side = right_side)
432 this->pb.add_r1cs_constraint(
433 libsnark::r1cs_constraint<FieldT>(1, left_side, right_side),
434 FMT(this->annotation_prefix, " lhs_rhs_equality_constraint"));
436 // See: https://github.com/zcash/zcash/issues/854
437 // Ensure that `left_side` is a 64-bit integer
438 for (size_t i = 0; i < ZETH_V_SIZE; i++) {
439 libsnark::generate_boolean_r1cs_constraint<FieldT>(
442 FMT(this->annotation_prefix, " zk_total_uint64[%zu]", i));
445 this->pb.add_r1cs_constraint(
446 libsnark::r1cs_constraint<FieldT>(
447 1, left_side, packed_addition(zk_total_uint64)),
448 FMT(this->annotation_prefix, " lhs_equal_zk_total_constraint"));
452 void generate_r1cs_witness(
454 const std::array<joinsplit_input<FieldT, TreeDepth>, NumInputs> &inputs,
455 const std::array<zeth_note, NumOutputs> &outputs,
458 const bits256 h_sig_in,
459 const bits256 phi_in)
462 this->pb.val(ZERO) = FieldT::zero();
464 // Witness the merkle root
465 this->pb.val(*merkle_root) = rt;
467 // Witness public values
469 // Witness LHS public value
470 vpub_in.fill_pb_variable_array(this->pb, zk_vpub_in);
472 // Witness RHS public value
473 vpub_out.fill_pb_variable_array(this->pb, zk_vpub_out);
476 h_sig->generate_r1cs_witness(h_sig_in.to_vector());
478 // Witness the h_iS, a_sk and rho_iS
479 for (size_t i = 0; i < NumInputs; i++) {
480 a_sks[i]->generate_r1cs_witness(
481 inputs[i].spending_key_a_sk.to_vector());
485 phi->generate_r1cs_witness(phi_in.to_vector());
488 // Witness total_uint64 bits
489 // We add binary numbers here see:
490 // https://stackoverflow.com/questions/13282825/adding-binary-numbers-in-c
491 // To check left_side_acc < 2^64, we set the function's bool to true
492 bits64 left_side_acc = vpub_in;
493 for (size_t i = 0; i < NumInputs; i++) {
494 left_side_acc = bits_add<ZETH_V_SIZE>(
495 left_side_acc, inputs[i].note.value, true);
498 left_side_acc.fill_pb_variable_array(this->pb, zk_total_uint64);
501 // Witness the JoinSplit inputs and the h_is
502 for (size_t i = 0; i < NumInputs; i++) {
503 input_notes[i]->generate_r1cs_witness(
504 inputs[i].witness_merkle_path,
505 inputs[i].address_bits,
508 h_i_gadgets[i]->generate_r1cs_witness();
511 // Witness the JoinSplit outputs
512 for (size_t i = 0; i < NumOutputs; i++) {
513 rho_i_gadgets[i]->generate_r1cs_witness();
514 output_notes[i]->generate_r1cs_witness(outputs[i]);
517 // This happens last, because only by now are all the
518 // verifier inputs resolved.
519 for (size_t i = 0; i < packers.size(); i++) {
520 packers[i]->generate_r1cs_witness_from_bits();
524 // Given a digest variable, assign to an unpacked field element
525 // `unpacked_element` and unpacked element holding residual bits.
526 void digest_variable_assign_to_field_element_and_residual(
527 const libsnark::digest_variable<FieldT> &digest_var,
528 libsnark::pb_variable_array<FieldT> &unpacked_element,
529 libsnark::pb_variable_array<FieldT> &unpacked_residual_bits)
531 const size_t field_capacity = FieldT::capacity();
533 // Digest_var holds bits high-order first. pb_variable_array will be
534 // packed with low-order bit first to match the evm.
536 // The field element holds the lowest order bits ordered 256 -
537 // digest_len_minus_field_cap bits.
538 unpacked_element.insert(
539 unpacked_element.end(),
540 digest_var.bits.rbegin(),
541 digest_var.bits.rbegin() + field_capacity);
543 // The remaining high order bits are appended to
544 // unpacked_residual_bits.
545 unpacked_residual_bits.insert(
546 unpacked_residual_bits.end(),
547 digest_var.bits.rbegin() + field_capacity,
548 digest_var.bits.rend());
551 static void assign_public_value_to_residual_bits(
552 const libsnark::pb_variable_array<FieldT> &unpacked_public_value,
553 libsnark::pb_variable_array<FieldT> &unpacked_residual_bits)
555 unpacked_residual_bits.insert(
556 unpacked_residual_bits.end(),
557 unpacked_public_value.rbegin(),
558 unpacked_public_value.rend());
561 // Computes the total bit-length of the primary inputs
562 static size_t get_inputs_bit_size()
566 // Bit-length of the Merkle Root
567 acc += FieldT::capacity();
569 // Bit-length of the CommitmentS
570 for (size_t i = 0; i < NumOutputs; i++) {
571 acc += FieldT::capacity();
574 // Bit-length of the NullifierS
575 for (size_t i = 0; i < NumInputs; i++) {
576 acc += HashT::get_digest_len();
579 // Bit-length of vpub_in
582 // Bit-length of vpub_out
585 // Bit-length of h_sig
586 acc += HashT::get_digest_len();
588 // Bit-length of the h_iS
589 for (size_t i = 0; i < NumInputs; i++) {
590 acc += HashT::get_digest_len();
596 // Computes the total bit-length of the unpacked primary inputs
597 static size_t get_unpacked_inputs_bit_size()
599 // The Merkle root and commitments are not in the `unpacked_inputs`
600 // so we subtract their bit-length to get the total bit-length of
601 // the primary inputs in `unpacked_inputs`
602 return get_inputs_bit_size() - (1 + NumOutputs) * FieldT::capacity();
605 // Computes the number of field elements in the public data
606 static size_t get_num_public_elements()
608 size_t nb_elements = 0;
610 // The merkle root is represented by 1 field element (bit_length(root) =
611 // FieldT::capacity())
614 // Each commitment is represented by 1 field element (bit_length(cm) =
615 // FieldT::capacity())
616 for (size_t i = 0; i < NumOutputs; i++) {
620 // Each nullifier is represented by 1 field element and
621 // (HashT::get_digest_len() - FieldT::capacity()) bits we aggregate in
622 // the residual field element(s) later on (c.f. last incrementation)
623 for (size_t i = 0; i < NumInputs; i++) {
627 // The h_sig is represented 1 field element and (HashT::get_digest_len()
628 // - FieldT::capacity()) bits we aggregate in the residual field
629 // element(s) later on (c.f. last incrementation)
632 // Each authentication tag is represented by 1 field element and
633 // (HashT::get_digest_len() - FieldT::capacity()) bits we aggregate in
634 // the residual field element(s) later on (c.f. last incrementation)
635 for (size_t i = 0; i < NumInputs; i++) {
639 // Residual bits and public values (in and out) aggregated in
640 // `nb_field_residual` field elements
641 nb_elements += libff::div_ceil(
642 2 * ZETH_V_SIZE + subtract_with_clamp(
643 HashT::get_digest_len(), FieldT::capacity()) *
651 } // namespace libzeth
653 #endif // __ZETH_CIRCUITS_JOINSPLIT_TCC__