github.com/consensys/gnark-crypto@v0.14.0/internal/generator/permutation/template/permutation.go.tmpl (about)

     1  import (
     2  	"crypto/sha256"
     3  	"errors"
     4  	"math/big"
     5  	"math/bits"
     6  
     7  	"github.com/consensys/gnark-crypto/ecc/{{ .Name }}"
     8  	"github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr"
     9  	"github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr/fft"
    10  	"github.com/consensys/gnark-crypto/ecc/{{ .Name }}/kzg"
    11  	fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir"
    12  )
    13  
    14  var (
    15  	ErrIncompatibleSize = errors.New("t1 and t2 should be of the same size")
    16  	ErrSize             = errors.New("t1 and t2 should be of size a power of 2")
    17  	ErrPermutationProof = errors.New("permutation proof verification failed")
    18  	ErrGenerator        = errors.New("wrong generator")
    19  )
    20  
    21  // Proof proof that the commitments of t1 and t2 come from
    22  // the same vector but permuted.
    23  type Proof struct {
    24  
    25  	// size of the polynomials
    26  	size int
    27  
    28  	// generator of the fft domain, used for shifting the evaluation point
    29  	g fr.Element
    30  
    31  	// commitments of t1 & t2, the permuted vectors, and z, the accumulation
    32  	// polynomial
    33  	t1, t2, z kzg.Digest
    34  
    35  	// commitment to the quotient polynomial
    36  	q kzg.Digest
    37  
    38  	// opening proofs of t1, t2, z, q (in that order)
    39  	batchedProof kzg.BatchOpeningProof
    40  
    41  	// shifted opening proof of z
    42  	shiftedProof kzg.OpeningProof
    43  }
    44  
    45  // evaluateAccumulationPolynomialBitReversed returns the accumulation polynomial in Lagrange basis.
    46  func evaluateAccumulationPolynomialBitReversed(lt1, lt2 []fr.Element, epsilon fr.Element) []fr.Element {
    47  
    48  	s := len(lt1)
    49  	z := make([]fr.Element, s)
    50  	d := make([]fr.Element, s)
    51  	z[0].SetOne()
    52  	d[0].SetOne()
    53  	nn := uint64(64 - bits.TrailingZeros64(uint64(s)))
    54  	var t fr.Element
    55  	for i := 0; i < s-1; i++ {
    56  		_i := int(bits.Reverse64(uint64(i)) >> nn)
    57  		_ii := int(bits.Reverse64(uint64((i+1)%s)) >> nn)
    58  		z[_ii].Mul(&z[_i], t.Sub(&epsilon, &lt1[i]))
    59  		d[i+1].Mul(&d[i], t.Sub(&epsilon, &lt2[i]))
    60  	}
    61  	d = fr.BatchInvert(d)
    62  	for i := 0; i < s-1; i++ {
    63  		_ii := int(bits.Reverse64(uint64((i+1)%s)) >> nn)
    64  		z[_ii].Mul(&z[_ii], &d[i+1])
    65  	}
    66  
    67  	return z
    68  }
    69  
    70  // evaluateFirstPartNumReverse computes lt2*z(gx) - lt1*z
    71  func evaluateFirstPartNumReverse(lt1, lt2, lz []fr.Element, epsilon fr.Element) []fr.Element {
    72  
    73  	s := len(lt1)
    74  	res := make([]fr.Element, s)
    75  	var a, b fr.Element
    76  	nn := uint64(64 - bits.TrailingZeros64(uint64(s)))
    77  	for i := 0; i < s; i++ {
    78  		_i := int(bits.Reverse64(uint64(i)) >> nn)
    79  		_ii := int(bits.Reverse64(uint64((i+1)%s)) >> nn)
    80  		a.Sub(&epsilon, &lt2[_i])
    81  		a.Mul(&lz[_ii], &a)
    82  		b.Sub(&epsilon, &lt1[_i])
    83  		b.Mul(&lz[_i], &b)
    84  		res[_i].Sub(&a, &b)
    85  	}
    86  	return res
    87  }
    88  
    89  // evaluateSecondPartNumReverse computes L0 * (z-1)
    90  func evaluateSecondPartNumReverse(lz []fr.Element, d *fft.Domain) []fr.Element {
    91  
    92  	var tn, o, g fr.Element
    93  	o.SetOne()
    94  	tn.Exp(d.FrMultiplicativeGen, big.NewInt(int64(d.Cardinality))).
    95  		Sub(&tn, &o)
    96  	s := len(lz)
    97  	u := make([]fr.Element, s)
    98  	g.Set(&d.FrMultiplicativeGen)
    99  	for i := 0; i < s; i++ {
   100  		u[i].Sub(&g, &o)
   101  		g.Mul(&g, &d.Generator)
   102  	}
   103  	u = fr.BatchInvert(u)
   104  	res := make([]fr.Element, s)
   105  	nn := uint64(64 - bits.TrailingZeros64(uint64(s)))
   106  	for i := 0; i < s; i++ {
   107  		_i := int(bits.Reverse64(uint64(i)) >> nn)
   108  		res[_i].Sub(&lz[_i], &o).
   109  			Mul(&res[_i], &u[i]).
   110  			Mul(&res[_i], &tn)
   111  	}
   112  	return res
   113  }
   114  
   115  // Prove generates a proof that t1 and t2 are the same but permuted.
   116  // The size of t1 and t2 should be the same and a power of 2.
   117  func Prove(pk kzg.ProvingKey, t1, t2 []fr.Element) (Proof, error) {
   118  
   119  	// res
   120  	var proof Proof
   121  	var err error
   122  
   123  	// size checking
   124  	if len(t1) != len(t2) {
   125  		return proof, ErrIncompatibleSize
   126  	}
   127  
   128  	// create the domains
   129  	d := fft.NewDomain(uint64(len(t1)))
   130  	if d.Cardinality != uint64(len(t1)) {
   131  		return proof, ErrSize
   132  	}
   133  	s := int(d.Cardinality)
   134  	proof.size = s
   135  	proof.g.Set(&d.Generator)
   136  
   137  	// hash function for Fiat Shamir
   138  	hFunc := sha256.New()
   139  
   140  	// transcript to derive the challenge
   141  	fs := fiatshamir.NewTranscript(hFunc, "epsilon", "omega", "eta")
   142  
   143  	// commit t1, t2
   144  	ct1 := make([]fr.Element, s)
   145  	ct2 := make([]fr.Element, s)
   146  	copy(ct1, t1)
   147  	copy(ct2, t2)
   148  	d.FFTInverse(ct1, fft.DIF)
   149  	d.FFTInverse(ct2, fft.DIF)
   150  	fft.BitReverse(ct1)
   151  	fft.BitReverse(ct2)
   152  	proof.t1, err = kzg.Commit(ct1, pk)
   153  	if err != nil {
   154  		return proof, err
   155  	}
   156  	proof.t2, err = kzg.Commit(ct2, pk)
   157  	if err != nil {
   158  		return proof, err
   159  	}
   160  
   161  	// derive challenge for z
   162  	epsilon, err := deriveRandomness(fs, "epsilon", &proof.t1, &proof.t2)
   163  	if err != nil {
   164  		return proof, err
   165  	}
   166  
   167  	// compute Z and commit it
   168  	cz := evaluateAccumulationPolynomialBitReversed(t1, t2, epsilon)
   169  	d.FFTInverse(cz, fft.DIT)
   170  	proof.z, err = kzg.Commit(cz, pk)
   171  	if err != nil {
   172  		return proof, err
   173  	}
   174  	lz := make([]fr.Element, s)
   175  	copy(lz, cz)
   176  	d.FFT(lz, fft.DIF, fft.OnCoset())
   177  
   178  	// compute the first part of the numerator
   179  	lt1 := make([]fr.Element, s)
   180  	lt2 := make([]fr.Element, s)
   181  	copy(lt1, ct1)
   182  	copy(lt2, ct2)
   183  	d.FFT(lt1, fft.DIF, fft.OnCoset())
   184  	d.FFT(lt2, fft.DIF, fft.OnCoset())
   185  	lsNumFirstPart := evaluateFirstPartNumReverse(lt1, lt2, lz, epsilon)
   186  
   187  	// compute second part of the numerator
   188  	lsNum := evaluateSecondPartNumReverse(lz, d)
   189  
   190  	// derive challenge used for the folding
   191  	omega, err := deriveRandomness(fs, "omega", &proof.z)
   192  	if err != nil {
   193  		return proof, err
   194  	}
   195  
   196  	// fold the numerator and divide it by x^n-1
   197  	var t, one fr.Element
   198  	one.SetOne()
   199  	t.Exp(d.FrMultiplicativeGen, big.NewInt(int64(d.Cardinality))).Sub(&t, &one).Inverse(&t)
   200  	for i := 0; i < s; i++ {
   201  		lsNum[i].Mul(&omega, &lsNum[i]).
   202  			Add(&lsNum[i], &lsNumFirstPart[i]).
   203  			Mul(&lsNum[i], &t)
   204  	}
   205  
   206  	// get the quotient and commit it
   207  	d.FFTInverse(lsNum, fft.DIT, fft.OnCoset())
   208  	proof.q, err = kzg.Commit(lsNum, pk)
   209  	if err != nil {
   210  		return proof, err
   211  	}
   212  
   213  	// derive the evaluation challenge
   214  	eta, err := deriveRandomness(fs, "eta", &proof.q)
   215  	if err != nil {
   216  		return proof, err
   217  	}
   218  
   219  	// compute the opening proofs
   220  	proof.batchedProof, err = kzg.BatchOpenSinglePoint(
   221  		[][]fr.Element{
   222  			ct1,
   223  			ct2,
   224  			cz,
   225  			lsNum,
   226  		},
   227  		[]kzg.Digest{
   228  			proof.t1,
   229  			proof.t2,
   230  			proof.z,
   231  			proof.q,
   232  		},
   233  		eta,
   234  		hFunc,
   235  		pk,
   236  	)
   237  	if err != nil {
   238  		return proof, err
   239  	}
   240  
   241  	var shiftedEta fr.Element
   242  	shiftedEta.Mul(&eta, &d.Generator)
   243  	proof.shiftedProof, err = kzg.Open(
   244  		cz,
   245  		shiftedEta,
   246  		pk,
   247  	)
   248  	if err != nil {
   249  		return proof, err
   250  	}
   251  
   252  	// done
   253  	return proof, nil
   254  
   255  }
   256  
   257  // Verify verifies a permutation proof.
   258  func Verify(vk kzg.VerifyingKey, proof Proof) error {
   259  
   260  	// hash function that is used for Fiat Shamir
   261  	hFunc := sha256.New()
   262  
   263  	// transcript to derive the challenge
   264  	fs := fiatshamir.NewTranscript(hFunc, "epsilon", "omega", "eta")
   265  
   266  	// derive the challenges
   267  	epsilon, err := deriveRandomness(fs, "epsilon", &proof.t1, &proof.t2)
   268  	if err != nil {
   269  		return err
   270  	}
   271  
   272  	omega, err := deriveRandomness(fs, "omega", &proof.z)
   273  	if err != nil {
   274  		return err
   275  	}
   276  
   277  	eta, err := deriveRandomness(fs, "eta", &proof.q)
   278  	if err != nil {
   279  		return err
   280  	}
   281  
   282  	// check the relation
   283  	bs := big.NewInt(int64(proof.size))
   284  	var l0, a, b, one, rhs, lhs fr.Element
   285  	one.SetOne()
   286  	rhs.Exp(eta, bs).
   287  		Sub(&rhs, &one)
   288  	a.Sub(&eta, &one)
   289  	l0.Div(&rhs, &a)
   290  	rhs.Mul(&rhs, &proof.batchedProof.ClaimedValues[3])
   291  	a.Sub(&epsilon, &proof.batchedProof.ClaimedValues[1]).
   292  		Mul(&a, &proof.shiftedProof.ClaimedValue)
   293  	b.Sub(&epsilon, &proof.batchedProof.ClaimedValues[0]).
   294  		Mul(&b, &proof.batchedProof.ClaimedValues[2])
   295  	lhs.Sub(&a, &b)
   296  	a.Sub(&proof.batchedProof.ClaimedValues[2], &one).
   297  		Mul(&a, &l0).
   298  		Mul(&a, &omega)
   299  	lhs.Add(&a, &lhs)
   300  	if !lhs.Equal(&rhs) {
   301  		return ErrPermutationProof
   302  	}
   303  
   304  	// check the opening proofs
   305  	err = kzg.BatchVerifySinglePoint(
   306  		[]kzg.Digest{
   307  			proof.t1,
   308  			proof.t2,
   309  			proof.z,
   310  			proof.q,
   311  		},
   312  		&proof.batchedProof,
   313  		eta,
   314  		hFunc,
   315  		vk,
   316  	)
   317  	if err != nil {
   318  		return err
   319  	}
   320  
   321  	var shiftedEta fr.Element
   322  	shiftedEta.Mul(&eta, &proof.g)
   323  	err = kzg.Verify(&proof.z, &proof.shiftedProof, shiftedEta, vk)
   324  	if err != nil {
   325  		return err
   326  	}
   327  
   328  	// check the generator is correct
   329  	var checkOrder fr.Element
   330  	checkOrder.Exp(proof.g, big.NewInt(int64(proof.size/2)))
   331  	if checkOrder.Equal(&one) {
   332  		return ErrGenerator
   333  	}
   334  	checkOrder.Square(&checkOrder)
   335  	if !checkOrder.Equal(&one) {
   336  		return ErrGenerator
   337  	}
   338  
   339  	return nil
   340  }
   341  
   342  // TODO put that in fiat-shamir package
   343  func deriveRandomness(fs *fiatshamir.Transcript, challenge string, points ...*{{ .CurvePackage }}.G1Affine) (fr.Element, error) {
   344  
   345  	var buf [{{ .CurvePackage }}.SizeOfG1AffineUncompressed]byte
   346  	var r fr.Element
   347  
   348  	for _, p := range points {
   349  		buf = p.RawBytes()
   350  		if err := fs.Bind(challenge, buf[:]); err != nil {
   351  			return r, err
   352  		}
   353  	}
   354  
   355  	b, err := fs.ComputeChallenge(challenge)
   356  	if err != nil {
   357  		return r, err
   358  	}
   359  	r.SetBytes(b)
   360  	return r, nil
   361  }