Browse Source

standalone fri circuit

main
Kevin Jue 2 years ago
parent
commit
1ad9e73634
5 changed files with 431 additions and 13 deletions
  1. +407
    -0
      fri_benchmark.go
  2. +1
    -1
      plonky2_verifier/challenger.go
  3. +9
    -2
      plonky2_verifier/fri.go
  4. +10
    -10
      plonky2_verifier/fri_utils.go
  5. +4
    -0
      poseidon/poseidon.go

+ 407
- 0
fri_benchmark.go

@ -0,0 +1,407 @@
package main
import (
"fmt"
. "gnark-ed25519/field"
. "gnark-ed25519/plonky2_verifier"
"gnark-ed25519/poseidon"
"os"
"time"
"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark/backend/groth16"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/frontend/cs/r1cs"
)
type BenchmarkLargeDummyFriCircuit struct {
zeta QuadraticExtension
openings FriOpenings
friChallenges FriChallenges
initialMerkleCaps []MerkleCap
friProof FriProof
}
func (circuit *BenchmarkLargeDummyFriCircuit) Define(api frontend.API) error {
commonCircuitData := DeserializeCommonCircuitData("./plonky2_verifier/data/dummy_2^14_gates/common_circuit_data.json")
field := NewFieldAPI(api)
qeAPI := NewQuadraticExtensionAPI(field, commonCircuitData.DegreeBits)
poseidonChip := poseidon.NewPoseidonChip(api, field)
friChip := NewFriChip(api, field, qeAPI, poseidonChip, &commonCircuitData.FriParams)
friChip.VerifyFriProof(
commonCircuitData.GetFriInstance(qeAPI, circuit.zeta, commonCircuitData.DegreeBits),
circuit.openings,
&circuit.friChallenges,
circuit.initialMerkleCaps,
&circuit.friProof,
)
return nil
}
func compileCircuit() frontend.CompiledConstraintSystem {
fmt.Println("compiling circuit", time.Now())
circuit := BenchmarkLargeDummyFriCircuit{}
/*
commonCircuitData := DeserializeCommonCircuitData("./plonky2_verifier/data/dummy_2^14_gates/common_circuit_data.json")
circuit.zeta[0] = emulated.NewElement[EmulatedField](nil)
circuit.zeta[1] = emulated.NewElement[EmulatedField](nil)
fmt.Println("circuit zeta allocated")
// Batch 0 has the following openings
// Constants (config.num_constants + 1)
// Sigmas (config.num_routed_wires)
// Wires (config.num_wires)
// Plonk_Z (config.num_challenges)
// Partial Products (config.num_challenges * config.num_partial_products)
// Quotient Polynomails (config.num_challenges * config.quotient_degree_factor)
// Batch 1 has the following openings
// Plonk_Z_next (config.num_challenges)
circuit.openings.Batches = make([]FriOpeningBatch, 2)
batch1Size := commonCircuitData.NumConstants + 1 +
commonCircuitData.Config.NumRoutedWires +
commonCircuitData.Config.NumWires +
commonCircuitData.Config.NumChallenges +
(commonCircuitData.Config.NumChallenges * commonCircuitData.NumPartialProducts) +
(commonCircuitData.Config.NumChallenges * commonCircuitData.QuotientDegreeFactor)
circuit.openings.Batches[0].Values = make([]QuadraticExtension, 0)
for i := uint64(0); i < batch1Size; i++ {
circuit.openings.Batches[0].Values = append(circuit.openings.Batches[0].Values, NewEmptyQuadraticExtension())
}
batch2Size := commonCircuitData.Config.NumChallenges
circuit.openings.Batches[1].Values = make([]QuadraticExtension, 0)
for i := uint64(0); i < batch2Size; i++ {
circuit.openings.Batches[1].Values = append(circuit.openings.Batches[1].Values, NewEmptyQuadraticExtension())
}
fmt.Println("circuit openings allocated")
circuit.friChallenges.FriAlpha = NewEmptyQuadraticExtension()
circuit.friChallenges.FriPowResponse = emulated.NewElement[EmulatedField](nil)
circuit.friChallenges.FriBetas = make([]QuadraticExtension, 0)
for i := 0; i < len(commonCircuitData.FriParams.ReductionArityBits); i++ {
circuit.friChallenges.FriBetas = append(circuit.friChallenges.FriBetas, NewEmptyQuadraticExtension())
}
circuit.friChallenges.FriQueryIndicies = make([]F, 0)
for i := uint64(0); i < commonCircuitData.FriParams.Config.NumQueryRounds; i++ {
circuit.friChallenges.FriQueryIndicies = append(circuit.friChallenges.FriQueryIndicies, NewEmptyFieldElement())
}
fmt.Println("circuit challenges allocated")
// initial merkle caps is the merkle cap for
// the constant/sigmas, wires, partial products,
// and quotient composite polynomial
// The merkle cap size is 2**cap_height hashes
numMerkleCaps := 4
merkleCapSize := 1 << commonCircuitData.Config.FriConfig.CapHeight
circuit.initialMerkleCaps = make([]MerkleCap, 0)
for i := 0; i < numMerkleCaps; i++ {
merkleCap := make([]Hash, 0)
for j := 0; j < merkleCapSize; j++ {
merkleCap = append(merkleCap, NewEmptyHash())
}
circuit.initialMerkleCaps = append(circuit.initialMerkleCaps, merkleCap)
}
fmt.Println("circuit initialMerkleCaps allocated")
// CommitPhaseMerkleCap is number of reduction_arity_bits
// finalPoly has 2^(degreeBits - sum(arity_bits)) coefficients
numCommitPhaseMerkleCaps := len(commonCircuitData.FriParams.ReductionArityBits)
for i := 0; i < numCommitPhaseMerkleCaps; i++ {
circuit.friProof.CommitPhaseMerkleCaps = make([]MerkleCap, 0)
merkleCap := make([]Hash, 0)
for j := 0; j < merkleCapSize; j++ {
merkleCap = append(merkleCap, NewEmptyHash())
}
circuit.friProof.CommitPhaseMerkleCaps = append(circuit.friProof.CommitPhaseMerkleCaps, merkleCap)
}
fmt.Println("circuit friproof CommitPhaseMerkleCaps allocated")
friOracleInfo := commonCircuitData.FriOracles()
circuit.friProof.QueryRoundProofs = make([]FriQueryRound, 0)
for i := 0; i < int(commonCircuitData.FriParams.Config.NumQueryRounds); i++ {
evalsProof := make([]EvalProof, 0)
// Allocation for the initial trees proof
for j := 0; j < len(friOracleInfo); j++ {
leafSize := friOracleInfo[0].NumPolys
merkleProofLen := commonCircuitData.DegreeBits + commonCircuitData.Config.FriConfig.RateBits - commonCircuitData.Config.FriConfig.CapHeight
evalElements := make([]F, 0)
for k := uint64(0); k < leafSize; k++ {
evalElements = append(evalElements, NewEmptyFieldElement())
}
merkleProofSiblings := make([]Hash, 0)
for k := uint64(0); k < merkleProofLen; k++ {
merkleProofSiblings = append(merkleProofSiblings, NewEmptyHash())
}
evalsProof = append(
evalsProof,
EvalProof{
Elements: evalElements,
MerkleProof: MerkleProof{merkleProofSiblings},
},
)
}
// Allocation for the steps
steps := make([]FriQueryStep, 0)
codewordLenBits := commonCircuitData.DegreeBits + commonCircuitData.Config.FriConfig.RateBits
for j := 0; j < len(commonCircuitData.FriParams.ReductionArityBits); j++ {
arityBits := commonCircuitData.FriParams.ReductionArityBits[j]
leafSize := 1 << int(arityBits)
codewordLenBits -= arityBits
merkleProofLen := codewordLenBits - commonCircuitData.Config.FriConfig.CapHeight
evalQEs := make([]QuadraticExtension, 0)
for k := 0; k < leafSize; k++ {
evalQEs = append(evalQEs, NewEmptyQuadraticExtension())
}
merkleProofSiblings := make([]Hash, 0)
for k := uint64(0); k < merkleProofLen; k++ {
merkleProofSiblings = append(merkleProofSiblings, NewEmptyHash())
}
steps = append(
steps,
FriQueryStep{
Evals: evalQEs,
MerkleProof: MerkleProof{merkleProofSiblings},
},
)
}
circuit.friProof.QueryRoundProofs = append(
circuit.friProof.QueryRoundProofs,
FriQueryRound{
InitialTreesProof: FriInitialTreeProof{evalsProof},
Steps: steps,
},
)
fmt.Println("circuit friproof QueryRoundProofs allocated for round", i)
}
// Final poly allocation
finalPolyLenBit := commonCircuitData.DegreeBits
for _, arityBit := range commonCircuitData.FriParams.ReductionArityBits {
finalPolyLenBit -= arityBit
}
circuit.friProof.FinalPoly.Coeffs = make([]QuadraticExtension, 0)
for i := 0; i < (1 << finalPolyLenBit); i++ {
circuit.friProof.FinalPoly.Coeffs = append(circuit.friProof.FinalPoly.Coeffs, NewEmptyQuadraticExtension())
}
fmt.Println("circuit friproof FinalPoly allocated")
// PowWitness allocation
circuit.friProof.PowWitness = NewEmptyFieldElement()
fmt.Println("Partial witness allocation done")
*/
proofWithPis := DeserializeProofWithPublicInputs("./plonky2_verifier/data/dummy_2^14_gates/proof_with_public_inputs.json")
verifierOnlyCircuitData := DeserializeVerifierOnlyCircuitData("./plonky2_verifier/data/dummy_2^14_gates/verifier_only_circuit_data.json")
zeta := QuadraticExtension{
NewFieldElementFromString("17377750363769967882"),
NewFieldElementFromString("11921191651424768462"),
}
friChallenges := FriChallenges{
FriAlpha: QuadraticExtension{
NewFieldElementFromString("16721004555774385479"),
NewFieldElementFromString("10688151135543754663"),
},
FriBetas: []QuadraticExtension{
{
NewFieldElementFromString("3312441922957827805"),
NewFieldElementFromString("15128092514958289671"),
},
{
NewFieldElementFromString("13630530769060141802"),
NewFieldElementFromString("14559883974933163008"),
},
{
NewFieldElementFromString("16146508250083930687"),
NewFieldElementFromString("5176346568444408396"),
},
},
FriPowResponse: NewFieldElement(4389),
FriQueryIndicies: []F{
NewFieldElementFromString("16334967868590615051"),
NewFieldElementFromString("2911473540496037915"),
NewFieldElementFromString("14887216056886344225"),
NewFieldElementFromString("7808811227805914295"),
NewFieldElementFromString("2018594961417375749"),
NewFieldElementFromString("3733368398777208435"),
NewFieldElementFromString("2623035669037055104"),
NewFieldElementFromString("299243030573481514"),
NewFieldElementFromString("7189789717962704433"),
NewFieldElementFromString("14566344026886816268"),
NewFieldElementFromString("12555390069003437453"),
NewFieldElementFromString("17225508403199418233"),
NewFieldElementFromString("5088797913879903292"),
NewFieldElementFromString("9715691392773433023"),
NewFieldElementFromString("7565836764713256165"),
NewFieldElementFromString("1500143546029322929"),
NewFieldElementFromString("1245802417104422080"),
NewFieldElementFromString("6831959786661245110"),
NewFieldElementFromString("17271054758535453780"),
NewFieldElementFromString("6225460404576395409"),
NewFieldElementFromString("15932661092896277351"),
NewFieldElementFromString("12452534049198240575"),
NewFieldElementFromString("4225199666055520177"),
NewFieldElementFromString("13235091290587791090"),
NewFieldElementFromString("2562357622728700774"),
NewFieldElementFromString("17676678042980201498"),
NewFieldElementFromString("5837067135702409874"),
NewFieldElementFromString("11238419549114325157"),
},
}
initialMerkleCaps := []MerkleCap{
verifierOnlyCircuitData.ConstantSigmasCap,
proofWithPis.Proof.WiresCap,
proofWithPis.Proof.PlonkZsPartialProductsCap,
proofWithPis.Proof.QuotientPolysCap,
}
circuit.zeta = zeta
circuit.openings = proofWithPis.Proof.Openings.ToFriOpenings()
circuit.friChallenges = friChallenges
circuit.initialMerkleCaps = initialMerkleCaps
circuit.friProof = proofWithPis.Proof.OpeningProof
r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit)
if err != nil {
fmt.Println("error in building circuit", err)
os.Exit(1)
}
return r1cs
}
func createProof(r1cs frontend.CompiledConstraintSystem) groth16.Proof {
proofWithPis := DeserializeProofWithPublicInputs("./plonky2_verifier/data/dummy_2^14_gates/proof_with_public_inputs.json")
verifierOnlyCircuitData := DeserializeVerifierOnlyCircuitData("./plonky2_verifier/data/dummy_2^14_gates/verifier_only_circuit_data.json")
zeta := QuadraticExtension{
NewFieldElementFromString("17377750363769967882"),
NewFieldElementFromString("11921191651424768462"),
}
friChallenges := FriChallenges{
FriAlpha: QuadraticExtension{
NewFieldElementFromString("16721004555774385479"),
NewFieldElementFromString("10688151135543754663"),
},
FriBetas: []QuadraticExtension{
{
NewFieldElementFromString("3312441922957827805"),
NewFieldElementFromString("15128092514958289671"),
},
{
NewFieldElementFromString("13630530769060141802"),
NewFieldElementFromString("14559883974933163008"),
},
{
NewFieldElementFromString("16146508250083930687"),
NewFieldElementFromString("5176346568444408396"),
},
},
FriPowResponse: NewFieldElement(4389),
FriQueryIndicies: []F{
NewFieldElementFromString("16334967868590615051"),
NewFieldElementFromString("2911473540496037915"),
NewFieldElementFromString("14887216056886344225"),
NewFieldElementFromString("7808811227805914295"),
NewFieldElementFromString("2018594961417375749"),
NewFieldElementFromString("3733368398777208435"),
NewFieldElementFromString("2623035669037055104"),
NewFieldElementFromString("299243030573481514"),
NewFieldElementFromString("7189789717962704433"),
NewFieldElementFromString("14566344026886816268"),
NewFieldElementFromString("12555390069003437453"),
NewFieldElementFromString("17225508403199418233"),
NewFieldElementFromString("5088797913879903292"),
NewFieldElementFromString("9715691392773433023"),
NewFieldElementFromString("7565836764713256165"),
NewFieldElementFromString("1500143546029322929"),
NewFieldElementFromString("1245802417104422080"),
NewFieldElementFromString("6831959786661245110"),
NewFieldElementFromString("17271054758535453780"),
NewFieldElementFromString("6225460404576395409"),
NewFieldElementFromString("15932661092896277351"),
NewFieldElementFromString("12452534049198240575"),
NewFieldElementFromString("4225199666055520177"),
NewFieldElementFromString("13235091290587791090"),
NewFieldElementFromString("2562357622728700774"),
NewFieldElementFromString("17676678042980201498"),
NewFieldElementFromString("5837067135702409874"),
NewFieldElementFromString("11238419549114325157"),
},
}
initialMerkleCaps := []MerkleCap{
verifierOnlyCircuitData.ConstantSigmasCap,
proofWithPis.Proof.WiresCap,
proofWithPis.Proof.PlonkZsPartialProductsCap,
proofWithPis.Proof.QuotientPolysCap,
}
// Witness
assignment := &BenchmarkLargeDummyFriCircuit{
zeta: zeta,
openings: proofWithPis.Proof.Openings.ToFriOpenings(),
friChallenges: friChallenges,
initialMerkleCaps: initialMerkleCaps,
friProof: proofWithPis.Proof.OpeningProof,
}
fmt.Println("Generating witness", time.Now())
witness, _ := frontend.NewWitness(assignment, ecc.BN254.ScalarField())
publicWitness, _ := witness.Public()
pk, vk, err := groth16.Setup(r1cs)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Creating proof", time.Now())
proof, err := groth16.Prove(r1cs, pk, witness)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Verifying proof", time.Now())
err = groth16.Verify(proof, vk, publicWitness)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
return proof
}
func main() {
r1cs := compileCircuit()
fi, _ := os.Open("dummy_fri.r1cs")
r1cs.WriteTo(fi)
proof := createProof(r1cs)
fmt.Println(proof.CurveID(), time.Now())
}

+ 1
- 1
plonky2_verifier/challenger.go

@ -67,7 +67,7 @@ func (c *ChallengerChip) ObserveExtensionElements(elements []QuadraticExtension)
func (c *ChallengerChip) ObserveOpenings(openings FriOpenings) {
for i := 0; i < len(openings.Batches); i++ {
c.ObserveExtensionElements(openings.Batches[i].values)
c.ObserveExtensionElements(openings.Batches[i].Values)
}
}

+ 9
- 2
plonky2_verifier/fri.go

@ -48,7 +48,7 @@ func (f *FriChip) fromOpeningsAndAlpha(openings *FriOpenings, alpha QuadraticExt
reducedOpenings := make([]QuadraticExtension, 0, 2)
for _, batch := range openings.Batches {
reducedOpenings = append(reducedOpenings, f.qeAPI.ReduceWithPowers(batch.values, alpha))
reducedOpenings = append(reducedOpenings, f.qeAPI.ReduceWithPowers(batch.Values, alpha))
}
return reducedOpenings
@ -93,6 +93,7 @@ func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits
copy(leftSiblingState[8:12], fourZeros[0:4])
leftHash := f.poseidonChip.Poseidon(leftSiblingState)
var leftHashCompress Hash
leftHashCompress[0] = leftHash[0]
leftHashCompress[1] = leftHash[1]
@ -105,6 +106,7 @@ func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits
copy(rightSiblingState[8:12], fourZeros[0:4])
rightHash := f.poseidonChip.Poseidon(rightSiblingState)
var rightHashCompress Hash
rightHashCompress[0] = rightHash[0]
rightHashCompress[1] = rightHash[1]
@ -116,7 +118,12 @@ func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits
// We assume that the cap_height is 4. Create two levels of the Lookup2 circuit
if len(capIndexBits) != 4 || len(merkleCap) != 16 {
panic("capIndexBits length should be 4 and the merkleCap length should be 16")
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)
}
const NUM_LEAF_LOOKUPS = 4

+ 10
- 10
plonky2_verifier/fri_utils.go

@ -6,7 +6,7 @@ import (
)
type FriOpeningBatch struct {
values []QuadraticExtension
Values []QuadraticExtension
}
type FriOpenings struct {
@ -14,14 +14,14 @@ type FriOpenings struct {
}
func (c *OpeningSet) ToFriOpenings() FriOpenings {
values := c.Constants
values = append(values, c.PlonkSigmas...)
values = append(values, c.Wires...)
values = append(values, c.PlonkZs...)
values = append(values, c.PartialProducts...)
values = append(values, c.QuotientPolys...)
zetaBatch := FriOpeningBatch{values: values}
zetaNextBatch := FriOpeningBatch{values: c.PlonkZsNext}
values := c.Constants // num_constants + 1
values = append(values, c.PlonkSigmas...) // num_routed_wires
values = append(values, c.Wires...) // num_wires
values = append(values, c.PlonkZs...) // num_challenges
values = append(values, c.PartialProducts...) // num_challenges * num_partial_products
values = append(values, c.QuotientPolys...) // num_challenges * quotient_degree_factor
zetaBatch := FriOpeningBatch{Values: values}
zetaNextBatch := FriOpeningBatch{Values: c.PlonkZsNext}
return FriOpenings{Batches: []FriOpeningBatch{zetaBatch, zetaNextBatch}}
}
@ -156,7 +156,7 @@ func (c *CommonCircuitData) GetFriInstance(qeAPI *QuadraticExtensionAPI, zeta Qu
}
g := field.GoldilocksPrimitiveRootOfUnity(degreeBits)
zetaNext := qeAPI.MulExtension(QuadraticExtension{field.NewFieldElement(g.Uint64()), field.ZERO_F}, zeta)
zetaNext := qeAPI.MulExtension(qeAPI.FieldToQE(field.NewFieldElement(g.Uint64())), zeta)
zetaNextBath := FriBatchInfo{
Point: zetaNext,

+ 4
- 0
poseidon/poseidon.go

@ -37,6 +37,10 @@ func (c *PoseidonChip) Poseidon(input PoseidonState) PoseidonState {
func (c *PoseidonChip) HashNToMNoPad(input []F, nbOutputs int) []F {
var state PoseidonState
for i := 0; i < WIDTH; i++ {
state[i] = ZERO_F
}
for i := 0; i < len(input); i += SPONGE_RATE {
for j := 0; j < SPONGE_RATE; j++ {
if i+j < len(input) {

Loading…
Cancel
Save