mirror of
https://github.com/logos-storage/gnark-plonky2-verifier.git
synced 2026-01-07 15:43:06 +00:00
expanded fri_test.go and implemented more for the fri verifier
This commit is contained in:
parent
bfcad86ce3
commit
da80afaac4
@ -1,6 +1,8 @@
|
|||||||
package field
|
package field
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/consensys/gnark-crypto/ecc"
|
"github.com/consensys/gnark-crypto/ecc"
|
||||||
"github.com/consensys/gnark/frontend"
|
"github.com/consensys/gnark/frontend"
|
||||||
"github.com/consensys/gnark/std/math/emulated"
|
"github.com/consensys/gnark/std/math/emulated"
|
||||||
@ -28,3 +30,9 @@ func NewFieldAPI(api frontend.API) frontend.API {
|
|||||||
}
|
}
|
||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var r EmulatedField
|
||||||
|
|
||||||
|
func EmulatedFieldModulus() *big.Int {
|
||||||
|
return r.Modulus()
|
||||||
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
package plonky2_verifier
|
package plonky2_verifier
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"gnark-ed25519/field"
|
"gnark-ed25519/field"
|
||||||
. "gnark-ed25519/field"
|
. "gnark-ed25519/field"
|
||||||
|
"gnark-ed25519/poseidon"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/consensys/gnark/frontend"
|
"github.com/consensys/gnark/frontend"
|
||||||
@ -34,16 +34,19 @@ type FriChip struct {
|
|||||||
field frontend.API
|
field frontend.API
|
||||||
qe *QuadraticExtensionAPI
|
qe *QuadraticExtensionAPI
|
||||||
|
|
||||||
|
poseidonChip *poseidon.PoseidonChip
|
||||||
|
|
||||||
friParams *FriParams
|
friParams *FriParams
|
||||||
verifierOnlyCircuitData *VerifierOnlyCircuitData
|
verifierOnlyCircuitData *VerifierOnlyCircuitData
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFriChip(api frontend.API, field frontend.API, qe *QuadraticExtensionAPI, friParams *FriParams) *FriChip {
|
func NewFriChip(api frontend.API, field frontend.API, qe *QuadraticExtensionAPI, poseidonChip *poseidon.PoseidonChip, friParams *FriParams) *FriChip {
|
||||||
return &FriChip{
|
return &FriChip{
|
||||||
api: api,
|
api: api,
|
||||||
field: field,
|
field: field,
|
||||||
qe: qe,
|
qe: qe,
|
||||||
friParams: friParams,
|
poseidonChip: poseidonChip,
|
||||||
|
friParams: friParams,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,9 +55,6 @@ func (f *FriChip) assertLeadingZeros(powWitness F, friConfig FriConfig) {
|
|||||||
// Note that this is assuming that the Goldilocks field is being used. Specfically that the
|
// Note that this is assuming that the Goldilocks field is being used. Specfically that the
|
||||||
// field is 64 bits long
|
// field is 64 bits long
|
||||||
maxPowWitness := uint64(math.Pow(2, float64(64-friConfig.ProofOfWorkBits))) - 1
|
maxPowWitness := uint64(math.Pow(2, float64(64-friConfig.ProofOfWorkBits))) - 1
|
||||||
f.field.Println(powWitness)
|
|
||||||
fmt.Println(maxPowWitness)
|
|
||||||
fmt.Println(friConfig.ProofOfWorkBits)
|
|
||||||
f.field.AssertIsLessOrEqual(powWitness, field.NewFieldElement(maxPowWitness))
|
f.field.AssertIsLessOrEqual(powWitness, field.NewFieldElement(maxPowWitness))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,10 +70,63 @@ func (f *FriChip) fromOpeningsAndAlpha(openings *FriOpenings, alpha QuadraticExt
|
|||||||
return reducedOpenings
|
return reducedOpenings
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FriChip) verifyMerkleProofToCap(leafData []F, leafIndex F, merkleCap MerkleCap, proof *MerkleProof) {
|
func (f *FriChip) hashOrNoop(data []F) Hash {
|
||||||
|
var elements Hash
|
||||||
|
if len(data) <= 4 {
|
||||||
|
// Pad the data to have a size of 4
|
||||||
|
for i, inputElement := range data {
|
||||||
|
elements[i] = inputElement
|
||||||
|
}
|
||||||
|
for i := len(data); i < 4; i++ {
|
||||||
|
elements[i] = f.qe.ZERO_F
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements
|
||||||
|
} else {
|
||||||
|
hashOutput := f.poseidonChip.HashNToMNoPad(data, 4)
|
||||||
|
|
||||||
|
if len(hashOutput) != len(elements) {
|
||||||
|
panic("The length of hashOutput and elements is different")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, hashField := range hashOutput {
|
||||||
|
elements[i] = hashField
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FriChip) verifyInitialProof(xIndex F, proof *FriInitialTreeProof, initialMerkleCaps []MerkleCap) {
|
func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits []frontend.Variable, capIndex F, merkleCap MerkleCap, proof *MerkleProof) {
|
||||||
|
currentDigest := f.hashOrNoop(leafData)
|
||||||
|
|
||||||
|
if len(leafIndexBits) != len(proof.Siblings) {
|
||||||
|
panic("len(leafIndexBits) != len(proof.Siblings)")
|
||||||
|
}
|
||||||
|
|
||||||
|
fourZeros := [4]F{f.qe.ZERO_F, f.qe.ZERO_F, f.qe.ZERO_F, f.qe.ZERO_F}
|
||||||
|
for i, bit := range leafIndexBits {
|
||||||
|
sibling := proof.Siblings[i]
|
||||||
|
|
||||||
|
var leftSiblingState poseidon.PoseidonState
|
||||||
|
copy(leftSiblingState[0:4], sibling[0:4])
|
||||||
|
copy(leftSiblingState[4:8], currentDigest[0:4])
|
||||||
|
copy(leftSiblingState[8:12], fourZeros[0:4])
|
||||||
|
leftHash := f.poseidonChip.Poseidon(leftSiblingState)
|
||||||
|
leftHashCompress := leftHash[0:4]
|
||||||
|
|
||||||
|
var rightSiblingState poseidon.PoseidonState
|
||||||
|
copy(rightSiblingState[0:4], currentDigest[0:4])
|
||||||
|
copy(rightSiblingState[4:8], sibling[0:4])
|
||||||
|
copy(rightSiblingState[8:12], fourZeros[0:4])
|
||||||
|
rightHash := f.poseidonChip.Poseidon(rightSiblingState)
|
||||||
|
rightHashCompress := rightHash[0:4]
|
||||||
|
|
||||||
|
currentDigest = f.api.Select(bit, leftHashCompress, rightHashCompress).(Hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FriChip) verifyInitialProof(xIndexBits []frontend.Variable, proof *FriInitialTreeProof, initialMerkleCaps []MerkleCap, capIndex F) {
|
||||||
if len(proof.EvalsProofs) != len(initialMerkleCaps) {
|
if len(proof.EvalsProofs) != len(initialMerkleCaps) {
|
||||||
panic("length of eval proofs in fri proof should equal length of initial merkle caps")
|
panic("length of eval proofs in fri proof should equal length of initial merkle caps")
|
||||||
}
|
}
|
||||||
@ -82,7 +135,28 @@ func (f *FriChip) verifyInitialProof(xIndex F, proof *FriInitialTreeProof, initi
|
|||||||
evals := proof.EvalsProofs[i].Elements
|
evals := proof.EvalsProofs[i].Elements
|
||||||
merkleProof := proof.EvalsProofs[i].MerkleProof
|
merkleProof := proof.EvalsProofs[i].MerkleProof
|
||||||
cap := initialMerkleCaps[i]
|
cap := initialMerkleCaps[i]
|
||||||
f.verifyMerkleProofToCap(evals, xIndex, cap, &merkleProof)
|
f.verifyMerkleProofToCapWithCapIndex(evals, xIndexBits, capIndex, cap, &merkleProof)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// / 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() {
|
||||||
|
numAmbiguousElems := uint64(math.MaxUint64) - EmulatedFieldModulus().Uint64() + 1
|
||||||
|
queryError := f.friParams.Config.rate()
|
||||||
|
pAmbiguous := float64(numAmbiguousElems) / float64(EmulatedFieldModulus().Uint64())
|
||||||
|
if pAmbiguous < queryError*1e-5 {
|
||||||
|
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.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,11 +169,17 @@ func (f *FriChip) verifyQueryRound(
|
|||||||
n uint64,
|
n uint64,
|
||||||
roundProof *FriQueryRound,
|
roundProof *FriQueryRound,
|
||||||
) {
|
) {
|
||||||
f.verifyInitialProof(xIndex, &roundProof.InitialTreesProof, initialMerkleCaps)
|
nLog := log2Strict(uint(n))
|
||||||
|
|
||||||
|
f.assertNoncanonicalIndicesOK()
|
||||||
|
xIndexBits := f.qe.field.ToBinary(xIndex, nLog)
|
||||||
|
capIndex := f.qe.field.FromBinary(xIndexBits[len(xIndexBits)-int(f.friParams.Config.CapHeight):]...).(F)
|
||||||
|
|
||||||
|
f.verifyInitialProof(xIndexBits, &roundProof.InitialTreesProof, initialMerkleCaps, capIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FriChip) VerifyFriProof(
|
func (f *FriChip) VerifyFriProof(
|
||||||
openings *FriOpenings,
|
openings FriOpenings,
|
||||||
friChallenges *FriChallenges,
|
friChallenges *FriChallenges,
|
||||||
initialMerkleCaps []MerkleCap,
|
initialMerkleCaps []MerkleCap,
|
||||||
friProof *FriProof,
|
friProof *FriProof,
|
||||||
@ -116,9 +196,9 @@ func (f *FriChip) VerifyFriProof(
|
|||||||
); */
|
); */
|
||||||
|
|
||||||
// Check POW
|
// Check POW
|
||||||
f.assertLeadingZeros(friProof.PowWitness, f.friParams.Config)
|
f.assertLeadingZeros(friChallenges.FriPowResponse, f.friParams.Config)
|
||||||
|
|
||||||
precomputedReducedEvals := f.fromOpeningsAndAlpha(openings, friChallenges.FriAlpha)
|
precomputedReducedEvals := f.fromOpeningsAndAlpha(&openings, friChallenges.FriAlpha)
|
||||||
|
|
||||||
// Size of the LDE domain.
|
// Size of the LDE domain.
|
||||||
n := uint64(math.Pow(2, float64(f.friParams.DegreeBits+f.friParams.Config.RateBits)))
|
n := uint64(math.Pow(2, float64(f.friParams.DegreeBits+f.friParams.Config.RateBits)))
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package plonky2_verifier
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
. "gnark-ed25519/field"
|
. "gnark-ed25519/field"
|
||||||
|
"gnark-ed25519/poseidon"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/consensys/gnark/frontend"
|
"github.com/consensys/gnark/frontend"
|
||||||
@ -13,11 +14,66 @@ type TestFriCircuit struct{}
|
|||||||
func (circuit *TestFriCircuit) Define(api frontend.API) error {
|
func (circuit *TestFriCircuit) Define(api frontend.API) error {
|
||||||
proofWithPis := DeserializeProofWithPublicInputs("./data/fibonacci/proof_with_public_inputs.json")
|
proofWithPis := DeserializeProofWithPublicInputs("./data/fibonacci/proof_with_public_inputs.json")
|
||||||
commonCircuitData := DeserializeCommonCircuitData("./data/fibonacci/common_circuit_data.json")
|
commonCircuitData := DeserializeCommonCircuitData("./data/fibonacci/common_circuit_data.json")
|
||||||
|
verifierOnlyCircuitData := DeserializeVerifierOnlyCircuitData("./data/fibonacci/verifier_only_circuit_data.json")
|
||||||
|
|
||||||
field := NewFieldAPI(api)
|
field := NewFieldAPI(api)
|
||||||
|
qe := NewQuadraticExtensionAPI(field, commonCircuitData.DegreeBits)
|
||||||
|
poseidonChip := poseidon.NewPoseidonChip(api, field)
|
||||||
|
friChip := NewFriChip(api, field, qe, poseidonChip, &commonCircuitData.FriParams)
|
||||||
|
|
||||||
|
friChallenges := FriChallenges{
|
||||||
|
FriAlpha: QuadraticExtension{
|
||||||
|
NewFieldElementFromString("14641715242626918707"),
|
||||||
|
NewFieldElementFromString("10574243340537902930"),
|
||||||
|
},
|
||||||
|
FriBetas: []QuadraticExtension{},
|
||||||
|
FriPowResponse: NewFieldElement(82451580476419),
|
||||||
|
FriQueryIndicies: []F{
|
||||||
|
NewFieldElement(6790812084677375942),
|
||||||
|
NewFieldElement(12394212020331474798),
|
||||||
|
NewFieldElement(16457600747000998582),
|
||||||
|
NewFieldElement(1543271328932331916),
|
||||||
|
NewFieldElement(12115726870906958644),
|
||||||
|
NewFieldElement(6775897107605342797),
|
||||||
|
NewFieldElement(15989401564746021030),
|
||||||
|
NewFieldElement(10691676456016926845),
|
||||||
|
NewFieldElement(1632499470630032007),
|
||||||
|
NewFieldElement(1317292355445098328),
|
||||||
|
NewFieldElement(18391440812534384252),
|
||||||
|
NewFieldElement(17321705613231354333),
|
||||||
|
NewFieldElement(6176487551308859603),
|
||||||
|
NewFieldElement(7119835651572002873),
|
||||||
|
NewFieldElement(3903019169623116693),
|
||||||
|
NewFieldElement(4886491111111487546),
|
||||||
|
NewFieldElement(4087641893164620518),
|
||||||
|
NewFieldElement(13801643080324181364),
|
||||||
|
NewFieldElement(16993775312274189321),
|
||||||
|
NewFieldElement(9268202926222765679),
|
||||||
|
NewFieldElement(10683001302406181735),
|
||||||
|
NewFieldElement(13359465725531647963),
|
||||||
|
NewFieldElement(4523327590105620849),
|
||||||
|
NewFieldElement(4883588003760409588),
|
||||||
|
NewFieldElement(187699146998097671),
|
||||||
|
NewFieldElement(14489263557623716717),
|
||||||
|
NewFieldElement(11748359318238148146),
|
||||||
|
NewFieldElement(13636347200053048758),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
initialMerkleCaps := []MerkleCap{
|
||||||
|
verifierOnlyCircuitData.ConstantSigmasCap,
|
||||||
|
proofWithPis.Proof.WiresCap,
|
||||||
|
proofWithPis.Proof.PlonkZsPartialProductsCap,
|
||||||
|
proofWithPis.Proof.QuotientPolysCap,
|
||||||
|
}
|
||||||
|
|
||||||
|
friChip.VerifyFriProof(
|
||||||
|
proofWithPis.Proof.Openings.ToFriOpenings(),
|
||||||
|
&friChallenges,
|
||||||
|
initialMerkleCaps,
|
||||||
|
&proofWithPis.Proof.OpeningProof,
|
||||||
|
)
|
||||||
|
|
||||||
friChip := NewFriChip(api, field, commonCircuitData.Config.FriConfig)
|
|
||||||
friChip.VerifyFriProof(&proofWithPis.Proof.OpeningProof)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -75,6 +75,10 @@ type FriConfig struct {
|
|||||||
// TODO: add FriReductionStrategy
|
// TODO: add FriReductionStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fc *FriConfig) rate() float64 {
|
||||||
|
return 1.0 / float64((uint64(1) << fc.RateBits))
|
||||||
|
}
|
||||||
|
|
||||||
type FriParams struct {
|
type FriParams struct {
|
||||||
Config FriConfig
|
Config FriConfig
|
||||||
Hiding bool
|
Hiding bool
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
package plonky2_verifier
|
package plonky2_verifier
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
. "gnark-ed25519/field"
|
. "gnark-ed25519/field"
|
||||||
|
"math/bits"
|
||||||
)
|
)
|
||||||
|
|
||||||
func reduceWithPowers(qe *QuadraticExtensionAPI, terms []QuadraticExtension, scalar QuadraticExtension) QuadraticExtension {
|
func reduceWithPowers(qe *QuadraticExtensionAPI, terms []QuadraticExtension, scalar QuadraticExtension) QuadraticExtension {
|
||||||
@ -19,3 +21,12 @@ func reduceWithPowers(qe *QuadraticExtensionAPI, terms []QuadraticExtension, sca
|
|||||||
|
|
||||||
return sum
|
return sum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Computes `log_2(n)`, panicking if `n` is not a power of two.
|
||||||
|
func log2Strict(n uint) int {
|
||||||
|
res := bits.TrailingZeros(n)
|
||||||
|
if n>>res != 1 {
|
||||||
|
panic(fmt.Sprintf("Not a power of two: %d", n))
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user