def verify_kzg_proof_batch(
commitments: Sequence[KZGCommitment],
zs: Sequence[BLSFieldElement],
ys: Sequence[BLSFieldElement],
proofs: Sequence[KZGProof],
) -> bool:
"""
Verify multiple KZG proofs efficiently.
"""
assert len(commitments) == len(zs) == len(ys) == len(proofs)
# Compute a random challenge. Note that it does not have to be computed from a hash,
# r just has to be random.
degree_poly = int.to_bytes(FIELD_ELEMENTS_PER_BLOB, 8, KZG_ENDIANNESS)
num_commitments = int.to_bytes(len(commitments), 8, KZG_ENDIANNESS)
data = RANDOM_CHALLENGE_KZG_BATCH_DOMAIN + degree_poly + num_commitments
# Append all inputs to the transcript before we hash
for commitment, z, y, proof in zip(commitments, zs, ys, proofs, strict=True):
data += commitment + bls_field_to_bytes(z) + bls_field_to_bytes(y) + proof
r = hash_to_bls_field(data)
r_powers = compute_powers(r, len(commitments))
# Verify: e(sum r^i proof_i, [s]) ==
# e(sum r^i (commitment_i - [y_i]) + sum r^i z_i proof_i, [1])
proof_lincomb = g1_lincomb(proofs, r_powers)
proof_z_lincomb = g1_lincomb(
proofs, [z * r_power for z, r_power in zip(zs, r_powers, strict=True)]
)
C_minus_ys = [
bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), -y))
for commitment, y in zip(commitments, ys, strict=True)
]
C_minus_y_as_KZGCommitments = [KZGCommitment(bls.G1_to_bytes48(x)) for x in C_minus_ys]
C_minus_y_lincomb = g1_lincomb(C_minus_y_as_KZGCommitments, r_powers)
return bls.pairing_check([
[
bls.bytes48_to_G1(proof_lincomb),
bls.neg(bls.bytes96_to_G2(KZG_SETUP_G2_MONOMIAL[1])),
],
[
bls.add(bls.bytes48_to_G1(C_minus_y_lincomb), bls.bytes48_to_G1(proof_z_lincomb)),
bls.G2(),
],
])