github.com/consensys/gnark@v0.11.0/internal/generator/backend/template/zkpschemes/plonk/plonk.setup.go.tmpl (about)

     1  import (
     2  	{{- template "import_kzg" . }}
     3  	{{- template "import_fr" . }}
     4  	{{- template "import_fft" . }}
     5  	{{- template "import_backend_cs" . }}
     6  	"fmt"
     7  	"github.com/consensys/gnark-crypto/ecc/{{toLower .Curve}}/fr/iop"
     8  	"github.com/consensys/gnark/backend/plonk/internal"
     9  	"github.com/consensys/gnark/constraint"
    10  )
    11  
    12  // VerifyingKey stores the data needed to verify a proof:
    13  // * The commitment scheme
    14  // * Commitments of ql prepended with as many ones as there are public inputs
    15  // * Commitments of qr, qm, qo, qk prepended with as many zeroes as there are public inputs
    16  // * Commitments to S1, S2, S3
    17  type VerifyingKey struct {
    18  	// Size circuit
    19  	Size              uint64
    20  	SizeInv           fr.Element
    21  	Generator         fr.Element
    22  	NbPublicVariables uint64
    23  
    24  	// Commitment scheme that is used for an instantiation of PLONK
    25  	Kzg kzg.VerifyingKey
    26  
    27  	// cosetShift generator of the coset on the small domain
    28  	CosetShift fr.Element
    29  
    30  	// S commitments to S1, S2, S3
    31  	S [3]kzg.Digest
    32  
    33  	// Commitments to ql, qr, qm, qo, qcp prepended with as many zeroes (ones for l) as there are public inputs.
    34  	// In particular Qk is not complete.
    35  	Ql, Qr, Qm, Qo, Qk kzg.Digest
    36  	Qcp                []kzg.Digest
    37  
    38  	CommitmentConstraintIndexes []uint64
    39  }
    40  
    41  // Trace stores a plonk trace as columns
    42  type Trace struct {
    43  	// Constants describing a plonk circuit. The first entries
    44  	// of LQk (whose index correspond to the public inputs) are set to 0, and are to be
    45  	// completed by the prover. At those indices i (so from 0 to nb_public_variables), LQl[i]=-1
    46  	// so the first nb_public_variables constraints look like this:
    47  	// -1*Wire[i] + 0* + 0 . It is zero when the constant coefficient is replaced by Wire[i].
    48  	Ql, Qr, Qm, Qo, Qk *iop.Polynomial
    49  	Qcp                []*iop.Polynomial
    50  
    51  	// Polynomials representing the splitted permutation. The full permutation's support is 3*N where N=nb wires.
    52  	// The set of interpolation is <g> of size N, so to represent the permutation S we let S acts on the
    53  	// set A=(<g>, u*<g>, u^{2}*<g>) of size 3*N, where u is outside <g> (its use is to shift the set <g>).
    54  	// We obtain a permutation of A, A'. We split A' in 3 (A'_{1}, A'_{2}, A'_{3}), and S1, S2, S3 are
    55  	// respectively the interpolation of A'_{1}, A'_{2}, A'_{3} on <g>.
    56  	S1, S2, S3 *iop.Polynomial
    57  
    58  	// S full permutation, i -> S[i]
    59  	S []int64
    60  }
    61  
    62  // ProvingKey stores the data needed to generate a proof
    63  type ProvingKey struct {
    64  	Kzg, KzgLagrange kzg.ProvingKey
    65  
    66  	// Verifying Key is embedded into the proving key (needed by Prove)
    67  	Vk *VerifyingKey
    68  }
    69  
    70  func Setup(spr *cs.SparseR1CS, srs, srsLagrange kzg.SRS) (*ProvingKey, *VerifyingKey, error) {
    71  
    72  	var pk ProvingKey
    73  	var vk VerifyingKey
    74  	pk.Vk = &vk
    75  	vk.CommitmentConstraintIndexes = internal.IntSliceToUint64Slice(spr.CommitmentInfo.CommitmentIndexes())
    76  
    77  	// step 0: set the fft domains
    78  	domain := initFFTDomain(spr)
    79  	if domain.Cardinality < 2 {
    80  		return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", len(spr.Public)+spr.GetNbConstraints())
    81  	}
    82  
    83  	// check the size of the kzg srs.
    84  	if len(srs.Pk.G1) < (int(domain.Cardinality) + 3) { // + 3 for the kzg.Open of blinded poly
    85  		return nil, nil, fmt.Errorf("kzg srs is too small: got %d, need %d", len(srs.Pk.G1), domain.Cardinality+3)
    86  	}
    87  
    88  	// same for the lagrange form
    89  	if len(srsLagrange.Pk.G1) != int(domain.Cardinality) {
    90  		return nil, nil, fmt.Errorf("kzg srs lagrange is too small: got %d, need %d", len(srsLagrange.Pk.G1), domain.Cardinality)
    91  	}
    92  
    93  	// step 1: set the verifying key
    94  	vk.CosetShift.Set(&domain.FrMultiplicativeGen)
    95  	vk.Size = domain.Cardinality
    96  	vk.SizeInv.SetUint64(vk.Size).Inverse(&vk.SizeInv)
    97  	vk.Generator.Set(&domain.Generator)
    98  	vk.NbPublicVariables = uint64(len(spr.Public))
    99  
   100  	pk.Kzg.G1 = srs.Pk.G1[:int(vk.Size)+3]
   101  	pk.KzgLagrange.G1 = srsLagrange.Pk.G1
   102  	vk.Kzg = srs.Vk
   103  
   104  	// step 2: ql, qr, qm, qo, qk, qcp in Lagrange Basis
   105  	// step 3: build the permutation and build the polynomials S1, S2, S3 to encode the permutation.
   106  	// Note: at this stage, the permutation takes in account the placeholders
   107  	trace := NewTrace(spr, domain)
   108  
   109  	// step 4: commit to s1, s2, s3, ql, qr, qm, qo, and (the incomplete version of) qk.
   110  	// All the above polynomials are expressed in canonical basis afterwards. This is why
   111  	// we save lqk before, because the prover needs to complete it in Lagrange form, and
   112  	// then express it on the Lagrange coset basis.
   113  	if err := vk.commitTrace(trace, domain, pk.KzgLagrange); err != nil {
   114  		return nil, nil, err
   115  	}
   116  
   117  	return &pk, &vk, nil
   118  }
   119  
   120  // NbPublicWitness returns the expected public witness size (number of field elements)
   121  func (vk *VerifyingKey) NbPublicWitness() int {
   122  	return int(vk.NbPublicVariables)
   123  }
   124  
   125  // VerifyingKey returns pk.Vk
   126  func (pk *ProvingKey) VerifyingKey() interface{} {
   127  	return pk.Vk
   128  }
   129  
   130  // NewTrace returns a new Trace object from the constraint system.
   131  // It fills the constant columns ql, qr, qm, qo, qk, and qcp with the
   132  // coefficients of the constraints.
   133  // Size is the size of the system that is next power of 2 (nb_constraints+nb_public_variables)
   134  // The permutation is also computed and stored in the Trace.
   135  func NewTrace(spr *cs.SparseR1CS, domain *fft.Domain) *Trace {
   136  	var trace Trace
   137  
   138  	size := int(domain.Cardinality)
   139  	commitmentInfo := spr.CommitmentInfo.(constraint.PlonkCommitments)
   140  
   141  	ql := make([]fr.Element, size)
   142  	qr := make([]fr.Element, size)
   143  	qm := make([]fr.Element, size)
   144  	qo := make([]fr.Element, size)
   145  	qk := make([]fr.Element, size)
   146  	qcp := make([][]fr.Element, len(commitmentInfo))
   147  
   148  	for i := 0; i < len(spr.Public); i++ { // placeholders (-PUB_INPUT_i + qk_i = 0) TODO should return error if size is inconsistent
   149  		ql[i].SetOne().Neg(&ql[i])
   150  		qr[i].SetZero()
   151  		qm[i].SetZero()
   152  		qo[i].SetZero()
   153  		qk[i].SetZero() // → to be completed by the prover
   154  	}
   155  	offset := len(spr.Public)
   156  
   157  	j := 0
   158  	it := spr.GetSparseR1CIterator()
   159  	for c := it.Next(); c != nil; c = it.Next() {
   160  		ql[offset+j].Set(&spr.Coefficients[c.QL])
   161  		qr[offset+j].Set(&spr.Coefficients[c.QR])
   162  		qm[offset+j].Set(&spr.Coefficients[c.QM])
   163  		qo[offset+j].Set(&spr.Coefficients[c.QO])
   164  		qk[offset+j].Set(&spr.Coefficients[c.QC])
   165  		j++
   166  	}
   167  
   168  	lagReg := iop.Form{Basis: iop.Lagrange, Layout: iop.Regular}
   169  
   170  	trace.Ql = iop.NewPolynomial(&ql, lagReg)
   171  	trace.Qr = iop.NewPolynomial(&qr, lagReg)
   172  	trace.Qm = iop.NewPolynomial(&qm, lagReg)
   173  	trace.Qo = iop.NewPolynomial(&qo, lagReg)
   174  	trace.Qk = iop.NewPolynomial(&qk, lagReg)
   175  	trace.Qcp = make([]*iop.Polynomial, len(qcp))
   176  
   177  	for i := range commitmentInfo {
   178  		qcp[i] = make([]fr.Element, size)
   179  		for _, committed := range commitmentInfo[i].Committed {
   180  			qcp[i][offset+committed].SetOne()
   181  		}
   182  		trace.Qcp[i] = iop.NewPolynomial(&qcp[i], lagReg)
   183  	}
   184  
   185  	// build the permutation and build the polynomials S1, S2, S3 to encode the permutation.
   186  	// Note: at this stage, the permutation takes in account the placeholders
   187  	nbVariables := spr.NbInternalVariables + len(spr.Public) + len(spr.Secret)
   188  	buildPermutation(spr, &trace, nbVariables)
   189  	s := computePermutationPolynomials(&trace, domain)
   190  	trace.S1 = s[0]
   191  	trace.S2 = s[1]
   192  	trace.S3 = s[2]
   193  
   194  	return &trace
   195  }
   196  
   197  // commitTrace commits to every polynomial in the trace, and put
   198  // the commitments int the verifying key.
   199  func (vk *VerifyingKey) commitTrace(trace *Trace, domain *fft.Domain, srsPk kzg.ProvingKey) error {
   200  
   201  
   202  	var err error
   203  	vk.Qcp = make([]kzg.Digest, len(trace.Qcp))
   204  	for i := range trace.Qcp {
   205  		if vk.Qcp[i], err = kzg.Commit(trace.Qcp[i].Coefficients(), srsPk); err != nil {
   206  			return err
   207  		}
   208  	}
   209  	if vk.Ql, err = kzg.Commit(trace.Ql.Coefficients(), srsPk); err != nil {
   210  		return err
   211  	}
   212  	if vk.Qr, err = kzg.Commit(trace.Qr.Coefficients(), srsPk); err != nil {
   213  		return err
   214  	}
   215  	if vk.Qm, err = kzg.Commit(trace.Qm.Coefficients(), srsPk); err != nil {
   216  		return err
   217  	}
   218  	if vk.Qo, err = kzg.Commit(trace.Qo.Coefficients(), srsPk); err != nil {
   219  		return err
   220  	}
   221  	if vk.Qk, err = kzg.Commit(trace.Qk.Coefficients(), srsPk); err != nil {
   222  		return err
   223  	}
   224  	if vk.S[0], err = kzg.Commit(trace.S1.Coefficients(), srsPk); err != nil {
   225  		return err
   226  	}
   227  	if vk.S[1], err = kzg.Commit(trace.S2.Coefficients(), srsPk); err != nil {
   228  		return err
   229  	}
   230  	if vk.S[2], err = kzg.Commit(trace.S3.Coefficients(), srsPk); err != nil {
   231  		return err
   232  	}
   233  	return nil
   234  }
   235  
   236  func initFFTDomain(spr *cs.SparseR1CS) *fft.Domain {
   237  	nbConstraints := spr.GetNbConstraints()
   238  	sizeSystem := uint64(nbConstraints + len(spr.Public)) // len(spr.Public) is for the placeholder constraints
   239  	return fft.NewDomain(sizeSystem, fft.WithoutPrecompute())
   240  }
   241  
   242  // buildPermutation builds the Permutation associated with a circuit.
   243  //
   244  // The permutation s is composed of cycles of maximum length such that
   245  //
   246  //	s. (l∥r∥o) = (l∥r∥o)
   247  //
   248  // , where l∥r∥o is the concatenation of the indices of l, r, o in
   249  // ql.l+qr.r+qm.l.r+qo.O+k = 0.
   250  //
   251  // The permutation is encoded as a slice s of size 3*size(l), where the
   252  // i-th entry of l∥r∥o is sent to the s[i]-th entry, so it acts on a tab
   253  // like this: for i in tab: tab[i] = tab[permutation[i]]
   254  func buildPermutation(spr *cs.SparseR1CS, trace *Trace, nbVariables int) {
   255  
   256  	// nbVariables := spr.NbInternalVariables + len(spr.Public) + len(spr.Secret)
   257  	sizeSolution := len(trace.Ql.Coefficients())
   258  	sizePermutation := 3 * sizeSolution
   259  
   260  	// init permutation
   261  	permutation := make([]int64, sizePermutation)
   262  	for i := 0; i < len(permutation); i++ {
   263  		permutation[i] = -1
   264  	}
   265  
   266  	// init LRO position -> variable_ID
   267  	lro := make([]int, sizePermutation) // position -> variable_ID
   268  	for i := 0; i < len(spr.Public); i++ {
   269  		lro[i] = i // IDs of LRO associated to placeholders (only L needs to be taken care of)
   270  	}
   271  
   272  	offset := len(spr.Public)
   273  
   274  	j := 0
   275  	it := spr.GetSparseR1CIterator()
   276  	for c := it.Next(); c != nil; c = it.Next() {
   277  		lro[offset+j] = int(c.XA)
   278  		lro[sizeSolution+offset+j] = int(c.XB)
   279  		lro[2*sizeSolution+offset+j] = int(c.XC)
   280  
   281  		j++
   282  	}
   283  
   284  	// init cycle:
   285  	// map ID -> last position the ID was seen
   286  	cycle := make([]int64, nbVariables)
   287  	for i := 0; i < len(cycle); i++ {
   288  		cycle[i] = -1
   289  	}
   290  
   291  	for i := 0; i < len(lro); i++ {
   292  		if cycle[lro[i]] != -1 {
   293  			// if != -1, it means we already encountered this value
   294  			// so we need to set the corresponding permutation index.
   295  			permutation[i] = cycle[lro[i]]
   296  		}
   297  		cycle[lro[i]] = int64(i)
   298  	}
   299  
   300  	// complete the Permutation by filling the first IDs encountered
   301  	for i := 0; i < sizePermutation; i++ {
   302  		if permutation[i] == -1 {
   303  			permutation[i] = cycle[lro[i]]
   304  		}
   305  	}
   306  
   307  	trace.S = permutation
   308  }
   309  
   310  // computePermutationPolynomials computes the LDE (Lagrange basis) of the permutation.
   311  // We let the permutation act on <g> || u<g> || u^{2}<g>, split the result in 3 parts,
   312  // and interpolate each of the 3 parts on <g>.
   313  func computePermutationPolynomials(trace *Trace, domain *fft.Domain) [3]*iop.Polynomial {
   314  
   315  	nbElmts := int(domain.Cardinality)
   316  
   317  	var res [3]*iop.Polynomial
   318  
   319  	// Lagrange form of ID
   320  	evaluationIDSmallDomain := getSupportPermutation(domain)
   321  
   322  	// Lagrange form of S1, S2, S3
   323  	s1Canonical := make([]fr.Element, nbElmts)
   324  	s2Canonical := make([]fr.Element, nbElmts)
   325  	s3Canonical := make([]fr.Element, nbElmts)
   326  	for i := 0; i < nbElmts; i++ {
   327  		s1Canonical[i].Set(&evaluationIDSmallDomain[trace.S[i]])
   328  		s2Canonical[i].Set(&evaluationIDSmallDomain[trace.S[nbElmts+i]])
   329  		s3Canonical[i].Set(&evaluationIDSmallDomain[trace.S[2*nbElmts+i]])
   330  	}
   331  
   332  	lagReg := iop.Form{Basis: iop.Lagrange, Layout: iop.Regular}
   333  	res[0] = iop.NewPolynomial(&s1Canonical, lagReg)
   334  	res[1] = iop.NewPolynomial(&s2Canonical, lagReg)
   335  	res[2] = iop.NewPolynomial(&s3Canonical, lagReg)
   336  
   337  	return res
   338  }
   339  
   340  // getSupportPermutation returns the support on which the permutation acts, it is
   341  // <g> || u<g> || u^{2}<g>
   342  func getSupportPermutation(domain *fft.Domain) []fr.Element {
   343  
   344  	res := make([]fr.Element, 3*domain.Cardinality)
   345  
   346  	res[0].SetOne()
   347  	res[domain.Cardinality].Set(&domain.FrMultiplicativeGen)
   348  	res[2*domain.Cardinality].Square(&domain.FrMultiplicativeGen)
   349  
   350  	for i := uint64(1); i < domain.Cardinality; i++ {
   351  		res[i].Mul(&res[i-1], &domain.Generator)
   352  		res[domain.Cardinality+i].Mul(&res[domain.Cardinality+i-1], &domain.Generator)
   353  		res[2*domain.Cardinality+i].Mul(&res[2*domain.Cardinality+i-1], &domain.Generator)
   354  	}
   355  
   356  	return res
   357  }