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

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