Browse Source

arity verification check with incomplete compute_evaluation

main
Kevin Jue 2 years ago
parent
commit
8797fd15ed
2 changed files with 142 additions and 21 deletions
  1. +125
    -20
      plonky2_verifier/fri.go
  2. +17
    -1
      plonky2_verifier/quadratic_extension.go

+ 125
- 20
plonky2_verifier/fri.go

@ -7,6 +7,7 @@ import (
"gnark-ed25519/poseidon" "gnark-ed25519/poseidon"
"math" "math"
"math/big" "math/big"
"math/bits"
"github.com/consensys/gnark-crypto/field/goldilocks" "github.com/consensys/gnark-crypto/field/goldilocks"
"github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend"
@ -47,7 +48,7 @@ func (f *FriChip) fromOpeningsAndAlpha(openings *FriOpenings, alpha QuadraticExt
reducedOpenings := make([]QuadraticExtension, 0, 2) reducedOpenings := make([]QuadraticExtension, 0, 2)
for _, batch := range openings.Batches { for _, batch := range openings.Batches {
reducedOpenings = append(reducedOpenings, reduceWithPowers(f.qeAPI, batch.values, alpha))
reducedOpenings = append(reducedOpenings, f.qeAPI.ReduceWithPowers(batch.values, alpha))
} }
return reducedOpenings return reducedOpenings
@ -170,24 +171,12 @@ func (f *FriChip) assertNoncanonicalIndicesOK() {
} }
} }
func (f *FriChip) calculateSubgroupX(
xIndexBits []frontend.Variable,
nLog uint64,
func (f *FriChip) expFromBitsConstBase(
base goldilocks.Element,
exponentBits []frontend.Variable,
) F { ) F {
// Compute x from its index
// `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain.
// TODO - Make these as global values
g := field.NewFieldElement(field.GOLDILOCKS_MULTIPLICATIVE_GROUP_GENERATOR.Uint64())
base := field.GoldilocksPrimitiveRootOfUnity(nLog)
product := ONE_F product := ONE_F
// Create a reverse list of xIndexBits
xIndexBitsRev := make([]frontend.Variable, 0)
for i := len(xIndexBits) - 1; i >= 0; i-- {
xIndexBitsRev = append(xIndexBitsRev, xIndexBits[i])
}
for i, bit := range xIndexBitsRev {
for i, bit := range exponentBits {
pow := int64(1 << i) pow := int64(1 << i)
// If the bit is on, we multiply product by base^pow. // If the bit is on, we multiply product by base^pow.
// We can arithmetize this as: // We can arithmetize this as:
@ -208,6 +197,27 @@ func (f *FriChip) calculateSubgroupX(
).(F) ).(F)
} }
return product
}
func (f *FriChip) calculateSubgroupX(
xIndexBits []frontend.Variable,
nLog uint64,
) F {
// Compute x from its index
// `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain.
// TODO - Make these as global values
g := field.NewFieldElement(field.GOLDILOCKS_MULTIPLICATIVE_GROUP_GENERATOR.Uint64())
base := field.GoldilocksPrimitiveRootOfUnity(nLog)
// Create a reverse list of xIndexBits
xIndexBitsRev := make([]frontend.Variable, 0)
for i := len(xIndexBits) - 1; i >= 0; i-- {
xIndexBitsRev = append(xIndexBitsRev, xIndexBits[i])
}
product := f.expFromBitsConstBase(base, xIndexBitsRev)
return f.fieldAPI.Mul(g, product).(F) return f.fieldAPI.Mul(g, product).(F)
} }
@ -237,7 +247,7 @@ func (f *FriChip) friCombineInitial(
) )
} }
reducedEvals := reduceWithPowers(f.qeAPI, evals, friAlpha)
reducedEvals := f.qeAPI.ReduceWithPowers(evals, friAlpha)
numerator := f.qeAPI.SubExtension(reducedEvals, reducedOpenings) numerator := f.qeAPI.SubExtension(reducedEvals, reducedOpenings)
denominator := f.qeAPI.SubExtension(subgroupX_QE, point) denominator := f.qeAPI.SubExtension(subgroupX_QE, point)
sum = f.qeAPI.MulExtension(f.qeAPI.ExpU64Extension(friAlpha, uint64(len(evals))), sum) sum = f.qeAPI.MulExtension(f.qeAPI.ExpU64Extension(friAlpha, uint64(len(evals))), sum)
@ -267,6 +277,44 @@ func (f *FriChip) finalPolyEval(finalPoly PolynomialCoeffs, point QuadraticExten
return ret return ret
} }
func (f *FriChip) computeEvaluation(
x F,
xIndexWithinCosetBits []frontend.Variable,
arityBits uint64,
evals []QuadraticExtension,
beta QuadraticExtension,
) QuadraticExtension {
arity := 1 << arityBits
if (len(evals)) != arity {
panic("len(evals) ! arity")
}
g := field.GoldilocksPrimitiveRootOfUnity(arityBits)
gInv := goldilocks.NewElement(0)
gInv.Exp(g, big.NewInt(int64(arity-1)))
// The evaluation vector needs to be reordered first. Permute the evals array such that each
// element's new index is the bit reverse of it's original index.
// TODO: Optimization - Since the size of the evals array should be constant (e.g. 2^arityBits),
// we can just hard code the permutation.
permutedEvals := make([]QuadraticExtension, len(evals))
for i := uint(0); i < uint(len(evals)); i++ {
bitLen := bits.Len(i)
rightPadLen := arityBits - uint64(bitLen)
newIndex := bits.Reverse(i) << rightPadLen
permutedEvals[newIndex] = evals[i]
}
// Want `g^(arity - rev_x_index_within_coset)` as in the out-of-circuit version. Compute it
// as `(g^-1)^rev_x_index_within_coset`.
revXIndexWithinCosetBits := make([]frontend.Variable, len(xIndexWithinCosetBits))
for i := 0; i < len(xIndexWithinCosetBits); i++ {
revXIndexWithinCosetBits[len(xIndexWithinCosetBits)-1-i] = xIndexWithinCosetBits[i]
}
start := f.expFromBitsConstBase(gInv, revXIndexWithinCosetBits)
cosetStart := f.fieldAPI.Mul(start, x)
}
func (f *FriChip) verifyQueryRound( func (f *FriChip) verifyQueryRound(
instance FriInstanceInfo, instance FriInstanceInfo,
challenges *FriChallenges, challenges *FriChallenges,
@ -299,10 +347,67 @@ func (f *FriChip) verifyQueryRound(
precomputedReducedEval, precomputedReducedEval,
) )
for i, arityBits := range f.friParams.ReductionArityBits {
evals := roundProof.Steps[i].Evals
cosetIndexBits := xIndexBits[arityBits:]
xIndexWithinCosetBits := xIndexBits[:arityBits]
// Assumes that the arity bits will be 4. That means that the range of
// xIndexWithCoset is [0,2^4-1]. This is based on plonky2's circuit recursive
// config: https://github.com/mir-protocol/plonky2/blob/main/plonky2/src/plonk/circuit_data.rs#L63
// Will use a two levels tree of 4-selector gadgets.
if arityBits != 4 {
panic("assuming arity bits is 4")
}
const NUM_LEAF_LOOKUPS = 4
var leafLookups [NUM_LEAF_LOOKUPS]QuadraticExtension
// First create the "leaf" lookup2 circuits
// The will use the least significant bits of the xIndexWithCosetBits array
for i := 0; i < NUM_LEAF_LOOKUPS; i++ {
leafLookups[i] = f.qeAPI.Lookup2(
xIndexWithinCosetBits[0], xIndexWithinCosetBits[1],
evals[i*NUM_LEAF_LOOKUPS], evals[i*NUM_LEAF_LOOKUPS+1], evals[i*NUM_LEAF_LOOKUPS+2], evals[i*NUM_LEAF_LOOKUPS+3],
)
}
// Use the most 2 significant bits of the xIndexWithCosetBits array for the "root" lookup
newEval := f.qeAPI.Lookup2(xIndexWithinCosetBits[2], xIndexWithinCosetBits[3], evals[0], evals[1], evals[2], evals[3])
f.qeAPI.AssertIsEqual(newEval, oldEval)
oldEval := f.computeEvaluation(
subgroupX,
xIndexWithinCosetBits,
arityBits,
evals,
challenges.FriBetas[i],
)
// Convert evals (array of QE) to fields by taking their 0th degree coefficients
fieldEvals := make([]F, len(evals))
for j := 0; j < len(evals); j++ {
fieldEvals[j] = evals[j][0]
}
f.verifyMerkleProofToCapWithCapIndex(
fieldEvals,
cosetIndexBits,
capIndexBits,
proof.CommitPhaseMerkleCaps[i],
&roundProof.Steps[i].MerkleProof,
)
// Update the point x to x^arity.
for j := uint64(0); j < arityBits; j++ {
subgroupX = f.fieldAPI.Mul(subgroupX, subgroupX).(F)
}
xIndexBits = cosetIndexBits
}
finalPolyEval := f.finalPolyEval(proof.FinalPoly, subgroupX_QE) finalPolyEval := f.finalPolyEval(proof.FinalPoly, subgroupX_QE)
f.fieldAPI.AssertIsEqual(oldEval[0], finalPolyEval[0])
f.fieldAPI.AssertIsEqual(oldEval[1], finalPolyEval[1])
f.qeAPI.AssertIsEqual(oldEval, finalPolyEval)
} }
func (f *FriChip) VerifyFriProof( func (f *FriChip) VerifyFriProof(

+ 17
- 1
plonky2_verifier/quadratic_extension.go

@ -109,7 +109,7 @@ func (c *QuadraticExtensionAPI) ExpU64Extension(a QuadraticExtension, exponent u
return product return product
} }
func (c *QuadraticExtensionAPI) reduceWithPowers(terms []QuadraticExtension, scalar QuadraticExtension) QuadraticExtension {
func (c *QuadraticExtensionAPI) ReduceWithPowers(terms []QuadraticExtension, scalar QuadraticExtension) QuadraticExtension {
sum := c.ZERO_QE sum := c.ZERO_QE
for i := len(terms) - 1; i >= 0; i-- { for i := len(terms) - 1; i >= 0; i-- {
@ -125,6 +125,22 @@ func (c *QuadraticExtensionAPI) reduceWithPowers(terms []QuadraticExtension, sca
return sum return sum
} }
func (c *QuadraticExtensionAPI) Lookup2(b0 frontend.Variable, b1 frontend.Variable, qe0, qe1, qe2, qe3 QuadraticExtension) QuadraticExtension {
var retQE QuadraticExtension
for i := 0; i < 2; i++ {
retQE[i] = c.fieldAPI.Lookup2(b0, b1, qe0[i], qe1[i], qe2[i], qe3[i]).(F)
}
return retQE
}
func (c *QuadraticExtensionAPI) AssertIsEqual(a, b QuadraticExtension) {
for i := 0; i < 2; i++ {
c.fieldAPI.AssertIsEqual(a[0], b[0])
}
}
func (c *QuadraticExtensionAPI) Println(a QuadraticExtension) { func (c *QuadraticExtensionAPI) Println(a QuadraticExtension) {
fmt.Print("Degree 0 coefficient") fmt.Print("Degree 0 coefficient")
c.fieldAPI.Println(a[0]) c.fieldAPI.Println(a[0])

Loading…
Cancel
Save