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: PairingCheck for BN254, BLS12-381, BLS12-377 and BW6-761 #1365

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b6deaf2
perf(bn254): PairingCheck saves ExpByU
yelhousni Dec 20, 2024
e02032b
perf(bls12-381): PairingCheck saves ExpByU
yelhousni Dec 20, 2024
2270e39
Merge branch 'master' into perf/pairing-check
yelhousni Dec 29, 2024
95e6979
Merge branch 'master' into perf/pairing-check
yelhousni Jan 6, 2025
4523b5e
Merge branch 'master' into perf/pairing-check
yelhousni Jan 7, 2025
6a89f91
Merge branch 'perf/pairing-check' of github.com:consensys/gnark into …
yelhousni Jan 7, 2025
61963f5
Merge branch 'master' into perf/pairing-check
yelhousni Jan 8, 2025
2a0dc3e
perf(bls12-377): PairingCheck saves ExpByU
yelhousni Jan 9, 2025
8b6dca1
Merge branch 'master' into perf/pairing-check
yelhousni Jan 9, 2025
7c5567c
Merge branch 'master' into perf/pairing-check
yelhousni Jan 15, 2025
89a5deb
perf(bw6-761): PairingCheck saves ExpByU1
yelhousni Jan 16, 2025
c166d96
refactor: clean code and comments
yelhousni Jan 16, 2025
22d06ad
Merge branch 'perf/pairing-check' of github.com:consensys/gnark into …
yelhousni Jan 16, 2025
2e6ef75
chore: up gnark-crypto
yelhousni Jan 16, 2025
77bf157
chore: run go generate
yelhousni Jan 16, 2025
f518f18
perf(bw6-761): PairingCheck saves ExpByU2
yelhousni Jan 16, 2025
57e4709
docs: correct comments
yelhousni Jan 16, 2025
7e63591
perf: small optims + comments
yelhousni Jan 16, 2025
2fc2f89
refactor: pairing hints
yelhousni Jan 17, 2025
029dcc2
fix(bls12-377): return error in PairingCheck
yelhousni Jan 17, 2025
9f2b497
refactor(bn254, bls12-381): PairingCheck uses millerLoopLines
yelhousni Jan 17, 2025
b4069bd
refactor(bw6-761): PairingCheck uses millerLoopLines
yelhousni Jan 17, 2025
fbddab3
Merge branch 'master' into perf/pairing-check
yelhousni Jan 17, 2025
c23c38b
refactor(bn254): millerLoopAndFinalExpResult
yelhousni Jan 17, 2025
9192b72
Merge branch 'perf/pairing-check' of github.com:consensys/gnark into …
yelhousni Jan 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ toolchain go1.22.6
require (
github.com/bits-and-blooms/bitset v1.14.2
github.com/blang/semver/v4 v4.0.0
github.com/consensys/bavard v0.1.24
github.com/consensys/bavard v0.1.25
github.com/consensys/compress v0.2.5
github.com/consensys/gnark-crypto v0.14.1-0.20241217131346-b998989abdbe
github.com/consensys/gnark-crypto v0.14.1-0.20250116204316-e7fd38b0a0a6
github.com/fxamacker/cbor/v2 v2.7.0
github.com/google/go-cmp v0.6.0
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/consensys/bavard v0.1.24 h1:Lfe+bjYbpaoT7K5JTFoMi5wo9V4REGLvQQbHmatoN2I=
github.com/consensys/bavard v0.1.24/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
github.com/consensys/bavard v0.1.25 h1:5YcSBnp03/HvfpKaIQLr/ecspTp2k8YNR5rQLOWvUyc=
github.com/consensys/bavard v0.1.25/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
github.com/consensys/compress v0.2.5 h1:gJr1hKzbOD36JFsF1AN8lfXz1yevnJi1YolffY19Ntk=
github.com/consensys/compress v0.2.5/go.mod h1:pyM+ZXiNUh7/0+AUjUf9RKUM6vSH7T/fsn5LLS0j1Tk=
github.com/consensys/gnark-crypto v0.14.1-0.20241217131346-b998989abdbe h1:WNuXPe50FqynKlUOMdsi1eCzYN8gU4sdCsW3eg3coGA=
github.com/consensys/gnark-crypto v0.14.1-0.20241217131346-b998989abdbe/go.mod h1:ePFa23CZLMRMHxQpY5nMaiAZ3yuEIayaB8ElEvlwLEs=
github.com/consensys/gnark-crypto v0.14.1-0.20250116204316-e7fd38b0a0a6 h1:P4DeR8HYfQGl4Vj6KEv0Eszcokroit/U1dRrUsgt+js=
github.com/consensys/gnark-crypto v0.14.1-0.20250116204316-e7fd38b0a0a6/go.mod h1:q9s22Y0WIHd9UCBfD+xGeW8wDJ7WAGZZpMrLFqzBzrQ=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
Expand Down
8 changes: 5 additions & 3 deletions internal/tinyfield/element.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 0 additions & 37 deletions std/algebra/emulated/fields_bw6761/e6_pairing.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,43 +423,6 @@ func (e *Ext6) mulBy02345(z *E6, x [5]*baseEl) *E6 {
}
}

// AssertFinalExponentiationIsOne checks that a Miller function output x lies in the
// same equivalence class as the reduced pairing. This replaces the final
// exponentiation step in-circuit.
// The method is adapted from Section 4 of [On Proving Pairings] paper by A. Novakovic and L. Eagen.
//
// [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf
func (e Ext6) AssertFinalExponentiationIsOne(x *E6) {
res, err := e.fp.NewHint(finalExpHint, 6, &x.A0, &x.A1, &x.A2, &x.A3, &x.A4, &x.A5)
if err != nil {
// err is non-nil only for invalid number of inputs
panic(err)
}

residueWitness := E6{
A0: *res[0],
A1: *res[1],
A2: *res[2],
A3: *res[3],
A4: *res[4],
A5: *res[5],
}

// Check that x == residueWitness^λ
// where λ = u^3-u^2+1 - (u+1)p, with u the BW6-761 seed
// and residueWitness from the hint.

// exponentiation by U1=u^3-u^2+1
t0 := e.ExpByU1(&residueWitness)
// exponentiation by U2=u+1
t1 := e.ExpByU2(&residueWitness)

t1 = e.Frobenius(t1)
t0 = e.DivUnchecked(t0, t1)

e.AssertIsEqual(t0, x)
}

// ExpByU2 set z to z^(x₀+1) in E12 and return z
// x₀+1 = 9586122913090633730
func (e Ext6) ExpByU2(z *E6) *E6 {
Expand Down
39 changes: 0 additions & 39 deletions std/algebra/emulated/fields_bw6761/hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ func GetHints() []solver.Hint {
divE6Hint,
inverseE6Hint,
divE6By362880Hint,
finalExpHint,
}
}

Expand Down Expand Up @@ -107,41 +106,3 @@ func divE6By362880Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.In
return nil
})
}

func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error {
// This adapted from section 4.3.2 of https://eprint.iacr.org/2024/640.pdf
return emulated.UnwrapHint(nativeInputs, nativeOutputs,
func(mod *big.Int, inputs, outputs []*big.Int) error {
var millerLoop, residueWitness bw6761.E6
var rInv, mInv big.Int

millerLoop.B0.A0.SetBigInt(inputs[0])
millerLoop.B0.A1.SetBigInt(inputs[2])
millerLoop.B0.A2.SetBigInt(inputs[4])
millerLoop.B1.A0.SetBigInt(inputs[1])
millerLoop.B1.A1.SetBigInt(inputs[3])
millerLoop.B1.A2.SetBigInt(inputs[5])

// 1. compute r-th root:
// Exponentiate to rInv where
// rInv = 1/r mod (p^6-1)/r
rInv.SetString("279142441805511726233822077180198394933430419224185936052953462287387912118470357993263103168031788043160461358474005435622327506926362567154401645657309519073154383052970657693950208844465818979551693587858245321454505472049236704031061301292776853925224359757586505231126091244204292668007110271845616234279927419974150119801003450133674289144711275201991607282264849765236206295842916353255855388186086438329721887082685697023028663652777877691341551982676874308309620809049793085180324511691754953492619183755890255644855765188965000691813063771086522132765764526955251054211157804606693386854395171192876178005945476647006847460976477055233044799299417913662363985523123796056692751028712679181978298499780752966303529102009307348414562366180130429432094237007700663759126264893082917308542509779442201840676518234962495304673134599305371982876385622279935346701152286347948653741121231188575146952014672242471261647823749129902237689180055673361938161119768341970519416039779128617354778773830515364777252518313057683396662835013368967463878342754251509207391537635831891662211848811733884861792121210263430418966889668537646457064092991696527814120385172941004264289812969796992647021735186941896252860419364971543301451924917610828019341224722038007513", 10)
residueWitness.Exp(millerLoop, &rInv)

// 2. compute m-th root:
// where m = (x+1 + x(x^2-x^1-1)q) / r
// Exponentiate to mInv where
// mInv = 1/m mod p^6-1/r
mInv.SetString("420096572758781926988571022578549119077996267041217186563532964653013626327499627643558150289556860284699838191238508062761264485377946319676011525555582097381055209304464769241709045835179375847000286979304653199040198646948595850434830718773056593021324330541604029824826938177546414778934883707126835848724258610612114712835130017082970786784508470382396148858570586085402148355642863720286568566937773459407961735112550507047306343380386401338522186960986251395049985320677251315016812720092326581314645206610216409714397970562842517827716362494341171265008409446148022671451843025093584702610246849007545665518399731546205544005105929880663530772806759681913801835273987094997504640832304570158760940364827187477825525048007459079382410480491250884588399683894539404567701993526561088158396861020181640181843560309670937868772703282755078557149854363818903590441797744966016708880143332350534049482338696654635346189790575286999280892407997722996866724226514621504774811766428733682155766330614074143245300182851212177081558245259537898592443393875891588079021560334726750431309338787970594548465289737362624558256642461612913108676326999205533110217714096123782036214164015261929502119392490941988919030563789520985909704716341786823561745842985678563", 10)
residueWitness.Exp(residueWitness, &mInv)

residueWitness.B0.A0.BigInt(outputs[0])
residueWitness.B0.A1.BigInt(outputs[2])
residueWitness.B0.A2.BigInt(outputs[4])
residueWitness.B1.A0.BigInt(outputs[1])
residueWitness.B1.A1.BigInt(outputs[3])
residueWitness.B1.A2.BigInt(outputs[5])

return nil
})
}
202 changes: 138 additions & 64 deletions std/algebra/emulated/sw_bls12381/hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ func init() {

// GetHints returns all hint functions used in the package.
func GetHints() []solver.Hint {
return []solver.Hint{finalExpHint}
return []solver.Hint{
finalExpHint,
pairingCheckHint,
}
}

func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error {
Expand All @@ -37,69 +40,7 @@ func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) er
millerLoop.C1.B2.A0.SetBigInt(inputs[10])
millerLoop.C1.B2.A1.SetBigInt(inputs[11])

var root, rootPthInverse, root27thInverse, residueWitness, scalingFactor bls12381.E12
var order3rd, order3rdPower, exponent, exponentInv, finalExpFactor, polyFactor big.Int
// polyFactor = (1-x)/3
polyFactor.SetString("5044125407647214251", 10)
// finalExpFactor = ((q^12 - 1) / r) / (27 * polyFactor)
finalExpFactor.SetString("2366356426548243601069753987687709088104621721678962410379583120840019275952471579477684846670499039076873213559162845121989217658133790336552276567078487633052653005423051750848782286407340332979263075575489766963251914185767058009683318020965829271737924625612375201545022326908440428522712877494557944965298566001441468676802477524234094954960009227631543471415676620753242466901942121887152806837594306028649150255258504417829961387165043999299071444887652375514277477719817175923289019181393803729926249507024121957184340179467502106891835144220611408665090353102353194448552304429530104218473070114105759487413726485729058069746063140422361472585604626055492939586602274983146215294625774144156395553405525711143696689756441298365274341189385646499074862712688473936093315628166094221735056483459332831845007196600723053356837526749543765815988577005929923802636375670820616189737737304893769679803809426304143627363860243558537831172903494450556755190448279875942974830469855835666815454271389438587399739607656399812689280234103023464545891697941661992848552456326290792224091557256350095392859243101357349751064730561345062266850238821755009430903520645523345000326783803935359711318798844368754833295302563158150573540616830138810935344206231367357992991289265295323280", 10)

// 1. get pth-root inverse
exponent.Mul(&finalExpFactor, big.NewInt(27))
root.Exp(millerLoop, &exponent)
if root.IsOne() {
rootPthInverse.SetOne()
} else {
exponentInv.ModInverse(&exponent, &polyFactor)
exponent.Neg(&exponentInv).Mod(&exponent, &polyFactor)
rootPthInverse.Exp(root, &exponent)
}

// 2.1. get order of 3rd primitive root
var three big.Int
three.SetUint64(3)
exponent.Mul(&polyFactor, &finalExpFactor)
root.Exp(millerLoop, &exponent)
if root.IsOne() {
order3rdPower.SetUint64(0)
}
root.Exp(root, &three)
if root.IsOne() {
order3rdPower.SetUint64(1)
}
root.Exp(root, &three)
if root.IsOne() {
order3rdPower.SetUint64(2)
}
root.Exp(root, &three)
if root.IsOne() {
order3rdPower.SetUint64(3)
}

// 2.2. get 27th root inverse
if order3rdPower.Uint64() == 0 {
root27thInverse.SetOne()
} else {
order3rd.Exp(&three, &order3rdPower, nil)
exponent.Mul(&polyFactor, &finalExpFactor)
root.Exp(millerLoop, &exponent)
exponentInv.ModInverse(&exponent, &order3rd)
exponent.Neg(&exponentInv).Mod(&exponent, &order3rd)
root27thInverse.Exp(root, &exponent)
}

// 2.3. shift the Miller loop result so that millerLoop * scalingFactor
// is of order finalExpFactor
scalingFactor.Mul(&rootPthInverse, &root27thInverse)
millerLoop.Mul(&millerLoop, &scalingFactor)

// 3. get the witness residue
//
// lambda = q - u, the optimal exponent
var lambda big.Int
lambda.SetString("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129030796414117214202539", 10)
exponent.ModInverse(&lambda, &finalExpFactor)
residueWitness.Exp(millerLoop, &exponent)
residueWitness, scalingFactor := finalExpWitness(&millerLoop)

// return the witness residue
residueWitness.C0.B0.A0.BigInt(outputs[0])
Expand All @@ -126,3 +67,136 @@ func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) er
return nil
})
}

func pairingCheckHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error {
// This is inspired from https://eprint.iacr.org/2024/640.pdf
// and based on a personal communication with the author Andrija Novakovic.
return emulated.UnwrapHint(nativeInputs, nativeOutputs,
func(mod *big.Int, inputs, outputs []*big.Int) error {
var P bls12381.G1Affine
var Q bls12381.G2Affine
n := len(inputs)
p := make([]bls12381.G1Affine, 0, n/6)
q := make([]bls12381.G2Affine, 0, n/6)
for k := 0; k < n/6+1; k += 2 {
P.X.SetBigInt(inputs[k])
P.Y.SetBigInt(inputs[k+1])
p = append(p, P)
}
for k := n / 3; k < n/2+3; k += 4 {
Q.X.A0.SetBigInt(inputs[k])
Q.X.A1.SetBigInt(inputs[k+1])
Q.Y.A0.SetBigInt(inputs[k+2])
Q.Y.A1.SetBigInt(inputs[k+3])
q = append(q, Q)
}

lines := make([][2][len(bls12381.LoopCounter) - 1]bls12381.LineEvaluationAff, 0, len(q))
for _, qi := range q {
lines = append(lines, bls12381.PrecomputeLines(qi))
}
millerLoop, err := bls12381.MillerLoopFixedQ(p, lines)
if err != nil {
return err
}
millerLoop.Conjugate(&millerLoop)

residueWitnessInv, scalingFactor := finalExpWitness(&millerLoop)
residueWitnessInv.Inverse(&residueWitnessInv)

// return the witness residue
residueWitnessInv.C0.B0.A0.BigInt(outputs[0])
residueWitnessInv.C0.B0.A1.BigInt(outputs[1])
residueWitnessInv.C0.B1.A0.BigInt(outputs[2])
residueWitnessInv.C0.B1.A1.BigInt(outputs[3])
residueWitnessInv.C0.B2.A0.BigInt(outputs[4])
residueWitnessInv.C0.B2.A1.BigInt(outputs[5])
residueWitnessInv.C1.B0.A0.BigInt(outputs[6])
residueWitnessInv.C1.B0.A1.BigInt(outputs[7])
residueWitnessInv.C1.B1.A0.BigInt(outputs[8])
residueWitnessInv.C1.B1.A1.BigInt(outputs[9])
residueWitnessInv.C1.B2.A0.BigInt(outputs[10])
residueWitnessInv.C1.B2.A1.BigInt(outputs[11])

// return the scaling factor
scalingFactor.C0.B0.A0.BigInt(outputs[12])
scalingFactor.C0.B0.A1.BigInt(outputs[13])
scalingFactor.C0.B1.A0.BigInt(outputs[14])
scalingFactor.C0.B1.A1.BigInt(outputs[15])
scalingFactor.C0.B2.A0.BigInt(outputs[16])
scalingFactor.C0.B2.A1.BigInt(outputs[17])

return nil

})

}

func finalExpWitness(millerLoop *bls12381.E12) (residueWitness, scalingFactor bls12381.E12) {

var root, rootPthInverse, root27thInverse bls12381.E12
var order3rd, order3rdPower, exponent, exponentInv, finalExpFactor, polyFactor big.Int
// polyFactor = (1-x)/3
polyFactor.SetString("5044125407647214251", 10)
// finalExpFactor = ((q^12 - 1) / r) / (27 * polyFactor)
finalExpFactor.SetString("2366356426548243601069753987687709088104621721678962410379583120840019275952471579477684846670499039076873213559162845121989217658133790336552276567078487633052653005423051750848782286407340332979263075575489766963251914185767058009683318020965829271737924625612375201545022326908440428522712877494557944965298566001441468676802477524234094954960009227631543471415676620753242466901942121887152806837594306028649150255258504417829961387165043999299071444887652375514277477719817175923289019181393803729926249507024121957184340179467502106891835144220611408665090353102353194448552304429530104218473070114105759487413726485729058069746063140422361472585604626055492939586602274983146215294625774144156395553405525711143696689756441298365274341189385646499074862712688473936093315628166094221735056483459332831845007196600723053356837526749543765815988577005929923802636375670820616189737737304893769679803809426304143627363860243558537831172903494450556755190448279875942974830469855835666815454271389438587399739607656399812689280234103023464545891697941661992848552456326290792224091557256350095392859243101357349751064730561345062266850238821755009430903520645523345000326783803935359711318798844368754833295302563158150573540616830138810935344206231367357992991289265295323280", 10)

// 1. get pth-root inverse
exponent.Mul(&finalExpFactor, big.NewInt(27))
root.Exp(*millerLoop, &exponent)
if root.IsOne() {
rootPthInverse.SetOne()
} else {
exponentInv.ModInverse(&exponent, &polyFactor)
exponent.Neg(&exponentInv).Mod(&exponent, &polyFactor)
rootPthInverse.Exp(root, &exponent)
}

// 2.1. get order of 3rd primitive root
var three big.Int
three.SetUint64(3)
exponent.Mul(&polyFactor, &finalExpFactor)
root.Exp(*millerLoop, &exponent)
if root.IsOne() {
order3rdPower.SetUint64(0)
}
root.Exp(root, &three)
if root.IsOne() {
order3rdPower.SetUint64(1)
}
root.Exp(root, &three)
if root.IsOne() {
order3rdPower.SetUint64(2)
}
root.Exp(root, &three)
if root.IsOne() {
order3rdPower.SetUint64(3)
}

// 2.2. get 27th root inverse
if order3rdPower.Uint64() == 0 {
root27thInverse.SetOne()
} else {
order3rd.Exp(&three, &order3rdPower, nil)
exponent.Mul(&polyFactor, &finalExpFactor)
root.Exp(*millerLoop, &exponent)
exponentInv.ModInverse(&exponent, &order3rd)
exponent.Neg(&exponentInv).Mod(&exponent, &order3rd)
root27thInverse.Exp(root, &exponent)
}

// 2.3. shift the Miller loop result so that millerLoop * scalingFactor
// is of order finalExpFactor
scalingFactor.Mul(&rootPthInverse, &root27thInverse)
millerLoop.Mul(millerLoop, &scalingFactor)

// 3. get the witness residue
//
// lambda = q - u, the optimal exponent
var lambda big.Int
lambda.SetString("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129030796414117214202539", 10)
exponent.ModInverse(&lambda, &finalExpFactor)
residueWitness.Exp(*millerLoop, &exponent)

return residueWitness, scalingFactor
}
Loading
Loading