|
package verifier
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"math/big"
|
|
"os"
|
|
|
|
"github.com/consensys/gnark/frontend"
|
|
gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks"
|
|
"github.com/succinctlabs/gnark-plonky2-verifier/plonk/gates"
|
|
"github.com/succinctlabs/gnark-plonky2-verifier/poseidon"
|
|
"github.com/succinctlabs/gnark-plonky2-verifier/types"
|
|
)
|
|
|
|
type ProofWithPublicInputsRaw struct {
|
|
Proof struct {
|
|
WiresCap []string `json:"wires_cap"`
|
|
PlonkZsPartialProductsCap []string `json:"plonk_zs_partial_products_cap"`
|
|
QuotientPolysCap []string `json:"quotient_polys_cap"`
|
|
Openings struct {
|
|
Constants [][]uint64 `json:"constants"`
|
|
PlonkSigmas [][]uint64 `json:"plonk_sigmas"`
|
|
Wires [][]uint64 `json:"wires"`
|
|
PlonkZs [][]uint64 `json:"plonk_zs"`
|
|
PlonkZsNext [][]uint64 `json:"plonk_zs_next"`
|
|
PartialProducts [][]uint64 `json:"partial_products"`
|
|
QuotientPolys [][]uint64 `json:"quotient_polys"`
|
|
} `json:"openings"`
|
|
OpeningProof struct {
|
|
CommitPhaseMerkleCaps [][]string `json:"commit_phase_merkle_caps"`
|
|
QueryRoundProofs []struct {
|
|
InitialTreesProof struct {
|
|
EvalsProofs []EvalProofRaw `json:"evals_proofs"`
|
|
} `json:"initial_trees_proof"`
|
|
Steps []struct {
|
|
Evals [][]uint64 `json:"evals"`
|
|
MerkleProof struct {
|
|
Siblings []string `json:"siblings"`
|
|
} `json:"merkle_proof"`
|
|
} `json:"steps"`
|
|
} `json:"query_round_proofs"`
|
|
FinalPoly struct {
|
|
Coeffs [][]uint64 `json:"coeffs"`
|
|
} `json:"final_poly"`
|
|
PowWitness uint64 `json:"pow_witness"`
|
|
} `json:"opening_proof"`
|
|
} `json:"proof"`
|
|
PublicInputs []uint64 `json:"public_inputs"`
|
|
}
|
|
|
|
type EvalProofRaw struct {
|
|
LeafElements []uint64
|
|
MerkleProof MerkleProofRaw
|
|
}
|
|
|
|
func (e *EvalProofRaw) UnmarshalJSON(data []byte) error {
|
|
return json.Unmarshal(data, &[]interface{}{&e.LeafElements, &e.MerkleProof})
|
|
}
|
|
|
|
type MerkleProofRaw struct {
|
|
Hash []string
|
|
}
|
|
|
|
func (m *MerkleProofRaw) UnmarshalJSON(data []byte) error {
|
|
type SiblingObject struct {
|
|
Siblings []string // "siblings"
|
|
}
|
|
|
|
var siblings SiblingObject
|
|
if err := json.Unmarshal(data, &siblings); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
m.Hash = make([]string, len(siblings.Siblings))
|
|
copy(m.Hash[:], siblings.Siblings)
|
|
|
|
return nil
|
|
}
|
|
|
|
type CommonCircuitDataRaw struct {
|
|
Config struct {
|
|
NumWires uint64 `json:"num_wires"`
|
|
NumRoutedWires uint64 `json:"num_routed_wires"`
|
|
NumConstants uint64 `json:"num_constants"`
|
|
UseBaseArithmeticGate bool `json:"use_base_arithmetic_gate"`
|
|
SecurityBits uint64 `json:"security_bits"`
|
|
NumChallenges uint64 `json:"num_challenges"`
|
|
ZeroKnowledge bool `json:"zero_knowledge"`
|
|
MaxQuotientDegreeFactor uint64 `json:"max_quotient_degree_factor"`
|
|
FriConfig struct {
|
|
RateBits uint64 `json:"rate_bits"`
|
|
CapHeight uint64 `json:"cap_height"`
|
|
ProofOfWorkBits uint64 `json:"proof_of_work_bits"`
|
|
ReductionStrategy struct {
|
|
ConstantArityBits []uint64 `json:"ConstantArityBits"`
|
|
} `json:"reduction_strategy"`
|
|
NumQueryRounds uint64 `json:"num_query_rounds"`
|
|
} `json:"fri_config"`
|
|
} `json:"config"`
|
|
FriParams struct {
|
|
Config struct {
|
|
RateBits uint64 `json:"rate_bits"`
|
|
CapHeight uint64 `json:"cap_height"`
|
|
ProofOfWorkBits uint64 `json:"proof_of_work_bits"`
|
|
ReductionStrategy struct {
|
|
ConstantArityBits []uint64 `json:"ConstantArityBits"`
|
|
} `json:"reduction_strategy"`
|
|
NumQueryRounds uint64 `json:"num_query_rounds"`
|
|
} `json:"config"`
|
|
Hiding bool `json:"hiding"`
|
|
DegreeBits uint64 `json:"degree_bits"`
|
|
ReductionArityBits []uint64 `json:"reduction_arity_bits"`
|
|
} `json:"fri_params"`
|
|
Gates []string `json:"gates"`
|
|
SelectorsInfo struct {
|
|
SelectorIndices []uint64 `json:"selector_indices"`
|
|
Groups []struct {
|
|
Start uint64 `json:"start"`
|
|
End uint64 `json:"end"`
|
|
} `json:"groups"`
|
|
} `json:"selectors_info"`
|
|
QuotientDegreeFactor uint64 `json:"quotient_degree_factor"`
|
|
NumGateConstraints uint64 `json:"num_gate_constraints"`
|
|
NumConstants uint64 `json:"num_constants"`
|
|
NumPublicInputs uint64 `json:"num_public_inputs"`
|
|
KIs []uint64 `json:"k_is"`
|
|
NumPartialProducts uint64 `json:"num_partial_products"`
|
|
}
|
|
|
|
type ProofChallengesRaw struct {
|
|
PlonkBetas []uint64 `json:"plonk_betas"`
|
|
PlonkGammas []uint64 `json:"plonk_gammas"`
|
|
PlonkAlphas []uint64 `json:"plonk_alphas"`
|
|
PlonkZeta []uint64 `json:"plonk_zeta"`
|
|
FriChallenges struct {
|
|
FriAlpha []uint64 `json:"fri_alpha"`
|
|
FriBetas [][]uint64 `json:"fri_betas"`
|
|
FriPowResponse uint64 `json:"fri_pow_response"`
|
|
FriQueryIndices []uint64 `json:"fri_query_indices"`
|
|
} `json:"fri_challenges"`
|
|
}
|
|
|
|
type VerifierOnlyCircuitDataRaw struct {
|
|
ConstantsSigmasCap []string `json:"constants_sigmas_cap"`
|
|
CircuitDigest string `json:"circuit_digest"`
|
|
}
|
|
|
|
func DeserializeMerkleCap(merkleCapRaw []string) types.FriMerkleCap {
|
|
n := len(merkleCapRaw)
|
|
merkleCap := make([]poseidon.BN254HashOut, n)
|
|
for i := 0; i < n; i++ {
|
|
capBigInt, _ := new(big.Int).SetString(merkleCapRaw[i], 10)
|
|
merkleCap[i] = frontend.Variable(capBigInt)
|
|
}
|
|
return merkleCap
|
|
}
|
|
|
|
func DeserializeMerkleProof(merkleProofRaw struct{ Siblings []interface{} }) types.FriMerkleProof {
|
|
n := len(merkleProofRaw.Siblings)
|
|
var mp types.FriMerkleProof
|
|
mp.Siblings = make([]poseidon.BN254HashOut, n)
|
|
for i := 0; i < n; i++ {
|
|
element := merkleProofRaw.Siblings[i].(struct{ Elements []uint64 })
|
|
mp.Siblings[i] = gl.Uint64ArrayToVariableArray(element.Elements)
|
|
}
|
|
return mp
|
|
}
|
|
|
|
func DeserializeOpeningSet(openingSetRaw struct {
|
|
Constants [][]uint64
|
|
PlonkSigmas [][]uint64
|
|
Wires [][]uint64
|
|
PlonkZs [][]uint64
|
|
PlonkZsNext [][]uint64
|
|
PartialProducts [][]uint64
|
|
QuotientPolys [][]uint64
|
|
}) types.OpeningSet {
|
|
return types.OpeningSet{
|
|
Constants: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.Constants),
|
|
PlonkSigmas: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.PlonkSigmas),
|
|
Wires: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.Wires),
|
|
PlonkZs: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.PlonkZs),
|
|
PlonkZsNext: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.PlonkZsNext),
|
|
PartialProducts: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.PartialProducts),
|
|
QuotientPolys: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.QuotientPolys),
|
|
}
|
|
}
|
|
|
|
func StringArrayToHashBN254Array(rawHashes []string) []poseidon.BN254HashOut {
|
|
hashes := []poseidon.BN254HashOut{}
|
|
|
|
for i := 0; i < len(rawHashes); i++ {
|
|
hashBigInt, _ := new(big.Int).SetString(rawHashes[i], 10)
|
|
hashVar := frontend.Variable(hashBigInt)
|
|
hashes = append(hashes, poseidon.BN254HashOut(hashVar))
|
|
}
|
|
|
|
return hashes
|
|
}
|
|
|
|
func DeserializeFriProof(openingProofRaw struct {
|
|
CommitPhaseMerkleCaps [][]string
|
|
QueryRoundProofs []struct {
|
|
InitialTreesProof struct {
|
|
EvalsProofs []EvalProofRaw
|
|
}
|
|
Steps []struct {
|
|
Evals [][]uint64
|
|
MerkleProof struct {
|
|
Siblings []string
|
|
}
|
|
}
|
|
}
|
|
FinalPoly struct {
|
|
Coeffs [][]uint64
|
|
}
|
|
PowWitness uint64
|
|
}) types.FriProof {
|
|
var openingProof types.FriProof
|
|
openingProof.PowWitness = gl.NewVariable(openingProofRaw.PowWitness)
|
|
openingProof.FinalPoly.Coeffs = gl.Uint64ArrayToQuadraticExtensionArray(openingProofRaw.FinalPoly.Coeffs)
|
|
|
|
openingProof.CommitPhaseMerkleCaps = make([]types.FriMerkleCap, len(openingProofRaw.CommitPhaseMerkleCaps))
|
|
for i := 0; i < len(openingProofRaw.CommitPhaseMerkleCaps); i++ {
|
|
openingProof.CommitPhaseMerkleCaps[i] = StringArrayToHashBN254Array(openingProofRaw.CommitPhaseMerkleCaps[i])
|
|
}
|
|
|
|
numQueryRoundProofs := len(openingProofRaw.QueryRoundProofs)
|
|
openingProof.QueryRoundProofs = make([]types.FriQueryRound, numQueryRoundProofs)
|
|
|
|
for i := 0; i < numQueryRoundProofs; i++ {
|
|
numEvalProofs := len(openingProofRaw.QueryRoundProofs[i].InitialTreesProof.EvalsProofs)
|
|
openingProof.QueryRoundProofs[i].InitialTreesProof.EvalsProofs = make([]types.FriEvalProof, numEvalProofs)
|
|
for j := 0; j < numEvalProofs; j++ {
|
|
openingProof.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].Elements = gl.Uint64ArrayToVariableArray(openingProofRaw.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].LeafElements)
|
|
openingProof.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].MerkleProof.Siblings = StringArrayToHashBN254Array(openingProofRaw.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].MerkleProof.Hash)
|
|
}
|
|
|
|
numSteps := len(openingProofRaw.QueryRoundProofs[i].Steps)
|
|
openingProof.QueryRoundProofs[i].Steps = make([]types.FriQueryStep, numSteps)
|
|
for j := 0; j < numSteps; j++ {
|
|
openingProof.QueryRoundProofs[i].Steps[j].Evals = gl.Uint64ArrayToQuadraticExtensionArray(openingProofRaw.QueryRoundProofs[i].Steps[j].Evals)
|
|
openingProof.QueryRoundProofs[i].Steps[j].MerkleProof.Siblings = StringArrayToHashBN254Array(openingProofRaw.QueryRoundProofs[i].Steps[j].MerkleProof.Siblings)
|
|
}
|
|
}
|
|
|
|
return openingProof
|
|
}
|
|
|
|
func DeserializeProofWithPublicInputs(path string) types.ProofWithPublicInputs {
|
|
jsonFile, err := os.Open(path)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
defer jsonFile.Close()
|
|
rawBytes, _ := io.ReadAll(jsonFile)
|
|
|
|
var raw ProofWithPublicInputsRaw
|
|
err = json.Unmarshal(rawBytes, &raw)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
var proofWithPis types.ProofWithPublicInputs
|
|
proofWithPis.Proof.WiresCap = DeserializeMerkleCap(raw.Proof.WiresCap)
|
|
proofWithPis.Proof.PlonkZsPartialProductsCap = DeserializeMerkleCap(raw.Proof.PlonkZsPartialProductsCap)
|
|
proofWithPis.Proof.QuotientPolysCap = DeserializeMerkleCap(raw.Proof.QuotientPolysCap)
|
|
proofWithPis.Proof.Openings = DeserializeOpeningSet(struct {
|
|
Constants [][]uint64
|
|
PlonkSigmas [][]uint64
|
|
Wires [][]uint64
|
|
PlonkZs [][]uint64
|
|
PlonkZsNext [][]uint64
|
|
PartialProducts [][]uint64
|
|
QuotientPolys [][]uint64
|
|
}(raw.Proof.Openings))
|
|
proofWithPis.Proof.OpeningProof = DeserializeFriProof(struct {
|
|
CommitPhaseMerkleCaps [][]string
|
|
QueryRoundProofs []struct {
|
|
InitialTreesProof struct {
|
|
EvalsProofs []EvalProofRaw
|
|
}
|
|
Steps []struct {
|
|
Evals [][]uint64
|
|
MerkleProof struct {
|
|
Siblings []string
|
|
}
|
|
}
|
|
}
|
|
FinalPoly struct{ Coeffs [][]uint64 }
|
|
PowWitness uint64
|
|
}(raw.Proof.OpeningProof))
|
|
proofWithPis.PublicInputs = gl.Uint64ArrayToVariableArray(raw.PublicInputs)
|
|
|
|
return proofWithPis
|
|
}
|
|
|
|
func DeserializeProofChallenges(path string) types.ProofChallenges {
|
|
jsonFile, err := os.Open(path)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
defer jsonFile.Close()
|
|
rawBytes, _ := io.ReadAll(jsonFile)
|
|
|
|
var raw ProofChallengesRaw
|
|
err = json.Unmarshal(rawBytes, &raw)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
var proofChallenges types.ProofChallenges
|
|
proofChallenges.PlonkBetas = gl.Uint64ArrayToVariableArray(raw.PlonkBetas)
|
|
proofChallenges.PlonkGammas = gl.Uint64ArrayToVariableArray(raw.PlonkGammas)
|
|
proofChallenges.PlonkAlphas = gl.Uint64ArrayToVariableArray(raw.PlonkAlphas)
|
|
proofChallenges.PlonkZeta = gl.Uint64ArrayToQuadraticExtension(raw.PlonkZeta)
|
|
proofChallenges.FriChallenges.FriAlpha = gl.Uint64ArrayToQuadraticExtension(raw.FriChallenges.FriAlpha)
|
|
proofChallenges.FriChallenges.FriBetas = gl.Uint64ArrayToQuadraticExtensionArray(raw.FriChallenges.FriBetas)
|
|
proofChallenges.FriChallenges.FriPowResponse = gl.NewVariable(raw.FriChallenges.FriPowResponse)
|
|
proofChallenges.FriChallenges.FriQueryIndices = gl.Uint64ArrayToVariableArray(raw.FriChallenges.FriQueryIndices)
|
|
|
|
return proofChallenges
|
|
}
|
|
|
|
func ReductionArityBits(
|
|
arityBits uint64,
|
|
finalPolyBits uint64,
|
|
degreeBits uint64,
|
|
rateBits uint64,
|
|
capHeight uint64,
|
|
) []uint64 {
|
|
returnArr := make([]uint64, 0)
|
|
|
|
for degreeBits > finalPolyBits && degreeBits+rateBits-arityBits >= capHeight {
|
|
returnArr = append(returnArr, arityBits)
|
|
if degreeBits < arityBits {
|
|
panic("degreeBits < arityBits")
|
|
}
|
|
degreeBits -= arityBits
|
|
}
|
|
|
|
return returnArr
|
|
}
|
|
|
|
func DeserializeCommonCircuitData(path string) types.CommonCircuitData {
|
|
jsonFile, err := os.Open(path)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
defer jsonFile.Close()
|
|
rawBytes, _ := io.ReadAll(jsonFile)
|
|
|
|
var raw CommonCircuitDataRaw
|
|
err = json.Unmarshal(rawBytes, &raw)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
var commonCircuitData types.CommonCircuitData
|
|
commonCircuitData.Config.NumWires = raw.Config.NumWires
|
|
commonCircuitData.Config.NumRoutedWires = raw.Config.NumRoutedWires
|
|
commonCircuitData.Config.NumConstants = raw.Config.NumConstants
|
|
commonCircuitData.Config.UseBaseArithmeticGate = raw.Config.UseBaseArithmeticGate
|
|
commonCircuitData.Config.SecurityBits = raw.Config.SecurityBits
|
|
commonCircuitData.Config.NumChallenges = raw.Config.NumChallenges
|
|
commonCircuitData.Config.ZeroKnowledge = raw.Config.ZeroKnowledge
|
|
commonCircuitData.Config.MaxQuotientDegreeFactor = raw.Config.MaxQuotientDegreeFactor
|
|
|
|
commonCircuitData.Config.FriConfig.RateBits = raw.Config.FriConfig.RateBits
|
|
commonCircuitData.Config.FriConfig.CapHeight = raw.Config.FriConfig.CapHeight
|
|
commonCircuitData.Config.FriConfig.ProofOfWorkBits = raw.Config.FriConfig.ProofOfWorkBits
|
|
commonCircuitData.Config.FriConfig.NumQueryRounds = raw.Config.FriConfig.NumQueryRounds
|
|
|
|
commonCircuitData.FriParams.DegreeBits = raw.FriParams.DegreeBits
|
|
commonCircuitData.DegreeBits = raw.FriParams.DegreeBits
|
|
commonCircuitData.FriParams.Config.RateBits = raw.FriParams.Config.RateBits
|
|
commonCircuitData.FriParams.Config.CapHeight = raw.FriParams.Config.CapHeight
|
|
commonCircuitData.FriParams.Config.ProofOfWorkBits = raw.FriParams.Config.ProofOfWorkBits
|
|
commonCircuitData.FriParams.Config.NumQueryRounds = raw.FriParams.Config.NumQueryRounds
|
|
commonCircuitData.FriParams.ReductionArityBits = raw.FriParams.ReductionArityBits
|
|
|
|
commonCircuitData.Gates = []gates.Gate{}
|
|
for _, gate := range raw.Gates {
|
|
commonCircuitData.Gates = append(commonCircuitData.Gates, gates.GateInstanceFromId(gate))
|
|
}
|
|
|
|
selectorGroupStart := []uint64{}
|
|
selectorGroupEnd := []uint64{}
|
|
for _, group := range raw.SelectorsInfo.Groups {
|
|
selectorGroupStart = append(selectorGroupStart, group.Start)
|
|
selectorGroupEnd = append(selectorGroupEnd, group.End)
|
|
}
|
|
|
|
commonCircuitData.SelectorsInfo = *gates.NewSelectorsInfo(
|
|
raw.SelectorsInfo.SelectorIndices,
|
|
selectorGroupStart,
|
|
selectorGroupEnd,
|
|
)
|
|
|
|
commonCircuitData.QuotientDegreeFactor = raw.QuotientDegreeFactor
|
|
commonCircuitData.NumGateConstraints = raw.NumGateConstraints
|
|
commonCircuitData.NumConstants = raw.NumConstants
|
|
commonCircuitData.NumPublicInputs = raw.NumPublicInputs
|
|
commonCircuitData.KIs = gl.Uint64ArrayToVariableArray(raw.KIs)
|
|
commonCircuitData.NumPartialProducts = raw.NumPartialProducts
|
|
|
|
return commonCircuitData
|
|
}
|
|
|
|
func DeserializeVerifierOnlyCircuitData(path string) types.VerifierOnlyCircuitData {
|
|
jsonFile, err := os.Open(path)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
defer jsonFile.Close()
|
|
rawBytes, _ := io.ReadAll(jsonFile)
|
|
|
|
var raw VerifierOnlyCircuitDataRaw
|
|
err = json.Unmarshal(rawBytes, &raw)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
var verifierOnlyCircuitData types.VerifierOnlyCircuitData
|
|
verifierOnlyCircuitData.ConstantSigmasCap = DeserializeMerkleCap(raw.ConstantsSigmasCap)
|
|
circuitDigestBigInt, _ := new(big.Int).SetString(raw.CircuitDigest, 10)
|
|
circuitDigestVar := frontend.Variable(circuitDigestBigInt)
|
|
verifierOnlyCircuitData.CircuitDigest = poseidon.BN254HashOut(circuitDigestVar)
|
|
return verifierOnlyCircuitData
|
|
}
|