package kzg import ( _ "embed" "encoding/binary" "encoding/hex" "encoding/json" "fmt" "hash" "math/big" "os" "runtime" "sync" "github.com/pkg/errors" "golang.org/x/crypto/sha3" "golang.org/x/sync/errgroup" "source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves" "source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves/native/bls48581" ) type PowersOfTauJson struct { G1Affines []string `json:"G1Powers"` G2Affines []string `json:"G2Powers"` G1FFT []string `json:"G1FFT"` } type ContributionJson struct { PowersOfTau PowersOfTauJson `json:"powersOfTau"` PotPubKey string `json:"potPubKey"` Witness Witness `json:"witness"` VoucherPubKey string `json:"voucherPubKey"` } type BatchContribution struct { Contribution Contribution } type PowersOfTau struct { G1Affines []*bls48581.ECP G2Affines []*bls48581.ECP8 G1FFT []*bls48581.ECP } type CeremonyState struct { PowersOfTau PowersOfTauJson `json:"powersOfTau"` PotPubKey string `json:"potPubKey"` Witness Witness `json:"witness"` VoucherPubKeys []string `json:"voucherPubKeys"` } type Witness struct { RunningProducts []string `json:"runningProducts"` PotPubKeys []string `json:"potPubKeys"` } type Contribution struct { NumG1Powers int NumG2Powers int PowersOfTau PowersOfTau PotPubKey *bls48581.ECP8 } type KZGProver struct { bytesPerScalar int curve *curves.PairingCurve hashFunc func() hash.Hash orderBI *big.Int } var RootOfUnityBLS48581 map[uint64]curves.PairingScalar = make(map[uint64]curves.PairingScalar) var RootsOfUnityBLS48581 map[uint64][]curves.PairingScalar = make(map[uint64][]curves.PairingScalar) var ReverseRootsOfUnityBLS48581 map[uint64][]curves.PairingScalar = make(map[uint64][]curves.PairingScalar) var CeremonyBLS48581G1 []curves.PairingPoint var CeremonyBLS48581G2 []curves.PairingPoint var CeremonyRunningProducts []curves.PairingPoint var CeremonyPotPubKeys []curves.PairingPoint var CeremonySignatories []curves.Point var FFTBLS48581 map[uint64][]curves.PairingPoint = make(map[uint64][]curves.PairingPoint) func TestInit(file string) { // start with phase 1 ceremony: csBytes, err := os.ReadFile(file) if err != nil { panic(err) } bls48581.Init() cs := &CeremonyState{} if err := json.Unmarshal(csBytes, cs); err != nil { panic(err) } g1s := make([]curves.PairingPoint, 1024) g2s := make([]curves.PairingPoint, 257) g1ffts := make([]curves.PairingPoint, 1024) wg := sync.WaitGroup{} wg.Add(1024) for i := 0; i < 1024; i++ { i := i go func() { b, err := hex.DecodeString(cs.PowersOfTau.G1Affines[i][2:]) if err != nil { panic(err) } g1, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(b) if err != nil { panic(err) } g1s[i] = g1.(curves.PairingPoint) f, err := hex.DecodeString(cs.PowersOfTau.G1FFT[i][2:]) if err != nil { panic(err) } g1fft, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(f) if err != nil { panic(err) } g1ffts[i] = g1fft.(curves.PairingPoint) if i < 257 { b, err := hex.DecodeString(cs.PowersOfTau.G2Affines[i][2:]) if err != nil { panic(err) } g2, err := curves.BLS48581G2().NewGeneratorPoint().FromAffineCompressed( b, ) if err != nil { panic(err) } g2s[i] = g2.(curves.PairingPoint) } wg.Done() }() } wg.Wait() wg.Add(len(cs.Witness.RunningProducts)) CeremonyRunningProducts = make([]curves.PairingPoint, len(cs.Witness.RunningProducts)) for i, s := range cs.Witness.RunningProducts { i, s := i, s go func() { b, err := hex.DecodeString(s[2:]) if err != nil { panic(err) } g1, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(b) if err != nil { panic(err) } CeremonyRunningProducts[i] = g1.(curves.PairingPoint) wg.Done() }() } wg.Wait() wg.Add(len(cs.Witness.PotPubKeys)) CeremonyPotPubKeys = make([]curves.PairingPoint, len(cs.Witness.PotPubKeys)) for i, s := range cs.Witness.PotPubKeys { i, s := i, s go func() { b, err := hex.DecodeString(s[2:]) if err != nil { panic(err) } g2, err := curves.BLS48581G2().NewGeneratorPoint().FromAffineCompressed(b) if err != nil { panic(err) } CeremonyPotPubKeys[i] = g2.(curves.PairingPoint) wg.Done() }() } wg.Wait() wg.Add(len(cs.VoucherPubKeys)) CeremonySignatories = make([]curves.Point, len(cs.VoucherPubKeys)) for i, s := range cs.VoucherPubKeys { i, s := i, s go func() { b, err := hex.DecodeString(s[2:]) if err != nil { panic(err) } CeremonySignatories[i], err = curves.ED448().Point.FromAffineCompressed(b) if err != nil { panic(err) } wg.Done() }() } wg.Wait() CeremonyBLS48581G1 = g1s CeremonyBLS48581G2 = g2s // Post-ceremony, precompute everything and put it in the finalized ceremony // state modulus := make([]byte, 73) bls48581.NewBIGints(bls48581.CURVE_Order, nil).ToBytes(modulus) q := new(big.Int).SetBytes(modulus) sizes := []int64{16, 128, 1024} wg.Add(len(sizes)) root := make([]curves.PairingScalar, 3) roots := make([][]curves.PairingScalar, 3) reverseRoots := make([][]curves.PairingScalar, 3) ffts := make([][]curves.PairingPoint, 3) for idx, i := range sizes { i := i idx := idx go func() { exp := new(big.Int).Quo( new(big.Int).Sub(q, big.NewInt(1)), big.NewInt(i), ) rootOfUnity := new(big.Int).Exp(big.NewInt(int64(37)), exp, q) roots[idx] = make([]curves.PairingScalar, i+1) reverseRoots[idx] = make([]curves.PairingScalar, i+1) wg2 := sync.WaitGroup{} wg2.Add(int(i)) for j := int64(0); j < i; j++ { j := j go func() { rev := big.NewInt(int64(j)) r := new(big.Int).Exp( rootOfUnity, rev, q, ) scalar, _ := (&curves.ScalarBls48581{}).SetBigInt(r) if rev.Cmp(big.NewInt(1)) == 0 { root[idx] = scalar.(curves.PairingScalar) } roots[idx][j] = scalar.(curves.PairingScalar) reverseRoots[idx][i-j] = roots[idx][j] wg2.Done() }() } wg2.Wait() roots[idx][i] = roots[idx][0] reverseRoots[idx][0] = reverseRoots[idx][i] wg.Done() }() } wg.Wait() wg.Add(len(sizes)) for i := range root { i := i RootOfUnityBLS48581[uint64(sizes[i])] = root[i] RootsOfUnityBLS48581[uint64(sizes[i])] = roots[i] ReverseRootsOfUnityBLS48581[uint64(sizes[i])] = reverseRoots[i] go func() { // We precomputed 65536, others are cheap and will be fully precomputed // post-ceremony if sizes[i] < 65536 { fftG1, err := FFTG1( CeremonyBLS48581G1[:sizes[i]], *curves.BLS48581( curves.BLS48581G1().NewGeneratorPoint(), ), uint64(sizes[i]), true, ) if err != nil { panic(err) } ffts[i] = fftG1 } else { ffts[i] = g1ffts } wg.Done() }() } wg.Wait() for i := range root { FFTBLS48581[uint64(sizes[i])] = ffts[i] } } //go:embed ceremony.json var csBytes []byte func Init() { // start with phase 1 ceremony: bls48581.Init() cs := &CeremonyState{} if err := json.Unmarshal(csBytes, cs); err != nil { panic(err) } g1s := make([]curves.PairingPoint, 65536) g2s := make([]curves.PairingPoint, 257) g1ffts := make([]curves.PairingPoint, 65536) wg := errgroup.Group{} wg.SetLimit(runtime.NumCPU()) for i := 0; i < 65536; i++ { i := i wg.Go(func() error { b, err := hex.DecodeString(cs.PowersOfTau.G1Affines[i][2:]) if err != nil { panic(err) } g1, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(b) if err != nil { panic(err) } g1s[i] = g1.(curves.PairingPoint) f, err := hex.DecodeString(cs.PowersOfTau.G1FFT[i][2:]) if err != nil { panic(err) } g1fft, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(f) if err != nil { panic(err) } g1ffts[i] = g1fft.(curves.PairingPoint) if i < 257 { b, err := hex.DecodeString(cs.PowersOfTau.G2Affines[i][2:]) if err != nil { panic(err) } g2, err := curves.BLS48581G2().NewGeneratorPoint().FromAffineCompressed( b, ) if err != nil { panic(err) } g2s[i] = g2.(curves.PairingPoint) } return nil }) } CeremonyRunningProducts = make([]curves.PairingPoint, len(cs.Witness.RunningProducts)) for i, s := range cs.Witness.RunningProducts { i, s := i, s wg.Go(func() error { b, err := hex.DecodeString(s[2:]) if err != nil { panic(err) } g1, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(b) if err != nil { panic(err) } CeremonyRunningProducts[i] = g1.(curves.PairingPoint) return nil }) } CeremonyPotPubKeys = make([]curves.PairingPoint, len(cs.Witness.PotPubKeys)) for i, s := range cs.Witness.PotPubKeys { i, s := i, s wg.Go(func() error { b, err := hex.DecodeString(s[2:]) if err != nil { panic(err) } g2, err := curves.BLS48581G2().NewGeneratorPoint().FromAffineCompressed(b) if err != nil { panic(err) } CeremonyPotPubKeys[i] = g2.(curves.PairingPoint) return nil }) } CeremonySignatories = make([]curves.Point, len(cs.VoucherPubKeys)) for i, s := range cs.VoucherPubKeys { i, s := i, s wg.Go(func() error { b, err := hex.DecodeString(s[2:]) if err != nil { panic(err) } CeremonySignatories[i], err = curves.ED448().Point.FromAffineCompressed(b) if err != nil { panic(err) } return nil }) } wg.Wait() CeremonyBLS48581G1 = g1s CeremonyBLS48581G2 = g2s // Post-ceremony, precompute everything and put it in the finalized ceremony // state modulus := make([]byte, 73) bls48581.NewBIGints(bls48581.CURVE_Order, nil).ToBytes(modulus) q := new(big.Int).SetBytes(modulus) sizes := []int64{16, 128, 1024, 65536} wg = errgroup.Group{} wg.SetLimit(runtime.NumCPU()) root := make([]curves.PairingScalar, 4) roots := make([][]curves.PairingScalar, 4) reverseRoots := make([][]curves.PairingScalar, 4) ffts := make([][]curves.PairingPoint, 4) for idx, i := range sizes { i := i idx := idx wg.Go(func() error { exp := new(big.Int).Quo( new(big.Int).Sub(q, big.NewInt(1)), big.NewInt(i), ) rootOfUnity := new(big.Int).Exp(big.NewInt(int64(37)), exp, q) roots[idx] = make([]curves.PairingScalar, i+1) reverseRoots[idx] = make([]curves.PairingScalar, i+1) wg2 := sync.WaitGroup{} wg2.Add(int(i)) for j := int64(0); j < i; j++ { j := j go func() { rev := big.NewInt(int64(j)) r := new(big.Int).Exp( rootOfUnity, rev, q, ) scalar, _ := (&curves.ScalarBls48581{}).SetBigInt(r) if rev.Cmp(big.NewInt(1)) == 0 { root[idx] = scalar.(curves.PairingScalar) } roots[idx][j] = scalar.(curves.PairingScalar) reverseRoots[idx][i-j] = roots[idx][j] wg2.Done() }() } wg2.Wait() roots[idx][i] = roots[idx][0] reverseRoots[idx][0] = reverseRoots[idx][i] return nil }) } wg.Wait() wg = errgroup.Group{} wg.SetLimit(runtime.NumCPU()) for i := range root { i := i RootOfUnityBLS48581[uint64(sizes[i])] = root[i] RootsOfUnityBLS48581[uint64(sizes[i])] = roots[i] ReverseRootsOfUnityBLS48581[uint64(sizes[i])] = reverseRoots[i] wg.Go(func() error { // We precomputed 65536, others are cheap and will be fully precomputed // post-ceremony if sizes[i] < 65536 { fftG1, err := FFTG1( CeremonyBLS48581G1[:sizes[i]], *curves.BLS48581( curves.BLS48581G1().NewGeneratorPoint(), ), uint64(sizes[i]), true, ) if err != nil { panic(err) } ffts[i] = fftG1 } else { ffts[i] = g1ffts } return nil }) } wg.Wait() for i := range root { FFTBLS48581[uint64(sizes[i])] = ffts[i] } } func NewKZGProver( curve *curves.PairingCurve, hashFunc func() hash.Hash, orderBI *big.Int, ) *KZGProver { if curve.Name != curves.BLS48581Name { // kzg ceremony transcript not available for any other curve return nil } return &KZGProver{ bytesPerScalar: 64, curve: curve, hashFunc: hashFunc, orderBI: orderBI, } } func DefaultKZGProver() *KZGProver { modulus := make([]byte, 73) bls48581.NewBIGints(bls48581.CURVE_Order, nil).ToBytes(modulus) q := new(big.Int).SetBytes(modulus) return NewKZGProver( curves.BLS48581(curves.BLS48581G1().Point), sha3.New256, q, ) } func (p *KZGProver) BytesToPolynomial( bytes []byte, ) ([]curves.PairingScalar, error) { size := len(bytes) / p.bytesPerScalar truncLast := false if len(bytes)%p.bytesPerScalar > 0 { truncLast = true } poly := []curves.PairingScalar{} var i int for i = 0; i < size; i++ { scalar, err := p.curve.NewScalar().SetBytes( bytes[i*p.bytesPerScalar : (i+1)*p.bytesPerScalar], ) if err != nil { return nil, errors.Wrap(err, "could not set bytes for scalar") } poly = append( poly, scalar.(curves.PairingScalar), ) } if truncLast { scalar, err := p.curve.NewScalar().SetBytes( bytes[i*p.bytesPerScalar:], ) if err != nil { return nil, errors.Wrap(err, "could not set bytes for scalar") } poly = append( poly, scalar.(curves.PairingScalar), ) } return poly, nil } func (p *KZGProver) PointLinearCombination( points []curves.PairingPoint, scalars []curves.PairingScalar, ) (curves.PairingPoint, error) { if len(points) != len(scalars) { return nil, fmt.Errorf( "length mismatch between arguments, points: %d, scalars: %d", len(points), len(scalars), ) } result := p.curve.NewG1IdentityPoint() for i, p := range points { result = result.Add(p.Mul(scalars[i])).(curves.PairingPoint) } return result, nil } func (p *KZGProver) PolynomialLinearCombination( polynomials [][]curves.PairingScalar, scalars []curves.PairingScalar, ) ([]curves.PairingScalar, error) { if len(polynomials) != len(scalars) { return nil, errors.New("length mismatch between arguments") } result := make([]curves.PairingScalar, len(polynomials[0])) for i := range polynomials[0] { result[i] = p.curve.NewScalar() } for j, ps := range polynomials { for i, p := range ps { result[i] = result[i].Add(p.Mul(scalars[j])).(curves.PairingScalar) } } return result, nil } func (p *KZGProver) EvaluateLagrangeForm( polynomial []curves.PairingScalar, x curves.PairingScalar, fftWidth uint64, scale uint8, ) (curves.PairingScalar, error) { if uint64(len(polynomial)) != fftWidth>>scale { return nil, errors.Wrap( errors.New("polynomial length does not match stride"), "evaluate lagrange form", ) } width := p.curve.NewScalar().New(len(polynomial)) y := p.curve.NewScalar() for i := 0; i < len(polynomial); i++ { numerator := polynomial[i].Mul(RootsOfUnityBLS48581[fftWidth][i<