diff --git a/plonky2_verifier/deserialize.go b/plonky2_verifier/deserialize.go index 758b0f4..2a73141 100644 --- a/plonky2_verifier/deserialize.go +++ b/plonky2_verifier/deserialize.go @@ -32,7 +32,7 @@ type ProofWithPublicInputsRaw struct { CommitPhaseMerkleCaps []interface{} `json:"commit_phase_merkle_caps"` QueryRoundProofs []struct { InitialTreesProof struct { - EvalsProofs [][]interface{} `json:"evals_proofs"` + EvalsProofs []EvalProofRaw `json:"evals_proofs"` } `json:"initial_trees_proof"` Steps []interface{} `json:"steps"` } `json:"query_round_proofs"` @@ -45,6 +45,38 @@ type ProofWithPublicInputsRaw struct { 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 [][]uint64 +} + +func (m *MerkleProofRaw) UnmarshalJSON(data []byte) error { + var siblingDict map[string]interface{} + if err := json.Unmarshal(data, &siblingDict); err != nil { + panic(err) + } + + siblings := siblingDict["siblings"].([]interface{}) + m.hash = make([][]uint64, len(siblings)) + for siblingIdx, sibling := range siblings { + siblingHash := sibling.(map[string]interface{})["elements"].([]interface{}) + m.hash[siblingIdx] = make([]uint64, 4) + for siblingElementIdx, siblingElement := range siblingHash { + m.hash[siblingIdx][siblingElementIdx] = uint64(siblingElement.(float64)) + } + } + + return nil +} + type CommonCircuitDataRaw struct { Config struct { NumWires uint64 `json:"num_wires"` @@ -113,6 +145,17 @@ func DeserializeMerkleCap(merkleCapRaw []struct{ Elements []uint64 }) MerkleCap return merkleCap } +func DeserializeMerkleProof(merkleProofRaw struct{ Siblings []interface{} }) MerkleProof { + n := len(merkleProofRaw.Siblings) + var mp MerkleProof + mp.Siblings = make([]Hash, n) + for i := 0; i < n; i++ { + element := merkleProofRaw.Siblings[i].(struct{ Elements []uint64 }) + copy(mp.Siblings[i][:], utils.Uint64ArrayToFArray(element.Elements)) + } + return mp +} + func DeserializeOpeningSet(openingSetRaw struct { Constants [][]uint64 PlonkSigmas [][]uint64 @@ -137,7 +180,7 @@ func DeserializeFriProof(openingProofRaw struct { CommitPhaseMerkleCaps []interface{} QueryRoundProofs []struct { InitialTreesProof struct { - EvalsProofs [][]interface{} + EvalsProofs []EvalProofRaw } Steps []interface{} } @@ -149,6 +192,19 @@ func DeserializeFriProof(openingProofRaw struct { var openingProof FriProof openingProof.PowWitness = NewFieldElement(openingProofRaw.PowWitness) openingProof.FinalPoly.Coeffs = utils.Uint64ArrayToQuadraticExtensionArray(openingProofRaw.FinalPoly.Coeffs) + + numQueryRoundProofs := len(openingProofRaw.QueryRoundProofs) + openingProof.QueryRoundProofs = make([]FriQueryRound, numQueryRoundProofs) + + for i := 0; i < numQueryRoundProofs; i++ { + numEvalProofs := len(openingProofRaw.QueryRoundProofs[i].InitialTreesProof.EvalsProofs) + openingProof.QueryRoundProofs[i].InitialTreesProof.EvalsProofs = make([]EvalProof, numEvalProofs) + for j := 0; j < numEvalProofs; j++ { + openingProof.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].Elements = utils.Uint64ArrayToFArray(openingProofRaw.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].leafElements) + openingProof.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].MerkleProof.Siblings = utils.Uint64ArrayToHashArray(openingProofRaw.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].merkleProof.hash) + } + } + return openingProof } @@ -183,8 +239,10 @@ func DeserializeProofWithPublicInputs(path string) ProofWithPublicInputs { proofWithPis.Proof.OpeningProof = DeserializeFriProof(struct { CommitPhaseMerkleCaps []interface{} QueryRoundProofs []struct { - InitialTreesProof struct{ EvalsProofs [][]interface{} } - Steps []interface{} + InitialTreesProof struct { + EvalsProofs []EvalProofRaw + } + Steps []interface{} } FinalPoly struct{ Coeffs [][]uint64 } PowWitness uint64 @@ -224,7 +282,8 @@ func DeserializeCommonCircuitData(path string) CommonCircuitData { commonCircuitData.Config.FriConfig.ProofOfWorkBits = raw.Config.FriConfig.ProofOfWorkBits commonCircuitData.Config.FriConfig.NumQueryRounds = raw.Config.FriConfig.NumQueryRounds - commonCircuitData.FriParams.Config.CapHeight = raw.FriParams.Config.CapHeight + commonCircuitData.FriParams.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 diff --git a/plonky2_verifier/fri.go b/plonky2_verifier/fri.go index cb673fe..b447440 100644 --- a/plonky2_verifier/fri.go +++ b/plonky2_verifier/fri.go @@ -1,6 +1,7 @@ package plonky2_verifier import ( + "fmt" "gnark-ed25519/field" . "gnark-ed25519/field" "gnark-ed25519/poseidon" @@ -155,7 +156,9 @@ 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 { + + // TODO: Check that pAmbiguous value is the same as the one in plonky2 verifier + 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.") } } @@ -167,12 +170,11 @@ func (f *FriChip) verifyQueryRound( proof *FriProof, xIndex F, n uint64, + nLog uint64, roundProof *FriQueryRound, ) { - nLog := log2Strict(uint(n)) - f.assertNoncanonicalIndicesOK() - xIndexBits := f.qe.field.ToBinary(xIndex, nLog) + xIndexBits := f.qe.field.ToBinary(xIndex, int(nLog)) capIndex := f.qe.field.FromBinary(xIndexBits[len(xIndexBits)-int(f.friParams.Config.CapHeight):]...).(F) f.verifyInitialProof(xIndexBits, &roundProof.InitialTreesProof, initialMerkleCaps, capIndex) @@ -201,10 +203,15 @@ func (f *FriChip) VerifyFriProof( precomputedReducedEvals := f.fromOpeningsAndAlpha(&openings, friChallenges.FriAlpha) // Size of the LDE domain. - n := uint64(math.Pow(2, float64(f.friParams.DegreeBits+f.friParams.Config.RateBits))) - - if len(friChallenges.FriQueryIndicies) != len(precomputedReducedEvals) { - panic("Number of queryRoundProofs should equal number of precomputedReducedEvals") + nLog := f.friParams.DegreeBits + f.friParams.Config.RateBits + n := uint64(math.Pow(2, float64(nLog))) + + if len(friChallenges.FriQueryIndicies) != len(friProof.QueryRoundProofs) { + panic(fmt.Sprintf( + "Number of query indices (%d) should equal number of query round proofs (%d)", + len(friChallenges.FriQueryIndicies), + len(friProof.QueryRoundProofs), + )) } for idx, xIndex := range friChallenges.FriQueryIndicies { @@ -217,6 +224,7 @@ func (f *FriChip) VerifyFriProof( friProof, xIndex, n, + nLog, &roundProof, ) } diff --git a/utils/utils.go b/utils/utils.go index 43c82da..e7b52e3 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -40,3 +40,11 @@ func Uint64ArrayToQuadraticExtensionArray(input [][]uint64) []QuadraticExtension } return output } + +func Uint64ArrayToHashArray(input [][]uint64) []Hash { + var output []Hash + for i := 0; i < len(input); i++ { + output = append(output, [4]F{NewFieldElement(input[i][0]), NewFieldElement(input[i][1]), NewFieldElement(input[i][2]), NewFieldElement(input[i][3])}) + } + return output +}