Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
9cf0c0d
refactor: separate bytes from Long API
ivokub May 23, 2025
2251097
refactor: use bytes api
ivokub Jul 14, 2025
4c3cb24
feat: handle edge cases directly in rangechecker
ivokub Jul 14, 2025
57ef0ff
test: test kvstore
ivokub May 23, 2025
617c7ee
chore: include all hints automatic registration
ivokub Jul 3, 2025
7866081
feat: implement conversion package
ivokub Jul 14, 2025
5a84374
fix: use api for asserting kvstore.Store
ivokub Jul 14, 2025
82e5546
refactor: use constant assignment
ivokub Jul 15, 2025
9969dc1
feat: unmarshall field elements ok
ThomasPiellard Apr 27, 2025
25983bc
feat: unmarshall compressed point ok
ThomasPiellard Apr 27, 2025
6f61683
feat: unmask x coord ok
ThomasPiellard Apr 27, 2025
8279e80
feat: subgroup check unmarshalled point ok
ThomasPiellard Apr 27, 2025
4b81747
feat: cleaned test
ThomasPiellard Apr 27, 2025
cbd821b
feat: point eval circuit ok
ThomasPiellard Apr 28, 2025
9da5865
feat: kzg proof ok
ThomasPiellard Apr 29, 2025
3699603
feat: kzg precompile takes []U8
ThomasPiellard Apr 29, 2025
287b5c1
style: Deserialise -> Unmarshall
ThomasPiellard Apr 29, 2025
ed57d10
fix: fixed byte 0 error
ThomasPiellard Apr 30, 2025
d54b646
feat: versionned hash ok
ThomasPiellard Apr 30, 2025
6b90b32
feat: redirecting subgroup check in case g = (0,0)
ThomasPiellard May 4, 2025
a5b2402
feat: logic switch for compression ok
ThomasPiellard May 5, 2025
436d96d
style: removed TODO
ThomasPiellard May 5, 2025
f2e7bbb
feat: updated #constraints
ThomasPiellard May 5, 2025
bc8faf7
fix: fixed golang lint
ThomasPiellard May 5, 2025
a50a39f
fix: linter errors
ivokub May 19, 2025
d0f412e
refactor: move the unmarshalHint to hints file
ivokub May 19, 2025
9b576bb
refactor: rename unmarshall -> unmarshal
ivokub May 19, 2025
893ec9a
Merge branch 'feat/bytes-conversion' into feat/EIP4844-precompile
ivokub Jul 15, 2025
68096f6
feat: use conversion package
ivokub May 23, 2025
e0a2a39
chore: remove constant error
ivokub Jul 3, 2025
1f750cd
refactor: kzg pointeval precompile
ivokub Jul 3, 2025
eedf47a
feat: include KZG SRS
ivokub Jul 3, 2025
13f5205
fix: order of 16-byte to bytearray
ivokub Jul 3, 2025
c21cdbc
refactor: unmarshal BLS12-381 point
ivokub Jul 11, 2025
a880338
test: add edge case tests for marshaling
ivokub Jul 15, 2025
2f2c1c2
docs: fix typo
ivokub Jul 15, 2025
8c9db8c
test: remove debug print line
ivokub Jul 15, 2025
ec5928b
test: use assert.CheckCircuit
ivokub Jul 16, 2025
1505678
feat: more robust range checking
ivokub Jul 16, 2025
58c6793
test: add tests for checking if byte is constrained
ivokub Jul 16, 2025
fbe4a96
feat: add Select
ivokub Jul 16, 2025
fc9ce81
docs: method docs
ivokub Jul 16, 2025
9c09cd1
refactor: use bytes select
ivokub Jul 16, 2025
beb1b22
feat: implement conversion package
ivokub Jul 14, 2025
6d119d2
docs: address comment
ivokub Jul 16, 2025
c369270
fix: check explicitly that bytes are checked
ivokub Jul 16, 2025
47cd263
test: use checkcircuit
ivokub Jul 16, 2025
49fc5df
Merge branch 'feat/bytes-conversion' into feat/EIP4844-precompile
ivokub Jul 16, 2025
95f7637
refactor: do not parse full proving key in package, only in test
ivokub Jul 16, 2025
77daf89
chore: we always hash fixed number of bytes
ivokub Jul 17, 2025
6c7d9da
refactor: use checked bytes values
ivokub Jul 17, 2025
a6530b4
Merge branch 'master' into feat/EIP4844-precompile
ivokub Jul 17, 2025
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
4 changes: 4 additions & 0 deletions std/algebra/emulated/sw_bls12381/g1.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"math/big"

bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
fp_bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fp"
fr_bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/algebra/emulated/sw_emulated"
Expand All @@ -31,6 +32,7 @@ type G1 struct {
api frontend.API
curveF *emulated.Field[BaseField]
w *emulated.Element[BaseField]
halfp *emulated.Element[BaseField]
}

func NewG1(api frontend.API) (*G1, error) {
Expand All @@ -39,10 +41,12 @@ func NewG1(api frontend.API) (*G1, error) {
return nil, fmt.Errorf("new base api: %w", err)
}
w := ba.NewElement("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436")
halfpE := ba.NewElement(new(big.Int).Div(fp_bls12381.Modulus(), big.NewInt(2))) // we know that the modulus is odd and division floors.
return &G1{
api: api,
curveF: ba,
w: w,
halfp: halfpE,
}, nil
}

Expand Down
30 changes: 30 additions & 0 deletions std/algebra/emulated/sw_bls12381/hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func GetHints() []solver.Hint {
decomposeScalarG1,
g1SqrtRatioHint,
g2SqrtRatioHint,
unmarshalG1,
}
}

Expand Down Expand Up @@ -374,3 +375,32 @@ func g2SqrtRatioHint(_ *big.Int, inputs []*big.Int, outputs []*big.Int) error {
return nil
})
}

// unmarshalG1 unmarshals the y coordinate of a compressed BLS12-381 G1 point.
// It takes as input the bytes of the compressed point and returns the y
// coordinate. It uses non-native methods for outputting non-native value.
func unmarshalG1(mod *big.Int, nativeInputs []*big.Int, outputs []*big.Int) error {
return emulated.UnwrapHintWithNativeInput(nativeInputs, outputs, func(emulatedMod *big.Int, nativeInputs, outputs []*big.Int) error {
nbBytes := fp.Bytes
xCoord := make([]byte, nbBytes)
if len(nativeInputs) != nbBytes {
return fmt.Errorf("expecting %d inputs, got %d", nbBytes, len(nativeInputs))
}
for i := range nbBytes {
tmp := nativeInputs[i].Bytes()
if len(tmp) == 0 {
xCoord[i] = 0
} else {
xCoord[i] = tmp[len(tmp)-1] // tmp is in big endian
}
}

var point bls12381.G1Affine
_, err := point.SetBytes(xCoord)
if err != nil {
return fmt.Errorf("set bytes: %w", err)
}
point.Y.BigInt(outputs[0])
return nil
})
}
121 changes: 121 additions & 0 deletions std/algebra/emulated/sw_bls12381/marshal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package sw_bls12381

import (
"fmt"

bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/consensys/gnark-crypto/ecc/bls12-381/fp"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/conversion"
"github.com/consensys/gnark/std/math/emulated"
"github.com/consensys/gnark/std/math/uints"
)

const (
mMask byte = 0b111 << 5
mUncompressed byte = 0b000 << 5
_ byte = 0b001 << 5 // invalid
mUncompressedInfinity byte = 0b010 << 5
_ byte = 0b011 << 5 // invalid
mCompressedSmallest byte = 0b100 << 5
mCompressedLargest byte = 0b101 << 5
mCompressedInfinity byte = 0b110 << 5
_ byte = 0b111 << 5 // invalid
)

// UnmarshalCompressed unmarshals a compressed point in G1. See [pairing
// friendly curves IETF draft] for the details of the encoding.
//
// We assume that the input is a valid compressed point. The length of the input
// must be 48 bytes (but we keep it as a slice for future compatibility with
// interfaces).
//
// The method supports the following compressed point formats:
// - compressed regular point, with y lexicographically smallest (header 0b100<<5)
// - compressed regular point, with y lexicographically largest (header 0b101<<5)
// - compressed point at infinity (header 0b110<<5).
//
// Particularly, the method DOES NOT support uncompressed points (header
// 0b000<<5) and uncompressed points at infinity (header 0b010<<5).
//
// The method performs curve membership check and subgroup membership check.
//
// [pairing friendly curves IETF draft]: https://datatracker.ietf.org/doc/draft-irtf-cfrg-pairing-friendly-curves/11/.
func (g1 *G1) UnmarshalCompressed(compressedPoint []uints.U8) (*G1Affine, error) {
// for future compatibility (adding method to the [algebra.Pairing]
// interface) we haven't set the method signature to be [48]uints.U8, but
// rather a slice. Thus we need to check the length of the input.
if len(compressedPoint) != bls12381.SizeOfG1AffineCompressed {
return nil, fmt.Errorf("compressed point must be %d bytes, got %d", bls12381.SizeOfG1AffineCompressed, len(compressedPoint))
}
// 1 - compute the x coordinate (so it fits in Fp)
nbBytes := fp.Bytes
uapi, err := uints.NewBytes(g1.api)
if err != nil {
return nil, fmt.Errorf("new uints api: %w", err)
}
unmask := uints.NewU8(mMask) // unmaks = ^mask
prefix := uapi.And(unmask, compressedPoint[0]) // prefix = compressedPoint[0] & unmask

// first we remove the prefix from the first byte. The prefix indicates if
// the input is compressed and point at infinity. It also indicates the sign
// of the y coordinate. The prefix is first three bits 0b11100000=0xE0. So to get
// unprefixed x coordinate, we need to mask the first byte with 0x1F = ^0xE0.
mask := uints.NewU8(^mMask) // mask = [0x1F]
firstByte := uapi.And(mask, compressedPoint[0]) // firstByte = compressedPoint[0] & mask
unmaskedXCoord := make([]uints.U8, nbBytes)
unmaskedXCoord[0] = firstByte
copy(unmaskedXCoord[1:], compressedPoint[1:])
x, err := conversion.BytesToEmulated[BaseField](g1.api, unmaskedXCoord)
if err != nil {
return nil, fmt.Errorf("bytes to emulated: %w", err)
}

// 1 - hint y coordinate of the result
rawBytesCompressedPoints := make([]frontend.Variable, nbBytes)
for i := range nbBytes {
rawBytesCompressedPoints[i] = uapi.Value(compressedPoint[i])
}
hout, err := g1.curveF.NewHintWithNativeInput(unmarshalG1, 1, rawBytesCompressedPoints...)
if err != nil {
return nil, fmt.Errorf("unmarshal hint: %w", err)
}
y := hout[0]
res := &G1Affine{X: *x, Y: *y}

// 3 - subgroup check

// if the point is infinity, we do the subgroup check on the base point (otherwise the subgroup
// check fails for (0,0) ). We check later on that the actual point is equal to (0,0).
isCompressedInfinity := g1.api.IsZero(g1.api.Sub(mCompressedInfinity, uapi.Value(prefix)))
_, _, g, _ := bls12381.Generators()
base := NewG1Affine(g)
resTmpX := g1.curveF.Select(isCompressedInfinity, &base.X, x)
resTmpY := g1.curveF.Select(isCompressedInfinity, &base.Y, y)
resTmp := &G1Affine{X: *resTmpX, Y: *resTmpY}
g1.AssertIsOnG1(resTmp)

// 4 - check logic with the mask

// if p=O, we set P'=(0,0) and check equality, else we artificially set P'=P and check equality
isInfinity := g1.api.IsZero(g1.api.Sub(mCompressedInfinity, uapi.Value(prefix)))
zero := emulated.ValueOf[BaseField](0)
infX := g1.curveF.Select(isInfinity, &zero, x)
infY := g1.curveF.Select(isInfinity, &zero, y)
g1.curveF.AssertIsEqual(infX, x)
g1.curveF.AssertIsEqual(infY, y)

// if we take the smallest y, then y < p/2. The constraint also works if p=0 and prefix=compressedInfinity
isCompressedSmallest := g1.api.IsZero(g1.api.Sub(mCompressedSmallest, uapi.Value(prefix)))
negY := g1.curveF.Neg(y)
negY = g1.curveF.Reduce(negY)
smallest := g1.curveF.Select(isCompressedSmallest, y, negY)
g1.curveF.AssertIsLessOrEqual(smallest, g1.halfp)

// if we take the largest y, then -y < p/2. The constraint also works if p=0 and prefix=compressedInfinity
isCompressedLargest := g1.api.IsZero(g1.api.Sub(mCompressedLargest, uapi.Value(prefix)))
smallest = g1.curveF.Select(isCompressedLargest, negY, y)
g1.curveF.AssertIsLessOrEqual(smallest, g1.halfp)

return res, nil
}
Loading