github.com/consensys/gnark-crypto@v0.14.0/internal/generator/sumcheck/template/sumcheck.go.tmpl (about) 1 import ( 2 "fmt" 3 "{{.FieldPackagePath}}" 4 "{{.FieldPackagePath}}/polynomial" 5 fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" 6 "strconv" 7 ) 8 9 // This does not make use of parallelism and represents polynomials as lists of coefficients 10 // It is currently geared towards arithmetic hashes. Once we have a more unified hash function interface, this can be generified. 11 12 // Claims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. 13 // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) 14 type Claims interface { 15 Combine(a {{.ElementType}}) 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₁. 16 Next({{.ElementType}}) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ 17 VarsNum() int //number of variables 18 ClaimsNum() int //number of claims 19 ProveFinalEval(r []{{.ElementType}}) 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 20 } 21 22 // LazyClaims is the Claims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. 23 type LazyClaims interface { 24 ClaimsNum() int // ClaimsNum = m 25 VarsNum() int // VarsNum = n 26 CombinedSum(a {{.ElementType}}) {{.ElementType}} // CombinedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ 27 Degree(i int) int //Degree of the total claim in the i'th variable 28 VerifyFinalEval(r []{{.ElementType}}, combinationCoeff {{.ElementType}}, purportedValue {{.ElementType}}, proof interface{}) error 29 } 30 31 // Proof of a multi-sumcheck statement. 32 type Proof struct { 33 PartialSumPolys []polynomial.Polynomial `json:"partialSumPolys"` 34 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 35 } 36 37 func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) (challengeNames []string, err error) { 38 numChallenges := varsNum 39 if claimsNum >= 2 { 40 numChallenges++ 41 } 42 challengeNames = make([]string, numChallenges) 43 if claimsNum >= 2 { 44 challengeNames[0] = settings.Prefix + "comb" 45 } 46 prefix := settings.Prefix + "pSP." 47 for i := 0; i < varsNum; i++ { 48 challengeNames[i+numChallenges-varsNum] = prefix + strconv.Itoa(i) 49 } 50 if settings.Transcript == nil { 51 transcript := fiatshamir.NewTranscript(settings.Hash, challengeNames...) 52 settings.Transcript = transcript 53 } 54 55 for i := range settings.BaseChallenges { 56 if err = settings.Transcript.Bind(challengeNames[0], settings.BaseChallenges[i]); err != nil { 57 return 58 } 59 } 60 return 61 } 62 63 func next(transcript *fiatshamir.Transcript, bindings []{{.ElementType}}, remainingChallengeNames *[]string) ({{.ElementType}}, error) { 64 challengeName := (*remainingChallengeNames)[0] 65 for i := range bindings { 66 bytes := bindings[i].Bytes() 67 if err := transcript.Bind(challengeName, bytes[:]); err != nil { 68 return {{.ElementType}}{}, err 69 } 70 } 71 var res {{.ElementType}} 72 bytes, err := transcript.ComputeChallenge(challengeName) 73 res.SetBytes(bytes) 74 75 *remainingChallengeNames = (*remainingChallengeNames)[1:] 76 77 return res, err 78 } 79 80 // Prove create a non-interactive sumcheck proof 81 func Prove(claims Claims, transcriptSettings fiatshamir.Settings) (Proof, error) { 82 83 var proof Proof 84 remainingChallengeNames, err := setupTranscript(claims.ClaimsNum(), claims.VarsNum(), &transcriptSettings) 85 transcript := transcriptSettings.Transcript 86 if err != nil { 87 return proof, err 88 } 89 90 var combinationCoeff {{.ElementType}} 91 if claims.ClaimsNum() >= 2 { 92 if combinationCoeff, err = next(transcript, []{{.ElementType}}{}, &remainingChallengeNames); err != nil { 93 return proof, err 94 } 95 } 96 97 varsNum := claims.VarsNum() 98 proof.PartialSumPolys = make([]polynomial.Polynomial, varsNum) 99 proof.PartialSumPolys[0] = claims.Combine(combinationCoeff) 100 challenges := make([]{{.ElementType}}, varsNum) 101 102 for j := 0; j+1 < varsNum; j++ { 103 if challenges[j], err = next(transcript, proof.PartialSumPolys[j], &remainingChallengeNames); err != nil { 104 return proof, err 105 } 106 proof.PartialSumPolys[j+1] = claims.Next(challenges[j]) 107 } 108 109 if challenges[varsNum-1], err = next(transcript, proof.PartialSumPolys[varsNum-1], &remainingChallengeNames); err != nil { 110 return proof, err 111 } 112 113 proof.FinalEvalProof = claims.ProveFinalEval(challenges) 114 115 return proof, nil 116 } 117 118 func Verify(claims LazyClaims, proof Proof, transcriptSettings fiatshamir.Settings) error { 119 remainingChallengeNames, err := setupTranscript(claims.ClaimsNum(), claims.VarsNum(), &transcriptSettings) 120 transcript := transcriptSettings.Transcript 121 if err != nil { 122 return err 123 } 124 125 var combinationCoeff {{.ElementType}} 126 127 if claims.ClaimsNum() >= 2 { 128 if combinationCoeff, err = next(transcript, []{{.ElementType}}{}, &remainingChallengeNames); err != nil { 129 return err 130 } 131 } 132 133 r := make([]{{.ElementType}}, claims.VarsNum()) 134 135 // Just so that there is enough room for gJ to be reused 136 maxDegree := claims.Degree(0) 137 for j := 1; j < claims.VarsNum(); j++ { 138 if d := claims.Degree(j); d > maxDegree { 139 maxDegree = d 140 } 141 } 142 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() 143 gJR := claims.CombinedSum(combinationCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) 144 145 for j := 0; j < claims.VarsNum(); j++ { 146 if len(proof.PartialSumPolys[j]) != claims.Degree(j) { 147 return fmt.Errorf("malformed proof") 148 } 149 copy(gJ[1:], proof.PartialSumPolys[j]) 150 gJ[0].Sub(&gJR, &proof.PartialSumPolys[j][0]) // Requirement that gⱼ(0) + gⱼ(1) = gⱼ₋₁(r) 151 // gJ is ready 152 153 //Prepare for the next iteration 154 if r[j], err = next(transcript, proof.PartialSumPolys[j], &remainingChallengeNames); err != nil { 155 return err 156 } 157 // This is an extremely inefficient way of interpolating. TODO: Interpolate without symbolically computing a polynomial 158 gJCoeffs := polynomial.InterpolateOnRange(gJ[:(claims.Degree(j) + 1)]) 159 gJR = gJCoeffs.Eval(&r[j]) 160 } 161 162 return claims.VerifyFinalEval(r, combinationCoeff, gJR, proof.FinalEvalProof) 163 }