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 }