In this post, we will review the batch opening scheme used in halo2 library. The paper for this scheme is introduced in the paper.
In this protocol, the prover and the verifier has access to SRS of the group elements from the groups that support the non-degenerate bilinear pairing.
The batch opening scheme is proven to be secure(has perfect completeness and knowledge soundness) in Algebraic Group Model. In algebraic group model, the adversary for the protocol, is assumed to be algebraic adversary. This means that whenever outputs an element for , it also outputs the vector such that .
Now, let's see the definition of knowledge soundness in algebraic group model.
A protocol between the prover and the verifier for a relation has knowledge soundness in algebraic group model, if there exists an efficient such that the probability of any algebraic adversary winning the following game is negligible.
- chooses input and plays the role of with input .
- given access to all of ’s messages during the protocol (including the coefficients of the linear combinations) outputs .
- wins if
(a) accepts at the end of the protocol, and
(b)
Extractor is an idealized object used for proving knowledge soundness, not implemented in the real-world case. In short, if the protocol has knowledge soundness in algebraic group model, there must be only two cases: the transcript is accepting and , or, the transcript is not accepting and .
The rationality behind this definition is because, in the case of transcript is accepting and , the extractor can solve discrete logarithm problem and break Q-DLOG assumption. We will visit this in the future post.
Let the initial setup is given as following.
is the opening set for each polynomial , and . is the polynomial that opens to in .
Let's visit the following claims.
Fix subsets , and a polynomial . Then divides if and only if divides .
Fix . Fix that decomposes to distinct linear factors over . Suppose that for some , . Then, except with the probability over uniform , does not divide .
Using this claim, let's visit batch opening scheme.
and . Sends .
where
and . Sends .
and checks
Let's prove the knowledge soundness of the protocol. First, algebraic adversary chooses the group elements and plays the role of . There is an efficient AGM-extractor that has access to 's choice of coefficients in to make . Let's say
.
AGM-extractor outputs the polynomials .
now participate as . After sends random , computes the polynomial
Suppose that there exists some such that . Then, by the claims we saw, is not divisible by except with the probability over uniform . Suppose that is not in that form, and computes for arbitrary .
sends random and computes . except with the negligible probability over uniform . Suppose that is not in that form, and computes for arbitrary .
Let's show that the final pairing check passes with negligible probability to prove the knowledge soundness. Since the pairing check is the randomized test of , and this pairing check satisfies only if is divisible by , . However, this holds except with negligible probability. Thus the pairing check passes with negligible probability, thereby proving the knowledge soundness.
There is a implementation of this batch opening scheme inside halo2. It would be worthy to review the implementation and get some deeper understanding.
The structs we first encounter is the following: RotationSet and IntermediateSets.
#[derive(Debug, Clone, PartialEq)]
struct RotationSet<F: Field, T: PartialEq + Clone> {
commitments: Vec<Commitment<F, T>>,
points: Vec<F>,
}
#[derive(Debug, PartialEq)]
struct IntermediateSets<F: Field, Q: Query<F>> {
rotation_sets: Vec<RotationSet<F, Q::Commitment>>,
super_point_set: BTreeSet<F>,
}
IntermediateSets has two fields, rotation_sets and super_point_set. These struct holds the information about all the evaluation points (super_point_set), and rotation_sets is the sets of the polynomials that will be opened in the same opening set . Inside RotationSet, there are the polynomial commitments that will be opened against the same opening set(commitments), and the opening points(points).
There are additional two intermediate types, CommitmentExtension and RotationSetExtension.
struct CommitmentExtension<'a, C: CurveAffine> {
commitment: Commitment<C::Scalar, PolynomialPointer<'a, C>>,
low_degree_equivalent: Polynomial<C::Scalar, Coeff>,
}
struct RotationSetExtension<'a, C: CurveAffine> {
commitments: Vec<CommitmentExtension<'a, C>>,
points: Vec<C::Scalar>,
}
CommitmentExtension groups the polynomial commitment (commitment) and evaluation polynomial on the opening set (low_degree_equivalent). RotationSetExtension is the same with RotationSet except that commitments field is replaced with the vector of CommitmentExtension type.
create_proofAfter having this in mind, let's look into create_proof function.
There is additional challenge y sampled by the verifier in the actual protocol, to random linearly combine the polynomials in the same RotationSet.
let intermediate_sets = construct_intermediate_sets(queries);
let (rotation_sets, super_point_set) = (
intermediate_sets.rotation_sets,
intermediate_sets.super_point_set,
);
let rotation_sets: Vec<RotationSetExtension<E::G1Affine>> = rotation_sets
.into_par_iter()
.map(|rotation_set| {
let commitments: Vec<CommitmentExtension<E::G1Affine>> = rotation_set
.commitments
.as_slice()
.into_par_iter()
.map(|commitment_data| commitment_data.extend(&rotation_set.points))
.collect();
rotation_set.extend(commitments)
})
.collect();
First, the prover builds rotation_sets and super_point_set from the queries. It extends each rotation set by extending the commitments of polynomials with polynomials.
let v: ChallengeV<_> = transcript.squeeze_challenge_scalar();
#[allow(clippy::needless_collect)]
let quotient_polynomials = rotation_sets
.as_slice()
.into_par_iter()
.map(quotient_contribution)
.collect::<Vec<_>>();
let h_x: Polynomial<E::Fr, Coeff> = quotient_polynomials
.into_iter()
.zip(powers(*v))
.map(|(poly, power_of_v)| poly * power_of_v)
.reduce(|acc, poly| acc + &poly)
.unwrap();
let h = self.params.commit(&h_x, Blind::default()).to_affine();
transcript.write_point(h)?;
verify_proofFor the rotation set , suppose that we are given polynomial evaluations on the set as . Verifier should construct the polynomial by Lagrange interpolation, thus Lagrange basis polynomials should be computed in EVM verifier side for each rotation set.
Both halo2 Rust verifier and EVM verifier utilizes memory-efficient algorithm to compute Lagrange basis polynomials using elementary algebra trick.
The constructed polynomial would have the form
Lagrange basis polynomials refer to for each . What we want to compute is the coefficient form of Lagrange basis polynomial. We will denote the coefficient form of the polynomial as the row vector . The algorithm proceeds inductively on .
Suppose that and we are trying to compute . Since , the coefficient of can be computed as . The constant coefficient is .
Let be the coefficient vector of and be the coefficient vector of . Then,
Each coefficient (third row) can be obtained by .