Skip to content

Commit

Permalink
perf(bls12-377): PairingCheck saves ExpByU
Browse files Browse the repository at this point in the history
  • Loading branch information
yelhousni committed Jan 9, 2025
1 parent 61963f5 commit 2a0dc3e
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 30 deletions.
2 changes: 1 addition & 1 deletion std/algebra/emulated/sw_bls12381/pairing.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error {
nP := len(P)
nQ := len(Q)
if nP == 0 || nP != nQ {
return nil
return errors.New("invalid inputs sizes")
}
// hint the non-residue witness
inputs := make([]*baseEl, 0, 2*nP+4*nQ)
Expand Down
85 changes: 85 additions & 0 deletions std/algebra/native/sw_bls12377/hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,98 @@ func GetHints() []solver.Hint {
scalarMulGLVG1Hint,
halfGCDEisenstein,
halfGCDEisensteinSigns,
pairingCheckHint,
}
}

func init() {
solver.RegisterHint(GetHints()...)
}

func pairingCheckHint(scalarField *big.Int, inputs, outputs []*big.Int) error {
var P bls12377.G1Affine
var Q bls12377.G2Affine
n := len(inputs)
p := make([]bls12377.G1Affine, 0, n/6)
q := make([]bls12377.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(bls12377.LoopCounter) - 1]bls12377.LineEvaluationAff, 0, len(q))
for _, qi := range q {
lines = append(lines, bls12377.PrecomputeLines(qi))
}
millerLoop, err := bls12377.MillerLoopFixedQ(p, lines)
if err != nil {
return err
}

var root, rootPthInverse, residueWitness, scalingFactor bls12377.E12
var exponent, exponentInv, finalExpFactor, polyFactor big.Int
// polyFactor = 12(x-1)
polyFactor.SetString("115033474957087604736", 10)
// finalExpFactor = ((q^12 - 1) / r) / polyFactor
finalExpFactor.SetString("92351561334497520756349650336409370070948672672207914824247073415859727964231807559847070685040742345026775319680739143654748316009031763764029886042408725311062057776702838555815712331129279611544378217895455619058809454575474763035923260395518532422855090028311239234310116353269618927871828693919559964406939845784130633021661399269804065961999062695977580539176029238189119059338698461832966347603096853909366901376879505972606045770762516580639801134008192256366142553202619529638202068488750102055204336502584141399828818871664747496033599618827160583206926869573005874449182200210044444351826855938563862937638034918413235278166699461287943529570559518592586872860190313088429391521694808994276205429071153237122495989095857292965461625387657577981811772819764071512345106346232882471034669258055302790607847924560040527682025558360106509628206144255667203317787586698694011876342903106644003067103035176245790275561392007119121995936066014208972135762663107247939004517852248103325700169848524693333524025685325993207375736519358185783520948988673594976115901587076295116293065682366935313875411927779217584729138600463438806153265891176654957439524358472291492028580820575807385461119025678550977847392818655362610734928283105671242634809807533919011078145", 10)

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

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

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

// return the witness residue
residueWitness.C0.B0.A0.BigInt(outputs[0])
residueWitness.C0.B0.A1.BigInt(outputs[1])
residueWitness.C0.B1.A0.BigInt(outputs[2])
residueWitness.C0.B1.A1.BigInt(outputs[3])
residueWitness.C0.B2.A0.BigInt(outputs[4])
residueWitness.C0.B2.A1.BigInt(outputs[5])
residueWitness.C1.B0.A0.BigInt(outputs[6])
residueWitness.C1.B0.A1.BigInt(outputs[7])
residueWitness.C1.B1.A0.BigInt(outputs[8])
residueWitness.C1.B1.A1.BigInt(outputs[9])
residueWitness.C1.B2.A0.BigInt(outputs[10])
residueWitness.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 decomposeScalarG1Simple(scalarField *big.Int, inputs []*big.Int, outputs []*big.Int) error {
if len(inputs) != 1 {
return errors.New("expecting one input")
Expand Down
124 changes: 110 additions & 14 deletions std/algebra/native/sw_bls12377/pairing.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,22 +240,118 @@ func Pair(api frontend.API, P []G1Affine, Q []G2Affine) (GT, error) {
//
// This function doesn't check that the inputs are in the correct subgroups
func PairingCheck(api frontend.API, P []G1Affine, Q []G2Affine) error {
f, err := MillerLoop(api, P, Q)

// check input size match
nP := len(P)
nQ := len(Q)
if nP == 0 || nP != nQ {
return errors.New("invalid inputs sizes")
}
// hint the non-residue witness
inputs := make([]frontend.Variable, 0, 2*nP+4*nQ)
for _, p := range P {
inputs = append(inputs, p.X, p.Y)
}
for _, q := range Q {
inputs = append(inputs, q.P.X.A0, q.P.X.A1, q.P.Y.A0, q.P.Y.A1)
}
hint, err := api.NewHint(pairingCheckHint, 18, inputs...)
if err != nil {
return err
// err is non-nil only for invalid number of inputs
panic(err)
}
var residueWitness GT
residueWitness.C0.B0.A0 = hint[0]
residueWitness.C0.B0.A1 = hint[1]
residueWitness.C0.B1.A0 = hint[2]
residueWitness.C0.B1.A1 = hint[3]
residueWitness.C0.B2.A0 = hint[4]
residueWitness.C0.B2.A1 = hint[5]
residueWitness.C1.B0.A0 = hint[6]
residueWitness.C1.B0.A1 = hint[7]
residueWitness.C1.B1.A0 = hint[8]
residueWitness.C1.B1.A1 = hint[9]
residueWitness.C1.B2.A0 = hint[10]
residueWitness.C1.B2.A1 = hint[11]

var scalingFactor fields_bls12377.E6
// constrain cubicNonResiduePower to be in Fp6
scalingFactor.B0.A0 = hint[12]
scalingFactor.B0.A1 = hint[13]
scalingFactor.B1.A0 = hint[14]
scalingFactor.B1.A1 = hint[15]
scalingFactor.B2.A0 = hint[16]
scalingFactor.B2.A1 = hint[17]

lines := make([]lineEvaluations, nQ)
for i := range Q {
if Q[i].Lines == nil {
Qlines := computeLines(api, Q[i].P)
Q[i].Lines = Qlines
}
lines[i] = *Q[i].Lines
}
// We perform the easy part of the final exp to push f to the cyclotomic
// subgroup so that AssertFinalExponentiationIsOne is carried with optimized
// cyclotomic squaring (e.g. Karabina12345).
//
// f = f^(p⁶-1)(p²+1)
var buf GT
buf.Conjugate(api, f)
buf.DivUnchecked(api, buf, f)
f.FrobeniusSquare(api, buf).
Mul(api, f, buf)

f.AssertFinalExponentiationIsOne(api)

// precomputations
yInv := make([]frontend.Variable, nP)
xNegOverY := make([]frontend.Variable, nP)
for k := 0; k < nP; k++ {
yInv[k] = api.DivUnchecked(1, P[k].Y)
xNegOverY[k] = api.Mul(P[k].X, yInv[k])
xNegOverY[k] = api.Neg(xNegOverY[k])
}

// init Miller loop accumulator to residueWitness to share the squarings
// of residueWitness^{x₀}
res := residueWitness

var prodLines [5]fields_bls12377.E2
var l0, l1 lineEvaluation

// Compute ∏ᵢ { fᵢ_{x₀,Q}(P) }
for i := 62; i >= 0; i-- {
// mutualize the square among n Miller loops
// (∏ᵢfᵢ)²
res.Square(api, res)

if loopCounter[i] == 0 {
for k := 0; k < nP; k++ {
// line evaluation at P
// ℓ × res
res.MulBy034(api,
*l0.R0.MulByFp(api, lines[k][0][i].R0, xNegOverY[k]),
*l0.R1.MulByFp(api, lines[k][0][i].R1, yInv[k]),
)
}
} else {
// multiply by residueWitness when bit=1
res.Mul(api, res, residueWitness)
for k := 0; k < nP; k++ {
// lines evaluation at P
// ℓ × ℓ
prodLines = *fields_bls12377.Mul034By034(api,
*l0.R0.MulByFp(api, lines[k][0][i].R0, xNegOverY[k]),
*l0.R1.MulByFp(api, lines[k][0][i].R1, yInv[k]),
*l1.R0.MulByFp(api, lines[k][1][i].R0, xNegOverY[k]),
*l1.R1.MulByFp(api, lines[k][1][i].R1, yInv[k]),
)
// (ℓ × ℓ) × res
res.MulBy01234(api, prodLines)
}
}
}

// Check that res * scalingFactor == residueWitness^(q)
// where u=0x8508c00000000001 is the BLS12-377 seed,
// and residueWitness, scalingFactor from the hint.
// Note that res is already MillerLoop(P,Q) * residueWitness^{x₀} since
// we initialized the Miller loop accumulator with residueWitness.
var t0, t1 GT
t1.C0.Mul(api, res.C0, scalingFactor)
t1.C1.Mul(api, res.C1, scalingFactor)
t0.Frobenius(api, residueWitness)

t0.AssertIsEqual(api, t1)

return nil
}
Expand Down
17 changes: 2 additions & 15 deletions std/algebra/native/sw_bls12377/pairing2.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,21 +313,8 @@ func (p *Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error {
for i := range Q {
inQ[i] = *Q[i]
}
res, err := MillerLoop(p.api, inP, inQ)
if err != nil {
return err
}
// We perform the easy part of the final exp to push res to the cyclotomic
// subgroup so that AssertFinalExponentiationIsOne is carried with optimized
// cyclotomic squaring (e.g. Karabina12345).
//
// res = res^(p⁶-1)(p²+1)
var buf GT
buf.Conjugate(p.api, res)
buf.DivUnchecked(p.api, buf, res)
res.FrobeniusSquare(p.api, buf).Mul(p.api, res, buf)

res.AssertFinalExponentiationIsOne(p.api)
PairingCheck(p.api, inP, inQ)

return nil
}

Expand Down

0 comments on commit 2a0dc3e

Please sign in to comment.