210 lines
5.9 KiB
Go
210 lines
5.9 KiB
Go
package ipa
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"math"
|
|
"runtime"
|
|
|
|
"github.com/crate-crypto/go-ipa/bandersnatch/fp"
|
|
"github.com/crate-crypto/go-ipa/bandersnatch/fr"
|
|
"github.com/crate-crypto/go-ipa/banderwagon"
|
|
"github.com/crate-crypto/go-ipa/common"
|
|
)
|
|
|
|
// IPAConfig contains all the necessary information to create an IPA related proofs,
|
|
// such as the SRS, Q, and precomputed weights for the barycentric formula.
|
|
type IPAConfig struct {
|
|
SRS []banderwagon.Element
|
|
Q banderwagon.Element
|
|
|
|
PrecompMSM banderwagon.MSMPrecomp
|
|
PrecomputedWeights *PrecomputedWeights
|
|
// The number of rounds the prover and verifier must complete
|
|
// in the IPA argument, this will be log2 of the size of the input vectors
|
|
// since the vector is halved on each round
|
|
numRounds uint32
|
|
}
|
|
|
|
// NewIPASettings generates the SRS, Q and precomputed weights for the barycentric formula.
|
|
// The SRS is generated as common.VectorLength random points where the relative discrete log is
|
|
// not known between each generator.
|
|
func NewIPASettings() (*IPAConfig, error) {
|
|
srs := GenerateRandomPoints(common.VectorLength)
|
|
precompMSM, err := banderwagon.NewPrecompMSM(srs)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("creating precomputed MSM: %s", err)
|
|
}
|
|
return &IPAConfig{
|
|
SRS: srs,
|
|
Q: banderwagon.Generator,
|
|
PrecompMSM: precompMSM,
|
|
PrecomputedWeights: NewPrecomputedWeights(),
|
|
numRounds: computeNumRounds(common.VectorLength),
|
|
}, nil
|
|
}
|
|
|
|
// MultiScalar computes the multi scalar multiplication of points and scalars.
|
|
func MultiScalar(points []banderwagon.Element, scalars []fr.Element) (banderwagon.Element, error) {
|
|
var result banderwagon.Element
|
|
result.SetIdentity()
|
|
|
|
res, err := result.MultiExp(points, scalars, banderwagon.MultiExpConfig{NbTasks: runtime.NumCPU(), ScalarsMont: true})
|
|
if err != nil {
|
|
return banderwagon.Element{}, fmt.Errorf("mult exponentiation was not successful: %w", err)
|
|
}
|
|
|
|
return *res, nil
|
|
}
|
|
|
|
// Commit calculates the Pedersen Commitment of a polynomial polynomial
|
|
// in evaluation form using the SRS.
|
|
func (ic *IPAConfig) Commit(polynomial []fr.Element) banderwagon.Element {
|
|
return ic.PrecompMSM.MSM(polynomial)
|
|
}
|
|
|
|
// commit commits to a polynomial using the input group elements
|
|
func commit(groupElements []banderwagon.Element, polynomial []fr.Element) (banderwagon.Element, error) {
|
|
if len(groupElements) != len(polynomial) {
|
|
return banderwagon.Element{}, fmt.Errorf("group elements and polynomial are different sizes, %d != %d", len(groupElements), len(polynomial))
|
|
}
|
|
return MultiScalar(groupElements, polynomial)
|
|
}
|
|
|
|
// InnerProd computes the inner product of a and b.
|
|
func InnerProd(a []fr.Element, b []fr.Element) (fr.Element, error) {
|
|
if len(a) != len(b) {
|
|
return fr.Element{}, fmt.Errorf("a and b are different sizes, %d != %d", len(a), len(b))
|
|
}
|
|
|
|
result := fr.Zero()
|
|
for i := 0; i < len(a); i++ {
|
|
var tmp fr.Element
|
|
|
|
tmp.Mul(&a[i], &b[i])
|
|
result.Add(&result, &tmp)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// Computes c[i] =a[i] + b[i] * x
|
|
// returns c
|
|
func foldScalars(a []fr.Element, b []fr.Element, x fr.Element) ([]fr.Element, error) {
|
|
if len(a) != len(b) {
|
|
return nil, fmt.Errorf("slices not equal length")
|
|
}
|
|
result := make([]fr.Element, len(a))
|
|
for i := 0; i < len(a); i++ {
|
|
var bx fr.Element
|
|
bx.Mul(&x, &b[i])
|
|
result[i].Add(&bx, &a[i])
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// Computes c[i] =a[i] + b[i] * x
|
|
// returns c
|
|
func foldPoints(a []banderwagon.Element, b []banderwagon.Element, x fr.Element) ([]banderwagon.Element, error) {
|
|
if len(a) != len(b) {
|
|
return nil, fmt.Errorf("slices not equal length")
|
|
}
|
|
|
|
result := make([]banderwagon.Element, len(a))
|
|
for i := 0; i < len(a); i++ {
|
|
var bx banderwagon.Element
|
|
bx.ScalarMul(&b[i], &x)
|
|
result[i].Add(&bx, &a[i])
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// Splits a slice of scalars into two slices of equal length
|
|
// Eg [S1,S2,S3,S4] becomes [S1,S2] , [S3,S4]
|
|
func splitScalars(x []fr.Element) ([]fr.Element, []fr.Element, error) {
|
|
if len(x)%2 != 0 {
|
|
return nil, nil, fmt.Errorf("slice should have an even length")
|
|
}
|
|
|
|
mid := len(x) / 2
|
|
return x[:mid], x[mid:], nil
|
|
}
|
|
|
|
// Splits a slice of points into two slices of equal length
|
|
// Eg [P1,P2,P3,P4,P5,P6] becomes [P1,P2,P3] , [P4,P5,P6]
|
|
func splitPoints(x []banderwagon.Element) ([]banderwagon.Element, []banderwagon.Element, error) {
|
|
if len(x)%2 != 0 {
|
|
return nil, nil, fmt.Errorf("slice should have an even length")
|
|
}
|
|
mid := len(x) / 2
|
|
|
|
return x[:mid], x[mid:], nil
|
|
}
|
|
|
|
// This function does log2(vector_size)
|
|
//
|
|
// Since we do not allow for 0 size vectors, this is checked
|
|
// since we also do not allow for vectors which are not powers of 2, this is also checked
|
|
//
|
|
// It is okay to panic here, because the input is a constant, so it will panic before
|
|
// any proofs are made.
|
|
func computeNumRounds(vectorSize uint32) uint32 {
|
|
// Check if this number is 0
|
|
// zero is not a valid input to this function for our usecase
|
|
if vectorSize == 0 {
|
|
panic("zero is not a valid input")
|
|
}
|
|
|
|
// See: https://stackoverflow.com/a/600306
|
|
isPow2 := (vectorSize & (vectorSize - 1)) == 0
|
|
|
|
if !isPow2 {
|
|
panic("non power of 2 numbers are not valid inputs")
|
|
}
|
|
|
|
res := math.Log2(float64(vectorSize))
|
|
|
|
return uint32(res)
|
|
}
|
|
|
|
// GenerateRandomPoints generates numPoints random points on the curve using
|
|
// hardcoded seed.
|
|
func GenerateRandomPoints(numPoints uint64) []banderwagon.Element {
|
|
seed := "eth_verkle_oct_2021"
|
|
|
|
points := []banderwagon.Element{}
|
|
|
|
var increment uint64 = 0
|
|
|
|
for uint64(len(points)) != numPoints {
|
|
|
|
digest := sha256.New()
|
|
digest.Write([]byte(seed))
|
|
|
|
b := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(b, increment)
|
|
digest.Write(b)
|
|
|
|
hash := digest.Sum(nil)
|
|
|
|
var x fp.Element
|
|
x.SetBytes(hash)
|
|
|
|
increment++
|
|
|
|
x_as_bytes := x.Bytes()
|
|
var point_found banderwagon.Element
|
|
err := point_found.SetBytes(x_as_bytes[:])
|
|
if err != nil {
|
|
// This point is not in the correct subgroup or on the curve
|
|
continue
|
|
}
|
|
points = append(points, point_found)
|
|
|
|
}
|
|
|
|
return points
|
|
}
|