201 lines
5.3 KiB
Go
201 lines
5.3 KiB
Go
package ipa
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/crate-crypto/go-ipa/bandersnatch/fr"
|
|
"github.com/crate-crypto/go-ipa/common"
|
|
)
|
|
|
|
// domainSize will always equal 256, which is the same
|
|
// as the degree of the polynomial (+1), we are committing to.
|
|
// This constant is defined here for semantic reasons.
|
|
const domainSize = common.VectorLength
|
|
|
|
// PrecomputedWeights contains precomputed coefficients for efficient
|
|
// usage of the Barycentric formula.
|
|
type PrecomputedWeights struct {
|
|
// This stores A'(x_i) and 1/A'(x_i)
|
|
barycentricWeights []fr.Element
|
|
// This stores 1/k and -1/k for k \in [0, 255]
|
|
invertedDomain []fr.Element
|
|
}
|
|
|
|
// NewPrecomputedWeights generates the precomputed weights for the barycentric formula.
|
|
func NewPrecomputedWeights() *PrecomputedWeights {
|
|
// Imagine we have two arrays of the same length and we concatenate them together
|
|
// This is how we will store the A'(x_i) and 1/A'(x_i)
|
|
// This midpoint variable is used to compute the offset that we need
|
|
// to place 1/A'(x_i)
|
|
midpoint := uint64(domainSize)
|
|
|
|
// Note there are DOMAIN_SIZE number of weights, but we are also storing their inverses
|
|
// so we need double the amount of space
|
|
barycentricWeights := make([]fr.Element, midpoint*2)
|
|
for i := uint64(0); i < midpoint; i++ {
|
|
weight := computeBarycentricWeightForElement(i)
|
|
|
|
var invWeight fr.Element
|
|
invWeight.Inverse(&weight)
|
|
|
|
barycentricWeights[i] = weight
|
|
barycentricWeights[i+midpoint] = invWeight
|
|
}
|
|
|
|
// Computing 1/k and -1/k for k \in [0, 255]
|
|
// Note that since we cannot do 1/0, we have one less element
|
|
midpoint = domainSize - 1
|
|
invertedDomain := make([]fr.Element, midpoint*2)
|
|
for i := uint64(1); i < domainSize; i++ {
|
|
var k fr.Element
|
|
k.SetUint64(i)
|
|
k.Inverse(&k)
|
|
|
|
var negative_k fr.Element
|
|
zero := fr.Zero()
|
|
negative_k.Sub(&zero, &k)
|
|
|
|
invertedDomain[i-1] = k
|
|
invertedDomain[(i-1)+midpoint] = negative_k
|
|
}
|
|
|
|
return &PrecomputedWeights{
|
|
barycentricWeights: barycentricWeights,
|
|
invertedDomain: invertedDomain,
|
|
}
|
|
|
|
}
|
|
|
|
// computes A'(x_j) where x_j must be an element in the domain
|
|
// This is computed as the product of x_j - x_i where x_i is an element in the domain
|
|
// and x_i is not equal to x_j
|
|
func computeBarycentricWeightForElement(element uint64) fr.Element {
|
|
// let domain_element_fr = Fr::from(domain_element as u128);
|
|
if element > domainSize {
|
|
panic(fmt.Sprintf("the domain is [0,255], %d is not in the domain", element))
|
|
}
|
|
|
|
var domain_element_fr fr.Element
|
|
domain_element_fr.SetUint64(element)
|
|
|
|
total := fr.One()
|
|
|
|
for i := uint64(0); i < domainSize; i++ {
|
|
if i == element {
|
|
continue
|
|
}
|
|
|
|
var i_fr fr.Element
|
|
i_fr.SetUint64(i)
|
|
|
|
var tmp fr.Element
|
|
tmp.Sub(&domain_element_fr, &i_fr)
|
|
|
|
total.Mul(&total, &tmp)
|
|
}
|
|
|
|
return total
|
|
}
|
|
|
|
// ComputeBarycentricCoefficients, computes the coefficients `bary_coeffs`
|
|
// for a point `z` such that when we have a polynomial `p` in lagrange
|
|
// basis, the inner product of `p` and `bary_coeffs` is equal to p(z)
|
|
// Note that `z` should not be in the domain.
|
|
// This can also be seen as the lagrange coefficients L_i(point)
|
|
func (preComp *PrecomputedWeights) ComputeBarycentricCoefficients(point fr.Element) []fr.Element {
|
|
// Compute A'(x_i) * (point - x_i)
|
|
lagrangeEvals := make([]fr.Element, domainSize)
|
|
for i := uint64(0); i < domainSize; i++ {
|
|
weight := preComp.barycentricWeights[i]
|
|
|
|
var i_fr fr.Element
|
|
i_fr.SetUint64(i)
|
|
lagrangeEvals[i].Sub(&point, &i_fr)
|
|
lagrangeEvals[i].Mul(&lagrangeEvals[i], &weight)
|
|
}
|
|
|
|
totalProd := fr.One()
|
|
for i := uint64(0); i < domainSize; i++ {
|
|
var i_fr fr.Element
|
|
i_fr.SetUint64(i)
|
|
|
|
var tmp fr.Element
|
|
tmp.Sub(&point, &i_fr)
|
|
totalProd.Mul(&totalProd, &tmp)
|
|
}
|
|
|
|
lagrangeEvals = fr.BatchInvert(lagrangeEvals)
|
|
for i := uint64(0); i < domainSize; i++ {
|
|
lagrangeEvals[i].Mul(&lagrangeEvals[i], &totalProd)
|
|
}
|
|
|
|
return lagrangeEvals
|
|
}
|
|
|
|
// DivideOnDomain computes f(x) - f(x_i) / x - x_i where x_i is an element in the domain
|
|
func (preComp *PrecomputedWeights) DivideOnDomain(index uint8, f []fr.Element) []fr.Element {
|
|
quotient := make([]fr.Element, domainSize)
|
|
|
|
y := f[index]
|
|
|
|
for i := 0; i < domainSize; i++ {
|
|
if i != int(index) {
|
|
den := i - int(index)
|
|
absDen, is_neg := absInt(den)
|
|
|
|
denInv := preComp.getInvertedElement(absDen, is_neg)
|
|
|
|
// compute q_i
|
|
quotient[i].Sub(&f[i], &y)
|
|
quotient[i].Mul("ient[i], &denInv)
|
|
|
|
weightRatio := preComp.getRatioOfWeights(int(index), i)
|
|
var tmp fr.Element
|
|
tmp.Mul(&weightRatio, "ient[i])
|
|
quotient[index].Sub("ient[index], &tmp)
|
|
}
|
|
}
|
|
|
|
return quotient
|
|
}
|
|
|
|
func (preComp *PrecomputedWeights) getInvertedElement(element int, is_neg bool) fr.Element {
|
|
index := element - 1
|
|
|
|
if is_neg {
|
|
midpoint := len(preComp.invertedDomain) / 2
|
|
index += midpoint
|
|
}
|
|
|
|
return preComp.invertedDomain[index]
|
|
}
|
|
|
|
func (preComp *PrecomputedWeights) getRatioOfWeights(numerator int, denominator int) fr.Element {
|
|
|
|
a := preComp.barycentricWeights[numerator]
|
|
midpoint := len(preComp.barycentricWeights) / 2
|
|
b := preComp.barycentricWeights[denominator+midpoint]
|
|
|
|
var result fr.Element
|
|
result.Mul(&a, &b)
|
|
return result
|
|
}
|
|
|
|
func (preComp *PrecomputedWeights) getInverseBarycentricWeight(i int) fr.Element {
|
|
|
|
midpoint := len(preComp.barycentricWeights) / 2
|
|
return preComp.barycentricWeights[i+midpoint]
|
|
}
|
|
|
|
// Returns the absolute value and true if
|
|
// the value was negative
|
|
func absInt(x int) (int, bool) {
|
|
is_negative := x < 0
|
|
|
|
if is_negative {
|
|
return -x, is_negative
|
|
}
|
|
|
|
return x, is_negative
|
|
}
|