Assessment reports>Universal Proof Aggregator>Threat Model>Universal batch verifier circuit

Universal batch verifier circuit

The universal batch verifier circuit is specified in detail in spec/circuits/universal_batch_verifier.md. There is one discrepancy between the code and the specification that we pointed out in Discussion section ref — for the purposes of the following description, we are going to assume that the endianess in the implementation is correct and assume a correspondingly fixed specification. We will also not mention the findings again here.

We will discuss how the specification corresponds to the implementation, starting from the highest level.

\begin{figure}[H] \begin{adjustwidth}{-5cm}{0cm} % adjust the L and R margins \centering \includegraphics[width=1\linewidth]{./universalbatch.png} \caption{Overview of the universal batch verifier circuit functions.} \label{fig:my_label} \end{adjustwidth} \end{figure}

Entry points

The SafeCircuit implementation for UniversalBatchVerifyCircuit offers the main entry points keygen and prover to generate the verification keys and to prove the universal batch verifier circuit. They handle the appropriate settings and otherwise act as a wrapper around UniversalBatchVerifyCircuit::create_builder_and_instance. The prover method also uses BatchEntries::from_ubv_inputs_and_config to pad inputs appropriately. This corresponds to the "Preprocessing" step in the specification.

The circuit uses the halo2-lib builder pattern, and the GateThreadBuilder is created in UniversalBatchVerifyCircuit::create_builder_and_instance, which otherwise acts as a wrapper around the universal_batch_verify_circuit function, creating and passing on the necessary range and finite field chips.

Step 1: Assignment and checking proof points lie on curve and padding is correct

The UniversalBatchVerifierChip::assign_batch_entry function is called by universal_batch_verify_circuit and handles step 1 of the specification.

First, BatchVerifierChip::assign_proof is called, which assigns circuit variables for all the inputs and uses BatchVerifierChip::check_points_on_curve to verify that the proof points lie on the curve, corresponding to step 1b of the specification. Curve points are given in affine coordinates, and checking whether such a point lies on the curve is done using halo2_ecc::ecc::check_is_on_curve, which verifies the curve equation. The point at infinity is thus not representable for curve points.

Step 1a is handled by UniversalBatchVerifierChip::assign_batch_entry via a call to UniversalBatchVerifierChip::check_padding, in which the padding is constrained to be correct, using the method described in the specification.

Steps 2 and 3: Computation of circuit IDs and the challenge point

The UniversalBatchVerifierChip::verify function is called by universal_batch_verify_circuit and handles steps 2 through 7 of the specification, split up in three calls.

First, circuit IDs are computed by calling UniversalBatchVerifierChip::compute_vk_hash, which performs the required Poseidon hash calculation via a call to var_len_poseidon_no_len_check in utils/hashing.rs. This implements step 2 of the specification.

Second, the challenge point is computed by the function UniversalBatchVerifierChip::compute_r. The challenge point is given by another Poseidon hash, of the circuit IDs, proofs, and public inputs, and is calculated with the PoseidonHasher from utils/hashing.rs. This implements step 3 of the specification.

Third, UniversalBatchVerifierChip::verify_with_challenge is called to handle steps 4 through 7.

Precomputation for steps 4 and 5

The UniversalBatchVerifierChip::verify_with_challenge function calls UniversalBatchVerifierChip::prepare_proofs to handle steps 4 and 5.

As step 5 involves taking the scalar multiple of curve points with a power of the challenge point, these powers are first precomputed. This is done by BatchVerifierChip::scalar_powers. A call to UniversalBatchVerifierChip::compute_pairs then handles the actual steps 4 and 5.

Step 4: Computation of and the public input pairs

To perform step 4 of the specification, UniversalBatchVerifierChip::compute_pairs calls UniversalBatchVerifierChip::compute_pi_pairs, which uses halo2_ecc::ecc::EccChip::variable_base_msm and halo2_ecc::ecc::EccChip::sum to compute the linear combinations .

Step 5: Scaling of pairs for the batched pairing check

Step 5 of the specification is performed by UniversalBatchVerifierChip::compute_pairs directly. In order to compute the relevant scalar products, BatchVerifierChip::scale_pairs is used, which replaces the first component of a pair with its scalar product with a scalar using halo2_ecc::ecc::scalar_multiply.

Step 6: Computation of the multipairing

To compute the multipairing of all the prepared pairs in the previous steps, UniversalBatchVerifierChip::verify_with_challenge calls BatchVerifierChip::multi_pairing, which uses the PairingChip from halo2_ecc::bn254::pairing to perform this calculation. This implements step 6 of the specification.

Step 7: Constraining the final result

For a valid batch of proofs, the result of the multipairing in the previous step should have been the neutral element of . This constraint is added by BatchVerifierChip::check_pairing_result, which is called by UniversalBatchVerifierChip::verify_with_challenge. This implements step 7 of the specification.

Zellic © 2024Back to top ↑