ceremonyclient/node/crypto/kzg/fft.go
Cassandra Heart 0cd6b41f5d
v1.2.4 (#43)
2024-02-13 01:04:56 -06:00

267 lines
5.4 KiB
Go

package kzg
import (
"math/big"
"github.com/pkg/errors"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
)
func recurseFFT(
values []curves.PairingScalar,
offset uint64,
stride uint64,
rootsStride uint64,
out []curves.PairingScalar,
fftWidth uint64,
inverse bool,
) {
roots := RootsOfUnityBLS48581
if inverse {
roots = ReverseRootsOfUnityBLS48581
}
if len(out) <= 16 {
l := uint64(len(out))
for i := uint64(0); i < l; i++ {
last := values[offset].Mul(roots[fftWidth][0])
for j := uint64(1); j < l; j++ {
last = last.Add(values[offset+j*stride].Mul(
roots[fftWidth][((i*j)%l)*rootsStride],
))
}
out[i] = last.(curves.PairingScalar)
}
return
}
half := uint64(len(out)) >> 1
// slide to the left
recurseFFT(
values,
offset,
stride<<1,
rootsStride<<1,
out[:half],
fftWidth,
inverse,
)
// slide to the right
recurseFFT(
values,
offset+stride,
stride<<1,
rootsStride<<1,
out[half:],
fftWidth,
inverse,
)
// cha cha now, y'all
for i := uint64(0); i < half; i++ {
mul := out[i+half].Mul(
roots[fftWidth][i*rootsStride],
).(curves.PairingScalar)
mulAdd := out[i].Add(mul).(curves.PairingScalar)
out[i+half] = out[i].Sub(mul).(curves.PairingScalar)
out[i] = mulAdd
}
}
func FFT(
values []curves.PairingScalar,
curve curves.PairingCurve,
fftWidth uint64,
inverse bool,
) ([]curves.PairingScalar, error) {
width := uint64(len(values))
if width > fftWidth {
return nil, errors.New("invalid width of values")
}
if width&(width-1) != 0 {
width = nearestPowerOfTwo(width)
}
// We make a copy so we can mutate it during the work.
workingValues := make([]curves.PairingScalar, width)
for i := 0; i < len(values); i++ {
workingValue := values[i].Clone()
workingValues[i] = workingValue.(curves.PairingScalar)
}
for i := uint64(len(values)); i < width; i++ {
workingValue, err := curve.NewScalar().SetBigInt(
big.NewInt(0),
)
if err != nil {
return nil, errors.Wrap(err, "invalid scalar")
}
workingValues[i] = workingValue.(curves.PairingScalar)
}
out := make([]curves.PairingScalar, width)
stride := fftWidth / width
for i := 0; i < len(out); i++ {
out[i] = curve.NewScalar()
}
if inverse {
invLen, err := curve.NewScalar().SetBigInt(big.NewInt((int64(width))))
if err != nil {
return nil, errors.Wrap(err, "invalid int")
}
inv, err := invLen.Invert()
if err != nil {
return nil, errors.Wrap(err, "could not invert")
}
invLen = inv.(curves.PairingScalar)
recurseFFT(workingValues, 0, 1, stride, out, fftWidth, inverse)
for i := 0; i < len(out); i++ {
out[i] = out[i].Mul(invLen).(curves.PairingScalar)
}
return out, nil
} else {
recurseFFT(workingValues, 0, 1, stride, out, fftWidth, inverse)
return out, nil
}
}
func recurseFFTG1(
values []curves.PairingPoint,
offset uint64,
stride uint64,
rootsStride uint64,
out []curves.PairingPoint,
fftWidth uint64,
inverse bool,
) {
roots := RootsOfUnityBLS48581
if inverse {
roots = ReverseRootsOfUnityBLS48581
}
if len(out) <= 16 {
l := uint64(len(out))
for i := uint64(0); i < l; i++ {
last := values[offset].Mul(roots[fftWidth][0])
for j := uint64(1); j < l; j++ {
last = last.Add(values[offset+j*stride].Mul(
roots[fftWidth][((i*j)%l)*rootsStride],
))
}
out[i] = last.(curves.PairingPoint)
}
return
}
half := uint64(len(out)) >> 1
// slide to the left
recurseFFTG1(
values,
offset,
stride<<1,
rootsStride<<1,
out[:half],
fftWidth,
inverse,
)
// slide to the right
recurseFFTG1(
values,
offset+stride,
stride<<1,
rootsStride<<1,
out[half:],
fftWidth,
inverse,
)
// cha cha now, y'all
for i := uint64(0); i < half; i++ {
mul := out[i+half].Mul(roots[fftWidth][i*rootsStride]).(curves.PairingPoint)
mulAdd := out[i].Add(mul).(curves.PairingPoint)
out[i+half] = out[i].Sub(mul).(curves.PairingPoint)
out[i] = mulAdd
}
}
func FFTG1(
values []curves.PairingPoint,
curve curves.PairingCurve,
fftWidth uint64,
inverse bool,
) ([]curves.PairingPoint, error) {
width := uint64(len(values))
if width > fftWidth {
return nil, errors.New("invalid width of values")
}
if width&(width-1) != 0 {
width = nearestPowerOfTwo(width)
}
workingValues := make([]curves.PairingPoint, width)
for i := 0; i < len(values); i++ {
workingValue, err := curve.NewG1GeneratorPoint().FromAffineCompressed(
values[i].ToAffineCompressed(),
)
if err != nil {
return nil, errors.Wrap(err, "invalid point")
}
workingValues[i] = workingValue.(curves.PairingPoint)
}
for i := uint64(len(values)); i < width; i++ {
workingValues[i] = curve.NewG1IdentityPoint()
}
out := make([]curves.PairingPoint, width)
stride := fftWidth / width
for i := 0; i < len(out); i++ {
out[i] = curve.NewG1IdentityPoint()
}
if inverse {
invLen, err := curve.NewScalar().SetBigInt(big.NewInt((int64(width))))
if err != nil {
return nil, errors.Wrap(err, "invalid int")
}
inv, err := invLen.Invert()
if err != nil {
return nil, errors.Wrap(err, "could not invert")
}
invLen = inv.(curves.PairingScalar)
recurseFFTG1(workingValues, 0, 1, stride, out, fftWidth, inverse)
for i := 0; i < len(out); i++ {
out[i] = out[i].Mul(invLen).(curves.PairingPoint)
}
return out, nil
} else {
recurseFFTG1(workingValues, 0, 1, stride, out, fftWidth, inverse)
return out, nil
}
}
func nearestPowerOfTwo(number uint64) uint64 {
power := uint64(1)
for number > power {
power = power << 1
}
return power
}