github.com/consensys/gnark-crypto@v0.14.0/ecc/bls12-381/fr/plookup/table.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 plookup
    18  
    19  import (
    20  	"crypto/sha256"
    21  	"errors"
    22  	"math/big"
    23  	"sort"
    24  
    25  	bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
    26  	"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
    27  	"github.com/consensys/gnark-crypto/ecc/bls12-381/fr/fft"
    28  	"github.com/consensys/gnark-crypto/ecc/bls12-381/fr/permutation"
    29  	"github.com/consensys/gnark-crypto/ecc/bls12-381/kzg"
    30  	fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir"
    31  )
    32  
    33  var (
    34  	ErrIncompatibleSize = errors.New("the tables in f and t are not of the same size")
    35  	ErrFoldedCommitment = errors.New("the folded commitment is malformed")
    36  	ErrNumberDigests    = errors.New("proof.ts and proof.fs are not of the same length")
    37  )
    38  
    39  // ProofLookupTables proofs that a list of tables
    40  type ProofLookupTables struct {
    41  
    42  	// commitments to the rows f
    43  	fs []kzg.Digest
    44  
    45  	// commitments to the rows of t
    46  	ts []kzg.Digest
    47  
    48  	// lookup proof for the f and t folded
    49  	foldedProof ProofLookupVector
    50  
    51  	// proof that the ts folded correspond to t in the folded proof
    52  	permutationProof permutation.Proof
    53  }
    54  
    55  // ProveLookupTables generates a proof that f, seen as a multi dimensional table,
    56  // consists of vectors that are in t. In other words for each i, f[:][i] must be one
    57  // of the t[:][j].
    58  //
    59  // For instance, if t is the truth table of the XOR function, t will be populated such
    60  // that t[:][i] contains the i-th entry of the truth table, so t[0][i] XOR t[1][i] = t[2][i].
    61  //
    62  // The fr.Vector in f and t are supposed to be of the same size constant size.
    63  func ProveLookupTables(pk kzg.ProvingKey, f, t []fr.Vector) (ProofLookupTables, error) {
    64  
    65  	// res
    66  	proof := ProofLookupTables{}
    67  	var err error
    68  
    69  	// hash function used for Fiat Shamir
    70  	hFunc := sha256.New()
    71  
    72  	// transcript to derive the challenge
    73  	fs := fiatshamir.NewTranscript(hFunc, "lambda")
    74  
    75  	// check the sizes
    76  	if len(f) != len(t) {
    77  		return proof, ErrIncompatibleSize
    78  	}
    79  	s := len(f[0])
    80  	for i := 1; i < len(f); i++ {
    81  		if len(f[i]) != s {
    82  			return proof, ErrIncompatibleSize
    83  		}
    84  	}
    85  	s = len(t[0])
    86  	for i := 1; i < len(t); i++ {
    87  		if len(t[i]) != s {
    88  			return proof, ErrIncompatibleSize
    89  		}
    90  	}
    91  
    92  	// commit to the tables in f and t
    93  	nbRows := len(t)
    94  	proof.fs = make([]kzg.Digest, nbRows)
    95  	proof.ts = make([]kzg.Digest, nbRows)
    96  	_nbColumns := len(f[0]) + 1
    97  	if _nbColumns < len(t[0]) {
    98  		_nbColumns = len(t[0])
    99  	}
   100  	d := fft.NewDomain(uint64(_nbColumns))
   101  	nbColumns := d.Cardinality
   102  	lfs := make([][]fr.Element, nbRows)
   103  	cfs := make([][]fr.Element, nbRows)
   104  	lts := make([][]fr.Element, nbRows)
   105  	cts := make([][]fr.Element, nbRows)
   106  
   107  	for i := 0; i < nbRows; i++ {
   108  
   109  		cfs[i] = make([]fr.Element, nbColumns)
   110  		lfs[i] = make([]fr.Element, nbColumns)
   111  		copy(cfs[i], f[i])
   112  		copy(lfs[i], f[i])
   113  		for j := len(f[i]); j < int(nbColumns); j++ {
   114  			cfs[i][j] = f[i][len(f[i])-1]
   115  			lfs[i][j] = f[i][len(f[i])-1]
   116  		}
   117  		d.FFTInverse(cfs[i], fft.DIF)
   118  		fft.BitReverse(cfs[i])
   119  		proof.fs[i], err = kzg.Commit(cfs[i], pk)
   120  		if err != nil {
   121  			return proof, err
   122  		}
   123  
   124  		cts[i] = make([]fr.Element, nbColumns)
   125  		lts[i] = make([]fr.Element, nbColumns)
   126  		copy(cts[i], t[i])
   127  		copy(lts[i], t[i])
   128  		for j := len(t[i]); j < int(d.Cardinality); j++ {
   129  			cts[i][j] = t[i][len(t[i])-1]
   130  			lts[i][j] = t[i][len(t[i])-1]
   131  		}
   132  		d.FFTInverse(cts[i], fft.DIF)
   133  		fft.BitReverse(cts[i])
   134  		proof.ts[i], err = kzg.Commit(cts[i], pk)
   135  		if err != nil {
   136  			return proof, err
   137  		}
   138  	}
   139  
   140  	// fold f and t
   141  	comms := make([]*kzg.Digest, 2*nbRows)
   142  	for i := 0; i < nbRows; i++ {
   143  		comms[i] = new(kzg.Digest)
   144  		comms[i].Set(&proof.fs[i])
   145  		comms[nbRows+i] = new(kzg.Digest)
   146  		comms[nbRows+i].Set(&proof.ts[i])
   147  	}
   148  	lambda, err := deriveRandomness(fs, "lambda", comms...)
   149  	if err != nil {
   150  		return proof, err
   151  	}
   152  	foldedf := make(fr.Vector, nbColumns)
   153  	foldedt := make(fr.Vector, nbColumns)
   154  	for i := 0; i < int(nbColumns); i++ {
   155  		for j := nbRows - 1; j >= 0; j-- {
   156  			foldedf[i].Mul(&foldedf[i], &lambda).
   157  				Add(&foldedf[i], &lfs[j][i])
   158  			foldedt[i].Mul(&foldedt[i], &lambda).
   159  				Add(&foldedt[i], &lts[j][i])
   160  		}
   161  	}
   162  
   163  	// generate a proof of permutation of the foldedt and sort(foldedt)
   164  	foldedtSorted := make(fr.Vector, nbColumns)
   165  	copy(foldedtSorted, foldedt)
   166  	sort.Sort(foldedtSorted)
   167  	proof.permutationProof, err = permutation.Prove(pk, foldedt, foldedtSorted)
   168  	if err != nil {
   169  		return proof, err
   170  	}
   171  
   172  	// call plookupVector, on foldedf[:len(foldedf)-1] to ensure that the domain size
   173  	// in ProveLookupVector is the same as d's
   174  	proof.foldedProof, err = ProveLookupVector(pk, foldedf[:len(foldedf)-1], foldedt)
   175  
   176  	return proof, err
   177  }
   178  
   179  // VerifyLookupTables verifies that a ProofLookupTables proof is correct.
   180  func VerifyLookupTables(vk kzg.VerifyingKey, proof ProofLookupTables) error {
   181  
   182  	// hash function used for Fiat Shamir
   183  	hFunc := sha256.New()
   184  
   185  	// transcript to derive the challenge
   186  	fs := fiatshamir.NewTranscript(hFunc, "lambda")
   187  
   188  	// check that the number of digests is the same
   189  	if len(proof.fs) != len(proof.ts) {
   190  		return ErrNumberDigests
   191  	}
   192  
   193  	// fold the commitments fs and ts
   194  	nbRows := len(proof.fs)
   195  	comms := make([]*kzg.Digest, 2*nbRows)
   196  	for i := 0; i < nbRows; i++ {
   197  		comms[i] = &proof.fs[i]
   198  		comms[i+nbRows] = &proof.ts[i]
   199  	}
   200  	lambda, err := deriveRandomness(fs, "lambda", comms...)
   201  	if err != nil {
   202  		return err
   203  	}
   204  
   205  	// fold the commitments of the rows of t and f
   206  	var comf, comt kzg.Digest
   207  	comf.Set(&proof.fs[nbRows-1])
   208  	comt.Set(&proof.ts[nbRows-1])
   209  	var blambda big.Int
   210  	lambda.BigInt(&blambda)
   211  	for i := nbRows - 2; i >= 0; i-- {
   212  		comf.ScalarMultiplication(&comf, &blambda).
   213  			Add(&comf, &proof.fs[i])
   214  		comt.ScalarMultiplication(&comt, &blambda).
   215  			Add(&comt, &proof.ts[i])
   216  	}
   217  
   218  	// check that the folded commitment of the fs correspond to foldedProof.f
   219  	if !comf.Equal(&proof.foldedProof.f) {
   220  		return ErrFoldedCommitment
   221  	}
   222  
   223  	// check that the folded commitment of the ts is a permutation of proof.FoldedProof.t
   224  	err = permutation.Verify(vk, proof.permutationProof)
   225  	if err != nil {
   226  		return err
   227  	}
   228  
   229  	// verify the inner proof
   230  	return VerifyLookupVector(vk, proof.foldedProof)
   231  }
   232  
   233  // TODO put that in fiat-shamir package
   234  func deriveRandomness(fs *fiatshamir.Transcript, challenge string, points ...*bls12381.G1Affine) (fr.Element, error) {
   235  
   236  	var buf [bls12381.SizeOfG1AffineUncompressed]byte
   237  	var r fr.Element
   238  
   239  	for _, p := range points {
   240  		buf = p.RawBytes()
   241  		if err := fs.Bind(challenge, buf[:]); err != nil {
   242  			return r, err
   243  		}
   244  	}
   245  
   246  	b, err := fs.ComputeChallenge(challenge)
   247  	if err != nil {
   248  		return r, err
   249  	}
   250  	r.SetBytes(b)
   251  	return r, nil
   252  }