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  }