Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: optimize DoublePairingCheck i.e. e(a,b)e(c,d) == 1 #1230

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

yelhousni
Copy link
Contributor

@yelhousni yelhousni commented Jul 30, 2024

Description

This PR needs Consensys/gnark-crypto#524 to be merged first.

We define the DoublePairingCheck method which asserts that e(a,b)e(c,d) == 1. We merge the squares in final exp equivalence check for witnessResidue^λ with the Miller loop computation. This PR optimizes KZG and Pedersen commitments.

Type of change

  • New feature (non-breaking change which adds functionality)

How has this been tested?

Added tests for the new method in addition to existing tests for PairingCheck.

How has this been benchmarked?

The PR saves for a double-pairing check:

  • 316,593 scs for BN254
  • 714,146 scs for BLS12-381
  • 3,877 scs for BLS12-377

Checklist:

  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • I did not modify files generated from templates
  • golangci-lint does not output errors locally
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

@yelhousni yelhousni self-assigned this Jul 30, 2024
@yelhousni yelhousni marked this pull request as draft July 30, 2024 01:26
@yelhousni yelhousni added the perf label Jul 30, 2024
@yelhousni yelhousni marked this pull request as ready for review July 30, 2024 19:31
@yelhousni yelhousni requested a review from ivokub July 30, 2024 19:33
@ivokub
Copy link
Collaborator

ivokub commented Sep 2, 2024

Suggested edit:

diff --git a/std/algebra/emulated/sw_bls12381/pairing.go b/std/algebra/emulated/sw_bls12381/pairing.go
index b59d18c8..27391122 100644
--- a/std/algebra/emulated/sw_bls12381/pairing.go
+++ b/std/algebra/emulated/sw_bls12381/pairing.go
@@ -266,9 +266,11 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error {
 }
 
 // DoublePairingCheck calculates the reduced pairing for a 2 pairs of points and asserts if the result is One
-// e(P0, Q0) * e(P1, Q1) =? 1
 //
-// This function doesn't check that the inputs are in the correct subgroups. See AssertIsOnG1 and AssertIsOnG2.
+//	e(P0, Q0) * e(P1, Q1) =? 1
+//
+// This function doesn't check that the inputs are in the correct subgroups. See
+// [Pairing.AssertIsOnG1] and [Pairing.AssertIsOnG2].
 func (pr Pairing) DoublePairingCheck(P [2]*G1Affine, Q [2]*G2Affine) error {
 	// hint the non-residue witness
 	hint, err := pr.curveF.NewHint(doublePairingCheckHint, 18, &P[0].X, &P[0].Y, &P[1].X, &P[1].Y, &Q[0].P.X.A0, &Q[0].P.X.A1, &Q[0].P.Y.A0, &Q[0].P.Y.A1, &Q[1].P.X.A0, &Q[1].P.X.A1, &Q[1].P.Y.A0, &Q[1].P.Y.A1)
diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go
index d0b1c672..bdc9f6b2 100644
--- a/std/algebra/emulated/sw_bn254/pairing.go
+++ b/std/algebra/emulated/sw_bn254/pairing.go
@@ -266,9 +266,11 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error {
 }
 
 // DoublePairingCheck calculates the reduced pairing for a 2 pairs of points and asserts if the result is One
-// e(P0, Q0) * e(P1, Q1) =? 1
 //
-// This function doesn't check that the inputs are in the correct subgroups. See AssertIsOnG1 and AssertIsOnG2.
+//	e(P0, Q0) * e(P1, Q1) =? 1
+//
+// This function doesn't check that the inputs are in the correct subgroups. See
+// [Pairing.AssertIsOnG1] and [Pairing.AssertIsOnG2].
 func (pr Pairing) DoublePairingCheck(P [2]*G1Affine, Q [2]*G2Affine) error {
 	// hint the non-residue witness
 	hint, err := pr.curveF.NewHint(doublePairingCheckHint, 18, &P[0].X, &P[0].Y, &P[1].X, &P[1].Y, &Q[0].P.X.A0, &Q[0].P.X.A1, &Q[0].P.Y.A0, &Q[0].P.Y.A1, &Q[1].P.X.A0, &Q[1].P.X.A1, &Q[1].P.Y.A0, &Q[1].P.Y.A1)
diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go
index 2c481e75..7a7dc331 100644
--- a/std/algebra/emulated/sw_bw6761/pairing.go
+++ b/std/algebra/emulated/sw_bw6761/pairing.go
@@ -164,7 +164,8 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error {
 // DoublePairingCheck calculates the reduced pairing for a 2 pairs of points and asserts if the result is One
 // e(P0, Q0) * e(P1, Q1) =? 1
 //
-// This function doesn't check that the inputs are in the correct subgroups. See AssertIsOnG1 and AssertIsOnG2.
+// This function doesn't check that the inputs are in the correct subgroups. See
+// [Pairing.AssertIsOnG1] and [Pairing.AssertIsOnG2].
 func (pr Pairing) DoublePairingCheck(P [2]*G1Affine, Q [2]*G2Affine) error {
 	return pr.PairingCheck(P[:], Q[:])
 }
diff --git a/std/algebra/native/sw_bls12377/pairing.go b/std/algebra/native/sw_bls12377/pairing.go
index 75e2af03..3dcd4c16 100644
--- a/std/algebra/native/sw_bls12377/pairing.go
+++ b/std/algebra/native/sw_bls12377/pairing.go
@@ -274,9 +274,10 @@ func PairingCheck(api frontend.API, P []G1Affine, Q []G2Affine) error {
 }
 
 // DoublePairingCheck calculates the reduced pairing for 2 pairs of points and asserts if the result is One
-// e(P0, Q0) * e(P1, Q1) =? 1
 //
-// This function doesn't check that the inputs are in the correct subgroups
+//	e(P0, Q0) * e(P1, Q1) =? 1
+//
+// This function doesn't check that the inputs are in the correct subgroups.
 func DoublePairingCheck(api frontend.API, P [2]G1Affine, Q [2]G2Affine) error {
 	// hint the non-residue witness
 	hint, err := api.NewHint(doublePairingCheckHint, 18, P[0].X, P[0].Y, P[1].X, P[1].Y, Q[0].P.X.A0, Q[0].P.X.A1, Q[0].P.Y.A0, Q[0].P.Y.A1, Q[1].P.X.A0, Q[1].P.X.A1, Q[1].P.Y.A0, Q[1].P.Y.A1)
diff --git a/std/algebra/native/sw_bls12377/pairing2.go b/std/algebra/native/sw_bls12377/pairing2.go
index b765d731..769c220f 100644
--- a/std/algebra/native/sw_bls12377/pairing2.go
+++ b/std/algebra/native/sw_bls12377/pairing2.go
@@ -330,9 +330,12 @@ func (p *Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error {
 	return nil
 }
 
-// DoublePairingCheck computes the multi-pairing of the 2 input pairs and asserts that
-// the result is an identity element in the target group. It returns an error if
-// there is a mismatch between the lengths of the inputs.
+// DoublePairingCheck calculates the reduced pairing for a 2 pairs of points and asserts if the result is One
+//
+//	e(P0, Q0) * e(P1, Q1) =? 1
+//
+// This function doesn't check that the inputs are in the correct subgroups. See
+// [Pairing.AssertIsOnG1] and [Pairing.AssertIsOnG2].
 func (p *Pairing) DoublePairingCheck(P [2]*G1Affine, Q [2]*G2Affine) error {
 	var inP [2]G1Affine
 	inP[0] = *P[0]
diff --git a/std/algebra/native/sw_bls24315/pairing2.go b/std/algebra/native/sw_bls24315/pairing2.go
index b86d06ee..9d7f97de 100644
--- a/std/algebra/native/sw_bls24315/pairing2.go
+++ b/std/algebra/native/sw_bls24315/pairing2.go
@@ -313,9 +313,12 @@ func (p *Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error {
 	return nil
 }
 
-// DoublePairingCheck computes the multi-pairing of the 2 input pairs and asserts that
-// the result is an identity element in the target group. It returns an error if
-// there is a mismatch between the lengths of the inputs.
+// DoublePairingCheck calculates the reduced pairing for a 2 pairs of points and asserts if the result is One
+//
+//	e(P0, Q0) * e(P1, Q1) =? 1
+//
+// This function doesn't check that the inputs are in the correct subgroups. See
+// [Pairing.AssertIsOnG1] and [Pairing.AssertIsOnG2].
 func (p *Pairing) DoublePairingCheck(P [2]*G1Affine, Q [2]*G2Affine) error {
 	return p.PairingCheck(P[:], Q[:])
 }

Copy link
Collaborator

@ivokub ivokub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in general it looks good. I only suggested some edits to the method documentation and maybe changing one of the hint functions to be a bit more descriptive (it took me a few minutes to understand what was happening in the loops).

I'm testing locally against gnark-crypto commit ffaaea73e1b67358e625343c0d32fc785792b3f6 and it looks like tests pass. That corresponding gnark-crypto PR is also good to merge on my side.

x.Exp(residueWitness, &exp2)

// 3^t is ord(x^3 / residueWitness)
x3.Square(&x).Mul(&x3, &x).Mul(&x3, &residueWitnessInv)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can assume that x cannot be zero at this point? It is related to the output of the Miller loop, but I think we can assume it is never zero for any inputs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes x is some nonzero power of the Miller loop which cannot be 0

}

for t != 0 {
x.Mul(&x, tmp.Exp(root27thOf1, &exp2))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo this part is a bit difficult to understand. We essentially take this loop only if x^3 * residueWitnessInv != 1, maybe we could have the check implict?

Maybe something like:

for i := 0; i < someBound; i++ {
    if i > 0 {
        x.Mul(&x, tmp.Exp(root27thof1, &exp2))
    }
    x3.Square(&x).Mul(&x3, &x).Mul(&x3, &residueWitnessInv)
    if x3.IsOne() {
        continue
    }
    for !x3.IsOne() {
        tmp.Square(&x3)
        x3.Mul(&tmp, &x3)
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that would work too I guess

@yelhousni yelhousni changed the title perf: optimize DoublePairingChekc i.e. e(a,b)e(c,d) == 1 perf: optimize DoublePairingCheck i.e. e(a,b)e(c,d) == 1 Sep 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants