Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
5 changes: 5 additions & 0 deletions std/algebra/native/fields_kb8/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright 2020-2026 Consensys Software Inc.
// Licensed under the Apache License, Version 2.0. See the LICENSE file for details.

// Package fields_kb8 implements KoalaBear-native Fp^8 arithmetic for kb8-based gadgets.
package fields_kb8
117 changes: 117 additions & 0 deletions std/algebra/native/fields_kb8/e2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package fields_kb8

import (
"github.com/consensys/gnark-crypto/field/koalabear"
"github.com/consensys/gnark-crypto/field/koalabear/extensions"
"github.com/consensys/gnark/frontend"
)

var uSquare = koalabear.NewElement(3)

type E2 struct {
A0, A1 frontend.Variable
}

func (e *E2) SetZero() *E2 {
e.A0 = 0
e.A1 = 0
return e
}

func (e *E2) SetOne() *E2 {
e.A0 = 1
e.A1 = 0
return e
}

func (e *E2) IsZero(api frontend.API) frontend.Variable {
return api.And(api.IsZero(e.A0), api.IsZero(e.A1))
}

func (e *E2) assign(e1 []frontend.Variable) {
e.A0 = e1[0]
e.A1 = e1[1]
}

func (e *E2) Neg(api frontend.API, e1 E2) *E2 {
e.A0 = api.Neg(e1.A0)
e.A1 = api.Neg(e1.A1)
return e
}

func (e *E2) Add(api frontend.API, e1, e2 E2) *E2 {
e.A0 = api.Add(e1.A0, e2.A0)
e.A1 = api.Add(e1.A1, e2.A1)
return e
}

func (e *E2) Double(api frontend.API, e1 E2) *E2 {
e.A0 = api.Mul(e1.A0, 2)
e.A1 = api.Mul(e1.A1, 2)
return e
}

func (e *E2) Sub(api frontend.API, e1, e2 E2) *E2 {
e.A0 = api.Sub(e1.A0, e2.A0)
e.A1 = api.Sub(e1.A1, e2.A1)
return e
}

func (e *E2) Mul(api frontend.API, e1, e2 E2) *E2 {
l1 := api.Add(e1.A0, e1.A1)
l2 := api.Add(e2.A0, e2.A1)
u := api.Mul(l1, l2)
ac := api.Mul(e1.A0, e2.A0)
bd := api.Mul(e1.A1, e2.A1)
e.A1 = api.Sub(u, api.Add(ac, bd))
e.A0 = api.Add(ac, api.Mul(bd, uSquare))
return e
}

func (e *E2) Square(api frontend.API, x E2) *E2 {
// Algorithm 22 from https://eprint.iacr.org/2010/354.pdf adapted to u^2 = 3.
c0 := api.Add(x.A0, x.A1)
c2 := api.Mul(x.A1, uSquare)
c2 = api.Add(c2, x.A0)

c0 = api.Mul(c0, c2)
c2 = api.Mul(x.A0, x.A1)
c2 = api.Mul(c2, 2)
e.A1 = c2
c2 = api.Mul(c2, 2)
e.A0 = api.Sub(c0, c2)
return e
}

func (e *E2) MulByFp(api frontend.API, e1 E2, c interface{}) *E2 {
e.A0 = api.Mul(e1.A0, c)
e.A1 = api.Mul(e1.A1, c)
return e
}

func (e *E2) MulByNonResidue(api frontend.API, e1 E2) *E2 {
x := e1.A0
e.A0 = api.Mul(e1.A1, uSquare)
e.A1 = x
return e
}

func (e *E2) AssertIsEqual(api frontend.API, other E2) {
api.AssertIsEqual(e.A0, other.A0)
api.AssertIsEqual(e.A1, other.A1)
}

func (e *E2) IsEqual(api frontend.API, other E2) frontend.Variable {
return api.And(api.IsZero(api.Sub(e.A0, other.A0)), api.IsZero(api.Sub(e.A1, other.A1)))
}

func (e *E2) Select(api frontend.API, b frontend.Variable, r1, r2 E2) *E2 {
e.A0 = api.Select(b, r1.A0, r2.A0)
e.A1 = api.Select(b, r1.A1, r2.A1)
return e
}

func (e *E2) Assign(a *extensions.E2) {
e.A0 = a.A0
e.A1 = a.A1
}
72 changes: 72 additions & 0 deletions std/algebra/native/fields_kb8/e2_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package fields_kb8

import (
"testing"

"github.com/consensys/gnark-crypto/field/koalabear/extensions"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/test"
)

type e2Add struct{ A, B, C E2 }

func (c *e2Add) Define(api frontend.API) error {
var e E2
e.Add(api, c.A, c.B)
e.AssertIsEqual(api, c.C)
return nil
}
func TestAddE2(t *testing.T) {
assert := test.NewAssert(t)
var a, b, c extensions.E2
a.SetRandom()
b.SetRandom()
c.Add(&a, &b)
var w e2Add
w.A.Assign(&a)
w.B.Assign(&b)
w.C.Assign(&c)
assert.CheckCircuit(&e2Add{}, test.WithValidAssignment(&w), test.WithoutCurveChecks(), test.WithSmallfieldCheck())
}

type e2Sub struct{ A, B, C E2 }

func (c *e2Sub) Define(api frontend.API) error {
var e E2
e.Sub(api, c.A, c.B)
e.AssertIsEqual(api, c.C)
return nil
}
func TestSubE2(t *testing.T) {
assert := test.NewAssert(t)
var a, b, c extensions.E2
a.SetRandom()
b.SetRandom()
c.Sub(&a, &b)
var w e2Sub
w.A.Assign(&a)
w.B.Assign(&b)
w.C.Assign(&c)
assert.CheckCircuit(&e2Sub{}, test.WithValidAssignment(&w), test.WithoutCurveChecks(), test.WithSmallfieldCheck())
}

type e2Mul struct{ A, B, C E2 }

func (c *e2Mul) Define(api frontend.API) error {
var e E2
e.Mul(api, c.A, c.B)
e.AssertIsEqual(api, c.C)
return nil
}
func TestMulE2(t *testing.T) {
assert := test.NewAssert(t)
var a, b, c extensions.E2
a.SetRandom()
b.SetRandom()
c.Mul(&a, &b)
var w e2Mul
w.A.Assign(&a)
w.B.Assign(&b)
w.C.Assign(&c)
assert.CheckCircuit(&e2Mul{}, test.WithValidAssignment(&w), test.WithoutCurveChecks(), test.WithSmallfieldCheck())
}
119 changes: 119 additions & 0 deletions std/algebra/native/fields_kb8/e4.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package fields_kb8

import (
"github.com/consensys/gnark-crypto/field/koalabear/extensions"
"github.com/consensys/gnark/frontend"
)

type E4 struct {
B0, B1 E2
}

func (e *E4) SetZero() *E4 {
e.B0.SetZero()
e.B1.SetZero()
return e
}

func (e *E4) SetOne() *E4 {
e.B0.SetOne()
e.B1.SetZero()
return e
}

func (e *E4) IsZero(api frontend.API) frontend.Variable {
return api.And(e.B0.IsZero(api), e.B1.IsZero(api))
}

func (e *E4) assign(e1 []frontend.Variable) {
e.B0.A0 = e1[0]
e.B0.A1 = e1[1]
e.B1.A0 = e1[2]
e.B1.A1 = e1[3]
}

func (e *E4) Neg(api frontend.API, e1 E4) *E4 {
e.B0.Neg(api, e1.B0)
e.B1.Neg(api, e1.B1)
return e
}

func (e *E4) Add(api frontend.API, e1, e2 E4) *E4 {
e.B0.Add(api, e1.B0, e2.B0)
e.B1.Add(api, e1.B1, e2.B1)
return e
}

func (e *E4) Double(api frontend.API, e1 E4) *E4 {
e.B0.Double(api, e1.B0)
e.B1.Double(api, e1.B1)
return e
}

func (e *E4) Sub(api frontend.API, e1, e2 E4) *E4 {
e.B0.Sub(api, e1.B0, e2.B0)
e.B1.Sub(api, e1.B1, e2.B1)
return e
}

func (e *E4) Mul(api frontend.API, e1, e2 E4) *E4 {
var l1, l2, u, ac, bd E2
l1.Add(api, e1.B0, e1.B1)
l2.Add(api, e2.B0, e2.B1)
u.Mul(api, l1, l2)
ac.Mul(api, e1.B0, e2.B0)
bd.Mul(api, e1.B1, e2.B1)
e.B0.MulByNonResidue(api, bd).Add(api, e.B0, ac)
e.B1.Add(api, ac, bd)
e.B1.Sub(api, u, e.B1)
return e
}

func (e *E4) Square(api frontend.API, x E4) *E4 {
// Quadratic-extension square over E2 with v^2 = u.
var c0, c2, tmp, tmpNR E2
tmp.MulByNonResidue(api, x.B1)
c0.Add(api, x.B0, x.B1)
tmp.Add(api, tmp, x.B0)
c0.Mul(api, c0, tmp)

c2.Mul(api, x.B0, x.B1)
e.B1.Double(api, c2)

tmpNR.MulByNonResidue(api, c2)
e.B0.Sub(api, c0, c2)
e.B0.Sub(api, e.B0, tmpNR)
return e
}

func (e *E4) MulByFp(api frontend.API, e1 E4, c interface{}) *E4 {
e.B0.MulByFp(api, e1.B0, c)
e.B1.MulByFp(api, e1.B1, c)
return e
}

func (e *E4) MulByNonResidue(api frontend.API, e1 E4) *E4 {
e.B0.MulByNonResidue(api, e1.B1)
e.B1 = e1.B0
return e
}

func (e *E4) AssertIsEqual(api frontend.API, other E4) {
e.B0.AssertIsEqual(api, other.B0)
e.B1.AssertIsEqual(api, other.B1)
}

func (e *E4) IsEqual(api frontend.API, other E4) frontend.Variable {
return api.And(e.B0.IsEqual(api, other.B0), e.B1.IsEqual(api, other.B1))
}

func (e *E4) Select(api frontend.API, b frontend.Variable, r1, r2 E4) *E4 {
e.B0.Select(api, b, r1.B0, r2.B0)
e.B1.Select(api, b, r1.B1, r2.B1)
return e
}

func (e *E4) Assign(a *extensions.E4) {
e.B0.Assign(&a.B0)
e.B1.Assign(&a.B1)
}
72 changes: 72 additions & 0 deletions std/algebra/native/fields_kb8/e4_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package fields_kb8

import (
"testing"

"github.com/consensys/gnark-crypto/field/koalabear/extensions"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/test"
)

type e4Add struct{ A, B, C E4 }

func (c *e4Add) Define(api frontend.API) error {
var e E4
e.Add(api, c.A, c.B)
e.AssertIsEqual(api, c.C)
return nil
}
func TestAddE4(t *testing.T) {
assert := test.NewAssert(t)
var a, b, c extensions.E4
a.SetRandom()
b.SetRandom()
c.Add(&a, &b)
var w e4Add
w.A.Assign(&a)
w.B.Assign(&b)
w.C.Assign(&c)
assert.CheckCircuit(&e4Add{}, test.WithValidAssignment(&w), test.WithoutCurveChecks(), test.WithSmallfieldCheck())
}

type e4Sub struct{ A, B, C E4 }

func (c *e4Sub) Define(api frontend.API) error {
var e E4
e.Sub(api, c.A, c.B)
e.AssertIsEqual(api, c.C)
return nil
}
func TestSubE4(t *testing.T) {
assert := test.NewAssert(t)
var a, b, c extensions.E4
a.SetRandom()
b.SetRandom()
c.Sub(&a, &b)
var w e4Sub
w.A.Assign(&a)
w.B.Assign(&b)
w.C.Assign(&c)
assert.CheckCircuit(&e4Sub{}, test.WithValidAssignment(&w), test.WithoutCurveChecks(), test.WithSmallfieldCheck())
}

type e4Mul struct{ A, B, C E4 }

func (c *e4Mul) Define(api frontend.API) error {
var e E4
e.Mul(api, c.A, c.B)
e.AssertIsEqual(api, c.C)
return nil
}
func TestMulE4(t *testing.T) {
assert := test.NewAssert(t)
var a, b, c extensions.E4
a.SetRandom()
b.SetRandom()
c.Mul(&a, &b)
var w e4Mul
w.A.Assign(&a)
w.B.Assign(&b)
w.C.Assign(&c)
assert.CheckCircuit(&e4Mul{}, test.WithValidAssignment(&w), test.WithoutCurveChecks(), test.WithSmallfieldCheck())
}
Loading
Loading