github.com/consensys/gnark-crypto@v0.14.0/ecc/bw6-633/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/bw6-633/fr"
    22  	"github.com/consensys/gnark-crypto/ecc/bw6-633/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  }