github.com/consensys/gnark-crypto@v0.14.0/ecc/bn254/fr/permutation/permutation.go (about)

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