Files
mev-beta/vendor/github.com/crate-crypto/go-eth-kzg/internal/kzg/kzg_verify.go

232 lines
7.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package kzg
import (
"math/big"
"github.com/consensys/gnark-crypto/ecc"
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
"github.com/crate-crypto/go-eth-kzg/internal/utils"
)
// OpeningProof is a struct holding a (cryptographic) proof to the claim that a polynomial f(X) (represented by a
// commitment to it) evaluates at a point `z` to `f(z)`.
type OpeningProof struct {
// Commitment to quotient polynomial (f(X) - f(z))/(X-z)
QuotientCommitment bls12381.G1Affine
// Point that we are evaluating the polynomial at : `z`
InputPoint fr.Element
// ClaimedValue purported value : `f(z)`
ClaimedValue fr.Element
}
// Verify a single KZG proof. See [verify_kzg_proof_impl]. Returns `nil` if verification was successful, an error
// otherwise. If verification failed due to the pairings check it will return [ErrVerifyOpeningProof].
//
// Note: We could make this method faster by storing pre-computations for the generators in G1 and G2
// as we only do scalar multiplications with those in this method.
//
// Modified from [gnark-crypto].
//
// [verify_kzg_proof_impl]: https://github.com/ethereum/consensus-specs/blob/017a8495f7671f5fff2075a9bfc9238c1a0982f8/specs/deneb/polynomial-commitments.md#verify_kzg_proof_impl
// [gnark-crypto]: https://github.com/ConsenSys/gnark-crypto/blob/8f7ca09273c24ed9465043566906cbecf5dcee91/ecc/bls12-381/fr/kzg/kzg.go#L166
func Verify(commitment *Commitment, proof *OpeningProof, openKey *OpeningKey) error {
// [-1]G₂
// It's possible to precompute this, however Negation
// is cheap (2 Fp negations), so doing it per verify
// should be insignificant compared to the rest of Verify.
var negG2 bls12381.G2Affine
negG2.Neg(&openKey.GenG2)
// Convert the G2 generator to Jacobian for
// later computations.
var genG2Jac bls12381.G2Jac
genG2Jac.FromAffine(&openKey.GenG2)
// This has been changed slightly from the way that gnark-crypto
// does it to show the symmetry in the computation required for
// G₂ and G₁. This is the way it is done in the specs.
// [z]G₂
var inputPointG2Jac bls12381.G2Jac
var pointBigInt big.Int
proof.InputPoint.BigInt(&pointBigInt)
inputPointG2Jac.ScalarMultiplication(&genG2Jac, &pointBigInt)
// In the specs, this is denoted as `X_minus_z`
//
// [α - z]G₂
var alphaMinusZG2Jac bls12381.G2Jac
alphaMinusZG2Jac.FromAffine(&openKey.AlphaG2)
alphaMinusZG2Jac.SubAssign(&inputPointG2Jac)
// [α-z]G₂ (Convert to Affine format)
var alphaMinusZG2Aff bls12381.G2Affine
alphaMinusZG2Aff.FromJacobian(&alphaMinusZG2Jac)
// [f(z)]G₁
var claimedValueG1Jac bls12381.G1Jac
var claimedValueG1Aff bls12381.G1Affine
var claimedValueBigInt big.Int
proof.ClaimedValue.BigInt(&claimedValueBigInt)
claimedValueG1Aff.ScalarMultiplication(&openKey.GenG1, &claimedValueBigInt)
claimedValueG1Jac.FromAffine(&claimedValueG1Aff)
// In the specs, this is denoted as `P_minus_y`
//
// [f(α) - f(z)]G₁
var fminusfzG1Jac bls12381.G1Jac
fminusfzG1Jac.FromAffine(commitment)
fminusfzG1Jac.SubAssign(&claimedValueG1Jac)
// [f(α) - f(z)]G₁ (Convert to Affine format)
var fminusfzG1Aff bls12381.G1Affine
fminusfzG1Aff.FromJacobian(&fminusfzG1Jac)
check, err := bls12381.PairingCheck(
[]bls12381.G1Affine{fminusfzG1Aff, proof.QuotientCommitment},
[]bls12381.G2Affine{negG2, alphaMinusZG2Aff},
)
if err != nil {
return err
}
if !check {
return ErrVerifyOpeningProof
}
return nil
}
// BatchVerifyMultiPoints verifies multiple KZG proofs in a batch. See [verify_kzg_proof_batch].
//
// - This method is more efficient than calling [Verify] multiple times.
// - Randomness is used to combine multiple proofs into one.
//
// Modified from [gnark-crypto].
//
// [verify_kzg_proof_batch]: https://github.com/ethereum/consensus-specs/blob/017a8495f7671f5fff2075a9bfc9238c1a0982f8/specs/deneb/polynomial-commitments.md#verify_kzg_proof_batch
// [gnark-crypto]: https://github.com/ConsenSys/gnark-crypto/blob/8f7ca09273c24ed9465043566906cbecf5dcee91/ecc/bls12-381/fr/kzg/kzg.go#L367)
func BatchVerifyMultiPoints(commitments []Commitment, proofs []OpeningProof, openKey *OpeningKey) error {
// Check consistency number of proofs is equal to the number of commitments.
if len(commitments) != len(proofs) {
return ErrInvalidNumDigests
}
batchSize := len(commitments)
// If there is nothing to verify, we return nil
// to signal that verification was true.
//
if batchSize == 0 {
return nil
}
// If batch size is `1`, call Verify
if batchSize == 1 {
return Verify(&commitments[0], &proofs[0], openKey)
}
// Sample random numbers for sampling.
//
// We only need to sample one random number and
// compute powers of that random number. This works
// since powers will produce a vandermonde matrix
// which is linearly independent.
var randomNumber fr.Element
_, err := randomNumber.SetRandom()
if err != nil {
return err
}
randomNumbers := utils.ComputePowers(randomNumber, uint(batchSize))
// Combine random_i*quotient_i
var foldedQuotients bls12381.G1Affine
quotients := make([]bls12381.G1Affine, len(proofs))
for i := 0; i < batchSize; i++ {
quotients[i].Set(&proofs[i].QuotientCommitment)
}
config := ecc.MultiExpConfig{}
_, err = foldedQuotients.MultiExp(quotients, randomNumbers, config)
if err != nil {
return err
}
// Fold commitments and evaluations using randomness
evaluations := make([]fr.Element, batchSize)
for i := 0; i < len(randomNumbers); i++ {
evaluations[i].Set(&proofs[i].ClaimedValue)
}
foldedCommitments, foldedEvaluations, err := fold(commitments, evaluations, randomNumbers)
if err != nil {
return err
}
// Compute commitment to folded Eval
var foldedEvaluationsCommit bls12381.G1Affine
var foldedEvaluationsBigInt big.Int
foldedEvaluations.BigInt(&foldedEvaluationsBigInt)
foldedEvaluationsCommit.ScalarMultiplication(&openKey.GenG1, &foldedEvaluationsBigInt)
// Compute F = foldedCommitments - foldedEvaluationsCommit
foldedCommitments.Sub(&foldedCommitments, &foldedEvaluationsCommit)
// Combine random_i*(point_i*quotient_i)
var foldedPointsQuotients bls12381.G1Affine
for i := 0; i < batchSize; i++ {
randomNumbers[i].Mul(&randomNumbers[i], &proofs[i].InputPoint)
}
_, err = foldedPointsQuotients.MultiExp(quotients, randomNumbers, config)
if err != nil {
return err
}
// `lhs` first pairing
foldedCommitments.Add(&foldedCommitments, &foldedPointsQuotients)
// `lhs` second pairing
foldedQuotients.Neg(&foldedQuotients)
check, err := bls12381.PairingCheck(
[]bls12381.G1Affine{foldedCommitments, foldedQuotients},
[]bls12381.G2Affine{openKey.GenG2, openKey.AlphaG2},
)
if err != nil {
return err
}
if !check {
return ErrVerifyOpeningProof
}
return nil
}
// fold computes two inner products with the same factors:
//
// - Between commitments and factors; This is a multi-exponentiation.
// - Between evaluations and factors; This is a dot product.
//
// Modified slightly from [gnark-crypto].
//
// [gnark-crypto]: https://github.com/ConsenSys/gnark-crypto/blob/8f7ca09273c24ed9465043566906cbecf5dcee91/ecc/bls12-381/fr/kzg/kzg.go#L464
func fold(commitments []Commitment, evaluations, factors []fr.Element) (Commitment, fr.Element, error) {
// Length inconsistency between commitments and evaluations should have been done before calling this function
batchSize := len(commitments)
// Fold the claimed values
var foldedEvaluations, tmp fr.Element
for i := 0; i < batchSize; i++ {
tmp.Mul(&evaluations[i], &factors[i])
foldedEvaluations.Add(&foldedEvaluations, &tmp)
}
// Fold the commitments
var foldedCommitments Commitment
_, err := foldedCommitments.MultiExp(commitments, factors, ecc.MultiExpConfig{})
if err != nil {
return foldedCommitments, foldedEvaluations, err
}
return foldedCommitments, foldedEvaluations, nil
}