2023-05-19 19:49:14 -07:00
package fri
2022-10-10 20:45:34 -07:00
import (
2022-11-08 14:52:22 -08:00
"fmt"
2022-11-04 21:23:32 -07:00
"math"
2022-11-10 08:05:30 -08:00
"math/big"
2022-11-14 18:40:28 -08:00
"math/bits"
2022-11-04 21:23:32 -07:00
2022-11-10 08:05:30 -08:00
"github.com/consensys/gnark-crypto/field/goldilocks"
2022-11-04 21:23:32 -07:00
"github.com/consensys/gnark/frontend"
2023-05-19 19:49:14 -07:00
"github.com/succinctlabs/gnark-plonky2-verifier/field"
"github.com/succinctlabs/gnark-plonky2-verifier/poseidon"
"github.com/succinctlabs/gnark-plonky2-verifier/verifier/common"
2022-10-10 20:45:34 -07:00
)
2022-11-04 21:23:32 -07:00
type FriChip struct {
2023-05-19 19:49:14 -07:00
api frontend . API ` gnark:"-" `
2023-05-25 07:39:06 -07:00
fieldAPI field . FieldAPI ` gnark:"-" `
2023-05-19 19:49:14 -07:00
qeAPI * field . QuadraticExtensionAPI ` gnark:"-" `
2022-11-04 21:23:32 -07:00
2023-06-06 17:36:51 -07:00
poseidonBN128Chip * poseidon . PoseidonBN128Chip
2022-11-07 18:33:06 -08:00
2023-05-19 19:49:14 -07:00
friParams * common . FriParams ` gnark:"-" `
2022-11-04 21:23:32 -07:00
}
2022-11-21 14:49:54 -08:00
func NewFriChip (
api frontend . API ,
2023-05-25 07:39:06 -07:00
fieldAPI field . FieldAPI ,
2023-05-19 19:49:14 -07:00
qeAPI * field . QuadraticExtensionAPI ,
2023-06-06 17:36:51 -07:00
poseidonBN128Chip * poseidon . PoseidonBN128Chip ,
2023-05-19 19:49:14 -07:00
friParams * common . FriParams ,
2022-11-21 14:49:54 -08:00
) * FriChip {
2022-11-04 21:23:32 -07:00
return & FriChip {
2023-06-06 17:36:51 -07:00
api : api ,
fieldAPI : fieldAPI ,
qeAPI : qeAPI ,
poseidonBN128Chip : poseidonBN128Chip ,
friParams : friParams ,
2022-11-04 21:23:32 -07:00
}
}
2023-05-19 19:49:14 -07:00
func ( f * FriChip ) assertLeadingZeros ( powWitness field . F , friConfig common . FriConfig ) {
2022-11-04 21:23:32 -07:00
// Asserts that powWitness'es big-endian bit representation has at least `leading_zeros` leading zeros.
// Note that this is assuming that the Goldilocks field is being used. Specfically that the
// field is 64 bits long
maxPowWitness := uint64 ( math . Pow ( 2 , float64 ( 64 - friConfig . ProofOfWorkBits ) ) ) - 1
2023-05-25 07:39:06 -07:00
reducedPOWWitness := f . fieldAPI . Reduce ( powWitness )
f . fieldAPI . AssertIsLessOrEqual ( reducedPOWWitness , field . NewFieldConst ( maxPowWitness ) )
2022-11-04 21:23:32 -07:00
}
2023-05-19 19:49:14 -07:00
func ( f * FriChip ) fromOpeningsAndAlpha ( openings * FriOpenings , alpha field . QuadraticExtension ) [ ] field . QuadraticExtension {
2022-11-04 21:23:32 -07:00
// One reduced opening for all openings evaluated at point Zeta.
// Another one for all openings evaluated at point Zeta * Omega (which is only PlonkZsNext polynomial)
2023-05-19 19:49:14 -07:00
reducedOpenings := make ( [ ] field . QuadraticExtension , 0 , 2 )
2022-11-04 21:23:32 -07:00
for _ , batch := range openings . Batches {
2022-11-17 16:23:14 -08:00
reducedOpenings = append ( reducedOpenings , f . qeAPI . ReduceWithPowers ( batch . Values , alpha ) )
2022-11-04 21:23:32 -07:00
}
return reducedOpenings
}
2023-05-19 19:49:14 -07:00
func ( f * FriChip ) verifyMerkleProofToCapWithCapIndex ( leafData [ ] field . F , leafIndexBits [ ] frontend . Variable , capIndexBits [ ] frontend . Variable , merkleCap common . MerkleCap , proof * common . MerkleProof ) {
2023-06-06 17:36:51 -07:00
currentDigest := f . poseidonBN128Chip . HashOrNoop ( leafData )
2022-11-08 17:54:01 -08:00
for i , sibling := range proof . Siblings {
bit := leafIndexBits [ i ]
2023-06-06 17:36:51 -07:00
// TODO: Don't need to do two hashes by using a trick that the plonky2 verifier circuit does
// https://github.com/mir-protocol/plonky2/blob/973624f12d2d12d74422b3ea051358b9eaacb050/plonky2/src/gates/poseidon.rs#L298
leftHash := f . poseidonBN128Chip . TwoToOne ( sibling , currentDigest )
rightHash := f . poseidonBN128Chip . TwoToOne ( currentDigest , sibling )
currentDigest = f . api . Select ( bit , leftHash , rightHash )
2022-11-07 18:33:06 -08:00
}
2022-11-09 10:54:27 -08:00
// We assume that the cap_height is 4. Create two levels of the Lookup2 circuit
if len ( capIndexBits ) != 4 || len ( merkleCap ) != 16 {
2022-11-17 16:23:14 -08:00
errorMsg , _ := fmt . Printf (
"capIndexBits length should be 4 and the merkleCap length should be 16. Actual values (capIndexBits: %d, merkleCap: %d)\n" ,
len ( capIndexBits ) ,
len ( merkleCap ) ,
)
panic ( errorMsg )
2022-11-09 10:54:27 -08:00
}
const NUM_LEAF_LOOKUPS = 4
2023-06-06 17:36:51 -07:00
var leafLookups [ NUM_LEAF_LOOKUPS ] poseidon . PoseidonBN128HashOut
2022-11-09 10:54:27 -08:00
// First create the "leaf" lookup2 circuits
// The will use the least significant bits of the capIndexBits array
for i := 0 ; i < NUM_LEAF_LOOKUPS ; i ++ {
2023-06-06 17:36:51 -07:00
leafLookups [ i ] = f . api . Lookup2 (
2022-11-21 14:49:54 -08:00
capIndexBits [ 0 ] , capIndexBits [ 1 ] ,
2022-11-09 10:54:27 -08:00
merkleCap [ i * NUM_LEAF_LOOKUPS ] , merkleCap [ i * NUM_LEAF_LOOKUPS + 1 ] , merkleCap [ i * NUM_LEAF_LOOKUPS + 2 ] , merkleCap [ i * NUM_LEAF_LOOKUPS + 3 ] ,
)
}
// Use the most 2 significant bits of the capIndexBits array for the "root" lookup
2023-06-06 17:36:51 -07:00
merkleCapEntry := f . api . Lookup2 ( capIndexBits [ 2 ] , capIndexBits [ 3 ] , leafLookups [ 0 ] , leafLookups [ 1 ] , leafLookups [ 2 ] , leafLookups [ 3 ] )
f . api . AssertIsEqual ( currentDigest , merkleCapEntry )
2022-11-07 18:33:06 -08:00
}
2023-05-19 19:49:14 -07:00
func ( f * FriChip ) verifyInitialProof ( xIndexBits [ ] frontend . Variable , proof * common . FriInitialTreeProof , initialMerkleCaps [ ] common . MerkleCap , capIndexBits [ ] frontend . Variable ) {
2022-11-04 21:23:32 -07:00
if len ( proof . EvalsProofs ) != len ( initialMerkleCaps ) {
panic ( "length of eval proofs in fri proof should equal length of initial merkle caps" )
}
for i := 0 ; i < len ( initialMerkleCaps ) ; i ++ {
evals := proof . EvalsProofs [ i ] . Elements
merkleProof := proof . EvalsProofs [ i ] . MerkleProof
cap := initialMerkleCaps [ i ]
2022-11-09 10:54:27 -08:00
f . verifyMerkleProofToCapWithCapIndex ( evals , xIndexBits , capIndexBits , cap , & merkleProof )
2022-11-07 18:33:06 -08:00
}
}
// / We decompose FRI query indices into bits without verifying that the decomposition given by
// / the prover is the canonical one. In particular, if `x_index < 2^field_bits - p`, then the
// / prover could supply the binary encoding of either `x_index` or `x_index + p`, since they are
// / congruent mod `p`. However, this only occurs with probability
// / p_ambiguous = (2^field_bits - p) / p
// / which is small for the field that we use in practice.
// /
// / In particular, the soundness error of one FRI query is roughly the codeword rate, which
// / is much larger than this ambiguous-element probability given any reasonable parameters.
// / Thus ambiguous elements contribute a negligible amount to soundness error.
// /
// / Here we compare the probabilities as a sanity check, to verify the claim above.
func ( f * FriChip ) assertNoncanonicalIndicesOK ( ) {
2022-11-10 08:05:30 -08:00
numAmbiguousElems := uint64 ( math . MaxUint64 ) - goldilocks . Modulus ( ) . Uint64 ( ) + 1
2023-05-19 19:49:14 -07:00
queryError := f . friParams . Config . Rate ( )
2022-11-10 08:05:30 -08:00
pAmbiguous := float64 ( numAmbiguousElems ) / float64 ( goldilocks . Modulus ( ) . Uint64 ( ) )
2022-11-08 14:52:22 -08:00
// TODO: Check that pAmbiguous value is the same as the one in plonky2 verifier
if pAmbiguous >= queryError * 1e-5 {
2022-11-07 18:33:06 -08:00
panic ( "A non-negligible portion of field elements are in the range that permits non-canonical encodings. Need to do more analysis or enforce canonical encodings." )
2022-11-04 21:23:32 -07:00
}
}
2022-11-14 18:40:28 -08:00
func ( f * FriChip ) expFromBitsConstBase (
base goldilocks . Element ,
exponentBits [ ] frontend . Variable ,
2023-05-19 19:49:14 -07:00
) field . F {
product := field . ONE_F
2022-11-14 18:40:28 -08:00
for i , bit := range exponentBits {
2022-11-10 08:05:30 -08:00
pow := int64 ( 1 << i )
// If the bit is on, we multiply product by base^pow.
// We can arithmetize this as:
// product *= 1 + bit (base^pow - 1)
// product = (base^pow - 1) product bit + product
basePow := goldilocks . NewElement ( 0 )
basePow . Exp ( base , big . NewInt ( pow ) )
2023-05-25 07:39:06 -07:00
basePowElement := field . NewFieldConst ( basePow . Uint64 ( ) - 1 )
2022-11-10 08:05:30 -08:00
product = f . fieldAPI . Add (
f . fieldAPI . Mul (
2023-05-25 07:39:06 -07:00
f . fieldAPI . Mul (
basePowElement ,
product ) ,
f . fieldAPI . NewElement ( bit ) ) ,
2022-11-10 08:05:30 -08:00
product ,
2023-05-25 07:39:06 -07:00
)
2022-11-10 08:05:30 -08:00
}
2022-11-14 18:40:28 -08:00
return product
}
func ( f * FriChip ) calculateSubgroupX (
xIndexBits [ ] frontend . Variable ,
nLog uint64 ,
2023-05-19 19:49:14 -07:00
) field . F {
2022-11-14 18:40:28 -08:00
// 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
2023-05-25 07:39:06 -07:00
g := field . NewFieldConst ( field . GOLDILOCKS_MULTIPLICATIVE_GROUP_GENERATOR . Uint64 ( ) )
2022-11-14 18:40:28 -08:00
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 )
2023-05-25 07:39:06 -07:00
return f . fieldAPI . Mul ( g , product )
2022-11-10 12:54:57 -08:00
}
func ( f * FriChip ) friCombineInitial (
instance FriInstanceInfo ,
2023-05-19 19:49:14 -07:00
proof common . FriInitialTreeProof ,
friAlpha field . QuadraticExtension ,
subgroupX_QE field . QuadraticExtension ,
precomputedReducedEval [ ] field . QuadraticExtension ,
) field . QuadraticExtension {
2022-11-10 12:54:57 -08:00
sum := f . qeAPI . ZERO_QE
if len ( instance . Batches ) != len ( precomputedReducedEval ) {
panic ( "len(openings) != len(precomputedReducedEval)" )
}
for i := 0 ; i < len ( instance . Batches ) ; i ++ {
batch := instance . Batches [ i ]
reducedOpenings := precomputedReducedEval [ i ]
point := batch . Point
2023-05-19 19:49:14 -07:00
evals := make ( [ ] field . QuadraticExtension , 0 )
2022-11-10 12:54:57 -08:00
for _ , polynomial := range batch . Polynomials {
evals = append (
evals ,
2023-05-19 19:49:14 -07:00
field . QuadraticExtension { proof . EvalsProofs [ polynomial . OracleIndex ] . Elements [ polynomial . PolynomialInfo ] , field . ZERO_F } ,
2022-11-10 12:54:57 -08:00
)
}
2022-11-14 18:40:28 -08:00
reducedEvals := f . qeAPI . ReduceWithPowers ( evals , friAlpha )
2022-11-10 12:54:57 -08:00
numerator := f . qeAPI . SubExtension ( reducedEvals , reducedOpenings )
denominator := f . qeAPI . SubExtension ( subgroupX_QE , point )
sum = f . qeAPI . MulExtension ( f . qeAPI . ExpU64Extension ( friAlpha , uint64 ( len ( evals ) ) ) , sum )
sum = f . qeAPI . AddExtension (
f . qeAPI . DivExtension (
numerator ,
denominator ,
) ,
sum ,
)
}
2023-05-11 15:31:32 -07:00
return sum
2022-11-10 12:54:57 -08:00
}
2023-05-19 19:49:14 -07:00
func ( f * FriChip ) finalPolyEval ( finalPoly common . PolynomialCoeffs , point field . QuadraticExtension ) field . QuadraticExtension {
2022-11-10 12:54:57 -08:00
ret := f . qeAPI . ZERO_QE
for i := len ( finalPoly . Coeffs ) - 1 ; i >= 0 ; i -- {
ret = f . qeAPI . AddExtension (
f . qeAPI . MulExtension (
ret ,
point ,
) ,
finalPoly . Coeffs [ i ] ,
)
}
return ret
}
2023-05-19 19:49:14 -07:00
func ( f * FriChip ) interpolate ( x field . QuadraticExtension , xPoints [ ] field . QuadraticExtension , yPoints [ ] field . QuadraticExtension , barycentricWeights [ ] field . QuadraticExtension ) field . QuadraticExtension {
2022-11-15 12:09:05 -08:00
if len ( xPoints ) != len ( yPoints ) || len ( xPoints ) != len ( barycentricWeights ) {
panic ( "length of xPoints, yPoints, and barycentricWeights are inconsistent" )
}
lX := f . qeAPI . ONE_QE
for i := 0 ; i < len ( xPoints ) ; i ++ {
lX = f . qeAPI . MulExtension (
lX ,
f . qeAPI . SubExtension (
x ,
xPoints [ i ] ,
) ,
)
}
sum := f . qeAPI . ZERO_QE
for i := 0 ; i < len ( xPoints ) ; i ++ {
sum = f . qeAPI . AddExtension (
f . qeAPI . MulExtension (
f . qeAPI . DivExtension (
barycentricWeights [ i ] ,
f . qeAPI . SubExtension (
x ,
xPoints [ i ] ,
) ,
) ,
yPoints [ i ] ,
) ,
sum ,
)
}
interpolation := f . qeAPI . MulExtension ( lX , sum )
returnField := interpolation
// Now check if x is already within the xPoints
for i := 0 ; i < len ( xPoints ) ; i ++ {
returnField = f . qeAPI . Select (
f . qeAPI . IsZero ( f . qeAPI . SubExtension ( x , xPoints [ i ] ) ) ,
yPoints [ i ] ,
returnField ,
)
}
return returnField
}
2022-11-14 18:40:28 -08:00
func ( f * FriChip ) computeEvaluation (
2023-05-19 19:49:14 -07:00
x field . F ,
2022-11-14 18:40:28 -08:00
xIndexWithinCosetBits [ ] frontend . Variable ,
arityBits uint64 ,
2023-05-19 19:49:14 -07:00
evals [ ] field . QuadraticExtension ,
beta field . QuadraticExtension ,
) field . QuadraticExtension {
2022-11-14 18:40:28 -08:00
arity := 1 << arityBits
if ( len ( evals ) ) != arity {
panic ( "len(evals) ! arity" )
}
2022-11-15 17:50:45 -08:00
if arityBits > 8 {
panic ( "currently assuming that arityBits is <= 8" )
}
2022-11-14 18:40:28 -08:00
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.
2023-05-19 19:49:14 -07:00
permutedEvals := make ( [ ] field . QuadraticExtension , len ( evals ) )
2022-11-15 17:50:45 -08:00
for i := uint8 ( 0 ) ; i < uint8 ( len ( evals ) ) ; i ++ {
newIndex := bits . Reverse8 ( i ) >> arityBits
2022-11-14 18:40:28 -08:00
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 )
2023-05-25 07:39:06 -07:00
cosetStart := f . fieldAPI . Mul ( start , x )
2022-11-14 19:03:52 -08:00
2023-05-19 19:49:14 -07:00
xPoints := make ( [ ] field . QuadraticExtension , len ( evals ) )
2022-11-14 19:03:52 -08:00
yPoints := permutedEvals
// TODO: Make g_F a constant
2023-05-25 07:39:06 -07:00
g_F := f . qeAPI . FieldToQE ( field . NewFieldConst ( g . Uint64 ( ) ) )
2022-11-15 12:09:05 -08:00
xPoints [ 0 ] = f . qeAPI . FieldToQE ( cosetStart )
2022-11-14 19:03:52 -08:00
for i := 1 ; i < len ( evals ) ; i ++ {
2022-11-15 17:50:45 -08:00
xPoints [ i ] = f . qeAPI . MulExtension ( xPoints [ i - 1 ] , g_F )
2022-11-14 19:03:52 -08:00
}
// TODO: This is n^2. Is there a way to do this better?
// Compute the barycentric weights
2023-05-19 19:49:14 -07:00
barycentricWeights := make ( [ ] field . QuadraticExtension , len ( xPoints ) )
2022-11-14 19:03:52 -08:00
for i := 0 ; i < len ( xPoints ) ; i ++ {
2022-11-15 12:09:05 -08:00
barycentricWeights [ i ] = f . qeAPI . ONE_QE
2022-11-14 19:03:52 -08:00
for j := 0 ; j < len ( xPoints ) ; j ++ {
if i != j {
2022-11-15 12:09:05 -08:00
barycentricWeights [ i ] = f . qeAPI . MulExtension (
f . qeAPI . SubExtension ( xPoints [ i ] , xPoints [ j ] ) ,
2022-11-14 19:03:52 -08:00
barycentricWeights [ i ] ,
2022-11-15 12:09:05 -08:00
)
2022-11-14 19:03:52 -08:00
}
}
// Take the inverse of the barycentric weights
// TODO: Can provide a witness to this value
2022-11-15 12:09:05 -08:00
barycentricWeights [ i ] = f . qeAPI . InverseExtension ( barycentricWeights [ i ] )
2022-11-14 19:03:52 -08:00
}
2022-11-15 12:09:05 -08:00
return f . interpolate ( beta , xPoints , yPoints , barycentricWeights )
2022-11-14 18:40:28 -08:00
}
2022-11-10 12:54:57 -08:00
func ( f * FriChip ) verifyQueryRound (
instance FriInstanceInfo ,
2023-05-19 19:49:14 -07:00
challenges * common . FriChallenges ,
precomputedReducedEval [ ] field . QuadraticExtension ,
initialMerkleCaps [ ] common . MerkleCap ,
proof * common . FriProof ,
xIndex field . F ,
2022-11-10 12:54:57 -08:00
n uint64 ,
nLog uint64 ,
2023-05-19 19:49:14 -07:00
roundProof * common . FriQueryRound ,
2022-11-10 12:54:57 -08:00
) {
f . assertNoncanonicalIndicesOK ( )
2023-06-06 17:36:51 -07:00
xIndex = f . fieldAPI . Reduce ( xIndex )
xIndexBits := f . fieldAPI . ToBits ( xIndex ) [ 0 : f . friParams . DegreeBits + f . friParams . Config . RateBits ]
2022-11-10 12:54:57 -08:00
capIndexBits := xIndexBits [ len ( xIndexBits ) - int ( f . friParams . Config . CapHeight ) : ]
f . verifyInitialProof ( xIndexBits , & roundProof . InitialTreesProof , initialMerkleCaps , capIndexBits )
subgroupX := f . calculateSubgroupX (
xIndexBits ,
nLog ,
)
2023-05-19 19:49:14 -07:00
subgroupX_QE := field . QuadraticExtension { subgroupX , field . ZERO_F }
2022-11-10 12:54:57 -08:00
oldEval := f . friCombineInitial (
instance ,
roundProof . InitialTreesProof ,
challenges . FriAlpha ,
subgroupX_QE ,
precomputedReducedEval ,
)
2022-11-14 18:40:28 -08:00
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
2023-05-19 19:49:14 -07:00
var leafLookups [ NUM_LEAF_LOOKUPS ] field . QuadraticExtension
2022-11-14 18:40:28 -08:00
// 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 (
2022-11-15 17:50:45 -08:00
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 ] ,
2022-11-14 18:40:28 -08:00
)
}
// Use the most 2 significant bits of the xIndexWithCosetBits array for the "root" lookup
2022-11-15 17:50:45 -08:00
newEval := f . qeAPI . Lookup2 (
xIndexWithinCosetBits [ 2 ] ,
xIndexWithinCosetBits [ 3 ] ,
leafLookups [ 0 ] ,
leafLookups [ 1 ] ,
leafLookups [ 2 ] ,
leafLookups [ 3 ] ,
)
2022-11-14 18:40:28 -08:00
f . qeAPI . AssertIsEqual ( newEval , oldEval )
2022-11-15 12:09:05 -08:00
oldEval = f . computeEvaluation (
2022-11-14 18:40:28 -08:00
subgroupX ,
xIndexWithinCosetBits ,
arityBits ,
evals ,
challenges . FriBetas [ i ] ,
)
// Convert evals (array of QE) to fields by taking their 0th degree coefficients
2023-05-19 19:49:14 -07:00
fieldEvals := make ( [ ] field . F , 0 , 2 * len ( evals ) )
2022-11-14 18:40:28 -08:00
for j := 0 ; j < len ( evals ) ; j ++ {
2022-11-15 17:50:45 -08:00
fieldEvals = append ( fieldEvals , evals [ j ] [ 0 ] )
fieldEvals = append ( fieldEvals , evals [ j ] [ 1 ] )
2022-11-14 18:40:28 -08:00
}
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 ++ {
2023-05-25 07:39:06 -07:00
subgroupX = f . fieldAPI . Mul ( subgroupX , subgroupX )
2022-11-14 18:40:28 -08:00
}
xIndexBits = cosetIndexBits
}
2022-11-15 17:50:45 -08:00
subgroupX_QE = f . qeAPI . FieldToQE ( subgroupX )
2022-11-10 12:54:57 -08:00
finalPolyEval := f . finalPolyEval ( proof . FinalPoly , subgroupX_QE )
2022-11-14 18:40:28 -08:00
f . qeAPI . AssertIsEqual ( oldEval , finalPolyEval )
2022-11-04 21:23:32 -07:00
}
func ( f * FriChip ) VerifyFriProof (
2022-11-10 12:54:57 -08:00
instance FriInstanceInfo ,
2022-11-07 18:33:06 -08:00
openings FriOpenings ,
2023-05-19 19:49:14 -07:00
friChallenges * common . FriChallenges ,
initialMerkleCaps [ ] common . MerkleCap ,
friProof * common . FriProof ,
2022-11-04 21:23:32 -07:00
) {
// TODO: Check fri config
/ * if let Some ( max_arity_bits ) = params . max_arity_bits ( ) {
self . check_recursion_config : : < C > ( max_arity_bits ) ;
}
debug_assert_eq ! (
params . final_poly_len ( ) ,
proof . final_poly . len ( ) ,
"Final polynomial has wrong degree."
) ; * /
// Check POW
2022-11-07 18:33:06 -08:00
f . assertLeadingZeros ( friChallenges . FriPowResponse , f . friParams . Config )
2022-11-04 21:23:32 -07:00
2022-11-07 18:33:06 -08:00
precomputedReducedEvals := f . fromOpeningsAndAlpha ( & openings , friChallenges . FriAlpha )
2022-11-04 21:23:32 -07:00
// Size of the LDE domain.
2022-11-08 14:52:22 -08:00
nLog := f . friParams . DegreeBits + f . friParams . Config . RateBits
n := uint64 ( math . Pow ( 2 , float64 ( nLog ) ) )
2023-04-26 16:05:23 -07:00
if len ( friChallenges . FriQueryIndices ) != len ( friProof . QueryRoundProofs ) {
2022-11-08 14:52:22 -08:00
panic ( fmt . Sprintf (
"Number of query indices (%d) should equal number of query round proofs (%d)" ,
2023-04-26 16:05:23 -07:00
len ( friChallenges . FriQueryIndices ) ,
2022-11-08 14:52:22 -08:00
len ( friProof . QueryRoundProofs ) ,
) )
2022-11-04 21:23:32 -07:00
}
2023-04-26 16:05:23 -07:00
for idx , xIndex := range friChallenges . FriQueryIndices {
2022-11-04 21:23:32 -07:00
roundProof := friProof . QueryRoundProofs [ idx ]
f . verifyQueryRound (
2022-11-10 12:54:57 -08:00
instance ,
2022-11-04 21:23:32 -07:00
friChallenges ,
precomputedReducedEvals ,
initialMerkleCaps ,
friProof ,
xIndex ,
n ,
2022-11-08 14:52:22 -08:00
nLog ,
2022-11-04 21:23:32 -07:00
& roundProof ,
)
}
}