Skip to content

Commit

Permalink
perf: PairingCheck for BN254, BLS12-381, BLS12-377 and BW6-761 (#1365)
Browse files Browse the repository at this point in the history
Co-authored-by: Ivo Kubjas <[email protected]>
  • Loading branch information
yelhousni and ivokub authored Jan 21, 2025
1 parent 3a407c1 commit 52291fa
Show file tree
Hide file tree
Showing 18 changed files with 1,269 additions and 564 deletions.
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

0 comments on commit 52291fa

Please sign in to comment.