Skip to content

Commit

Permalink
Merge branch 'Consensys:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
liuxiaobleach authored Aug 5, 2024
2 parents f1d3ae0 + ea53f37 commit 0aca3f4
Show file tree
Hide file tree
Showing 35 changed files with 994 additions and 193 deletions.
22 changes: 5 additions & 17 deletions frontend/cs/r1cs/api_assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,6 @@ func (builder *builder) mustBeLessOrEqVar(a, bound frontend.Variable) {
// here bound is NOT a constant,
// but a can be either constant or a wire.

_, aConst := builder.constantValue(a)

nbBits := builder.cs.FieldBitLen()

aBits := bits.ToBinary(builder, a, bits.WithNbDigits(nbBits), bits.WithUnconstrainedOutputs(), bits.OmitModulusCheck())
Expand All @@ -152,28 +150,18 @@ func (builder *builder) mustBeLessOrEqVar(a, bound frontend.Variable) {
// else
// p[i] = p[i+1] * a[i]
// t = 0

v := builder.Mul(p[i+1], aBits[i])
p[i] = builder.Select(boundBits[i], v, p[i+1])

p[i] = builder.Select(boundBits[i], v, p[i+1])
t := builder.Select(boundBits[i], zero, p[i+1])

// (1 - t - ai) * ai == 0
var l frontend.Variable
l = builder.cstOne()
l = builder.Sub(l, t, aBits[i])

// note if bound[i] == 1, this constraint is (1 - ai) * ai == 0
// → this is a boolean constraint
// if bound[i] == 0, t must be 0 or 1, thus ai must be 0 or 1 too

if aConst {
// aBits[i] is a constant;
l = builder.Mul(l, aBits[i])
// TODO @gbotrel this constraint seems useless.
added = append(added, builder.cs.AddR1C(builder.newR1C(l, zero, zero), builder.genericGate))
} else {
added = append(added, builder.cs.AddR1C(builder.newR1C(l, aBits[i], zero), builder.genericGate))
}
// (1 - t - ai) * ai == 0
l := builder.Sub(builder.cstOne(), t, aBits[i])
added = append(added, builder.cs.AddR1C(builder.newR1C(l, builder.Mul(aBits[i], builder.cstOne()), zero), builder.genericGate))
}

if debug.Debug {
Expand Down
4 changes: 1 addition & 3 deletions frontend/cs/r1cs/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,7 @@ func (builder *builder) getLinearExpression(_l interface{}) constraint.LinearExp
case constraint.LinearExpression:
L = tl
default:
if debug.Debug {
panic("invalid input for getLinearExpression") // sanity check
}
panic("invalid input for getLinearExpression") // sanity check
}

return L
Expand Down
6 changes: 4 additions & 2 deletions frontend/cs/scs/api_assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ func (builder *builder) AssertIsEqual(i1, i2 frontend.Variable) {
// AssertIsDifferent fails if i1 == i2
func (builder *builder) AssertIsDifferent(i1, i2 frontend.Variable) {
s := builder.Sub(i1, i2)
if c, ok := builder.constantValue(s); ok && c.IsZero() {
panic("AssertIsDifferent(x,x) will never be satisfied")
if c, ok := builder.constantValue(s); ok {
if c.IsZero() {
panic("AssertIsDifferent(x,x) will never be satisfied")
}
} else if t := s.(expr.Term); t.Coeff.IsZero() {
panic("AssertIsDifferent(x,x) will never be satisfied")
}
Expand Down
29 changes: 29 additions & 0 deletions internal/regression_tests/issue1227/issue_1227_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package issue1226

import (
"testing"

"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/test"
)

type Circuit struct {
constVal int
X frontend.Variable
}

func (circuit *Circuit) Define(api frontend.API) error {
api.AssertIsLessOrEqual(circuit.constVal, circuit.X)
return nil
}

func TestConstantPath(t *testing.T) {
assert := test.NewAssert(t)
assert.CheckCircuit(&Circuit{constVal: 1},
test.WithValidAssignment(&Circuit{X: 1}), // 1 <= 1 --> true
test.WithInvalidAssignment(&Circuit{X: 0})) // 1 <= 0 --> false
// test edge case where constant is 0
assert.CheckCircuit(&Circuit{constVal: 0},
test.WithValidAssignment(&Circuit{X: 1}), // 0 <= 1 --> true
test.WithValidAssignment(&Circuit{X: 0})) // 0 <= 0 --> true
}
20 changes: 20 additions & 0 deletions std/algebra/algopts/algopts.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type algebraCfg struct {
NbScalarBits int
FoldMulti bool
CompleteArithmetic bool
ToBitsCanonical bool
}

// AlgebraOption allows modifying algebraic operation behaviour.
Expand Down Expand Up @@ -57,6 +58,25 @@ func WithCompleteArithmetic() AlgebraOption {
}
}

// WithCanonicalBitRepresentation enforces the marshalling methods to assert
// that the bit representation is in canonical form. For field elements this
// means that the bits represent a number less than the modulus.
//
// This option is useful when performing direct comparison between the bit form
// of two elements. It can be avoided when the bit representation is used in
// other cases, such as computing a challenge using a hash function, where
// non-canonical bit representation leads to incorrect challenge (which in turn
// makes the verification fail).
func WithCanonicalBitRepresentation() AlgebraOption {
return func(ac *algebraCfg) error {
if ac.ToBitsCanonical {
return fmt.Errorf("WithCanonicalBitRepresentation already set")
}
ac.ToBitsCanonical = true
return nil
}
}

// NewConfig applies all given options and returns a configuration to be used.
func NewConfig(opts ...AlgebraOption) (*algebraCfg, error) {
ret := new(algebraCfg)
Expand Down
31 changes: 19 additions & 12 deletions std/algebra/emulated/fields_bls12381/e12_pairing.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,14 +384,15 @@ func (e Ext12) FrobeniusSquareTorus(y *E6) *E6 {
return &E6{B0: *t0, B1: *t1, B2: *t2}
}

// FinalExponentiationCheck checks that a Miller function output x lies in the
// 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 follows Section 4 of [On Proving Pairings] paper by A. Novakovic and L. Eagen.
// The method is inspired from [On Proving Pairings] paper by A. Novakovic and
// L. Eagen, and is based on a personal communication with A. Novakovic.
//
// [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf
func (e Ext12) FinalExponentiationCheck(x *E12) *E12 {
res, err := e.fp.NewHint(finalExpHint, 12, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1)
func (e Ext12) AssertFinalExponentiationIsOne(x *E12) {
res, err := e.fp.NewHint(finalExpHint, 18, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1)
if err != nil {
// err is non-nil only for invalid number of inputs
panic(err)
Expand All @@ -409,21 +410,27 @@ func (e Ext12) FinalExponentiationCheck(x *E12) *E12 {
B2: E2{A0: *res[10], A1: *res[11]},
},
}
// constrain cubicNonResiduePower to be in Fp6
scalingFactor := E12{
C0: E6{
B0: E2{A0: *res[12], A1: *res[13]},
B1: E2{A0: *res[14], A1: *res[15]},
B2: E2{A0: *res[16], A1: *res[17]},
},
C1: (*e.Ext6.Zero()),
}

// Check that x == residueWitness^r by checking that:
// x^k == residueWitness^(q-u)
// where k = (u-1)^2/3, u=-0xd201000000010000 the BLS12-381 seed
// and residueWitness from the hint.
// Check that x * scalingFactor == residueWitness^(q-u)
// where u=-0xd201000000010000 is the BLS12-381 seed,
// and residueWitness, scalingFactor from the hint.
t0 := e.Frobenius(&residueWitness)
// exponentiation by -u
t1 := e.Expt(&residueWitness)
t0 = e.Mul(t0, t1)
// exponentiation by U=(u-1)^2/3
t1 = e.ExpByU(x)

e.AssertIsEqual(t0, t1)
t1 = e.Mul(x, &scalingFactor)

return nil
e.AssertIsEqual(t0, t1)
}

func (e Ext12) Frobenius(x *E12) *E12 {
Expand Down
85 changes: 76 additions & 9 deletions std/algebra/emulated/fields_bls12381/hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,11 @@ func divE12Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) erro
}

func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error {
// This follows section 4.1 of https://eprint.iacr.org/2024/640.pdf (Th. 1)
// 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 millerLoop, residueWitness bls12381.E12
var rInv big.Int
var millerLoop bls12381.E12

millerLoop.C0.B0.A0.SetBigInt(inputs[0])
millerLoop.C0.B0.A1.SetBigInt(inputs[1])
Expand All @@ -290,12 +290,71 @@ func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) er
millerLoop.C1.B2.A0.SetBigInt(inputs[10])
millerLoop.C1.B2.A1.SetBigInt(inputs[11])

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

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)

// 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])
Expand All @@ -309,6 +368,14 @@ func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) er
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
})
}
6 changes: 6 additions & 0 deletions std/algebra/emulated/fields_bn254/e12.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,12 @@ func (e Ext12) CyclotomicSquare(x *E12) *E12 {
}
}

func (e Ext12) IsEqual(x, y *E12) frontend.Variable {
isC0Equal := e.Ext6.IsEqual(&x.C0, &y.C0)
isC1Equal := e.Ext6.IsEqual(&x.C1, &y.C1)
return e.api.And(isC0Equal, isC1Equal)
}

func (e Ext12) AssertIsEqual(x, y *E12) {
e.Ext6.AssertIsEqual(&x.C0, &y.C0)
e.Ext6.AssertIsEqual(&x.C1, &y.C1)
Expand Down
6 changes: 2 additions & 4 deletions std/algebra/emulated/fields_bn254/e12_pairing.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,13 +422,13 @@ func (e Ext12) FrobeniusCubeTorus(y *E6) *E6 {
return res
}

// FinalExponentiationCheck checks that a Miller function output x lies in the
// 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 follows 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 Ext12) FinalExponentiationCheck(x *E12) *E12 {
func (e Ext12) AssertFinalExponentiationIsOne(x *E12) {
res, err := e.fp.NewHint(finalExpHint, 24, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1)
if err != nil {
// err is non-nil only for invalid number of inputs
Expand Down Expand Up @@ -474,8 +474,6 @@ func (e Ext12) FinalExponentiationCheck(x *E12) *E12 {
t0 = e.Mul(t0, t1)

e.AssertIsEqual(t0, t2)

return nil
}

func (e Ext12) Frobenius(x *E12) *E12 {
Expand Down
9 changes: 9 additions & 0 deletions std/algebra/emulated/fields_bn254/e6.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,15 @@ func (e Ext6) FrobeniusSquare(x *E6) *E6 {
return &E6{B0: x.B0, B1: *z01, B2: *z02}
}

func (e Ext6) IsEqual(x, y *E6) frontend.Variable {
isB0Equal := e.Ext2.IsEqual(&x.B0, &y.B0)
isB1Equal := e.Ext2.IsEqual(&x.B1, &y.B1)
isB2Equal := e.Ext2.IsEqual(&x.B2, &y.B2)
res := e.api.And(isB0Equal, isB1Equal)
res = e.api.And(res, isB2Equal)
return res
}

func (e Ext6) AssertIsEqual(x, y *E6) {
e.Ext2.AssertIsEqual(&x.B0, &y.B0)
e.Ext2.AssertIsEqual(&x.B1, &y.B1)
Expand Down
6 changes: 2 additions & 4 deletions std/algebra/emulated/fields_bw6761/e6_pairing.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,13 +322,13 @@ func (e *Ext6) MulBy02345(z *E6, x [5]*baseEl) *E6 {
}
}

// FinalExponentiationCheck checks that a Miller function output x lies in the
// 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) FinalExponentiationCheck(x *E6) *E6 {
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
Expand Down Expand Up @@ -357,8 +357,6 @@ func (e Ext6) FinalExponentiationCheck(x *E6) *E6 {
t0 = e.DivUnchecked(t0, t1)

e.AssertIsEqual(t0, x)

return nil
}

// ExpByU2 set z to z^(x₀+1) in E12 and return z
Expand Down
4 changes: 2 additions & 2 deletions std/algebra/emulated/sw_bls12381/pairing.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error {

}
// We perform the easy part of the final exp to push f to the cyclotomic
// subgroup so that FinalExponentiationCheck is carried with optimized
// subgroup so that AssertFinalExponentiationIsOne is carried with optimized
// cyclotomic squaring (e.g. Karabina12345).
//
// f = f^(p⁶-1)(p²+1)
Expand All @@ -260,7 +260,7 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error {
f = pr.FrobeniusSquare(buf)
f = pr.Mul(f, buf)

pr.FinalExponentiationCheck(f)
pr.AssertFinalExponentiationIsOne(f)

return nil
}
Expand Down
Loading

0 comments on commit 0aca3f4

Please sign in to comment.