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-07-24 16:08:17 -07:00
gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks"
2023-05-19 19:49:14 -07:00
"github.com/succinctlabs/gnark-plonky2-verifier/poseidon"
2023-10-11 12:20:48 -07:00
"github.com/succinctlabs/gnark-plonky2-verifier/types"
2022-10-10 20:45:34 -07:00
)
2023-07-24 16:08:17 -07:00
type Chip struct {
2023-10-11 17:39:52 -07:00
api frontend . API ` gnark:"-" `
gl gl . Chip ` gnark:"-" `
poseidonBN254Chip * poseidon . BN254Chip
friParams * types . FriParams ` gnark:"-" `
2022-11-04 21:23:32 -07:00
}
2023-07-24 16:08:17 -07:00
func NewChip (
2022-11-21 14:49:54 -08:00
api frontend . API ,
2023-10-11 12:20:48 -07:00
friParams * types . FriParams ,
2023-07-24 16:08:17 -07:00
) * Chip {
poseidonBN254Chip := poseidon . NewBN254Chip ( api )
return & Chip {
2023-06-06 17:36:51 -07:00
api : api ,
2023-07-24 16:08:17 -07:00
poseidonBN254Chip : poseidonBN254Chip ,
2023-06-06 17:36:51 -07:00
friParams : friParams ,
2023-10-11 17:39:52 -07:00
gl : * gl . NewChip ( api ) ,
2022-11-04 21:23:32 -07:00
}
}
2023-10-11 12:20:48 -07:00
func ( f * Chip ) assertLeadingZeros ( powWitness gl . Variable , friConfig types . 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-07-24 16:08:17 -07:00
reducedPowWitness := f . gl . Reduce ( powWitness )
f . api . AssertIsLessOrEqual ( reducedPowWitness . Limb , frontend . Variable ( maxPowWitness ) )
2022-11-04 21:23:32 -07:00
}
2023-07-24 16:08:17 -07:00
func ( f * Chip ) fromOpeningsAndAlpha (
openings * Openings ,
alpha gl . QuadraticExtensionVariable ,
) [ ] gl . QuadraticExtensionVariable {
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-07-24 16:08:17 -07:00
reducedOpenings := make ( [ ] gl . QuadraticExtensionVariable , 0 , 2 )
2022-11-04 21:23:32 -07:00
for _ , batch := range openings . Batches {
2023-07-24 16:08:17 -07:00
reducedOpenings = append ( reducedOpenings , f . gl . ReduceWithPowers ( batch . Values , alpha ) )
2022-11-04 21:23:32 -07:00
}
return reducedOpenings
}
2023-07-24 16:08:17 -07:00
func ( f * Chip ) verifyMerkleProofToCapWithCapIndex (
2023-10-11 11:37:45 -07:00
leafData [ ] gl . Variable ,
2023-07-24 16:08:17 -07:00
leafIndexBits [ ] frontend . Variable ,
capIndexBits [ ] frontend . Variable ,
2023-10-11 17:39:52 -07:00
merkleCap types . FriMerkleCap ,
proof * types . FriMerkleProof ,
2023-07-24 16:08:17 -07:00
) {
currentDigest := f . poseidonBN254Chip . 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
2023-07-24 16:08:17 -07:00
leftHash := f . poseidonBN254Chip . TwoToOne ( sibling , currentDigest )
rightHash := f . poseidonBN254Chip . TwoToOne ( currentDigest , sibling )
2023-06-06 17:36:51 -07:00
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-07-24 16:08:17 -07:00
var leafLookups [ NUM_LEAF_LOOKUPS ] poseidon . BN254HashOut
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-10-11 17:39:52 -07:00
func ( f * Chip ) verifyInitialProof ( xIndexBits [ ] frontend . Variable , proof * types . FriInitialTreeProof , initialMerkleCaps [ ] types . FriMerkleCap , 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.
2023-07-24 16:08:17 -07:00
func ( f * Chip ) 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
}
}
2023-07-24 16:08:17 -07:00
func ( f * Chip ) expFromBitsConstBase (
2022-11-14 18:40:28 -08:00
base goldilocks . Element ,
exponentBits [ ] frontend . Variable ,
2023-10-11 11:37:45 -07:00
) gl . Variable {
2023-07-24 16:08:17 -07:00
product := gl . One ( )
2022-11-14 18:40:28 -08:00
for i , bit := range exponentBits {
2022-11-10 08:05:30 -08:00
// 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
2023-07-24 16:08:17 -07:00
pow := int64 ( 1 << i )
2022-11-10 08:05:30 -08:00
basePow := goldilocks . NewElement ( 0 )
basePow . Exp ( base , big . NewInt ( pow ) )
2023-07-24 16:08:17 -07:00
basePowVariable := gl . NewVariable ( basePow . Uint64 ( ) - 1 )
product = f . gl . Add (
f . gl . Mul (
f . gl . Mul (
basePowVariable ,
product ,
) ,
gl . NewVariable ( 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
}
2023-07-24 16:08:17 -07:00
func ( f * Chip ) calculateSubgroupX (
2022-11-14 18:40:28 -08:00
xIndexBits [ ] frontend . Variable ,
nLog uint64 ,
2023-10-11 11:37:45 -07:00
) gl . Variable {
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-07-24 16:08:17 -07:00
g := gl . NewVariable ( gl . MULTIPLICATIVE_GROUP_GENERATOR . Uint64 ( ) )
base := gl . PrimitiveRootOfUnity ( nLog )
2022-11-14 18:40:28 -08:00
// 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-07-24 16:08:17 -07:00
return f . gl . Mul ( g , product )
2022-11-10 12:54:57 -08:00
}
2023-07-24 16:08:17 -07:00
func ( f * Chip ) friCombineInitial (
instance InstanceInfo ,
2023-10-11 17:39:52 -07:00
proof types . FriInitialTreeProof ,
2023-07-24 16:08:17 -07:00
friAlpha gl . QuadraticExtensionVariable ,
subgroupX_QE gl . QuadraticExtensionVariable ,
precomputedReducedEval [ ] gl . QuadraticExtensionVariable ,
) gl . QuadraticExtensionVariable {
sum := gl . ZeroExtension ( )
2022-11-10 12:54:57 -08:00
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-07-24 16:08:17 -07:00
evals := make ( [ ] gl . QuadraticExtensionVariable , 0 )
2022-11-10 12:54:57 -08:00
for _ , polynomial := range batch . Polynomials {
evals = append (
evals ,
2023-07-24 16:08:17 -07:00
gl . QuadraticExtensionVariable {
proof . EvalsProofs [ polynomial . OracleIndex ] . Elements [ polynomial . PolynomialInfo ] ,
gl . Zero ( ) ,
} ,
2022-11-10 12:54:57 -08:00
)
}
2023-07-24 16:08:17 -07:00
reducedEvals := f . gl . ReduceWithPowers ( evals , friAlpha )
numerator := f . gl . SubExtensionNoReduce ( reducedEvals , reducedOpenings )
denominator := f . gl . SubExtension ( subgroupX_QE , point )
sum = f . gl . MulExtension ( f . gl . ExpExtension ( friAlpha , uint64 ( len ( evals ) ) ) , sum )
sum = f . gl . MulAddExtension (
numerator ,
f . gl . InverseExtension ( denominator ) ,
2022-11-10 12:54:57 -08:00
sum ,
)
}
2023-05-11 15:31:32 -07:00
return sum
2022-11-10 12:54:57 -08:00
}
2023-10-11 17:39:52 -07:00
func ( f * Chip ) finalPolyEval ( finalPoly types . PolynomialCoeffs , point gl . QuadraticExtensionVariable ) gl . QuadraticExtensionVariable {
2023-07-24 16:08:17 -07:00
ret := gl . ZeroExtension ( )
2022-11-10 12:54:57 -08:00
for i := len ( finalPoly . Coeffs ) - 1 ; i >= 0 ; i -- {
2023-07-24 16:08:17 -07:00
ret = f . gl . MulAddExtension ( ret , point , finalPoly . Coeffs [ i ] )
2022-11-10 12:54:57 -08:00
}
return ret
}
2023-07-24 16:08:17 -07:00
func ( f * Chip ) interpolate (
x gl . QuadraticExtensionVariable ,
xPoints [ ] gl . QuadraticExtensionVariable ,
yPoints [ ] gl . QuadraticExtensionVariable ,
barycentricWeights [ ] gl . QuadraticExtensionVariable ,
) gl . QuadraticExtensionVariable {
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" )
}
2023-07-24 16:08:17 -07:00
lX := gl . OneExtension ( )
2022-11-15 12:09:05 -08:00
for i := 0 ; i < len ( xPoints ) ; i ++ {
2023-07-24 16:08:17 -07:00
lX = f . gl . SubMulExtension ( x , xPoints [ i ] , lX )
2022-11-15 12:09:05 -08:00
}
2023-07-24 16:08:17 -07:00
sum := gl . ZeroExtension ( )
2022-11-15 12:09:05 -08:00
for i := 0 ; i < len ( xPoints ) ; i ++ {
2023-07-24 16:08:17 -07:00
sum = f . gl . AddExtension (
f . gl . MulExtension (
f . gl . DivExtension (
2022-11-15 12:09:05 -08:00
barycentricWeights [ i ] ,
2023-07-24 16:08:17 -07:00
f . gl . SubExtension (
2022-11-15 12:09:05 -08:00
x ,
xPoints [ i ] ,
) ,
) ,
yPoints [ i ] ,
) ,
sum ,
)
}
2023-07-24 16:08:17 -07:00
interpolation := f . gl . MulExtension ( lX , sum )
2022-11-15 12:09:05 -08:00
returnField := interpolation
// Now check if x is already within the xPoints
for i := 0 ; i < len ( xPoints ) ; i ++ {
2023-07-24 16:08:17 -07:00
returnField = f . gl . Lookup (
f . gl . IsZero ( f . gl . SubExtension ( x , xPoints [ i ] ) ) ,
2022-11-15 12:09:05 -08:00
returnField ,
2023-07-24 16:08:17 -07:00
yPoints [ i ] ,
2022-11-15 12:09:05 -08:00
)
}
return returnField
}
2023-07-24 16:08:17 -07:00
func ( f * Chip ) computeEvaluation (
2023-10-11 11:37:45 -07:00
x gl . Variable ,
2022-11-14 18:40:28 -08:00
xIndexWithinCosetBits [ ] frontend . Variable ,
arityBits uint64 ,
2023-07-24 16:08:17 -07:00
evals [ ] gl . QuadraticExtensionVariable ,
beta gl . QuadraticExtensionVariable ,
) gl . QuadraticExtensionVariable {
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
2023-07-24 16:08:17 -07:00
g := gl . PrimitiveRootOfUnity ( arityBits )
2022-11-14 18:40:28 -08:00
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-07-24 16:08:17 -07:00
permutedEvals := make ( [ ] gl . QuadraticExtensionVariable , 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-07-24 16:08:17 -07:00
cosetStart := f . gl . Mul ( start , x )
2022-11-14 19:03:52 -08:00
2023-07-24 16:08:17 -07:00
xPoints := make ( [ ] gl . QuadraticExtensionVariable , len ( evals ) )
2022-11-14 19:03:52 -08:00
yPoints := permutedEvals
// TODO: Make g_F a constant
2023-07-24 16:08:17 -07:00
g_F := gl . NewVariable ( g . Uint64 ( ) ) . ToQuadraticExtension ( )
xPoints [ 0 ] = gl . QuadraticExtensionVariable { cosetStart , gl . Zero ( ) }
2022-11-14 19:03:52 -08:00
for i := 1 ; i < len ( evals ) ; i ++ {
2023-07-24 16:08:17 -07:00
xPoints [ i ] = f . gl . 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-07-24 16:08:17 -07:00
barycentricWeights := make ( [ ] gl . QuadraticExtensionVariable , len ( xPoints ) )
2022-11-14 19:03:52 -08:00
for i := 0 ; i < len ( xPoints ) ; i ++ {
2023-07-24 16:08:17 -07:00
barycentricWeights [ i ] = gl . OneExtension ( )
2022-11-14 19:03:52 -08:00
for j := 0 ; j < len ( xPoints ) ; j ++ {
if i != j {
2023-07-24 16:08:17 -07:00
barycentricWeights [ i ] = f . gl . SubMulExtension (
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
2023-07-24 16:08:17 -07:00
barycentricWeights [ i ] = f . gl . 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
}
2023-07-24 16:08:17 -07:00
func ( f * Chip ) verifyQueryRound (
instance InstanceInfo ,
2023-10-11 17:39:52 -07:00
challenges * types . FriChallenges ,
2023-07-24 16:08:17 -07:00
precomputedReducedEval [ ] gl . QuadraticExtensionVariable ,
2023-10-11 17:39:52 -07:00
initialMerkleCaps [ ] types . FriMerkleCap ,
proof * types . FriProof ,
2023-10-11 11:37:45 -07:00
xIndex gl . Variable ,
2022-11-10 12:54:57 -08:00
n uint64 ,
nLog uint64 ,
2023-10-11 17:39:52 -07:00
roundProof * types . FriQueryRound ,
2022-11-10 12:54:57 -08:00
) {
f . assertNoncanonicalIndicesOK ( )
2023-07-24 16:08:17 -07:00
xIndex = f . gl . Reduce ( xIndex )
xIndexBits := f . api . ToBinary ( xIndex . Limb , 64 ) [ 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-07-24 16:08:17 -07:00
subgroupX_QE := subgroupX . ToQuadraticExtension ( )
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-07-24 16:08:17 -07:00
var leafLookups [ NUM_LEAF_LOOKUPS ] gl . QuadraticExtensionVariable
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 ++ {
2023-07-24 16:08:17 -07:00
leafLookups [ i ] = f . gl . 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
2023-07-24 16:08:17 -07:00
newEval := f . gl . Lookup2 (
2022-11-15 17:50:45 -08:00
xIndexWithinCosetBits [ 2 ] ,
xIndexWithinCosetBits [ 3 ] ,
leafLookups [ 0 ] ,
leafLookups [ 1 ] ,
leafLookups [ 2 ] ,
leafLookups [ 3 ] ,
)
2023-07-24 16:08:17 -07:00
f . gl . AssertIsEqual ( newEval [ 0 ] , oldEval [ 0 ] )
f . gl . AssertIsEqual ( newEval [ 1 ] , oldEval [ 1 ] )
2022-11-14 18:40:28 -08:00
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-10-11 11:37:45 -07:00
fieldEvals := make ( [ ] gl . Variable , 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-07-24 16:08:17 -07:00
subgroupX = f . gl . Mul ( subgroupX , subgroupX )
2022-11-14 18:40:28 -08:00
}
xIndexBits = cosetIndexBits
}
2023-07-24 16:08:17 -07:00
subgroupX_QE = subgroupX . ToQuadraticExtension ( )
2022-11-10 12:54:57 -08:00
finalPolyEval := f . finalPolyEval ( proof . FinalPoly , subgroupX_QE )
2023-07-24 16:08:17 -07:00
f . gl . AssertIsEqual ( oldEval [ 0 ] , finalPolyEval [ 0 ] )
f . gl . AssertIsEqual ( oldEval [ 1 ] , finalPolyEval [ 1 ] )
2022-11-04 21:23:32 -07:00
}
2023-07-24 16:08:17 -07:00
func ( f * Chip ) VerifyFriProof (
instance InstanceInfo ,
openings Openings ,
2023-10-11 17:39:52 -07:00
friChallenges * types . FriChallenges ,
initialMerkleCaps [ ] types . FriMerkleCap ,
friProof * types . 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
2023-07-24 16:08:17 -07:00
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 ,
)
}
}