Skip to content

Commit 371dab0

Browse files
committed
feat: add BLS precompile glue
1 parent fbf82d8 commit 371dab0

22 files changed

+3223
-0
lines changed

prover/zkevm/prover/bls/bls.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package bls
2+
3+
import (
4+
"github.com/consensys/linea-monorepo/prover/protocol/ifaces"
5+
"github.com/consensys/linea-monorepo/prover/protocol/wizard"
6+
)
7+
8+
type group int
9+
10+
const (
11+
G1 group = iota
12+
G2
13+
)
14+
15+
type membership int
16+
17+
const (
18+
CURVE membership = iota
19+
GROUP
20+
)
21+
22+
const ROUND_NR = 0
23+
24+
func (g group) String() string {
25+
switch g {
26+
case G1:
27+
return "G1"
28+
case G2:
29+
return "G2"
30+
default:
31+
panic("unknown group")
32+
}
33+
}
34+
35+
func (g group) StringCurve() string {
36+
switch g {
37+
case G1:
38+
return "C1"
39+
case G2:
40+
return "C2"
41+
default:
42+
panic("unknown group")
43+
}
44+
}
45+
46+
func (g group) StringMap() string {
47+
switch g {
48+
case G1:
49+
return "FP"
50+
case G2:
51+
return "FP2"
52+
default:
53+
panic("unknown group")
54+
}
55+
}
56+
57+
func createColFn(comp *wizard.CompiledIOP, rootName string, size int) func(name string) ifaces.Column {
58+
return func(name string) ifaces.Column {
59+
return comp.InsertCommit(ROUND_NR, ifaces.ColIDf("%s_%s", rootName, name), size)
60+
}
61+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package bls
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/consensys/gnark/frontend"
7+
"github.com/consensys/gnark/std/algebra/emulated/sw_bls12381"
8+
"github.com/consensys/gnark/std/evmprecompiles"
9+
"github.com/consensys/gnark/std/math/emulated"
10+
)
11+
12+
const (
13+
nbRowsPerG1Add = 3 * nbG1Limbs // 2 for the inputs, 1 for the output
14+
nbRowsPerG2Add = 3 * nbG2Limbs // 2 for the inputs, 1 for the output
15+
)
16+
17+
type addInstance[C convertable[T], T element] struct {
18+
InputLeft, InputRight C `gnark:",public"`
19+
Res C `gnark:",public"`
20+
}
21+
type multiAddCircuit[C convertable[T], T element] struct {
22+
Instances []addInstance[C, T]
23+
}
24+
25+
func (c *multiAddCircuit[C, T]) Define(api frontend.API) error {
26+
f, err := emulated.NewField[sw_bls12381.BaseField](api)
27+
if err != nil {
28+
return fmt.Errorf("new field: %w", err)
29+
}
30+
nbInstances := len(c.Instances)
31+
switch vv := any(c.Instances).(type) {
32+
case []addInstance[g1ElementWizard, sw_bls12381.G1Affine]:
33+
for i := 0; i < nbInstances; i++ {
34+
left := vv[i].InputLeft.ToElement(api, f)
35+
right := vv[i].InputRight.ToElement(api, f)
36+
expected := vv[i].Res.ToElement(api, f)
37+
evmprecompiles.ECAddG1BLS(api, left, right, expected)
38+
}
39+
case []addInstance[g2ElementWizard, sw_bls12381.G2Affine]:
40+
for i := 0; i < nbInstances; i++ {
41+
left := vv[i].InputLeft.ToElement(api, f)
42+
right := vv[i].InputRight.ToElement(api, f)
43+
expected := vv[i].Res.ToElement(api, f)
44+
evmprecompiles.ECAddG2BLS(api, left, right, expected)
45+
}
46+
default:
47+
return fmt.Errorf("unknown element type %T for bls add circuit", vv)
48+
}
49+
50+
return nil
51+
}
52+
53+
func newAddCircuit(g group, limits *Limits) frontend.Circuit {
54+
switch g {
55+
case G1:
56+
return &multiAddCircuit[g1ElementWizard, sw_bls12381.G1Affine]{Instances: make([]addInstance[g1ElementWizard, sw_bls12381.G1Affine], limits.NbG1AddInputInstances)}
57+
case G2:
58+
return &multiAddCircuit[g2ElementWizard, sw_bls12381.G2Affine]{Instances: make([]addInstance[g2ElementWizard, sw_bls12381.G2Affine], limits.NbG2AddInputInstances)}
59+
default:
60+
panic(fmt.Sprintf("unknown group for bls add circuit: %v", g))
61+
}
62+
}
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package bls
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/consensys/gnark/frontend"
7+
"github.com/consensys/gnark/std/algebra/emulated/fields_bls12381"
8+
"github.com/consensys/gnark/std/algebra/emulated/sw_bls12381"
9+
"github.com/consensys/gnark/std/math/bitslice"
10+
"github.com/consensys/gnark/std/math/emulated"
11+
)
12+
13+
const (
14+
nbBits = 128 // we use 128 bits limbs for the BLS12-381 field
15+
nbBytes = nbBits / 8 // 128 bits = 16 bytes
16+
17+
// BLS scalar field is 255 bits, and we use 2 limbs of 128 bits to represent
18+
nbFrLimbs = 2 // (x_1, x_0) MSB order
19+
20+
// BLS base field is 381 bits, and we use 4 limbs of 128 bits to represent
21+
// it. However, the highest limb is always zero, but the arithmetization
22+
// keeps it for nice alignment. We pass it to the circuit but check
23+
// explicitly that its 0.
24+
nbFpLimbs = 4 // (x_3, x_2, x_1, x_0) MSB order
25+
26+
nbG1Limbs = 2 * nbFpLimbs // (Ax, Ay)
27+
nbG2Limbs = 4 * nbFpLimbs // (BxIm, BxRe, ByIm, ByRe)
28+
nbGtLimbs = 12 * nbFpLimbs // representation according to gnark - we don't use Gt in arithmetization, only in glue for accumulation
29+
)
30+
31+
func nbLimbs(g group) int {
32+
switch g {
33+
case G1:
34+
return nbG1Limbs
35+
case G2:
36+
return nbG2Limbs
37+
default:
38+
panic("unknown group for bls nbLimbs")
39+
}
40+
}
41+
42+
var fpParams sw_bls12381.BaseField
43+
var frParams sw_bls12381.ScalarField
44+
45+
type scalarElementWizard struct {
46+
S [nbFrLimbs]frontend.Variable
47+
}
48+
49+
func (c scalarElementWizard) ToElement(api frontend.API, fr *emulated.Field[sw_bls12381.ScalarField]) *sw_bls12381.Scalar {
50+
// gnark represents the BLS12-381 Fr element on 4 limbs of 64 bits.
51+
Slimbs := make([]frontend.Variable, frParams.NbLimbs())
52+
Slimbs[2], Slimbs[3] = bitslice.Partition(api, c.S[0], 64, bitslice.WithNbDigits(128))
53+
Slimbs[0], Slimbs[1] = bitslice.Partition(api, c.S[1], 64, bitslice.WithNbDigits(128))
54+
return fr.NewElement(Slimbs)
55+
}
56+
57+
type baseElementWizard struct {
58+
P [nbFpLimbs]frontend.Variable
59+
}
60+
61+
func (c baseElementWizard) ToElement(api frontend.API, fp *emulated.Field[sw_bls12381.BaseField]) *emulated.Element[sw_bls12381.BaseField] {
62+
// gnark represents the BLS12-381 Fp elements on 6 limbs of 64 bits.
63+
Plimbs := make([]frontend.Variable, fpParams.NbLimbs())
64+
Plimbs[4], Plimbs[5] = bitslice.Partition(api, c.P[1], 64, bitslice.WithNbDigits(128))
65+
Plimbs[2], Plimbs[3] = bitslice.Partition(api, c.P[2], 64, bitslice.WithNbDigits(128))
66+
Plimbs[0], Plimbs[1] = bitslice.Partition(api, c.P[3], 64, bitslice.WithNbDigits(128))
67+
return fp.NewElement(Plimbs)
68+
}
69+
70+
type g1ElementWizard struct {
71+
P [nbG1Limbs]frontend.Variable
72+
}
73+
74+
func (c g1ElementWizard) ToElement(api frontend.API, fp *emulated.Field[sw_bls12381.BaseField]) *sw_bls12381.G1Affine {
75+
PXlimbs := make([]frontend.Variable, fpParams.NbLimbs())
76+
PYlimbs := make([]frontend.Variable, fpParams.NbLimbs())
77+
78+
// gnark represents the BLS12-381 Fp element on 6 limbs of 64 bits.
79+
// Arithmetization uses 4 limbs of 128 bits, but the MSB limb is always 0.
80+
api.AssertIsEqual(c.P[0], 0)
81+
api.AssertIsEqual(c.P[nbFpLimbs], 0)
82+
for i := range nbFpLimbs - 1 {
83+
PXlimbs[len(PXlimbs)-(2*i+2)], PXlimbs[len(PXlimbs)-(2*i+1)] = bitslice.Partition(api, c.P[i+1], 64, bitslice.WithNbDigits(128))
84+
PYlimbs[len(PYlimbs)-(2*i+2)], PYlimbs[len(PYlimbs)-(2*i+1)] = bitslice.Partition(api, c.P[nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
85+
}
86+
PX := fp.NewElement(PXlimbs)
87+
PY := fp.NewElement(PYlimbs)
88+
P := sw_bls12381.G1Affine{
89+
X: *PX,
90+
Y: *PY,
91+
}
92+
return &P
93+
}
94+
95+
type g2ElementWizard struct {
96+
Q [nbG2Limbs]frontend.Variable
97+
}
98+
99+
func (c g2ElementWizard) ToElement(api frontend.API, fp *emulated.Field[sw_bls12381.BaseField]) *sw_bls12381.G2Affine {
100+
QXAlimbs := make([]frontend.Variable, fpParams.NbLimbs())
101+
QXBlimbs := make([]frontend.Variable, fpParams.NbLimbs())
102+
QYAlimbs := make([]frontend.Variable, fpParams.NbLimbs())
103+
QYBlimbs := make([]frontend.Variable, fpParams.NbLimbs())
104+
105+
// assert that the MSB limb is 0 in arithmetization
106+
for i := range 4 {
107+
api.AssertIsEqual(c.Q[i*nbFpLimbs], 0)
108+
}
109+
110+
for i := range nbFpLimbs - 1 {
111+
QXBlimbs[len(QXBlimbs)-(2*i+2)], QXBlimbs[len(QXBlimbs)-(2*i+1)] = bitslice.Partition(api, c.Q[i+1], 64, bitslice.WithNbDigits(128))
112+
QXAlimbs[len(QXAlimbs)-(2*i+2)], QXAlimbs[len(QXAlimbs)-(2*i+1)] = bitslice.Partition(api, c.Q[nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
113+
QYBlimbs[len(QYBlimbs)-(2*i+2)], QYBlimbs[len(QYBlimbs)-(2*i+1)] = bitslice.Partition(api, c.Q[2*nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
114+
QYAlimbs[len(QYAlimbs)-(2*i+2)], QYAlimbs[len(QYAlimbs)-(2*i+1)] = bitslice.Partition(api, c.Q[3*nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
115+
}
116+
QXA := fp.NewElement(QXAlimbs)
117+
QXB := fp.NewElement(QXBlimbs)
118+
QX := fields_bls12381.E2{
119+
A0: *QXA,
120+
A1: *QXB,
121+
}
122+
QYA := fp.NewElement(QYAlimbs)
123+
QYB := fp.NewElement(QYBlimbs)
124+
QY := fields_bls12381.E2{
125+
A0: *QYA,
126+
A1: *QYB,
127+
}
128+
var Q sw_bls12381.G2Affine
129+
Q.P.X = QX
130+
Q.P.Y = QY
131+
132+
return &Q
133+
}
134+
135+
type gtElementWizard struct {
136+
T [nbGtLimbs]frontend.Variable
137+
}
138+
139+
func (c gtElementWizard) ToElement(api frontend.API, fp *emulated.Field[sw_bls12381.BaseField]) *sw_bls12381.GTEl {
140+
A0Limbs := make([]frontend.Variable, fpParams.NbLimbs())
141+
A1Limbs := make([]frontend.Variable, fpParams.NbLimbs())
142+
A2Limbs := make([]frontend.Variable, fpParams.NbLimbs())
143+
A3Limbs := make([]frontend.Variable, fpParams.NbLimbs())
144+
A4Limbs := make([]frontend.Variable, fpParams.NbLimbs())
145+
A5Limbs := make([]frontend.Variable, fpParams.NbLimbs())
146+
A6Limbs := make([]frontend.Variable, fpParams.NbLimbs())
147+
A7Limbs := make([]frontend.Variable, fpParams.NbLimbs())
148+
A8Limbs := make([]frontend.Variable, fpParams.NbLimbs())
149+
A9Limbs := make([]frontend.Variable, fpParams.NbLimbs())
150+
A10Limbs := make([]frontend.Variable, fpParams.NbLimbs())
151+
A11Limbs := make([]frontend.Variable, fpParams.NbLimbs())
152+
153+
// assert that the MSB limb is 0 in arithmetization
154+
for i := range 12 {
155+
api.AssertIsEqual(c.T[i*nbFpLimbs], 0)
156+
}
157+
158+
for i := range nbFpLimbs - 1 {
159+
A0Limbs[len(A0Limbs)-(2*i+2)], A0Limbs[len(A0Limbs)-(2*i+1)] = bitslice.Partition(api, c.T[i+1], 64, bitslice.WithNbDigits(128))
160+
A1Limbs[len(A1Limbs)-(2*i+2)], A1Limbs[len(A1Limbs)-(2*i+1)] = bitslice.Partition(api, c.T[nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
161+
A2Limbs[len(A2Limbs)-(2*i+2)], A2Limbs[len(A2Limbs)-(2*i+1)] = bitslice.Partition(api, c.T[2*nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
162+
A3Limbs[len(A3Limbs)-(2*i+2)], A3Limbs[len(A3Limbs)-(2*i+1)] = bitslice.Partition(api, c.T[3*nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
163+
A4Limbs[len(A4Limbs)-(2*i+2)], A4Limbs[len(A4Limbs)-(2*i+1)] = bitslice.Partition(api, c.T[4*nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
164+
A5Limbs[len(A5Limbs)-(2*i+2)], A5Limbs[len(A5Limbs)-(2*i+1)] = bitslice.Partition(api, c.T[5*nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
165+
A6Limbs[len(A6Limbs)-(2*i+2)], A6Limbs[len(A6Limbs)-(2*i+1)] = bitslice.Partition(api, c.T[6*nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
166+
A7Limbs[len(A7Limbs)-(2*i+2)], A7Limbs[len(A7Limbs)-(2*i+1)] = bitslice.Partition(api, c.T[7*nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
167+
A8Limbs[len(A8Limbs)-(2*i+2)], A8Limbs[len(A8Limbs)-(2*i+1)] = bitslice.Partition(api, c.T[8*nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
168+
A9Limbs[len(A9Limbs)-(2*i+2)], A9Limbs[len(A9Limbs)-(2*i+1)] = bitslice.Partition(api, c.T[9*nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
169+
A10Limbs[len(A10Limbs)-(2*i+2)], A10Limbs[len(A10Limbs)-(2*i+1)] = bitslice.Partition(api, c.T[10*nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
170+
A11Limbs[len(A11Limbs)-(2*i+2)], A11Limbs[len(A11Limbs)-(2*i+1)] = bitslice.Partition(api, c.T[11*nbFpLimbs+i+1], 64, bitslice.WithNbDigits(128))
171+
}
172+
173+
pairing, err := sw_bls12381.NewPairing(api)
174+
if err != nil {
175+
panic(fmt.Sprintf("new pairing: %v", err))
176+
}
177+
T := pairing.Ext12.FromTower([12]*emulated.Element[sw_bls12381.BaseField]{
178+
fp.NewElement(A0Limbs),
179+
fp.NewElement(A1Limbs),
180+
fp.NewElement(A2Limbs),
181+
fp.NewElement(A3Limbs),
182+
fp.NewElement(A4Limbs),
183+
fp.NewElement(A5Limbs),
184+
fp.NewElement(A6Limbs),
185+
fp.NewElement(A7Limbs),
186+
fp.NewElement(A8Limbs),
187+
fp.NewElement(A9Limbs),
188+
fp.NewElement(A10Limbs),
189+
fp.NewElement(A11Limbs),
190+
})
191+
192+
return T
193+
}
194+
195+
type element interface {
196+
sw_bls12381.G1Affine | sw_bls12381.G2Affine | sw_bls12381.GTEl
197+
}
198+
199+
type convertable[T element] interface {
200+
g1ElementWizard | g2ElementWizard | gtElementWizard
201+
ToElement(api frontend.API, fp *emulated.Field[sw_bls12381.BaseField]) *T
202+
}

0 commit comments

Comments
 (0)