diff --git a/std/commitments/kzg/native_doc_test.go b/std/commitments/kzg/native_doc_test.go index f5ff956aa5..691acb01f8 100644 --- a/std/commitments/kzg/native_doc_test.go +++ b/std/commitments/kzg/native_doc_test.go @@ -71,21 +71,29 @@ func Example_native() { panic("commitment witness failed: " + err.Error()) } - // create a witness element of the opening proof and the evaluation point - wProof, err := kzg.ValueOfOpeningProof[sw_bls12377.ScalarField, sw_bls12377.G1Affine](point, proof) + // create a witness element of the opening proof + wProof, err := kzg.ValueOfOpeningProof[sw_bls12377.ScalarField, sw_bls12377.G1Affine](proof) if err != nil { panic("opening proof witness failed: " + err.Error()) } // create a witness element of the SRS - wVk, err := kzg.ValueOfVerifyingKey[sw_bls12377.G2Affine](srs.Vk) + wVk, err := kzg.ValueOfVerifyingKey[sw_bls12377.G1Affine, sw_bls12377.G2Affine](srs.Vk) if err != nil { panic("verifying key witness failed: " + err.Error()) } + + // create a witness element of the evaluation point + wPt, err := kzg.ValueOfScalar[sw_bls12377.ScalarField](point) + if err != nil { + panic("point witness failed: " + err.Error()) + } + assignment := KZGVerificationCircuit[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{ VerifyingKey: wVk, Commitment: wCmt, OpeningProof: wProof, + Point: wPt, } circuit := KZGVerificationCircuit[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{} diff --git a/std/commitments/kzg/nonnative_doc_test.go b/std/commitments/kzg/nonnative_doc_test.go index 0289ee0959..9278d41821 100644 --- a/std/commitments/kzg/nonnative_doc_test.go +++ b/std/commitments/kzg/nonnative_doc_test.go @@ -17,22 +17,18 @@ import ( ) type KZGVerificationCircuit[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT, GTEl algebra.GtElementT] struct { - kzg.VerifyingKey[G2El] + kzg.VerifyingKey[G1El, G2El] kzg.Commitment[G1El] kzg.OpeningProof[FR, G1El] + Point emulated.Element[FR] } func (c *KZGVerificationCircuit[FR, G1El, G2El, GTEl]) Define(api frontend.API) error { - curve, err := algebra.GetCurve[FR, G1El](api) + verifier, err := kzg.NewVerifier[FR, G1El, G2El, GTEl](api) if err != nil { - return fmt.Errorf("get curve: %w", err) + return fmt.Errorf("new verifier: %w", err) } - pairing, err := algebra.GetPairing[G1El, G2El, GTEl](api) - if err != nil { - return fmt.Errorf("get pairing: %w", err) - } - verifier := kzg.NewVerifier(c.VerifyingKey, curve, pairing) - if err := verifier.AssertProof(c.Commitment, c.OpeningProof); err != nil { + if err := verifier.CheckOpeningProof(c.Commitment, c.OpeningProof, c.Point, c.VerifyingKey); err != nil { return fmt.Errorf("assert proof: %w", err) } return nil @@ -94,21 +90,29 @@ func Example_emulated() { panic("commitment witness failed: " + err.Error()) } - // create a witness element of the opening proof and the evaluation point - wProof, err := kzg.ValueOfOpeningProof[sw_bn254.ScalarField, sw_bn254.G1Affine](point, proof) + // create a witness element of the opening proof + wProof, err := kzg.ValueOfOpeningProof[sw_bn254.ScalarField, sw_bn254.G1Affine](proof) if err != nil { panic("opening proof witness failed: " + err.Error()) } // create a witness element of the SRS - wVk, err := kzg.ValueOfVerifyingKey[sw_bn254.G2Affine](srs.Vk) + wVk, err := kzg.ValueOfVerifyingKey[sw_bn254.G1Affine, sw_bn254.G2Affine](srs.Vk) if err != nil { panic("verifying key witness failed: " + err.Error()) } + + // create a witness element of the evaluation point + wPt, err := kzg.ValueOfScalar[sw_bn254.ScalarField](point) + if err != nil { + panic("point witness failed: " + err.Error()) + } + assignment := KZGVerificationCircuit[sw_bn254.ScalarField, sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl]{ VerifyingKey: wVk, Commitment: wCmt, OpeningProof: wProof, + Point: wPt, } circuit := KZGVerificationCircuit[sw_bn254.ScalarField, sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl]{} diff --git a/std/commitments/kzg/verifier.go b/std/commitments/kzg/verifier.go index 60ba6d621d..3b077a6354 100644 --- a/std/commitments/kzg/verifier.go +++ b/std/commitments/kzg/verifier.go @@ -29,6 +29,7 @@ import ( bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" fr_bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" kzg_bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/kzg" + "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/algebra" "github.com/consensys/gnark/std/algebra/emulated/sw_bls12381" "github.com/consensys/gnark/std/algebra/emulated/sw_bn254" @@ -36,8 +37,50 @@ import ( "github.com/consensys/gnark/std/algebra/native/sw_bls12377" "github.com/consensys/gnark/std/algebra/native/sw_bls24315" "github.com/consensys/gnark/std/math/emulated" + "github.com/consensys/gnark/std/recursion" ) +// ValueOfScalar initializes a scalar in a witness from a native scalar (Fr) point. +// The scalars are always emulated. +func ValueOfScalar[FR emulated.FieldParams](scalar any) (emulated.Element[FR], error) { + var ret emulated.Element[FR] + switch s := any(&ret).(type) { + case *emulated.Element[sw_bn254.ScalarField]: + tScalar, ok := scalar.(fr_bn254.Element) + if !ok { + return ret, fmt.Errorf("mismatching types %T %T", ret, tScalar) + } + *s = sw_bn254.NewScalar(tScalar) + case *emulated.Element[sw_bls12377.ScalarField]: + tScalar, ok := scalar.(fr_bls12377.Element) + if !ok { + return ret, fmt.Errorf("mismatching types %T %T", ret, tScalar) + } + *s = sw_bls12377.NewScalar(tScalar) + case *emulated.Element[sw_bls12381.ScalarField]: + tScalar, ok := scalar.(fr_bls12381.Element) + if !ok { + return ret, fmt.Errorf("mismatching types %T %T", ret, tScalar) + } + *s = sw_bls12381.NewScalar(tScalar) + case *emulated.Element[sw_bw6761.ScalarField]: + tScalar, ok := scalar.(fr_bw6761.Element) + if !ok { + return ret, fmt.Errorf("mismatching types %T %T", ret, tScalar) + } + *s = sw_bw6761.NewScalar(tScalar) + case *emulated.Element[sw_bls24315.ScalarField]: + tScalar, ok := scalar.(fr_bls24315.Element) + if !ok { + return ret, fmt.Errorf("mismatching types %T %T", ret, tScalar) + } + *s = sw_bls24315.NewScalar(tScalar) + default: + return ret, fmt.Errorf("unknown type parametrization") + } + return ret, nil +} + // Commitment is an KZG commitment to a polynomial. Use [ValueOfCommitment] to // initialize a witness from the native commitment. type Commitment[G1El algebra.G1ElementT] struct { @@ -90,15 +133,14 @@ func ValueOfCommitment[G1El algebra.G1ElementT](cmt any) (Commitment[G1El], erro // equal to ClaimedValue. Use [ValueOfOpeningProof] to initialize a witness from // a native opening proof. type OpeningProof[FR emulated.FieldParams, G1El algebra.G1ElementT] struct { - QuotientPoly G1El + Quotient G1El ClaimedValue emulated.Element[FR] - Point emulated.Element[FR] } // ValueOfOpeningProof initializes an opening proof from the given proof and // point. It returns an error if there is a mismatch between the type parameters // and types of the provided point and proof. -func ValueOfOpeningProof[FR emulated.FieldParams, G1El algebra.G1ElementT](point any, proof any) (OpeningProof[FR, G1El], error) { +func ValueOfOpeningProof[FR emulated.FieldParams, G1El algebra.G1ElementT](proof any) (OpeningProof[FR, G1El], error) { var ret OpeningProof[FR, G1El] switch s := any(&ret).(type) { case *OpeningProof[sw_bn254.ScalarField, sw_bn254.G1Affine]: @@ -106,61 +148,100 @@ func ValueOfOpeningProof[FR emulated.FieldParams, G1El algebra.G1ElementT](point if !ok { return ret, fmt.Errorf("mismatching types %T %T", ret, proof) } - tPoint, ok := point.(fr_bn254.Element) - if !ok { - return ret, fmt.Errorf("mismatching types %T %T", s.Point, point) - } - s.QuotientPoly = sw_bn254.NewG1Affine(tProof.H) + s.Quotient = sw_bn254.NewG1Affine(tProof.H) s.ClaimedValue = sw_bn254.NewScalar(tProof.ClaimedValue) - s.Point = sw_bn254.NewScalar(tPoint) case *OpeningProof[sw_bls12377.ScalarField, sw_bls12377.G1Affine]: tProof, ok := proof.(kzg_bls12377.OpeningProof) if !ok { return ret, fmt.Errorf("mismatching types %T %T", ret, proof) } - tPoint, ok := point.(fr_bls12377.Element) - if !ok { - return ret, fmt.Errorf("mismatching types %T %T", s.Point, point) - } - s.QuotientPoly = sw_bls12377.NewG1Affine(tProof.H) + s.Quotient = sw_bls12377.NewG1Affine(tProof.H) s.ClaimedValue = sw_bls12377.NewScalar(tProof.ClaimedValue) - s.Point = sw_bls12377.NewScalar(tPoint) case *OpeningProof[sw_bls12381.ScalarField, sw_bls12381.G1Affine]: tProof, ok := proof.(kzg_bls12381.OpeningProof) if !ok { return ret, fmt.Errorf("mismatching types %T %T", ret, proof) } - tPoint, ok := point.(fr_bls12381.Element) - if !ok { - return ret, fmt.Errorf("mismatching types %T %T", s.Point, point) - } - s.QuotientPoly = sw_bls12381.NewG1Affine(tProof.H) + s.Quotient = sw_bls12381.NewG1Affine(tProof.H) s.ClaimedValue = sw_bls12381.NewScalar(tProof.ClaimedValue) - s.Point = sw_bls12381.NewScalar(tPoint) case *OpeningProof[sw_bw6761.ScalarField, sw_bw6761.G1Affine]: tProof, ok := proof.(kzg_bw6761.OpeningProof) if !ok { return ret, fmt.Errorf("mismatching types %T %T", ret, proof) } - tPoint, ok := point.(fr_bw6761.Element) - if !ok { - return ret, fmt.Errorf("mismatching types %T %T", s.Point, point) - } - s.QuotientPoly = sw_bw6761.NewG1Affine(tProof.H) + s.Quotient = sw_bw6761.NewG1Affine(tProof.H) s.ClaimedValue = sw_bw6761.NewScalar(tProof.ClaimedValue) - s.Point = sw_bw6761.NewScalar(tPoint) case *OpeningProof[sw_bls24315.ScalarField, sw_bls24315.G1Affine]: tProof, ok := proof.(kzg_bls24315.OpeningProof) if !ok { return ret, fmt.Errorf("mismatching types %T %T", ret, proof) } - tPoint, ok := point.(fr_bls24315.Element) + s.Quotient = sw_bls24315.NewG1Affine(tProof.H) + s.ClaimedValue = sw_bls24315.NewScalar(tProof.ClaimedValue) + default: + return ret, fmt.Errorf("unknown type parametrization") + } + return ret, nil +} + +type BatchOpeningProof[FR emulated.FieldParams, G1El algebra.G1ElementT] struct { + Quotient G1El + ClaimedValues []emulated.Element[FR] +} + +func ValueOfBatchOpeningProof[FR emulated.FieldParams, G1El any](proof any) (BatchOpeningProof[FR, G1El], error) { + var ret BatchOpeningProof[FR, G1El] + switch s := any(&ret).(type) { + case *BatchOpeningProof[sw_bn254.ScalarField, sw_bn254.G1Affine]: + tProof, ok := proof.(kzg_bn254.BatchOpeningProof) if !ok { - return ret, fmt.Errorf("mismatching types %T %T", s.Point, point) + return ret, fmt.Errorf("mismatching types %T %T", ret, proof) + } + s.Quotient = sw_bn254.NewG1Affine(tProof.H) + s.ClaimedValues = make([]emulated.Element[sw_bn254.ScalarField], len(tProof.ClaimedValues)) + for i := 0; i < len(s.ClaimedValues); i++ { + s.ClaimedValues[i] = sw_bn254.NewScalar(tProof.ClaimedValues[i]) + } + case *BatchOpeningProof[sw_bls12377.ScalarField, sw_bls12377.G1Affine]: + tProof, ok := proof.(kzg_bls12377.BatchOpeningProof) + if !ok { + return ret, fmt.Errorf("mismatching types %T %T", ret, proof) + } + s.Quotient = sw_bls12377.NewG1Affine(tProof.H) + s.ClaimedValues = make([]emulated.Element[sw_bls12377.ScalarField], len(tProof.ClaimedValues)) + for i := 0; i < len(s.ClaimedValues); i++ { + s.ClaimedValues[i] = sw_bls12377.NewScalar(tProof.ClaimedValues[i]) + } + case *BatchOpeningProof[sw_bls12381.ScalarField, sw_bls12381.G1Affine]: + tProof, ok := proof.(kzg_bls12381.BatchOpeningProof) + if !ok { + return ret, fmt.Errorf("mismatching types %T %T", ret, proof) + } + s.Quotient = sw_bls12381.NewG1Affine(tProof.H) + s.ClaimedValues = make([]emulated.Element[sw_bls12381.ScalarField], len(tProof.ClaimedValues)) + for i := 0; i < len(s.ClaimedValues); i++ { + s.ClaimedValues[i] = sw_bls12381.NewScalar(tProof.ClaimedValues[i]) + } + case *BatchOpeningProof[sw_bw6761.ScalarField, sw_bw6761.G1Affine]: + tProof, ok := proof.(kzg_bw6761.BatchOpeningProof) + if !ok { + return ret, fmt.Errorf("mismatching types %T %T", ret, proof) + } + s.Quotient = sw_bw6761.NewG1Affine(tProof.H) + s.ClaimedValues = make([]emulated.Element[sw_bw6761.ScalarField], len(tProof.ClaimedValues)) + for i := 0; i < len(s.ClaimedValues); i++ { + s.ClaimedValues[i] = sw_bw6761.NewScalar(tProof.ClaimedValues[i]) + } + case *BatchOpeningProof[sw_bls24315.ScalarField, sw_bls24315.G1Affine]: + tProof, ok := proof.(kzg_bls24315.BatchOpeningProof) + if !ok { + return ret, fmt.Errorf("mismatching types %T %T", ret, proof) + } + s.Quotient = sw_bls24315.NewG1Affine(tProof.H) + s.ClaimedValues = make([]emulated.Element[sw_bls24315.ScalarField], len(tProof.ClaimedValues)) + for i := 0; i < len(s.ClaimedValues); i++ { + s.ClaimedValues[i] = sw_bls24315.NewScalar(tProof.ClaimedValues[i]) } - s.QuotientPoly = sw_bls24315.NewG1Affine(tProof.H) - s.ClaimedValue = sw_bls24315.NewScalar(tProof.ClaimedValue) - s.Point = sw_bls24315.NewScalar(tPoint) default: return ret, fmt.Errorf("unknown type parametrization") } @@ -169,51 +250,57 @@ func ValueOfOpeningProof[FR emulated.FieldParams, G1El algebra.G1ElementT](point // VerifyingKey is the trusted setup for KZG polynomial commitment scheme. Use // [ValueOfVerifyingKey] to initialize a witness from the native VerifyingKey. -type VerifyingKey[G2El algebra.G2ElementT] struct { - SRS [2]G2El +type VerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT] struct { + G2 [2]G2El + G1 G1El } // ValueOfVerifyingKey initializes verifying key witness from the native // verifying key. It returns an error if there is a mismatch between the type // parameters and the provided verifying key type. -func ValueOfVerifyingKey[G2El algebra.G2ElementT](vk any) (VerifyingKey[G2El], error) { - var ret VerifyingKey[G2El] +func ValueOfVerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT](vk any) (VerifyingKey[G1El, G2El], error) { + var ret VerifyingKey[G1El, G2El] switch s := any(&ret).(type) { - case *VerifyingKey[sw_bn254.G2Affine]: + case *VerifyingKey[sw_bn254.G1Affine, sw_bn254.G2Affine]: tVk, ok := vk.(kzg_bn254.VerifyingKey) if !ok { return ret, fmt.Errorf("mismatching types %T %T", ret, vk) } - s.SRS[0] = sw_bn254.NewG2Affine(tVk.G2[0]) - s.SRS[1] = sw_bn254.NewG2Affine(tVk.G2[1]) - case *VerifyingKey[sw_bls12377.G2Affine]: + s.G1 = sw_bn254.NewG1Affine(tVk.G1) + s.G2[0] = sw_bn254.NewG2Affine(tVk.G2[0]) + s.G2[1] = sw_bn254.NewG2Affine(tVk.G2[1]) + case *VerifyingKey[sw_bls12377.G1Affine, sw_bls12377.G2Affine]: tVk, ok := vk.(kzg_bls12377.VerifyingKey) if !ok { return ret, fmt.Errorf("mismatching types %T %T", ret, vk) } - s.SRS[0] = sw_bls12377.NewG2Affine(tVk.G2[0]) - s.SRS[1] = sw_bls12377.NewG2Affine(tVk.G2[1]) - case *VerifyingKey[sw_bls12381.G2Affine]: + s.G1 = sw_bls12377.NewG1Affine(tVk.G1) + s.G2[0] = sw_bls12377.NewG2Affine(tVk.G2[0]) + s.G2[1] = sw_bls12377.NewG2Affine(tVk.G2[1]) + case *VerifyingKey[sw_bls12381.G1Affine, sw_bls12381.G2Affine]: tVk, ok := vk.(kzg_bls12381.VerifyingKey) if !ok { return ret, fmt.Errorf("mismatching types %T %T", ret, vk) } - s.SRS[0] = sw_bls12381.NewG2Affine(tVk.G2[0]) - s.SRS[1] = sw_bls12381.NewG2Affine(tVk.G2[1]) - case *VerifyingKey[sw_bw6761.G2Affine]: + s.G1 = sw_bls12381.NewG1Affine(tVk.G1) + s.G2[0] = sw_bls12381.NewG2Affine(tVk.G2[0]) + s.G2[1] = sw_bls12381.NewG2Affine(tVk.G2[1]) + case *VerifyingKey[sw_bw6761.G1Affine, sw_bw6761.G2Affine]: tVk, ok := vk.(kzg_bw6761.VerifyingKey) if !ok { return ret, fmt.Errorf("mismatching types %T %T", ret, vk) } - s.SRS[0] = sw_bw6761.NewG2Affine(tVk.G2[0]) - s.SRS[1] = sw_bw6761.NewG2Affine(tVk.G2[1]) - case *VerifyingKey[sw_bls24315.G2Affine]: + s.G1 = sw_bw6761.NewG1Affine(tVk.G1) + s.G2[0] = sw_bw6761.NewG2Affine(tVk.G2[0]) + s.G2[1] = sw_bw6761.NewG2Affine(tVk.G2[1]) + case *VerifyingKey[sw_bls24315.G1Affine, sw_bls24315.G2Affine]: tVk, ok := vk.(kzg_bls24315.VerifyingKey) if !ok { return ret, fmt.Errorf("mismatching types %T %T", ret, vk) } - s.SRS[0] = sw_bls24315.NewG2Affine(tVk.G2[0]) - s.SRS[1] = sw_bls24315.NewG2Affine(tVk.G2[1]) + s.G1 = sw_bls24315.NewG1Affine(tVk.G1) + s.G2[0] = sw_bls24315.NewG2Affine(tVk.G2[0]) + s.G2[1] = sw_bls24315.NewG2Affine(tVk.G2[1]) default: return ret, fmt.Errorf("unknown type parametrization") } @@ -222,44 +309,279 @@ func ValueOfVerifyingKey[G2El algebra.G2ElementT](vk any) (VerifyingKey[G2El], e // Verifier allows verifying KZG opening proofs. type Verifier[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.G2ElementT] struct { - VerifyingKey[G2El] - - curve algebra.Curve[FR, G1El] - pairing algebra.Pairing[G1El, G2El, GtEl] + api frontend.API + scalarApi *emulated.Field[FR] + curve algebra.Curve[FR, G1El] + pairing algebra.Pairing[G1El, G2El, GtEl] } // NewVerifier initializes a new Verifier instance. -func NewVerifier[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.G2ElementT](vk VerifyingKey[G2El], curve algebra.Curve[FR, G1El], pairing algebra.Pairing[G1El, G2El, GtEl]) *Verifier[FR, G1El, G2El, GtEl] { - return &Verifier[FR, G1El, G2El, GtEl]{ - VerifyingKey: vk, - curve: curve, - pairing: pairing, +func NewVerifier[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.G2ElementT](api frontend.API) (*Verifier[FR, G1El, G2El, GtEl], error) { + curve, err := algebra.GetCurve[FR, G1El](api) + if err != nil { + return nil, err + } + scalarApi, err := emulated.NewField[FR](api) + if err != nil { + return nil, err } + pairing, err := algebra.GetPairing[G1El, G2El, GtEl](api) + if err != nil { + return nil, err + } + return &Verifier[FR, G1El, G2El, GtEl]{ + api: api, + scalarApi: scalarApi, + curve: curve, + pairing: pairing, + }, nil } -// AssertProof asserts the validity of the opening proof for the given -// commitment. -func (vk *Verifier[FR, G1El, G2El, GtEl]) AssertProof(commitment Commitment[G1El], proof OpeningProof[FR, G1El]) error { - // [f(a)]G₁ - claimedValueG1 := vk.curve.ScalarMulBase(&proof.ClaimedValue) +// CheckOpeningProof asserts the validity of the opening proof for the given +// commitment at point. +func (v *Verifier[FR, G1El, G2El, GTEl]) CheckOpeningProof(commitment Commitment[G1El], proof OpeningProof[FR, G1El], point emulated.Element[FR], vk VerifyingKey[G1El, G2El]) error { + + claimedValueG1 := v.curve.ScalarMulBase(&proof.ClaimedValue) // [f(α) - f(a)]G₁ - fminusfaG1 := vk.curve.Neg(claimedValueG1) - fminusfaG1 = vk.curve.Add(fminusfaG1, &commitment.G1El) + fminusfaG1 := v.curve.Neg(claimedValueG1) + fminusfaG1 = v.curve.Add(fminusfaG1, &commitment.G1El) // [-H(α)]G₁ - negQuotientPoly := vk.curve.Neg(&proof.QuotientPoly) + negQuotientPoly := v.curve.Neg(&proof.Quotient) // [f(α) - f(a) + a*H(α)]G₁ - totalG1 := vk.curve.ScalarMul(&proof.QuotientPoly, &proof.Point) - totalG1 = vk.curve.Add(totalG1, fminusfaG1) + totalG1 := v.curve.ScalarMul(&proof.Quotient, &point) + totalG1 = v.curve.Add(totalG1, fminusfaG1) // e([f(α)-f(a)+aH(α)]G₁], G₂).e([-H(α)]G₁, [α]G₂) == 1 - if err := vk.pairing.PairingCheck( + if err := v.pairing.PairingCheck( []*G1El{totalG1, negQuotientPoly}, - []*G2El{&vk.SRS[0], &vk.SRS[1]}, + []*G2El{&vk.G2[0], &vk.G2[1]}, ); err != nil { return fmt.Errorf("pairing check: %w", err) } return nil } + +func (v *Verifier[FR, G1El, G2El, GTEl]) BatchVerifySinglePoint(digests []Commitment[G1El], batchOpeningProof BatchOpeningProof[FR, G1El], point emulated.Element[FR], vk VerifyingKey[G1El, G2El], dataTranscript ...frontend.Variable) error { + // fold the proof + foldedProof, foldedDigest, err := v.FoldProof(digests, batchOpeningProof, point, dataTranscript...) + if err != nil { + return fmt.Errorf("fold proofs: %w", err) + } + // verify the foldedProof against the foldedDigest + err = v.CheckOpeningProof(foldedDigest, foldedProof, point, vk) + if err != nil { + return fmt.Errorf("check opening proof: %w", err) + } + return nil +} + +func (v *Verifier[FR, G1El, G2El, GTEl]) BatchVerifyMultiPoints(digests []Commitment[G1El], proofs []OpeningProof[FR, G1El], points []emulated.Element[FR], vk VerifyingKey[G1El, G2El]) error { + var fr FR + + // check consistency nb proogs vs nb digests + if len(digests) != len(proofs) { + return fmt.Errorf("number of commitments doesn't match number of proofs") + } + if len(digests) != len(points) { + return fmt.Errorf("number of commitments doesn't match number of points ") + } + + // len(digests) should be nonzero because of randomNumbers + if len(digests) == 0 { + return fmt.Errorf("number of digests should be nonzero") + } + + // if only one digest, call Verify + if len(digests) == 1 { + return v.CheckOpeningProof(digests[0], proofs[0], points[0], vk) + } + + // sample random numbers λᵢ for sampling + randomNumbers := make([]*emulated.Element[FR], len(digests)) + randomNumbers[0] = v.scalarApi.One() + whSnark, err := recursion.NewHash(v.api, fr.Modulus(), true) + if err != nil { + return err + } + for i := 0; i < len(digests); i++ { + marshalledG1 := v.curve.MarshalG1(digests[i].G1El) + whSnark.Write(marshalledG1...) + marshalledG1 = v.curve.MarshalG1(proofs[i].Quotient) + whSnark.Write(marshalledG1...) + marshalledScalar := v.curve.MarshalScalar(proofs[i].ClaimedValue) + whSnark.Write(marshalledScalar...) + marshalledScalar = v.curve.MarshalScalar(points[i]) + whSnark.Write(marshalledScalar...) + } + + seed := whSnark.Sum() + binSeed := v.api.ToBinary(seed) + randomNumbers[1] = v.scalarApi.FromBits(binSeed...) + + for i := 2; i < len(randomNumbers); i++ { + // TODO use real random numbers, follow the solidity smart contract to know which variables are used as seed + randomNumbers[i] = v.scalarApi.Mul(randomNumbers[1], randomNumbers[i-1]) + } + + // fold the committed quotients compute ∑ᵢλᵢ[Hᵢ(α)]G₁ + var foldedQuotients *G1El + quotients := make([]G1El, len(proofs)) + for i := 0; i < len(randomNumbers); i++ { + quotients[i] = proofs[i].Quotient + } + foldedQuotients = v.curve.ScalarMul("ients[0], randomNumbers[0]) + for i := 1; i < len(digests); i++ { + tmp := v.curve.ScalarMul("ients[i], randomNumbers[i]) + foldedQuotients = v.curve.Add(tmp, foldedQuotients) + } + + // fold digests and evals + evals := make([]emulated.Element[FR], len(digests)) + + // fold the digests: ∑ᵢλᵢ[f_i(α)]G₁ + // fold the evals : ∑ᵢλᵢfᵢ(aᵢ) + for i := 0; i < len(digests); i++ { + evals[i] = proofs[i].ClaimedValue + } + + foldedDigests, foldedEvals := v.fold(digests, evals, randomNumbers) + + // compute commitment to folded Eval [∑ᵢλᵢfᵢ(aᵢ)]G₁ + foldedEvalsCommit := v.curve.ScalarMul(&vk.G1, foldedEvals) + + // compute foldedDigests = ∑ᵢλᵢ[fᵢ(α)]G₁ - [∑ᵢλᵢfᵢ(aᵢ)]G₁ + tmp := v.curve.Neg(foldedEvalsCommit) + var foldedDigest *G1El + foldedDigest = v.curve.Add(&foldedDigests.G1El, tmp) + + // combien the points and the quotients using γᵢ + // ∑ᵢλᵢ[p_i]([Hᵢ(α)]G₁) + var foldedPointsQuotients *G1El + for i := 0; i < len(randomNumbers); i++ { + randomNumbers[i] = v.scalarApi.Mul(randomNumbers[i], &points[i]) + } + foldedPointsQuotients = v.curve.ScalarMul("ients[0], randomNumbers[0]) + for i := 1; i < len(digests); i++ { + tmp = v.curve.ScalarMul("ients[i], randomNumbers[i]) + foldedPointsQuotients = v.curve.Add(foldedPointsQuotients, tmp) + } + + // ∑ᵢλᵢ[f_i(α)]G₁ - [∑ᵢλᵢfᵢ(aᵢ)]G₁ + ∑ᵢλᵢ[p_i]([Hᵢ(α)]G₁) + // = [∑ᵢλᵢf_i(α) - ∑ᵢλᵢfᵢ(aᵢ) + ∑ᵢλᵢpᵢHᵢ(α)]G₁ + foldedDigest = v.curve.Add(foldedDigest, foldedPointsQuotients) + + // -∑ᵢλᵢ[Qᵢ(α)]G₁ + // foldedQuotients.Neg(&foldedQuotients) + foldedQuotients = v.curve.Neg(foldedQuotients) + + // pairing check + err = v.pairing.PairingCheck( + []*G1El{foldedDigest, foldedQuotients}, + []*G2El{&vk.G2[0], &vk.G2[1]}, + ) + if err != nil { + return fmt.Errorf("pairingcheck: %w", err) + } + + return err +} + +func (v *Verifier[FR, G1El, G2El, GTEl]) FoldProof(digests []Commitment[G1El], batchOpeningProof BatchOpeningProof[FR, G1El], point emulated.Element[FR], dataTranscript ...frontend.Variable) (OpeningProof[FR, G1El], Commitment[G1El], error) { + var retP OpeningProof[FR, G1El] + var retC Commitment[G1El] + nbDigests := len(digests) + + // check consistency between numbers of claims vs number of digests + if nbDigests != len(batchOpeningProof.ClaimedValues) { + return retP, retC, fmt.Errorf("length mismatch for digests and claimed values") + } + + // derive the challenge γ, binded to the point and the commitments + gamma, err := v.deriveGamma(point, digests, batchOpeningProof.ClaimedValues, dataTranscript...) + if err != nil { + return retP, retC, err + } + + // fold the claimed values and digests + // gammai = [1,γ,γ²,..,γⁿ⁻¹] + gammai := make([]*emulated.Element[FR], nbDigests) + gammai[0] = v.scalarApi.One() + if nbDigests > 1 { + gammai[1] = gamma + } + for i := 2; i < nbDigests; i++ { + gammai[i] = v.scalarApi.Mul(gammai[i-1], gamma) + } + foldedDigests, foldedEvaluations := v.fold(digests, batchOpeningProof.ClaimedValues, gammai) + return OpeningProof[FR, G1El]{ + Quotient: batchOpeningProof.Quotient, + ClaimedValue: *foldedEvaluations, + }, foldedDigests, nil + +} + +// deriveGamma derives a challenge using Fiat Shamir to fold proofs. +// dataTranscript are supposed to be bits. +func (v *Verifier[FR, G1El, G2El, GTEl]) deriveGamma(point emulated.Element[FR], digests []Commitment[G1El], claimedValues []emulated.Element[FR], dataTranscript ...frontend.Variable) (*emulated.Element[FR], error) { + var fr FR + fs, err := recursion.NewTranscript(v.api, fr.Modulus(), []string{"gamma"}) + if err != nil { + return nil, fmt.Errorf("new transcript: %w", err) + } + if err := fs.Bind("gamma", v.curve.MarshalScalar(point)); err != nil { + return nil, fmt.Errorf("bind point: %w", err) + } + + for i := range digests { + if err := fs.Bind("gamma", v.curve.MarshalG1(digests[i].G1El)); err != nil { + return nil, fmt.Errorf("bind %d-th commitment: %w", i, err) + } + } + for i := range claimedValues { + if err := fs.Bind("gamma", v.curve.MarshalScalar(claimedValues[i])); err != nil { + return nil, fmt.Errorf("bing %d-th claimed value: %w", i, err) + } + } + + if err := fs.Bind("gamma", dataTranscript); err != nil { + return nil, fmt.Errorf("bind data transcript: %w", err) + } + + gamma, err := fs.ComputeChallenge("gamma") + if err != nil { + return nil, fmt.Errorf("compute challenge: %w", err) + } + bGamma := v.api.ToBinary(gamma) + gammaS := v.scalarApi.FromBits(bGamma...) + + return gammaS, nil +} + +func (v *Verifier[FR, G1El, G2El, GTEl]) fold(digests []Commitment[G1El], fai []emulated.Element[FR], ci []*emulated.Element[FR]) (Commitment[G1El], *emulated.Element[FR]) { + // length inconsistency between digests and evaluations should have been done before calling this function + nbDigests := len(digests) + + // fold the claimed values ∑ᵢcᵢf(aᵢ) + var tmp *emulated.Element[FR] + foldedEvaluations := v.scalarApi.Zero() + for i := 0; i < nbDigests; i++ { + tmp = v.scalarApi.Mul(&fai[i], ci[i]) + foldedEvaluations = v.scalarApi.Add(foldedEvaluations, tmp) + } + + // fold the digests ∑ᵢ[cᵢ]([fᵢ(α)]G₁) + foldedDigest := v.curve.ScalarMul(&digests[0].G1El, ci[0]) + for i := 1; i < nbDigests; i++ { + tmp := v.curve.ScalarMul(&digests[i].G1El, ci[i]) + foldedDigest = v.curve.Add(tmp, foldedDigest) + } + + // folding done + return Commitment[G1El]{ + G1El: *foldedDigest, + }, foldedEvaluations + +} diff --git a/std/commitments/kzg/verifier_test.go b/std/commitments/kzg/verifier_test.go index 2d3eefff79..9d57b62b4f 100644 --- a/std/commitments/kzg/verifier_test.go +++ b/std/commitments/kzg/verifier_test.go @@ -3,6 +3,7 @@ package kzg import ( "crypto/rand" "fmt" + "math/big" "testing" "github.com/consensys/gnark-crypto/ecc" @@ -21,6 +22,7 @@ import ( bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" fr_bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" kzg_bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/kzg" + "github.com/consensys/gnark/backend" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/algebra" "github.com/consensys/gnark/std/algebra/emulated/sw_bls12381" @@ -29,6 +31,7 @@ import ( "github.com/consensys/gnark/std/algebra/native/sw_bls12377" "github.com/consensys/gnark/std/algebra/native/sw_bls24315" "github.com/consensys/gnark/std/math/emulated" + "github.com/consensys/gnark/std/recursion" "github.com/consensys/gnark/test" ) @@ -38,22 +41,18 @@ const ( ) type KZGVerificationCircuit[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT, GTEl algebra.GtElementT] struct { - VerifyingKey[G2El] + VerifyingKey[G1El, G2El] Commitment[G1El] OpeningProof[FR, G1El] + Point emulated.Element[FR] } func (c *KZGVerificationCircuit[FR, G1El, G2El, GTEl]) Define(api frontend.API) error { - curve, err := algebra.GetCurve[FR, G1El](api) + verifier, err := NewVerifier[FR, G1El, G2El, GTEl](api) if err != nil { - return fmt.Errorf("get curve: %w", err) + return fmt.Errorf("new verifier: %w", err) } - pairing, err := algebra.GetPairing[G1El, G2El, GTEl](api) - if err != nil { - return fmt.Errorf("get pairing: %w", err) - } - verifier := NewVerifier(c.VerifyingKey, curve, pairing) - if err := verifier.AssertProof(c.Commitment, c.OpeningProof); err != nil { + if err := verifier.CheckOpeningProof(c.Commitment, c.OpeningProof, c.Point, c.VerifyingKey); err != nil { return fmt.Errorf("assert proof: %w", err) } return nil @@ -86,15 +85,18 @@ func TestKZGVerificationEmulated(t *testing.T) { wCmt, err := ValueOfCommitment[sw_bn254.G1Affine](com) assert.NoError(err) - wProof, err := ValueOfOpeningProof[sw_bn254.ScalarField, sw_bn254.G1Affine](point, proof) + wProof, err := ValueOfOpeningProof[sw_bn254.ScalarField, sw_bn254.G1Affine](proof) + assert.NoError(err) + wVk, err := ValueOfVerifyingKey[sw_bn254.G1Affine, sw_bn254.G2Affine](srs.Vk) assert.NoError(err) - wVk, err := ValueOfVerifyingKey[sw_bn254.G2Affine](srs.Vk) + wPt, err := ValueOfScalar[sw_bn254.ScalarField](point) assert.NoError(err) assignment := KZGVerificationCircuit[sw_bn254.ScalarField, sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl]{ VerifyingKey: wVk, Commitment: wCmt, OpeningProof: wProof, + Point: wPt, } assert.CheckCircuit(&KZGVerificationCircuit[sw_bn254.ScalarField, sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl]{}, test.WithValidAssignment(&assignment)) } @@ -126,15 +128,18 @@ func TestKZGVerificationEmulated2(t *testing.T) { wCmt, err := ValueOfCommitment[sw_bls12381.G1Affine](com) assert.NoError(err) - wProof, err := ValueOfOpeningProof[sw_bls12381.ScalarField, sw_bls12381.G1Affine](point, proof) + wProof, err := ValueOfOpeningProof[sw_bls12381.ScalarField, sw_bls12381.G1Affine](proof) assert.NoError(err) - wVk, err := ValueOfVerifyingKey[sw_bls12381.G2Affine](srs.Vk) + wVk, err := ValueOfVerifyingKey[sw_bls12381.G1Affine, sw_bls12381.G2Affine](srs.Vk) + assert.NoError(err) + wPt, err := ValueOfScalar[sw_bls12381.ScalarField](point) assert.NoError(err) assignment := KZGVerificationCircuit[sw_bls12381.ScalarField, sw_bls12381.G1Affine, sw_bls12381.G2Affine, sw_bls12381.GTEl]{ VerifyingKey: wVk, Commitment: wCmt, OpeningProof: wProof, + Point: wPt, } assert.CheckCircuit(&KZGVerificationCircuit[sw_bls12381.ScalarField, sw_bls12381.G1Affine, sw_bls12381.G2Affine, sw_bls12381.GTEl]{}, test.WithValidAssignment(&assignment)) } @@ -166,15 +171,18 @@ func TestKZGVerificationEmulated3(t *testing.T) { wCmt, err := ValueOfCommitment[sw_bw6761.G1Affine](com) assert.NoError(err) - wProof, err := ValueOfOpeningProof[sw_bw6761.ScalarField, sw_bw6761.G1Affine](point, proof) + wProof, err := ValueOfOpeningProof[sw_bw6761.ScalarField, sw_bw6761.G1Affine](proof) + assert.NoError(err) + wVk, err := ValueOfVerifyingKey[sw_bw6761.G1Affine, sw_bw6761.G2Affine](srs.Vk) assert.NoError(err) - wVk, err := ValueOfVerifyingKey[sw_bw6761.G2Affine](srs.Vk) + wPt, err := ValueOfScalar[sw_bw6761.ScalarField](point) assert.NoError(err) assignment := KZGVerificationCircuit[sw_bw6761.ScalarField, sw_bw6761.G1Affine, sw_bw6761.G2Affine, sw_bw6761.GTEl]{ VerifyingKey: wVk, Commitment: wCmt, OpeningProof: wProof, + Point: wPt, } assert.CheckCircuit(&KZGVerificationCircuit[sw_bw6761.ScalarField, sw_bw6761.G1Affine, sw_bw6761.G2Affine, sw_bw6761.GTEl]{}, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BN254)) } @@ -206,15 +214,18 @@ func TestKZGVerificationTwoChain(t *testing.T) { wCmt, err := ValueOfCommitment[sw_bls12377.G1Affine](com) assert.NoError(err) - wProof, err := ValueOfOpeningProof[sw_bls12377.ScalarField, sw_bls12377.G1Affine](point, proof) + wProof, err := ValueOfOpeningProof[sw_bls12377.ScalarField, sw_bls12377.G1Affine](proof) assert.NoError(err) - wVk, err := ValueOfVerifyingKey[sw_bls12377.G2Affine](srs.Vk) + wVk, err := ValueOfVerifyingKey[sw_bls12377.G1Affine, sw_bls12377.G2Affine](srs.Vk) + assert.NoError(err) + wPt, err := ValueOfScalar[sw_bls12377.ScalarField](point) assert.NoError(err) assignment := KZGVerificationCircuit[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{ VerifyingKey: wVk, Commitment: wCmt, OpeningProof: wProof, + Point: wPt, } assert.CheckCircuit(&KZGVerificationCircuit[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{}, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BW6_761)) @@ -247,15 +258,18 @@ func TestKZGVerificationTwoChain2(t *testing.T) { wCmt, err := ValueOfCommitment[sw_bls24315.G1Affine](com) assert.NoError(err) - wProof, err := ValueOfOpeningProof[sw_bls24315.ScalarField, sw_bls24315.G1Affine](point, proof) + wProof, err := ValueOfOpeningProof[sw_bls24315.ScalarField, sw_bls24315.G1Affine](proof) + assert.NoError(err) + wVk, err := ValueOfVerifyingKey[sw_bls24315.G1Affine, sw_bls24315.G2Affine](srs.Vk) assert.NoError(err) - wVk, err := ValueOfVerifyingKey[sw_bls24315.G2Affine](srs.Vk) + wPt, err := ValueOfScalar[sw_bls24315.ScalarField](point) assert.NoError(err) assignment := KZGVerificationCircuit[sw_bls24315.ScalarField, sw_bls24315.G1Affine, sw_bls24315.G2Affine, sw_bls24315.GT]{ VerifyingKey: wVk, Commitment: wCmt, OpeningProof: wProof, + Point: wPt, } assert.CheckCircuit(&KZGVerificationCircuit[sw_bls24315.ScalarField, sw_bls24315.G1Affine, sw_bls24315.G2Affine, sw_bls24315.GT]{}, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BW6_633)) @@ -306,7 +320,7 @@ func TestValueOfOpeningProof(t *testing.T) { H: G1, ClaimedValue: value, } - assignment, err := ValueOfOpeningProof[sw_bn254.ScalarField, sw_bn254.G1Affine](point, proof) + assignment, err := ValueOfOpeningProof[sw_bn254.ScalarField, sw_bn254.G1Affine](proof) assert.NoError(err) _ = assignment }, "bn254") @@ -319,7 +333,7 @@ func TestValueOfOpeningProof(t *testing.T) { H: G1, ClaimedValue: value, } - assignment, err := ValueOfOpeningProof[sw_bls12377.ScalarField, sw_bls12377.G1Affine](point, proof) + assignment, err := ValueOfOpeningProof[sw_bls12377.ScalarField, sw_bls12377.G1Affine](proof) assert.NoError(err) _ = assignment }, "bls12377") @@ -332,7 +346,7 @@ func TestValueOfOpeningProof(t *testing.T) { H: G1, ClaimedValue: value, } - assignment, err := ValueOfOpeningProof[sw_bls12381.ScalarField, sw_bls12381.G1Affine](point, proof) + assignment, err := ValueOfOpeningProof[sw_bls12381.ScalarField, sw_bls12381.G1Affine](proof) assert.NoError(err) _ = assignment }, "bls12381") @@ -345,7 +359,7 @@ func TestValueOfOpeningProof(t *testing.T) { H: G1, ClaimedValue: value, } - assignment, err := ValueOfOpeningProof[sw_bw6761.ScalarField, sw_bw6761.G1Affine](point, proof) + assignment, err := ValueOfOpeningProof[sw_bw6761.ScalarField, sw_bw6761.G1Affine](proof) assert.NoError(err) _ = assignment }, "bw6761") @@ -358,7 +372,7 @@ func TestValueOfOpeningProof(t *testing.T) { H: G1, ClaimedValue: value, } - assignment, err := ValueOfOpeningProof[sw_bls24315.ScalarField, sw_bls24315.G1Affine](point, proof) + assignment, err := ValueOfOpeningProof[sw_bls24315.ScalarField, sw_bls24315.G1Affine](proof) assert.NoError(err) _ = assignment }, "bls24315") @@ -371,7 +385,7 @@ func TestValueOfSRS(t *testing.T) { vk := kzg_bn254.VerifyingKey{ G2: [2]bn254.G2Affine{G2, G2}, } - assignment, err := ValueOfVerifyingKey[sw_bn254.G2Affine](vk) + assignment, err := ValueOfVerifyingKey[sw_bn254.G1Affine, sw_bn254.G2Affine](vk) assert.NoError(err) _ = assignment }, "bn254") @@ -380,7 +394,7 @@ func TestValueOfSRS(t *testing.T) { vk := kzg_bls12377.VerifyingKey{ G2: [2]bls12377.G2Affine{G2, G2}, } - assignment, err := ValueOfVerifyingKey[sw_bls12377.G2Affine](vk) + assignment, err := ValueOfVerifyingKey[sw_bls12377.G1Affine, sw_bls12377.G2Affine](vk) assert.NoError(err) _ = assignment }, "bls12377") @@ -389,7 +403,7 @@ func TestValueOfSRS(t *testing.T) { vk := kzg_bls12381.VerifyingKey{ G2: [2]bls12381.G2Affine{G2, G2}, } - assignment, err := ValueOfVerifyingKey[sw_bls12381.G2Affine](vk) + assignment, err := ValueOfVerifyingKey[sw_bls12381.G1Affine, sw_bls12381.G2Affine](vk) assert.NoError(err) _ = assignment }, "bls12381") @@ -398,7 +412,7 @@ func TestValueOfSRS(t *testing.T) { vk := kzg_bw6761.VerifyingKey{ G2: [2]bw6761.G2Affine{G2, G2}, } - assignment, err := ValueOfVerifyingKey[sw_bw6761.G2Affine](vk) + assignment, err := ValueOfVerifyingKey[sw_bw6761.G1Affine, sw_bw6761.G2Affine](vk) assert.NoError(err) _ = assignment }, "bw6761") @@ -407,8 +421,276 @@ func TestValueOfSRS(t *testing.T) { vk := kzg_bls24315.VerifyingKey{ G2: [2]bls24315.G2Affine{G2, G2}, } - assignment, err := ValueOfVerifyingKey[sw_bls24315.G2Affine](vk) + assignment, err := ValueOfVerifyingKey[sw_bls24315.G1Affine, sw_bls24315.G2Affine](vk) assert.NoError(err) _ = assignment }, "bls24315") } + +type FoldProofTest[FR emulated.FieldParams, G1El, G2El, GTEl any] struct { + Point emulated.Element[FR] + Digests [10]Commitment[G1El] + BatchOpeningProof BatchOpeningProof[FR, G1El] + ExpectedFoldedProof OpeningProof[FR, G1El] + ExpectedFoldedDigest Commitment[G1El] +} + +func (c *FoldProofTest[FR, G1El, G2El, GTEl]) Define(api frontend.API) error { + verifier, err := NewVerifier[FR, G1El, G2El, GTEl](api) + if err != nil { + return fmt.Errorf("get pairing: %w", err) + } + + foldedProof, foldedDigests, err := verifier.FoldProof(c.Digests[:], c.BatchOpeningProof, c.Point) + if err != nil { + return err + } + + curve, err := algebra.GetCurve[FR, G1El](api) + if err != nil { + return err + } + + curve.AssertIsEqual(&foldedDigests.G1El, &c.ExpectedFoldedDigest.G1El) + curve.AssertIsEqual(&foldedProof.Quotient, &c.ExpectedFoldedProof.Quotient) + + f, err := emulated.NewField[FR](api) + if err != nil { + return err + } + f.AssertIsEqual(&foldedProof.ClaimedValue, &c.ExpectedFoldedProof.ClaimedValue) + + return nil +} +func TestFoldProof(t *testing.T) { + + assert := test.NewAssert(t) + + // prepare test data + alpha, err := rand.Int(rand.Reader, ecc.BN254.ScalarField()) + assert.NoError(err) + srs, err := kzg_bn254.NewSRS(kzgSize, alpha) + assert.NoError(err) + + var polynomials [10][]fr_bn254.Element + var coms [10]kzg_bn254.Digest + for i := 0; i < 10; i++ { + polynomials[i] = make([]fr_bn254.Element, polynomialSize) + for j := 0; j < polynomialSize; j++ { + polynomials[i][j].SetRandom() + } + coms[i], err = kzg_bn254.Commit(polynomials[i], srs.Pk) + assert.NoError(err) + } + + var point fr_bn254.Element + point.SetRandom() + var target big.Int + target.SetUint64(1) + nbBits := ecc.BLS12_381.ScalarField().BitLen() + nn := ((nbBits+7)/8)*8 - 8 + target.Lsh(&target, uint(nn)) + h, err := recursion.NewShort(ecc.BLS12_381.ScalarField(), &target) + assert.NoError(err) + + batchOpeningProof, err := kzg_bn254.BatchOpenSinglePoint(polynomials[:], coms[:], point, h, srs.Pk) + assert.NoError(err) + + foldedProofs, foldedDigest, err := kzg_bn254.FoldProof(coms[:], &batchOpeningProof, point, h) + assert.NoError(err) + + // prepare witness + wPoint, err := ValueOfScalar[emulated.BN254Fr](point) + assert.NoError(err) + var wDigests [10]Commitment[sw_bn254.G1Affine] + for i := 0; i < 10; i++ { + wDigests[i], err = ValueOfCommitment[sw_bn254.G1Affine](coms[i]) + assert.NoError(err) + } + wBatchOpeningProof, err := ValueOfBatchOpeningProof[emulated.BN254Fr, sw_bn254.G1Affine](batchOpeningProof) + assert.NoError(err) + wExpectedFoldedProof, err := ValueOfOpeningProof[emulated.BN254Fr, sw_bn254.G1Affine](foldedProofs) + assert.NoError(err) + wExpectedFoldedDigest, err := ValueOfCommitment[sw_bn254.G1Affine](foldedDigest) + assert.NoError(err) + + assignment := FoldProofTest[emulated.BN254Fr, sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl]{ + Point: wPoint, + Digests: wDigests, + BatchOpeningProof: wBatchOpeningProof, + ExpectedFoldedProof: wExpectedFoldedProof, + ExpectedFoldedDigest: wExpectedFoldedDigest, + } + + var circuit FoldProofTest[emulated.BN254Fr, sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl] + circuit.BatchOpeningProof.ClaimedValues = make([]emulated.Element[emulated.BN254Fr], 10) + assert.CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BLS12_381), test.WithBackends(backend.PLONK)) + +} + +type BatchVerifySinglePointTest[S emulated.FieldParams, G1El, G2El, GTEl any] struct { + Vk VerifyingKey[G1El, G2El] + Point emulated.Element[S] + Digests [10]Commitment[G1El] + BatchOpeningProof BatchOpeningProof[S, G1El] +} + +func (c *BatchVerifySinglePointTest[S, G1El, G2El, GTEl]) Define(api frontend.API) error { + verifier, err := NewVerifier[S, G1El, G2El, GTEl](api) + if err != nil { + return fmt.Errorf("get pairing: %w", err) + } + verifier.BatchVerifySinglePoint(c.Digests[:], c.BatchOpeningProof, c.Point, c.Vk) + + return nil +} + +func TestBatchVerifySinglePoint(t *testing.T) { + + assert := test.NewAssert(t) + + // prepare test data + alpha, err := rand.Int(rand.Reader, ecc.BN254.ScalarField()) + assert.NoError(err) + srs, err := kzg_bn254.NewSRS(kzgSize, alpha) + assert.NoError(err) + + var polynomials [10][]fr_bn254.Element + var coms [10]kzg_bn254.Digest + for i := 0; i < 10; i++ { + polynomials[i] = make([]fr_bn254.Element, polynomialSize) + for j := 0; j < polynomialSize; j++ { + polynomials[i][j].SetRandom() + } + coms[i], err = kzg_bn254.Commit(polynomials[i], srs.Pk) + assert.NoError(err) + } + + // random point at which the polynomials are evaluated + var point fr_bn254.Element + point.SetRandom() + + // build short hash, we pick a number one byte less than the snark field... + var target big.Int + target.SetUint64(1) + nbBits := ecc.BLS12_381.ScalarField().BitLen() + nn := ((nbBits+7)/8)*8 - 8 + target.Lsh(&target, uint(nn)) + h, err := recursion.NewShort(ecc.BLS12_381.ScalarField(), &target) + assert.NoError(err) + + batchOpeningProof, err := kzg_bn254.BatchOpenSinglePoint(polynomials[:], coms[:], point, h, srs.Pk) + assert.NoError(err) + + err = kzg_bn254.BatchVerifySinglePoint(coms[:], &batchOpeningProof, point, h, srs.Vk) + assert.NoError(err) + + // prepare witness + wVk, err := ValueOfVerifyingKey[sw_bn254.G1Affine, sw_bn254.G2Affine](srs.Vk) + assert.NoError(err) + wPoint, err := ValueOfScalar[emulated.BN254Fr](point) + assert.NoError(err) + var wDigests [10]Commitment[sw_bn254.G1Affine] + for i := 0; i < 10; i++ { + wDigests[i], err = ValueOfCommitment[sw_bn254.G1Affine](coms[i]) + assert.NoError(err) + } + wBatchOpeningProof, err := ValueOfBatchOpeningProof[emulated.BN254Fr, sw_bn254.G1Affine](batchOpeningProof) + assert.NoError(err) + + assignment := BatchVerifySinglePointTest[emulated.BN254Fr, sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl]{ + Vk: wVk, + Point: wPoint, + Digests: wDigests, + BatchOpeningProof: wBatchOpeningProof, + } + + var circuit BatchVerifySinglePointTest[emulated.BN254Fr, sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl] + circuit.BatchOpeningProof.ClaimedValues = make([]emulated.Element[emulated.BN254Fr], 10) + assert.CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BLS12_381), test.WithBackends(backend.PLONK)) + +} + +type BatchVerifyMultiPointsTest[S emulated.FieldParams, G1El, G2El, GTEl any] struct { + Vk VerifyingKey[G1El, G2El] + Digests [4]Commitment[G1El] + Proofs [4]OpeningProof[S, G1El] + Points [4]emulated.Element[S] +} + +func (circuit *BatchVerifyMultiPointsTest[S, G1El, G2El, GTEl]) Define(api frontend.API) error { + + verifier, err := NewVerifier[S, G1El, G2El, GTEl](api) + if err != nil { + return fmt.Errorf("get pairing: %w", err) + } + + verifier.BatchVerifyMultiPoints(circuit.Digests[:], circuit.Proofs[:], circuit.Points[:], circuit.Vk) + + return nil +} + +func TestBatchVerifyMultiPoints(t *testing.T) { + + assert := test.NewAssert(t) + + // prepare test data + alpha, err := rand.Int(rand.Reader, ecc.BN254.ScalarField()) + assert.NoError(err) + srs, err := kzg_bn254.NewSRS(kzgSize, alpha) + assert.NoError(err) + + var polynomials [4][]fr_bn254.Element + var coms [4]kzg_bn254.Digest + for i := 0; i < 4; i++ { + polynomials[i] = make([]fr_bn254.Element, polynomialSize) + for j := 0; j < polynomialSize; j++ { + polynomials[i][j].SetRandom() + } + coms[i], err = kzg_bn254.Commit(polynomials[i], srs.Pk) + assert.NoError(err) + } + + // random points at which the polynomials are evaluated + var points [4]fr_bn254.Element + for i := 0; i < 4; i++ { + points[i].SetRandom() + } + + // build opening proofs + var openingProofs [4]kzg_bn254.OpeningProof + for i := 0; i < 4; i++ { + openingProofs[i], err = kzg_bn254.Open(polynomials[i], points[i], srs.Pk) + assert.NoError(err) + } + + // check that the proofs are correct + err = kzg_bn254.BatchVerifyMultiPoints(coms[:], openingProofs[:], points[:], srs.Vk) + assert.NoError(err) + + // prepare witness + wVk, err := ValueOfVerifyingKey[sw_bn254.G1Affine, sw_bn254.G2Affine](srs.Vk) + assert.NoError(err) + var wDigests [4]Commitment[sw_bn254.G1Affine] + var wPoints [4]emulated.Element[emulated.BN254Fr] + var wOpeningProofs [4]OpeningProof[emulated.BN254Fr, sw_bn254.G1Affine] + for i := 0; i < 4; i++ { + wPoints[i], err = ValueOfScalar[emulated.BN254Fr](points[i]) + assert.NoError(err) + wDigests[i], err = ValueOfCommitment[sw_bn254.G1Affine](coms[i]) + assert.NoError(err) + wOpeningProofs[i], err = ValueOfOpeningProof[emulated.BN254Fr, sw_bn254.G1Affine](openingProofs[i]) + assert.NoError(err) + } + + assignment := BatchVerifyMultiPointsTest[emulated.BN254Fr, sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl]{ + Vk: wVk, + Points: wPoints, + Digests: wDigests, + Proofs: wOpeningProofs, + } + + var circuit BatchVerifyMultiPointsTest[emulated.BN254Fr, sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl] + assert.CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BLS12_381), test.WithBackends(backend.PLONK)) + +}