github.com/consensys/gnark-crypto@v0.14.0/ecc/bn254/fr/sumcheck/sumcheck.go (about) 1 // Copyright 2020 Consensys Software Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Code generated by consensys/gnark-crypto DO NOT EDIT 16 17 package sumcheck 18 19 import ( 20 "fmt" 21 "github.com/consensys/gnark-crypto/ecc/bn254/fr" 22 "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" 23 fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" 24 "strconv" 25 ) 26 27 // This does not make use of parallelism and represents polynomials as lists of coefficients 28 // It is currently geared towards arithmetic hashes. Once we have a more unified hash function interface, this can be generified. 29 30 // Claims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. 31 // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) 32 type Claims interface { 33 Combine(a fr.Element) polynomial.Polynomial // Combine into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. 34 Next(fr.Element) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ 35 VarsNum() int //number of variables 36 ClaimsNum() int //number of claims 37 ProveFinalEval(r []fr.Element) interface{} //in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof 38 } 39 40 // LazyClaims is the Claims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. 41 type LazyClaims interface { 42 ClaimsNum() int // ClaimsNum = m 43 VarsNum() int // VarsNum = n 44 CombinedSum(a fr.Element) fr.Element // CombinedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ 45 Degree(i int) int //Degree of the total claim in the i'th variable 46 VerifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof interface{}) error 47 } 48 49 // Proof of a multi-sumcheck statement. 50 type Proof struct { 51 PartialSumPolys []polynomial.Polynomial `json:"partialSumPolys"` 52 FinalEvalProof interface{} `json:"finalEvalProof"` //in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof 53 } 54 55 func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) (challengeNames []string, err error) { 56 numChallenges := varsNum 57 if claimsNum >= 2 { 58 numChallenges++ 59 } 60 challengeNames = make([]string, numChallenges) 61 if claimsNum >= 2 { 62 challengeNames[0] = settings.Prefix + "comb" 63 } 64 prefix := settings.Prefix + "pSP." 65 for i := 0; i < varsNum; i++ { 66 challengeNames[i+numChallenges-varsNum] = prefix + strconv.Itoa(i) 67 } 68 if settings.Transcript == nil { 69 transcript := fiatshamir.NewTranscript(settings.Hash, challengeNames...) 70 settings.Transcript = transcript 71 } 72 73 for i := range settings.BaseChallenges { 74 if err = settings.Transcript.Bind(challengeNames[0], settings.BaseChallenges[i]); err != nil { 75 return 76 } 77 } 78 return 79 } 80 81 func next(transcript *fiatshamir.Transcript, bindings []fr.Element, remainingChallengeNames *[]string) (fr.Element, error) { 82 challengeName := (*remainingChallengeNames)[0] 83 for i := range bindings { 84 bytes := bindings[i].Bytes() 85 if err := transcript.Bind(challengeName, bytes[:]); err != nil { 86 return fr.Element{}, err 87 } 88 } 89 var res fr.Element 90 bytes, err := transcript.ComputeChallenge(challengeName) 91 res.SetBytes(bytes) 92 93 *remainingChallengeNames = (*remainingChallengeNames)[1:] 94 95 return res, err 96 } 97 98 // Prove create a non-interactive sumcheck proof 99 func Prove(claims Claims, transcriptSettings fiatshamir.Settings) (Proof, error) { 100 101 var proof Proof 102 remainingChallengeNames, err := setupTranscript(claims.ClaimsNum(), claims.VarsNum(), &transcriptSettings) 103 transcript := transcriptSettings.Transcript 104 if err != nil { 105 return proof, err 106 } 107 108 var combinationCoeff fr.Element 109 if claims.ClaimsNum() >= 2 { 110 if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { 111 return proof, err 112 } 113 } 114 115 varsNum := claims.VarsNum() 116 proof.PartialSumPolys = make([]polynomial.Polynomial, varsNum) 117 proof.PartialSumPolys[0] = claims.Combine(combinationCoeff) 118 challenges := make([]fr.Element, varsNum) 119 120 for j := 0; j+1 < varsNum; j++ { 121 if challenges[j], err = next(transcript, proof.PartialSumPolys[j], &remainingChallengeNames); err != nil { 122 return proof, err 123 } 124 proof.PartialSumPolys[j+1] = claims.Next(challenges[j]) 125 } 126 127 if challenges[varsNum-1], err = next(transcript, proof.PartialSumPolys[varsNum-1], &remainingChallengeNames); err != nil { 128 return proof, err 129 } 130 131 proof.FinalEvalProof = claims.ProveFinalEval(challenges) 132 133 return proof, nil 134 } 135 136 func Verify(claims LazyClaims, proof Proof, transcriptSettings fiatshamir.Settings) error { 137 remainingChallengeNames, err := setupTranscript(claims.ClaimsNum(), claims.VarsNum(), &transcriptSettings) 138 transcript := transcriptSettings.Transcript 139 if err != nil { 140 return err 141 } 142 143 var combinationCoeff fr.Element 144 145 if claims.ClaimsNum() >= 2 { 146 if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { 147 return err 148 } 149 } 150 151 r := make([]fr.Element, claims.VarsNum()) 152 153 // Just so that there is enough room for gJ to be reused 154 maxDegree := claims.Degree(0) 155 for j := 1; j < claims.VarsNum(); j++ { 156 if d := claims.Degree(j); d > maxDegree { 157 maxDegree = d 158 } 159 } 160 gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.VarsNum() 161 gJR := claims.CombinedSum(combinationCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) 162 163 for j := 0; j < claims.VarsNum(); j++ { 164 if len(proof.PartialSumPolys[j]) != claims.Degree(j) { 165 return fmt.Errorf("malformed proof") 166 } 167 copy(gJ[1:], proof.PartialSumPolys[j]) 168 gJ[0].Sub(&gJR, &proof.PartialSumPolys[j][0]) // Requirement that gⱼ(0) + gⱼ(1) = gⱼ₋₁(r) 169 // gJ is ready 170 171 //Prepare for the next iteration 172 if r[j], err = next(transcript, proof.PartialSumPolys[j], &remainingChallengeNames); err != nil { 173 return err 174 } 175 // This is an extremely inefficient way of interpolating. TODO: Interpolate without symbolically computing a polynomial 176 gJCoeffs := polynomial.InterpolateOnRange(gJ[:(claims.Degree(j) + 1)]) 177 gJR = gJCoeffs.Eval(&r[j]) 178 } 179 180 return claims.VerifyFinalEval(r, combinationCoeff, gJR, proof.FinalEvalProof) 181 }