github.com/consensys/gnark-crypto@v0.14.0/internal/generator/pedersen/template/pedersen.go.tmpl (about) 1 import ( 2 "crypto/rand" 3 "errors" 4 "github.com/consensys/gnark-crypto/ecc" 5 curve "github.com/consensys/gnark-crypto/ecc/{{.Name}}" 6 "github.com/consensys/gnark-crypto/ecc/{{.Name}}/fr" 7 "io" 8 "math/big" 9 ) 10 11 // ProvingKey for committing and proofs of knowledge 12 type ProvingKey struct { 13 Basis []curve.G1Affine 14 BasisExpSigma []curve.G1Affine // basisExpSigma[i] = Basis[i]^{σ} 15 } 16 17 type VerifyingKey struct { 18 G curve.G2Affine 19 GSigma curve.G2Affine // GRootSigmaNeg = G^{-σ} 20 } 21 22 func randomFrSizedBytes() ([]byte, error) { 23 res := make([]byte, fr.Bytes) 24 _, err := rand.Read(res) 25 return res, err 26 } 27 28 type setupConfig struct { 29 g2Gen *curve.G2Affine 30 } 31 32 // SetupOption allows to customize Pedersen vector commitment setup. 33 type SetupOption func(cfg *setupConfig) 34 35 // WithG2Point allows to set the G2 generator for the Pedersen vector commitment 36 // setup. If this is not set, we sample a random G2 point. 37 func WithG2Point(g2 curve.G2Affine) SetupOption { 38 return func(cfg *setupConfig) { 39 cfg.g2Gen = &g2 40 } 41 } 42 43 // Setup generates the proving keys for Pedersen commitments over the given 44 // bases allowing for batch proving. The common verifying key can be used to 45 // verify the batched proof of knowledge. 46 // 47 // By default the G2 generator is sampled randomly. This can be overridden by 48 // providing a custom G2 generator using [WithG2Point] option. 49 // 50 // The input bases do not have to be of the same length for individual 51 // committing and proving. The elements in bases[i] should be linearly 52 // independent of each other. Otherwise the prover may be able to construct 53 // multiple valid openings for a commitment. 54 // 55 // NB! This is a trusted setup process. The randomness during the setup must be discarded. 56 // Failing to do so allows to create proofs without knowing the committed values. 57 func Setup(bases [][]curve.G1Affine, options ...SetupOption) (pk []ProvingKey, vk VerifyingKey, err error) { 58 var cfg setupConfig 59 for _, o := range options { 60 o(&cfg) 61 } 62 if cfg.g2Gen == nil { 63 if vk.G, err = curve.RandomOnG2(); err != nil { 64 return 65 } 66 } else { 67 vk.G = *cfg.g2Gen 68 } 69 70 var modMinusOne big.Int 71 modMinusOne.Sub(fr.Modulus(), big.NewInt(1)) 72 var sigma *big.Int 73 if sigma, err = rand.Int(rand.Reader, &modMinusOne); err != nil { 74 return 75 } 76 sigma.Add(sigma, big.NewInt(1)) 77 78 sigmaNeg := new(big.Int).Neg(sigma) 79 vk.GSigma.ScalarMultiplication(&vk.G, sigmaNeg) 80 81 pk = make([]ProvingKey, len(bases)) 82 for i := range bases { 83 pk[i].BasisExpSigma = make([]curve.G1Affine, len(bases[i])) 84 for j := range bases[i] { 85 pk[i].BasisExpSigma[j].ScalarMultiplication(&bases[i][j], sigma) 86 } 87 pk[i].Basis = bases[i] 88 } 89 return 90 } 91 92 // ProveKnowledge generates a proof of knowledge of a commitment to the given 93 // values over proving key's basis. 94 func (pk *ProvingKey) ProveKnowledge(values []fr.Element) (pok curve.G1Affine, err error) { 95 if len(values) != len(pk.Basis) { 96 err = errors.New("must have as many values as basis elements") 97 return 98 } 99 100 // TODO @gbotrel this will spawn more than one task, see 101 // https://github.com/ConsenSys/gnark-crypto/issues/269 102 config := ecc.MultiExpConfig{ 103 NbTasks: 1, // TODO Experiment 104 } 105 106 _, err = pok.MultiExp(pk.BasisExpSigma, values, config) 107 return 108 } 109 110 // Commit computes a commitment to the values over proving key's basis 111 func (pk *ProvingKey) Commit(values []fr.Element) (commitment curve.G1Affine, err error) { 112 113 if len(values) != len(pk.Basis) { 114 err = errors.New("must have as many values as basis elements") 115 return 116 } 117 118 // TODO @gbotrel this will spawn more than one task, see 119 // https://github.com/ConsenSys/gnark-crypto/issues/269 120 config := ecc.MultiExpConfig{ 121 NbTasks: 1, 122 } 123 _, err = commitment.MultiExp(pk.Basis, values, config) 124 125 return 126 } 127 128 // BatchProve computes a single proof of knowledge for multiple commitments. The 129 // single PoK can be verified with a single call to [VerifyingKey.Verify] with 130 // folded commitments. The commitments can be folded into one using [curve.G1Affine.Fold]. 131 // 132 // The argument combinationCoeff is used as a linear combination coefficient to 133 // fold separate proofs into one. It must be the same for batch proving and when 134 // folding commitments. This means that in an interactive setting, it must be 135 // randomly generated by the verifier and sent to the prover. Otherwise, it must 136 // be generated via Fiat-Shamir. 137 func BatchProve(pk []ProvingKey, values [][]fr.Element, combinationCoeff fr.Element) (pok curve.G1Affine, err error) { 138 if len(pk) != len(values) { 139 err = errors.New("must have as many value vectors as bases") 140 return 141 } 142 143 if len(pk) == 1 { // no need to fold 144 pok, err = pk[0].ProveKnowledge(values[0]) 145 return 146 } else if len(pk) == 0 { // nothing to do at all 147 return 148 } 149 150 offset := 0 151 for i := range pk { 152 if len(values[i]) != len(pk[i].Basis) { 153 err = errors.New("must have as many values as basis elements") 154 return 155 } 156 offset += len(values[i]) 157 } 158 159 // prepare one amalgamated MSM 160 scaledValues := make([]fr.Element, offset) 161 basis := make([]curve.G1Affine, offset) 162 163 copy(basis, pk[0].BasisExpSigma) // #nosec G602 false positive 164 copy(scaledValues, values[0]) // #nosec G602 false positive 165 166 offset = len(values[0]) // #nosec G602 false positive 167 rI := combinationCoeff 168 for i := 1; i < len(pk); i++ { 169 copy(basis[offset:], pk[i].BasisExpSigma) 170 for j := range pk[i].Basis { 171 scaledValues[offset].Mul(&values[i][j], &rI) 172 offset++ 173 } 174 if i+1 < len(pk) { 175 rI.Mul(&rI, &combinationCoeff) 176 } 177 } 178 179 // TODO @gbotrel this will spawn more than one task, see 180 // https://github.com/ConsenSys/gnark-crypto/issues/269 181 config := ecc.MultiExpConfig{ 182 NbTasks: 1, 183 } 184 185 _, err = pok.MultiExp(basis, scaledValues, config) 186 return 187 } 188 189 // Verify checks if the proof of knowledge is valid for a given commitment. 190 func (vk *VerifyingKey) Verify(commitment curve.G1Affine, knowledgeProof curve.G1Affine) error { 191 192 if !commitment.IsInSubGroup() || !knowledgeProof.IsInSubGroup() { 193 return errors.New("subgroup check failed") 194 } 195 196 if isOne, err := curve.PairingCheck([]curve.G1Affine{commitment, knowledgeProof}, []curve.G2Affine{vk.GSigma, vk.G}); err != nil { 197 return err 198 } else if !isOne { 199 return errors.New("proof rejected") 200 } 201 return nil 202 } 203 204 // BatchVerifyMultiVk verifies multiple separate proofs of knowledge using n+1 205 // pairings instead of 2n pairings. 206 // 207 // The verifying keys may be from different setup ceremonies, but the G2 point 208 // must be the same. This can be enforced using [WithG2Point] option during 209 // setup. 210 // 211 // The argument combinationCoeff is used as a linear combination coefficient to 212 // fold separate proofs into one. This means that in an interactive setting, it 213 // must be randomly generated by the verifier and sent to the prover. Otherwise, 214 // it must be generated via Fiat-Shamir. 215 // 216 // The prover can fold the proofs using [curve.G1Affine.Fold] itself using the 217 // random challenge, providing the verifier only the folded proof. In this case 218 // the argument pok should contain only the single folded proof. 219 func BatchVerifyMultiVk(vk []VerifyingKey, commitments []curve.G1Affine, pok []curve.G1Affine, combinationCoeff fr.Element) error { 220 if len(commitments) != len(vk) { 221 return errors.New("commitments length mismatch") 222 } 223 // we use folded POK if provided 224 if len(vk) != len(pok) && len(pok) != 1 { 225 return errors.New("pok length mismatch") 226 } 227 for i := range commitments { 228 if !commitments[i].IsInSubGroup() { 229 return errors.New("commitment subgroup check failed") 230 } 231 if i != 0 && vk[i].G != vk[0].G { 232 return errors.New("parameter mismatch: G2 element") 233 } 234 } 235 for i := range pok { 236 if !pok[i].IsInSubGroup() { 237 return errors.New("pok subgroup check failed") 238 } 239 } 240 241 pairingG1 := make([]curve.G1Affine, len(vk)+1) 242 pairingG2 := make([]curve.G2Affine, len(vk)+1) 243 r := combinationCoeff 244 pairingG1[0] = commitments[0] 245 var rI big.Int 246 for i := range vk { 247 pairingG2[i] = vk[i].GSigma 248 if i != 0 { 249 r.BigInt(&rI) 250 pairingG1[i].ScalarMultiplication(&commitments[i], &rI) 251 if i+1 != len(vk) { 252 r.Mul(&r, &combinationCoeff) 253 } 254 } 255 } 256 if foldedPok, err := new(curve.G1Affine).Fold(pok, combinationCoeff, ecc.MultiExpConfig{NbTasks: 1}); err != nil { 257 return err 258 } else { 259 pairingG1[len(vk)] = *foldedPok 260 } 261 pairingG2[len(vk)] = vk[0].G 262 263 if isOne, err := curve.PairingCheck(pairingG1, pairingG2); err != nil { 264 return err 265 } else if !isOne { 266 return errors.New("proof rejected") 267 } 268 return nil 269 } 270 271 // Marshal 272 273 func (pk *ProvingKey) writeTo(enc *curve.Encoder) (int64, error) { 274 if err := enc.Encode(pk.Basis); err != nil { 275 return enc.BytesWritten(), err 276 } 277 278 err := enc.Encode(pk.BasisExpSigma) 279 280 return enc.BytesWritten(), err 281 } 282 283 func (pk *ProvingKey) WriteTo(w io.Writer) (int64, error) { 284 return pk.writeTo(curve.NewEncoder(w)) 285 } 286 287 func (pk *ProvingKey) WriteRawTo(w io.Writer) (int64, error) { 288 return pk.writeTo(curve.NewEncoder(w, curve.RawEncoding())) 289 } 290 291 func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { 292 dec := curve.NewDecoder(r) 293 294 if err := dec.Decode(&pk.Basis); err != nil { 295 return dec.BytesRead(), err 296 } 297 if err := dec.Decode(&pk.BasisExpSigma); err != nil { 298 return dec.BytesRead(), err 299 } 300 301 if len(pk.Basis) != len(pk.BasisExpSigma) { 302 return dec.BytesRead(), errors.New("commitment/proof length mismatch") 303 } 304 305 return dec.BytesRead(), nil 306 } 307 308 func (vk *VerifyingKey) WriteTo(w io.Writer) (int64, error) { 309 return vk.writeTo(curve.NewEncoder(w)) 310 } 311 312 func (vk *VerifyingKey) WriteRawTo(w io.Writer) (int64, error) { 313 return vk.writeTo(curve.NewEncoder(w, curve.RawEncoding())) 314 } 315 316 func (vk *VerifyingKey) writeTo(enc *curve.Encoder) (int64, error) { 317 var err error 318 319 if err = enc.Encode(&vk.G); err != nil { 320 return enc.BytesWritten(), err 321 } 322 err = enc.Encode(&vk.GSigma) 323 return enc.BytesWritten(), err 324 } 325 326 func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { 327 return vk.readFrom(r) 328 } 329 330 func (vk *VerifyingKey) UnsafeReadFrom(r io.Reader) (int64, error) { 331 return vk.readFrom(r, curve.NoSubgroupChecks()) 332 } 333 334 func (vk *VerifyingKey) readFrom(r io.Reader, decOptions ...func(*curve.Decoder)) (int64, error) { 335 dec := curve.NewDecoder(r, decOptions...) 336 var err error 337 338 if err = dec.Decode(&vk.G); err != nil { 339 return dec.BytesRead(), err 340 } 341 err = dec.Decode(&vk.GSigma) 342 return dec.BytesRead(), err 343 }