|
package verifier
|
|
|
|
import (
|
|
"github.com/consensys/gnark/frontend"
|
|
"github.com/succinctlabs/gnark-plonky2-verifier/challenger"
|
|
"github.com/succinctlabs/gnark-plonky2-verifier/fri"
|
|
gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks"
|
|
"github.com/succinctlabs/gnark-plonky2-verifier/plonk"
|
|
"github.com/succinctlabs/gnark-plonky2-verifier/poseidon"
|
|
"github.com/succinctlabs/gnark-plonky2-verifier/types"
|
|
"github.com/succinctlabs/gnark-plonky2-verifier/variables"
|
|
)
|
|
|
|
type VerifierChip struct {
|
|
api frontend.API `gnark:"-"`
|
|
glChip *gl.Chip `gnark:"-"`
|
|
poseidonGlChip *poseidon.GoldilocksChip `gnark:"-"`
|
|
poseidonBN254Chip *poseidon.BN254Chip `gnark:"-"`
|
|
plonkChip *plonk.PlonkChip `gnark:"-"`
|
|
friChip *fri.Chip `gnark:"-"`
|
|
commonData types.CommonCircuitData `gnark:"-"`
|
|
}
|
|
|
|
func NewVerifierChip(api frontend.API, commonCircuitData types.CommonCircuitData) *VerifierChip {
|
|
glChip := gl.New(api)
|
|
friChip := fri.NewChip(api, &commonCircuitData, &commonCircuitData.FriParams)
|
|
plonkChip := plonk.NewPlonkChip(api, commonCircuitData)
|
|
poseidonGlChip := poseidon.NewGoldilocksChip(api)
|
|
poseidonBN254Chip := poseidon.NewBN254Chip(api)
|
|
return &VerifierChip{
|
|
api: api,
|
|
glChip: glChip,
|
|
poseidonGlChip: poseidonGlChip,
|
|
poseidonBN254Chip: poseidonBN254Chip,
|
|
plonkChip: plonkChip,
|
|
friChip: friChip,
|
|
commonData: commonCircuitData,
|
|
}
|
|
}
|
|
|
|
func (c *VerifierChip) GetPublicInputsHash(publicInputs []gl.Variable) poseidon.GoldilocksHashOut {
|
|
return c.poseidonGlChip.HashNoPad(publicInputs)
|
|
}
|
|
|
|
func (c *VerifierChip) GetChallenges(
|
|
proof variables.Proof,
|
|
publicInputsHash poseidon.GoldilocksHashOut,
|
|
verifierData variables.VerifierOnlyCircuitData,
|
|
) variables.ProofChallenges {
|
|
config := c.commonData.Config
|
|
numChallenges := config.NumChallenges
|
|
challenger := challenger.NewChip(c.api)
|
|
|
|
var circuitDigest = verifierData.CircuitDigest
|
|
|
|
challenger.ObserveBN254Hash(circuitDigest)
|
|
challenger.ObserveHash(publicInputsHash)
|
|
challenger.ObserveCap(proof.WiresCap)
|
|
plonkBetas := challenger.GetNChallenges(numChallenges)
|
|
plonkGammas := challenger.GetNChallenges(numChallenges)
|
|
|
|
challenger.ObserveCap(proof.PlonkZsPartialProductsCap)
|
|
plonkAlphas := challenger.GetNChallenges(numChallenges)
|
|
|
|
challenger.ObserveCap(proof.QuotientPolysCap)
|
|
plonkZeta := challenger.GetExtensionChallenge()
|
|
|
|
challenger.ObserveOpenings(c.friChip.ToOpenings(proof.Openings))
|
|
|
|
return variables.ProofChallenges{
|
|
PlonkBetas: plonkBetas,
|
|
PlonkGammas: plonkGammas,
|
|
PlonkAlphas: plonkAlphas,
|
|
PlonkZeta: plonkZeta,
|
|
FriChallenges: challenger.GetFriChallenges(
|
|
proof.OpeningProof.CommitPhaseMerkleCaps,
|
|
proof.OpeningProof.FinalPoly,
|
|
proof.OpeningProof.PowWitness,
|
|
config.FriConfig,
|
|
),
|
|
}
|
|
}
|
|
|
|
func (c *VerifierChip) rangeCheckProof(proof variables.Proof) {
|
|
// Need to verify the plonky2 proof's openings, openings proof (other than the sibling elements), fri's final poly, pow witness.
|
|
|
|
// Note that this is NOT range checking the public inputs (first 32 elements should be no more than 8 bits and the last 4 elements should be no more than 64 bits). Since this is currently being inputted via the smart contract,
|
|
// we will assume that caller is doing that check.
|
|
|
|
// Range check the proof's openings.
|
|
for _, constant := range proof.Openings.Constants {
|
|
c.glChip.RangeCheckQE(constant)
|
|
}
|
|
|
|
for _, plonkSigma := range proof.Openings.PlonkSigmas {
|
|
c.glChip.RangeCheckQE(plonkSigma)
|
|
}
|
|
|
|
for _, wire := range proof.Openings.Wires {
|
|
c.glChip.RangeCheckQE(wire)
|
|
}
|
|
|
|
for _, plonkZ := range proof.Openings.PlonkZs {
|
|
c.glChip.RangeCheckQE(plonkZ)
|
|
}
|
|
|
|
for _, plonkZNext := range proof.Openings.PlonkZsNext {
|
|
c.glChip.RangeCheckQE(plonkZNext)
|
|
}
|
|
|
|
for _, partialProduct := range proof.Openings.PartialProducts {
|
|
c.glChip.RangeCheckQE(partialProduct)
|
|
}
|
|
|
|
for _, quotientPoly := range proof.Openings.QuotientPolys {
|
|
c.glChip.RangeCheckQE(quotientPoly)
|
|
}
|
|
|
|
// Range check the openings proof.
|
|
for _, queryRound := range proof.OpeningProof.QueryRoundProofs {
|
|
for _, evalsProof := range queryRound.InitialTreesProof.EvalsProofs {
|
|
for _, evalsProofElement := range evalsProof.Elements {
|
|
c.glChip.RangeCheck(evalsProofElement)
|
|
}
|
|
}
|
|
|
|
for _, queryStep := range queryRound.Steps {
|
|
for _, eval := range queryStep.Evals {
|
|
c.glChip.RangeCheckQE(eval)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Range check the fri's final poly.
|
|
for _, coeff := range proof.OpeningProof.FinalPoly.Coeffs {
|
|
c.glChip.RangeCheckQE(coeff)
|
|
}
|
|
|
|
// Range check the pow witness.
|
|
c.glChip.RangeCheck(proof.OpeningProof.PowWitness)
|
|
}
|
|
|
|
func (c *VerifierChip) Verify(
|
|
proof variables.Proof,
|
|
publicInputs []gl.Variable,
|
|
verifierData variables.VerifierOnlyCircuitData,
|
|
) {
|
|
c.rangeCheckProof(proof)
|
|
|
|
// Generate the parts of the witness that is for the plonky2 proof input
|
|
publicInputsHash := c.GetPublicInputsHash(publicInputs)
|
|
proofChallenges := c.GetChallenges(proof, publicInputsHash, verifierData)
|
|
|
|
c.plonkChip.Verify(proofChallenges, proof.Openings, publicInputsHash)
|
|
|
|
initialMerkleCaps := []variables.FriMerkleCap{
|
|
verifierData.ConstantSigmasCap,
|
|
proof.WiresCap,
|
|
proof.PlonkZsPartialProductsCap,
|
|
proof.QuotientPolysCap,
|
|
}
|
|
|
|
c.friChip.VerifyFriProof(
|
|
c.friChip.GetInstance(proofChallenges.PlonkZeta),
|
|
c.friChip.ToOpenings(proof.Openings),
|
|
&proofChallenges.FriChallenges,
|
|
initialMerkleCaps,
|
|
&proof.OpeningProof,
|
|
)
|
|
}
|